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