public override SucoExpression Optimize(SucoEnvironment env, int?[] givens) { var constants = ElementType.CreateArray(Elements.Count); var expressions = new SucoExpression[Elements.Count]; var anyExpressions = false; for (var i = 0; i < Elements.Count; i++) { var opt = Elements[i].Optimize(env, givens); if (opt is SucoConstant c) { constants.SetValue(c.Value, i); } else { expressions[i] = opt; anyExpressions = true; } } if (!anyExpressions) { return(new SucoConstant(StartIndex, EndIndex, Type, constants)); } return(new SucoOptimizedArrayExpression(StartIndex, EndIndex, constants, expressions, Type)); }
public SucoListClause(int startIndex, int endIndex, string variableName, bool hasDollar, bool hasPlus, bool hasSingleton, SucoExpression fromExpression, List <SucoListCondition> conditions, SucoType varType = null) : base(startIndex, endIndex) { VariableName = variableName; HasDollar = hasDollar; HasPlus = hasPlus; HasSingleton = hasSingleton; FromExpression = fromExpression; Conditions = conditions; VariableType = varType; }
private SucoExpression parseListComprehension(int?startIx = null, bool consumeCloseCurly = false) { var rStartIx = startIx ?? _ix; var clauses = new List <SucoListClause>(); SucoExpression selector = null; nextClause: var clauseStartIx = _ix; var flags = new Dictionary <string, SucoToken>(); nextFlag: foreach (var flag in _flags) { if (token(flag, out var flagToken)) { if (flags.ContainsKey(flag)) { throw new SucoParseException($"Duplicate flag: “{flag}”.", flagToken, flags[flag]); } flags[flag] = flagToken; goto nextFlag; } } var variableName = getToken(); if (variableName.Type != SucoTokenType.Identifier) { throw new SucoParseException($"Expected variable name{_flags.Except(flags.Keys).ToArray().Select(s => $" or “{s}”").JoinString()}.", variableName); } _ix = variableName.EndIndex; var conditions = new List <SucoListCondition>(); SucoExpression fromExpression = null; void commitClause(int endIndex) { SucoListClause clause; if (!flags.ContainsKey("1") && (clause = clauses.LastOrDefault(c => c.HasSingleton)) != null) { throw new SucoParseException("A clause with a “1” flag cannot be followed by a clause without one.", clauseStartIx, endIndex, clause); } if (flags.ContainsKey("$") && fromExpression != null) { throw new SucoParseException("A clause with a “$” flag cannot also have a “from” condition.", clauseStartIx, endIndex); } clauses.Add(new SucoListClause(clauseStartIx, endIndex, variableName.StringValue, flags.ContainsKey("$"), flags.ContainsKey("+"), flags.ContainsKey("1"), fromExpression, conditions)); } nextCondition: if (token(":", out var tok)) { commitClause(tok.EndIndex); selector = parseExpression(); goto done; } else if (token(",", out tok)) { commitClause(tok.EndIndex); goto nextClause; } else if (token("(", out tok)) { var innerExpr = parseExpression(); if (!token(")")) { throw new SucoParseException("Unmatched “(”: condition must end in “)”.", getToken(), tok); } conditions.Add(new SucoListExpressionCondition(tok.StartIndex, _ix, innerExpr)); goto nextCondition; } foreach (var shc in new[] { "~", "<", ">", "^", "v", "←", "→", "↑", "↓" }) { if (token(shc, out tok)) { conditions.Add(new SucoListShortcutCondition(tok.StartIndex, tok.EndIndex, shc)); goto nextCondition; } } tok = getToken(); if (tok.Type == SucoTokenType.Identifier && tok.StringValue == "from") { if (fromExpression != null) { throw new SucoParseException("Duplicate “from” condition.", tok, fromExpression); } _ix = tok.EndIndex; var tokVarName = getToken(); if (tokVarName.Type == SucoTokenType.Identifier) { fromExpression = new SucoIdentifierExpression(tokVarName.StartIndex, tokVarName.EndIndex, tokVarName.StringValue); _ix = tokVarName.EndIndex; } else { if (!token("(")) { throw new SucoParseException("“from” must be followed by either a variable name, or a parenthesized expression.", getToken(), tok); } fromExpression = parseExpression(); if (!token(")")) { throw new SucoParseException("Unmatched “(”: “from” expression must end in “)”.", getToken(), tok); } } goto nextCondition; } if (tok.Type == SucoTokenType.Identifier) { conditions.Add(new SucoListShortcutCondition(tok.StartIndex, tok.EndIndex, tok.StringValue)); _ix = tok.EndIndex; goto nextCondition; } commitClause(_ix); done: if (selector == null) { if (clauses.Count != 1) { throw new SucoParseException("List comprehensions with more than one clause must have a selector.", _ix, rStartIx); } selector = new SucoIdentifierExpression(_ix, _ix, clauses[0].VariableName); } if (consumeCloseCurly) { if (!token("}")) { throw new SucoParseException("Unmatched “{”: list comprehension must be terminated with “}”.", _ix, rStartIx); } } return(new SucoListComprehensionExpression(rStartIx, _ix, clauses, selector)); }
public SucoCallExpression(int startIndex, int endIndex, SucoExpression operand, SucoExpression[] arguments, SucoType type = null) : base(startIndex, endIndex, type) { Operand = operand; Arguments = arguments; }
public SucoListComprehensionExpression(int startIndex, int endIndex, List <SucoListClause> clauses, SucoExpression selector, SucoType type = null) : base(startIndex, endIndex, type) { Clauses = clauses ?? throw new ArgumentNullException(nameof(clauses)); Selector = selector ?? throw new ArgumentNullException(nameof(selector)); }