protected override SucoExpression deduceTypes(SucoTypeEnvironment env, SucoContext context) { var newEnv = env; var newClauses = new List <SucoListClause>(); var anySingleton = false; foreach (var clause in Clauses) { var newFromExpression = clause.FromExpression?.DeduceTypes(newEnv, context); // Deduce the type of the iterator variable SucoType collectionType; if (newFromExpression != null) { collectionType = newFromExpression.Type; } else if (clause.HasDollar) { collectionType = SucoType.Cell.List(); } else { try { collectionType = env.GetVariableType("cells"); } catch (SucoTempCompileException tc) { throw new SucoCompileException(tc.Message, clause.StartIndex, clause.EndIndex); } } if (collectionType is not SucoListType { ElementType: SucoType elementType })
public override SucoListCondition DeduceTypes(SucoTypeEnvironment env, SucoContext context, SucoType elementType) { switch (Name) { case "first": case "last": case "before": case "after": case "~": break; case "^": case ">": case "v": case "<": case "↑": case "→": case "↓": case "←": case "diagonal": case "adjacent": case "orthogonal": case "above": case "right": case "below": case "left": case "samerow": case "samecol": case "samebox": case "topleft": case "topright": case "bottomleft": case "bottomright": case "lefttop": case "righttop": case "leftbottom": case "rightbottom": if (!elementType.Equals(SucoType.Cell)) { throw new SucoCompileException($"“{Name}” can only be used on lists of cells.", StartIndex, EndIndex); } break; default: throw new SucoCompileException($"Unknown shortcut condition: “{Name}”.", StartIndex, EndIndex); } return(this); }
protected override SucoExpression deduceTypes(SucoTypeEnvironment env, SucoContext context) { if (Elements.Count == 0) { throw new SucoCompileException("Empty array literals are not currently supported.", StartIndex, EndIndex); } var newElements = Elements.Select(e => e.DeduceTypes(env, context)).ToList(); for (var i = 0; i < newElements.Count; i++) { if (newElements.All(e => e.Type.ImplicitlyConvertibleTo(newElements[i].Type))) { return(new SucoArrayExpression(StartIndex, EndIndex, newElements.Select(e => e.ImplicitlyConvertTo(newElements[i].Type)).ToList(), newElements[i].Type.List())); } } throw new SucoCompileException("This array contains elements that are not compatible with one another.", StartIndex, EndIndex); }
protected override SucoExpression deduceTypes(SucoTypeEnvironment env, SucoContext context) { var newPieces = Pieces.Select(p => { if (p is not SucoStringLiteralPieceExpression expr) { return(p); } var typedExpr = expr.Expression.DeduceTypes(env, context); if (!typedExpr.Type.ImplicitlyConvertibleTo(SucoType.String)) { throw new SucoCompileException($"Expression interpolated into a string literal is of type “{typedExpr.Type}”, which not implicitly convertible to string.", expr.Expression.StartIndex, expr.Expression.EndIndex); } return(typedExpr.ImplicitlyConvertTo(SucoType.String)); }).ToArray(); return(new SucoStringLiteralExpression(StartIndex, EndIndex, newPieces, SucoType.String)); }
public static bool IsValidCode(string source, SucoTypeEnvironment env, SucoContext context, SucoType expectedResultType, out string error) { try { ParseCode(source, env, context, expectedResultType); error = null; return(true); } catch (SucoCompileException sce) { error = sce.Message; return(false); } catch (SucoParseException spe) { error = spe.Message; return(false); } }
public static SucoExpression ParseCode(string source, SucoTypeEnvironment env, SucoContext context, SucoType expectedResultType = null) { if (source == null) { throw new ArgumentNullException(nameof(source)); } try { // Try parsing as a standalone expression (e.g. “cells.unique”). var parser = new SucoParser(source, context); var ret = parser.parseExpression(); parser.EnforceEof(); ret = ret.DeduceTypes(env, context); if (expectedResultType != null && !ret.Type.ImplicitlyConvertibleTo(expectedResultType)) { throw new SucoParseException($"The expression is of type “{ret.Type}”, which is not implicitly convertible to the required type, “{expectedResultType}”.", ret.StartIndex, ret.EndIndex); } return(expectedResultType == null ? ret : ret.ImplicitlyConvertTo(expectedResultType)); } catch (SucoParseException pe1) { try { // Try parsing as a list comprehension (e.g. “a: a.odd”). var parser = new SucoParser(source, context); var ret = parser.parseListComprehension(); parser.EnforceEof(); ret = ret.DeduceTypes(env, context); if (expectedResultType != null && !ret.Type.ImplicitlyConvertibleTo(expectedResultType)) { throw new SucoParseException($"The expression is of type “{ret.Type}”, which is not implicitly convertible to the required type, “{expectedResultType}”.", ret.StartIndex, ret.EndIndex); } return(expectedResultType == null ? ret : ret.ImplicitlyConvertTo(expectedResultType)); } catch (SucoParseException pe2) when(pe2.StartIndex <= pe1.StartIndex) { } throw; } }
public SucoExpression DeduceTypes(SucoTypeEnvironment env, SucoContext context) { var result = deduceTypes(env, context); switch (context) { case SucoContext.Constraint: if (result.Type is SucoDecimalType) { throw new SucoCompileException("You cannot use decimal numbers in a puzzle constraint.", StartIndex, EndIndex); } if (result.Type is SucoStringType) { throw new SucoCompileException("You cannot use strings in a puzzle constraint.", StartIndex, EndIndex); } break; case SucoContext.Svg: break; } return(result); }
protected override SucoExpression deduceTypes(SucoTypeEnvironment env, SucoContext context) { try { var operand = Operand.DeduceTypes(env, context); if (operand.Type is not SucoFunctionType fnc) { throw new SucoCompileException($"“{operand.Type}” is not a function type.", operand.StartIndex, operand.EndIndex); } var newArguments = Arguments.Select(arg => arg.DeduceTypes(env, context)).ToArray(); var(parameterTypes, returnType) = fnc.Resolve(newArguments.Select(a => a.Type).ToArray()); for (var i = 0; i < newArguments.Length; i++) { newArguments[i] = newArguments[i].ImplicitlyConvertTo(parameterTypes[i]); } return(new SucoCallExpression(StartIndex, EndIndex, operand, newArguments, returnType)); } catch (SucoFunctionResolutionException re) { throw new SucoCompileException(re.Message, StartIndex, EndIndex); } }
protected abstract SucoExpression deduceTypes(SucoTypeEnvironment env, SucoContext context);
protected override SucoExpression deduceTypes(SucoTypeEnvironment env, SucoContext context) => this;