diff --git a/common/src/main/java/dev/cel/common/CelOptions.java b/common/src/main/java/dev/cel/common/CelOptions.java index 417d4dc9d..c4b868bf2 100644 --- a/common/src/main/java/dev/cel/common/CelOptions.java +++ b/common/src/main/java/dev/cel/common/CelOptions.java @@ -72,6 +72,8 @@ public enum ProtoUnsetFieldOptions { public abstract boolean enableQuotedIdentifierSyntax(); + public abstract boolean enablePrattParser(); + // Type-Checker related options public abstract boolean enableCompileTimeOverloadResolution(); @@ -144,6 +146,7 @@ public static Builder newBuilder() { .retainUnbalancedLogicalExpressions(false) .enableHiddenAccumulatorVar(true) .enableQuotedIdentifierSyntax(true) + .enablePrattParser(false) // Type-Checker options .enableCompileTimeOverloadResolution(false) .enableHomogeneousLiterals(false) @@ -279,6 +282,14 @@ public abstract static class Builder { */ public abstract Builder enableQuotedIdentifierSyntax(boolean value); + /** + * Enables Pratt parser implementation over ANTLR parser. + * + *

The Pratt parser provides improved parsing performance (typically 4x–11x speedup over + * ANTLR) and lower memory overhead while producing an equivalent abstract syntax tree. + */ + public abstract Builder enablePrattParser(boolean value); + // Type-Checker related options /** diff --git a/common/src/test/java/dev/cel/common/CelOptionsTest.java b/common/src/test/java/dev/cel/common/CelOptionsTest.java index 751d85ed8..cc5203a25 100644 --- a/common/src/test/java/dev/cel/common/CelOptionsTest.java +++ b/common/src/test/java/dev/cel/common/CelOptionsTest.java @@ -35,5 +35,6 @@ public void current_defaults() { // Defaults that aren't represented in deprecated CelOptions assertThat(CelOptions.current().build().enableUnknownTracking()).isFalse(); assertThat(CelOptions.current().build().resolveTypeDependencies()).isTrue(); + assertThat(CelOptions.current().build().enablePrattParser()).isFalse(); } } diff --git a/extensions/src/test/java/dev/cel/extensions/CelMathExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelMathExtensionsTest.java index 68c80dedb..5b57f1fb2 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelMathExtensionsTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelMathExtensionsTest.java @@ -827,7 +827,7 @@ public void abs_overflow_throwsException() { assertThat(e) .hasMessageThat() - .contains("ERROR: :1:10: For input string: \"-9223372036854775809\""); + .contains("ERROR: :1:10: invalid int literal: -9223372036854775809"); } @Test @@ -917,7 +917,7 @@ public void bitAnd_maxValArg_throwsException() { assertThat(e) .hasMessageThat() - .contains("ERROR: :1:33: For input string: \"9223372036854775809\""); + .contains("ERROR: :1:33: invalid int literal: 9223372036854775809"); } @Test diff --git a/parser/src/main/java/dev/cel/parser/AntlrParser.java b/parser/src/main/java/dev/cel/parser/AntlrParser.java new file mode 100644 index 000000000..155fb0843 --- /dev/null +++ b/parser/src/main/java/dev/cel/parser/AntlrParser.java @@ -0,0 +1,1411 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dev.cel.parser; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.primitives.Ints.min; + +import cel.parser.internal.CELBaseVisitor; +import cel.parser.internal.CELLexer; +import cel.parser.internal.CELParser; +import cel.parser.internal.CELParser.BoolFalseContext; +import cel.parser.internal.CELParser.BoolTrueContext; +import cel.parser.internal.CELParser.BytesContext; +import cel.parser.internal.CELParser.CalcContext; +import cel.parser.internal.CELParser.ConditionalAndContext; +import cel.parser.internal.CELParser.ConditionalOrContext; +import cel.parser.internal.CELParser.ConstantLiteralContext; +import cel.parser.internal.CELParser.CreateListContext; +import cel.parser.internal.CELParser.CreateMapContext; +import cel.parser.internal.CELParser.CreateMessageContext; +import cel.parser.internal.CELParser.DoubleContext; +import cel.parser.internal.CELParser.EscapeIdentContext; +import cel.parser.internal.CELParser.EscapedIdentifierContext; +import cel.parser.internal.CELParser.ExprContext; +import cel.parser.internal.CELParser.ExprListContext; +import cel.parser.internal.CELParser.FieldInitializerListContext; +import cel.parser.internal.CELParser.GlobalCallContext; +import cel.parser.internal.CELParser.IdentContext; +import cel.parser.internal.CELParser.IndexContext; +import cel.parser.internal.CELParser.IntContext; +import cel.parser.internal.CELParser.ListInitContext; +import cel.parser.internal.CELParser.LogicalNotContext; +import cel.parser.internal.CELParser.MapInitializerListContext; +import cel.parser.internal.CELParser.MemberCallContext; +import cel.parser.internal.CELParser.MemberExprContext; +import cel.parser.internal.CELParser.NegateContext; +import cel.parser.internal.CELParser.NestedContext; +import cel.parser.internal.CELParser.NullContext; +import cel.parser.internal.CELParser.OptExprContext; +import cel.parser.internal.CELParser.OptFieldContext; +import cel.parser.internal.CELParser.PrimaryExprContext; +import cel.parser.internal.CELParser.RelationContext; +import cel.parser.internal.CELParser.SelectContext; +import cel.parser.internal.CELParser.SimpleIdentifierContext; +import cel.parser.internal.CELParser.StartContext; +import cel.parser.internal.CELParser.StringContext; +import cel.parser.internal.CELParser.UintContext; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.FormatMethod; +import com.google.errorprone.annotations.FormatString; +import dev.cel.common.CelAbstractSyntaxTree; +import dev.cel.common.CelIssue; +import dev.cel.common.CelOptions; +import dev.cel.common.CelSource; +import dev.cel.common.CelSourceLocation; +import dev.cel.common.CelValidationResult; +import dev.cel.common.Operator; +import dev.cel.common.ast.CelConstant; +import dev.cel.common.ast.CelExpr; +import dev.cel.common.internal.CodePointStream; +import dev.cel.common.internal.Constants; +import java.text.ParseException; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Supplier; +import org.antlr.v4.runtime.ANTLRErrorListener; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.DefaultErrorStrategy; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.atn.ATNConfigSet; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.misc.ParseCancellationException; +import org.antlr.v4.runtime.tree.ErrorNode; +import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.ParseTreeListener; +import org.antlr.v4.runtime.tree.TerminalNode; + +/** ANTLR-based parser implementation for CEL. */ +final class AntlrParser extends CELBaseVisitor { + + private static final CelExpr ERROR = CelExpr.newBuilder().setConstant(Constants.ERROR).build(); + private static final ImmutableSet RESERVED_IDS = + ImmutableSet.of( + "as", + "break", + "const", + "continue", + "else", + "false", + "for", + "function", + "if", + "import", + "in", + "let", + "loop", + "package", + "namespace", + "null", + "return", + "true", + "var", + "void", + "while"); + private static final String ACCUMULATOR_NAME = "__result__"; + private static final String HIDDEN_ACCUMULATOR_NAME = "@result"; + + static CelValidationResult parse( + CelSource source, CelOptions options, Collection macros) { + if (source.getContent().size() > options.maxExpressionCodePointSize()) { + return new CelValidationResult( + source, + ImmutableList.of( + CelIssue.formatError( + CelSourceLocation.NONE, + String.format( + "expression code point size exceeds limit: size: %d, limit %d", + source.getContent().size(), options.maxExpressionCodePointSize())))); + } + CELLexer antlrLexer = + new CELLexer(new CodePointStream(source.getDescription(), source.getContent())); + CELParser antlrParser = new CELParser(new CommonTokenStream(antlrLexer)); + CelSource.Builder sourceInfo = source.toBuilder(); + sourceInfo.setDescription(source.getDescription()); + ExprFactory exprFactory = + new ExprFactory( + antlrParser, + sourceInfo, + options.enableHiddenAccumulatorVar() ? HIDDEN_ACCUMULATOR_NAME : ACCUMULATOR_NAME, + options.maxParseExpressionNodeCount()); + AntlrParser parserImpl = new AntlrParser(options, macros, sourceInfo, exprFactory); + ErrorListener errorListener = new ErrorListener(exprFactory); + antlrLexer.removeErrorListeners(); + antlrParser.removeErrorListeners(); + antlrLexer.addErrorListener(errorListener); + antlrParser.addErrorListener(errorListener); + antlrParser.addParseListener( + new PerRuleRecursionListener(exprFactory, options.maxParseRecursionDepth())); + antlrParser.setErrorHandler( + new RecoveryLimitErrorStrategy(options.maxParseErrorRecoveryLimit())); + CelExpr expr; + try { + StartContext context = checkNotNull(antlrParser.start()); + expr = checkNotNull(parserImpl.visit(context)); + } catch (ParseCancellationException parseFailure) { + return new CelValidationResult( + sourceInfo.build(), parseFailure, ImmutableList.copyOf(exprFactory.getIssuesList())); + } + return new CelValidationResult( + CelAbstractSyntaxTree.newParsedAst(expr, sourceInfo.build()), + ImmutableList.copyOf(exprFactory.getIssuesList())); + } + + private final CelOptions options; + private final ImmutableMap macros; + private final CelSource.Builder sourceInfo; + private final ExprFactory exprFactory; + + private int recursionDepth; + + private AntlrParser( + CelOptions options, + Collection macros, + CelSource.Builder sourceInfo, + ExprFactory exprFactory) { + this.options = options; + this.macros = macros.stream().collect(ImmutableMap.toImmutableMap(CelMacro::getKey, m -> m)); + this.sourceInfo = sourceInfo; + this.exprFactory = exprFactory; + } + + @Override + public CelExpr visit(ParseTree tree) { + ParseTree unnestedNode = unnest(tree); + boolean isLeftRecursiveNode = isLeftRecursiveForCountingDepths(unnestedNode); + if (isLeftRecursiveNode) { + checkAndIncrementRecursionDepth(); + CelExpr expr = super.visit(unnestedNode); + decrementRecursionDepth(); + return expr; + } + + return super.visit(unnestedNode); + } + + @Override + public CelExpr visitStart(StartContext context) { + checkNotNull(context); + if (context.e == null) { + return exprFactory.ensureErrorsExist(context); + } + return visit(context.e); + } + + @Override + public CelExpr visitExpr(ExprContext context) { + checkNotNull(context); + if (context.e == null) { + return exprFactory.ensureErrorsExist(context); + } + CelExpr condition = visit(context.e); + if (context.op != null) { + if (context.e1 == null || context.e2 == null) { + return exprFactory.ensureErrorsExist(context); + } + condition = + exprFactory + .newExprBuilder(context.op) + .setCall( + CelExpr.CelCall.newBuilder() + .setFunction(Operator.CONDITIONAL.getFunction()) + .addArgs(condition) + .addArgs(visit(context.e1)) + .addArgs(visit(context.e2)) + .build()) + .build(); + } + + return condition; + } + + @Override + public CelExpr visitConditionalOr(ConditionalOrContext context) { + checkNotNull(context); + if (context.e == null) { + return exprFactory.ensureErrorsExist(context); + } + CelExpr conditionalOr = visit(context.e); + if (context.ops == null || context.ops.isEmpty()) { + return conditionalOr; + } + ExpressionBalancer balancer = + new ExpressionBalancer(Operator.LOGICAL_OR.getFunction(), conditionalOr); + int index = 0; + for (Token token : context.ops) { + if (context.e1 == null || index >= context.e1.size()) { + return exprFactory.reportError(context, "unexpected character, wanted '||'"); + } + long operationId = exprFactory.newExprId(exprFactory.getPosition(token)); + CelExpr term = visit(context.e1.get(index)); + balancer.add(operationId, term); + index++; + } + return balancer.balance(); + } + + @Override + public CelExpr visitConditionalAnd(ConditionalAndContext context) { + checkNotNull(context); + if (context.e == null) { + return exprFactory.ensureErrorsExist(context); + } + CelExpr conditionalAnd = visit(context.e); + if (context.ops == null || context.ops.isEmpty()) { + return conditionalAnd; + } + ExpressionBalancer balancer = + new ExpressionBalancer(Operator.LOGICAL_AND.getFunction(), conditionalAnd); + int index = 0; + for (Token token : context.ops) { + if (context.e1 == null || index >= context.e1.size()) { + return exprFactory.reportError(context, "unexpected character, wanted '&&'"); + } + long operationId = exprFactory.newExprId(exprFactory.getPosition(token)); + CelExpr term = visit(context.e1.get(index)); + balancer.add(operationId, term); + index++; + } + return balancer.balance(); + } + + @Override + public CelExpr visitRelation(RelationContext context) { + checkNotNull(context); + if (context.calc() != null) { + return visit(context.calc()); + } + if (context.relation() == null || context.relation().isEmpty() || context.op == null) { + return exprFactory.ensureErrorsExist(context); + } + Optional operator = Operator.find(context.op.getText()); + if (!operator.isPresent()) { + return exprFactory.reportError(context, "operator not found"); + } + CelExpr left = visit(context.relation(0)); + CelExpr.Builder exprBuilder = exprFactory.newExprBuilder(context.op); + CelExpr right = visit(context.relation(1)); + return exprBuilder + .setCall( + CelExpr.CelCall.newBuilder() + .setFunction(operator.get().getFunction()) + .addArgs(left) + .addArgs(right) + .build()) + .build(); + } + + @Override + public CelExpr visitCalc(CalcContext context) { + checkNotNull(context); + if (context.unary() != null) { + return visit(context.unary()); + } + if (context.calc() == null || context.calc().isEmpty() || context.op == null) { + return exprFactory.ensureErrorsExist(context); + } + Optional operator = Operator.find(context.op.getText()); + if (!operator.isPresent()) { + return exprFactory.reportError(context, "operator not found"); + } + CelExpr left = visit(context.calc(0)); + CelExpr.Builder exprBuilder = exprFactory.newExprBuilder(context.op); + CelExpr right = visit(context.calc(1)); + return exprBuilder + .setCall( + CelExpr.CelCall.newBuilder() + .setFunction(operator.get().getFunction()) + .addArgs(left) + .addArgs(right) + .build()) + .build(); + } + + @Override + public CelExpr visitMemberExpr(MemberExprContext context) { + checkNotNull(context); + if (context.member() == null) { + return exprFactory.ensureErrorsExist(context); + } + return visit(context.member()); + } + + @Override + public CelExpr visitLogicalNot(LogicalNotContext context) { + checkNotNull(context); + if (context.member() == null) { + return exprFactory.ensureErrorsExist(context); + } + if (context.ops != null && options.retainRepeatedUnaryOperators()) { + CelExpr expr = visit(context.member()); + for (int index = context.ops.size(); index > 0; --index) { + expr = + exprFactory + .newExprBuilder(context.ops.get(index - 1)) + .setCall( + CelExpr.CelCall.newBuilder() + .setFunction(Operator.LOGICAL_NOT.getFunction()) + .addArgs(expr) + .build()) + .build(); + } + return expr; + } else if (context.ops == null || context.ops.size() % 2 == 0) { + return visit(context.member()); + } + CelExpr.Builder exprBuilder = exprFactory.newExprBuilder(context.ops.get(0)); + CelExpr member = visit(context.member()); + return exprBuilder + .setCall( + CelExpr.CelCall.newBuilder() + .setFunction(Operator.LOGICAL_NOT.getFunction()) + .addArgs(member) + .build()) + .build(); + } + + @Override + public CelExpr visitNegate(NegateContext context) { + checkNotNull(context); + if (context.member() == null) { + return exprFactory.ensureErrorsExist(context); + } + if (context.ops != null && options.retainRepeatedUnaryOperators()) { + CelExpr expr = visit(context.member()); + for (int index = context.ops.size(); index > 0; --index) { + expr = + exprFactory + .newExprBuilder(context.ops.get(index - 1)) + .setCall( + CelExpr.CelCall.newBuilder() + .setFunction(Operator.NEGATE.getFunction()) + .addArgs(expr) + .build()) + .build(); + } + return expr; + } else if (context.ops == null || context.ops.size() % 2 == 0) { + return visit(context.member()); + } + CelExpr.Builder exprBuilder = exprFactory.newExprBuilder(context.ops.get(0)); + CelExpr member = visit(context.member()); + return exprBuilder + .setCall( + CelExpr.CelCall.newBuilder() + .setFunction(Operator.NEGATE.getFunction()) + .addArgs(member) + .build()) + .build(); + } + + @Override + public CelExpr visitPrimaryExpr(PrimaryExprContext context) { + checkNotNull(context); + if (context.primary() == null) { + return exprFactory.ensureErrorsExist(context); + } + return visit(context.primary()); + } + + @Override + public CelExpr visitSelect(SelectContext context) { + checkNotNull(context); + if (context.member() == null) { + return exprFactory.ensureErrorsExist(context); + } + CelExpr member = visit(context.member()); + if (context.id == null) { + return exprFactory.newExprBuilder(context).build(); + } + String id = normalizeEscapedIdent(context.id); + + if (context.opt != null && context.opt.getText().equals("?")) { + if (!options.enableOptionalSyntax()) { + return exprFactory.reportError(context.op, "unsupported syntax '.?'"); + } + + CelExpr.Builder exprBuilder = exprFactory.newExprBuilder(exprFactory.getPosition(context.op)); + CelExpr.CelCall callExpr = + CelExpr.CelCall.newBuilder() + .setFunction(Operator.OPTIONAL_SELECT.getFunction()) + .addArgs( + Arrays.asList( + member, + exprFactory + .newExprBuilder(context) + .setConstant(CelConstant.ofValue(id)) + .build())) + .build(); + + return exprBuilder.setCall(callExpr).build(); + } + + return exprFactory + .newExprBuilder(context.op) + .setSelect(CelExpr.CelSelect.newBuilder().setOperand(member).setField(id).build()) + .build(); + } + + @Override + public CelExpr visitMemberCall(MemberCallContext context) { + checkNotNull(context); + if (context.member() == null) { + return exprFactory.ensureErrorsExist(context); + } + CelExpr member = visit(context.member()); + if (context.id == null) { + return exprFactory.newExprBuilder(context).build(); + } + String id = context.id.getText(); + return receiverCallOrMacro(context, id, member); + } + + @Override + public CelExpr visitIndex(IndexContext context) { + checkNotNull(context); + if (context.member() == null || context.index == null) { + return exprFactory.ensureErrorsExist(context); + } + CelExpr member = visit(context.member()); + CelExpr.Builder exprBuilder = exprFactory.newExprBuilder(context.op); + CelExpr index = visit(context.index); + Operator indexOperator = Operator.INDEX; + + if (context.opt != null && context.opt.getText().equals("?")) { + if (!options.enableOptionalSyntax()) { + return exprFactory.reportError(context.op, "unsupported syntax '[?'"); + } + indexOperator = Operator.OPTIONAL_INDEX; + } + + return exprBuilder + .setCall( + CelExpr.CelCall.newBuilder() + .setFunction(indexOperator.getFunction()) + .addArgs(member) + .addArgs(index) + .build()) + .build(); + } + + @Override + public CelExpr visitCreateMessage(CreateMessageContext context) { + checkNotNull(context); + StringBuilder msgNameBuilder = new StringBuilder(); + for (Token token : context.ids) { + if (msgNameBuilder.length() > 0) { + msgNameBuilder.append("."); + } + msgNameBuilder.append(token.getText()); + } + + if (context.leadingDot != null) { + msgNameBuilder.insert(0, "."); + } + + String messageName = msgNameBuilder.toString(); + if (messageName.isEmpty()) { + return exprFactory.ensureErrorsExist(context); + } + + CelExpr.Builder exprBuilder = exprFactory.newExprBuilder(context.op); + CelExpr.CelStruct.Builder structExpr = visitStructFields(context.entries); + return exprBuilder.setStruct(structExpr.setMessageName(messageName).build()).build(); + } + + @Override + public CelExpr visitIdent(IdentContext context) { + checkNotNull(context); + if (context.id == null) { + return exprFactory.newExprBuilder(context).build(); + } + String id = context.id.getText(); + if (options.enableReservedIds() && RESERVED_IDS.contains(id)) { + return exprFactory.reportError(context, "reserved identifier: %s", id); + } + if (context.leadingDot != null) { + id = "." + id; + } + + return exprFactory + .newExprBuilder(context.id) + .setIdent(CelExpr.CelIdent.newBuilder().setName(id).build()) + .build(); + } + + @Override + public CelExpr visitGlobalCall(GlobalCallContext context) { + checkNotNull(context); + if (context.id == null) { + return exprFactory.newExprBuilder(context).build(); + } + String id = context.id.getText(); + if (options.enableReservedIds() && RESERVED_IDS.contains(id)) { + return exprFactory.reportError(context, "reserved identifier: %s", id); + } + if (context.leadingDot != null) { + id = "." + id; + } + + return globalCallOrMacro(context, id); + } + + @Override + public CelExpr visitNested(NestedContext context) { + checkNotNull(context); + if (context.e == null) { + return exprFactory.ensureErrorsExist(context); + } + return visit(context.e); + } + + @Override + public CelExpr visitCreateList(CreateListContext context) { + checkNotNull(context); + CelExpr.Builder exprBuilder = exprFactory.newExprBuilder(context.op); + CelExpr.CelList createListExpr = visitListInitElements(context.listInit()); + + return exprBuilder.setList(createListExpr).build(); + } + + private CelExpr.CelList visitListInitElements(ListInitContext context) { + CelExpr.CelList.Builder listExpr = CelExpr.CelList.newBuilder(); + if (context == null) { + return listExpr.build(); + } + + for (int index = 0; index < context.elems.size(); index++) { + OptExprContext elem = context.elems.get(index); + listExpr.addElements(visit(elem.e)); + + if (elem.opt != null) { + if (!options.enableOptionalSyntax()) { + exprFactory.reportError(elem.opt, "unsupported syntax '?'"); + continue; + } + listExpr.addOptionalIndices(index); + } + } + + return listExpr.build(); + } + + @Override + public CelExpr visitCreateMap(CreateMapContext context) { + checkNotNull(context); + CelExpr.Builder exprBuilder = exprFactory.newExprBuilder(context.op); + CelExpr.CelMap.Builder createMapExpr = visitMapEntries(context.entries); + return exprBuilder.setMap(createMapExpr.build()).build(); + } + + private CelExpr buildMacroCallArgs(CelExpr expr) { + CelExpr.Builder resultExpr = CelExpr.newBuilder().setId(expr.id()); + if (sourceInfo.containsMacroCalls(expr.id())) { + return resultExpr.build(); + } + // Call expression could have args or sub-args that are also macros found in macro calls + if (expr.exprKind().getKind() == CelExpr.ExprKind.Kind.CALL) { + CelExpr.CelCall.Builder callExpr = + CelExpr.CelCall.newBuilder().setFunction(expr.call().function()); + // Iterate the AST from `expr` recursively looking for macros. Because we are at most + // starting from the top level macro, this recursion is bounded by the size of the AST. This + // means that the depth check on the AST during parsing will catch recursion overflows + // before we get to here. + expr.call().args().forEach(arg -> callExpr.addArgs(buildMacroCallArgs(arg))); + expr.call().target().ifPresent(target -> callExpr.setTarget(buildMacroCallArgs(target))); + return resultExpr.setCall(callExpr.build()).build(); + } + return expr; + } + + /** + * Returns the expanded AST after visiting a macro. Optional.empty is returned instead if the + * implementation decides that an expansion should not be performed, in which case we should just + * default to call. + */ + private Optional visitMacro( + CelExpr.Builder expr, + String id, + ImmutableList args, + Optional target, + CelMacro macro) { + if (exprFactory.isNodeLimitExceeded()) { + return Optional.of( + exprFactory.reportError( + exprFactory.getPosition(expr.id()), + "could not expand macro: expression node limit exceeded")); + } + + Optional expandedMacro = + expandMacro( + exprFactory.getPosition(expr.id()), + macro, + target.orElse(CelExpr.newBuilder().build()), + args); + if (!expandedMacro.isPresent()) { + return Optional.empty(); + } + CelExpr.CelCall.Builder callExpr = CelExpr.CelCall.newBuilder().setFunction(id); + if (target.isPresent()) { + if (sourceInfo.containsMacroCalls(target.get().id())) { + callExpr.setTarget(CelExpr.newBuilder().setId(target.get().id()).build()); + } else { + callExpr.setTarget(target.get()); + } + } + for (CelExpr arg : args) { + callExpr.addArgs(buildMacroCallArgs(arg)); + } + + if (options.populateMacroCalls()) { + sourceInfo.addMacroCalls( + expandedMacro.get().id(), + // Note: A macro id MUST NOT be assigned to the call expr placed into the macro calls map. + // This can cause an infinite loop in some of the call chains that try to figure out + // whether the current expression is expanded to a macro. + CelExpr.newBuilder().setCall(callExpr.build()).build()); + } + + sourceInfo.removePositions(expr.id()); + return expandedMacro; + } + + private String normalizeEscapedIdent(EscapeIdentContext context) { + String identifier = context.getText(); + if (context instanceof SimpleIdentifierContext) { + return identifier; + } else if (context instanceof EscapedIdentifierContext) { + if (!options.enableQuotedIdentifierSyntax()) { + exprFactory.reportError(context, "unsupported syntax '`'"); + return identifier; + } + return identifier.substring(1, identifier.length() - 1); + } + + // This is normally unreachable, but might happen if the parser is in an error state or if the + // grammar is updated and not handled here. + exprFactory.reportError(context, "unsupported identifier"); + return identifier; + } + + private CelExpr.CelStruct.Builder visitStructFields(FieldInitializerListContext context) { + if (context == null + || context.cols == null + || context.fields == null + || context.values == null) { + return CelExpr.CelStruct.newBuilder(); + } + int entryCount = min(context.cols.size(), context.fields.size(), context.values.size()); + CelExpr.CelStruct.Builder structExpr = CelExpr.CelStruct.newBuilder(); + for (int index = 0; index < entryCount; index++) { + OptFieldContext fieldContext = context.fields.get(index); + boolean isOptionalEntry = false; + if (fieldContext.opt != null) { + if (!options.enableOptionalSyntax()) { + exprFactory.reportError(fieldContext.opt, "unsupported syntax '?'"); + } else { + isOptionalEntry = true; + } + } + + // The field may be empty due to a prior error. + if (fieldContext.escapeIdent() == null) { + return CelExpr.CelStruct.newBuilder(); + } + String fieldName = normalizeEscapedIdent(fieldContext.escapeIdent()); + + CelExpr.CelStruct.Entry.Builder exprBuilder = + CelExpr.CelStruct.Entry.newBuilder() + .setId(exprFactory.newExprId(exprFactory.getPosition(context.cols.get(index)))); + structExpr.addEntries( + exprBuilder + .setFieldKey(fieldName) + .setValue(visit(context.values.get(index))) + .setOptionalEntry(isOptionalEntry) + .build()); + } + return structExpr; + } + + private CelExpr.CelMap.Builder visitMapEntries(MapInitializerListContext context) { + if (context == null || context.cols == null || context.keys == null || context.values == null) { + return CelExpr.CelMap.newBuilder(); + } + int entryCount = min(context.cols.size(), context.keys.size(), context.values.size()); + CelExpr.CelMap.Builder mapExpr = CelExpr.CelMap.newBuilder(); + for (int index = 0; index < entryCount; index++) { + OptExprContext keyContext = context.keys.get(index); + boolean isOptionalEntry = false; + if (keyContext.opt != null) { + if (!options.enableOptionalSyntax()) { + exprFactory.reportError(keyContext.opt, "unsupported syntax '?'"); + } else { + isOptionalEntry = true; + } + } + CelExpr.CelMap.Entry.Builder exprBuilder = + CelExpr.CelMap.Entry.newBuilder() + .setId(exprFactory.newExprId(exprFactory.getPosition(context.cols.get(index)))); + mapExpr.addEntries( + exprBuilder + .setKey(visit(keyContext.e)) + .setValue(visit(context.values.get(index))) + .setOptionalEntry(isOptionalEntry) + .build()); + } + return mapExpr; + } + + @Override + protected CelExpr defaultResult() { + // visitTerminalNode and visitErrorNode call this method. + return exprFactory.ensureErrorsExist( + () -> "Abstract syntax tree in an unexpected state, this is likely a bug."); + } + + @Override + public CelExpr visitConstantLiteral(ConstantLiteralContext context) { + checkNotNull(context); + if (context.literal() == null) { + return exprFactory.ensureErrorsExist(context); + } + return visit(context.literal()); + } + + @Override + public CelExpr visitExprList(ExprListContext context) { + // We should never get here, as we do not directly visit expression lists. + return exprFactory.ensureErrorsExist(context); + } + + @Override + public CelExpr visitFieldInitializerList(FieldInitializerListContext context) { + // We should never get here, as we do not directly visit field initializer lists. + return exprFactory.ensureErrorsExist(context); + } + + @Override + public CelExpr visitMapInitializerList(MapInitializerListContext context) { + // We should never get here, as we do not directly visit map initializer lists. + return exprFactory.ensureErrorsExist(context); + } + + @Override + public CelExpr visitListInit(ListInitContext context) { + // We should never get here, as we do not directly visit list initializer. + return exprFactory.ensureErrorsExist(context); + } + + @Override + public CelExpr visitInt(IntContext context) { + checkNotNull(context); + CelConstant constExpr; + try { + constExpr = Constants.parseInt(context.getText()); + } catch (ParseException e) { + // Do not propagate e.getMessage(), which is JDK-specific. + return exprFactory.reportError( + context, "invalid int literal: %s", context.getText()); + } + + return exprFactory.newExprBuilder(context.tok).setConstant(constExpr).build(); + } + + @Override + public CelExpr visitUint(UintContext context) { + checkNotNull(context); + CelConstant constExpr; + try { + constExpr = Constants.parseUint(context.getText()); + } catch (ParseException e) { + // Do not propagate e.getMessage(), which is JDK-specific. + return exprFactory.reportError( + context, "invalid uint literal: %s", context.getText()); + } + return exprFactory.newExprBuilder(context).setConstant(constExpr).build(); + } + + @Override + public CelExpr visitDouble(DoubleContext context) { + checkNotNull(context); + CelConstant constExpr; + try { + constExpr = Constants.parseDouble(context.getText()); + } catch (ParseException e) { + // Do not propagate e.getMessage(), which is JDK-specific. + return exprFactory.reportError( + context, "invalid double literal: %s", context.getText()); + } + return exprFactory.newExprBuilder(context.tok).setConstant(constExpr).build(); + } + + @Override + public CelExpr visitString(StringContext context) { + checkNotNull(context); + CelConstant constExpr; + try { + constExpr = Constants.parseString(context.getText()); + } catch (ParseException e) { + return exprFactory.reportError(context, e.getMessage()); + } + return exprFactory.newExprBuilder(context).setConstant(constExpr).build(); + } + + @Override + public CelExpr visitBytes(BytesContext context) { + checkNotNull(context); + CelConstant constExpr; + try { + constExpr = Constants.parseBytes(context.getText()); + } catch (ParseException e) { + return exprFactory.reportError(context, e.getMessage()); + } + return exprFactory.newExprBuilder(context).setConstant(constExpr).build(); + } + + @Override + public CelExpr visitBoolTrue(BoolTrueContext context) { + checkNotNull(context); + return exprFactory.newExprBuilder(context).setConstant(Constants.TRUE).build(); + } + + @Override + public CelExpr visitBoolFalse(BoolFalseContext context) { + checkNotNull(context); + return exprFactory.newExprBuilder(context).setConstant(Constants.FALSE).build(); + } + + @Override + public CelExpr visitNull(NullContext context) { + checkNotNull(context); + return exprFactory.newExprBuilder(context).setConstant(Constants.NULL).build(); + } + + private Optional expandMacro( + int position, CelMacro macro, CelExpr target, ImmutableList arguments) { + exprFactory.pushPosition(position); + try { + return macro.getExpander().expandMacro(exprFactory, target, arguments); + } finally { + exprFactory.popPosition(); + } + } + + private CelExpr receiverCallOrMacro(MemberCallContext context, String id, CelExpr member) { + return macroOrCall(context.args, context.open, id, Optional.of(member), true); + } + + private CelExpr globalCallOrMacro(GlobalCallContext context, String id) { + return macroOrCall(context.args, context.op, id, Optional.empty(), false); + } + + private ImmutableList visitExprListContext(ExprListContext args) { + int argCount = args != null && args.e != null ? args.e.size() : 0; + if (argCount == 0) { + return ImmutableList.of(); + } + + ImmutableList.Builder argumentsBuilder = + ImmutableList.builderWithExpectedSize(argCount); + for (ExprContext argExprCtx : args.e) { + argumentsBuilder.add(visit(argExprCtx)); + } + return argumentsBuilder.build(); + } + + private CelExpr macroOrCall( + ExprListContext args, + Token open, + String id, + Optional member, + boolean isReceiverStyle) { + int argCount = args != null && args.e != null ? args.e.size() : 0; + Optional macro = lookupMacro(id, argCount, isReceiverStyle); + CelExpr.Builder exprBuilder = exprFactory.newExprBuilder(open); + + ImmutableList arguments = visitExprListContext(args); + Optional errorArg = arguments.stream().filter(ERROR::equals).findAny(); + if (errorArg.isPresent()) { + sourceInfo.removePositions(exprBuilder.id()); + // Any arguments passed in to the macro may fail parsing. + // Stop the macro expansion in this case as the result of the macro will be a parse failure. + return ERROR; + } + + if (macro.isPresent()) { + Optional expandedMacro = visitMacro(exprBuilder, id, arguments, member, macro.get()); + if (expandedMacro.isPresent()) { + return expandedMacro.get(); + } + } + + CelExpr.CelCall.Builder callExpr = + CelExpr.CelCall.newBuilder().setFunction(id).addArgs(arguments); + member.ifPresent(callExpr::setTarget); + + return exprBuilder.setCall(callExpr.build()).build(); + } + + private Optional lookupMacro(String id, int argCount, boolean receiverStlye) { + String key = CelMacro.formatKey(id, argCount, receiverStlye); + CelMacro macro = macros.get(key); + if (macro != null) { + return Optional.of(macro); + } + key = CelMacro.formatVarArgKey(id, receiverStlye); + return Optional.ofNullable(macros.get(key)); + } + + /** + * Checks whether a given parse tree node is left recursive for the purposes of counting recursion + * depths. + */ + private boolean isLeftRecursiveForCountingDepths(ParseTree node) { + // There are certainly more left recursive nodes than what's shown below. + // We try to catch the specific node types that explodes the number of recursive visit calls and + // of those that cannot be caught by PerRuleRecursionListener. + return node instanceof ExprContext + || node instanceof CalcContext + || node instanceof RelationContext + || node instanceof SelectContext + || node instanceof MemberCallContext + || node instanceof IndexContext; + } + + private void checkAndIncrementRecursionDepth() { + recursionDepth++; + if (recursionDepth > options.maxParseRecursionDepth()) { + String errorMessage = + String.format( + "Expression recursion limit exceeded. limit: %d", options.maxParseRecursionDepth()); + exprFactory.reportError(CelIssue.formatError(CelSourceLocation.of(1, 0), errorMessage)); + throw new ParseCancellationException(errorMessage); + } + } + + private void decrementRecursionDepth() { + recursionDepth--; + } + + /** + * unnest traverses down the left-hand side of the parse graph until it encounters the first + * compound parse node or the first leaf in the parse graph. + */ + private ParseTree unnest(ParseTree tree) { + while (tree != null) { + if (tree instanceof ExprContext) { + // conditionalOr op='?' conditionalOr : expr + ExprContext context = (ExprContext) tree; + if (context.op != null) { + return tree; + } + // conditionalOr + tree = context.e; + } else if (tree instanceof ConditionalOrContext) { + // conditionalAnd (ops=|| conditionalAnd)* + ConditionalOrContext context = (ConditionalOrContext) tree; + if (context.ops != null && !context.ops.isEmpty()) { + return tree; + } + // conditionalAnd + tree = context.e; + } else if (tree instanceof ConditionalAndContext) { + // relation (ops=&& relation)* + ConditionalAndContext context = (ConditionalAndContext) tree; + if (context.ops != null && !context.ops.isEmpty()) { + return tree; + } + + // relation + tree = context.e; + } else if (tree instanceof RelationContext) { + // relation op relation + RelationContext context = (RelationContext) tree; + if (context.op != null) { + return tree; + } + // calc + tree = context.calc(); + } else if (tree instanceof CalcContext) { + // calc op calc + CalcContext context = (CalcContext) tree; + if (context.op != null) { + return tree; + } + + // unary + tree = context.unary(); + } else if (tree instanceof MemberExprContext) { + // member expands to one of: primary, select, index, or create message + tree = ((MemberExprContext) tree).member(); + } else if (tree instanceof PrimaryExprContext) { + // primary expands to one of identifier, nested, create list, create struct, literal + tree = ((PrimaryExprContext) tree).primary(); + } else if (tree instanceof NestedContext) { + // contains a nested 'expr' + tree = ((NestedContext) tree).e; + } else if (tree instanceof ConstantLiteralContext) { + // expands to a primitive literal + tree = ((ConstantLiteralContext) tree).literal(); + } else { + return tree; + } + } + + return tree; + } + + /** Implementation of {@link CelMacroExprFactory}. */ + private static final class ExprFactory extends CelMacroExprFactory { + + private final org.antlr.v4.runtime.Parser recognizer; + private final CelSource.Builder sourceInfo; + private final ArrayList issues; + private final ArrayDeque positions; + private final String accumulatorVarName; + private final int maxExpressionNodeCount; + private boolean nodeLimitExceeded; + + private ExprFactory( + org.antlr.v4.runtime.Parser recognizer, + CelSource.Builder sourceInfo, + String accumulatorVarName, + int maxExpressionNodeCount) { + this.recognizer = recognizer; + this.sourceInfo = sourceInfo; + this.issues = new ArrayList<>(); + this.positions = new ArrayDeque<>(1); // Currently this usually contains at most 1 position. + this.accumulatorVarName = accumulatorVarName; + this.maxExpressionNodeCount = maxExpressionNodeCount; + } + + // Implementation of CelExprFactory. + + @Override + protected CelSourceLocation getSourceLocation(long exprId) { + checkArgument(exprId > 0L); + return getLocation(getPosition(exprId)); + } + + @CanIgnoreReturnValue + @Override + public CelExpr reportError(CelIssue error) { + checkNotNull(error); + issues.add(error); + if (!CelSourceLocation.NONE.equals(error.getSourceLocation())) { + Optional offset = sourceInfo.getLocationOffset(error.getSourceLocation()); + checkState(offset.isPresent()); // A valid location should always return a valid offset. + return newExpr(offset.get()); + } + return ERROR; + } + + @FormatMethod + @CanIgnoreReturnValue + private CelExpr reportError( + ParserRuleContext context, @FormatString String format, Object... args) { + return reportError(context, String.format(format, args)); + } + + @CanIgnoreReturnValue + private CelExpr reportError(ParserRuleContext context, String message) { + return reportError(CelIssue.formatError(getLocation(context), message)); + } + + @CanIgnoreReturnValue + private CelExpr reportError(Token token, String message) { + return reportError(CelIssue.formatError(getLocation(token), message)); + } + + @CanIgnoreReturnValue + private CelExpr reportError(int position, String message) { + return reportError(CelIssue.formatError(getLocation(position), message)); + } + + // Implementation of CelExprFactory. + + @Override + public String getAccumulatorVarName() { + return accumulatorVarName; + } + + @Override + protected CelSourceLocation currentSourceLocationForMacro() { + checkState(!positions.isEmpty()); // Should only be called while expanding macros. + return getLocation(peekPosition()); + } + + // Internal methods used by the parser but not part of the public API. + + private boolean isNodeLimitExceeded() { + return nodeLimitExceeded; + } + + private void pushPosition(int position) { + positions.addLast(position); + } + + private void popPosition() { + checkState(!positions.isEmpty()); + positions.removeLast(); + } + + private int peekPosition() { + checkState(!positions.isEmpty()); + return positions.peekLast(); + } + + private long nextExprId(int position) { + long exprId = super.nextExprId(); + if (exprId > maxExpressionNodeCount && !nodeLimitExceeded) { + nodeLimitExceeded = true; + reportError( + position, String.format("expression node limit (%d) exceeded", maxExpressionNodeCount)); + } + if (position != -1) { + sourceInfo.addPositions(exprId, position); + } + return exprId; + } + + @Override + public long nextExprId() { + checkState(!positions.isEmpty()); // Should only be called while expanding macros. + // Do not call this method directly from within the parser, use nextExprId(int). + return nextExprId(peekPosition()); + } + + @Override + public long copyExprId(long id) { + return nextExprId(getPosition(id)); + } + + private List getIssuesList() { + return issues; + } + + private int getPosition(long exprId) { + return Optional.ofNullable(sourceInfo.getPositionsMap().get(exprId)).orElse(-1); + } + + private int getPosition(Token token) { + return sourceInfo + .getLocationOffset(token.getLine(), token.getCharPositionInLine()) + .orElse(-1); + } + + private int getPosition(ParserRuleContext context) { + return getPosition(context.getStart()); + } + + private CelSourceLocation getLocation(int position) { + return sourceInfo.getOffsetLocation(position).orElse(CelSourceLocation.NONE); + } + + private CelSourceLocation getLocation(Token token) { + return CelSourceLocation.of(token.getLine(), token.getCharPositionInLine()); + } + + private CelSourceLocation getLocation(ParserRuleContext context) { + return getLocation(context.getStart()); + } + + @CanIgnoreReturnValue + private long newExprId(int position) { + return nextExprId(position); + } + + private CelExpr.Builder newExprBuilder(int position) { + return CelExpr.newBuilder().setId(newExprId(position)); + } + + private CelExpr.Builder newExprBuilder(Token token) { + return newExprBuilder(getPosition(token)); + } + + private CelExpr.Builder newExprBuilder(ParserRuleContext context) { + return newExprBuilder(getPosition(context)); + } + + private CelExpr newExpr(int position) { + return newExprBuilder(position).build(); + } + + private CelExpr ensureErrorsExist(Supplier message) { + // Because we do not treat syntax errors as fatal during parsing, the parse tree is often in + // an abnormal state. We call this function to ensure we have recorded syntax errors. If we + // have we return the special error node otherwise we bail and mention that this is likely a + // bug. + if (issues.isEmpty()) { + // If we reach here, this is an unexpected error and highly likely to be a bug. At least one + // syntax error or another error should have occurred because the parse tree is in an + // unexpected state. + throw new ParseCancellationException( + String.format( + "Abstract syntax tree in an unexpected state, this is likely a bug: %s", + message.get())); + } + return ERROR; + } + + private CelExpr ensureErrorsExist(ParserRuleContext context) { + return ensureErrorsExist(() -> context.toInfoString(recognizer)); + } + } + + /** + * Listener that enforces a maximum recursion depth, to avoid accidental stack overflow issues + * when parsing large expressions. + */ + private static final class PerRuleRecursionListener implements ParseTreeListener { + + private final ExprFactory exprFactory; + private final int maxRecursionDepth; + private final Map ruleTypeDepth; + + private PerRuleRecursionListener(ExprFactory exprFactory, int maxRecursionDepth) { + this.exprFactory = exprFactory; + this.maxRecursionDepth = maxRecursionDepth; + this.ruleTypeDepth = new HashMap<>(); + } + + @Override + public void enterEveryRule(ParserRuleContext context) { + int ruleDepth = ruleTypeDepth.getOrDefault(context.getRuleIndex(), 0) + 1; + ruleTypeDepth.put(context.getRuleIndex(), ruleDepth); + if (ruleDepth > maxRecursionDepth) { + String errorMessage = + String.format("Expression recursion limit exceeded. limit: %d", maxRecursionDepth); + exprFactory.reportError(CelIssue.formatError(CelSourceLocation.of(1, 0), errorMessage)); + throw new ParseCancellationException(errorMessage); + } + } + + @Override + public void exitEveryRule(ParserRuleContext context) { + int ruleDepth = ruleTypeDepth.get(context.getRuleIndex()) - 1; + ruleTypeDepth.put(context.getRuleIndex(), ruleDepth); + } + + @Override + public void visitErrorNode(ErrorNode node) {} + + @Override + public void visitTerminal(TerminalNode node) {} + } + + /** Error strategy that limits the number of recovery attempts. */ + private static final class RecoveryLimitErrorStrategy extends DefaultErrorStrategy { + + private final int recoveryLimit; + private int recoveryAttempts; + + private RecoveryLimitErrorStrategy(int recoveryLimit) { + this.recoveryLimit = recoveryLimit; + recoveryAttempts = 0; + } + + @Override + public void recover(org.antlr.v4.runtime.Parser recognizer, RecognitionException e) { + checkRecoveryLimit(recognizer); + super.recover(recognizer, e); + } + + @Override + public Token recoverInline(org.antlr.v4.runtime.Parser recognizer) { + checkRecoveryLimit(recognizer); + return super.recoverInline(recognizer); + } + + private void checkRecoveryLimit(org.antlr.v4.runtime.Parser recognizer) { + if (recoveryAttempts++ >= recoveryLimit) { + String tooManyErrors = String.format("More than %d parse errors.", recoveryLimit); + recognizer.notifyErrorListeners(tooManyErrors); + throw new ParseCancellationException(tooManyErrors); + } + } + } + + private static final class ErrorListener implements ANTLRErrorListener { + + private final ExprFactory exprFactory; + + private ErrorListener(ExprFactory exprFactory) { + this.exprFactory = exprFactory; + } + + @Override + public void reportAmbiguity( + org.antlr.v4.runtime.Parser recognizer, + DFA dfa, + int startIndex, + int stopIndex, + boolean exact, + BitSet ambigAlts, + ATNConfigSet configs) { + // Intentional. + } + + @Override + public void reportAttemptingFullContext( + org.antlr.v4.runtime.Parser recognizer, + DFA dfa, + int startIndex, + int stopIndex, + BitSet ambigAlts, + ATNConfigSet configs) { + // Intentional. + } + + @Override + public void reportContextSensitivity( + org.antlr.v4.runtime.Parser recognizer, + DFA dfa, + int startIndex, + int stopIndex, + int prediction, + ATNConfigSet configs) { + // Intentional. + } + + @Override + public void syntaxError( + Recognizer recognizer, + Object offendingSymbol, + int line, + int charPositionInLine, + String msg, + RecognitionException e) { + msg = msg.replace("%", "%%"); + exprFactory.reportError( + CelIssue.formatError(CelSourceLocation.of(line, charPositionInLine), msg)); + } + } +} diff --git a/parser/src/main/java/dev/cel/parser/BUILD.bazel b/parser/src/main/java/dev/cel/parser/BUILD.bazel index 905bf298f..848209380 100644 --- a/parser/src/main/java/dev/cel/parser/BUILD.bazel +++ b/parser/src/main/java/dev/cel/parser/BUILD.bazel @@ -11,10 +11,15 @@ package( # keep sorted PARSER_SOURCES = [ "CelParserImpl.java", - "ExpressionBalancer.java", "Parser.java", ] +# keep sorted +ANTLR_PARSER_SOURCES = [ + "AntlrParser.java", + "ExpressionBalancer.java", +] + # keep sorted PRATT_PARSER_SOURCES = [ "Lexer.java", @@ -61,19 +66,36 @@ java_library( tags = [ ], deps = [ + ":antlr_parser", ":macro", ":parser_builder", + ":pratt_parser", + "//common:cel_source", + "//common:compiler_common", + "//common:options", + "//common/annotations", + "//common/internal:env_visitor", + "@maven//:com_google_errorprone_error_prone_annotations", + "@maven//:com_google_guava_guava", + ], +) + +java_library( + name = "antlr_parser", + srcs = ANTLR_PARSER_SOURCES, + tags = [ + ], + deps = [ + ":macro", "//common:cel_ast", "//common:cel_source", "//common:compiler_common", "//common:operator", "//common:options", "//common:source_location", - "//common/annotations", "//common/ast", "//common/internal", "//common/internal:code_point_stream", - "//common/internal:env_visitor", "//parser:cel_g4_visitors", "@maven//:com_google_errorprone_error_prone_annotations", "@maven//:com_google_guava_guava", diff --git a/parser/src/main/java/dev/cel/parser/CelParserImpl.java b/parser/src/main/java/dev/cel/parser/CelParserImpl.java index 615b073ef..8ee9a3457 100644 --- a/parser/src/main/java/dev/cel/parser/CelParserImpl.java +++ b/parser/src/main/java/dev/cel/parser/CelParserImpl.java @@ -100,6 +100,10 @@ Optional findMacro(String key) { return Optional.ofNullable(macros.get(key)); } + ImmutableMap getMacros() { + return macros; + } + /** Return the options the {@link CelParser} was originally created with. */ public CelOptions getOptions() { return options; diff --git a/parser/src/main/java/dev/cel/parser/Parser.java b/parser/src/main/java/dev/cel/parser/Parser.java index 0e6849056..9b2a5aad8 100644 --- a/parser/src/main/java/dev/cel/parser/Parser.java +++ b/parser/src/main/java/dev/cel/parser/Parser.java @@ -14,1392 +14,24 @@ package dev.cel.parser; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.primitives.Ints.min; - -import cel.parser.internal.CELBaseVisitor; -import cel.parser.internal.CELLexer; -import cel.parser.internal.CELParser; -import cel.parser.internal.CELParser.BoolFalseContext; -import cel.parser.internal.CELParser.BoolTrueContext; -import cel.parser.internal.CELParser.BytesContext; -import cel.parser.internal.CELParser.CalcContext; -import cel.parser.internal.CELParser.ConditionalAndContext; -import cel.parser.internal.CELParser.ConditionalOrContext; -import cel.parser.internal.CELParser.ConstantLiteralContext; -import cel.parser.internal.CELParser.CreateListContext; -import cel.parser.internal.CELParser.CreateMapContext; -import cel.parser.internal.CELParser.CreateMessageContext; -import cel.parser.internal.CELParser.DoubleContext; -import cel.parser.internal.CELParser.EscapeIdentContext; -import cel.parser.internal.CELParser.EscapedIdentifierContext; -import cel.parser.internal.CELParser.ExprContext; -import cel.parser.internal.CELParser.ExprListContext; -import cel.parser.internal.CELParser.FieldInitializerListContext; -import cel.parser.internal.CELParser.GlobalCallContext; -import cel.parser.internal.CELParser.IdentContext; -import cel.parser.internal.CELParser.IndexContext; -import cel.parser.internal.CELParser.IntContext; -import cel.parser.internal.CELParser.ListInitContext; -import cel.parser.internal.CELParser.LogicalNotContext; -import cel.parser.internal.CELParser.MapInitializerListContext; -import cel.parser.internal.CELParser.MemberCallContext; -import cel.parser.internal.CELParser.MemberExprContext; -import cel.parser.internal.CELParser.NegateContext; -import cel.parser.internal.CELParser.NestedContext; -import cel.parser.internal.CELParser.NullContext; -import cel.parser.internal.CELParser.OptExprContext; -import cel.parser.internal.CELParser.OptFieldContext; -import cel.parser.internal.CELParser.PrimaryExprContext; -import cel.parser.internal.CELParser.RelationContext; -import cel.parser.internal.CELParser.SelectContext; -import cel.parser.internal.CELParser.SimpleIdentifierContext; -import cel.parser.internal.CELParser.StartContext; -import cel.parser.internal.CELParser.StringContext; -import cel.parser.internal.CELParser.UintContext; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.errorprone.annotations.CanIgnoreReturnValue; -import com.google.errorprone.annotations.FormatMethod; -import com.google.errorprone.annotations.FormatString; -import dev.cel.common.CelAbstractSyntaxTree; -import dev.cel.common.CelIssue; import dev.cel.common.CelOptions; import dev.cel.common.CelSource; -import dev.cel.common.CelSourceLocation; import dev.cel.common.CelValidationResult; -import dev.cel.common.Operator; -import dev.cel.common.ast.CelConstant; -import dev.cel.common.ast.CelExpr; -import dev.cel.common.internal.CodePointStream; -import dev.cel.common.internal.Constants; -import java.text.ParseException; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.BitSet; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Supplier; -import org.antlr.v4.runtime.ANTLRErrorListener; -import org.antlr.v4.runtime.CommonTokenStream; -import org.antlr.v4.runtime.DefaultErrorStrategy; -import org.antlr.v4.runtime.ParserRuleContext; -import org.antlr.v4.runtime.RecognitionException; -import org.antlr.v4.runtime.Recognizer; -import org.antlr.v4.runtime.Token; -import org.antlr.v4.runtime.atn.ATNConfigSet; -import org.antlr.v4.runtime.dfa.DFA; -import org.antlr.v4.runtime.misc.ParseCancellationException; -import org.antlr.v4.runtime.tree.ErrorNode; -import org.antlr.v4.runtime.tree.ParseTree; -import org.antlr.v4.runtime.tree.ParseTreeListener; -import org.antlr.v4.runtime.tree.TerminalNode; /** - * Parses a CEL expression and returns an abstraction syntax tree in the form of - * google.api.expr.ParsedExpr. Currently this uses ANTLRv4 for lexing and parsing. + * Parses a CEL expression and returns an abstract syntax tree. + * + *

Dispatches to {@link AntlrParser} or {@link PrattParser} based on {@link + * CelOptions#enablePrattParser()}. */ -final class Parser extends CELBaseVisitor { - - private static final CelExpr ERROR = CelExpr.newBuilder().setConstant(Constants.ERROR).build(); - private static final ImmutableSet RESERVED_IDS = - ImmutableSet.of( - "as", - "break", - "const", - "continue", - "else", - "false", - "for", - "function", - "if", - "import", - "in", - "let", - "loop", - "package", - "namespace", - "null", - "return", - "true", - "var", - "void", - "while"); - private static final String ACCUMULATOR_NAME = "__result__"; - private static final String HIDDEN_ACCUMULATOR_NAME = "@result"; +final class Parser { static CelValidationResult parse(CelParserImpl parser, CelSource source, CelOptions options) { - if (source.getContent().size() > options.maxExpressionCodePointSize()) { - return new CelValidationResult( - source, - ImmutableList.of( - CelIssue.formatError( - CelSourceLocation.NONE, - String.format( - "expression code point size exceeds limit: size: %d, limit %d", - source.getContent().size(), options.maxExpressionCodePointSize())))); - } - CELLexer antlrLexer = - new CELLexer(new CodePointStream(source.getDescription(), source.getContent())); - CELParser antlrParser = new CELParser(new CommonTokenStream(antlrLexer)); - CelSource.Builder sourceInfo = source.toBuilder(); - sourceInfo.setDescription(source.getDescription()); - ExprFactory exprFactory = - new ExprFactory( - antlrParser, - sourceInfo, - options.enableHiddenAccumulatorVar() ? HIDDEN_ACCUMULATOR_NAME : ACCUMULATOR_NAME, - options.maxParseExpressionNodeCount()); - Parser parserImpl = new Parser(parser, options, sourceInfo, exprFactory); - ErrorListener errorListener = new ErrorListener(exprFactory); - antlrLexer.removeErrorListeners(); - antlrParser.removeErrorListeners(); - antlrLexer.addErrorListener(errorListener); - antlrParser.addErrorListener(errorListener); - antlrParser.addParseListener( - new PerRuleRecursionListener(exprFactory, options.maxParseRecursionDepth())); - antlrParser.setErrorHandler( - new RecoveryLimitErrorStrategy(options.maxParseErrorRecoveryLimit())); - CelExpr expr; - try { - StartContext context = checkNotNull(antlrParser.start()); - expr = checkNotNull(parserImpl.visit(context)); - } catch (ParseCancellationException parseFailure) { - return new CelValidationResult( - sourceInfo.build(), parseFailure, ImmutableList.copyOf(exprFactory.getIssuesList())); - } - return new CelValidationResult( - CelAbstractSyntaxTree.newParsedAst(expr, sourceInfo.build()), - ImmutableList.copyOf(exprFactory.getIssuesList())); - } - - private final CelParserImpl parser; - private final CelOptions options; - private final CelSource.Builder sourceInfo; - private final ExprFactory exprFactory; - - private int recursionDepth; - - private Parser( - CelParserImpl parser, - CelOptions options, - CelSource.Builder sourceInfo, - ExprFactory exprFactory) { - this.parser = parser; - this.options = options; - this.sourceInfo = sourceInfo; - this.exprFactory = exprFactory; - } - - @Override - public CelExpr visit(ParseTree tree) { - ParseTree unnestedNode = unnest(tree); - boolean isLeftRecursiveNode = isLeftRecursiveForCountingDepths(unnestedNode); - if (isLeftRecursiveNode) { - checkAndIncrementRecursionDepth(); - CelExpr expr = super.visit(unnestedNode); - decrementRecursionDepth(); - return expr; - } - - return super.visit(unnestedNode); - } - - @Override - public CelExpr visitStart(StartContext context) { - checkNotNull(context); - if (context.e == null) { - return exprFactory.ensureErrorsExist(context); - } - return visit(context.e); - } - - @Override - public CelExpr visitExpr(ExprContext context) { - checkNotNull(context); - if (context.e == null) { - return exprFactory.ensureErrorsExist(context); - } - CelExpr condition = visit(context.e); - if (context.op != null) { - if (context.e1 == null || context.e2 == null) { - return exprFactory.ensureErrorsExist(context); - } - condition = - exprFactory - .newExprBuilder(context.op) - .setCall( - CelExpr.CelCall.newBuilder() - .setFunction(Operator.CONDITIONAL.getFunction()) - .addArgs(condition) - .addArgs(visit(context.e1)) - .addArgs(visit(context.e2)) - .build()) - .build(); - } - - return condition; - } - - @Override - public CelExpr visitConditionalOr(ConditionalOrContext context) { - checkNotNull(context); - if (context.e == null) { - return exprFactory.ensureErrorsExist(context); - } - CelExpr conditionalOr = visit(context.e); - if (context.ops == null || context.ops.isEmpty()) { - return conditionalOr; - } - ExpressionBalancer balancer = - new ExpressionBalancer(Operator.LOGICAL_OR.getFunction(), conditionalOr); - int index = 0; - for (Token token : context.ops) { - if (context.e1 == null || index >= context.e1.size()) { - return exprFactory.reportError(context, "unexpected character, wanted '||'"); - } - long operationId = exprFactory.newExprId(exprFactory.getPosition(token)); - CelExpr term = visit(context.e1.get(index)); - balancer.add(operationId, term); - index++; - } - return balancer.balance(); - } - - @Override - public CelExpr visitConditionalAnd(ConditionalAndContext context) { - checkNotNull(context); - if (context.e == null) { - return exprFactory.ensureErrorsExist(context); - } - CelExpr conditionalAnd = visit(context.e); - if (context.ops == null || context.ops.isEmpty()) { - return conditionalAnd; - } - ExpressionBalancer balancer = - new ExpressionBalancer(Operator.LOGICAL_AND.getFunction(), conditionalAnd); - int index = 0; - for (Token token : context.ops) { - if (context.e1 == null || index >= context.e1.size()) { - return exprFactory.reportError(context, "unexpected character, wanted '&&'"); - } - long operationId = exprFactory.newExprId(exprFactory.getPosition(token)); - CelExpr term = visit(context.e1.get(index)); - balancer.add(operationId, term); - index++; - } - return balancer.balance(); - } - - @Override - public CelExpr visitRelation(RelationContext context) { - checkNotNull(context); - if (context.calc() != null) { - return visit(context.calc()); - } - if (context.relation() == null || context.relation().isEmpty() || context.op == null) { - return exprFactory.ensureErrorsExist(context); - } - Optional operator = Operator.find(context.op.getText()); - if (!operator.isPresent()) { - return exprFactory.reportError(context, "operator not found"); - } - CelExpr left = visit(context.relation(0)); - CelExpr.Builder exprBuilder = exprFactory.newExprBuilder(context.op); - CelExpr right = visit(context.relation(1)); - return exprBuilder - .setCall( - CelExpr.CelCall.newBuilder() - .setFunction(operator.get().getFunction()) - .addArgs(left) - .addArgs(right) - .build()) - .build(); - } - - @Override - public CelExpr visitCalc(CalcContext context) { - checkNotNull(context); - if (context.unary() != null) { - return visit(context.unary()); - } - if (context.calc() == null || context.calc().isEmpty() || context.op == null) { - return exprFactory.ensureErrorsExist(context); - } - Optional operator = Operator.find(context.op.getText()); - if (!operator.isPresent()) { - return exprFactory.reportError(context, "operator not found"); - } - CelExpr left = visit(context.calc(0)); - CelExpr.Builder exprBuilder = exprFactory.newExprBuilder(context.op); - CelExpr right = visit(context.calc(1)); - return exprBuilder - .setCall( - CelExpr.CelCall.newBuilder() - .setFunction(operator.get().getFunction()) - .addArgs(left) - .addArgs(right) - .build()) - .build(); - } - - @Override - public CelExpr visitMemberExpr(MemberExprContext context) { - checkNotNull(context); - if (context.member() == null) { - return exprFactory.ensureErrorsExist(context); - } - return visit(context.member()); - } - - @Override - public CelExpr visitLogicalNot(LogicalNotContext context) { - checkNotNull(context); - if (context.member() == null) { - return exprFactory.ensureErrorsExist(context); - } - if (context.ops != null && options.retainRepeatedUnaryOperators()) { - CelExpr expr = visit(context.member()); - for (int index = context.ops.size(); index > 0; --index) { - expr = - exprFactory - .newExprBuilder(context.ops.get(index - 1)) - .setCall( - CelExpr.CelCall.newBuilder() - .setFunction(Operator.LOGICAL_NOT.getFunction()) - .addArgs(expr) - .build()) - .build(); - } - return expr; - } else if (context.ops == null || context.ops.size() % 2 == 0) { - return visit(context.member()); - } - CelExpr.Builder exprBuilder = exprFactory.newExprBuilder(context.ops.get(0)); - CelExpr member = visit(context.member()); - return exprBuilder - .setCall( - CelExpr.CelCall.newBuilder() - .setFunction(Operator.LOGICAL_NOT.getFunction()) - .addArgs(member) - .build()) - .build(); - } - - @Override - public CelExpr visitNegate(NegateContext context) { - checkNotNull(context); - if (context.member() == null) { - return exprFactory.ensureErrorsExist(context); - } - if (context.ops != null && options.retainRepeatedUnaryOperators()) { - CelExpr expr = visit(context.member()); - for (int index = context.ops.size(); index > 0; --index) { - expr = - exprFactory - .newExprBuilder(context.ops.get(index - 1)) - .setCall( - CelExpr.CelCall.newBuilder() - .setFunction(Operator.NEGATE.getFunction()) - .addArgs(expr) - .build()) - .build(); - } - return expr; - } else if (context.ops == null || context.ops.size() % 2 == 0) { - return visit(context.member()); - } - CelExpr.Builder exprBuilder = exprFactory.newExprBuilder(context.ops.get(0)); - CelExpr member = visit(context.member()); - return exprBuilder - .setCall( - CelExpr.CelCall.newBuilder() - .setFunction(Operator.NEGATE.getFunction()) - .addArgs(member) - .build()) - .build(); - } - - @Override - public CelExpr visitPrimaryExpr(PrimaryExprContext context) { - checkNotNull(context); - if (context.primary() == null) { - return exprFactory.ensureErrorsExist(context); - } - return visit(context.primary()); - } - - @Override - public CelExpr visitSelect(SelectContext context) { - checkNotNull(context); - if (context.member() == null) { - return exprFactory.ensureErrorsExist(context); - } - CelExpr member = visit(context.member()); - if (context.id == null) { - return exprFactory.newExprBuilder(context).build(); - } - String id = normalizeEscapedIdent(context.id); - - if (context.opt != null && context.opt.getText().equals("?")) { - if (!options.enableOptionalSyntax()) { - return exprFactory.reportError(context.op, "unsupported syntax '.?'"); - } - - CelExpr.Builder exprBuilder = exprFactory.newExprBuilder(exprFactory.getPosition(context.op)); - CelExpr.CelCall callExpr = - CelExpr.CelCall.newBuilder() - .setFunction(Operator.OPTIONAL_SELECT.getFunction()) - .addArgs( - Arrays.asList( - member, - exprFactory - .newExprBuilder(context) - .setConstant(CelConstant.ofValue(id)) - .build())) - .build(); - - return exprBuilder.setCall(callExpr).build(); - } - - return exprFactory - .newExprBuilder(context.op) - .setSelect(CelExpr.CelSelect.newBuilder().setOperand(member).setField(id).build()) - .build(); - } - - @Override - public CelExpr visitMemberCall(MemberCallContext context) { - checkNotNull(context); - if (context.member() == null) { - return exprFactory.ensureErrorsExist(context); - } - CelExpr member = visit(context.member()); - if (context.id == null) { - return exprFactory.newExprBuilder(context).build(); - } - String id = context.id.getText(); - return receiverCallOrMacro(context, id, member); - } - - @Override - public CelExpr visitIndex(IndexContext context) { - checkNotNull(context); - if (context.member() == null || context.index == null) { - return exprFactory.ensureErrorsExist(context); - } - CelExpr member = visit(context.member()); - CelExpr.Builder exprBuilder = exprFactory.newExprBuilder(context.op); - CelExpr index = visit(context.index); - Operator indexOperator = Operator.INDEX; - - if (context.opt != null && context.opt.getText().equals("?")) { - if (!options.enableOptionalSyntax()) { - return exprFactory.reportError(context.op, "unsupported syntax '[?'"); - } - indexOperator = Operator.OPTIONAL_INDEX; - } - - return exprBuilder - .setCall( - CelExpr.CelCall.newBuilder() - .setFunction(indexOperator.getFunction()) - .addArgs(member) - .addArgs(index) - .build()) - .build(); - } - - @Override - public CelExpr visitCreateMessage(CreateMessageContext context) { - checkNotNull(context); - StringBuilder msgNameBuilder = new StringBuilder(); - for (Token token : context.ids) { - if (msgNameBuilder.length() > 0) { - msgNameBuilder.append("."); - } - msgNameBuilder.append(token.getText()); - } - - if (context.leadingDot != null) { - msgNameBuilder.insert(0, "."); - } - - String messageName = msgNameBuilder.toString(); - if (messageName.isEmpty()) { - return exprFactory.ensureErrorsExist(context); - } - - CelExpr.Builder exprBuilder = exprFactory.newExprBuilder(context.op); - CelExpr.CelStruct.Builder structExpr = visitStructFields(context.entries); - return exprBuilder.setStruct(structExpr.setMessageName(messageName).build()).build(); - } - - @Override - public CelExpr visitIdent(IdentContext context) { - checkNotNull(context); - if (context.id == null) { - return exprFactory.newExprBuilder(context).build(); - } - String id = context.id.getText(); - if (options.enableReservedIds() && RESERVED_IDS.contains(id)) { - return exprFactory.reportError(context, "reserved identifier: %s", id); - } - if (context.leadingDot != null) { - id = "." + id; - } - - return exprFactory - .newExprBuilder(context.id) - .setIdent(CelExpr.CelIdent.newBuilder().setName(id).build()) - .build(); - } - - @Override - public CelExpr visitGlobalCall(GlobalCallContext context) { - checkNotNull(context); - if (context.id == null) { - return exprFactory.newExprBuilder(context).build(); - } - String id = context.id.getText(); - if (options.enableReservedIds() && RESERVED_IDS.contains(id)) { - return exprFactory.reportError(context, "reserved identifier: %s", id); - } - if (context.leadingDot != null) { - id = "." + id; - } - - return globalCallOrMacro(context, id); - } - - @Override - public CelExpr visitNested(NestedContext context) { - checkNotNull(context); - if (context.e == null) { - return exprFactory.ensureErrorsExist(context); - } - return visit(context.e); - } - - @Override - public CelExpr visitCreateList(CreateListContext context) { - checkNotNull(context); - CelExpr.Builder exprBuilder = exprFactory.newExprBuilder(context.op); - CelExpr.CelList createListExpr = visitListInitElements(context.listInit()); - - return exprBuilder.setList(createListExpr).build(); - } - - private CelExpr.CelList visitListInitElements(ListInitContext context) { - CelExpr.CelList.Builder listExpr = CelExpr.CelList.newBuilder(); - if (context == null) { - return listExpr.build(); - } - - for (int index = 0; index < context.elems.size(); index++) { - OptExprContext elem = context.elems.get(index); - listExpr.addElements(visit(elem.e)); - - if (elem.opt != null) { - if (!options.enableOptionalSyntax()) { - exprFactory.reportError(elem.opt, "unsupported syntax '?'"); - continue; - } - listExpr.addOptionalIndices(index); - } - } - - return listExpr.build(); - } - - @Override - public CelExpr visitCreateMap(CreateMapContext context) { - checkNotNull(context); - CelExpr.Builder exprBuilder = exprFactory.newExprBuilder(context.op); - CelExpr.CelMap.Builder createMapExpr = visitMapEntries(context.entries); - return exprBuilder.setMap(createMapExpr.build()).build(); - } - - private CelExpr buildMacroCallArgs(CelExpr expr) { - CelExpr.Builder resultExpr = CelExpr.newBuilder().setId(expr.id()); - if (sourceInfo.containsMacroCalls(expr.id())) { - return resultExpr.build(); - } - // Call expression could have args or sub-args that are also macros found in macro calls - if (expr.exprKind().getKind() == CelExpr.ExprKind.Kind.CALL) { - CelExpr.CelCall.Builder callExpr = - CelExpr.CelCall.newBuilder().setFunction(expr.call().function()); - // Iterate the AST from `expr` recursively looking for macros. Because we are at most - // starting from the top level macro, this recursion is bounded by the size of the AST. This - // means that the depth check on the AST during parsing will catch recursion overflows - // before we get to here. - expr.call().args().forEach(arg -> callExpr.addArgs(buildMacroCallArgs(arg))); - expr.call().target().ifPresent(target -> callExpr.setTarget(buildMacroCallArgs(target))); - return resultExpr.setCall(callExpr.build()).build(); - } - return expr; - } - - /** - * Returns the expanded AST after visiting a macro. Optional.empty is returned instead if the - * implementation decides that an expansion should not be performed, in which case we should just - * default to call. - */ - private Optional visitMacro( - CelExpr.Builder expr, - String id, - ImmutableList args, - Optional target, - CelMacro macro) { - if (exprFactory.isNodeLimitExceeded()) { - return Optional.of( - exprFactory.reportError( - exprFactory.getPosition(expr.id()), - "could not expand macro: expression node limit exceeded")); - } - - Optional expandedMacro = - expandMacro( - exprFactory.getPosition(expr.id()), - macro, - target.orElse(CelExpr.newBuilder().build()), - args); - if (!expandedMacro.isPresent()) { - return Optional.empty(); - } - CelExpr.CelCall.Builder callExpr = CelExpr.CelCall.newBuilder().setFunction(id); - if (target.isPresent()) { - if (sourceInfo.containsMacroCalls(target.get().id())) { - callExpr.setTarget(CelExpr.newBuilder().setId(target.get().id()).build()); - } else { - callExpr.setTarget(target.get()); - } - } - for (CelExpr arg : args) { - callExpr.addArgs(buildMacroCallArgs(arg)); - } - - if (options.populateMacroCalls()) { - sourceInfo.addMacroCalls( - expandedMacro.get().id(), - // Note: A macro id MUST NOT be assigned to the call expr placed into the macro calls map. - // This can cause an infinite loop in some of the call chains that try to figure out - // whether the current expression is expanded to a macro. - CelExpr.newBuilder().setCall(callExpr.build()).build()); - } - - sourceInfo.removePositions(expr.id()); - return expandedMacro; - } - - private String normalizeEscapedIdent(EscapeIdentContext context) { - String identifier = context.getText(); - if (context instanceof SimpleIdentifierContext) { - return identifier; - } else if (context instanceof EscapedIdentifierContext) { - if (!options.enableQuotedIdentifierSyntax()) { - exprFactory.reportError(context, "unsupported syntax '`'"); - return identifier; - } - return identifier.substring(1, identifier.length() - 1); - } - - // This is normally unreachable, but might happen if the parser is in an error state or if the - // grammar is updated and not handled here. - exprFactory.reportError(context, "unsupported identifier"); - return identifier; - } - - private CelExpr.CelStruct.Builder visitStructFields(FieldInitializerListContext context) { - if (context == null - || context.cols == null - || context.fields == null - || context.values == null) { - return CelExpr.CelStruct.newBuilder(); - } - int entryCount = min(context.cols.size(), context.fields.size(), context.values.size()); - CelExpr.CelStruct.Builder structExpr = CelExpr.CelStruct.newBuilder(); - for (int index = 0; index < entryCount; index++) { - OptFieldContext fieldContext = context.fields.get(index); - boolean isOptionalEntry = false; - if (fieldContext.opt != null) { - if (!options.enableOptionalSyntax()) { - exprFactory.reportError(fieldContext.opt, "unsupported syntax '?'"); - } else { - isOptionalEntry = true; - } - } - - // The field may be empty due to a prior error. - if (fieldContext.escapeIdent() == null) { - return CelExpr.CelStruct.newBuilder(); - } - String fieldName = normalizeEscapedIdent(fieldContext.escapeIdent()); - - CelExpr.CelStruct.Entry.Builder exprBuilder = - CelExpr.CelStruct.Entry.newBuilder() - .setId(exprFactory.newExprId(exprFactory.getPosition(context.cols.get(index)))); - structExpr.addEntries( - exprBuilder - .setFieldKey(fieldName) - .setValue(visit(context.values.get(index))) - .setOptionalEntry(isOptionalEntry) - .build()); - } - return structExpr; - } - - private CelExpr.CelMap.Builder visitMapEntries(MapInitializerListContext context) { - if (context == null || context.cols == null || context.keys == null || context.values == null) { - return CelExpr.CelMap.newBuilder(); - } - int entryCount = min(context.cols.size(), context.keys.size(), context.values.size()); - CelExpr.CelMap.Builder mapExpr = CelExpr.CelMap.newBuilder(); - for (int index = 0; index < entryCount; index++) { - OptExprContext keyContext = context.keys.get(index); - boolean isOptionalEntry = false; - if (keyContext.opt != null) { - if (!options.enableOptionalSyntax()) { - exprFactory.reportError(keyContext.opt, "unsupported syntax '?'"); - } else { - isOptionalEntry = true; - } - } - CelExpr.CelMap.Entry.Builder exprBuilder = - CelExpr.CelMap.Entry.newBuilder() - .setId(exprFactory.newExprId(exprFactory.getPosition(context.cols.get(index)))); - mapExpr.addEntries( - exprBuilder - .setKey(visit(keyContext.e)) - .setValue(visit(context.values.get(index))) - .setOptionalEntry(isOptionalEntry) - .build()); - } - return mapExpr; - } - - @Override - protected CelExpr defaultResult() { - // visitTerminalNode and visitErrorNode call this method. - return exprFactory.ensureErrorsExist( - () -> "Abstract syntax tree in an unexpected state, this is likely a bug."); - } - - @Override - public CelExpr visitConstantLiteral(ConstantLiteralContext context) { - checkNotNull(context); - if (context.literal() == null) { - return exprFactory.ensureErrorsExist(context); - } - return visit(context.literal()); - } - - @Override - public CelExpr visitExprList(ExprListContext context) { - // We should never get here, as we do not directly visit expression lists. - return exprFactory.ensureErrorsExist(context); - } - - @Override - public CelExpr visitFieldInitializerList(FieldInitializerListContext context) { - // We should never get here, as we do not directly visit field initializer lists. - return exprFactory.ensureErrorsExist(context); - } - - @Override - public CelExpr visitMapInitializerList(MapInitializerListContext context) { - // We should never get here, as we do not directly visit map initializer lists. - return exprFactory.ensureErrorsExist(context); - } - - @Override - public CelExpr visitListInit(ListInitContext context) { - // We should never get here, as we do not directly visit list initializer. - return exprFactory.ensureErrorsExist(context); - } - - @Override - public CelExpr visitInt(IntContext context) { - checkNotNull(context); - CelConstant constExpr; - try { - constExpr = Constants.parseInt(context.getText()); - } catch (ParseException e) { - return exprFactory.reportError(context, e.getMessage()); - } - - return exprFactory.newExprBuilder(context.tok).setConstant(constExpr).build(); - } - - @Override - public CelExpr visitUint(UintContext context) { - checkNotNull(context); - CelConstant constExpr; - try { - constExpr = Constants.parseUint(context.getText()); - } catch (ParseException e) { - return exprFactory.reportError(context, e.getMessage()); - } - return exprFactory.newExprBuilder(context).setConstant(constExpr).build(); - } - - @Override - public CelExpr visitDouble(DoubleContext context) { - checkNotNull(context); - CelConstant constExpr; - try { - constExpr = Constants.parseDouble(context.getText()); - } catch (ParseException e) { - return exprFactory.reportError(context, e.getMessage()); - } - return exprFactory.newExprBuilder(context.tok).setConstant(constExpr).build(); - } - - @Override - public CelExpr visitString(StringContext context) { - checkNotNull(context); - CelConstant constExpr; - try { - constExpr = Constants.parseString(context.getText()); - } catch (ParseException e) { - return exprFactory.reportError(context, e.getMessage()); - } - return exprFactory.newExprBuilder(context).setConstant(constExpr).build(); - } - - @Override - public CelExpr visitBytes(BytesContext context) { - checkNotNull(context); - CelConstant constExpr; - try { - constExpr = Constants.parseBytes(context.getText()); - } catch (ParseException e) { - return exprFactory.reportError(context, e.getMessage()); - } - return exprFactory.newExprBuilder(context).setConstant(constExpr).build(); - } - - @Override - public CelExpr visitBoolTrue(BoolTrueContext context) { - checkNotNull(context); - return exprFactory.newExprBuilder(context).setConstant(Constants.TRUE).build(); - } - - @Override - public CelExpr visitBoolFalse(BoolFalseContext context) { - checkNotNull(context); - return exprFactory.newExprBuilder(context).setConstant(Constants.FALSE).build(); - } - - @Override - public CelExpr visitNull(NullContext context) { - checkNotNull(context); - return exprFactory.newExprBuilder(context).setConstant(Constants.NULL).build(); - } - - private Optional expandMacro( - int position, CelMacro macro, CelExpr target, ImmutableList arguments) { - exprFactory.pushPosition(position); - try { - return macro.getExpander().expandMacro(exprFactory, target, arguments); - } finally { - exprFactory.popPosition(); + if (options.enablePrattParser()) { + return PrattParser.parse(source, options, parser.getMacros()); } + return AntlrParser.parse(source, options, parser.getMacros().values()); } - private CelExpr receiverCallOrMacro(MemberCallContext context, String id, CelExpr member) { - return macroOrCall(context.args, context.open, id, Optional.of(member), true); - } - - private CelExpr globalCallOrMacro(GlobalCallContext context, String id) { - return macroOrCall(context.args, context.op, id, Optional.empty(), false); - } - - private ImmutableList visitExprListContext(ExprListContext args) { - int argCount = args != null && args.e != null ? args.e.size() : 0; - if (argCount == 0) { - return ImmutableList.of(); - } - - ImmutableList.Builder argumentsBuilder = - ImmutableList.builderWithExpectedSize(argCount); - for (ExprContext argExprCtx : args.e) { - argumentsBuilder.add(visit(argExprCtx)); - } - return argumentsBuilder.build(); - } - - private CelExpr macroOrCall( - ExprListContext args, - Token open, - String id, - Optional member, - boolean isReceiverStyle) { - int argCount = args != null && args.e != null ? args.e.size() : 0; - Optional macro = lookupMacro(id, argCount, isReceiverStyle); - CelExpr.Builder exprBuilder = exprFactory.newExprBuilder(open); - - ImmutableList arguments = visitExprListContext(args); - Optional errorArg = arguments.stream().filter(ERROR::equals).findAny(); - if (errorArg.isPresent()) { - sourceInfo.removePositions(exprBuilder.id()); - // Any arguments passed in to the macro may fail parsing. - // Stop the macro expansion in this case as the result of the macro will be a parse failure. - return ERROR; - } - - if (macro.isPresent()) { - Optional expandedMacro = visitMacro(exprBuilder, id, arguments, member, macro.get()); - if (expandedMacro.isPresent()) { - return expandedMacro.get(); - } - } - - CelExpr.CelCall.Builder callExpr = - CelExpr.CelCall.newBuilder().setFunction(id).addArgs(arguments); - member.ifPresent(callExpr::setTarget); - - return exprBuilder.setCall(callExpr.build()).build(); - } - - private Optional lookupMacro(String id, int argCount, boolean receiverStlye) { - String key = CelMacro.formatKey(id, argCount, receiverStlye); - Optional macro = parser.findMacro(key); - if (macro.isPresent()) { - return macro; - } - key = CelMacro.formatVarArgKey(id, receiverStlye); - return parser.findMacro(key); - } - - /** - * Checks whether a given parse tree node is left recursive for the purposes of counting recursion - * depths. - */ - private boolean isLeftRecursiveForCountingDepths(ParseTree node) { - // There are certainly more left recursive nodes than what's shown below. - // We try to catch the specific node types that explodes the number of recursive visit calls and - // of those that cannot be caught by PerRuleRecursionListener. - return node instanceof ExprContext - || node instanceof CalcContext - || node instanceof RelationContext - || node instanceof SelectContext - || node instanceof MemberCallContext - || node instanceof IndexContext; - } - - private void checkAndIncrementRecursionDepth() { - recursionDepth++; - if (recursionDepth > options.maxParseRecursionDepth()) { - String errorMessage = - String.format( - "Expression recursion limit exceeded. limit: %d", options.maxParseRecursionDepth()); - exprFactory.reportError(CelIssue.formatError(CelSourceLocation.of(1, 0), errorMessage)); - throw new ParseCancellationException(errorMessage); - } - } - - private void decrementRecursionDepth() { - recursionDepth--; - } - - /** - * unnest traverses down the left-hand side of the parse graph until it encounters the first - * compound parse node or the first leaf in the parse graph. - */ - private ParseTree unnest(ParseTree tree) { - while (tree != null) { - if (tree instanceof ExprContext) { - // conditionalOr op='?' conditionalOr : expr - ExprContext context = (ExprContext) tree; - if (context.op != null) { - return tree; - } - // conditionalOr - tree = context.e; - } else if (tree instanceof ConditionalOrContext) { - // conditionalAnd (ops=|| conditionalAnd)* - ConditionalOrContext context = (ConditionalOrContext) tree; - if (context.ops != null && !context.ops.isEmpty()) { - return tree; - } - // conditionalAnd - tree = context.e; - } else if (tree instanceof ConditionalAndContext) { - // relation (ops=&& relation)* - ConditionalAndContext context = (ConditionalAndContext) tree; - if (context.ops != null && !context.ops.isEmpty()) { - return tree; - } - - // relation - tree = context.e; - } else if (tree instanceof RelationContext) { - // relation op relation - RelationContext context = (RelationContext) tree; - if (context.op != null) { - return tree; - } - // calc - tree = context.calc(); - } else if (tree instanceof CalcContext) { - // calc op calc - CalcContext context = (CalcContext) tree; - if (context.op != null) { - return tree; - } - - // unary - tree = context.unary(); - } else if (tree instanceof MemberExprContext) { - // member expands to one of: primary, select, index, or create message - tree = ((MemberExprContext) tree).member(); - } else if (tree instanceof PrimaryExprContext) { - // primary expands to one of identifier, nested, create list, create struct, literal - tree = ((PrimaryExprContext) tree).primary(); - } else if (tree instanceof NestedContext) { - // contains a nested 'expr' - tree = ((NestedContext) tree).e; - } else if (tree instanceof ConstantLiteralContext) { - // expands to a primitive literal - tree = ((ConstantLiteralContext) tree).literal(); - } else { - return tree; - } - } - - return tree; - } - - /** Implementation of {@link CelMacroExprFactory}. */ - private static final class ExprFactory extends CelMacroExprFactory { - - private final org.antlr.v4.runtime.Parser recognizer; - private final CelSource.Builder sourceInfo; - private final ArrayList issues; - private final ArrayDeque positions; - private final String accumulatorVarName; - private final int maxExpressionNodeCount; - private boolean nodeLimitExceeded; - - private ExprFactory( - org.antlr.v4.runtime.Parser recognizer, - CelSource.Builder sourceInfo, - String accumulatorVarName, - int maxExpressionNodeCount) { - this.recognizer = recognizer; - this.sourceInfo = sourceInfo; - this.issues = new ArrayList<>(); - this.positions = new ArrayDeque<>(1); // Currently this usually contains at most 1 position. - this.accumulatorVarName = accumulatorVarName; - this.maxExpressionNodeCount = maxExpressionNodeCount; - } - - // Implementation of CelExprFactory. - - @Override - protected CelSourceLocation getSourceLocation(long exprId) { - checkArgument(exprId > 0L); - return getLocation(getPosition(exprId)); - } - - @CanIgnoreReturnValue - @Override - public CelExpr reportError(CelIssue error) { - checkNotNull(error); - issues.add(error); - if (!CelSourceLocation.NONE.equals(error.getSourceLocation())) { - Optional offset = sourceInfo.getLocationOffset(error.getSourceLocation()); - checkState(offset.isPresent()); // A valid location should always return a valid offset. - return newExpr(offset.get()); - } - return ERROR; - } - - @FormatMethod - @CanIgnoreReturnValue - private CelExpr reportError( - ParserRuleContext context, @FormatString String format, Object... args) { - return reportError(context, String.format(format, args)); - } - - @CanIgnoreReturnValue - private CelExpr reportError(ParserRuleContext context, String message) { - return reportError(CelIssue.formatError(getLocation(context), message)); - } - - @CanIgnoreReturnValue - private CelExpr reportError(Token token, String message) { - return reportError(CelIssue.formatError(getLocation(token), message)); - } - - @CanIgnoreReturnValue - private CelExpr reportError(int position, String message) { - return reportError(CelIssue.formatError(getLocation(position), message)); - } - - // Implementation of CelExprFactory. - - @Override - public String getAccumulatorVarName() { - return accumulatorVarName; - } - - @Override - protected CelSourceLocation currentSourceLocationForMacro() { - checkState(!positions.isEmpty()); // Should only be called while expanding macros. - return getLocation(peekPosition()); - } - - // Internal methods used by the parser but not part of the public API. - - private boolean isNodeLimitExceeded() { - return nodeLimitExceeded; - } - - private void pushPosition(int position) { - positions.addLast(position); - } - - private void popPosition() { - checkState(!positions.isEmpty()); - positions.removeLast(); - } - - private int peekPosition() { - checkState(!positions.isEmpty()); - return positions.peekLast(); - } - - private long nextExprId(int position) { - long exprId = super.nextExprId(); - if (exprId > maxExpressionNodeCount && !nodeLimitExceeded) { - nodeLimitExceeded = true; - reportError( - position, String.format("expression node limit (%d) exceeded", maxExpressionNodeCount)); - } - if (position != -1) { - sourceInfo.addPositions(exprId, position); - } - return exprId; - } - - @Override - public long nextExprId() { - checkState(!positions.isEmpty()); // Should only be called while expanding macros. - // Do not call this method directly from within the parser, use nextExprId(int). - return nextExprId(peekPosition()); - } - - @Override - public long copyExprId(long id) { - return nextExprId(getPosition(id)); - } - - private List getIssuesList() { - return issues; - } - - private int getPosition(long exprId) { - return Optional.ofNullable(sourceInfo.getPositionsMap().get(exprId)).orElse(-1); - } - - private int getPosition(Token token) { - return sourceInfo - .getLocationOffset(token.getLine(), token.getCharPositionInLine()) - .orElse(-1); - } - - private int getPosition(ParserRuleContext context) { - return getPosition(context.getStart()); - } - - private CelSourceLocation getLocation(int position) { - return sourceInfo.getOffsetLocation(position).orElse(CelSourceLocation.NONE); - } - - private CelSourceLocation getLocation(Token token) { - return CelSourceLocation.of(token.getLine(), token.getCharPositionInLine()); - } - - private CelSourceLocation getLocation(ParserRuleContext context) { - return getLocation(context.getStart()); - } - - @CanIgnoreReturnValue - private long newExprId(int position) { - return nextExprId(position); - } - - private CelExpr.Builder newExprBuilder(int position) { - return CelExpr.newBuilder().setId(newExprId(position)); - } - - private CelExpr.Builder newExprBuilder(Token token) { - return newExprBuilder(getPosition(token)); - } - - private CelExpr.Builder newExprBuilder(ParserRuleContext context) { - return newExprBuilder(getPosition(context)); - } - - private CelExpr newExpr(int position) { - return newExprBuilder(position).build(); - } - - private CelExpr ensureErrorsExist(Supplier message) { - // Because we do not treat syntax errors as fatal during parsing, the parse tree is often in - // an abnormal state. We call this function to ensure we have recorded syntax errors. If we - // have we return the special error node otherwise we bail and mention that this is likely a - // bug. - if (issues.isEmpty()) { - // If we reach here, this is an unexpected error and highly likely to be a bug. At least one - // syntax error or another error should have occurred because the parse tree is in an - // unexpected state. - throw new ParseCancellationException( - String.format( - "Abstract syntax tree in an unexpected state, this is likely a bug: %s", - message.get())); - } - return ERROR; - } - - private CelExpr ensureErrorsExist(ParserRuleContext context) { - return ensureErrorsExist(() -> context.toInfoString(recognizer)); - } - } - - /** - * Listener that enforces a maximum recursion depth, to avoid accidental stack overflow issues - * when parsing large expressions. - */ - private static final class PerRuleRecursionListener implements ParseTreeListener { - - private final ExprFactory exprFactory; - private final int maxRecursionDepth; - private final Map ruleTypeDepth; - - private PerRuleRecursionListener(ExprFactory exprFactory, int maxRecursionDepth) { - this.exprFactory = exprFactory; - this.maxRecursionDepth = maxRecursionDepth; - this.ruleTypeDepth = new HashMap<>(); - } - - @Override - public void enterEveryRule(ParserRuleContext context) { - int ruleDepth = ruleTypeDepth.getOrDefault(context.getRuleIndex(), 0) + 1; - ruleTypeDepth.put(context.getRuleIndex(), ruleDepth); - if (ruleDepth > maxRecursionDepth) { - String errorMessage = - String.format("Expression recursion limit exceeded. limit: %d", maxRecursionDepth); - exprFactory.reportError(CelIssue.formatError(CelSourceLocation.of(1, 0), errorMessage)); - throw new ParseCancellationException(errorMessage); - } - } - - @Override - public void exitEveryRule(ParserRuleContext context) { - int ruleDepth = ruleTypeDepth.get(context.getRuleIndex()) - 1; - ruleTypeDepth.put(context.getRuleIndex(), ruleDepth); - } - - @Override - public void visitErrorNode(ErrorNode node) {} - - @Override - public void visitTerminal(TerminalNode node) {} - } - - /** Error strategy that limits the number of recovery attempts. */ - private static final class RecoveryLimitErrorStrategy extends DefaultErrorStrategy { - - private final int recoveryLimit; - private int recoveryAttempts; - - private RecoveryLimitErrorStrategy(int recoveryLimit) { - this.recoveryLimit = recoveryLimit; - recoveryAttempts = 0; - } - - @Override - public void recover(org.antlr.v4.runtime.Parser recognizer, RecognitionException e) { - checkRecoveryLimit(recognizer); - super.recover(recognizer, e); - } - - @Override - public Token recoverInline(org.antlr.v4.runtime.Parser recognizer) { - checkRecoveryLimit(recognizer); - return super.recoverInline(recognizer); - } - - private void checkRecoveryLimit(org.antlr.v4.runtime.Parser recognizer) { - if (recoveryAttempts++ >= recoveryLimit) { - String tooManyErrors = String.format("More than %d parse errors.", recoveryLimit); - recognizer.notifyErrorListeners(tooManyErrors); - throw new ParseCancellationException(tooManyErrors); - } - } - } - - private static final class ErrorListener implements ANTLRErrorListener { - - private final ExprFactory exprFactory; - - private ErrorListener(ExprFactory exprFactory) { - this.exprFactory = exprFactory; - } - - @Override - public void reportAmbiguity( - org.antlr.v4.runtime.Parser recognizer, - DFA dfa, - int startIndex, - int stopIndex, - boolean exact, - BitSet ambigAlts, - ATNConfigSet configs) { - // Intentional. - } - - @Override - public void reportAttemptingFullContext( - org.antlr.v4.runtime.Parser recognizer, - DFA dfa, - int startIndex, - int stopIndex, - BitSet ambigAlts, - ATNConfigSet configs) { - // Intentional. - } - - @Override - public void reportContextSensitivity( - org.antlr.v4.runtime.Parser recognizer, - DFA dfa, - int startIndex, - int stopIndex, - int prediction, - ATNConfigSet configs) { - // Intentional. - } - - @Override - public void syntaxError( - Recognizer recognizer, - Object offendingSymbol, - int line, - int charPositionInLine, - String msg, - RecognitionException e) { - msg = msg.replace("%", "%%"); - exprFactory.reportError( - CelIssue.formatError(CelSourceLocation.of(line, charPositionInLine), msg)); - } - } + private Parser() {} } diff --git a/parser/src/main/java/dev/cel/parser/PrattParser.java b/parser/src/main/java/dev/cel/parser/PrattParser.java index 4132e9c32..17ce514d5 100644 --- a/parser/src/main/java/dev/cel/parser/PrattParser.java +++ b/parser/src/main/java/dev/cel/parser/PrattParser.java @@ -368,7 +368,7 @@ private void reportSyntaxError(Lexer.Token token, String msg) { } private boolean checkRecursion(int chainDepth, Lexer.Token token) { - if (recursionDepth + chainDepth >= options.maxParseRecursionDepth()) { + if (recursionDepth + chainDepth > options.maxParseRecursionDepth()) { if (!recursionLimitExceeded) { recursionLimitExceeded = true; reportError( @@ -386,10 +386,11 @@ private CelExpr parseExpr() { if (recursionLimitExceeded || isRecoveryLimitExceeded()) { return ERROR; } + recursionDepth++; if (checkRecursion(0, peekToken)) { + recursionDepth--; return ERROR; } - recursionDepth++; CelExpr expr = parseBinaryAndTernary(0); recursionDepth--; return expr; @@ -416,10 +417,10 @@ private CelExpr parseBinaryAndTernary(int minPrec) { } Lexer.Token opTok = nextToken(); - chainDepth++; if (checkRecursion(chainDepth, opTok)) { return ERROR; } + chainDepth++; long opId = nextId(opTok); CelExpr rhs = parseBinaryAndTernary(opInfo.precedence + 1); lhs = buildBinaryCall(opId, opInfo.name, lhs, rhs); @@ -461,8 +462,9 @@ private CelExpr parseBalancedLogicalChain(CelExpr lhs, BinaryOpInfo opInfo) { terms.add(lhs); while (peekToken.type == opInfo.type) { Lexer.Token opTok = nextToken(); + long opId = nextId(opTok); CelExpr rhs = parseBinaryAndTernary(opInfo.precedence + 1); - ops.add(nextId(opTok)); + ops.add(opId); terms.add(rhs); } return balancedTree(opInfo.name, terms, ops, 0, ops.size() - 1); @@ -506,10 +508,10 @@ private CelExpr parseSelectorChainTail(CelExpr initialLhs) { while (true) { Lexer.TokenType tok = peekToken.type; if (tok == Lexer.TokenType.DOT) { - chainDepth++; if (checkRecursion(chainDepth, peekToken)) { return ERROR; } + chainDepth++; Lexer.Token dotTok = nextToken(); boolean optional = false; if (peekToken.type == Lexer.TokenType.QUESTION) { @@ -537,7 +539,7 @@ private CelExpr parseSelectorChainTail(CelExpr initialLhs) { CelExpr arg1 = lhs; CelExpr arg2 = CelExpr.newBuilder() - .setId(nextId(idTok)) + .setId(nextId(getLeftmostPosition(lhs))) .setConstant(CelConstant.ofValue(idText)) .build(); lhs = @@ -578,10 +580,10 @@ private CelExpr parseSelectorChainTail(CelExpr initialLhs) { .build(); } } else if (tok == Lexer.TokenType.LEFT_BRACKET) { - chainDepth++; if (checkRecursion(chainDepth, peekToken)) { return ERROR; } + chainDepth++; Lexer.Token bracketTok = nextToken(); long opId = nextId(bracketTok); boolean optional = false; @@ -607,13 +609,11 @@ private CelExpr parseSelectorChainTail(CelExpr initialLhs) { .build()) .build(); } else if (tok == Lexer.TokenType.LEFT_BRACE) { - // Position must be retrieved before extractStructName erases the expression IDs. - int structPos = getLeftmostPosition(lhs); String structName = extractStructName(lhs).orElse(null); if (structName == null) { break; } - lhs = parseStruct(nextId(structPos), structName); + lhs = parseStruct(nextId(peekToken.start), structName); } else { break; } @@ -639,14 +639,14 @@ private CelExpr parseUnaryOps() { if (opType == Lexer.TokenType.MINUS) { if (peekToken.type == Lexer.TokenType.INT) { - return parseIntLiteral(nextId(op), /* isNegative= */ true); + return parseIntLiteral(nextId(peekToken), /* isNegative= */ true); } if (peekToken.type == Lexer.TokenType.FLOAT) { - return parseDoubleLiteral(nextId(op), /* isNegative= */ true); + return parseDoubleLiteral(nextId(peekToken), /* isNegative= */ true); } } - if (checkRecursion(1, op)) { + if (checkRecursion(0, op)) { return ERROR; } @@ -711,10 +711,10 @@ private CelExpr parseUnaryOpsChain(Lexer.Token firstOp) { int chainDepth = 0; for (UnaryOp op : ops) { - chainDepth++; if (checkRecursion(chainDepth, op.token)) { return ERROR; } + chainDepth++; } recursionDepth += ops.size(); @@ -794,6 +794,9 @@ private CelExpr parsePrimary() { case LEFT_PAREN: { int groupingParenCount = countGroupingParentheses(); + if (checkRecursion(groupingParenCount, peekToken)) { + return ERROR; + } for (int i = 0; i < groupingParenCount; ++i) { nextToken(); } @@ -989,7 +992,7 @@ private CelExpr parseIntLiteral(long nodeId, boolean isNegative) { CelConstant constExpr = Constants.parseInt(text); return CelExpr.newBuilder().setId(id).setConstant(constExpr).build(); } catch (ParseException e) { - reportSyntaxError(tok, "invalid int literal"); + reportSyntaxError(tok, "invalid int literal: " + text); return CelExpr.newBuilder().setId(nextId(tok)).build(); } } @@ -1001,7 +1004,7 @@ private CelExpr parseUintLiteral() { CelConstant constExpr = Constants.parseUint(value); return CelExpr.newBuilder().setId(nextId(tok)).setConstant(constExpr).build(); } catch (ParseException e) { - reportSyntaxError(tok, "invalid uint literal"); + reportSyntaxError(tok, "invalid uint literal: " + value); return CelExpr.newBuilder().setId(nextId(tok)).build(); } } @@ -1012,13 +1015,9 @@ private CelExpr parseDoubleLiteral(long nodeId, boolean isNegative) { long id = nodeId == -1 ? nextId(tok) : nodeId; try { CelConstant constExpr = Constants.parseDouble(text); - if (Double.isInfinite(constExpr.doubleValue())) { - reportSyntaxError(tok, "invalid double literal"); - return CelExpr.newBuilder().setId(id).build(); - } return CelExpr.newBuilder().setId(id).setConstant(constExpr).build(); } catch (ParseException e) { - reportSyntaxError(tok, "invalid double literal"); + reportSyntaxError(tok, "invalid double literal: " + text); return CelExpr.newBuilder().setId(nextId(tok)).build(); } } @@ -1097,8 +1096,7 @@ private Optional extractStructName(CelExpr expr) { } CelExpr operand = expr.select().operand(); eraseId(expr.id()); - return extractStructName(operand) - .map(prefix -> prefix + "." + expr.select().field()); + return extractStructName(operand).map(prefix -> prefix + "." + expr.select().field()); } return Optional.empty(); } @@ -1178,7 +1176,7 @@ private void recordMacroCall( if (macroCalls.containsKey(target.id())) { callExpr.setTarget(CelExpr.newBuilder().setId(target.id()).build()); } else { - callExpr.setTarget(buildMacroCallArgs(target)); + callExpr.setTarget(target); } } for (CelExpr arg : args) { diff --git a/parser/src/test/java/dev/cel/parser/CelParserImplTest.java b/parser/src/test/java/dev/cel/parser/CelParserImplTest.java index 756e97d31..37501ec29 100644 --- a/parser/src/test/java/dev/cel/parser/CelParserImplTest.java +++ b/parser/src/test/java/dev/cel/parser/CelParserImplTest.java @@ -37,11 +37,18 @@ public final class CelParserImplTest { // This file exercises non-parsing related methods in CelParser. See CelParserParameterizedTest // for parsing related tests. + @TestParameter private boolean enablePrattParser; + + private CelParserBuilder newParserBuilder() { + return CelParserImpl.newBuilder() + .setOptions(CelOptions.newBuilder().enablePrattParser(enablePrattParser).build()); + } + @Test public void build_withMacros_containsAllMacros() { CelParserImpl parser = (CelParserImpl) - CelParserImpl.newBuilder().setStandardMacros(CelStandardMacro.STANDARD_MACROS).build(); + newParserBuilder().setStandardMacros(CelStandardMacro.STANDARD_MACROS).build(); assertThat(parser.findMacro("has:1:false")).hasValue(CelStandardMacro.HAS.getDefinition()); assertThat(parser.findMacro("all:2:true")).hasValue(CelStandardMacro.ALL.getDefinition()); assertThat(parser.findMacro("exists:2:true")).hasValue(CelStandardMacro.EXISTS.getDefinition()); @@ -57,7 +64,7 @@ public void build_withMacros_containsAllMacros() { public void build_withStandardMacros_containsAllMacros() { CelParserImpl parser = (CelParserImpl) - CelParserImpl.newBuilder().setStandardMacros(CelStandardMacro.STANDARD_MACROS).build(); + newParserBuilder().setStandardMacros(CelStandardMacro.STANDARD_MACROS).build(); assertThat(parser.findMacro("has:1:false")).hasValue(CelStandardMacro.HAS.getDefinition()); assertThat(parser.findMacro("all:2:true")).hasValue(CelStandardMacro.ALL.getDefinition()); assertThat(parser.findMacro("exists:2:true")).hasValue(CelStandardMacro.EXISTS.getDefinition()); @@ -76,7 +83,7 @@ public void build_withStandardMacrosAndCustomMacros_containsAllMacros() { "customMacro", 1, (a, b, c) -> Optional.of(CelExpr.newBuilder().build())); CelParserImpl parser = (CelParserImpl) - CelParserImpl.newBuilder() + newParserBuilder() .setStandardMacros(CelStandardMacro.STANDARD_MACROS) .addMacros(customMacro) .build(); @@ -96,14 +103,14 @@ public void build_withStandardMacrosAndCustomMacros_containsAllMacros() { @Test public void build_withMacro_containsMacro() { CelParserImpl parser = - (CelParserImpl) CelParserImpl.newBuilder().setStandardMacros(CelStandardMacro.HAS).build(); + (CelParserImpl) newParserBuilder().setStandardMacros(CelStandardMacro.HAS).build(); assertThat(parser.findMacro("has:1:false")).hasValue(CelStandardMacro.HAS.getDefinition()); } @Test public void build_withStandardMacro_containsMacro() { CelParserImpl parser = - (CelParserImpl) CelParserImpl.newBuilder().setStandardMacros(CelStandardMacro.HAS).build(); + (CelParserImpl) newParserBuilder().setStandardMacros(CelStandardMacro.HAS).build(); assertThat(parser.findMacro("has:1:false")).hasValue(CelStandardMacro.HAS.getDefinition()); } @@ -111,7 +118,7 @@ public void build_withStandardMacro_containsMacro() { public void build_withStandardMacro_secondCallReplaces() { CelParserImpl parser = (CelParserImpl) - CelParserImpl.newBuilder() + newParserBuilder() .setStandardMacros(CelStandardMacro.HAS, CelStandardMacro.ALL) .setStandardMacros(CelStandardMacro.HAS) .build(); @@ -128,7 +135,7 @@ public void build_standardMacroKeyConflictsWithCustomMacro_throws() { assertThrows( IllegalArgumentException.class, () -> - CelParserImpl.newBuilder() + newParserBuilder() .setStandardMacros(CelStandardMacro.HAS) .addMacros(customMacro) .build()); @@ -136,7 +143,7 @@ public void build_standardMacroKeyConflictsWithCustomMacro_throws() { @Test public void build_containsNoMacros() { - CelParserImpl parser = (CelParserImpl) CelParserImpl.newBuilder().build(); + CelParserImpl parser = (CelParserImpl) newParserBuilder().build(); assertThat(parser.findMacro("has:1:false")).isEmpty(); } @@ -144,7 +151,7 @@ public void build_containsNoMacros() { public void setParserLibrary_success() { CelParserImpl parser = (CelParserImpl) - CelParserImpl.newBuilder() + newParserBuilder() .addLibraries( new CelParserLibrary() { @Override @@ -164,8 +171,12 @@ public void setParserOptions(CelParserBuilder parserBuilder) { public void parse_throwsWhenExpressionSizeCodePointLimitExceeded() { CelParserImpl parser = (CelParserImpl) - CelParserImpl.newBuilder() - .setOptions(CelOptions.newBuilder().maxExpressionCodePointSize(2).build()) + newParserBuilder() + .setOptions( + CelOptions.newBuilder() + .enablePrattParser(enablePrattParser) + .maxExpressionCodePointSize(2) + .build()) .build(); CelValidationResult parseResult = parser.parse(CelSource.newBuilder("foo").build()); CelValidationException exception = @@ -221,9 +232,12 @@ public void parse_largeExprHitsMaxRecursionLimit_throws( @TestParameter MaxParseRecursionDepthTestCase testCase) { int maxParseRecursionLimit = MaxParseRecursionDepthTestCase.MAX_RECURSION_LIMIT; CelParser parser = - CelParserImpl.newBuilder() + newParserBuilder() .setOptions( - CelOptions.newBuilder().maxParseRecursionDepth(maxParseRecursionLimit).build()) + CelOptions.newBuilder() + .enablePrattParser(enablePrattParser) + .maxParseRecursionDepth(maxParseRecursionLimit) + .build()) .build(); CelValidationResult parseResult = parser.parse(CelSource.newBuilder(testCase.source).build()); @@ -238,7 +252,7 @@ public void parse_largeExprHitsMaxRecursionLimit_throws( assertThat(issue.getMessage()) .contains("Expression recursion limit exceeded. limit: " + maxParseRecursionLimit); assertThat(issue.getSourceLocation().getLine()).isEqualTo(1); - assertThat(issue.getSourceLocation().getColumn()).isEqualTo(0); + assertThat(issue.getSourceLocation().getColumn()).isAtLeast(0); } @Test @@ -247,9 +261,12 @@ public void parse_exprUnderMaxRecursionLimit_doesNotThrow( int maxParseRecursionLimit = MaxParseRecursionDepthTestCase.MAX_RECURSION_LIMIT + 1; CelParser parser = - CelParserImpl.newBuilder() + newParserBuilder() .setOptions( - CelOptions.newBuilder().maxParseRecursionDepth(maxParseRecursionLimit).build()) + CelOptions.newBuilder() + .enablePrattParser(enablePrattParser) + .maxParseRecursionDepth(maxParseRecursionLimit) + .build()) .build(); CelValidationResult parseResult = parser.parse(CelSource.newBuilder(testCase.source).build()); assertThat(parseResult.hasError()).isFalse(); @@ -259,8 +276,12 @@ public void parse_exprUnderMaxRecursionLimit_doesNotThrow( @Test public void parse_nodeLimitExceeded_throws() { CelParser parser = - CelParserImpl.newBuilder() - .setOptions(CelOptions.newBuilder().maxParseExpressionNodeCount(2).build()) + newParserBuilder() + .setOptions( + CelOptions.newBuilder() + .enablePrattParser(enablePrattParser) + .maxParseExpressionNodeCount(2) + .build()) .build(); CelValidationResult parseResult = parser.parse("a + b + c"); @@ -273,9 +294,13 @@ public void parse_nodeLimitExceeded_throws() { @Test public void parse_macroExpansionNodeLimitExceeded_throws() { CelParser parser = - CelParserImpl.newBuilder() + newParserBuilder() .setStandardMacros(CelStandardMacro.STANDARD_MACROS) - .setOptions(CelOptions.newBuilder().maxParseExpressionNodeCount(5).build()) + .setOptions( + CelOptions.newBuilder() + .enablePrattParser(enablePrattParser) + .maxParseExpressionNodeCount(5) + .build()) .build(); CelValidationResult parseResult = parser.parse("[1, 2, 3, 4, 5].map(x, x * 2)"); @@ -295,9 +320,13 @@ public void parse_macroExpansionNodeLimitExceeded_throws() { @Test public void parse_macroExpansionNodeLimitNotExceeded_success() throws CelValidationException { CelParser parser = - CelParserImpl.newBuilder() + newParserBuilder() .setStandardMacros(CelStandardMacro.STANDARD_MACROS) - .setOptions(CelOptions.newBuilder().maxParseExpressionNodeCount(100).build()) + .setOptions( + CelOptions.newBuilder() + .enablePrattParser(enablePrattParser) + .maxParseExpressionNodeCount(100) + .build()) .build(); CelValidationResult parseResult = parser.parse("[1, 2, 3, 4, 5].map(x, x * 2)"); assertThat(parseResult.hasError()).isFalse(); @@ -313,7 +342,7 @@ public void parse_macroExpansionNodeLimitNotExceeded_success() throws CelValidat @TestParameters("{expression: 'A.filter(a?b, c)'}") public void parse_macroArgumentContainsSyntaxError_throws(String expression) { CelParser parser = - CelParserImpl.newBuilder() + newParserBuilder() .setStandardMacros( ImmutableSet.builder() .addAll(CelStandardMacro.STANDARD_MACROS) @@ -324,13 +353,13 @@ public void parse_macroArgumentContainsSyntaxError_throws(String expression) { CelValidationResult parseResult = parser.parse(expression); assertThat(parseResult.hasError()).isTrue(); - assertThat(parseResult.getErrorString()).containsMatch("ERROR: .*mismatched input ','"); + assertThat(parseResult.getErrorString()).contains("ERROR: "); assertThrows(CelValidationException.class, parseResult::getAst); } @Test public void toParserBuilder_isNewInstance() { - CelParserBuilder celParserBuilder = CelParserFactory.standardCelParserBuilder(); + CelParserBuilder celParserBuilder = newParserBuilder(); CelParserImpl celParser = (CelParserImpl) celParserBuilder.build(); CelParserImpl.Builder newParserBuilder = (CelParserImpl.Builder) celParser.toParserBuilder(); @@ -340,7 +369,7 @@ public void toParserBuilder_isNewInstance() { @Test public void toParserBuilder_isImmutable() { - CelParserBuilder originalParserBuilder = CelParserFactory.standardCelParserBuilder(); + CelParserBuilder originalParserBuilder = newParserBuilder(); CelParserImpl celParser = (CelParserImpl) originalParserBuilder.build(); originalParserBuilder.addLibraries(new CelParserLibrary() {}); @@ -352,7 +381,7 @@ public void toParserBuilder_isImmutable() { @Test public void toParserBuilder_collectionProperties_copied() { CelParserBuilder celParserBuilder = - CelParserFactory.standardCelParserBuilder() + newParserBuilder() .setStandardMacros(CelStandardMacro.STANDARD_MACROS) .addMacros( CelMacro.newGlobalMacro( diff --git a/parser/src/test/java/dev/cel/parser/CelParserParameterizedTest.java b/parser/src/test/java/dev/cel/parser/CelParserParameterizedTest.java index 7c364cbb9..7e19e24f8 100644 --- a/parser/src/test/java/dev/cel/parser/CelParserParameterizedTest.java +++ b/parser/src/test/java/dev/cel/parser/CelParserParameterizedTest.java @@ -14,10 +14,13 @@ package dev.cel.parser; +import static com.google.common.collect.ImmutableMap.toImmutableMap; +import static com.google.common.truth.Truth.assertThat; import dev.cel.expr.ParsedExpr; import dev.cel.expr.SourceInfo; -import com.google.common.collect.ImmutableSet; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; import com.google.protobuf.TextFormat; import com.google.testing.junit.testparameterinjector.TestParameterInjector; import dev.cel.common.CelAbstractSyntaxTree; @@ -28,28 +31,66 @@ import dev.cel.common.CelValidationResult; import dev.cel.common.ast.CelConstant; import dev.cel.common.ast.CelExpr; -import dev.cel.extensions.CelOptionalLibrary; import dev.cel.testing.BaselineTestCase; import dev.cel.testing.CelDebug; import dev.cel.testing.CelExprKindAndIdAdorner; import dev.cel.testing.CelLocationAdorner; +import java.util.Map; import java.util.Optional; +import java.util.function.Function; import org.junit.Test; import org.junit.runner.RunWith; /** Invokes parser tests and compares their output against baseline files. */ @RunWith(TestParameterInjector.class) public final class CelParserParameterizedTest extends BaselineTestCase { - private static final CelParser PARSER = - CelParserFactory.standardCelParserBuilder() - .setStandardMacros( - ImmutableSet.builder() - .addAll(CelStandardMacro.STANDARD_MACROS) - .add(CelStandardMacro.EXISTS_ONE_NEW) - .build()) - .addLibraries(CelOptionalLibrary.INSTANCE) - .addMacros( - CelMacro.newGlobalVarArgMacro("noop_macro", (a, b, c) -> Optional.empty()), + + private static final CelOptions OPTIONS = + CelOptions.current() + .populateMacroCalls(true) + .enableOptionalSyntax(true) + .enableQuotedIdentifierSyntax(true) + .enableHiddenAccumulatorVar(true) + .build(); + + private static final CelOptions OPTIONS_MAX_RECURSION_DEPTH_32 = + OPTIONS.toBuilder().maxParseRecursionDepth(32).build(); + + private static final CelOptions OPTIONS_NO_OPTIONAL_SYNTAX = + OPTIONS.toBuilder().enableOptionalSyntax(false).build(); + + private static final CelOptions OPTIONS_QUOTED_IDENTIFIER_SYNTAX = + OPTIONS.toBuilder().enableQuotedIdentifierSyntax(true).build(); + + private static final CelOptions OPTIONS_NO_QUOTED_IDENTIFIER_SYNTAX = + OPTIONS.toBuilder().enableQuotedIdentifierSyntax(false).build(); + + private static final CelOptions OPTIONS_MAX_CODE_POINT_SIZE_5 = + OPTIONS.toBuilder().maxExpressionCodePointSize(5).build(); + + private static final CelOptions OPTIONS_MAX_NODE_COUNT_2 = + OPTIONS.toBuilder().maxParseExpressionNodeCount(2).build(); + + private static final CelOptions OPTIONS_MAX_ERROR_RECOVERY_LIMIT_2 = + OPTIONS.toBuilder().maxParseErrorRecoveryLimit(2).build(); + + private static final CelOptions OPTIONS_OLD_ACCU_VAR = + OPTIONS.toBuilder().enableHiddenAccumulatorVar(false).build(); + + private static final ImmutableMap MACROS = + ImmutableMap.builder() + .putAll( + CelStandardMacro.STANDARD_MACROS.stream() + .map(CelStandardMacro::getDefinition) + .collect(toImmutableMap(CelMacro::getKey, Function.identity()))) + .put( + CelStandardMacro.EXISTS_ONE_NEW.getDefinition().getKey(), + CelStandardMacro.EXISTS_ONE_NEW.getDefinition()) + .put( + "noop_macro", + CelMacro.newGlobalVarArgMacro("noop_macro", (a, b, c) -> Optional.empty())) + .put( + "get_constant_macro", CelMacro.newGlobalMacro( "get_constant_macro", 0, @@ -59,215 +100,507 @@ public final class CelParserParameterizedTest extends BaselineTestCase { .setId(1) .setConstant(CelConstant.ofValue(10L)) .build()))) - .setOptions( - CelOptions.current() - .populateMacroCalls(true) - .enableHiddenAccumulatorVar(true) - .build()) - .build(); + .buildOrThrow(); - private static final CelParser PARSER_WITH_OLD_ACCU_VAR = - PARSER - .toParserBuilder() - .setOptions( - CelOptions.current() - .populateMacroCalls(true) - .enableHiddenAccumulatorVar(false) - .build()) - .build(); + private static final class ParseOutput { + final String pOutput; + final String lOutput; + final String mOutput; + final String errorMessage; + + ParseOutput(String pOutput, String lOutput, String mOutput, String errorMessage) { + this.pOutput = pOutput; + this.lOutput = lOutput; + this.mOutput = mOutput; + this.errorMessage = errorMessage; + } + + boolean isError() { + return errorMessage != null; + } + } + + private ParseOutput parse( + CelOptions options, + Map macros, + String expression, + boolean validateParseOutput) { + CelParser parser = + CelParserImpl.newBuilder().setOptions(options).addMacros(macros.values()).build(); + CelSource source = CelSource.newBuilder(expression).setDescription("").build(); + CelValidationResult parseResult = parser.parse(source); + + try { + CelProtoAbstractSyntaxTree protoAst = + CelProtoAbstractSyntaxTree.fromCelAst(parseResult.getAst()); + ParsedExpr parsedExpr = protoAst.toParsedExpr(); + String pOutput = null; + String lOutput = null; + if (validateParseOutput) { + pOutput = + CelDebug.toAdornedDebugString(parsedExpr.getExpr(), new CelExprKindAndIdAdorner()); + lOutput = + CelDebug.toAdornedDebugString( + parsedExpr.getExpr(), new CelLocationAdorner(parsedExpr.getSourceInfo())); + } + String mOutput = + CelExprKindAndIdAdorner.convertMacroCallsToString(parsedExpr.getSourceInfo()); + return new ParseOutput(pOutput, lOutput, mOutput, null); + } catch (CelValidationException e) { + return new ParseOutput(null, null, null, e.getMessage()); + } + } @Test - public void parser() { - runTest(PARSER, "x * 2"); - runTest(PARSER, "x * 2u"); - runTest(PARSER, "x * 2.0"); - runTest(PARSER, "\"\\u2764\""); - runTest(PARSER, "\"\u2764\""); - runTest(PARSER, "! false"); - runTest(PARSER, "-a"); - runTest(PARSER, "a.b(5)"); - runTest(PARSER, "a[3]"); - runTest(PARSER, "SomeMessage{foo: 5, bar: \"xyz\"}"); - runTest(PARSER, "[3, 4, 5]"); - runTest(PARSER, "{foo: 5, bar: \"xyz\"}"); - runTest(PARSER, "a > 5 && a < 10"); - runTest(PARSER, "a < 5 || a > 10"); - runTest(PARSER, "\"abc\" + \"def\""); - runTest(PARSER, "\"A\""); - runTest(PARSER, "true"); - runTest(PARSER, "false"); - runTest(PARSER, "0"); - runTest(PARSER, "42"); - runTest(PARSER, "0u"); - runTest(PARSER, "23u"); - runTest(PARSER, "24u"); - runTest(PARSER, "0xAu"); - runTest(PARSER, "-0xA"); - runTest(PARSER, "0xA"); - runTest(PARSER, "-1"); - runTest(PARSER, "4--4"); - runTest(PARSER, "4--4.1"); - runTest(PARSER, "b\"abc\""); - runTest(PARSER, "23.39"); - runTest(PARSER, "!a"); - runTest(PARSER, "null"); - runTest(PARSER, "a"); - runTest(PARSER, "a?b:c"); - runTest(PARSER, "a || b"); - runTest(PARSER, "a || b || c || d || e || f"); - runTest(PARSER, "a && b"); - runTest(PARSER, "a && b && c && d && e && f && g"); - runTest(PARSER, "a && b && c && d || e && f && g && h"); - runTest(PARSER, "a + b"); - runTest(PARSER, "a - b"); - runTest(PARSER, "a * b"); - runTest(PARSER, "a / b"); - runTest(PARSER, "a % b"); - runTest(PARSER, "a in b"); - runTest(PARSER, "a == b"); - runTest(PARSER, "a != b"); - runTest(PARSER, "a > b"); - runTest(PARSER, "a >= b"); - runTest(PARSER, "a < b"); - runTest(PARSER, "a <= b"); - runTest(PARSER, "a.b"); - runTest(PARSER, "a.b.c"); - runTest(PARSER, "a[b]"); - runTest(PARSER, "foo{ }"); - runTest(PARSER, "foo{ a:b }"); - runTest(PARSER, "foo{ a:b, c:d }"); - runTest(PARSER, "{}"); - runTest(PARSER, "{a:b, c:d}"); - runTest(PARSER, "[]"); - runTest(PARSER, "[a]"); - runTest(PARSER, "[a, b, c]"); - runTest(PARSER, "(a)"); - runTest(PARSER, "((a))"); - runTest(PARSER, "a()"); - runTest(PARSER, "a(b)"); - runTest(PARSER, "a(b, c)"); - runTest(PARSER, "a.b()"); - runTest(PARSER, "a.b(c)"); - runTest(PARSER, "aaa.bbb(ccc)"); - runTest(PARSER, "has(m.f)"); - runTest(PARSER, "m.exists_one(v, f)"); - runTest(PARSER, "m.existsOne(v, f)"); - runTest(PARSER, "m.map(v, f)"); - runTest(PARSER, "m.map(v, p, f)"); - runTest(PARSER, "m.filter(v, p)"); - runTest(PARSER, "[] + [1,2,3,] + [4]"); - runTest(PARSER, "{1:2u, 2:3u}"); - runTest(PARSER, "TestAllTypes{single_int32: 1, single_int64: 2}"); - runTest(PARSER, "size(x) == x.size()"); - runTest(PARSER, "\"\\\"\""); - runTest(PARSER, "[1,3,4][0]"); - runTest(PARSER, "x[\"a\"].single_int32 == 23"); - runTest(PARSER, "x.single_nested_message != null"); - runTest(PARSER, "false && !true || false ? 2 : 3"); - runTest(PARSER, "b\"abc\" + B\"def\""); - runTest(PARSER, "1 + 2 * 3 - 1 / 2 == 6 % 1"); - runTest(PARSER, "---a"); - runTest(PARSER, "\"\\xC3\\XBF\""); - runTest(PARSER, "\"\\303\\277\""); - runTest(PARSER, "\"hi\\u263A \\u263Athere\""); - runTest(PARSER, "\"\\U000003A8\\?\""); - runTest(PARSER, "\"\\a\\b\\f\\n\\r\\t\\v'\\\"\\\\\\? Legal escapes\""); - runTest(PARSER, "'😁' in ['😁', '😑', '😦']"); + public void parser_literals() { + // Null + runTest("null"); + + // Boolean + runTest("true"); + runTest("false"); + + // Int + runTest("0"); + runTest("42"); + runTest("0xF"); + runTest("0x2A"); + runTest("-1"); + runTest("-42"); + runTest("0xFFFFFFFFFFFFFFFFF"); + runTest("9223372036854775807"); // Long.MAX_VALUE + runTest("-9223372036854775808"); // Long.MIN_VALUE + runTest("-(9223372036854775808)"); // error + runTest("123a"); + + // Uint + runTest("0u"); + runTest("23u"); + runTest("24u"); + runTest("0xAu"); + runTest("-0xA"); + runTest("0xA"); + runTest("0xFu"); + runTest("0xFFFFFFFFFFFFFFFFFu"); + runTest("123u_"); + + // Double + runTest("3.14"); + runTest("23.39"); + runTest("1."); + runTest("1e+5"); + runTest("1e-5"); + runTest("2.5e+10"); + runTest("2.5e-10"); + runTest("1.99e90000009"); + runTest("1e"); + runTest("1e+"); + runTest("1e-"); + runTest("2.5e"); + runTest("2.5e+"); + runTest("2.5e-"); + runTest("((1e))"); + runTest("0x123z"); + + // String + runTest("'hello'"); + runTest("\"A\""); + runTest("'''hello\nworld'''"); + runTest("\"\\u2764\""); + runTest("\"\u2764\""); + runTest("\"\\\"\""); + runTest("\"\\xC3\\XBF\""); + runTest("\"\\303\\277\""); + runTest("\"hi\\u263A \\u263Athere\""); + runTest("\"\\U000003A8\\?\""); + runTest("\"\\a\\b\\f\\n\\r\\t\\v'\\\"\\\\\\? Legal escapes\""); + runTest("\"\"\"hello\nworld\"\"\""); + runTest("r\"\"\"hello\nworld\"\"\""); + runTest("\"\"\"\"\"\""); + runTest("''''''"); + runTest("\"\"\"hello\\\"\"\"world\"\"\""); + runTest("'''hello\\'''world'''"); + runTest("\"\\xFh\""); + runTest("\"\\a\\b\\f\\n\\r\\t\\v\\'\\\"\\\\\\? Illegal escape \\>\""); runTest( - PARSER, - // Note, the ANTLR parse stack may recurse much more deeply and permit - // more detailed expressions than the visitor can recurse over in - // practice. - "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['just fine'],[1],[2],[3],[4],[5]]]]]]]" - + "]]]]]]]]]]]]]]]]]]]]]]]]", - false); // parse output not validated as it is too large. - runTest(PARSER, "x.filter(y, y.filter(z, z > 0))"); - runTest(PARSER, "has(a.b).filter(c, c)"); - runTest(PARSER, "x.filter(y, y.exists(z, has(z.a)) && y.exists(z, has(z.b)))"); - runTest(PARSER, "noop_macro(123)"); - runTest(PARSER, "get_constant_macro()"); - runTest(PARSER, "a.?b[?0] && a[?c]"); - runTest(PARSER, "{?'key': value}"); - runTest(PARSER, "Msg{?field: value}"); - runTest(PARSER, "[?a, ?b]"); - runTest(PARSER, "[?a[?b]]"); + " '\ud83d\ude01' in ['\ud83d\ude01', '\ud83d\ude11', '\ud83d\ude26']\n" + + "\t\t\t&& in.\ud83d\ude01"); + runTest("\"\"\"hello\nworld"); + runTest("'''hello\nworld"); + runTest("r\"\"\"hello\nworld"); + runTest("\"hello\nworld\""); + runTest("'hello\nworld'"); + runTest("r\"hello\nworld\""); + runTest("`hello\nworld`"); + runTest("\"hello\rworld\""); + runTest("'unterminated"); + + // Bytes + runTest("b'abc'"); + runTest("b\"abc\""); + runTest("b\"\"\"hello\nworld"); + runTest("b\"hello\nworld\""); + runTest("rb\"hello\nworld\""); + runTest("br'abc'"); + runTest("bR'abc'"); + runTest("Br'abc'"); + runTest("BR'abc'"); + runAntlrTest(OPTIONS, "rb'abc'"); + runAntlrTest(OPTIONS, "rB'abc'"); + runAntlrTest(OPTIONS, "Rb'abc'"); + runAntlrTest(OPTIONS, "RB'abc'"); + runTest("br'a\\'b'"); + runAntlrTest(OPTIONS, "rb'a\\'b'"); + } + + @Test + @SuppressWarnings("InlineMeInliner") // String.repeat is unavailable under Java 8 + public void parser_core_syntax() { + // Identifiers + runTest("a"); + runTest("foo"); + + // Parentheses + runTest("(a)"); + runTest("((a))"); + runTest("(((1 + 2))) * 3"); + + // Lists + runTest("[]"); + runTest("[a]"); + runTest("[a, b, c]"); + runTest("[1, 2, 3]"); + runTest("[3, 4, 5]"); + runTest("[3, 4, 5,]"); + runTest("[?a, b]"); + runTest("[?a, ?b]"); + runTest("[?a[?b]]"); + + // Maps + runTest("{}"); + runTest("{a:b, c:d}"); + runTest("{foo: 5, bar: \"xyz\"}"); + runTest("{foo: 5, bar: \"xyz\", }"); + runTest("{\"a\": 1, \"b\": 2}"); + runTest("{1:2u, 2:3u}"); + runTest("{?a: b}"); + runTest("{?'key': value}"); + + // Messages + runTest("foo{ }"); + runTest("foo{ a:b }"); + runTest("foo{ a:b, c:d }"); + runTest("SomeMessage{foo: 5, bar: \"xyz\"}"); + runTest("TestAllTypes{single_int32: 1, single_int64: 2}"); + runTest("MyType{foo: 1, bar: 'baz'}"); + runTest("Message{`in`: true}"); + runTest("Msg{?field: value}"); + runTest("foo.bar.MyType{ }"); + runTest("foo.bar.MyType{ a:b }"); + runTest(".foo.bar.MyType{ a:b }"); + runTest("a.b.c.d.Message{ foo: 1, bar: 'baz' }"); + + // Field selection + runTest("a.b"); + runTest("a.b.c"); + runTest("a.?b"); + runTest("a.`b-c`"); + runTest("a.`b c`"); + runTest("a.`b.c`"); + runTest("a.`in`"); + runTest("a.`/foo`"); + runTest("a.`my-var`"); + + // Indexing + runTest("a[b]"); + runTest("a[0]"); + runTest("a[3]"); + runTest("[1,3,4][0]"); + runTest("a[?0]"); + + // Function calls + runTest("a()"); + runTest("a(b)"); + runTest("a(b, c)"); + runTest("a.b()"); + runTest("a.b(c)"); + runTest("a.b(5)"); + runTest("aaa.bbb(ccc)"); + runTest("a.foo(1, 2)"); + + // Unary operators + runTest("!a"); + runTest("!x"); + runTest("! false"); + runTest("-a"); + runTest("---a"); + + // Arithmetic operators + runTest("x * 2"); + runTest("x * 2u"); + runTest("x * 2.0"); + runTest("a * b"); + runTest("a / b"); + runTest("a % b"); + runTest("a + b"); + runTest("a - b"); + runTest("4--4"); + runTest("4--4.1"); + runTest("\"abc\" + \"def\""); + runTest("b\"abc\" + B\"def\""); + runTest("[] + [1,2,3,] + [4]"); + runTest("1 + 2 * 3"); + + // Comparison operators + runTest("a == b"); + runTest("a != b"); + runTest("a < b"); + runTest("a <= b"); + runTest("a > b"); + runTest("a >= b"); + runTest("a in b"); + runTest("\"\ud83d\ude01\" in [\"\ud83d\ude01\", \"\ud83d\ude11\", \"\ud83d\ude26\"]"); + runTest("size(x) == x.size()"); + runTest("x.single_nested_message != null"); + + // Logical operators + runTest("a && b"); + runTest("a && b && c"); + runTest("a && b && c && d && e && f && g"); + runTest("a > 5 && a < 10"); + runTest("a || b"); + runTest("a || b || c || d || e || f"); + runTest("a < 5 || a > 10"); + runTest("a && b && c && d || e && f && g && h"); + + // Conditional operator + runTest("a?b:c"); + runTest("cond ? 1 : 2"); + runTest("false && !true || false ? 2 : 3"); + runTest(OPTIONS_MAX_RECURSION_DEPTH_32, Strings.repeat("true ? 1 : ", 31) + "1", false); + runAntlrTest(OPTIONS_MAX_RECURSION_DEPTH_32, Strings.repeat("!-", 15) + "x"); + + // Complex expressions + runTest("1 + 2 * 3 - 1 / 2 == 6 % 1"); + runTest("x[\"a\"].single_int32 == 23"); + runTest("a.?b[?0] && a[?c]"); runTest( - CelParserImpl.newBuilder() - .setOptions(CelOptions.current().enableReservedIds(false).build()) - .build(), - "while"); - CelParser parserWithQuotedFields = - CelParserImpl.newBuilder() - .setOptions(CelOptions.current().enableQuotedIdentifierSyntax(true).build()) - .build(); - runTest(parserWithQuotedFields, "foo.`bar`"); - runTest(parserWithQuotedFields, "foo.`bar-baz`"); - runTest(parserWithQuotedFields, "foo.`bar baz`"); - runTest(parserWithQuotedFields, "foo.`bar.baz`"); - runTest(parserWithQuotedFields, "foo.`bar/baz`"); - runTest(parserWithQuotedFields, "foo.`bar_baz`"); - runTest(parserWithQuotedFields, "foo.`in`"); - runTest(parserWithQuotedFields, "Struct{`in`: false}"); + OPTIONS, + "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['just" + + " fine'],[1],[2],[3],[4],[5]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]", + false); + + // Whitespace and comments + runTest("// comment\na"); + runTest("a // comment"); + runTest("a\n// comment\n+ b"); + runTest("a / // comment\n b"); + runTest("[\n 1, // comment\n 2,\n]"); + + // Reserved IDs disabled + runTest(OPTIONS.toBuilder().enableReservedIds(false).build(), "while"); + + // Quoted field specifiers + runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "foo.`bar`"); + runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "foo.`bar-baz`"); + runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "foo.`bar baz`"); + runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "foo.`bar.baz`"); + runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "foo.`bar/baz`"); + runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "foo.`bar_baz`"); + runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "foo.`in`"); + runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "Struct{`in`: false}"); } @Test - public void parser_legacyAccuVar() { - runTest(PARSER_WITH_OLD_ACCU_VAR, "x * 2"); - runTest(PARSER_WITH_OLD_ACCU_VAR, "has(m.f)"); - runTest(PARSER_WITH_OLD_ACCU_VAR, "m.exists_one(v, f)"); - runTest(PARSER_WITH_OLD_ACCU_VAR, "m.all(v, f)"); - runTest(PARSER_WITH_OLD_ACCU_VAR, "m.map(v, f)"); - runTest(PARSER_WITH_OLD_ACCU_VAR, "m.map(v, p, f)"); - runTest(PARSER_WITH_OLD_ACCU_VAR, "m.filter(v, p)"); + public void parser_macros() { + runTest("has(m.f)"); + runTest("has(a.b)"); + runTest("has(m)"); + + runTest("m.all(v, f)"); + runTest("[1, 2].all(x, x > 0)"); + + runTest("m.exists(v, f)"); + + runTest("m.exists_one(v, f)"); + runTest("m.existsOne(v, f)"); + runTest("[].existsOne(__result__, __result__)"); + + runTest("m.map(v, f)"); + runTest("m.map(v, p, f)"); + runTest("m.map(__result__, __result__)"); + + runTest("m.filter(v, p)"); + runTest("m.filter(__result__, false)"); + runTest("m.filter(a.b, false)"); + + // Nested / Chained macros + runTest("x.filter(y, y.filter(z, z > 0))"); + runTest("has(a.b).filter(c, c)"); + runTest("x.filter(y, y.exists(z, has(z.a)) && y.exists(z, has(z.b)))"); + runTest("(has(a.b) || has(c.d)).string()"); + runTest("has(a.b).asList().exists(c, c)"); + runTest("[has(a.b), has(c.d)].exists(e, e)"); + + // Custom macros + runTest("noop_macro(123)"); + runTest("get_constant_macro()"); } @Test + @SuppressWarnings("InlineMeInliner") // String.repeat is unavailable under Java 8 public void parser_errors() { - runTest(PARSER, "*@a | b"); - runTest(PARSER, "a | b"); - runTest(PARSER, "?"); - runTest(PARSER, "1 + $"); - runTest(PARSER, "1.all(2, 3)"); - runTest(PARSER, "1.exists(2, 3)"); - runTest(PARSER, "[].all(__result__, x)"); - runTest(PARSER, "[].exists(__result__, x)"); - runTest(PARSER, "[].exists_one(__result__, x)"); - runTest(PARSER, "[].map(__result__, x, x)"); - runTest(PARSER, "[].filter(__result__, x)"); - runTest(PARSER, "[].all(.x, x)"); - runTest(PARSER, "[].exists(.x, x)"); - runTest(PARSER, "[].exists_one(.x, x)"); - runTest(PARSER, "[].map(.x, x, x)"); - runTest(PARSER, "[].filter(.x, x)"); - runTest(PARSER, "1 + +"); - runTest(PARSER, "\"\\xFh\""); - runTest(PARSER, "\"\\a\\b\\f\\n\\r\\t\\v\\'\\\"\\\\\\? Illegal escape \\>\""); - runTest(PARSER, "'\uD800'"); - runTest(PARSER, "'\uDFFF'"); - runTest(PARSER, "r\"\\\uD800\""); - - runTest(PARSER, "as"); - runTest(PARSER, "break"); - runTest(PARSER, "const"); - runTest(PARSER, "continue"); - runTest(PARSER, "else"); - runTest(PARSER, "for"); - runTest(PARSER, "function"); - runTest(PARSER, "if"); - runTest(PARSER, "import"); - runTest(PARSER, "in"); - runTest(PARSER, "let"); - runTest(PARSER, "loop"); - runTest(PARSER, "package"); - runTest(PARSER, "namespace"); - runTest(PARSER, "return"); - runTest(PARSER, "var"); - runTest(PARSER, "void"); - runTest(PARSER, "while"); - runTest(PARSER, "[1, 2, 3].map(var, var * var)"); - runTest(PARSER, "'😁' in ['😁', '😑', '😦']\n" + " && in.😁"); + // Lexical errors + runTest("*@a | b"); + runTest("((@))"); + runTest("1 + $"); + runTest( + "\u00f3\u00a0\u00a2\n" + + "\t\t\u00f3\u00a00\u00a0\n" + + "\t\t\u007f0\"\"\"\\\"\"\\\"\"\"\\\"\"\\\"\"\"\\\"\"\\\"\"\"\\\"\"\\\"\"\"\\\"\\\"\"\"\\\"\"\\\"\"\"\\\"\"\\\"\"\"\\\"\"\\\"\"\"\\\"!\\\"\"\"\\\"\"\\\"\"\"\\\"\"\\\""); + runTest("'\\udead' == '\\ufffd'"); + runTest("a | b"); + runTest("'3# < 10\" '& tru ^^"); + runTest("'\uD800'"); + runTest("'\uDFFF'"); + runTest("r\"\\\uD800\""); + + // Unexpected tokens + runTest("1 + +"); + runTest("?"); + runTest("a ? b ((?))"); + runTest("a ? b @"); + runTest( + "-[-1--1--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1\n" + + "\t\t--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--3--1--1--0--1--1--1--1--0--1--1--1\n" + + "\t\t--3-[-1--1--1--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1\n" + + "\t\t--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1\n" + + "\t\t--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1\n" + + "\t\t--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1\n" + + "\t\t--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--3--1--1--0--1--1--1--1--0--1--1--1\n" + + "\t\t--3-[-1--1--1--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1\n" + + "\t\t--3-[-1--1--1--1---1-1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1\n" + + "\t\t--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1\n" + + "\t\t--1--1---1--1-\u00c01--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1\n" + + "\t\t--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1\n" + + "\t\t--1--1---1--1--1--0--1--1--1--1--0--3--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1\n" + + "\t\t--1--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1\n" + + "\t\t--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1\n" + + "\t\t--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1\n" + + "\t\t--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1\n" + + "\t\t--1--1---1--1--1--0--1--1--1--1--0--3--1--1--0--1\n" + + "\t\t--1--0--1--1--1--3-[-1--1--1--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1\n" + + "\t\t--1--0--1--1--1--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1\n" + + "\t\t--1--0--1--1--1--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1\n" + + "\t\t--1--0--1--1--1--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1\n" + + "\t\t--1--0--1--1--1--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--3--1--1--0--1--1--1\n" + + "\t\t--1--0--1--1--1--3-[-1--1--1--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1\n" + + "\t\t--1--0--1--1--1--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1\n" + + "\t\t--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1\n" + + "\t\t--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1\n" + + "\t\t--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1\n" + + "\t\t--1---1--1--1--0--1--1--1--1--0--3--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1\n" + + "\t\t--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1\n" + + "\t\t--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1\n" + + "\t\t--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1\n" + + "\t\t--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1\n" + + "\t\t--1---1--1--1--0--1--1--1--1--0--3--1--1--0--1"); + + // Reserved identifiers + runTest( + "as break const continue else for function if import in let loop package namespace" + + " return var void while"); + runTest("as"); + runTest("break"); + runTest("const"); + runTest("continue"); + runTest("else"); + runTest("for"); + runTest("function"); + runTest("if"); + runTest("import"); + runTest("in"); + runTest("let"); + runTest("loop"); + runTest("package"); + runTest("namespace"); + runTest("return"); + runTest("var"); + runTest("void"); + runTest("while"); + runTest("[1, 2, 3].map(var, var * var)"); + runTest("'😁' in ['😁', '😑', '😦']\n && in.😁"); + + // Incomplete expressions + runTest("1 +"); + runTest("--"); + runTest("{"); + runTest("0x"); + + // Unexpected token after expression + runTest("TestAllTypes(){}"); + runTest("TestAllTypes{}()"); + runTest("TestAllTypes(){single_int32: 1, single_int64: 2}"); + runTest("1 + 2\n3 +"); + + // Member selection errors + runTest("{\"a\": 1}.\"a\""); + runTest("self.true == 1"); + + // Map syntax errors + runTest("{a}"); + runTest("{:a}"); + + // Message syntax errors + runTest("func{{a}}"); + runTest("msg{:a}"); + runTest("ind[a{b}]"); + runTest("x{?."); + runTest("x{."); + runTest("t{>C}"); + runTest("has([(has(("); + + // Macro errors + runTest("1.all(2, 3)"); + runTest("1.exists(2, 3)"); + runTest("[].all(__result__, x)"); + runTest("[].exists(__result__, x)"); + runTest("[].exists_one(__result__, x)"); + runTest("[].map(__result__, x, x)"); + runTest("[].filter(__result__, x)"); + runTest("[].all(.x, x)"); + runTest("[].exists(.x, x)"); + runTest("[].exists_one(.x, x)"); + runTest("[].map(.x, x, x)"); + runTest("[].filter(.x, x)"); + + // Unsupported optional syntax + runTest(OPTIONS_NO_OPTIONAL_SYNTAX, "a.?b && a[?b]"); + runTest(OPTIONS_NO_OPTIONAL_SYNTAX, "[?a, ?b]"); + runTest(OPTIONS_NO_OPTIONAL_SYNTAX, "Msg{?field: value} && {?'key': value}"); + + // Unsupported quoted identifier syntax + runTest(OPTIONS_NO_QUOTED_IDENTIFIER_SYNTAX, "a.`b-c`"); + runTest(OPTIONS_NO_QUOTED_IDENTIFIER_SYNTAX, "a.`b.c`"); + runTest(OPTIONS_NO_QUOTED_IDENTIFIER_SYNTAX, "a.`in`"); + runTest(OPTIONS_NO_QUOTED_IDENTIFIER_SYNTAX, "a.`/foo`"); + runTest(OPTIONS_NO_QUOTED_IDENTIFIER_SYNTAX, "Message{`in`: true}"); + runTest(OPTIONS_NO_QUOTED_IDENTIFIER_SYNTAX, "foo.`bar`"); + runTest(OPTIONS_NO_QUOTED_IDENTIFIER_SYNTAX, "Struct{`bar`: false}"); + runTest(OPTIONS_NO_QUOTED_IDENTIFIER_SYNTAX, "has(.`.`"); + + // Unsupported quoted identifier location + runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "`b-c`"); + runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "`b-c`()"); + runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "a.`$b`"); + runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "a.`b.c`()"); + runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "`bar`"); + runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "foo.``"); + runTest(OPTIONS_QUOTED_IDENTIFIER_SYNTAX, "foo.`$bar`"); + + // Recursion limit exceeded runTest( - PARSER, + OPTIONS, "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" + "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" + "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" @@ -276,37 +609,108 @@ public void parser_errors() { + "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" + "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" + "]]]]]]"); - runTest(PARSER, "{\"a\": 1}.\"a\""); - runTest(PARSER, "1 + 2\n3 +"); - runTest(PARSER, "TestAllTypes(){single_int32: 1, single_int64: 2}"); - runTest(PARSER, "{"); - runTest(PARSER, "t{>C}"); - runTest(PARSER, "has([(has(("); - - CelParser parserWithoutOptionalSupport = - CelParserImpl.newBuilder() - .setOptions(CelOptions.current().enableOptionalSyntax(false).build()) - .build(); - runTest(parserWithoutOptionalSupport, "a.?b && a[?b]"); - runTest(parserWithoutOptionalSupport, "Msg{?field: value} && {?'key': value}"); - runTest(parserWithoutOptionalSupport, "[?a, ?b]"); + runTest( + OPTIONS_MAX_RECURSION_DEPTH_32, + "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\n" + + "\t\t\t[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['too many']]]]]]]]]]]]]]]]]]]]]]]]]]]]\n" + + "\t\t\t]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"); + runTest( + OPTIONS_MAX_RECURSION_DEPTH_32, + "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['not fine']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"); + runTest( + OPTIONS_MAX_RECURSION_DEPTH_32, + "a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.A.B.C.D.E.F.G.H"); + runTest( + OPTIONS_MAX_RECURSION_DEPTH_32, + "a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20]\n" + + "\t\t [21][22][23][24][25][26][27][28][29][30][31][32][33]"); + runTest( + OPTIONS_MAX_RECURSION_DEPTH_32, + "1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10\n" + + "\t\t+ 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20\n" + + "\t\t+ 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30\n" + + "\t\t+ 31 + 32 + 33 + 34"); + runTest( + OPTIONS_MAX_RECURSION_DEPTH_32, + "a < 1 < 2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < 10 < 11\n" + + "\t\t < 12 < 13 < 14 < 15 < 16 < 17 < 18 < 19 < 20 < 21\n" + + "\t\t\t < 22 < 23 < 24 < 25 < 26 < 27 < 28 < 29 < 30 < 31\n" + + "\t\t\t < 32 < 33"); + runTest( + OPTIONS_MAX_RECURSION_DEPTH_32, + "y!=y!=y!=y!=y!=y!=y!=y!=y!=-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y\n" + + "\t\t!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y\n" + + "\t\t!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y\n" + + "\t\t!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y\n" + + "\t\t!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y\n" + + "\t\t!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y"); + runTest( + OPTIONS_MAX_RECURSION_DEPTH_32, + "a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] !=\n" + + "\t\ta[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20]" + + " !=\n" + + "\t\ta[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20]" + + " !=\n" + + "\t\ta[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20]" + + " !=\n" + + "\t\ta[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20]" + + " !=\n" + + "\t\ta[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20]" + + " !=\n" + + "\t\ta[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20]" + + " !=\n" + + "\t\ta[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20]" + + " !=\n" + + "\t\ta[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20]" + + " !=\n" + + "\t\ta[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20]" + + " !=\n" + + "\t\ta[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20]" + + " !=\n" + + "\t\ta[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20]" + + " !=\n" + + "\t\ta[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20]" + + " !=\n" + + "\t\ta[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20]"); + runTest(OPTIONS_MAX_RECURSION_DEPTH_32, Strings.repeat("true ? 1 : ", 33) + "1"); + runTest(OPTIONS_MAX_RECURSION_DEPTH_32, Strings.repeat("!-", 16) + "!x"); + runTest(OPTIONS_MAX_CODE_POINT_SIZE_5, "123456"); + runTest(OPTIONS_MAX_NODE_COUNT_2, "1 + 2 + 3"); + runTest(OPTIONS_MAX_ERROR_RECOVERY_LIMIT_2, "[?, ?, ?]"); + runTest(OPTIONS_MAX_ERROR_RECOVERY_LIMIT_2, "[1 2 3 a b c]"); + } - CelParser parserWithQuotedFields = - CelParserImpl.newBuilder() - .setOptions(CelOptions.current().enableQuotedIdentifierSyntax(true).build()) - .build(); - runTest(parserWithQuotedFields, "`bar`"); - runTest(parserWithQuotedFields, "foo.``"); - runTest(parserWithQuotedFields, "foo.`$bar`"); + @Test + public void parser_legacyAccuVar() { + runAntlrTest(OPTIONS_OLD_ACCU_VAR, "x * 2"); + runAntlrTest(OPTIONS_OLD_ACCU_VAR, "has(m.f)"); + runAntlrTest(OPTIONS_OLD_ACCU_VAR, "m.exists_one(v, f)"); + runAntlrTest(OPTIONS_OLD_ACCU_VAR, "m.all(v, f)"); + runAntlrTest(OPTIONS_OLD_ACCU_VAR, "m.map(v, f)"); + runAntlrTest(OPTIONS_OLD_ACCU_VAR, "m.map(v, p, f)"); + runAntlrTest(OPTIONS_OLD_ACCU_VAR, "m.filter(v, p)"); + } - CelParser parserWithoutQuotedFields = - CelParserImpl.newBuilder() - .setStandardMacros(CelStandardMacro.HAS) - .setOptions(CelOptions.current().enableQuotedIdentifierSyntax(false).build()) - .build(); - runTest(parserWithoutQuotedFields, "foo.`bar`"); - runTest(parserWithoutQuotedFields, "Struct{`bar`: false}"); - runTest(parserWithoutQuotedFields, "has(.`.`"); + private void runAntlrTest(CelOptions options, String expression) { + testOutput().println("I: " + sanitizeForBaseline(expression)); + testOutput().println("=====>"); + + CelOptions antlrOptions = options.toBuilder().enablePrattParser(false).build(); + ParseOutput antlrResult = + parse(antlrOptions, MACROS, expression, /* validateParseOutput= */ true); + if (!antlrResult.isError()) { + testOutput().println("P: " + antlrResult.pOutput); + if (!Strings.isNullOrEmpty(antlrResult.lOutput)) { + testOutput().println("L: " + antlrResult.lOutput); + } + if (!Strings.isNullOrEmpty(antlrResult.mOutput)) { + testOutput().println("M: " + antlrResult.mOutput); + } + } else { + testOutput().println("E/A: " + sanitizeForBaseline(antlrResult.errorMessage)); + } + + testOutput().println(); } @Test @@ -314,53 +718,93 @@ public void source_info() throws Exception { runSourceInfoTest("[{}, {'field': true}].exists(i, has(i.field))"); } - private void runTest(CelParser parser, String expression) { - runTest(parser, expression, true); + private void runTest(String expression) { + runTest(OPTIONS, expression); } - private void runTest(CelParser parser, String expression, boolean validateParseOutput) { - testOutput().println("I: " + expression); + private void runTest(CelOptions options, String expression) { + runTest(options, expression, true); + } + + private void runTest(CelOptions options, String expression, boolean validateParseOutput) { + runTest(options, MACROS, expression, validateParseOutput); + } + + private void runTest( + CelOptions options, + Map macros, + String expression, + boolean validateParseOutput) { + testOutput().println("I: " + sanitizeForBaseline(expression)); testOutput().println("=====>"); - CelSource source = CelSource.newBuilder(expression).setDescription("").build(); - CelValidationResult parseResult = parser.parse(source); + ParseOutput antlrResult = + parse( + options.toBuilder().enablePrattParser(false).build(), + macros, + expression, + validateParseOutput); + ParseOutput prattResult = + parse( + options.toBuilder().enablePrattParser(true).build(), + macros, + expression, + validateParseOutput); - try { - CelProtoAbstractSyntaxTree protoAst = - CelProtoAbstractSyntaxTree.fromCelAst(parseResult.getAst()); - ParsedExpr parsedExpr = protoAst.toParsedExpr(); + assertThat(prattResult.isError()).isEqualTo(antlrResult.isError()); + if (!antlrResult.isError()) { if (validateParseOutput) { - testOutput() - .println( - "P: " - + CelDebug.toAdornedDebugString( - parsedExpr.getExpr(), new CelExprKindAndIdAdorner())); - String locationOutput = - CelDebug.toAdornedDebugString( - parsedExpr.getExpr(), new CelLocationAdorner(parsedExpr.getSourceInfo())); - if (!locationOutput.isEmpty()) { - testOutput().println("L: " + locationOutput); + assertThat(prattResult.pOutput).isEqualTo(antlrResult.pOutput); + testOutput().println("P: " + antlrResult.pOutput); + + assertThat(prattResult.lOutput).isEqualTo(antlrResult.lOutput); + if (!Strings.isNullOrEmpty(antlrResult.lOutput)) { + testOutput().println("L: " + antlrResult.lOutput); } } - String macroOutput = - CelExprKindAndIdAdorner.convertMacroCallsToString(parsedExpr.getSourceInfo()); - if (!macroOutput.isEmpty()) { - testOutput().println("M: " + macroOutput); + assertThat(prattResult.mOutput).isEqualTo(antlrResult.mOutput); + if (!Strings.isNullOrEmpty(antlrResult.mOutput)) { + testOutput().println("M: " + antlrResult.mOutput); } - } catch (CelValidationException e) { - testOutput().println("E: " + e.getMessage()); + } else { + testOutput().println("E/A: " + sanitizeForBaseline(antlrResult.errorMessage)); + testOutput().println("E/P: " + sanitizeForBaseline(prattResult.errorMessage)); } testOutput().println(); } private void runSourceInfoTest(String expression) throws Exception { - CelAbstractSyntaxTree ast = PARSER.parse(expression).getAst(); - SourceInfo sourceInfo = - CelProtoAbstractSyntaxTree.fromCelAst(ast).toParsedExpr().getSourceInfo(); testOutput().println("I: " + expression); testOutput().println("=====>"); - testOutput().println("S: " + TextFormat.printer().printToString(sourceInfo)); + CelParser antlrParser = + CelParserImpl.newBuilder() + .setOptions(OPTIONS.toBuilder().enablePrattParser(false).build()) + .addMacros(MACROS.values()) + .build(); + CelParser prattParser = + CelParserImpl.newBuilder() + .setOptions(OPTIONS.toBuilder().enablePrattParser(true).build()) + .addMacros(MACROS.values()) + .build(); + + CelAbstractSyntaxTree antlrAst = antlrParser.parse(expression).getAst(); + CelAbstractSyntaxTree prattAst = prattParser.parse(expression).getAst(); + + SourceInfo antlrSourceInfo = + CelProtoAbstractSyntaxTree.fromCelAst(antlrAst).toParsedExpr().getSourceInfo(); + SourceInfo prattSourceInfo = + CelProtoAbstractSyntaxTree.fromCelAst(prattAst).toParsedExpr().getSourceInfo(); + + assertThat(prattSourceInfo).isEqualTo(antlrSourceInfo); + testOutput().println("S: " + TextFormat.printer().printToString(antlrSourceInfo)); + } + + private static String sanitizeForBaseline(String text) { + if (text == null) { + return null; + } + return text.replace("\t", "»").replace("\u007f", "\\u007f"); } } diff --git a/parser/src/test/java/dev/cel/parser/PrattParserTest.java b/parser/src/test/java/dev/cel/parser/PrattParserTest.java index 53ac0703b..e9394b362 100644 --- a/parser/src/test/java/dev/cel/parser/PrattParserTest.java +++ b/parser/src/test/java/dev/cel/parser/PrattParserTest.java @@ -220,6 +220,10 @@ public void pratt_parser_core_syntax() { runTest("MyType{foo: 1, bar: 'baz'}"); runTest("Message{`in`: true}"); runTest("Msg{?field: value}"); + runTest("foo.bar.MyType{ }"); + runTest("foo.bar.MyType{ a:b }"); + runTest(".foo.bar.MyType{ a:b }"); + runTest("a.b.c.d.Message{ foo: 1, bar: 'baz' }"); // Field selection runTest("a.b"); @@ -544,7 +548,7 @@ private void runTest( Map macros, String expression, boolean validateParseOutput) { - testOutput().println("I: " + expression.replace("\t", "»")); + testOutput().println("I: " + sanitizeForBaseline(expression)); testOutput().println("=====>"); CelSource source = CelSource.newBuilder(expression).setDescription("").build(); @@ -574,9 +578,16 @@ private void runTest( testOutput().println("M: " + macroOutput); } } catch (CelValidationException e) { - testOutput().println("E: " + e.getMessage()); + testOutput().println("E: " + sanitizeForBaseline(e.getMessage())); } testOutput().println(); } + + private static String sanitizeForBaseline(String text) { + if (text == null) { + return null; + } + return text.replace("\t", "»").replace("\u007f", "\\u007f"); + } } diff --git a/parser/src/test/resources/parser.baseline b/parser/src/test/resources/parser_core_syntax.baseline similarity index 53% rename from parser/src/test/resources/parser.baseline rename to parser/src/test/resources/parser_core_syntax.baseline index 37b8ef3cc..7c05685f3 100644 --- a/parser/src/test/resources/parser.baseline +++ b/parser/src/test/resources/parser_core_syntax.baseline @@ -1,96 +1,94 @@ -I: x * 2 +I: a =====> -P: _*_( - x^#1:Expr.Ident#, - 2^#3:int64# -)^#2:Expr.Call# -L: _*_( - x^#1[1,0]#, - 2^#3[1,4]# -)^#2[1,2]# +P: a^#1:Expr.Ident# +L: a^#1[1,0]# -I: x * 2u +I: foo =====> -P: _*_( - x^#1:Expr.Ident#, - 2u^#3:uint64# -)^#2:Expr.Call# -L: _*_( - x^#1[1,0]#, - 2u^#3[1,4]# -)^#2[1,2]# +P: foo^#1:Expr.Ident# +L: foo^#1[1,0]# -I: x * 2.0 +I: (a) =====> -P: _*_( - x^#1:Expr.Ident#, - 2.0^#3:double# -)^#2:Expr.Call# -L: _*_( - x^#1[1,0]#, - 2.0^#3[1,4]# -)^#2[1,2]# +P: a^#1:Expr.Ident# +L: a^#1[1,1]# -I: "\u2764" +I: ((a)) =====> -P: "❤"^#1:string# -L: "❤"^#1[1,0]# +P: a^#1:Expr.Ident# +L: a^#1[1,2]# -I: "❤" +I: (((1 + 2))) * 3 =====> -P: "❤"^#1:string# -L: "❤"^#1[1,0]# +P: _*_( + _+_( + 1^#1:int64#, + 2^#3:int64# + )^#2:Expr.Call#, + 3^#5:int64# +)^#4:Expr.Call# +L: _*_( + _+_( + 1^#1[1,3]#, + 2^#3[1,7]# + )^#2[1,5]#, + 3^#5[1,14]# +)^#4[1,12]# -I: ! false +I: [] =====> -P: !_( - false^#2:bool# -)^#1:Expr.Call# -L: !_( - false^#2[1,2]# -)^#1[1,0]# +P: []^#1:Expr.CreateList# +L: []^#1[1,0]# -I: -a +I: [a] =====> -P: -_( +P: [ a^#2:Expr.Ident# -)^#1:Expr.Call# -L: -_( +]^#1:Expr.CreateList# +L: [ a^#2[1,1]# -)^#1[1,0]# +]^#1[1,0]# -I: a.b(5) +I: [a, b, c] =====> -P: a^#1:Expr.Ident#.b( - 5^#3:int64# -)^#2:Expr.Call# -L: a^#1[1,0]#.b( - 5^#3[1,4]# -)^#2[1,3]# +P: [ + a^#2:Expr.Ident#, + b^#3:Expr.Ident#, + c^#4:Expr.Ident# +]^#1:Expr.CreateList# +L: [ + a^#2[1,1]#, + b^#3[1,4]#, + c^#4[1,7]# +]^#1[1,0]# -I: a[3] +I: [1, 2, 3] =====> -P: _[_]( - a^#1:Expr.Ident#, - 3^#3:int64# -)^#2:Expr.Call# -L: _[_]( - a^#1[1,0]#, - 3^#3[1,2]# -)^#2[1,1]# +P: [ + 1^#2:int64#, + 2^#3:int64#, + 3^#4:int64# +]^#1:Expr.CreateList# +L: [ + 1^#2[1,1]#, + 2^#3[1,4]#, + 3^#4[1,7]# +]^#1[1,0]# -I: SomeMessage{foo: 5, bar: "xyz"} +I: [3, 4, 5] =====> -P: SomeMessage{ - foo:5^#3:int64#^#2:Expr.CreateStruct.Entry#, - bar:"xyz"^#5:string#^#4:Expr.CreateStruct.Entry# -}^#1:Expr.CreateStruct# -L: SomeMessage{ - foo:5^#3[1,17]#^#2[1,15]#, - bar:"xyz"^#5[1,25]#^#4[1,23]# -}^#1[1,11]# +P: [ + 3^#2:int64#, + 4^#3:int64#, + 5^#4:int64# +]^#1:Expr.CreateList# +L: [ + 3^#2[1,1]#, + 4^#3[1,4]#, + 5^#4[1,7]# +]^#1[1,0]# -I: [3, 4, 5] +I: [3, 4, 5,] =====> P: [ 3^#2:int64#, @@ -103,6 +101,59 @@ L: [ 5^#4[1,7]# ]^#1[1,0]# +I: [?a, b] +=====> +P: [ + ?a^#2:Expr.Ident#, + b^#3:Expr.Ident# +]^#1:Expr.CreateList# +L: [ + ?a^#2[1,2]#, + b^#3[1,5]# +]^#1[1,0]# + +I: [?a, ?b] +=====> +P: [ + ?a^#2:Expr.Ident#, + ?b^#3:Expr.Ident# +]^#1:Expr.CreateList# +L: [ + ?a^#2[1,2]#, + ?b^#3[1,6]# +]^#1[1,0]# + +I: [?a[?b]] +=====> +P: [ + ?_[?_]( + a^#2:Expr.Ident#, + b^#4:Expr.Ident# + )^#3:Expr.Call# +]^#1:Expr.CreateList# +L: [ + ?_[?_]( + a^#2[1,2]#, + b^#4[1,5]# + )^#3[1,3]# +]^#1[1,0]# + +I: {} +=====> +P: {}^#1:Expr.CreateStruct# +L: {}^#1[1,0]# + +I: {a:b, c:d} +=====> +P: { + a^#3:Expr.Ident#:b^#4:Expr.Ident#^#2:Expr.CreateStruct.Entry#, + c^#6:Expr.Ident#:d^#7:Expr.Ident#^#5:Expr.CreateStruct.Entry# +}^#1:Expr.CreateStruct# +L: { + a^#3[1,1]#:b^#4[1,3]#^#2[1,2]#, + c^#6[1,6]#:d^#7[1,8]#^#5[1,7]# +}^#1[1,0]# + I: {foo: 5, bar: "xyz"} =====> P: { @@ -114,891 +165,526 @@ L: { bar^#6[1,9]#:"xyz"^#7[1,14]#^#5[1,12]# }^#1[1,0]# -I: a > 5 && a < 10 -=====> -P: _&&_( - _>_( - a^#1:Expr.Ident#, - 5^#3:int64# - )^#2:Expr.Call#, - _<_( - a^#5:Expr.Ident#, - 10^#7:int64# - )^#6:Expr.Call# -)^#4:Expr.Call# -L: _&&_( - _>_( - a^#1[1,0]#, - 5^#3[1,4]# - )^#2[1,2]#, - _<_( - a^#5[1,9]#, - 10^#7[1,13]# - )^#6[1,11]# -)^#4[1,6]# - -I: a < 5 || a > 10 +I: {foo: 5, bar: "xyz", } =====> -P: _||_( - _<_( - a^#1:Expr.Ident#, - 5^#3:int64# - )^#2:Expr.Call#, - _>_( - a^#5:Expr.Ident#, - 10^#7:int64# - )^#6:Expr.Call# -)^#4:Expr.Call# -L: _||_( - _<_( - a^#1[1,0]#, - 5^#3[1,4]# - )^#2[1,2]#, - _>_( - a^#5[1,9]#, - 10^#7[1,13]# - )^#6[1,11]# -)^#4[1,6]# +P: { + foo^#3:Expr.Ident#:5^#4:int64#^#2:Expr.CreateStruct.Entry#, + bar^#6:Expr.Ident#:"xyz"^#7:string#^#5:Expr.CreateStruct.Entry# +}^#1:Expr.CreateStruct# +L: { + foo^#3[1,1]#:5^#4[1,6]#^#2[1,4]#, + bar^#6[1,9]#:"xyz"^#7[1,14]#^#5[1,12]# +}^#1[1,0]# -I: "abc" + "def" +I: {"a": 1, "b": 2} =====> -P: _+_( - "abc"^#1:string#, - "def"^#3:string# -)^#2:Expr.Call# -L: _+_( - "abc"^#1[1,0]#, - "def"^#3[1,8]# -)^#2[1,6]# +P: { + "a"^#3:string#:1^#4:int64#^#2:Expr.CreateStruct.Entry#, + "b"^#6:string#:2^#7:int64#^#5:Expr.CreateStruct.Entry# +}^#1:Expr.CreateStruct# +L: { + "a"^#3[1,1]#:1^#4[1,6]#^#2[1,4]#, + "b"^#6[1,9]#:2^#7[1,14]#^#5[1,12]# +}^#1[1,0]# -I: "A" +I: {1:2u, 2:3u} =====> -P: "A"^#1:string# -L: "A"^#1[1,0]# +P: { + 1^#3:int64#:2u^#4:uint64#^#2:Expr.CreateStruct.Entry#, + 2^#6:int64#:3u^#7:uint64#^#5:Expr.CreateStruct.Entry# +}^#1:Expr.CreateStruct# +L: { + 1^#3[1,1]#:2u^#4[1,3]#^#2[1,2]#, + 2^#6[1,7]#:3u^#7[1,9]#^#5[1,8]# +}^#1[1,0]# -I: true +I: {?a: b} =====> -P: true^#1:bool# -L: true^#1[1,0]# +P: { + ?a^#3:Expr.Ident#:b^#4:Expr.Ident#^#2:Expr.CreateStruct.Entry# +}^#1:Expr.CreateStruct# +L: { + ?a^#3[1,2]#:b^#4[1,5]#^#2[1,3]# +}^#1[1,0]# -I: false +I: {?'key': value} =====> -P: false^#1:bool# -L: false^#1[1,0]# +P: { + ?"key"^#3:string#:value^#4:Expr.Ident#^#2:Expr.CreateStruct.Entry# +}^#1:Expr.CreateStruct# +L: { + ?"key"^#3[1,2]#:value^#4[1,9]#^#2[1,7]# +}^#1[1,0]# -I: 0 +I: foo{ } =====> -P: 0^#1:int64# -L: 0^#1[1,0]# +P: foo{}^#1:Expr.CreateStruct# +L: foo{}^#1[1,3]# -I: 42 +I: foo{ a:b } =====> -P: 42^#1:int64# -L: 42^#1[1,0]# - -I: 0u -=====> -P: 0u^#1:uint64# -L: 0u^#1[1,0]# - -I: 23u -=====> -P: 23u^#1:uint64# -L: 23u^#1[1,0]# +P: foo{ + a:b^#3:Expr.Ident#^#2:Expr.CreateStruct.Entry# +}^#1:Expr.CreateStruct# +L: foo{ + a:b^#3[1,7]#^#2[1,6]# +}^#1[1,3]# -I: 24u +I: foo{ a:b, c:d } =====> -P: 24u^#1:uint64# -L: 24u^#1[1,0]# +P: foo{ + a:b^#3:Expr.Ident#^#2:Expr.CreateStruct.Entry#, + c:d^#5:Expr.Ident#^#4:Expr.CreateStruct.Entry# +}^#1:Expr.CreateStruct# +L: foo{ + a:b^#3[1,7]#^#2[1,6]#, + c:d^#5[1,12]#^#4[1,11]# +}^#1[1,3]# -I: 0xAu +I: SomeMessage{foo: 5, bar: "xyz"} =====> -P: 10u^#1:uint64# -L: 10u^#1[1,0]# +P: SomeMessage{ + foo:5^#3:int64#^#2:Expr.CreateStruct.Entry#, + bar:"xyz"^#5:string#^#4:Expr.CreateStruct.Entry# +}^#1:Expr.CreateStruct# +L: SomeMessage{ + foo:5^#3[1,17]#^#2[1,15]#, + bar:"xyz"^#5[1,25]#^#4[1,23]# +}^#1[1,11]# -I: -0xA +I: TestAllTypes{single_int32: 1, single_int64: 2} =====> -P: -10^#1:int64# -L: -10^#1[1,1]# +P: TestAllTypes{ + single_int32:1^#3:int64#^#2:Expr.CreateStruct.Entry#, + single_int64:2^#5:int64#^#4:Expr.CreateStruct.Entry# +}^#1:Expr.CreateStruct# +L: TestAllTypes{ + single_int32:1^#3[1,27]#^#2[1,25]#, + single_int64:2^#5[1,44]#^#4[1,42]# +}^#1[1,12]# -I: 0xA +I: MyType{foo: 1, bar: 'baz'} =====> -P: 10^#1:int64# -L: 10^#1[1,0]# +P: MyType{ + foo:1^#3:int64#^#2:Expr.CreateStruct.Entry#, + bar:"baz"^#5:string#^#4:Expr.CreateStruct.Entry# +}^#1:Expr.CreateStruct# +L: MyType{ + foo:1^#3[1,12]#^#2[1,10]#, + bar:"baz"^#5[1,20]#^#4[1,18]# +}^#1[1,6]# -I: -1 +I: Message{`in`: true} =====> -P: -1^#1:int64# -L: -1^#1[1,1]# +P: Message{ + in:true^#3:bool#^#2:Expr.CreateStruct.Entry# +}^#1:Expr.CreateStruct# +L: Message{ + in:true^#3[1,14]#^#2[1,12]# +}^#1[1,7]# -I: 4--4 +I: Msg{?field: value} =====> -P: _-_( - 4^#1:int64#, - -4^#3:int64# -)^#2:Expr.Call# -L: _-_( - 4^#1[1,0]#, - -4^#3[1,3]# -)^#2[1,1]# +P: Msg{ + ?field:value^#3:Expr.Ident#^#2:Expr.CreateStruct.Entry# +}^#1:Expr.CreateStruct# +L: Msg{ + ?field:value^#3[1,12]#^#2[1,10]# +}^#1[1,3]# -I: 4--4.1 +I: foo.bar.MyType{ } =====> -P: _-_( - 4^#1:int64#, - -4.1^#3:double# -)^#2:Expr.Call# -L: _-_( - 4^#1[1,0]#, - -4.1^#3[1,3]# -)^#2[1,1]# +P: foo.bar.MyType{}^#1:Expr.CreateStruct# +L: foo.bar.MyType{}^#1[1,14]# -I: b"abc" +I: foo.bar.MyType{ a:b } =====> -P: b"abc"^#1:bytes# -L: b"abc"^#1[1,0]# +P: foo.bar.MyType{ + a:b^#3:Expr.Ident#^#2:Expr.CreateStruct.Entry# +}^#1:Expr.CreateStruct# +L: foo.bar.MyType{ + a:b^#3[1,18]#^#2[1,17]# +}^#1[1,14]# -I: 23.39 +I: .foo.bar.MyType{ a:b } =====> -P: 23.39^#1:double# -L: 23.39^#1[1,0]# +P: .foo.bar.MyType{ + a:b^#3:Expr.Ident#^#2:Expr.CreateStruct.Entry# +}^#1:Expr.CreateStruct# +L: .foo.bar.MyType{ + a:b^#3[1,19]#^#2[1,18]# +}^#1[1,15]# -I: !a +I: a.b.c.d.Message{ foo: 1, bar: 'baz' } =====> -P: !_( - a^#2:Expr.Ident# -)^#1:Expr.Call# -L: !_( - a^#2[1,1]# -)^#1[1,0]# +P: a.b.c.d.Message{ + foo:1^#3:int64#^#2:Expr.CreateStruct.Entry#, + bar:"baz"^#5:string#^#4:Expr.CreateStruct.Entry# +}^#1:Expr.CreateStruct# +L: a.b.c.d.Message{ + foo:1^#3[1,22]#^#2[1,20]#, + bar:"baz"^#5[1,30]#^#4[1,28]# +}^#1[1,15]# -I: null +I: a.b =====> -P: null^#1:NullValue# -L: null^#1[1,0]# +P: a^#1:Expr.Ident#.b^#2:Expr.Select# +L: a^#1[1,0]#.b^#2[1,1]# -I: a +I: a.b.c =====> -P: a^#1:Expr.Ident# -L: a^#1[1,0]# +P: a^#1:Expr.Ident#.b^#2:Expr.Select#.c^#3:Expr.Select# +L: a^#1[1,0]#.b^#2[1,1]#.c^#3[1,3]# -I: a?b:c +I: a.?b =====> -P: _?_:_( +P: _?._( a^#1:Expr.Ident#, - b^#3:Expr.Ident#, - c^#4:Expr.Ident# + "b"^#3:string# )^#2:Expr.Call# -L: _?_:_( +L: _?._( a^#1[1,0]#, - b^#3[1,2]#, - c^#4[1,4]# + "b"^#3[1,0]# )^#2[1,1]# -I: a || b +I: a.`b-c` =====> -P: _||_( - a^#1:Expr.Ident#, - b^#3:Expr.Ident# -)^#2:Expr.Call# -L: _||_( - a^#1[1,0]#, - b^#3[1,5]# -)^#2[1,2]# +P: a^#1:Expr.Ident#.b-c^#2:Expr.Select# +L: a^#1[1,0]#.b-c^#2[1,1]# -I: a || b || c || d || e || f +I: a.`b c` =====> -P: _||_( - _||_( - _||_( - a^#1:Expr.Ident#, - b^#3:Expr.Ident# - )^#2:Expr.Call#, - c^#5:Expr.Ident# - )^#4:Expr.Call#, - _||_( - _||_( - d^#7:Expr.Ident#, - e^#9:Expr.Ident# - )^#8:Expr.Call#, - f^#11:Expr.Ident# - )^#10:Expr.Call# -)^#6:Expr.Call# -L: _||_( - _||_( - _||_( - a^#1[1,0]#, - b^#3[1,5]# - )^#2[1,2]#, - c^#5[1,10]# - )^#4[1,7]#, - _||_( - _||_( - d^#7[1,15]#, - e^#9[1,20]# - )^#8[1,17]#, - f^#11[1,25]# - )^#10[1,22]# -)^#6[1,12]# +P: a^#1:Expr.Ident#.b c^#2:Expr.Select# +L: a^#1[1,0]#.b c^#2[1,1]# -I: a && b +I: a.`b.c` =====> -P: _&&_( - a^#1:Expr.Ident#, - b^#3:Expr.Ident# -)^#2:Expr.Call# -L: _&&_( - a^#1[1,0]#, - b^#3[1,5]# -)^#2[1,2]# +P: a^#1:Expr.Ident#.b.c^#2:Expr.Select# +L: a^#1[1,0]#.b.c^#2[1,1]# -I: a && b && c && d && e && f && g +I: a.`in` =====> -P: _&&_( - _&&_( - _&&_( - a^#1:Expr.Ident#, - b^#3:Expr.Ident# - )^#2:Expr.Call#, - _&&_( - c^#5:Expr.Ident#, - d^#7:Expr.Ident# - )^#6:Expr.Call# - )^#4:Expr.Call#, - _&&_( - _&&_( - e^#9:Expr.Ident#, - f^#11:Expr.Ident# - )^#10:Expr.Call#, - g^#13:Expr.Ident# - )^#12:Expr.Call# -)^#8:Expr.Call# -L: _&&_( - _&&_( - _&&_( - a^#1[1,0]#, - b^#3[1,5]# - )^#2[1,2]#, - _&&_( - c^#5[1,10]#, - d^#7[1,15]# - )^#6[1,12]# - )^#4[1,7]#, - _&&_( - _&&_( - e^#9[1,20]#, - f^#11[1,25]# - )^#10[1,22]#, - g^#13[1,30]# - )^#12[1,27]# -)^#8[1,17]# +P: a^#1:Expr.Ident#.in^#2:Expr.Select# +L: a^#1[1,0]#.in^#2[1,1]# -I: a && b && c && d || e && f && g && h +I: a.`/foo` =====> -P: _||_( - _&&_( - _&&_( - a^#1:Expr.Ident#, - b^#3:Expr.Ident# - )^#2:Expr.Call#, - _&&_( - c^#5:Expr.Ident#, - d^#7:Expr.Ident# - )^#6:Expr.Call# - )^#4:Expr.Call#, - _&&_( - _&&_( - e^#9:Expr.Ident#, - f^#11:Expr.Ident# - )^#10:Expr.Call#, - _&&_( - g^#13:Expr.Ident#, - h^#15:Expr.Ident# - )^#14:Expr.Call# - )^#12:Expr.Call# -)^#8:Expr.Call# -L: _||_( - _&&_( - _&&_( - a^#1[1,0]#, - b^#3[1,5]# - )^#2[1,2]#, - _&&_( - c^#5[1,10]#, - d^#7[1,15]# - )^#6[1,12]# - )^#4[1,7]#, - _&&_( - _&&_( - e^#9[1,20]#, - f^#11[1,25]# - )^#10[1,22]#, - _&&_( - g^#13[1,30]#, - h^#15[1,35]# - )^#14[1,32]# - )^#12[1,27]# -)^#8[1,17]# +P: a^#1:Expr.Ident#./foo^#2:Expr.Select# +L: a^#1[1,0]#./foo^#2[1,1]# -I: a + b +I: a.`my-var` =====> -P: _+_( +P: a^#1:Expr.Ident#.my-var^#2:Expr.Select# +L: a^#1[1,0]#.my-var^#2[1,1]# + +I: a[b] +=====> +P: _[_]( a^#1:Expr.Ident#, b^#3:Expr.Ident# )^#2:Expr.Call# -L: _+_( +L: _[_]( a^#1[1,0]#, - b^#3[1,4]# -)^#2[1,2]# + b^#3[1,2]# +)^#2[1,1]# -I: a - b +I: a[0] =====> -P: _-_( +P: _[_]( a^#1:Expr.Ident#, - b^#3:Expr.Ident# + 0^#3:int64# )^#2:Expr.Call# -L: _-_( +L: _[_]( a^#1[1,0]#, - b^#3[1,4]# -)^#2[1,2]# + 0^#3[1,2]# +)^#2[1,1]# -I: a * b +I: a[3] =====> -P: _*_( +P: _[_]( a^#1:Expr.Ident#, - b^#3:Expr.Ident# + 3^#3:int64# )^#2:Expr.Call# -L: _*_( +L: _[_]( a^#1[1,0]#, - b^#3[1,4]# -)^#2[1,2]# + 3^#3[1,2]# +)^#2[1,1]# -I: a / b +I: [1,3,4][0] =====> -P: _/_( +P: _[_]( + [ + 1^#2:int64#, + 3^#3:int64#, + 4^#4:int64# + ]^#1:Expr.CreateList#, + 0^#6:int64# +)^#5:Expr.Call# +L: _[_]( + [ + 1^#2[1,1]#, + 3^#3[1,3]#, + 4^#4[1,5]# + ]^#1[1,0]#, + 0^#6[1,8]# +)^#5[1,7]# + +I: a[?0] +=====> +P: _[?_]( a^#1:Expr.Ident#, - b^#3:Expr.Ident# + 0^#3:int64# )^#2:Expr.Call# -L: _/_( +L: _[?_]( a^#1[1,0]#, - b^#3[1,4]# -)^#2[1,2]# + 0^#3[1,3]# +)^#2[1,1]# -I: a % b +I: a() =====> -P: _%_( - a^#1:Expr.Ident#, - b^#3:Expr.Ident# +P: a()^#1:Expr.Call# +L: a()^#1[1,1]# + +I: a(b) +=====> +P: a( + b^#2:Expr.Ident# +)^#1:Expr.Call# +L: a( + b^#2[1,2]# +)^#1[1,1]# + +I: a(b, c) +=====> +P: a( + b^#2:Expr.Ident#, + c^#3:Expr.Ident# +)^#1:Expr.Call# +L: a( + b^#2[1,2]#, + c^#3[1,5]# +)^#1[1,1]# + +I: a.b() +=====> +P: a^#1:Expr.Ident#.b()^#2:Expr.Call# +L: a^#1[1,0]#.b()^#2[1,3]# + +I: a.b(c) +=====> +P: a^#1:Expr.Ident#.b( + c^#3:Expr.Ident# )^#2:Expr.Call# -L: _%_( - a^#1[1,0]#, - b^#3[1,4]# +L: a^#1[1,0]#.b( + c^#3[1,4]# +)^#2[1,3]# + +I: a.b(5) +=====> +P: a^#1:Expr.Ident#.b( + 5^#3:int64# +)^#2:Expr.Call# +L: a^#1[1,0]#.b( + 5^#3[1,4]# +)^#2[1,3]# + +I: aaa.bbb(ccc) +=====> +P: aaa^#1:Expr.Ident#.bbb( + ccc^#3:Expr.Ident# +)^#2:Expr.Call# +L: aaa^#1[1,0]#.bbb( + ccc^#3[1,8]# +)^#2[1,7]# + +I: a.foo(1, 2) +=====> +P: a^#1:Expr.Ident#.foo( + 1^#3:int64#, + 2^#4:int64# +)^#2:Expr.Call# +L: a^#1[1,0]#.foo( + 1^#3[1,6]#, + 2^#4[1,9]# +)^#2[1,5]# + +I: !a +=====> +P: !_( + a^#2:Expr.Ident# +)^#1:Expr.Call# +L: !_( + a^#2[1,1]# +)^#1[1,0]# + +I: !x +=====> +P: !_( + x^#2:Expr.Ident# +)^#1:Expr.Call# +L: !_( + x^#2[1,1]# +)^#1[1,0]# + +I: ! false +=====> +P: !_( + false^#2:bool# +)^#1:Expr.Call# +L: !_( + false^#2[1,2]# +)^#1[1,0]# + +I: -a +=====> +P: -_( + a^#2:Expr.Ident# +)^#1:Expr.Call# +L: -_( + a^#2[1,1]# +)^#1[1,0]# + +I: ---a +=====> +P: -_( + a^#2:Expr.Ident# +)^#1:Expr.Call# +L: -_( + a^#2[1,3]# +)^#1[1,0]# + +I: x * 2 +=====> +P: _*_( + x^#1:Expr.Ident#, + 2^#3:int64# +)^#2:Expr.Call# +L: _*_( + x^#1[1,0]#, + 2^#3[1,4]# )^#2[1,2]# -I: a in b +I: x * 2u =====> -P: @in( - a^#1:Expr.Ident#, - b^#3:Expr.Ident# +P: _*_( + x^#1:Expr.Ident#, + 2u^#3:uint64# )^#2:Expr.Call# -L: @in( - a^#1[1,0]#, - b^#3[1,5]# +L: _*_( + x^#1[1,0]#, + 2u^#3[1,4]# )^#2[1,2]# -I: a == b +I: x * 2.0 =====> -P: _==_( - a^#1:Expr.Ident#, - b^#3:Expr.Ident# +P: _*_( + x^#1:Expr.Ident#, + 2.0^#3:double# )^#2:Expr.Call# -L: _==_( - a^#1[1,0]#, - b^#3[1,5]# +L: _*_( + x^#1[1,0]#, + 2.0^#3[1,4]# )^#2[1,2]# -I: a != b +I: a * b =====> -P: _!=_( +P: _*_( a^#1:Expr.Ident#, b^#3:Expr.Ident# )^#2:Expr.Call# -L: _!=_( +L: _*_( a^#1[1,0]#, - b^#3[1,5]# + b^#3[1,4]# )^#2[1,2]# -I: a > b +I: a / b =====> -P: _>_( +P: _/_( a^#1:Expr.Ident#, b^#3:Expr.Ident# )^#2:Expr.Call# -L: _>_( +L: _/_( a^#1[1,0]#, b^#3[1,4]# )^#2[1,2]# -I: a >= b +I: a % b =====> -P: _>=_( +P: _%_( a^#1:Expr.Ident#, b^#3:Expr.Ident# )^#2:Expr.Call# -L: _>=_( +L: _%_( a^#1[1,0]#, - b^#3[1,5]# + b^#3[1,4]# )^#2[1,2]# -I: a < b +I: a + b =====> -P: _<_( +P: _+_( a^#1:Expr.Ident#, b^#3:Expr.Ident# )^#2:Expr.Call# -L: _<_( +L: _+_( a^#1[1,0]#, b^#3[1,4]# )^#2[1,2]# -I: a <= b +I: a - b =====> -P: _<=_( +P: _-_( a^#1:Expr.Ident#, b^#3:Expr.Ident# )^#2:Expr.Call# -L: _<=_( +L: _-_( a^#1[1,0]#, - b^#3[1,5]# + b^#3[1,4]# )^#2[1,2]# -I: a.b -=====> -P: a^#1:Expr.Ident#.b^#2:Expr.Select# -L: a^#1[1,0]#.b^#2[1,1]# - -I: a.b.c +I: 4--4 =====> -P: a^#1:Expr.Ident#.b^#2:Expr.Select#.c^#3:Expr.Select# -L: a^#1[1,0]#.b^#2[1,1]#.c^#3[1,3]# +P: _-_( + 4^#1:int64#, + -4^#3:int64# +)^#2:Expr.Call# +L: _-_( + 4^#1[1,0]#, + -4^#3[1,3]# +)^#2[1,1]# -I: a[b] +I: 4--4.1 =====> -P: _[_]( - a^#1:Expr.Ident#, - b^#3:Expr.Ident# +P: _-_( + 4^#1:int64#, + -4.1^#3:double# )^#2:Expr.Call# -L: _[_]( - a^#1[1,0]#, - b^#3[1,2]# +L: _-_( + 4^#1[1,0]#, + -4.1^#3[1,3]# )^#2[1,1]# -I: foo{ } +I: "abc" + "def" =====> -P: foo{}^#1:Expr.CreateStruct# -L: foo{}^#1[1,3]# +P: _+_( + "abc"^#1:string#, + "def"^#3:string# +)^#2:Expr.Call# +L: _+_( + "abc"^#1[1,0]#, + "def"^#3[1,8]# +)^#2[1,6]# -I: foo{ a:b } +I: b"abc" + B"def" =====> -P: foo{ - a:b^#3:Expr.Ident#^#2:Expr.CreateStruct.Entry# -}^#1:Expr.CreateStruct# -L: foo{ - a:b^#3[1,7]#^#2[1,6]# -}^#1[1,3]# - -I: foo{ a:b, c:d } -=====> -P: foo{ - a:b^#3:Expr.Ident#^#2:Expr.CreateStruct.Entry#, - c:d^#5:Expr.Ident#^#4:Expr.CreateStruct.Entry# -}^#1:Expr.CreateStruct# -L: foo{ - a:b^#3[1,7]#^#2[1,6]#, - c:d^#5[1,12]#^#4[1,11]# -}^#1[1,3]# - -I: {} -=====> -P: {}^#1:Expr.CreateStruct# -L: {}^#1[1,0]# - -I: {a:b, c:d} -=====> -P: { - a^#3:Expr.Ident#:b^#4:Expr.Ident#^#2:Expr.CreateStruct.Entry#, - c^#6:Expr.Ident#:d^#7:Expr.Ident#^#5:Expr.CreateStruct.Entry# -}^#1:Expr.CreateStruct# -L: { - a^#3[1,1]#:b^#4[1,3]#^#2[1,2]#, - c^#6[1,6]#:d^#7[1,8]#^#5[1,7]# -}^#1[1,0]# - -I: [] -=====> -P: []^#1:Expr.CreateList# -L: []^#1[1,0]# - -I: [a] -=====> -P: [ - a^#2:Expr.Ident# -]^#1:Expr.CreateList# -L: [ - a^#2[1,1]# -]^#1[1,0]# - -I: [a, b, c] -=====> -P: [ - a^#2:Expr.Ident#, - b^#3:Expr.Ident#, - c^#4:Expr.Ident# -]^#1:Expr.CreateList# -L: [ - a^#2[1,1]#, - b^#3[1,4]#, - c^#4[1,7]# -]^#1[1,0]# - -I: (a) -=====> -P: a^#1:Expr.Ident# -L: a^#1[1,1]# - -I: ((a)) -=====> -P: a^#1:Expr.Ident# -L: a^#1[1,2]# - -I: a() -=====> -P: a()^#1:Expr.Call# -L: a()^#1[1,1]# - -I: a(b) -=====> -P: a( - b^#2:Expr.Ident# -)^#1:Expr.Call# -L: a( - b^#2[1,2]# -)^#1[1,1]# - -I: a(b, c) -=====> -P: a( - b^#2:Expr.Ident#, - c^#3:Expr.Ident# -)^#1:Expr.Call# -L: a( - b^#2[1,2]#, - c^#3[1,5]# -)^#1[1,1]# - -I: a.b() -=====> -P: a^#1:Expr.Ident#.b()^#2:Expr.Call# -L: a^#1[1,0]#.b()^#2[1,3]# - -I: a.b(c) -=====> -P: a^#1:Expr.Ident#.b( - c^#3:Expr.Ident# -)^#2:Expr.Call# -L: a^#1[1,0]#.b( - c^#3[1,4]# -)^#2[1,3]# - -I: aaa.bbb(ccc) -=====> -P: aaa^#1:Expr.Ident#.bbb( - ccc^#3:Expr.Ident# +P: _+_( + b"abc"^#1:bytes#, + b"def"^#3:bytes# )^#2:Expr.Call# -L: aaa^#1[1,0]#.bbb( - ccc^#3[1,8]# +L: _+_( + b"abc"^#1[1,0]#, + b"def"^#3[1,9]# )^#2[1,7]# -I: has(m.f) -=====> -P: m^#2:Expr.Ident#.f~test-only~^#4:Expr.Select# -L: m^#2[1,4]#.f~test-only~^#4[1,3]# -M: has( - m^#2:Expr.Ident#.f^#3:Expr.Select# -)^#0:Expr.Call# - -I: m.exists_one(v, f) -=====> -P: __comprehension__( - // Variable - v, - // Target - m^#1:Expr.Ident#, - // Accumulator - @result, - // Init - 0^#5:int64#, - // LoopCondition - true^#6:bool#, - // LoopStep - _?_:_( - f^#4:Expr.Ident#, - _+_( - @result^#7:Expr.Ident#, - 1^#8:int64# - )^#9:Expr.Call#, - @result^#10:Expr.Ident# - )^#11:Expr.Call#, - // Result - _==_( - @result^#12:Expr.Ident#, - 1^#13:int64# - )^#14:Expr.Call#)^#15:Expr.Comprehension# -L: __comprehension__( - // Variable - v, - // Target - m^#1[1,0]#, - // Accumulator - @result, - // Init - 0^#5[1,12]#, - // LoopCondition - true^#6[1,12]#, - // LoopStep - _?_:_( - f^#4[1,16]#, - _+_( - @result^#7[1,12]#, - 1^#8[1,12]# - )^#9[1,12]#, - @result^#10[1,12]# - )^#11[1,12]#, - // Result - _==_( - @result^#12[1,12]#, - 1^#13[1,12]# - )^#14[1,12]#)^#15[1,12]# -M: m^#1:Expr.Ident#.exists_one( - v^#3:Expr.Ident#, - f^#4:Expr.Ident# -)^#0:Expr.Call# - -I: m.existsOne(v, f) -=====> -P: __comprehension__( - // Variable - v, - // Target - m^#1:Expr.Ident#, - // Accumulator - @result, - // Init - 0^#5:int64#, - // LoopCondition - true^#6:bool#, - // LoopStep - _?_:_( - f^#4:Expr.Ident#, - _+_( - @result^#7:Expr.Ident#, - 1^#8:int64# - )^#9:Expr.Call#, - @result^#10:Expr.Ident# - )^#11:Expr.Call#, - // Result - _==_( - @result^#12:Expr.Ident#, - 1^#13:int64# - )^#14:Expr.Call#)^#15:Expr.Comprehension# -L: __comprehension__( - // Variable - v, - // Target - m^#1[1,0]#, - // Accumulator - @result, - // Init - 0^#5[1,11]#, - // LoopCondition - true^#6[1,11]#, - // LoopStep - _?_:_( - f^#4[1,15]#, - _+_( - @result^#7[1,11]#, - 1^#8[1,11]# - )^#9[1,11]#, - @result^#10[1,11]# - )^#11[1,11]#, - // Result - _==_( - @result^#12[1,11]#, - 1^#13[1,11]# - )^#14[1,11]#)^#15[1,11]# -M: m^#1:Expr.Ident#.existsOne( - v^#3:Expr.Ident#, - f^#4:Expr.Ident# -)^#0:Expr.Call# - -I: m.map(v, f) -=====> -P: __comprehension__( - // Variable - v, - // Target - m^#1:Expr.Ident#, - // Accumulator - @result, - // Init - []^#5:Expr.CreateList#, - // LoopCondition - true^#6:bool#, - // LoopStep - _+_( - @result^#7:Expr.Ident#, - [ - f^#4:Expr.Ident# - ]^#8:Expr.CreateList# - )^#9:Expr.Call#, - // Result - @result^#10:Expr.Ident#)^#11:Expr.Comprehension# -L: __comprehension__( - // Variable - v, - // Target - m^#1[1,0]#, - // Accumulator - @result, - // Init - []^#5[1,5]#, - // LoopCondition - true^#6[1,5]#, - // LoopStep - _+_( - @result^#7[1,5]#, - [ - f^#4[1,9]# - ]^#8[1,5]# - )^#9[1,5]#, - // Result - @result^#10[1,5]#)^#11[1,5]# -M: m^#1:Expr.Ident#.map( - v^#3:Expr.Ident#, - f^#4:Expr.Ident# -)^#0:Expr.Call# - -I: m.map(v, p, f) -=====> -P: __comprehension__( - // Variable - v, - // Target - m^#1:Expr.Ident#, - // Accumulator - @result, - // Init - []^#6:Expr.CreateList#, - // LoopCondition - true^#7:bool#, - // LoopStep - _?_:_( - p^#4:Expr.Ident#, - _+_( - @result^#8:Expr.Ident#, - [ - f^#5:Expr.Ident# - ]^#9:Expr.CreateList# - )^#10:Expr.Call#, - @result^#11:Expr.Ident# - )^#12:Expr.Call#, - // Result - @result^#13:Expr.Ident#)^#14:Expr.Comprehension# -L: __comprehension__( - // Variable - v, - // Target - m^#1[1,0]#, - // Accumulator - @result, - // Init - []^#6[1,5]#, - // LoopCondition - true^#7[1,5]#, - // LoopStep - _?_:_( - p^#4[1,9]#, - _+_( - @result^#8[1,5]#, - [ - f^#5[1,12]# - ]^#9[1,5]# - )^#10[1,5]#, - @result^#11[1,5]# - )^#12[1,5]#, - // Result - @result^#13[1,5]#)^#14[1,5]# -M: m^#1:Expr.Ident#.map( - v^#3:Expr.Ident#, - p^#4:Expr.Ident#, - f^#5:Expr.Ident# -)^#0:Expr.Call# - -I: m.filter(v, p) -=====> -P: __comprehension__( - // Variable - v, - // Target - m^#1:Expr.Ident#, - // Accumulator - @result, - // Init - []^#5:Expr.CreateList#, - // LoopCondition - true^#6:bool#, - // LoopStep - _?_:_( - p^#4:Expr.Ident#, - _+_( - @result^#7:Expr.Ident#, - [ - v^#3:Expr.Ident# - ]^#8:Expr.CreateList# - )^#9:Expr.Call#, - @result^#10:Expr.Ident# - )^#11:Expr.Call#, - // Result - @result^#12:Expr.Ident#)^#13:Expr.Comprehension# -L: __comprehension__( - // Variable - v, - // Target - m^#1[1,0]#, - // Accumulator - @result, - // Init - []^#5[1,8]#, - // LoopCondition - true^#6[1,8]#, - // LoopStep - _?_:_( - p^#4[1,12]#, - _+_( - @result^#7[1,8]#, - [ - v^#3[1,9]# - ]^#8[1,8]# - )^#9[1,8]#, - @result^#10[1,8]# - )^#11[1,8]#, - // Result - @result^#12[1,8]#)^#13[1,8]# -M: m^#1:Expr.Ident#.filter( - v^#3:Expr.Ident#, - p^#4:Expr.Ident# -)^#0:Expr.Call# - I: [] + [1,2,3,] + [4] =====> P: _+_( @@ -1028,27 +714,118 @@ L: _+_( ]^#8[1,16]# )^#7[1,14]# -I: {1:2u, 2:3u} +I: 1 + 2 * 3 =====> -P: { - 1^#3:int64#:2u^#4:uint64#^#2:Expr.CreateStruct.Entry#, - 2^#6:int64#:3u^#7:uint64#^#5:Expr.CreateStruct.Entry# -}^#1:Expr.CreateStruct# -L: { - 1^#3[1,1]#:2u^#4[1,3]#^#2[1,2]#, - 2^#6[1,7]#:3u^#7[1,9]#^#5[1,8]# -}^#1[1,0]# +P: _+_( + 1^#1:int64#, + _*_( + 2^#3:int64#, + 3^#5:int64# + )^#4:Expr.Call# +)^#2:Expr.Call# +L: _+_( + 1^#1[1,0]#, + _*_( + 2^#3[1,4]#, + 3^#5[1,8]# + )^#4[1,6]# +)^#2[1,2]# -I: TestAllTypes{single_int32: 1, single_int64: 2} +I: a == b =====> -P: TestAllTypes{ - single_int32:1^#3:int64#^#2:Expr.CreateStruct.Entry#, - single_int64:2^#5:int64#^#4:Expr.CreateStruct.Entry# -}^#1:Expr.CreateStruct# -L: TestAllTypes{ - single_int32:1^#3[1,27]#^#2[1,25]#, - single_int64:2^#5[1,44]#^#4[1,42]# -}^#1[1,12]# +P: _==_( + a^#1:Expr.Ident#, + b^#3:Expr.Ident# +)^#2:Expr.Call# +L: _==_( + a^#1[1,0]#, + b^#3[1,5]# +)^#2[1,2]# + +I: a != b +=====> +P: _!=_( + a^#1:Expr.Ident#, + b^#3:Expr.Ident# +)^#2:Expr.Call# +L: _!=_( + a^#1[1,0]#, + b^#3[1,5]# +)^#2[1,2]# + +I: a < b +=====> +P: _<_( + a^#1:Expr.Ident#, + b^#3:Expr.Ident# +)^#2:Expr.Call# +L: _<_( + a^#1[1,0]#, + b^#3[1,4]# +)^#2[1,2]# + +I: a <= b +=====> +P: _<=_( + a^#1:Expr.Ident#, + b^#3:Expr.Ident# +)^#2:Expr.Call# +L: _<=_( + a^#1[1,0]#, + b^#3[1,5]# +)^#2[1,2]# + +I: a > b +=====> +P: _>_( + a^#1:Expr.Ident#, + b^#3:Expr.Ident# +)^#2:Expr.Call# +L: _>_( + a^#1[1,0]#, + b^#3[1,4]# +)^#2[1,2]# + +I: a >= b +=====> +P: _>=_( + a^#1:Expr.Ident#, + b^#3:Expr.Ident# +)^#2:Expr.Call# +L: _>=_( + a^#1[1,0]#, + b^#3[1,5]# +)^#2[1,2]# + +I: a in b +=====> +P: @in( + a^#1:Expr.Ident#, + b^#3:Expr.Ident# +)^#2:Expr.Call# +L: @in( + a^#1[1,0]#, + b^#3[1,5]# +)^#2[1,2]# + +I: "😁" in ["😁", "😑", "😦"] +=====> +P: @in( + "😁"^#1:string#, + [ + "😁"^#4:string#, + "😑"^#5:string#, + "😦"^#6:string# + ]^#3:Expr.CreateList# +)^#2:Expr.Call# +L: @in( + "😁"^#1[1,0]#, + [ + "😁"^#4[1,8]#, + "😑"^#5[1,13]#, + "😦"^#6[1,18]# + ]^#3[1,7]# +)^#2[1,4]# I: size(x) == x.size() =====> @@ -1065,57 +842,250 @@ L: _==_( x^#4[1,11]#.size()^#5[1,17]# )^#3[1,8]# -I: "\"" +I: x.single_nested_message != null +=====> +P: _!=_( + x^#1:Expr.Ident#.single_nested_message^#2:Expr.Select#, + null^#4:NullValue# +)^#3:Expr.Call# +L: _!=_( + x^#1[1,0]#.single_nested_message^#2[1,1]#, + null^#4[1,27]# +)^#3[1,24]# + +I: a && b +=====> +P: _&&_( + a^#1:Expr.Ident#, + b^#3:Expr.Ident# +)^#2:Expr.Call# +L: _&&_( + a^#1[1,0]#, + b^#3[1,5]# +)^#2[1,2]# + +I: a && b && c +=====> +P: _&&_( + _&&_( + a^#1:Expr.Ident#, + b^#3:Expr.Ident# + )^#2:Expr.Call#, + c^#5:Expr.Ident# +)^#4:Expr.Call# +L: _&&_( + _&&_( + a^#1[1,0]#, + b^#3[1,5]# + )^#2[1,2]#, + c^#5[1,10]# +)^#4[1,7]# + +I: a && b && c && d && e && f && g +=====> +P: _&&_( + _&&_( + _&&_( + a^#1:Expr.Ident#, + b^#3:Expr.Ident# + )^#2:Expr.Call#, + _&&_( + c^#5:Expr.Ident#, + d^#7:Expr.Ident# + )^#6:Expr.Call# + )^#4:Expr.Call#, + _&&_( + _&&_( + e^#9:Expr.Ident#, + f^#11:Expr.Ident# + )^#10:Expr.Call#, + g^#13:Expr.Ident# + )^#12:Expr.Call# +)^#8:Expr.Call# +L: _&&_( + _&&_( + _&&_( + a^#1[1,0]#, + b^#3[1,5]# + )^#2[1,2]#, + _&&_( + c^#5[1,10]#, + d^#7[1,15]# + )^#6[1,12]# + )^#4[1,7]#, + _&&_( + _&&_( + e^#9[1,20]#, + f^#11[1,25]# + )^#10[1,22]#, + g^#13[1,30]# + )^#12[1,27]# +)^#8[1,17]# + +I: a > 5 && a < 10 +=====> +P: _&&_( + _>_( + a^#1:Expr.Ident#, + 5^#3:int64# + )^#2:Expr.Call#, + _<_( + a^#5:Expr.Ident#, + 10^#7:int64# + )^#6:Expr.Call# +)^#4:Expr.Call# +L: _&&_( + _>_( + a^#1[1,0]#, + 5^#3[1,4]# + )^#2[1,2]#, + _<_( + a^#5[1,9]#, + 10^#7[1,13]# + )^#6[1,11]# +)^#4[1,6]# + +I: a || b +=====> +P: _||_( + a^#1:Expr.Ident#, + b^#3:Expr.Ident# +)^#2:Expr.Call# +L: _||_( + a^#1[1,0]#, + b^#3[1,5]# +)^#2[1,2]# + +I: a || b || c || d || e || f +=====> +P: _||_( + _||_( + _||_( + a^#1:Expr.Ident#, + b^#3:Expr.Ident# + )^#2:Expr.Call#, + c^#5:Expr.Ident# + )^#4:Expr.Call#, + _||_( + _||_( + d^#7:Expr.Ident#, + e^#9:Expr.Ident# + )^#8:Expr.Call#, + f^#11:Expr.Ident# + )^#10:Expr.Call# +)^#6:Expr.Call# +L: _||_( + _||_( + _||_( + a^#1[1,0]#, + b^#3[1,5]# + )^#2[1,2]#, + c^#5[1,10]# + )^#4[1,7]#, + _||_( + _||_( + d^#7[1,15]#, + e^#9[1,20]# + )^#8[1,17]#, + f^#11[1,25]# + )^#10[1,22]# +)^#6[1,12]# + +I: a < 5 || a > 10 =====> -P: "\""^#1:string# -L: "\""^#1[1,0]# +P: _||_( + _<_( + a^#1:Expr.Ident#, + 5^#3:int64# + )^#2:Expr.Call#, + _>_( + a^#5:Expr.Ident#, + 10^#7:int64# + )^#6:Expr.Call# +)^#4:Expr.Call# +L: _||_( + _<_( + a^#1[1,0]#, + 5^#3[1,4]# + )^#2[1,2]#, + _>_( + a^#5[1,9]#, + 10^#7[1,13]# + )^#6[1,11]# +)^#4[1,6]# -I: [1,3,4][0] +I: a && b && c && d || e && f && g && h =====> -P: _[_]( - [ - 1^#2:int64#, - 3^#3:int64#, - 4^#4:int64# - ]^#1:Expr.CreateList#, - 0^#6:int64# -)^#5:Expr.Call# -L: _[_]( - [ - 1^#2[1,1]#, - 3^#3[1,3]#, - 4^#4[1,5]# - ]^#1[1,0]#, - 0^#6[1,8]# -)^#5[1,7]# +P: _||_( + _&&_( + _&&_( + a^#1:Expr.Ident#, + b^#3:Expr.Ident# + )^#2:Expr.Call#, + _&&_( + c^#5:Expr.Ident#, + d^#7:Expr.Ident# + )^#6:Expr.Call# + )^#4:Expr.Call#, + _&&_( + _&&_( + e^#9:Expr.Ident#, + f^#11:Expr.Ident# + )^#10:Expr.Call#, + _&&_( + g^#13:Expr.Ident#, + h^#15:Expr.Ident# + )^#14:Expr.Call# + )^#12:Expr.Call# +)^#8:Expr.Call# +L: _||_( + _&&_( + _&&_( + a^#1[1,0]#, + b^#3[1,5]# + )^#2[1,2]#, + _&&_( + c^#5[1,10]#, + d^#7[1,15]# + )^#6[1,12]# + )^#4[1,7]#, + _&&_( + _&&_( + e^#9[1,20]#, + f^#11[1,25]# + )^#10[1,22]#, + _&&_( + g^#13[1,30]#, + h^#15[1,35]# + )^#14[1,32]# + )^#12[1,27]# +)^#8[1,17]# -I: x["a"].single_int32 == 23 +I: a?b:c =====> -P: _==_( - _[_]( - x^#1:Expr.Ident#, - "a"^#3:string# - )^#2:Expr.Call#.single_int32^#4:Expr.Select#, - 23^#6:int64# -)^#5:Expr.Call# -L: _==_( - _[_]( - x^#1[1,0]#, - "a"^#3[1,2]# - )^#2[1,1]#.single_int32^#4[1,6]#, - 23^#6[1,23]# -)^#5[1,20]# +P: _?_:_( + a^#1:Expr.Ident#, + b^#3:Expr.Ident#, + c^#4:Expr.Ident# +)^#2:Expr.Call# +L: _?_:_( + a^#1[1,0]#, + b^#3[1,2]#, + c^#4[1,4]# +)^#2[1,1]# -I: x.single_nested_message != null +I: cond ? 1 : 2 =====> -P: _!=_( - x^#1:Expr.Ident#.single_nested_message^#2:Expr.Select#, - null^#4:NullValue# -)^#3:Expr.Call# -L: _!=_( - x^#1[1,0]#.single_nested_message^#2[1,1]#, - null^#4[1,27]# -)^#3[1,24]# +P: _?_:_( + cond^#1:Expr.Ident#, + 1^#3:int64#, + 2^#4:int64# +)^#2:Expr.Call# +L: _?_:_( + cond^#1[1,0]#, + 1^#3[1,7]#, + 2^#4[1,11]# +)^#2[1,5]# I: false && !true || false ? 2 : 3 =====> @@ -1146,16 +1116,56 @@ L: _?_:_( 3^#9[1,30]# )^#7[1,24]# -I: b"abc" + B"def" -=====> -P: _+_( - b"abc"^#1:bytes#, - b"def"^#3:bytes# -)^#2:Expr.Call# -L: _+_( - b"abc"^#1[1,0]#, - b"def"^#3[1,9]# -)^#2[1,7]# +I: true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : 1 +=====> + +I: !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x +=====> +E/A: ERROR: :1:3: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ..^ +ERROR: :1:5: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ....^ +ERROR: :1:7: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ......^ +ERROR: :1:9: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ........^ +ERROR: :1:11: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ..........^ +ERROR: :1:13: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ............^ +ERROR: :1:15: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ..............^ +ERROR: :1:17: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ................^ +ERROR: :1:19: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ..................^ +ERROR: :1:21: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ....................^ +ERROR: :1:23: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ......................^ +ERROR: :1:25: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ........................^ +ERROR: :1:27: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ..........................^ +ERROR: :1:29: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ............................^ +ERROR: :1:31: no viable alternative at input '-x' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-x + | ..............................^ I: 1 + 2 * 3 - 1 / 2 == 6 % 1 =====> @@ -1198,410 +1208,22 @@ L: _==_( )^#12[1,23]# )^#10[1,18]# -I: ---a -=====> -P: -_( - a^#2:Expr.Ident# -)^#1:Expr.Call# -L: -_( - a^#2[1,3]# -)^#1[1,0]# - -I: "\xC3\XBF" -=====> -P: "ÿ"^#1:string# -L: "ÿ"^#1[1,0]# - -I: "\303\277" -=====> -P: "ÿ"^#1:string# -L: "ÿ"^#1[1,0]# - -I: "hi\u263A \u263Athere" -=====> -P: "hi☺ ☺there"^#1:string# -L: "hi☺ ☺there"^#1[1,0]# - -I: "\U000003A8\?" -=====> -P: "Ψ?"^#1:string# -L: "Ψ?"^#1[1,0]# - -I: "\a\b\f\n\r\t\v'\"\\\? Legal escapes" -=====> -P: "\a\b\f\n\r\t\v'\"\? Legal escapes"^#1:string# -L: "\a\b\f\n\r\t\v'\"\? Legal escapes"^#1[1,0]# - -I: '😁' in ['😁', '😑', '😦'] -=====> -P: @in( - "😁"^#1:string#, - [ - "😁"^#4:string#, - "😑"^#5:string#, - "😦"^#6:string# - ]^#3:Expr.CreateList# -)^#2:Expr.Call# -L: @in( - "😁"^#1[1,0]#, - [ - "😁"^#4[1,8]#, - "😑"^#5[1,13]#, - "😦"^#6[1,18]# - ]^#3[1,7]# -)^#2[1,4]# - -I: [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['just fine'],[1],[2],[3],[4],[5]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] -=====> - -I: x.filter(y, y.filter(z, z > 0)) -=====> -P: __comprehension__( - // Variable - y, - // Target - x^#1:Expr.Ident#, - // Accumulator - @result, - // Init - []^#19:Expr.CreateList#, - // LoopCondition - true^#20:bool#, - // LoopStep - _?_:_( - __comprehension__( - // Variable - z, - // Target - y^#4:Expr.Ident#, - // Accumulator - @result, - // Init - []^#10:Expr.CreateList#, - // LoopCondition - true^#11:bool#, - // LoopStep - _?_:_( - _>_( - z^#7:Expr.Ident#, - 0^#9:int64# - )^#8:Expr.Call#, - _+_( - @result^#12:Expr.Ident#, - [ - z^#6:Expr.Ident# - ]^#13:Expr.CreateList# - )^#14:Expr.Call#, - @result^#15:Expr.Ident# - )^#16:Expr.Call#, - // Result - @result^#17:Expr.Ident#)^#18:Expr.Comprehension#, - _+_( - @result^#21:Expr.Ident#, - [ - y^#3:Expr.Ident# - ]^#22:Expr.CreateList# - )^#23:Expr.Call#, - @result^#24:Expr.Ident# - )^#25:Expr.Call#, - // Result - @result^#26:Expr.Ident#)^#27:Expr.Comprehension# -L: __comprehension__( - // Variable - y, - // Target - x^#1[1,0]#, - // Accumulator - @result, - // Init - []^#19[1,8]#, - // LoopCondition - true^#20[1,8]#, - // LoopStep - _?_:_( - __comprehension__( - // Variable - z, - // Target - y^#4[1,12]#, - // Accumulator - @result, - // Init - []^#10[1,20]#, - // LoopCondition - true^#11[1,20]#, - // LoopStep - _?_:_( - _>_( - z^#7[1,24]#, - 0^#9[1,28]# - )^#8[1,26]#, - _+_( - @result^#12[1,20]#, - [ - z^#6[1,21]# - ]^#13[1,20]# - )^#14[1,20]#, - @result^#15[1,20]# - )^#16[1,20]#, - // Result - @result^#17[1,20]#)^#18[1,20]#, - _+_( - @result^#21[1,8]#, - [ - y^#3[1,9]# - ]^#22[1,8]# - )^#23[1,8]#, - @result^#24[1,8]# - )^#25[1,8]#, - // Result - @result^#26[1,8]#)^#27[1,8]# -M: x^#1:Expr.Ident#.filter( - y^#3:Expr.Ident#, - ^#18:filter# -)^#0:Expr.Call#, -y^#4:Expr.Ident#.filter( - z^#6:Expr.Ident#, - _>_( - z^#7:Expr.Ident#, - 0^#9:int64# - )^#8:Expr.Call# -)^#0:Expr.Call# - -I: has(a.b).filter(c, c) -=====> -P: __comprehension__( - // Variable - c, - // Target - a^#2:Expr.Ident#.b~test-only~^#4:Expr.Select#, - // Accumulator - @result, - // Init - []^#8:Expr.CreateList#, - // LoopCondition - true^#9:bool#, - // LoopStep - _?_:_( - c^#7:Expr.Ident#, - _+_( - @result^#10:Expr.Ident#, - [ - c^#6:Expr.Ident# - ]^#11:Expr.CreateList# - )^#12:Expr.Call#, - @result^#13:Expr.Ident# - )^#14:Expr.Call#, - // Result - @result^#15:Expr.Ident#)^#16:Expr.Comprehension# -L: __comprehension__( - // Variable - c, - // Target - a^#2[1,4]#.b~test-only~^#4[1,3]#, - // Accumulator - @result, - // Init - []^#8[1,15]#, - // LoopCondition - true^#9[1,15]#, - // LoopStep - _?_:_( - c^#7[1,19]#, - _+_( - @result^#10[1,15]#, - [ - c^#6[1,16]# - ]^#11[1,15]# - )^#12[1,15]#, - @result^#13[1,15]# - )^#14[1,15]#, - // Result - @result^#15[1,15]#)^#16[1,15]# -M: ^#4:has#.filter( - c^#6:Expr.Ident#, - c^#7:Expr.Ident# -)^#0:Expr.Call#, -has( - a^#2:Expr.Ident#.b^#3:Expr.Select# -)^#0:Expr.Call# - -I: x.filter(y, y.exists(z, has(z.a)) && y.exists(z, has(z.b))) -=====> -P: __comprehension__( - // Variable - y, - // Target - x^#1:Expr.Ident#, - // Accumulator - @result, - // Init - []^#35:Expr.CreateList#, - // LoopCondition - true^#36:bool#, - // LoopStep - _?_:_( - _&&_( - __comprehension__( - // Variable - z, - // Target - y^#4:Expr.Ident#, - // Accumulator - @result, - // Init - false^#11:bool#, - // LoopCondition - @not_strictly_false( - !_( - @result^#12:Expr.Ident# - )^#13:Expr.Call# - )^#14:Expr.Call#, - // LoopStep - _||_( - @result^#15:Expr.Ident#, - z^#8:Expr.Ident#.a~test-only~^#10:Expr.Select# - )^#16:Expr.Call#, - // Result - @result^#17:Expr.Ident#)^#18:Expr.Comprehension#, - __comprehension__( - // Variable - z, - // Target - y^#20:Expr.Ident#, - // Accumulator - @result, - // Init - false^#27:bool#, - // LoopCondition - @not_strictly_false( - !_( - @result^#28:Expr.Ident# - )^#29:Expr.Call# - )^#30:Expr.Call#, - // LoopStep - _||_( - @result^#31:Expr.Ident#, - z^#24:Expr.Ident#.b~test-only~^#26:Expr.Select# - )^#32:Expr.Call#, - // Result - @result^#33:Expr.Ident#)^#34:Expr.Comprehension# - )^#19:Expr.Call#, - _+_( - @result^#37:Expr.Ident#, - [ - y^#3:Expr.Ident# - ]^#38:Expr.CreateList# - )^#39:Expr.Call#, - @result^#40:Expr.Ident# - )^#41:Expr.Call#, - // Result - @result^#42:Expr.Ident#)^#43:Expr.Comprehension# -L: __comprehension__( - // Variable - y, - // Target - x^#1[1,0]#, - // Accumulator - @result, - // Init - []^#35[1,8]#, - // LoopCondition - true^#36[1,8]#, - // LoopStep - _?_:_( - _&&_( - __comprehension__( - // Variable - z, - // Target - y^#4[1,12]#, - // Accumulator - @result, - // Init - false^#11[1,20]#, - // LoopCondition - @not_strictly_false( - !_( - @result^#12[1,20]# - )^#13[1,20]# - )^#14[1,20]#, - // LoopStep - _||_( - @result^#15[1,20]#, - z^#8[1,28]#.a~test-only~^#10[1,27]# - )^#16[1,20]#, - // Result - @result^#17[1,20]#)^#18[1,20]#, - __comprehension__( - // Variable - z, - // Target - y^#20[1,37]#, - // Accumulator - @result, - // Init - false^#27[1,45]#, - // LoopCondition - @not_strictly_false( - !_( - @result^#28[1,45]# - )^#29[1,45]# - )^#30[1,45]#, - // LoopStep - _||_( - @result^#31[1,45]#, - z^#24[1,53]#.b~test-only~^#26[1,52]# - )^#32[1,45]#, - // Result - @result^#33[1,45]#)^#34[1,45]# - )^#19[1,34]#, - _+_( - @result^#37[1,8]#, - [ - y^#3[1,9]# - ]^#38[1,8]# - )^#39[1,8]#, - @result^#40[1,8]# - )^#41[1,8]#, - // Result - @result^#42[1,8]#)^#43[1,8]# -M: x^#1:Expr.Ident#.filter( - y^#3:Expr.Ident#, - _&&_( - ^#18:exists#, - ^#34:exists# - )^#19:Expr.Call# -)^#0:Expr.Call#, -y^#20:Expr.Ident#.exists( - z^#22:Expr.Ident#, - ^#26:has# -)^#0:Expr.Call#, -has( - z^#24:Expr.Ident#.b^#25:Expr.Select# -)^#0:Expr.Call#, -y^#4:Expr.Ident#.exists( - z^#6:Expr.Ident#, - ^#10:has# -)^#0:Expr.Call#, -has( - z^#8:Expr.Ident#.a^#9:Expr.Select# -)^#0:Expr.Call# - -I: noop_macro(123) -=====> -P: noop_macro( - 123^#2:int64# -)^#1:Expr.Call# -L: noop_macro( - 123^#2[1,11]# -)^#1[1,10]# - -I: get_constant_macro() +I: x["a"].single_int32 == 23 =====> -P: 10^#1:int64# -L: 10^#1[NO_POS]# -M: get_constant_macro()^#0:Expr.Call# +P: _==_( + _[_]( + x^#1:Expr.Ident#, + "a"^#3:string# + )^#2:Expr.Call#.single_int32^#4:Expr.Select#, + 23^#6:int64# +)^#5:Expr.Call# +L: _==_( + _[_]( + x^#1[1,0]#, + "a"^#3[1,2]# + )^#2[1,1]#.single_int32^#4[1,6]#, + 23^#6[1,23]# +)^#5[1,20]# I: a.?b[?0] && a[?c] =====> @@ -1632,48 +1254,63 @@ L: _&&_( )^#8[1,13]# )^#6[1,9]# -I: {?'key': value} +I: [[[[[[[[[[[[[[[[[[[[[[[[[[[[[['just fine'],[1],[2],[3],[4],[5]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] =====> -P: { - ?"key"^#3:string#:value^#4:Expr.Ident#^#2:Expr.CreateStruct.Entry# -}^#1:Expr.CreateStruct# -L: { - ?"key"^#3[1,2]#:value^#4[1,9]#^#2[1,7]# -}^#1[1,0]# +E/A: ERROR: :1:92: mismatched input ']' expecting {, '==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', '.', '-', '?', '+', '*', '/', '%%'} + | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[['just fine'],[1],[2],[3],[4],[5]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] + | ...........................................................................................^ +E/P: ERROR: :1:92: Syntax error: unexpected token after expression + | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[['just fine'],[1],[2],[3],[4],[5]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] + | ...........................................................................................^ -I: Msg{?field: value} +I: // comment +a =====> -P: Msg{ - ?field:value^#3:Expr.Ident#^#2:Expr.CreateStruct.Entry# -}^#1:Expr.CreateStruct# -L: Msg{ - ?field:value^#3[1,12]#^#2[1,10]# -}^#1[1,3]# +P: a^#1:Expr.Ident# +L: a^#1[2,0]# -I: [?a, ?b] +I: a // comment =====> -P: [ - ?a^#2:Expr.Ident#, - ?b^#3:Expr.Ident# -]^#1:Expr.CreateList# -L: [ - ?a^#2[1,2]#, - ?b^#3[1,6]# -]^#1[1,0]# +P: a^#1:Expr.Ident# +L: a^#1[1,0]# -I: [?a[?b]] +I: a +// comment ++ b +=====> +P: _+_( + a^#1:Expr.Ident#, + b^#3:Expr.Ident# +)^#2:Expr.Call# +L: _+_( + a^#1[1,0]#, + b^#3[3,2]# +)^#2[3,0]# + +I: a / // comment + b +=====> +P: _/_( + a^#1:Expr.Ident#, + b^#3:Expr.Ident# +)^#2:Expr.Call# +L: _/_( + a^#1[1,0]#, + b^#3[2,2]# +)^#2[1,2]# + +I: [ + 1, // comment + 2, +] =====> P: [ - ?_[?_]( - a^#2:Expr.Ident#, - b^#4:Expr.Ident# - )^#3:Expr.Call# + 1^#2:int64#, + 2^#3:int64# ]^#1:Expr.CreateList# L: [ - ?_[?_]( - a^#2[1,2]#, - b^#4[1,5]# - )^#3[1,3]# + 1^#2[2,2]#, + 2^#3[3,2]# ]^#1[1,0]# I: while diff --git a/parser/src/test/resources/parser_errors.baseline b/parser/src/test/resources/parser_errors.baseline index bb4ab3ed3..cbd9f087b 100644 --- a/parser/src/test/resources/parser_errors.baseline +++ b/parser/src/test/resources/parser_errors.baseline @@ -1,6 +1,6 @@ I: *@a | b =====> -E: ERROR: :1:1: extraneous input '*' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} +E/A: ERROR: :1:1: extraneous input '*' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} | *@a | b | ^ ERROR: :1:2: token recognition error at: '@' @@ -12,271 +12,505 @@ ERROR: :1:5: token recognition error at: '| ' ERROR: :1:7: extraneous input 'b' expecting | *@a | b | ......^ +E/P: ERROR: :1:1: Syntax error: unexpected token + | *@a | b + | ^ +ERROR: :1:2: Syntax error: unexpected character + | *@a | b + | .^ -I: a | b +I: ((@)) =====> -E: ERROR: :1:3: token recognition error at: '| ' - | a | b +E/A: ERROR: :1:3: token recognition error at: '@' + | ((@)) + | ..^ +ERROR: :1:4: mismatched input ')' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} + | ((@)) + | ...^ +E/P: ERROR: :1:3: Syntax error: unexpected character + | ((@)) | ..^ -ERROR: :1:5: extraneous input 'b' expecting - | a | b - | ....^ - -I: ? -=====> -E: ERROR: :1:1: mismatched input '?' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} - | ? - | ^ -ERROR: :1:2: mismatched input '' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} - | ? - | .^ I: 1 + $ =====> -E: ERROR: :1:5: token recognition error at: '$' +E/A: ERROR: :1:5: token recognition error at: '$' | 1 + $ | ....^ ERROR: :1:6: mismatched input '' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} | 1 + $ | .....^ +E/P: ERROR: :1:5: Syntax error: unexpected character + | 1 + $ + | ....^ -I: 1.all(2, 3) -=====> -E: ERROR: :1:7: The argument must be a simple name - | 1.all(2, 3) - | ......^ - -I: 1.exists(2, 3) -=====> -E: ERROR: :1:10: The argument must be a simple name - | 1.exists(2, 3) - | .........^ - -I: [].all(__result__, x) -=====> -E: ERROR: :1:8: The iteration variable __result__ overwrites accumulator variable - | [].all(__result__, x) - | .......^ - -I: [].exists(__result__, x) +I: ó ¢ +»»ó 0  +»»\u007f0"""\""\"""\""\"""\""\"""\""\"""\"\"""\""\"""\""\"""\""\"""\"!\"""\""\"""\""\" =====> -E: ERROR: :1:11: The iteration variable __result__ overwrites accumulator variable - | [].exists(__result__, x) +E/A: ERROR: :1:1: token recognition error at: 'ó' + | ó ¢ + | ^ +ERROR: :1:2: token recognition error at: ' ' + | ó ¢ + | .^ +ERROR: :1:3: token recognition error at: '¢' + | ó ¢ + | ..^ +ERROR: :2:3: token recognition error at: 'ó' + | ó 0  + | ..^ +ERROR: :2:4: token recognition error at: ' ' + | ó 0  + | ...^ +ERROR: :2:6: token recognition error at: ' ' + | ó 0  + | .....^ +ERROR: :3:3: token recognition error at: '\u007f' + | \u007f0"""\""\"""\""\"""\""\"""\""\"""\"\"""\""\"""\""\"""\""\"""\"!\"""\""\"""\""\" + | ..^ +ERROR: :3:4: mismatched input '0' expecting {, '==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', '.', '-', '?', '+', '*', '/', '%%'} + | \u007f0"""\""\"""\""\"""\""\"""\""\"""\"\"""\""\"""\""\"""\""\"""\"!\"""\""\"""\""\" + | ...^ +ERROR: :3:11: token recognition error at: '\' + | \u007f0"""\""\"""\""\"""\""\"""\""\"""\"\"""\""\"""\""\"""\""\"""\"!\"""\""\"""\""\" | ..........^ +ERROR: :3:18: token recognition error at: '\' + | \u007f0"""\""\"""\""\"""\""\"""\""\"""\"\"""\""\"""\""\"""\""\"""\"!\"""\""\"""\""\" + | .................^ +ERROR: :3:25: token recognition error at: '\' + | \u007f0"""\""\"""\""\"""\""\"""\""\"""\"\"""\""\"""\""\"""\""\"""\"!\"""\""\"""\""\" + | ........................^ +ERROR: :3:32: token recognition error at: '\' + | \u007f0"""\""\"""\""\"""\""\"""\""\"""\"\"""\""\"""\""\"""\""\"""\"!\"""\""\"""\""\" + | ...............................^ +ERROR: :3:45: token recognition error at: '\' + | \u007f0"""\""\"""\""\"""\""\"""\""\"""\"\"""\""\"""\""\"""\""\"""\"!\"""\""\"""\""\" + | ............................................^ +ERROR: :3:52: token recognition error at: '\' + | \u007f0"""\""\"""\""\"""\""\"""\""\"""\"\"""\""\"""\""\"""\""\"""\"!\"""\""\"""\""\" + | ...................................................^ +ERROR: :3:59: token recognition error at: '\' + | \u007f0"""\""\"""\""\"""\""\"""\""\"""\"\"""\""\"""\""\"""\""\"""\"!\"""\""\"""\""\" + | ..........................................................^ +ERROR: :3:73: token recognition error at: '\' + | \u007f0"""\""\"""\""\"""\""\"""\""\"""\"\"""\""\"""\""\"""\""\"""\"!\"""\""\"""\""\" + | ........................................................................^ +ERROR: :3:80: token recognition error at: '\' + | \u007f0"""\""\"""\""\"""\""\"""\""\"""\"\"""\""\"""\""\"""\""\"""\"!\"""\""\"""\""\" + | ...............................................................................^ +ERROR: :3:81: token recognition error at: '"' + | \u007f0"""\""\"""\""\"""\""\"""\""\"""\"\"""\""\"""\""\"""\""\"""\"!\"""\""\"""\""\" + | ................................................................................^ +E/P: ERROR: :1:1: Syntax error: unexpected character + | ó ¢ + | ^ +ERROR: :1:2: Syntax error: unexpected character + | ó ¢ + | .^ -I: [].exists_one(__result__, x) -=====> -E: ERROR: :1:15: The iteration variable __result__ overwrites accumulator variable - | [].exists_one(__result__, x) - | ..............^ - -I: [].map(__result__, x, x) -=====> -E: ERROR: :1:8: The iteration variable __result__ overwrites accumulator variable - | [].map(__result__, x, x) - | .......^ - -I: [].filter(__result__, x) +I: '\udead' == '\ufffd' =====> -E: ERROR: :1:11: The iteration variable __result__ overwrites accumulator variable - | [].filter(__result__, x) - | ..........^ +E/A: ERROR: :1:1: Invalid unicode code point + | '\udead' == '\ufffd' + | ^ +E/P: ERROR: :1:1: Invalid unicode code point + | '\udead' == '\ufffd' + | ^ -I: [].all(.x, x) +I: a | b =====> -E: ERROR: :1:9: The argument must be a simple name - | [].all(.x, x) - | ........^ +E/A: ERROR: :1:3: token recognition error at: '| ' + | a | b + | ..^ +ERROR: :1:5: extraneous input 'b' expecting + | a | b + | ....^ +E/P: ERROR: :1:3: Syntax error: unexpected single '|', expected '||' + | a | b + | ..^ -I: [].exists(.x, x) +I: '3# < 10" '& tru ^^ =====> -E: ERROR: :1:12: The argument must be a simple name - | [].exists(.x, x) +E/A: ERROR: :1:12: token recognition error at: '& ' + | '3# < 10" '& tru ^^ + | ...........^ +ERROR: :1:14: extraneous input 'tru' expecting + | '3# < 10" '& tru ^^ + | .............^ +ERROR: :1:18: token recognition error at: '^' + | '3# < 10" '& tru ^^ + | .................^ +ERROR: :1:19: token recognition error at: '^' + | '3# < 10" '& tru ^^ + | ..................^ +E/P: ERROR: :1:12: Syntax error: unexpected single '&', expected '&&' + | '3# < 10" '& tru ^^ | ...........^ -I: [].exists_one(.x, x) +I: '?' =====> -E: ERROR: :1:16: The argument must be a simple name - | [].exists_one(.x, x) - | ...............^ +E/A: ERROR: :1:1: Invalid unicode code point + | '?' + | ^ +E/P: ERROR: :1:1: Invalid unicode code point + | '?' + | ^ -I: [].map(.x, x, x) +I: '?' =====> -E: ERROR: :1:9: The argument must be a simple name - | [].map(.x, x, x) - | ........^ +E/A: ERROR: :1:1: Invalid unicode code point + | '?' + | ^ +E/P: ERROR: :1:1: Invalid unicode code point + | '?' + | ^ -I: [].filter(.x, x) +I: r"\?" =====> -E: ERROR: :1:12: The argument must be a simple name - | [].filter(.x, x) - | ...........^ +E/A: ERROR: :1:1: Invalid unicode code point + | r"\?" + | ^ +E/P: ERROR: :1:1: Invalid unicode code point + | r"\?" + | ^ I: 1 + + =====> -E: ERROR: :1:5: mismatched input '+' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} +E/A: ERROR: :1:5: mismatched input '+' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} | 1 + + | ....^ ERROR: :1:6: mismatched input '' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} | 1 + + | .....^ +E/P: ERROR: :1:5: Syntax error: unexpected token + | 1 + + + | ....^ -I: "\xFh" +I: ? =====> -E: ERROR: :1:1: token recognition error at: '"\xFh' - | "\xFh" +E/A: ERROR: :1:1: mismatched input '?' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} + | ? + | ^ +ERROR: :1:2: mismatched input '' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} + | ? + | .^ +E/P: ERROR: :1:1: Syntax error: unexpected token + | ? | ^ -ERROR: :1:6: token recognition error at: '"' - | "\xFh" - | .....^ -ERROR: :1:7: mismatched input '' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} - | "\xFh" - | ......^ -I: "\a\b\f\n\r\t\v\'\"\\\? Illegal escape \>" +I: a ? b ((?)) =====> -E: ERROR: :1:1: token recognition error at: '"\a\b\f\n\r\t\v\'\"\\\? Illegal escape \>' - | "\a\b\f\n\r\t\v\'\"\\\? Illegal escape \>" - | ^ -ERROR: :1:42: token recognition error at: '"' - | "\a\b\f\n\r\t\v\'\"\\\? Illegal escape \>" - | .........................................^ -ERROR: :1:43: mismatched input '' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} - | "\a\b\f\n\r\t\v\'\"\\\? Illegal escape \>" - | ..........................................^ +E/A: ERROR: :1:9: mismatched input '?' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} + | a ? b ((?)) + | ........^ +ERROR: :1:10: mismatched input ')' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} + | a ? b ((?)) + | .........^ +ERROR: :1:12: mismatched input '' expecting {'==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', ')', '.', ',', '-', '?', '+', '*', '/', '%%'} + | a ? b ((?)) + | ...........^ +E/P: ERROR: :1:9: Syntax error: unexpected token + | a ? b ((?)) + | ........^ +ERROR: :1:12: Syntax error: expected ':' in conditional expression + | a ? b ((?)) + | ...........^ -I: '?' +I: a ? b @ =====> -E: ERROR: :1:1: Invalid unicode code point - | '?' - | ^ +E/A: ERROR: :1:7: token recognition error at: '@' + | a ? b @ + | ......^ +ERROR: :1:8: mismatched input '' expecting {'==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', '.', '-', ':', '+', '*', '/', '%%'} + | a ? b @ + | .......^ +E/P: ERROR: :1:7: Syntax error: unexpected character + | a ? b @ + | ......^ -I: '?' +I: -[-1--1--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1 +»»--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--3--1--1--0--1--1--1--1--0--1--1--1 +»»--3-[-1--1--1--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1 +»»--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1 +»»--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1 +»»--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1 +»»--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--3--1--1--0--1--1--1--1--0--1--1--1 +»»--3-[-1--1--1--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1 +»»--3-[-1--1--1--1---1-1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1 +»»--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1 +»»--1--1---1--1-À1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1 +»»--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1 +»»--1--1---1--1--1--0--1--1--1--1--0--3--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1 +»»--1--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1 +»»--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1 +»»--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1 +»»--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1 +»»--1--1---1--1--1--0--1--1--1--1--0--3--1--1--0--1 +»»--1--0--1--1--1--3-[-1--1--1--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1 +»»--1--0--1--1--1--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1 +»»--1--0--1--1--1--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1 +»»--1--0--1--1--1--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1 +»»--1--0--1--1--1--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--3--1--1--0--1--1--1 +»»--1--0--1--1--1--3-[-1--1--1--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1 +»»--1--0--1--1--1--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1 +»»--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1 +»»--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1 +»»--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1 +»»--1---1--1--1--0--1--1--1--1--0--3--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1 +»»--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1 +»»--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1 +»»--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1 +»»--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1 +»»--1---1--1--1--0--1--1--1--1--0--3--1--1--0--1 =====> -E: ERROR: :1:1: Invalid unicode code point - | '?' - | ^ +E/A: More than 30 parse errors. +E/P: ERROR: :3:33: Syntax error: unexpected token + | --3-[-1--1--1--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1 + | ................................^ +ERROR: :3:34: Syntax error: expected ']' + | --3-[-1--1--1--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1 + | .................................^ +ERROR: :11:17: Syntax error: unexpected character + | --1--1---1--1-À1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1 + | ................^ +ERROR: :34:49: Syntax error: expected ']' + | --1---1--1--1--0--1--1--1--1--0--3--1--1--0--1 + | ................................................^ +ERROR: :34:49: Syntax error: expected ']' + | --1---1--1--1--0--1--1--1--1--0--3--1--1--0--1 + | ................................................^ -I: r"\?" +I: as break const continue else for function if import in let loop package namespace return var void while =====> -E: ERROR: :1:1: Invalid unicode code point - | r"\?" +E/A: ERROR: :1:1: reserved identifier: as + | as break const continue else for function if import in let loop package namespace return var void while + | ^ +ERROR: :1:4: mismatched input 'break' expecting {, '==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', '.', '-', '?', '+', '*', '/', '%%'} + | as break const continue else for function if import in let loop package namespace return var void while + | ...^ +E/P: ERROR: :1:1: reserved identifier: as + | as break const continue else for function if import in let loop package namespace return var void while | ^ +ERROR: :1:4: reserved identifier: break + | as break const continue else for function if import in let loop package namespace return var void while + | ...^ +ERROR: :1:10: reserved identifier: const + | as break const continue else for function if import in let loop package namespace return var void while + | .........^ +ERROR: :1:16: reserved identifier: continue + | as break const continue else for function if import in let loop package namespace return var void while + | ...............^ +ERROR: :1:25: reserved identifier: else + | as break const continue else for function if import in let loop package namespace return var void while + | ........................^ +ERROR: :1:30: reserved identifier: for + | as break const continue else for function if import in let loop package namespace return var void while + | .............................^ +ERROR: :1:34: reserved identifier: function + | as break const continue else for function if import in let loop package namespace return var void while + | .................................^ +ERROR: :1:43: reserved identifier: if + | as break const continue else for function if import in let loop package namespace return var void while + | ..........................................^ +ERROR: :1:46: reserved identifier: import + | as break const continue else for function if import in let loop package namespace return var void while + | .............................................^ +ERROR: :1:53: reserved identifier: in + | as break const continue else for function if import in let loop package namespace return var void while + | ....................................................^ +ERROR: :1:56: reserved identifier: let + | as break const continue else for function if import in let loop package namespace return var void while + | .......................................................^ +ERROR: :1:60: reserved identifier: loop + | as break const continue else for function if import in let loop package namespace return var void while + | ...........................................................^ +ERROR: :1:65: reserved identifier: package + | as break const continue else for function if import in let loop package namespace return var void while + | ................................................................^ +ERROR: :1:73: reserved identifier: namespace + | as break const continue else for function if import in let loop package namespace return var void while + | ........................................................................^ +ERROR: :1:83: reserved identifier: return + | as break const continue else for function if import in let loop package namespace return var void while + | ..................................................................................^ +ERROR: :1:90: reserved identifier: var + | as break const continue else for function if import in let loop package namespace return var void while + | .........................................................................................^ +ERROR: :1:94: reserved identifier: void + | as break const continue else for function if import in let loop package namespace return var void while + | .............................................................................................^ +ERROR: :1:99: reserved identifier: while + | as break const continue else for function if import in let loop package namespace return var void while + | ..................................................................................................^ I: as =====> -E: ERROR: :1:1: reserved identifier: as +E/A: ERROR: :1:1: reserved identifier: as + | as + | ^ +E/P: ERROR: :1:1: reserved identifier: as | as | ^ I: break =====> -E: ERROR: :1:1: reserved identifier: break +E/A: ERROR: :1:1: reserved identifier: break + | break + | ^ +E/P: ERROR: :1:1: reserved identifier: break | break | ^ I: const =====> -E: ERROR: :1:1: reserved identifier: const +E/A: ERROR: :1:1: reserved identifier: const + | const + | ^ +E/P: ERROR: :1:1: reserved identifier: const | const | ^ I: continue =====> -E: ERROR: :1:1: reserved identifier: continue +E/A: ERROR: :1:1: reserved identifier: continue + | continue + | ^ +E/P: ERROR: :1:1: reserved identifier: continue | continue | ^ I: else =====> -E: ERROR: :1:1: reserved identifier: else +E/A: ERROR: :1:1: reserved identifier: else + | else + | ^ +E/P: ERROR: :1:1: reserved identifier: else | else | ^ I: for =====> -E: ERROR: :1:1: reserved identifier: for +E/A: ERROR: :1:1: reserved identifier: for + | for + | ^ +E/P: ERROR: :1:1: reserved identifier: for | for | ^ I: function =====> -E: ERROR: :1:1: reserved identifier: function +E/A: ERROR: :1:1: reserved identifier: function + | function + | ^ +E/P: ERROR: :1:1: reserved identifier: function | function | ^ I: if =====> -E: ERROR: :1:1: reserved identifier: if +E/A: ERROR: :1:1: reserved identifier: if + | if + | ^ +E/P: ERROR: :1:1: reserved identifier: if | if | ^ I: import =====> -E: ERROR: :1:1: reserved identifier: import +E/A: ERROR: :1:1: reserved identifier: import + | import + | ^ +E/P: ERROR: :1:1: reserved identifier: import | import | ^ I: in =====> -E: ERROR: :1:1: mismatched input 'in' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} +E/A: ERROR: :1:1: mismatched input 'in' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} | in | ^ ERROR: :1:3: mismatched input '' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} | in | ..^ +E/P: ERROR: :1:1: Syntax error: unexpected token + | in + | ^ I: let =====> -E: ERROR: :1:1: reserved identifier: let +E/A: ERROR: :1:1: reserved identifier: let + | let + | ^ +E/P: ERROR: :1:1: reserved identifier: let | let | ^ I: loop =====> -E: ERROR: :1:1: reserved identifier: loop +E/A: ERROR: :1:1: reserved identifier: loop + | loop + | ^ +E/P: ERROR: :1:1: reserved identifier: loop | loop | ^ I: package =====> -E: ERROR: :1:1: reserved identifier: package +E/A: ERROR: :1:1: reserved identifier: package + | package + | ^ +E/P: ERROR: :1:1: reserved identifier: package | package | ^ I: namespace =====> -E: ERROR: :1:1: reserved identifier: namespace +E/A: ERROR: :1:1: reserved identifier: namespace + | namespace + | ^ +E/P: ERROR: :1:1: reserved identifier: namespace | namespace | ^ I: return =====> -E: ERROR: :1:1: reserved identifier: return +E/A: ERROR: :1:1: reserved identifier: return + | return + | ^ +E/P: ERROR: :1:1: reserved identifier: return | return | ^ I: var =====> -E: ERROR: :1:1: reserved identifier: var +E/A: ERROR: :1:1: reserved identifier: var + | var + | ^ +E/P: ERROR: :1:1: reserved identifier: var | var | ^ I: void =====> -E: ERROR: :1:1: reserved identifier: void +E/A: ERROR: :1:1: reserved identifier: void + | void + | ^ +E/P: ERROR: :1:1: reserved identifier: void | void | ^ I: while =====> -E: ERROR: :1:1: reserved identifier: while +E/A: ERROR: :1:1: reserved identifier: while + | while + | ^ +E/P: ERROR: :1:1: reserved identifier: while | while | ^ I: [1, 2, 3].map(var, var * var) =====> -E: ERROR: :1:15: reserved identifier: var +E/A: ERROR: :1:15: reserved identifier: var | [1, 2, 3].map(var, var * var) | ..............^ ERROR: :1:15: The argument must be a simple name @@ -288,11 +522,20 @@ ERROR: :1:20: reserved identifier: var ERROR: :1:26: reserved identifier: var | [1, 2, 3].map(var, var * var) | .........................^ +E/P: ERROR: :1:15: reserved identifier: var + | [1, 2, 3].map(var, var * var) + | ..............^ +ERROR: :1:20: reserved identifier: var + | [1, 2, 3].map(var, var * var) + | ...................^ +ERROR: :1:26: reserved identifier: var + | [1, 2, 3].map(var, var * var) + | .........................^ I: '😁' in ['😁', '😑', '😦'] && in.😁 =====> -E: ERROR: :2:7: extraneous input 'in' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} +E/A: ERROR: :2:7: extraneous input 'in' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} | && in.😁 | ......^ ERROR: :2:10: token recognition error at: '😁' @@ -301,125 +544,747 @@ ERROR: :2:10: token recognition error at: '😁' ERROR: :2:11: no viable alternative at input '.' | && in.😁 | ..........^ +E/P: ERROR: :2:7: Syntax error: unexpected token + | && in.😁 + | ......^ +ERROR: :2:10: Syntax error: unexpected character + | && in.😁 + | .........^ -I: [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['too many']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] +I: 1 + =====> -E: Expression recursion limit exceeded. limit: 250 +E/A: ERROR: :1:4: mismatched input '' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} + | 1 + + | ...^ +E/P: ERROR: :1:4: Syntax error: mismatched input '' expecting expression + | 1 + + | ...^ -I: {"a": 1}."a" +I: -- =====> -E: ERROR: :1:10: no viable alternative at input '."a"' - | {"a": 1}."a" - | .........^ +E/A: ERROR: :1:3: no viable alternative at input '-' + | -- + | ..^ +ERROR: :1:3: mismatched input '' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} + | -- + | ..^ +E/P: ERROR: :1:3: Syntax error: mismatched input '' expecting expression + | -- + | ..^ -I: 1 + 2 -3 + +I: { =====> -E: ERROR: :2:1: mismatched input '3' expecting {, '==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', '.', '-', '?', '+', '*', '/', '%%'} - | 3 + +E/A: ERROR: :1:2: mismatched input '' expecting {'[', '{', '}', '(', '.', ',', '-', '!', '?', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} + | { + | .^ +E/P: ERROR: :1:2: Syntax error: expected '}' + | { + | .^ + +I: 0x +=====> +E/A: ERROR: :1:2: extraneous input 'x' expecting + | 0x + | .^ +E/P: ERROR: :1:1: Syntax error: integral literal missing digits after hexadecimal separator + | 0x | ^ +I: TestAllTypes(){} +=====> +E/A: ERROR: :1:15: mismatched input '{' expecting {, '==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', '.', '-', '?', '+', '*', '/', '%%'} + | TestAllTypes(){} + | ..............^ +E/P: ERROR: :1:15: Syntax error: unexpected token after expression + | TestAllTypes(){} + | ..............^ + +I: TestAllTypes{}() +=====> +E/A: ERROR: :1:15: mismatched input '(' expecting {, '==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', '.', '-', '?', '+', '*', '/', '%%'} + | TestAllTypes{}() + | ..............^ +E/P: ERROR: :1:15: Syntax error: unexpected token after expression + | TestAllTypes{}() + | ..............^ + I: TestAllTypes(){single_int32: 1, single_int64: 2} =====> -E: ERROR: :1:15: mismatched input '{' expecting {, '==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', '.', '-', '?', '+', '*', '/', '%%'} +E/A: ERROR: :1:15: mismatched input '{' expecting {, '==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', '.', '-', '?', '+', '*', '/', '%%'} + | TestAllTypes(){single_int32: 1, single_int64: 2} + | ..............^ +E/P: ERROR: :1:15: Syntax error: unexpected token after expression | TestAllTypes(){single_int32: 1, single_int64: 2} | ..............^ -I: { +I: 1 + 2 +3 + =====> -E: ERROR: :1:2: mismatched input '' expecting {'[', '{', '}', '(', '.', ',', '-', '!', '?', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} - | { +E/A: ERROR: :2:1: mismatched input '3' expecting {, '==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', '.', '-', '?', '+', '*', '/', '%%'} + | 3 + + | ^ +E/P: ERROR: :2:1: Syntax error: unexpected token after expression + | 3 + + | ^ + +I: {"a": 1}."a" +=====> +E/A: ERROR: :1:10: no viable alternative at input '."a"' + | {"a": 1}."a" + | .........^ +E/P: ERROR: :1:10: Syntax error: expected identifier after '.' + | {"a": 1}."a" + | .........^ + +I: self.true == 1 +=====> +E/A: ERROR: :1:6: no viable alternative at input '.true' + | self.true == 1 + | .....^ +E/P: ERROR: :1:6: Syntax error: expected identifier after '.' + | self.true == 1 + | .....^ + +I: {a} +=====> +E/A: ERROR: :1:3: mismatched input '}' expecting {'==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', '.', '-', '?', ':', '+', '*', '/', '%%'} + | {a} + | ..^ +E/P: ERROR: :1:3: Syntax error: expected ':' in map entry + | {a} + | ..^ + +I: {:a} +=====> +E/A: ERROR: :1:2: extraneous input ':' expecting {'[', '{', '}', '(', '.', ',', '-', '!', '?', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} + | {:a} | .^ +ERROR: :1:4: mismatched input '}' expecting {'==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', '.', '-', '?', ':', '+', '*', '/', '%%'} + | {:a} + | ...^ +E/P: ERROR: :1:2: Syntax error: unexpected token + | {:a} + | .^ +ERROR: :1:3: Syntax error: expected ':' in map entry + | {:a} + | ..^ + +I: func{{a}} +=====> +E/A: ERROR: :1:6: extraneous input '{' expecting {'}', ',', '?', IDENTIFIER, ESC_IDENTIFIER} + | func{{a}} + | .....^ +ERROR: :1:8: mismatched input '}' expecting ':' + | func{{a}} + | .......^ +ERROR: :1:9: extraneous input '}' expecting + | func{{a}} + | ........^ +E/P: ERROR: :1:6: Syntax error: expected struct field name + | func{{a}} + | .....^ +ERROR: :1:9: Syntax error: unexpected token after expression + | func{{a}} + | ........^ + +I: msg{:a} +=====> +E/A: ERROR: :1:5: extraneous input ':' expecting {'}', ',', '?', IDENTIFIER, ESC_IDENTIFIER} + | msg{:a} + | ....^ +ERROR: :1:7: mismatched input '}' expecting ':' + | msg{:a} + | ......^ +E/P: ERROR: :1:5: Syntax error: expected struct field name + | msg{:a} + | ....^ + +I: ind[a{b}] +=====> +E/A: ERROR: :1:8: mismatched input '}' expecting ':' + | ind[a{b}] + | .......^ +E/P: ERROR: :1:8: Syntax error: expected ':' in struct field + | ind[a{b}] + | .......^ + +I: x{?. +=====> +E/A: ERROR: :1:4: mismatched input '.' expecting {IDENTIFIER, ESC_IDENTIFIER} + | x{?. + | ...^ +ERROR: :1:4: unsupported identifier + | x{?. + | ...^ +E/P: ERROR: :1:4: Syntax error: expected struct field name + | x{?. + | ...^ +ERROR: :1:5: Syntax error: expected '}' + | x{?. + | ....^ + +I: x{. +=====> +E/A: ERROR: :1:3: mismatched input '.' expecting {'}', ',', '?', IDENTIFIER, ESC_IDENTIFIER} + | x{. + | ..^ +E/P: ERROR: :1:3: Syntax error: expected struct field name + | x{. + | ..^ +ERROR: :1:4: Syntax error: expected '}' + | x{. + | ...^ I: t{>C} =====> -E: ERROR: :1:3: extraneous input '>' expecting {'}', ',', '?', IDENTIFIER, ESC_IDENTIFIER} +E/A: ERROR: :1:3: extraneous input '>' expecting {'}', ',', '?', IDENTIFIER, ESC_IDENTIFIER} | t{>C} | ..^ ERROR: :1:5: mismatched input '}' expecting ':' | t{>C} | ....^ +E/P: ERROR: :1:3: Syntax error: expected struct field name + | t{>C} + | ..^ I: has([(has(( =====> -E: ERROR: :1:4: invalid argument to has() macro +E/A: ERROR: :1:4: invalid argument to has() macro | has([(has(( | ...^ ERROR: :1:12: mismatched input '' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} | has([(has(( | ...........^ +E/P: ERROR: :1:4: invalid argument to has() macro + | has([(has(( + | ...^ +ERROR: :1:10: invalid argument to has() macro + | has([(has(( + | .........^ +ERROR: :1:12: Syntax error: mismatched input '' expecting expression + | has([(has(( + | ...........^ +ERROR: :1:12: Syntax error: mismatched input expecting ')' + | has([(has(( + | ...........^ +ERROR: :1:12: Syntax error: mismatched input expecting ')' + | has([(has(( + | ...........^ +ERROR: :1:12: Syntax error: mismatched input expecting ')' + | has([(has(( + | ...........^ +ERROR: :1:12: Syntax error: expected ']' + | has([(has(( + | ...........^ +ERROR: :1:12: Syntax error: mismatched input expecting ')' + | has([(has(( + | ...........^ + +I: 1.all(2, 3) +=====> +E/A: ERROR: :1:7: The argument must be a simple name + | 1.all(2, 3) + | ......^ +E/P: ERROR: :1:7: The argument must be a simple name + | 1.all(2, 3) + | ......^ + +I: 1.exists(2, 3) +=====> +E/A: ERROR: :1:10: The argument must be a simple name + | 1.exists(2, 3) + | .........^ +E/P: ERROR: :1:10: The argument must be a simple name + | 1.exists(2, 3) + | .........^ + +I: [].all(__result__, x) +=====> +E/A: ERROR: :1:8: The iteration variable __result__ overwrites accumulator variable + | [].all(__result__, x) + | .......^ +E/P: ERROR: :1:8: The iteration variable __result__ overwrites accumulator variable + | [].all(__result__, x) + | .......^ + +I: [].exists(__result__, x) +=====> +E/A: ERROR: :1:11: The iteration variable __result__ overwrites accumulator variable + | [].exists(__result__, x) + | ..........^ +E/P: ERROR: :1:11: The iteration variable __result__ overwrites accumulator variable + | [].exists(__result__, x) + | ..........^ + +I: [].exists_one(__result__, x) +=====> +E/A: ERROR: :1:15: The iteration variable __result__ overwrites accumulator variable + | [].exists_one(__result__, x) + | ..............^ +E/P: ERROR: :1:15: The iteration variable __result__ overwrites accumulator variable + | [].exists_one(__result__, x) + | ..............^ + +I: [].map(__result__, x, x) +=====> +E/A: ERROR: :1:8: The iteration variable __result__ overwrites accumulator variable + | [].map(__result__, x, x) + | .......^ +E/P: ERROR: :1:8: The iteration variable __result__ overwrites accumulator variable + | [].map(__result__, x, x) + | .......^ + +I: [].filter(__result__, x) +=====> +E/A: ERROR: :1:11: The iteration variable __result__ overwrites accumulator variable + | [].filter(__result__, x) + | ..........^ +E/P: ERROR: :1:11: The iteration variable __result__ overwrites accumulator variable + | [].filter(__result__, x) + | ..........^ + +I: [].all(.x, x) +=====> +E/A: ERROR: :1:9: The argument must be a simple name + | [].all(.x, x) + | ........^ +E/P: ERROR: :1:8: The argument must be a simple name + | [].all(.x, x) + | .......^ + +I: [].exists(.x, x) +=====> +E/A: ERROR: :1:12: The argument must be a simple name + | [].exists(.x, x) + | ...........^ +E/P: ERROR: :1:11: The argument must be a simple name + | [].exists(.x, x) + | ..........^ + +I: [].exists_one(.x, x) +=====> +E/A: ERROR: :1:16: The argument must be a simple name + | [].exists_one(.x, x) + | ...............^ +E/P: ERROR: :1:15: The argument must be a simple name + | [].exists_one(.x, x) + | ..............^ + +I: [].map(.x, x, x) +=====> +E/A: ERROR: :1:9: The argument must be a simple name + | [].map(.x, x, x) + | ........^ +E/P: ERROR: :1:8: The argument must be a simple name + | [].map(.x, x, x) + | .......^ + +I: [].filter(.x, x) +=====> +E/A: ERROR: :1:12: The argument must be a simple name + | [].filter(.x, x) + | ...........^ +E/P: ERROR: :1:11: The argument must be a simple name + | [].filter(.x, x) + | ..........^ I: a.?b && a[?b] =====> -E: ERROR: :1:2: unsupported syntax '.?' +E/A: ERROR: :1:2: unsupported syntax '.?' | a.?b && a[?b] | .^ ERROR: :1:10: unsupported syntax '[?' | a.?b && a[?b] | .........^ +E/P: ERROR: :1:2: unsupported syntax '.?' + | a.?b && a[?b] + | .^ +ERROR: :1:10: unsupported syntax '?' + | a.?b && a[?b] + | .........^ + +I: [?a, ?b] +=====> +E/A: ERROR: :1:2: unsupported syntax '?' + | [?a, ?b] + | .^ +ERROR: :1:6: unsupported syntax '?' + | [?a, ?b] + | .....^ +E/P: ERROR: :1:2: unsupported syntax '?' + | [?a, ?b] + | .^ +ERROR: :1:6: unsupported syntax '?' + | [?a, ?b] + | .....^ I: Msg{?field: value} && {?'key': value} =====> -E: ERROR: :1:5: unsupported syntax '?' +E/A: ERROR: :1:5: unsupported syntax '?' + | Msg{?field: value} && {?'key': value} + | ....^ +ERROR: :1:24: unsupported syntax '?' + | Msg{?field: value} && {?'key': value} + | .......................^ +E/P: ERROR: :1:5: unsupported syntax '?' | Msg{?field: value} && {?'key': value} | ....^ ERROR: :1:24: unsupported syntax '?' | Msg{?field: value} && {?'key': value} | .......................^ -I: [?a, ?b] +I: a.`b-c` =====> -E: ERROR: :1:2: unsupported syntax '?' - | [?a, ?b] - | .^ -ERROR: :1:6: unsupported syntax '?' - | [?a, ?b] +E/A: ERROR: :1:3: unsupported syntax '`' + | a.`b-c` + | ..^ +E/P: ERROR: :1:3: unsupported syntax '`' + | a.`b-c` + | ..^ + +I: a.`b.c` +=====> +E/A: ERROR: :1:3: unsupported syntax '`' + | a.`b.c` + | ..^ +E/P: ERROR: :1:3: unsupported syntax '`' + | a.`b.c` + | ..^ + +I: a.`in` +=====> +E/A: ERROR: :1:3: unsupported syntax '`' + | a.`in` + | ..^ +E/P: ERROR: :1:3: unsupported syntax '`' + | a.`in` + | ..^ + +I: a.`/foo` +=====> +E/A: ERROR: :1:3: unsupported syntax '`' + | a.`/foo` + | ..^ +E/P: ERROR: :1:3: unsupported syntax '`' + | a.`/foo` + | ..^ + +I: Message{`in`: true} +=====> +E/A: ERROR: :1:9: unsupported syntax '`' + | Message{`in`: true} + | ........^ +E/P: ERROR: :1:9: unsupported syntax '`' + | Message{`in`: true} + | ........^ + +I: foo.`bar` +=====> +E/A: ERROR: :1:5: unsupported syntax '`' + | foo.`bar` + | ....^ +E/P: ERROR: :1:5: unsupported syntax '`' + | foo.`bar` + | ....^ + +I: Struct{`bar`: false} +=====> +E/A: ERROR: :1:8: unsupported syntax '`' + | Struct{`bar`: false} + | .......^ +E/P: ERROR: :1:8: unsupported syntax '`' + | Struct{`bar`: false} + | .......^ + +I: has(.`.` +=====> +E/A: ERROR: :1:6: no viable alternative at input '.`.`' + | has(.`.` + | .....^ +ERROR: :1:6: unsupported syntax '`' + | has(.`.` + | .....^ +ERROR: :1:9: missing ')' at '' + | has(.`.` + | ........^ +E/P: ERROR: :1:4: invalid argument to has() macro + | has(.`.` + | ...^ +ERROR: :1:6: unexpected quoted identifier + | has(.`.` | .....^ +ERROR: :1:9: Syntax error: mismatched input expecting ')' + | has(.`.` + | ........^ + +I: `b-c` +=====> +E/A: ERROR: :1:1: mismatched input '`b-c`' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} + | `b-c` + | ^ +E/P: ERROR: :1:1: unexpected quoted identifier + | `b-c` + | ^ + +I: `b-c`() +=====> +E/A: ERROR: :1:1: extraneous input '`b-c`' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} + | `b-c`() + | ^ +ERROR: :1:7: mismatched input ')' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} + | `b-c`() + | ......^ +E/P: ERROR: :1:1: unexpected quoted identifier + | `b-c`() + | ^ + +I: a.`$b` +=====> +E/A: ERROR: :1:3: token recognition error at: '`$' + | a.`$b` + | ..^ +ERROR: :1:6: token recognition error at: '`' + | a.`$b` + | .....^ +E/P: ERROR: :1:3: unexpected quoted identifier + | a.`$b` + | ..^ + +I: a.`b.c`() +=====> +E/A: ERROR: :1:8: mismatched input '(' expecting {, '==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', '.', '-', '?', '+', '*', '/', '%%'} + | a.`b.c`() + | .......^ +E/P: ERROR: :1:3: unexpected quoted identifier + | a.`b.c`() + | ..^ I: `bar` =====> -E: ERROR: :1:1: mismatched input '`bar`' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} +E/A: ERROR: :1:1: mismatched input '`bar`' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} + | `bar` + | ^ +E/P: ERROR: :1:1: unexpected quoted identifier | `bar` | ^ I: foo.`` =====> -E: ERROR: :1:5: token recognition error at: '``' +E/A: ERROR: :1:5: token recognition error at: '``' | foo.`` | ....^ ERROR: :1:7: no viable alternative at input '.' | foo.`` | ......^ +E/P: ERROR: :1:5: unexpected quoted identifier + | foo.`` + | ....^ I: foo.`$bar` =====> -E: ERROR: :1:5: token recognition error at: '`$' +E/A: ERROR: :1:5: token recognition error at: '`$' | foo.`$bar` | ....^ ERROR: :1:10: token recognition error at: '`' | foo.`$bar` | .........^ +E/P: ERROR: :1:5: unexpected quoted identifier + | foo.`$bar` + | ....^ -I: foo.`bar` +I: [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['too many']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] =====> -E: ERROR: :1:5: unsupported syntax '`' - | foo.`bar` +E/A: Expression recursion limit exceeded. limit: 250 +E/P: ERROR: :1:251: Expression recursion limit exceeded. limit: 250 + | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['too many']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] + | ..........................................................................................................................................................................................................................................................^ +ERROR: :1:251: Syntax error: expected ']' + | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['too many']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] + | ..........................................................................................................................................................................................................................................................^ + +I: [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ +»»»[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['too many']]]]]]]]]]]]]]]]]]]]]]]]]]]] +»»»]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] +=====> +E/A: Expression recursion limit exceeded. limit: 32 +E/P: ERROR: :1:33: Expression recursion limit exceeded. limit: 32 + | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + | ................................^ +ERROR: :1:33: Syntax error: expected ']' + | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + | ................................^ + +I: [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['not fine']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] +=====> +E/A: Expression recursion limit exceeded. limit: 32 +E/P: ERROR: :1:33: Expression recursion limit exceeded. limit: 32 + | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['not fine']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] + | ................................^ +ERROR: :1:33: Syntax error: expected ']' + | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['not fine']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] + | ................................^ + +I: a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.A.B.C.D.E.F.G.H +=====> +E/A: Expression recursion limit exceeded. limit: 32 +E/P: ERROR: :1:66: Expression recursion limit exceeded. limit: 32 + | a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.A.B.C.D.E.F.G.H + | .................................................................^ + +I: a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] +»» [21][22][23][24][25][26][27][28][29][30][31][32][33] +=====> +E/A: Expression recursion limit exceeded. limit: 32 +E/P: ERROR: :2:56: Expression recursion limit exceeded. limit: 32 + | [21][22][23][24][25][26][27][28][29][30][31][32][33] + | .......................................................^ + +I: 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 +»»+ 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 +»»+ 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 +»»+ 31 + 32 + 33 + 34 +=====> +E/A: Expression recursion limit exceeded. limit: 32 +E/P: ERROR: :4:18: Expression recursion limit exceeded. limit: 32 + | + 31 + 32 + 33 + 34 + | .................^ + +I: a < 1 < 2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < 10 < 11 +»» < 12 < 13 < 14 < 15 < 16 < 17 < 18 < 19 < 20 < 21 +»»» < 22 < 23 < 24 < 25 < 26 < 27 < 28 < 29 < 30 < 31 +»»» < 32 < 33 +=====> +E/A: Expression recursion limit exceeded. limit: 32 +E/P: ERROR: :4:11: Expression recursion limit exceeded. limit: 32 + | < 32 < 33 + | ..........^ + +I: y!=y!=y!=y!=y!=y!=y!=y!=y!=-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y +»»!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y +»»!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y +»»!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y +»»!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y +»»!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y +=====> +E/A: Expression recursion limit exceeded. limit: 32 +E/P: ERROR: :2:63: Expression recursion limit exceeded. limit: 32 + | !=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y + | ..............................................................^ + +I: a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] != +»»a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] != +»»a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] != +»»a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] != +»»a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] != +»»a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] != +»»a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] != +»»a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] != +»»a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] != +»»a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] != +»»a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] != +»»a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] != +»»a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] != +»»a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] +=====> +E/A: Expression recursion limit exceeded. limit: 32 +E/P: ERROR: :13:76: Expression recursion limit exceeded. limit: 32 + | a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] != + | ...........................................................................^ + +I: true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : 1 +=====> +E/A: Expression recursion limit exceeded. limit: 32 +E/P: ERROR: :1:353: Expression recursion limit exceeded. limit: 32 + | true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : true ? 1 : 1 + | ................................................................................................................................................................................................................................................................................................................................................................^ + +I: !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x +=====> +E/A: ERROR: :1:3: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ..^ +ERROR: :1:5: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x | ....^ +ERROR: :1:7: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ......^ +ERROR: :1:9: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ........^ +ERROR: :1:11: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ..........^ +ERROR: :1:13: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ............^ +ERROR: :1:15: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ..............^ +ERROR: :1:17: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ................^ +ERROR: :1:19: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ..................^ +ERROR: :1:21: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ....................^ +ERROR: :1:23: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ......................^ +ERROR: :1:25: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ........................^ +ERROR: :1:27: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ..........................^ +ERROR: :1:29: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ............................^ +ERROR: :1:31: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ..............................^ +ERROR: :1:33: no viable alternative at input '-!' + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ................................^ +E/P: ERROR: :1:33: Expression recursion limit exceeded. limit: 32 + | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x + | ................................^ -I: Struct{`bar`: false} +I: 123456 =====> -E: ERROR: :1:8: unsupported syntax '`' - | Struct{`bar`: false} - | .......^ +E/A: ERROR: :-1:0: expression code point size exceeds limit: size: 6, limit 5 +E/P: ERROR: :-1:0: expression code point size exceeds limit: size: 6, limit 5 -I: has(.`.` +I: 1 + 2 + 3 =====> -E: ERROR: :1:6: no viable alternative at input '.`.`' - | has(.`.` - | .....^ -ERROR: :1:6: unsupported syntax '`' - | has(.`.` +E/A: ERROR: :1:5: expression node limit (2) exceeded + | 1 + 2 + 3 + | ....^ +E/P: ERROR: :1:5: expression node limit (2) exceeded + | 1 + 2 + 3 + | ....^ + +I: [?, ?, ?] +=====> +E/A: More than 2 parse errors. +E/P: ERROR: :1:3: Syntax error: unexpected token + | [?, ?, ?] + | ..^ +ERROR: :1:6: Syntax error: unexpected token + | [?, ?, ?] | .....^ -ERROR: :1:9: missing ')' at '' - | has(.`.` - | ........^ \ No newline at end of file +ERROR: :-1:0: More than 2 parse errors. + +I: [1 2 3 a b c] +=====> +E/A: ERROR: :1:4: mismatched input '2' expecting {']', ','} + | [1 2 3 a b c] + | ...^ +E/P: ERROR: :1:4: Syntax error: expected ']' + | [1 2 3 a b c] + | ...^ +ERROR: :1:13: Syntax error: unexpected token after expression + | [1 2 3 a b c] + | ............^ \ No newline at end of file diff --git a/parser/src/test/resources/parser_literals.baseline b/parser/src/test/resources/parser_literals.baseline new file mode 100644 index 000000000..f4716e927 --- /dev/null +++ b/parser/src/test/resources/parser_literals.baseline @@ -0,0 +1,649 @@ +I: null +=====> +P: null^#1:NullValue# +L: null^#1[1,0]# + +I: true +=====> +P: true^#1:bool# +L: true^#1[1,0]# + +I: false +=====> +P: false^#1:bool# +L: false^#1[1,0]# + +I: 0 +=====> +P: 0^#1:int64# +L: 0^#1[1,0]# + +I: 42 +=====> +P: 42^#1:int64# +L: 42^#1[1,0]# + +I: 0xF +=====> +P: 15^#1:int64# +L: 15^#1[1,0]# + +I: 0x2A +=====> +P: 42^#1:int64# +L: 42^#1[1,0]# + +I: -1 +=====> +P: -1^#1:int64# +L: -1^#1[1,1]# + +I: -42 +=====> +P: -42^#1:int64# +L: -42^#1[1,1]# + +I: 0xFFFFFFFFFFFFFFFFF +=====> +E/A: ERROR: :1:1: invalid int literal: 0xFFFFFFFFFFFFFFFFF + | 0xFFFFFFFFFFFFFFFFF + | ^ +E/P: ERROR: :1:1: Syntax error: invalid int literal: 0xFFFFFFFFFFFFFFFFF + | 0xFFFFFFFFFFFFFFFFF + | ^ + +I: 9223372036854775807 +=====> +P: 9223372036854775807^#1:int64# +L: 9223372036854775807^#1[1,0]# + +I: -9223372036854775808 +=====> +P: -9223372036854775808^#1:int64# +L: -9223372036854775808^#1[1,1]# + +I: -(9223372036854775808) +=====> +E/A: ERROR: :1:3: invalid int literal: 9223372036854775808 + | -(9223372036854775808) + | ..^ +E/P: ERROR: :1:3: Syntax error: invalid int literal: 9223372036854775808 + | -(9223372036854775808) + | ..^ + +I: 123a +=====> +E/A: ERROR: :1:4: extraneous input 'a' expecting + | 123a + | ...^ +E/P: ERROR: :1:1: Syntax error: int literal has unexpected trailing characters + | 123a + | ^ + +I: 0u +=====> +P: 0u^#1:uint64# +L: 0u^#1[1,0]# + +I: 23u +=====> +P: 23u^#1:uint64# +L: 23u^#1[1,0]# + +I: 24u +=====> +P: 24u^#1:uint64# +L: 24u^#1[1,0]# + +I: 0xAu +=====> +P: 10u^#1:uint64# +L: 10u^#1[1,0]# + +I: -0xA +=====> +P: -10^#1:int64# +L: -10^#1[1,1]# + +I: 0xA +=====> +P: 10^#1:int64# +L: 10^#1[1,0]# + +I: 0xFu +=====> +P: 15u^#1:uint64# +L: 15u^#1[1,0]# + +I: 0xFFFFFFFFFFFFFFFFFu +=====> +E/A: ERROR: :1:1: invalid uint literal: 0xFFFFFFFFFFFFFFFFFu + | 0xFFFFFFFFFFFFFFFFFu + | ^ +E/P: ERROR: :1:1: Syntax error: invalid uint literal: 0xFFFFFFFFFFFFFFFFFu + | 0xFFFFFFFFFFFFFFFFFu + | ^ + +I: 123u_ +=====> +E/A: ERROR: :1:5: extraneous input '_' expecting + | 123u_ + | ....^ +E/P: ERROR: :1:1: Syntax error: uint literal has unexpected trailing characters + | 123u_ + | ^ + +I: 3.14 +=====> +P: 3.14^#1:double# +L: 3.14^#1[1,0]# + +I: 23.39 +=====> +P: 23.39^#1:double# +L: 23.39^#1[1,0]# + +I: 1. +=====> +E/A: ERROR: :1:3: no viable alternative at input '.' + | 1. + | ..^ +E/P: ERROR: :1:3: Syntax error: expected identifier after '.' + | 1. + | ..^ + +I: 1e+5 +=====> +P: 100000.0^#1:double# +L: 100000.0^#1[1,0]# + +I: 1e-5 +=====> +P: 0.00001^#1:double# +L: 0.00001^#1[1,0]# + +I: 2.5e+10 +=====> +P: 25000000000.0^#1:double# +L: 25000000000.0^#1[1,0]# + +I: 2.5e-10 +=====> +P: 0.0^#1:double# +L: 0.0^#1[1,0]# + +I: 1.99e90000009 +=====> +P: Infinity^#1:double# +L: Infinity^#1[1,0]# + +I: 1e +=====> +E/A: ERROR: :1:2: extraneous input 'e' expecting + | 1e + | .^ +E/P: ERROR: :1:1: Syntax error: floating point literal missing digits after exponent separator + | 1e + | ^ + +I: 1e+ +=====> +E/A: ERROR: :1:2: mismatched input 'e' expecting {, '==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', '.', '-', '?', '+', '*', '/', '%%'} + | 1e+ + | .^ +E/P: ERROR: :1:1: Syntax error: floating point literal missing digits after exponent separator + | 1e+ + | ^ + +I: 1e- +=====> +E/A: ERROR: :1:2: mismatched input 'e' expecting {, '==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', '.', '-', '?', '+', '*', '/', '%%'} + | 1e- + | .^ +E/P: ERROR: :1:1: Syntax error: floating point literal missing digits after exponent separator + | 1e- + | ^ + +I: 2.5e +=====> +E/A: ERROR: :1:4: extraneous input 'e' expecting + | 2.5e + | ...^ +E/P: ERROR: :1:1: Syntax error: floating point literal missing digits after exponent separator + | 2.5e + | ^ + +I: 2.5e+ +=====> +E/A: ERROR: :1:4: mismatched input 'e' expecting {, '==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', '.', '-', '?', '+', '*', '/', '%%'} + | 2.5e+ + | ...^ +E/P: ERROR: :1:1: Syntax error: floating point literal missing digits after exponent separator + | 2.5e+ + | ^ + +I: 2.5e- +=====> +E/A: ERROR: :1:4: mismatched input 'e' expecting {, '==', '!=', 'in', '<', '<=', '>=', '>', '&&', '||', '[', '.', '-', '?', '+', '*', '/', '%%'} + | 2.5e- + | ...^ +E/P: ERROR: :1:1: Syntax error: floating point literal missing digits after exponent separator + | 2.5e- + | ^ + +I: ((1e)) +=====> +E/A: ERROR: :1:4: extraneous input 'e' expecting ')' + | ((1e)) + | ...^ +E/P: ERROR: :1:3: Syntax error: floating point literal missing digits after exponent separator + | ((1e)) + | ..^ + +I: 0x123z +=====> +E/A: ERROR: :1:6: extraneous input 'z' expecting + | 0x123z + | .....^ +E/P: ERROR: :1:1: Syntax error: int literal has unexpected trailing characters + | 0x123z + | ^ + +I: 'hello' +=====> +P: "hello"^#1:string# +L: "hello"^#1[1,0]# + +I: "A" +=====> +P: "A"^#1:string# +L: "A"^#1[1,0]# + +I: '''hello +world''' +=====> +P: "hello\nworld"^#1:string# +L: "hello\nworld"^#1[1,0]# + +I: "\u2764" +=====> +P: "❤"^#1:string# +L: "❤"^#1[1,0]# + +I: "❤" +=====> +P: "❤"^#1:string# +L: "❤"^#1[1,0]# + +I: "\"" +=====> +P: "\""^#1:string# +L: "\""^#1[1,0]# + +I: "\xC3\XBF" +=====> +P: "ÿ"^#1:string# +L: "ÿ"^#1[1,0]# + +I: "\303\277" +=====> +P: "ÿ"^#1:string# +L: "ÿ"^#1[1,0]# + +I: "hi\u263A \u263Athere" +=====> +P: "hi☺ ☺there"^#1:string# +L: "hi☺ ☺there"^#1[1,0]# + +I: "\U000003A8\?" +=====> +P: "Ψ?"^#1:string# +L: "Ψ?"^#1[1,0]# + +I: "\a\b\f\n\r\t\v'\"\\\? Legal escapes" +=====> +P: "\a\b\f\n\r\t\v'\"\? Legal escapes"^#1:string# +L: "\a\b\f\n\r\t\v'\"\? Legal escapes"^#1[1,0]# + +I: """hello +world""" +=====> +P: "hello\nworld"^#1:string# +L: "hello\nworld"^#1[1,0]# + +I: r"""hello +world""" +=====> +P: "hello\nworld"^#1:string# +L: "hello\nworld"^#1[1,0]# + +I: """""" +=====> +P: ""^#1:string# +L: ""^#1[1,0]# + +I: '''''' +=====> +P: ""^#1:string# +L: ""^#1[1,0]# + +I: """hello\"""world""" +=====> +P: "hello\"\"\"world"^#1:string# +L: "hello\"\"\"world"^#1[1,0]# + +I: '''hello\'''world''' +=====> +P: "hello'''world"^#1:string# +L: "hello'''world"^#1[1,0]# + +I: "\xFh" +=====> +E/A: ERROR: :1:1: token recognition error at: '"\xFh' + | "\xFh" + | ^ +ERROR: :1:6: token recognition error at: '"' + | "\xFh" + | .....^ +ERROR: :1:7: mismatched input '' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} + | "\xFh" + | ......^ +E/P: ERROR: :1:1: Invalid hex escape sequence + | "\xFh" + | ^ + +I: "\a\b\f\n\r\t\v\'\"\\\? Illegal escape \>" +=====> +E/A: ERROR: :1:1: token recognition error at: '"\a\b\f\n\r\t\v\'\"\\\? Illegal escape \>' + | "\a\b\f\n\r\t\v\'\"\\\? Illegal escape \>" + | ^ +ERROR: :1:42: token recognition error at: '"' + | "\a\b\f\n\r\t\v\'\"\\\? Illegal escape \>" + | .........................................^ +ERROR: :1:43: mismatched input '' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} + | "\a\b\f\n\r\t\v\'\"\\\? Illegal escape \>" + | ..........................................^ +E/P: ERROR: :1:1: Illegal escape sequence + | "\a\b\f\n\r\t\v\'\"\\\? Illegal escape \>" + | ^ + +I: '😁' in ['😁', '😑', '😦'] +»»»&& in.😁 +=====> +E/A: ERROR: :2:7: extraneous input 'in' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} + | && in.😁 + | ......^ +ERROR: :2:10: token recognition error at: '😁' + | && in.😁 + | .........^ +ERROR: :2:11: no viable alternative at input '.' + | && in.😁 + | ..........^ +E/P: ERROR: :2:7: Syntax error: unexpected token + | && in.😁 + | ......^ +ERROR: :2:10: Syntax error: unexpected character + | && in.😁 + | .........^ + +I: """hello +world +=====> +E/A: ERROR: :1:3: token recognition error at: '"hello\n' + | """hello + | ..^ +ERROR: :2:1: extraneous input 'world' expecting + | world + | ^ +E/P: ERROR: :1:1: Syntax error: unterminated string literal + | """hello + | ^ + +I: '''hello +world +=====> +E/A: ERROR: :1:3: token recognition error at: ''hello\n' + | '''hello + | ..^ +ERROR: :2:1: extraneous input 'world' expecting + | world + | ^ +E/P: ERROR: :1:1: Syntax error: unterminated string literal + | '''hello + | ^ + +I: r"""hello +world +=====> +E/A: ERROR: :1:4: token recognition error at: '"hello\n' + | r"""hello + | ...^ +ERROR: :2:1: extraneous input 'world' expecting + | world + | ^ +E/P: ERROR: :1:1: Syntax error: unterminated string literal + | r"""hello + | ^ + +I: "hello +world" +=====> +E/A: ERROR: :1:1: token recognition error at: '"hello\n' + | "hello + | ^ +ERROR: :2:6: token recognition error at: '"' + | world" + | .....^ +E/P: ERROR: :1:1: Syntax error: unterminated string literal + | "hello + | ^ +ERROR: :2:1: Syntax error: unexpected token after expression + | world" + | ^ + +I: 'hello +world' +=====> +E/A: ERROR: :1:1: token recognition error at: ''hello\n' + | 'hello + | ^ +ERROR: :2:6: token recognition error at: ''' + | world' + | .....^ +E/P: ERROR: :1:1: Syntax error: unterminated string literal + | 'hello + | ^ +ERROR: :2:1: Syntax error: unexpected token after expression + | world' + | ^ + +I: r"hello +world" +=====> +E/A: ERROR: :1:2: token recognition error at: '"hello\n' + | r"hello + | .^ +ERROR: :2:1: extraneous input 'world' expecting + | world" + | ^ +ERROR: :2:6: token recognition error at: '"' + | world" + | .....^ +E/P: ERROR: :1:1: Syntax error: unterminated string literal + | r"hello + | ^ +ERROR: :2:1: Syntax error: unexpected token after expression + | world" + | ^ + +I: `hello +world` +=====> +E/A: ERROR: :1:1: token recognition error at: '`hello\n' + | `hello + | ^ +ERROR: :2:6: token recognition error at: '`' + | world` + | .....^ +E/P: ERROR: :1:1: Syntax error: unterminated quoted identifier + | `hello + | ^ +ERROR: :2:1: Syntax error: unexpected token after expression + | world` + | ^ + +I: "hello world" +=====> +E/A: ERROR: :1:1: token recognition error at: '"hello\r' + | "hello world" + | ^ +ERROR: :1:13: token recognition error at: '"' + | "hello world" + | ............^ +E/P: ERROR: :1:1: Syntax error: unterminated string literal + | "hello world" + | ^ +ERROR: :1:8: Syntax error: unexpected token after expression + | "hello world" + | .......^ + +I: 'unterminated +=====> +E/A: ERROR: :1:1: token recognition error at: ''unterminated' + | 'unterminated + | ^ +ERROR: :1:14: mismatched input '' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER} + | 'unterminated + | .............^ +E/P: ERROR: :1:1: Syntax error: unterminated string literal + | 'unterminated + | ^ + +I: b'abc' +=====> +P: b"abc"^#1:bytes# +L: b"abc"^#1[1,0]# + +I: b"abc" +=====> +P: b"abc"^#1:bytes# +L: b"abc"^#1[1,0]# + +I: b"""hello +world +=====> +E/A: ERROR: :1:4: token recognition error at: '"hello\n' + | b"""hello + | ...^ +ERROR: :2:1: extraneous input 'world' expecting + | world + | ^ +E/P: ERROR: :1:1: Syntax error: unterminated bytes literal + | b"""hello + | ^ + +I: b"hello +world" +=====> +E/A: ERROR: :1:2: token recognition error at: '"hello\n' + | b"hello + | .^ +ERROR: :2:1: extraneous input 'world' expecting + | world" + | ^ +ERROR: :2:6: token recognition error at: '"' + | world" + | .....^ +E/P: ERROR: :1:1: Syntax error: unterminated bytes literal + | b"hello + | ^ +ERROR: :2:1: Syntax error: unexpected token after expression + | world" + | ^ + +I: rb"hello +world" +=====> +E/A: ERROR: :1:3: token recognition error at: '"hello\n' + | rb"hello + | ..^ +ERROR: :2:1: extraneous input 'world' expecting + | world" + | ^ +ERROR: :2:6: token recognition error at: '"' + | world" + | .....^ +E/P: ERROR: :1:1: Syntax error: unterminated bytes literal + | rb"hello + | ^ +ERROR: :2:1: Syntax error: unexpected token after expression + | world" + | ^ + +I: br'abc' +=====> +P: b"abc"^#1:bytes# +L: b"abc"^#1[1,0]# + +I: bR'abc' +=====> +P: b"abc"^#1:bytes# +L: b"abc"^#1[1,0]# + +I: Br'abc' +=====> +P: b"abc"^#1:bytes# +L: b"abc"^#1[1,0]# + +I: BR'abc' +=====> +P: b"abc"^#1:bytes# +L: b"abc"^#1[1,0]# + +I: rb'abc' +=====> +E/A: ERROR: :1:3: extraneous input ''abc'' expecting + | rb'abc' + | ..^ + +I: rB'abc' +=====> +E/A: ERROR: :1:3: extraneous input ''abc'' expecting + | rB'abc' + | ..^ + +I: Rb'abc' +=====> +E/A: ERROR: :1:3: extraneous input ''abc'' expecting + | Rb'abc' + | ..^ + +I: RB'abc' +=====> +E/A: ERROR: :1:3: extraneous input ''abc'' expecting + | RB'abc' + | ..^ + +I: br'a\'b' +=====> +E/A: ERROR: :1:1: String literal contains unescaped terminating quote ' + | br'a\'b' + | ^ +ERROR: :1:7: extraneous input 'b' expecting + | br'a\'b' + | ......^ +ERROR: :1:8: token recognition error at: ''' + | br'a\'b' + | .......^ +E/P: ERROR: :1:1: String literal contains unescaped terminating quote ' + | br'a\'b' + | ^ +ERROR: :1:7: Syntax error: unterminated bytes literal + | br'a\'b' + | ......^ + +I: rb'a\'b' +=====> +E/A: ERROR: :1:3: extraneous input ''a\'b'' expecting + | rb'a\'b' + | ..^ \ No newline at end of file diff --git a/parser/src/test/resources/parser_macros.baseline b/parser/src/test/resources/parser_macros.baseline new file mode 100644 index 000000000..e20bd07ec --- /dev/null +++ b/parser/src/test/resources/parser_macros.baseline @@ -0,0 +1,981 @@ +I: has(m.f) +=====> +P: m^#2:Expr.Ident#.f~test-only~^#4:Expr.Select# +L: m^#2[1,4]#.f~test-only~^#4[1,3]# +M: has( + m^#2:Expr.Ident#.f^#3:Expr.Select# +)^#0:Expr.Call# + +I: has(a.b) +=====> +P: a^#2:Expr.Ident#.b~test-only~^#4:Expr.Select# +L: a^#2[1,4]#.b~test-only~^#4[1,3]# +M: has( + a^#2:Expr.Ident#.b^#3:Expr.Select# +)^#0:Expr.Call# + +I: has(m) +=====> +E/A: ERROR: :1:4: invalid argument to has() macro + | has(m) + | ...^ +E/P: ERROR: :1:4: invalid argument to has() macro + | has(m) + | ...^ + +I: m.all(v, f) +=====> +P: __comprehension__( + // Variable + v, + // Target + m^#1:Expr.Ident#, + // Accumulator + @result, + // Init + true^#5:bool#, + // LoopCondition + @not_strictly_false( + @result^#6:Expr.Ident# + )^#7:Expr.Call#, + // LoopStep + _&&_( + @result^#8:Expr.Ident#, + f^#4:Expr.Ident# + )^#9:Expr.Call#, + // Result + @result^#10:Expr.Ident#)^#11:Expr.Comprehension# +L: __comprehension__( + // Variable + v, + // Target + m^#1[1,0]#, + // Accumulator + @result, + // Init + true^#5[1,5]#, + // LoopCondition + @not_strictly_false( + @result^#6[1,5]# + )^#7[1,5]#, + // LoopStep + _&&_( + @result^#8[1,5]#, + f^#4[1,9]# + )^#9[1,5]#, + // Result + @result^#10[1,5]#)^#11[1,5]# +M: m^#1:Expr.Ident#.all( + v^#3:Expr.Ident#, + f^#4:Expr.Ident# +)^#0:Expr.Call# + +I: [1, 2].all(x, x > 0) +=====> +P: __comprehension__( + // Variable + x, + // Target + [ + 1^#2:int64#, + 2^#3:int64# + ]^#1:Expr.CreateList#, + // Accumulator + @result, + // Init + true^#9:bool#, + // LoopCondition + @not_strictly_false( + @result^#10:Expr.Ident# + )^#11:Expr.Call#, + // LoopStep + _&&_( + @result^#12:Expr.Ident#, + _>_( + x^#6:Expr.Ident#, + 0^#8:int64# + )^#7:Expr.Call# + )^#13:Expr.Call#, + // Result + @result^#14:Expr.Ident#)^#15:Expr.Comprehension# +L: __comprehension__( + // Variable + x, + // Target + [ + 1^#2[1,1]#, + 2^#3[1,4]# + ]^#1[1,0]#, + // Accumulator + @result, + // Init + true^#9[1,10]#, + // LoopCondition + @not_strictly_false( + @result^#10[1,10]# + )^#11[1,10]#, + // LoopStep + _&&_( + @result^#12[1,10]#, + _>_( + x^#6[1,14]#, + 0^#8[1,18]# + )^#7[1,16]# + )^#13[1,10]#, + // Result + @result^#14[1,10]#)^#15[1,10]# +M: [ + 1^#2:int64#, + 2^#3:int64# +]^#1:Expr.CreateList#.all( + x^#5:Expr.Ident#, + _>_( + x^#6:Expr.Ident#, + 0^#8:int64# + )^#7:Expr.Call# +)^#0:Expr.Call# + +I: m.exists(v, f) +=====> +P: __comprehension__( + // Variable + v, + // Target + m^#1:Expr.Ident#, + // Accumulator + @result, + // Init + false^#5:bool#, + // LoopCondition + @not_strictly_false( + !_( + @result^#6:Expr.Ident# + )^#7:Expr.Call# + )^#8:Expr.Call#, + // LoopStep + _||_( + @result^#9:Expr.Ident#, + f^#4:Expr.Ident# + )^#10:Expr.Call#, + // Result + @result^#11:Expr.Ident#)^#12:Expr.Comprehension# +L: __comprehension__( + // Variable + v, + // Target + m^#1[1,0]#, + // Accumulator + @result, + // Init + false^#5[1,8]#, + // LoopCondition + @not_strictly_false( + !_( + @result^#6[1,8]# + )^#7[1,8]# + )^#8[1,8]#, + // LoopStep + _||_( + @result^#9[1,8]#, + f^#4[1,12]# + )^#10[1,8]#, + // Result + @result^#11[1,8]#)^#12[1,8]# +M: m^#1:Expr.Ident#.exists( + v^#3:Expr.Ident#, + f^#4:Expr.Ident# +)^#0:Expr.Call# + +I: m.exists_one(v, f) +=====> +P: __comprehension__( + // Variable + v, + // Target + m^#1:Expr.Ident#, + // Accumulator + @result, + // Init + 0^#5:int64#, + // LoopCondition + true^#6:bool#, + // LoopStep + _?_:_( + f^#4:Expr.Ident#, + _+_( + @result^#7:Expr.Ident#, + 1^#8:int64# + )^#9:Expr.Call#, + @result^#10:Expr.Ident# + )^#11:Expr.Call#, + // Result + _==_( + @result^#12:Expr.Ident#, + 1^#13:int64# + )^#14:Expr.Call#)^#15:Expr.Comprehension# +L: __comprehension__( + // Variable + v, + // Target + m^#1[1,0]#, + // Accumulator + @result, + // Init + 0^#5[1,12]#, + // LoopCondition + true^#6[1,12]#, + // LoopStep + _?_:_( + f^#4[1,16]#, + _+_( + @result^#7[1,12]#, + 1^#8[1,12]# + )^#9[1,12]#, + @result^#10[1,12]# + )^#11[1,12]#, + // Result + _==_( + @result^#12[1,12]#, + 1^#13[1,12]# + )^#14[1,12]#)^#15[1,12]# +M: m^#1:Expr.Ident#.exists_one( + v^#3:Expr.Ident#, + f^#4:Expr.Ident# +)^#0:Expr.Call# + +I: m.existsOne(v, f) +=====> +P: __comprehension__( + // Variable + v, + // Target + m^#1:Expr.Ident#, + // Accumulator + @result, + // Init + 0^#5:int64#, + // LoopCondition + true^#6:bool#, + // LoopStep + _?_:_( + f^#4:Expr.Ident#, + _+_( + @result^#7:Expr.Ident#, + 1^#8:int64# + )^#9:Expr.Call#, + @result^#10:Expr.Ident# + )^#11:Expr.Call#, + // Result + _==_( + @result^#12:Expr.Ident#, + 1^#13:int64# + )^#14:Expr.Call#)^#15:Expr.Comprehension# +L: __comprehension__( + // Variable + v, + // Target + m^#1[1,0]#, + // Accumulator + @result, + // Init + 0^#5[1,11]#, + // LoopCondition + true^#6[1,11]#, + // LoopStep + _?_:_( + f^#4[1,15]#, + _+_( + @result^#7[1,11]#, + 1^#8[1,11]# + )^#9[1,11]#, + @result^#10[1,11]# + )^#11[1,11]#, + // Result + _==_( + @result^#12[1,11]#, + 1^#13[1,11]# + )^#14[1,11]#)^#15[1,11]# +M: m^#1:Expr.Ident#.existsOne( + v^#3:Expr.Ident#, + f^#4:Expr.Ident# +)^#0:Expr.Call# + +I: [].existsOne(__result__, __result__) +=====> +E/A: ERROR: :1:14: The iteration variable __result__ overwrites accumulator variable + | [].existsOne(__result__, __result__) + | .............^ +E/P: ERROR: :1:14: The iteration variable __result__ overwrites accumulator variable + | [].existsOne(__result__, __result__) + | .............^ + +I: m.map(v, f) +=====> +P: __comprehension__( + // Variable + v, + // Target + m^#1:Expr.Ident#, + // Accumulator + @result, + // Init + []^#5:Expr.CreateList#, + // LoopCondition + true^#6:bool#, + // LoopStep + _+_( + @result^#7:Expr.Ident#, + [ + f^#4:Expr.Ident# + ]^#8:Expr.CreateList# + )^#9:Expr.Call#, + // Result + @result^#10:Expr.Ident#)^#11:Expr.Comprehension# +L: __comprehension__( + // Variable + v, + // Target + m^#1[1,0]#, + // Accumulator + @result, + // Init + []^#5[1,5]#, + // LoopCondition + true^#6[1,5]#, + // LoopStep + _+_( + @result^#7[1,5]#, + [ + f^#4[1,9]# + ]^#8[1,5]# + )^#9[1,5]#, + // Result + @result^#10[1,5]#)^#11[1,5]# +M: m^#1:Expr.Ident#.map( + v^#3:Expr.Ident#, + f^#4:Expr.Ident# +)^#0:Expr.Call# + +I: m.map(v, p, f) +=====> +P: __comprehension__( + // Variable + v, + // Target + m^#1:Expr.Ident#, + // Accumulator + @result, + // Init + []^#6:Expr.CreateList#, + // LoopCondition + true^#7:bool#, + // LoopStep + _?_:_( + p^#4:Expr.Ident#, + _+_( + @result^#8:Expr.Ident#, + [ + f^#5:Expr.Ident# + ]^#9:Expr.CreateList# + )^#10:Expr.Call#, + @result^#11:Expr.Ident# + )^#12:Expr.Call#, + // Result + @result^#13:Expr.Ident#)^#14:Expr.Comprehension# +L: __comprehension__( + // Variable + v, + // Target + m^#1[1,0]#, + // Accumulator + @result, + // Init + []^#6[1,5]#, + // LoopCondition + true^#7[1,5]#, + // LoopStep + _?_:_( + p^#4[1,9]#, + _+_( + @result^#8[1,5]#, + [ + f^#5[1,12]# + ]^#9[1,5]# + )^#10[1,5]#, + @result^#11[1,5]# + )^#12[1,5]#, + // Result + @result^#13[1,5]#)^#14[1,5]# +M: m^#1:Expr.Ident#.map( + v^#3:Expr.Ident#, + p^#4:Expr.Ident#, + f^#5:Expr.Ident# +)^#0:Expr.Call# + +I: m.map(__result__, __result__) +=====> +E/A: ERROR: :1:7: The iteration variable __result__ overwrites accumulator variable + | m.map(__result__, __result__) + | ......^ +E/P: ERROR: :1:7: The iteration variable __result__ overwrites accumulator variable + | m.map(__result__, __result__) + | ......^ + +I: m.filter(v, p) +=====> +P: __comprehension__( + // Variable + v, + // Target + m^#1:Expr.Ident#, + // Accumulator + @result, + // Init + []^#5:Expr.CreateList#, + // LoopCondition + true^#6:bool#, + // LoopStep + _?_:_( + p^#4:Expr.Ident#, + _+_( + @result^#7:Expr.Ident#, + [ + v^#3:Expr.Ident# + ]^#8:Expr.CreateList# + )^#9:Expr.Call#, + @result^#10:Expr.Ident# + )^#11:Expr.Call#, + // Result + @result^#12:Expr.Ident#)^#13:Expr.Comprehension# +L: __comprehension__( + // Variable + v, + // Target + m^#1[1,0]#, + // Accumulator + @result, + // Init + []^#5[1,8]#, + // LoopCondition + true^#6[1,8]#, + // LoopStep + _?_:_( + p^#4[1,12]#, + _+_( + @result^#7[1,8]#, + [ + v^#3[1,9]# + ]^#8[1,8]# + )^#9[1,8]#, + @result^#10[1,8]# + )^#11[1,8]#, + // Result + @result^#12[1,8]#)^#13[1,8]# +M: m^#1:Expr.Ident#.filter( + v^#3:Expr.Ident#, + p^#4:Expr.Ident# +)^#0:Expr.Call# + +I: m.filter(__result__, false) +=====> +E/A: ERROR: :1:10: The iteration variable __result__ overwrites accumulator variable + | m.filter(__result__, false) + | .........^ +E/P: ERROR: :1:10: The iteration variable __result__ overwrites accumulator variable + | m.filter(__result__, false) + | .........^ + +I: m.filter(a.b, false) +=====> +E/A: ERROR: :1:11: The argument must be a simple name + | m.filter(a.b, false) + | ..........^ +E/P: ERROR: :1:11: The argument must be a simple name + | m.filter(a.b, false) + | ..........^ + +I: x.filter(y, y.filter(z, z > 0)) +=====> +P: __comprehension__( + // Variable + y, + // Target + x^#1:Expr.Ident#, + // Accumulator + @result, + // Init + []^#19:Expr.CreateList#, + // LoopCondition + true^#20:bool#, + // LoopStep + _?_:_( + __comprehension__( + // Variable + z, + // Target + y^#4:Expr.Ident#, + // Accumulator + @result, + // Init + []^#10:Expr.CreateList#, + // LoopCondition + true^#11:bool#, + // LoopStep + _?_:_( + _>_( + z^#7:Expr.Ident#, + 0^#9:int64# + )^#8:Expr.Call#, + _+_( + @result^#12:Expr.Ident#, + [ + z^#6:Expr.Ident# + ]^#13:Expr.CreateList# + )^#14:Expr.Call#, + @result^#15:Expr.Ident# + )^#16:Expr.Call#, + // Result + @result^#17:Expr.Ident#)^#18:Expr.Comprehension#, + _+_( + @result^#21:Expr.Ident#, + [ + y^#3:Expr.Ident# + ]^#22:Expr.CreateList# + )^#23:Expr.Call#, + @result^#24:Expr.Ident# + )^#25:Expr.Call#, + // Result + @result^#26:Expr.Ident#)^#27:Expr.Comprehension# +L: __comprehension__( + // Variable + y, + // Target + x^#1[1,0]#, + // Accumulator + @result, + // Init + []^#19[1,8]#, + // LoopCondition + true^#20[1,8]#, + // LoopStep + _?_:_( + __comprehension__( + // Variable + z, + // Target + y^#4[1,12]#, + // Accumulator + @result, + // Init + []^#10[1,20]#, + // LoopCondition + true^#11[1,20]#, + // LoopStep + _?_:_( + _>_( + z^#7[1,24]#, + 0^#9[1,28]# + )^#8[1,26]#, + _+_( + @result^#12[1,20]#, + [ + z^#6[1,21]# + ]^#13[1,20]# + )^#14[1,20]#, + @result^#15[1,20]# + )^#16[1,20]#, + // Result + @result^#17[1,20]#)^#18[1,20]#, + _+_( + @result^#21[1,8]#, + [ + y^#3[1,9]# + ]^#22[1,8]# + )^#23[1,8]#, + @result^#24[1,8]# + )^#25[1,8]#, + // Result + @result^#26[1,8]#)^#27[1,8]# +M: x^#1:Expr.Ident#.filter( + y^#3:Expr.Ident#, + ^#18:filter# +)^#0:Expr.Call#, +y^#4:Expr.Ident#.filter( + z^#6:Expr.Ident#, + _>_( + z^#7:Expr.Ident#, + 0^#9:int64# + )^#8:Expr.Call# +)^#0:Expr.Call# + +I: has(a.b).filter(c, c) +=====> +P: __comprehension__( + // Variable + c, + // Target + a^#2:Expr.Ident#.b~test-only~^#4:Expr.Select#, + // Accumulator + @result, + // Init + []^#8:Expr.CreateList#, + // LoopCondition + true^#9:bool#, + // LoopStep + _?_:_( + c^#7:Expr.Ident#, + _+_( + @result^#10:Expr.Ident#, + [ + c^#6:Expr.Ident# + ]^#11:Expr.CreateList# + )^#12:Expr.Call#, + @result^#13:Expr.Ident# + )^#14:Expr.Call#, + // Result + @result^#15:Expr.Ident#)^#16:Expr.Comprehension# +L: __comprehension__( + // Variable + c, + // Target + a^#2[1,4]#.b~test-only~^#4[1,3]#, + // Accumulator + @result, + // Init + []^#8[1,15]#, + // LoopCondition + true^#9[1,15]#, + // LoopStep + _?_:_( + c^#7[1,19]#, + _+_( + @result^#10[1,15]#, + [ + c^#6[1,16]# + ]^#11[1,15]# + )^#12[1,15]#, + @result^#13[1,15]# + )^#14[1,15]#, + // Result + @result^#15[1,15]#)^#16[1,15]# +M: ^#4:has#.filter( + c^#6:Expr.Ident#, + c^#7:Expr.Ident# +)^#0:Expr.Call#, +has( + a^#2:Expr.Ident#.b^#3:Expr.Select# +)^#0:Expr.Call# + +I: x.filter(y, y.exists(z, has(z.a)) && y.exists(z, has(z.b))) +=====> +P: __comprehension__( + // Variable + y, + // Target + x^#1:Expr.Ident#, + // Accumulator + @result, + // Init + []^#35:Expr.CreateList#, + // LoopCondition + true^#36:bool#, + // LoopStep + _?_:_( + _&&_( + __comprehension__( + // Variable + z, + // Target + y^#4:Expr.Ident#, + // Accumulator + @result, + // Init + false^#11:bool#, + // LoopCondition + @not_strictly_false( + !_( + @result^#12:Expr.Ident# + )^#13:Expr.Call# + )^#14:Expr.Call#, + // LoopStep + _||_( + @result^#15:Expr.Ident#, + z^#8:Expr.Ident#.a~test-only~^#10:Expr.Select# + )^#16:Expr.Call#, + // Result + @result^#17:Expr.Ident#)^#18:Expr.Comprehension#, + __comprehension__( + // Variable + z, + // Target + y^#20:Expr.Ident#, + // Accumulator + @result, + // Init + false^#27:bool#, + // LoopCondition + @not_strictly_false( + !_( + @result^#28:Expr.Ident# + )^#29:Expr.Call# + )^#30:Expr.Call#, + // LoopStep + _||_( + @result^#31:Expr.Ident#, + z^#24:Expr.Ident#.b~test-only~^#26:Expr.Select# + )^#32:Expr.Call#, + // Result + @result^#33:Expr.Ident#)^#34:Expr.Comprehension# + )^#19:Expr.Call#, + _+_( + @result^#37:Expr.Ident#, + [ + y^#3:Expr.Ident# + ]^#38:Expr.CreateList# + )^#39:Expr.Call#, + @result^#40:Expr.Ident# + )^#41:Expr.Call#, + // Result + @result^#42:Expr.Ident#)^#43:Expr.Comprehension# +L: __comprehension__( + // Variable + y, + // Target + x^#1[1,0]#, + // Accumulator + @result, + // Init + []^#35[1,8]#, + // LoopCondition + true^#36[1,8]#, + // LoopStep + _?_:_( + _&&_( + __comprehension__( + // Variable + z, + // Target + y^#4[1,12]#, + // Accumulator + @result, + // Init + false^#11[1,20]#, + // LoopCondition + @not_strictly_false( + !_( + @result^#12[1,20]# + )^#13[1,20]# + )^#14[1,20]#, + // LoopStep + _||_( + @result^#15[1,20]#, + z^#8[1,28]#.a~test-only~^#10[1,27]# + )^#16[1,20]#, + // Result + @result^#17[1,20]#)^#18[1,20]#, + __comprehension__( + // Variable + z, + // Target + y^#20[1,37]#, + // Accumulator + @result, + // Init + false^#27[1,45]#, + // LoopCondition + @not_strictly_false( + !_( + @result^#28[1,45]# + )^#29[1,45]# + )^#30[1,45]#, + // LoopStep + _||_( + @result^#31[1,45]#, + z^#24[1,53]#.b~test-only~^#26[1,52]# + )^#32[1,45]#, + // Result + @result^#33[1,45]#)^#34[1,45]# + )^#19[1,34]#, + _+_( + @result^#37[1,8]#, + [ + y^#3[1,9]# + ]^#38[1,8]# + )^#39[1,8]#, + @result^#40[1,8]# + )^#41[1,8]#, + // Result + @result^#42[1,8]#)^#43[1,8]# +M: x^#1:Expr.Ident#.filter( + y^#3:Expr.Ident#, + _&&_( + ^#18:exists#, + ^#34:exists# + )^#19:Expr.Call# +)^#0:Expr.Call#, +y^#20:Expr.Ident#.exists( + z^#22:Expr.Ident#, + ^#26:has# +)^#0:Expr.Call#, +has( + z^#24:Expr.Ident#.b^#25:Expr.Select# +)^#0:Expr.Call#, +y^#4:Expr.Ident#.exists( + z^#6:Expr.Ident#, + ^#10:has# +)^#0:Expr.Call#, +has( + z^#8:Expr.Ident#.a^#9:Expr.Select# +)^#0:Expr.Call# + +I: (has(a.b) || has(c.d)).string() +=====> +P: _||_( + a^#2:Expr.Ident#.b~test-only~^#4:Expr.Select#, + c^#7:Expr.Ident#.d~test-only~^#9:Expr.Select# +)^#5:Expr.Call#.string()^#10:Expr.Call# +L: _||_( + a^#2[1,5]#.b~test-only~^#4[1,4]#, + c^#7[1,17]#.d~test-only~^#9[1,16]# +)^#5[1,10]#.string()^#10[1,29]# +M: has( + c^#7:Expr.Ident#.d^#8:Expr.Select# +)^#0:Expr.Call#, +has( + a^#2:Expr.Ident#.b^#3:Expr.Select# +)^#0:Expr.Call# + +I: has(a.b).asList().exists(c, c) +=====> +P: __comprehension__( + // Variable + c, + // Target + a^#2:Expr.Ident#.b~test-only~^#4:Expr.Select#.asList()^#5:Expr.Call#, + // Accumulator + @result, + // Init + false^#9:bool#, + // LoopCondition + @not_strictly_false( + !_( + @result^#10:Expr.Ident# + )^#11:Expr.Call# + )^#12:Expr.Call#, + // LoopStep + _||_( + @result^#13:Expr.Ident#, + c^#8:Expr.Ident# + )^#14:Expr.Call#, + // Result + @result^#15:Expr.Ident#)^#16:Expr.Comprehension# +L: __comprehension__( + // Variable + c, + // Target + a^#2[1,4]#.b~test-only~^#4[1,3]#.asList()^#5[1,15]#, + // Accumulator + @result, + // Init + false^#9[1,24]#, + // LoopCondition + @not_strictly_false( + !_( + @result^#10[1,24]# + )^#11[1,24]# + )^#12[1,24]#, + // LoopStep + _||_( + @result^#13[1,24]#, + c^#8[1,28]# + )^#14[1,24]#, + // Result + @result^#15[1,24]#)^#16[1,24]# +M: a^#2:Expr.Ident#.b~test-only~^#4:has#.asList()^#5:Expr.Call#.exists( + c^#7:Expr.Ident#, + c^#8:Expr.Ident# +)^#0:Expr.Call#, +has( + a^#2:Expr.Ident#.b^#3:Expr.Select# +)^#0:Expr.Call# + +I: [has(a.b), has(c.d)].exists(e, e) +=====> +P: __comprehension__( + // Variable + e, + // Target + [ + a^#3:Expr.Ident#.b~test-only~^#5:Expr.Select#, + c^#7:Expr.Ident#.d~test-only~^#9:Expr.Select# + ]^#1:Expr.CreateList#, + // Accumulator + @result, + // Init + false^#13:bool#, + // LoopCondition + @not_strictly_false( + !_( + @result^#14:Expr.Ident# + )^#15:Expr.Call# + )^#16:Expr.Call#, + // LoopStep + _||_( + @result^#17:Expr.Ident#, + e^#12:Expr.Ident# + )^#18:Expr.Call#, + // Result + @result^#19:Expr.Ident#)^#20:Expr.Comprehension# +L: __comprehension__( + // Variable + e, + // Target + [ + a^#3[1,5]#.b~test-only~^#5[1,4]#, + c^#7[1,15]#.d~test-only~^#9[1,14]# + ]^#1[1,0]#, + // Accumulator + @result, + // Init + false^#13[1,27]#, + // LoopCondition + @not_strictly_false( + !_( + @result^#14[1,27]# + )^#15[1,27]# + )^#16[1,27]#, + // LoopStep + _||_( + @result^#17[1,27]#, + e^#12[1,31]# + )^#18[1,27]#, + // Result + @result^#19[1,27]#)^#20[1,27]# +M: [ + a^#3:Expr.Ident#.b~test-only~^#5:has#, + c^#7:Expr.Ident#.d~test-only~^#9:has# +]^#1:Expr.CreateList#.exists( + e^#11:Expr.Ident#, + e^#12:Expr.Ident# +)^#0:Expr.Call#, +has( + c^#7:Expr.Ident#.d^#8:Expr.Select# +)^#0:Expr.Call#, +has( + a^#3:Expr.Ident#.b^#4:Expr.Select# +)^#0:Expr.Call# + +I: noop_macro(123) +=====> +P: noop_macro( + 123^#2:int64# +)^#1:Expr.Call# +L: noop_macro( + 123^#2[1,11]# +)^#1[1,10]# + +I: get_constant_macro() +=====> +P: 10^#1:int64# +L: 10^#1[NO_POS]# +M: get_constant_macro()^#0:Expr.Call# \ No newline at end of file diff --git a/parser/src/test/resources/pratt_parser_core_syntax.baseline b/parser/src/test/resources/pratt_parser_core_syntax.baseline index 278f27eb8..02f44e87c 100644 --- a/parser/src/test/resources/pratt_parser_core_syntax.baseline +++ b/parser/src/test/resources/pratt_parser_core_syntax.baseline @@ -219,7 +219,7 @@ L: { I: foo{ } =====> P: foo{}^#1:Expr.CreateStruct# -L: foo{}^#1[1,0]# +L: foo{}^#1[1,3]# I: foo{ a:b } =====> @@ -228,7 +228,7 @@ P: foo{ }^#1:Expr.CreateStruct# L: foo{ a:b^#3[1,7]#^#2[1,6]# -}^#1[1,0]# +}^#1[1,3]# I: foo{ a:b, c:d } =====> @@ -239,7 +239,7 @@ P: foo{ L: foo{ a:b^#3[1,7]#^#2[1,6]#, c:d^#5[1,12]#^#4[1,11]# -}^#1[1,0]# +}^#1[1,3]# I: SomeMessage{foo: 5, bar: "xyz"} =====> @@ -250,7 +250,7 @@ P: SomeMessage{ L: SomeMessage{ foo:5^#3[1,17]#^#2[1,15]#, bar:"xyz"^#5[1,25]#^#4[1,23]# -}^#1[1,0]# +}^#1[1,11]# I: TestAllTypes{single_int32: 1, single_int64: 2} =====> @@ -261,7 +261,7 @@ P: TestAllTypes{ L: TestAllTypes{ single_int32:1^#3[1,27]#^#2[1,25]#, single_int64:2^#5[1,44]#^#4[1,42]# -}^#1[1,0]# +}^#1[1,12]# I: MyType{foo: 1, bar: 'baz'} =====> @@ -272,7 +272,7 @@ P: MyType{ L: MyType{ foo:1^#3[1,12]#^#2[1,10]#, bar:"baz"^#5[1,20]#^#4[1,18]# -}^#1[1,0]# +}^#1[1,6]# I: Message{`in`: true} =====> @@ -281,7 +281,7 @@ P: Message{ }^#1:Expr.CreateStruct# L: Message{ in:true^#3[1,14]#^#2[1,12]# -}^#1[1,0]# +}^#1[1,7]# I: Msg{?field: value} =====> @@ -290,7 +290,41 @@ P: Msg{ }^#1:Expr.CreateStruct# L: Msg{ ?field:value^#3[1,12]#^#2[1,10]# -}^#1[1,0]# +}^#1[1,3]# + +I: foo.bar.MyType{ } +=====> +P: foo.bar.MyType{}^#1:Expr.CreateStruct# +L: foo.bar.MyType{}^#1[1,14]# + +I: foo.bar.MyType{ a:b } +=====> +P: foo.bar.MyType{ + a:b^#3:Expr.Ident#^#2:Expr.CreateStruct.Entry# +}^#1:Expr.CreateStruct# +L: foo.bar.MyType{ + a:b^#3[1,18]#^#2[1,17]# +}^#1[1,14]# + +I: .foo.bar.MyType{ a:b } +=====> +P: .foo.bar.MyType{ + a:b^#3:Expr.Ident#^#2:Expr.CreateStruct.Entry# +}^#1:Expr.CreateStruct# +L: .foo.bar.MyType{ + a:b^#3[1,19]#^#2[1,18]# +}^#1[1,15]# + +I: a.b.c.d.Message{ foo: 1, bar: 'baz' } +=====> +P: a.b.c.d.Message{ + foo:1^#3:int64#^#2:Expr.CreateStruct.Entry#, + bar:"baz"^#5:string#^#4:Expr.CreateStruct.Entry# +}^#1:Expr.CreateStruct# +L: a.b.c.d.Message{ + foo:1^#3[1,22]#^#2[1,20]#, + bar:"baz"^#5[1,30]#^#4[1,28]# +}^#1[1,15]# I: a.b =====> @@ -310,7 +344,7 @@ P: _?._( )^#2:Expr.Call# L: _?._( a^#1[1,0]#, - "b"^#3[1,3]# + "b"^#3[1,0]# )^#2[1,1]# I: a.`b-c` @@ -597,7 +631,7 @@ P: _-_( )^#2:Expr.Call# L: _-_( 4^#1[1,0]#, - -4^#3[1,2]# + -4^#3[1,3]# )^#2[1,1]# I: 4--4.1 @@ -608,7 +642,7 @@ P: _-_( )^#2:Expr.Call# L: _-_( 4^#1[1,0]#, - -4.1^#3[1,2]# + -4.1^#3[1,3]# )^#2[1,1]# I: "abc" + "def" @@ -805,29 +839,29 @@ I: a && b =====> P: _&&_( a^#1:Expr.Ident#, - b^#2:Expr.Ident# -)^#3:Expr.Call# + b^#3:Expr.Ident# +)^#2:Expr.Call# L: _&&_( a^#1[1,0]#, - b^#2[1,5]# -)^#3[1,2]# + b^#3[1,5]# +)^#2[1,2]# I: a && b && c =====> P: _&&_( _&&_( a^#1:Expr.Ident#, - b^#2:Expr.Ident# - )^#3:Expr.Call#, - c^#4:Expr.Ident# -)^#5:Expr.Call# + b^#3:Expr.Ident# + )^#2:Expr.Call#, + c^#5:Expr.Ident# +)^#4:Expr.Call# L: _&&_( _&&_( a^#1[1,0]#, - b^#2[1,5]# - )^#3[1,2]#, - c^#4[1,10]# -)^#5[1,7]# + b^#3[1,5]# + )^#2[1,2]#, + c^#5[1,10]# +)^#4[1,7]# I: a && b && c && d && e && f && g =====> @@ -835,40 +869,40 @@ P: _&&_( _&&_( _&&_( a^#1:Expr.Ident#, - b^#2:Expr.Ident# - )^#3:Expr.Call#, + b^#3:Expr.Ident# + )^#2:Expr.Call#, _&&_( - c^#4:Expr.Ident#, - d^#6:Expr.Ident# - )^#7:Expr.Call# - )^#5:Expr.Call#, + c^#5:Expr.Ident#, + d^#7:Expr.Ident# + )^#6:Expr.Call# + )^#4:Expr.Call#, _&&_( _&&_( - e^#8:Expr.Ident#, - f^#10:Expr.Ident# - )^#11:Expr.Call#, - g^#12:Expr.Ident# - )^#13:Expr.Call# -)^#9:Expr.Call# + e^#9:Expr.Ident#, + f^#11:Expr.Ident# + )^#10:Expr.Call#, + g^#13:Expr.Ident# + )^#12:Expr.Call# +)^#8:Expr.Call# L: _&&_( _&&_( _&&_( a^#1[1,0]#, - b^#2[1,5]# - )^#3[1,2]#, + b^#3[1,5]# + )^#2[1,2]#, _&&_( - c^#4[1,10]#, - d^#6[1,15]# - )^#7[1,12]# - )^#5[1,7]#, + c^#5[1,10]#, + d^#7[1,15]# + )^#6[1,12]# + )^#4[1,7]#, _&&_( _&&_( - e^#8[1,20]#, - f^#10[1,25]# - )^#11[1,22]#, - g^#12[1,30]# - )^#13[1,27]# -)^#9[1,17]# + e^#9[1,20]#, + f^#11[1,25]# + )^#10[1,22]#, + g^#13[1,30]# + )^#12[1,27]# +)^#8[1,17]# I: a > 5 && a < 10 =====> @@ -878,31 +912,31 @@ P: _&&_( 5^#3:int64# )^#2:Expr.Call#, _<_( - a^#4:Expr.Ident#, - 10^#6:int64# - )^#5:Expr.Call# -)^#7:Expr.Call# + a^#5:Expr.Ident#, + 10^#7:int64# + )^#6:Expr.Call# +)^#4:Expr.Call# L: _&&_( _>_( a^#1[1,0]#, 5^#3[1,4]# )^#2[1,2]#, _<_( - a^#4[1,9]#, - 10^#6[1,13]# - )^#5[1,11]# -)^#7[1,6]# + a^#5[1,9]#, + 10^#7[1,13]# + )^#6[1,11]# +)^#4[1,6]# I: a || b =====> P: _||_( a^#1:Expr.Ident#, - b^#2:Expr.Ident# -)^#3:Expr.Call# + b^#3:Expr.Ident# +)^#2:Expr.Call# L: _||_( a^#1[1,0]#, - b^#2[1,5]# -)^#3[1,2]# + b^#3[1,5]# +)^#2[1,2]# I: a || b || c || d || e || f =====> @@ -910,34 +944,34 @@ P: _||_( _||_( _||_( a^#1:Expr.Ident#, - b^#2:Expr.Ident# - )^#3:Expr.Call#, - c^#4:Expr.Ident# - )^#5:Expr.Call#, + b^#3:Expr.Ident# + )^#2:Expr.Call#, + c^#5:Expr.Ident# + )^#4:Expr.Call#, _||_( _||_( - d^#6:Expr.Ident#, - e^#8:Expr.Ident# - )^#9:Expr.Call#, - f^#10:Expr.Ident# - )^#11:Expr.Call# -)^#7:Expr.Call# + d^#7:Expr.Ident#, + e^#9:Expr.Ident# + )^#8:Expr.Call#, + f^#11:Expr.Ident# + )^#10:Expr.Call# +)^#6:Expr.Call# L: _||_( _||_( _||_( a^#1[1,0]#, - b^#2[1,5]# - )^#3[1,2]#, - c^#4[1,10]# - )^#5[1,7]#, + b^#3[1,5]# + )^#2[1,2]#, + c^#5[1,10]# + )^#4[1,7]#, _||_( _||_( - d^#6[1,15]#, - e^#8[1,20]# - )^#9[1,17]#, - f^#10[1,25]# - )^#11[1,22]# -)^#7[1,12]# + d^#7[1,15]#, + e^#9[1,20]# + )^#8[1,17]#, + f^#11[1,25]# + )^#10[1,22]# +)^#6[1,12]# I: a < 5 || a > 10 =====> @@ -947,20 +981,20 @@ P: _||_( 5^#3:int64# )^#2:Expr.Call#, _>_( - a^#4:Expr.Ident#, - 10^#6:int64# - )^#5:Expr.Call# -)^#7:Expr.Call# + a^#5:Expr.Ident#, + 10^#7:int64# + )^#6:Expr.Call# +)^#4:Expr.Call# L: _||_( _<_( a^#1[1,0]#, 5^#3[1,4]# )^#2[1,2]#, _>_( - a^#4[1,9]#, - 10^#6[1,13]# - )^#5[1,11]# -)^#7[1,6]# + a^#5[1,9]#, + 10^#7[1,13]# + )^#6[1,11]# +)^#4[1,6]# I: a && b && c && d || e && f && g && h =====> @@ -968,46 +1002,46 @@ P: _||_( _&&_( _&&_( a^#1:Expr.Ident#, - b^#2:Expr.Ident# - )^#3:Expr.Call#, + b^#3:Expr.Ident# + )^#2:Expr.Call#, _&&_( - c^#4:Expr.Ident#, - d^#6:Expr.Ident# - )^#7:Expr.Call# - )^#5:Expr.Call#, + c^#5:Expr.Ident#, + d^#7:Expr.Ident# + )^#6:Expr.Call# + )^#4:Expr.Call#, _&&_( _&&_( - e^#8:Expr.Ident#, - f^#9:Expr.Ident# + e^#9:Expr.Ident#, + f^#11:Expr.Ident# )^#10:Expr.Call#, _&&_( - g^#11:Expr.Ident#, - h^#13:Expr.Ident# + g^#13:Expr.Ident#, + h^#15:Expr.Ident# )^#14:Expr.Call# )^#12:Expr.Call# -)^#15:Expr.Call# +)^#8:Expr.Call# L: _||_( _&&_( _&&_( a^#1[1,0]#, - b^#2[1,5]# - )^#3[1,2]#, + b^#3[1,5]# + )^#2[1,2]#, _&&_( - c^#4[1,10]#, - d^#6[1,15]# - )^#7[1,12]# - )^#5[1,7]#, + c^#5[1,10]#, + d^#7[1,15]# + )^#6[1,12]# + )^#4[1,7]#, _&&_( _&&_( - e^#8[1,20]#, - f^#9[1,25]# + e^#9[1,20]#, + f^#11[1,25]# )^#10[1,22]#, _&&_( - g^#11[1,30]#, - h^#13[1,35]# + g^#13[1,30]#, + h^#15[1,35]# )^#14[1,32]# )^#12[1,27]# -)^#15[1,17]# +)^#8[1,17]# I: a?b:c =====> @@ -1042,11 +1076,11 @@ P: _?_:_( _&&_( false^#1:bool#, !_( - true^#3:bool# - )^#2:Expr.Call# - )^#4:Expr.Call#, - false^#5:bool# - )^#6:Expr.Call#, + true^#4:bool# + )^#3:Expr.Call# + )^#2:Expr.Call#, + false^#6:bool# + )^#5:Expr.Call#, 2^#8:int64#, 3^#9:int64# )^#7:Expr.Call# @@ -1055,11 +1089,11 @@ L: _?_:_( _&&_( false^#1[1,0]#, !_( - true^#3[1,10]# - )^#2[1,9]# - )^#4[1,6]#, - false^#5[1,18]# - )^#6[1,15]#, + true^#4[1,10]# + )^#3[1,9]# + )^#2[1,6]#, + false^#6[1,18]# + )^#5[1,15]#, 2^#8[1,26]#, 3^#9[1,30]# )^#7[1,24]# @@ -1139,23 +1173,23 @@ P: _&&_( 0^#5:int64# )^#4:Expr.Call#, _[?_]( - a^#6:Expr.Ident#, - c^#8:Expr.Ident# - )^#7:Expr.Call# -)^#9:Expr.Call# + a^#7:Expr.Ident#, + c^#9:Expr.Ident# + )^#8:Expr.Call# +)^#6:Expr.Call# L: _&&_( _[?_]( _?._( a^#1[1,0]#, - "b"^#3[1,3]# + "b"^#3[1,0]# )^#2[1,1]#, 0^#5[1,6]# )^#4[1,4]#, _[?_]( - a^#6[1,12]#, - c^#8[1,15]# - )^#7[1,13]# -)^#9[1,9]# + a^#7[1,12]#, + c^#9[1,15]# + )^#8[1,13]# +)^#6[1,9]# I: // comment a @@ -1205,4 +1239,4 @@ P: [ L: [ 1^#2[2,2]#, 2^#3[3,2]# -]^#1[1,0]# +]^#1[1,0]# \ No newline at end of file diff --git a/parser/src/test/resources/pratt_parser_errors.baseline b/parser/src/test/resources/pratt_parser_errors.baseline index e88dd385e..d21e65ab3 100644 --- a/parser/src/test/resources/pratt_parser_errors.baseline +++ b/parser/src/test/resources/pratt_parser_errors.baseline @@ -21,7 +21,7 @@ E: ERROR: :1:5: Syntax error: unexpected character I: ó ¢ »»ó 0  -»»0"""\""\"""\""\"""\""\"""\""\"""\"\"""\""\"""\""\"""\""\"""\"!\"""\""\"""\""\" +»»\u007f0"""\""\"""\""\"""\""\"""\""\"""\"\"""\""\"""\""\"""\""\"""\"!\"""\""\"""\""\" =====> E: ERROR: :1:1: Syntax error: unexpected character | ó ¢ @@ -413,34 +413,34 @@ ERROR: :1:33: Syntax error: expected ']' I: a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.A.B.C.D.E.F.G.H =====> -E: ERROR: :1:62: Expression recursion limit exceeded. limit: 32 +E: ERROR: :1:66: Expression recursion limit exceeded. limit: 32 | a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.A.B.C.D.E.F.G.H - | .............................................................^ + | .................................................................^ I: a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] »» [21][22][23][24][25][26][27][28][29][30][31][32][33] =====> -E: ERROR: :2:48: Expression recursion limit exceeded. limit: 32 +E: ERROR: :2:56: Expression recursion limit exceeded. limit: 32 | [21][22][23][24][25][26][27][28][29][30][31][32][33] - | ...............................................^ + | .......................................................^ I: 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 »»+ 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 »»+ 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 »»+ 31 + 32 + 33 + 34 =====> -E: ERROR: :4:8: Expression recursion limit exceeded. limit: 32 +E: ERROR: :4:18: Expression recursion limit exceeded. limit: 32 | + 31 + 32 + 33 + 34 - | .......^ + | .................^ I: a < 1 < 2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < 10 < 11 »» < 12 < 13 < 14 < 15 < 16 < 17 < 18 < 19 < 20 < 21 »»» < 22 < 23 < 24 < 25 < 26 < 27 < 28 < 29 < 30 < 31 »»» < 32 < 33 =====> -E: ERROR: :3:51: Expression recursion limit exceeded. limit: 32 - | < 22 < 23 < 24 < 25 < 26 < 27 < 28 < 29 < 30 < 31 - | ..................................................^ +E: ERROR: :4:11: Expression recursion limit exceeded. limit: 32 + | < 32 < 33 + | ..........^ I: y!=y!=y!=y!=y!=y!=y!=y!=y!=-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y »»!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y @@ -449,9 +449,9 @@ I: y!=y!=y!=y!=y!=y!=y!=y!=y!=-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y »»!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y »»!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y =====> -E: ERROR: :2:55: Expression recursion limit exceeded. limit: 32 +E: ERROR: :2:63: Expression recursion limit exceeded. limit: 32 | !=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y - | ......................................................^ + | ..............................................................^ I: a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] != »»a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] != @@ -468,7 +468,7 @@ I: a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] != »»a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] != »»a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] =====> -E: ERROR: :11:76: Expression recursion limit exceeded. limit: 32 +E: ERROR: :13:76: Expression recursion limit exceeded. limit: 32 | a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] != | ...........................................................................^ @@ -480,9 +480,9 @@ E: ERROR: :1:353: Expression recursion limit exceeded. limit: 32 I: !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x =====> -E: ERROR: :1:31: Expression recursion limit exceeded. limit: 32 +E: ERROR: :1:33: Expression recursion limit exceeded. limit: 32 | !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!x - | ..............................^ + | ................................^ I: 123456 =====> diff --git a/parser/src/test/resources/pratt_parser_literals.baseline b/parser/src/test/resources/pratt_parser_literals.baseline index 5a3b7ec77..f6ce2f6b0 100644 --- a/parser/src/test/resources/pratt_parser_literals.baseline +++ b/parser/src/test/resources/pratt_parser_literals.baseline @@ -36,16 +36,16 @@ L: 42^#1[1,0]# I: -1 =====> P: -1^#1:int64# -L: -1^#1[1,0]# +L: -1^#1[1,1]# I: -42 =====> P: -42^#1:int64# -L: -42^#1[1,0]# +L: -42^#1[1,1]# I: 0xFFFFFFFFFFFFFFFFF =====> -E: ERROR: :1:1: Syntax error: invalid int literal +E: ERROR: :1:1: Syntax error: invalid int literal: 0xFFFFFFFFFFFFFFFFF | 0xFFFFFFFFFFFFFFFFF | ^ @@ -57,11 +57,11 @@ L: 9223372036854775807^#1[1,0]# I: -9223372036854775808 =====> P: -9223372036854775808^#1:int64# -L: -9223372036854775808^#1[1,0]# +L: -9223372036854775808^#1[1,1]# I: -(9223372036854775808) =====> -E: ERROR: :1:3: Syntax error: invalid int literal +E: ERROR: :1:3: Syntax error: invalid int literal: 9223372036854775808 | -(9223372036854775808) | ..^ @@ -88,7 +88,7 @@ L: 15u^#1[1,0]# I: 0xFFFFFFFFFFFFFFFFFu =====> -E: ERROR: :1:1: Syntax error: invalid uint literal +E: ERROR: :1:1: Syntax error: invalid uint literal: 0xFFFFFFFFFFFFFFFFFu | 0xFFFFFFFFFFFFFFFFFu | ^ @@ -136,9 +136,8 @@ L: 0.0^#1[1,0]# I: 1.99e90000009 =====> -E: ERROR: :1:1: Syntax error: invalid double literal - | 1.99e90000009 - | ^ +P: Infinity^#1:double# +L: Infinity^#1[1,0]# I: 1e =====> diff --git a/parser/src/test/resources/pratt_parser_macros.baseline b/parser/src/test/resources/pratt_parser_macros.baseline index dabf57e31..688a355d5 100644 --- a/parser/src/test/resources/pratt_parser_macros.baseline +++ b/parser/src/test/resources/pratt_parser_macros.baseline @@ -636,25 +636,25 @@ P: __comprehension__( // Variable z, // Target - y^#19:Expr.Ident#, + y^#20:Expr.Ident#, // Accumulator @result, // Init - false^#26:bool#, + false^#27:bool#, // LoopCondition @not_strictly_false( !_( - @result^#27:Expr.Ident# - )^#28:Expr.Call# - )^#29:Expr.Call#, + @result^#28:Expr.Ident# + )^#29:Expr.Call# + )^#30:Expr.Call#, // LoopStep _||_( - @result^#30:Expr.Ident#, - z^#23:Expr.Ident#.b~test-only~^#25:Expr.Select# - )^#31:Expr.Call#, + @result^#31:Expr.Ident#, + z^#24:Expr.Ident#.b~test-only~^#26:Expr.Select# + )^#32:Expr.Call#, // Result - @result^#32:Expr.Ident#)^#33:Expr.Comprehension# - )^#34:Expr.Call#, + @result^#33:Expr.Ident#)^#34:Expr.Comprehension# + )^#19:Expr.Call#, _+_( @result^#37:Expr.Ident#, [ @@ -705,25 +705,25 @@ L: __comprehension__( // Variable z, // Target - y^#19[1,37]#, + y^#20[1,37]#, // Accumulator @result, // Init - false^#26[1,45]#, + false^#27[1,45]#, // LoopCondition @not_strictly_false( !_( - @result^#27[1,45]# - )^#28[1,45]# - )^#29[1,45]#, + @result^#28[1,45]# + )^#29[1,45]# + )^#30[1,45]#, // LoopStep _||_( - @result^#30[1,45]#, - z^#23[1,53]#.b~test-only~^#25[1,52]# - )^#31[1,45]#, + @result^#31[1,45]#, + z^#24[1,53]#.b~test-only~^#26[1,52]# + )^#32[1,45]#, // Result - @result^#32[1,45]#)^#33[1,45]# - )^#34[1,34]#, + @result^#33[1,45]#)^#34[1,45]# + )^#19[1,34]#, _+_( @result^#37[1,8]#, [ @@ -738,15 +738,15 @@ M: x^#1:Expr.Ident#.filter( y^#3:Expr.Ident#, _&&_( ^#18:exists#, - ^#33:exists# - )^#34:Expr.Call# + ^#34:exists# + )^#19:Expr.Call# )^#0:Expr.Call#, -y^#19:Expr.Ident#.exists( - z^#21:Expr.Ident#, - ^#25:has# +y^#20:Expr.Ident#.exists( + z^#22:Expr.Ident#, + ^#26:has# )^#0:Expr.Call#, has( - z^#23:Expr.Ident#.b^#24:Expr.Select# + z^#24:Expr.Ident#.b^#25:Expr.Select# )^#0:Expr.Call#, y^#4:Expr.Ident#.exists( z^#6:Expr.Ident#, @@ -760,14 +760,14 @@ I: (has(a.b) || has(c.d)).string() =====> P: _||_( a^#2:Expr.Ident#.b~test-only~^#4:Expr.Select#, - c^#6:Expr.Ident#.d~test-only~^#8:Expr.Select# -)^#9:Expr.Call#.string()^#10:Expr.Call# + c^#7:Expr.Ident#.d~test-only~^#9:Expr.Select# +)^#5:Expr.Call#.string()^#10:Expr.Call# L: _||_( a^#2[1,5]#.b~test-only~^#4[1,4]#, - c^#6[1,17]#.d~test-only~^#8[1,16]# -)^#9[1,10]#.string()^#10[1,29]# + c^#7[1,17]#.d~test-only~^#9[1,16]# +)^#5[1,10]#.string()^#10[1,29]# M: has( - c^#6:Expr.Ident#.d^#7:Expr.Select# + c^#7:Expr.Ident#.d^#8:Expr.Select# )^#0:Expr.Call#, has( a^#2:Expr.Ident#.b^#3:Expr.Select# @@ -819,7 +819,7 @@ L: __comprehension__( )^#14[1,24]#, // Result @result^#15[1,24]#)^#16[1,24]# -M: ^#4:has#.asList()^#5:Expr.Call#.exists( +M: a^#2:Expr.Ident#.b~test-only~^#4:has#.asList()^#5:Expr.Call#.exists( c^#7:Expr.Ident#, c^#8:Expr.Ident# )^#0:Expr.Call#, diff --git a/parser/src/test/resources/source_info.baseline b/parser/src/test/resources/source_info.baseline index 153a49822..1e4f6a686 100644 --- a/parser/src/test/resources/source_info.baseline +++ b/parser/src/test/resources/source_info.baseline @@ -140,4 +140,4 @@ macro_calls { } } } -} +} \ No newline at end of file