ruby / lrama

Pure Ruby LALR parser generator
190 stars 26 forks source link

Intoroduce parameterizing rules with conditional statement #418

Open ydah opened 4 months ago

ydah commented 4 months ago

I would like to propose a new grammar in this PR. I believe that more parameterizing rules can handle more abstract rules if we can switch between rules and actions that are expanded by conditions in order to make rules common.

Syntax is as follows:

%rule defined_rule(X, condition): /* empty */
                                | X { $$ = $1; } %if(condition)
                                ;

%%

r_true        : defined_rule(number, %true)
              ;

r_false       : defined_rule(number, %false)
              ;

It's like a postfix if in Ruby. If condition is false, it is equivalent to missing this line.

❯ exe/lrama --trace=rules spec/fixtures/parameterizing_rules/user_defined/if.y
Grammar rules:
$accept -> r_true YYEOF
defined_rule_number_true -> ε
defined_rule_number_true -> number
r_true -> defined_rule_number_true
defined_rule_number_false -> ε
r_false -> defined_rule_number_false
Old design DesignDoc: https://gist.github.com/ydah/62008655a6f6c3118ab01134dc91da6f Syntax is as follows: ``` %rule defined_rule(X, condition): /* empty */ | X { $$ = $1; } %if(condition) /* 1 */ | %if(condition) X %endif X { $$ = $1; } /* 2 */ ; %% r_true : defined_rule(number, %true) ; r_false : defined_rule(number, %false) ; ``` 1. It's like a postfix if in Ruby. If condition is false, it is equivalent to missing this line. 2. If statementIf condition is false, it is equivalent to missing RHS between `%if` and`% endif`. ``` ❯ exe/lrama --trace=rules spec/fixtures/parameterizing_rules/user_defined/if.y Grammar rules: $accept -> r_true YYEOF defined_rule_number_true -> ε defined_rule_number_true -> number defined_rule_number_true -> number number r_true -> defined_rule_number_true defined_rule_number_false -> ε defined_rule_number_false -> number r_false -> defined_rule_number_false ```

Motivation

I believe it will solve the problem mentioned in the article below with the tight coupling with Lexer "to disable certain generation rules under certain conditions" and I would like to propose this feature to solve this problem. https://yui-knk.hatenablog.com/entry/2023/04/04/190413

We can trace the RHS to f_args > args_tail > args_forward, where f_args is the RHS of both the lambda argument (f_larglist) and the method definition argument (f_arglist). So if we can switch between RHS and actions by passing parameters, we can break up the Lexer/Parser coupling here.

yui-knk commented 1 month ago

I agree to the feature. What I concern is the syntax. There are 3 points for discussion:

  1. Modifier style (%if(condition)) makes senes for me
  2. The syntax for parameters (defined_rule(X, condition)) is a point of concern. Parameters for rule (X) are different from parameters for condition (condition). If so I want to make the difference clear from the syntax. For example, in Ruby optional parameter is clear from its syntax (a = 1) and we can set the constrain on the order of parameters if condition parameter has different syntax from rule parameter. One example is adding some prefix to condition parameter like defined_rule(X, @condition).
  3. The syntax for condition argument is another point of concern. I think the meaning of rule argument and condition argument are different. Condition argument might be a kind of expression. If so it might be better to introduce different syntax like defined_rule(number, %{true}) or defined_rule(number, {true}) and so on.

Sorry for the late response.