-
Notifications
You must be signed in to change notification settings - Fork 598
parser: fix ambiguous grammar rules #10569
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
dfb9173 to
03cf3b1
Compare
|
This is another topic I don't fully understand (yet), so bear with me, I may be entirely wrong: I've experimented a bit and found that Which leads to my primary confusion: What is that expression expected to do in the first place? If not, maybe we should rather detect an error like "Attempted to call non-function object." or something like that. If we clearly parsed out that kind of erroneous semantics, it would apply to all permutations where it causes ambiguity otherwise. No idea how that would look like exactly, probably extending the |
Are you sure? I can't reproduce this: $ cat icinga2.conf
() => { {{ {{{foo}}} }} }()()
$ prefix/sbin/icinga2 daemon -c icinga2.conf
[2025-09-29 17:08:49 +0200] information/cli: Icinga application loader (version: v2.15.0-112-g03cf3b16f; debug)
[2025-09-29 17:08:49 +0200] critical/config: Error: Value computed is not used.
Location: in icinga2.conf: 1:8-1:22
icinga2.conf(1): () => { {{ {{{foo}}} }} }()()
^^^^^^^^^^^^^^^
[2025-09-29 17:08:49 +0200] critical/cli: Config validation failed. Re-run with 'icinga2 daemon -C' after fixing the config.$ cat icinga2.conf
() => { "foo" }()
$ prefix/sbin/icinga2 daemon -c icinga2.conf
[2025-09-29 17:06:57 +0200] information/cli: Icinga application loader (version: v2.15.0-112-g03cf3b16f; debug)
[2025-09-29 17:06:57 +0200] critical/config: Error: Value computed is not used.
Location: in icinga2.conf: 1:8-1:12
icinga2.conf(1): () => { "foo" }()
^^^^^
[2025-09-29 17:06:57 +0200] critical/cli: Config validation failed. Re-run with 'icinga2 daemon -C' after fixing the config.
Why don't you think so? That's exactly what the expression should do. First the parser defines an anonymous lambda which by definition is a callable object, then constructs a function call expression with the lambda as callee. The ambiguity without this PR arises because the parser has too many options that all lead to the
The parser isn't responsible to detect such semantic errors. Its job is just to parse the input and produce some output var x = () => { {{ {{{foo}}} }} }()
log(x())This won't trigger any parsing ambiguities and will actually work as expected and print |
From the wording, that error sounds more like a runtime error than a parse error to me.
Is it? Isn't that just the question which of the two ambiguous options is chosen? The minimal example is actually this: |
Sorry, I can't reproduce it anymore either today. I double checked yesterday, back and forth to make sure I'm not imagining it, but seems I did... 🤦
The "Argument is not a callable object." might be a runtime error, but first you'd need to parse it correctly so we can detect it.
But then why doesn't it? I think either the parser should correctly hand the Function object to the FunctionCallExpression object, or it should fail with an error at parse time because we don't allow this syntax. |
Okay, such error handling is already implemented at evaluation time, so I don't know what else you want me to do then.
You are questioning the entire behavior of such an otherwise perfectly fine language construct. So yes, it is a language change if you would want to forbid that. As I already explained, we're using a GLR parser, normally any reduce/reduce conflict is resolved by starting sub-parsers and letting them run multiple parse trees in parallel until one of them produces a valid parse tree and the others silently fail with a syntax error at some point. In this case, however, both parse trees are valid until the very end, thus both parsers succeed and produce an identical function call expression. The first parse tree interpreted the expression as a function call but it would mark it as with side effects through the So, the parser can't decide which of the two parse trees to pick, because both are valid interpretations of the expression and both of them have the same
The former is a lambda expression that is immediately called, the latter is a lambda expression with a malformed body, two very different things. However, with the first expression you seem to somehow forced the parser to pick the first parse tree described above, thus eliminating the ambiguity. In that case, it produced the function call exp with alleged side effects through the Footnotes
|
This PR fixes a DSL config parsing ambiguity discovered by @julianbrost by using this expression
() => { {{ {{{foo}}} }} }(). First, I thought that only the last two rules were conflicting each other due to the same assigned dynamic precedence. However, after adding this expression as a test case, I found that the entirelterm_items_innerrule was conflicting due to the inconsistent precedence assignments in its alternate rules. I've now changed the precedence assignments so that it always prefers the rule that reduces on/shiftsrterm_no_side_effectfirst, so that when you have pure expressions like the trigger of this bug, it reports an error about unused value instead of a parsing error. The precedence only needs to be consistent with the rules on the same level, i.e. a rule that can directly be reduced tolterm_items_innerwithout a recursion doesn't need to have a smaller or higher precedence than a rule that has left recursion, because they will never conflict.Expand Me