diff --git a/doc/langdef.md b/doc/langdef.md index 8e495342..ede69d3f 100644 --- a/doc/langdef.md +++ b/doc/langdef.md @@ -51,8 +51,19 @@ FieldInits = IDENT ":" Expr {"," IDENT ":" Expr} ; MapInits = Expr ":" Expr {"," Expr ":" Expr} ; ``` -Implementations are required to support at least 250 levels of recursion in -parsing, but may reject programs that exceed that limit. +Implementations are required to support at least: + +* 32 levels of nested expressions; +* 32 repetitions of self-recursive or repetitive rules, i.e.: + * 32 terms separated by `||` in a row; + * 32 terms separated by `&&` in a row; + * 32 relations in a row; + * 32 binary arithmetic operators of the same precedence in a row; + * 32 selection (`.`) operators in a row; + * 32 indexing (`[_]`) operators in a row; + * 32 function call arguments; + * list literals with 32 elements; + * map or message literals with 32 fields. This grammar corresponds to the following operator precedence and associativity: diff --git a/tests/simple/testdata/parse.textproto b/tests/simple/testdata/parse.textproto index 7cc10c34..c73e6aeb 100644 --- a/tests/simple/testdata/parse.textproto +++ b/tests/simple/testdata/parse.textproto @@ -1,11 +1,129 @@ name: "parse" description: "End-to-end parsing tests." section { - name: "depth" + name: "nest" description: "Deep parse trees which all implementations must support." + test { + name: "list_index" + description: "Member = Member '[' Expr ']'" + expr: "a[a[a[a[a[a[a[a[a[a[a[a[a[a[a[a[a[a[a[a[a[a[a[a[a[a[a[a[a[a[a[a[0]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" + type_env { + name: "a" + ident { type { list_type { elem_type { primitive: INT64 } } } } + } + bindings { + key: "a" + value { value { list_value { values { int64_value: 0 } } } } + } + value { int64_value: 0 } + } + test { + name: "message_literal" + description: "Member = Member '{' [FieldInits] '}'" + container: "google.api.expr.test.v1.proto3" + expr: "NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{child: NestedTestAllTypes{payload: TestAllTypes{single_int64: 137}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}.payload.single_int64" + value { int64_value: 0 } + } + test { + name: "funcall" + description: "Primary = ['.'] IDENT ['(' [ExprList] ')']" + expr: "int(uint(int(uint(int(uint(int(uint(int(uint(int(uint(int(uint(int(uint(int(uint(int(uint(int(uint(int(uint(int(uint(int(uint(int(uint(int(uint(7))))))))))))))))))))))))))))))))" + value { int64_value: 7 } + } + test { + name: "parens" + description: "Primary = '(' Expr ')'" + expr: "((((((((((((((((((((((((((((((((7))))))))))))))))))))))))))))))))" + value { int64_value: 7 } + } test { name: "list_literal" - expr: "size([[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['just fine'],[1],[2],[3],[4],[5]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]])" + description: "Primary = '[' [ExprList] ']'" + expr: "size([[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[0]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]])" value { int64_value: 1 } } + test { + name: "map_literal" + description: "Primary = '{' [MapInits] '}'" + expr: "size({0: {0: {0: {0: {0: {0: {0: {0: {0: {0: {0: {0: {0: {0: {0: {0: {0: {0: {0: {0: {0: {0: {0: {0: {0: {0: {0: {0: {0: {0: {0: 'foo'}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}})" + value { int64_value: 1 } + } +} +section { + name: "repeat" + description: "Repetitive parse trees which all implementations must support." + test { + name: "conditional" + description: "Expr = ConditionalOr ['?' ConditionalOr ':' Expr]" + expr: "true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : true ? true : false" + value { bool_value: true } + } + test { + name: "or" + description: "ConditionalOr = [ConditionalOr '||'] ConditionalAnd" + expr: "false || false || false || false || false || false || false || false || false || false || false || false || false || false || false || false || false || false || false || false || false || false || false || false || false || false || false || false || false || false || false || false || true" + value { bool_value: true } + } + test { + name: "and" + description: "ConditionalAnd = [ConditionalAnd '&&'] Relation" + expr: "true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && true && false" + value { bool_value: false } + } + test { + name: "add_sub" + description: "Addition = [Addition ('+' | '-')] Multiplication" + expr: "3 - 3 + 3 - 3 + 3 - 3 + 3 - 3 + 3 - 3 + 3 - 3 + 3 - 3 + 3 - 3 + 3 - 3 + 3 - 3 + 3 - 3 + 3 - 3 + 3 - 3 + 3 - 3 + 3 - 3 + 3 - 3 + 3" + value { int64_value: 3 } + } + test { + name: "mul_div" + description: "Multiplication = [Multiplication ('*' | '/' | '%')] Unary" + expr: "4 * 4 / 4 * 4 / 4 * 4 / 4 * 4 / 4 * 4 / 4 * 4 / 4 * 4 / 4 * 4 / 4 * 4 / 4 * 4 / 4 * 4 / 4 * 4 / 4 * 4 / 4 * 4 / 4 * 4 / 4 * 4 / 4" + value { int64_value: 4 } + } + test { + name: "not" + description: "Unary = '!' {'!'} Member" + expr: "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!true" + value { bool_value: true } + } + test { + name: "unary_neg" + description: "Unary = '-' {'-'} Member" + expr: "--------------------------------19" + value { int64_value: 19 } + } + test { + name: "select" + description: "Member = Member '.' IDENT ['(' [ExprList] ')']" + container: "google.api.expr.test.v1.proto3" + expr: "NestedTestAllTypes{}.child.child.child.child.child.child.child.child.child.child.child.child.child.child.child.child.child.child.child.child.child.child.child.child.child.child.child.child.child.child.payload.single_int32" + value { int64_value: 0 } + } + test { + name: "index" + description: "Member = Member '[' Expr ']'" + expr: "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['foo']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0]" + value { string_value: "foo" } + } + test { + name: "list_literal" + description: "Primary = '[' [ExprList] ']'" + expr: "[0, 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][17]" + value { int64_value: 17 } + } + test { + name: "map_literal" + description: "Primary = '{' [MapInits] '}'" + expr: "{0: 'zero', 1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five', 6: 'six', 7: 'seven', 8: 'eight', 9: 'nine', 10: 'ten', 11: 'eleven', 12: 'twelve', 13: 'thirteen', 14: 'fourteen', 15: 'fifteen', 16: 'sixteen', 17: 'seventeen', 18: 'eighteen', 19: 'nineteen', 20: 'twenty', 21: 'twenty-one', 22: 'twenty-two', 23: 'twenty-three', 24: 'twenty-four', 25: 'twenty-five', 26: 'twenty-six', 27: 'twenty-seven', 28: 'twenty-eight', 29: 'twenty-nine', 30: 'thirty', 31: 'thirty-one'}[17]" + value { string_value: 'seventeen' } + } + test { + name: "message_literal" + description: "Member = Member '{' [FieldInits] '}'" + container: "google.api.expr.test.v1.proto3" + expr: "TestAllTypes{single_int32: 5, single_int64: 10, single_uint32: 15u, single_uint64: 20u, single_sint32: 25, single_sint64: 30, single_fixed32: 35u, single_fixed64: 40u, single_float: 45.0, single_double: 50.0, single_bool: true, single_string: 'sixty', single_bytes: b'sixty-five', single_value: 70.0, single_int64_wrapper: 75, single_int32_wrapper: 80, single_double_wrapper: 85.0, single_float_wrapper: 90.0, single_uint64_wrapper: 95u, single_uint32_wrapper: 100u, single_string_wrapper: 'one hundred five', single_bool_wrapper: true, repeated_int32: [115], repeated_int64: [120], repeated_uint32: [125u], repeated_uint64: [130u], repeated_sint32: [135], repeated_sint64: [140], repeated_fixed32: [145u], repeated_fixed64: [150u], repeated_sfixed32: [155], repeated_float: [160.0]}.single_sint64" + value { int64_value: 30 } + } }