Skip to content

Instantly share code, notes, and snippets.

@happy-barney
Last active August 17, 2020 20:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save happy-barney/90de5cedee52a7855c0a780a3649cf7d to your computer and use it in GitHub Desktop.
Save happy-barney/90de5cedee52a7855c0a780a3649cf7d to your computer and use it in GitHub Desktop.

Conditional inclusion/exclusion of alternatives

Requires parser capable to evaluate lookup predicate, such as GLR parser. With additional complexity on declaration side it may solvable by error handling magic but I didn't solve this puzzle so far.

Example: multiple grammar variants in one definition file:

allowed_indirect_method:
		INDIRECT_METHOD 
	|	%?{ FEATURE_INDIRECT_IS_ENABLED } METHOD

Example: simplify lexer by moving predicates into grammar (using say to show idea, not a final implementation)

	keyword.c:
-                return (all_keywords || FEATURE_SAY_IS_ENABLED ? KEY_say : 0);
+                return KEY_say;

	toke.c:
		case (KEY_say): TOKEN(SAY);

	perly.y:
bareword: BAREWORD
	| %?{ ! FEATURE_SAY_IS_ENABLED } SAY
	;

Move actions out of perly.y

Example:

expr:	expr[lhs] ANDOP expr[rhs]
			{ $$ = parser->action->flow_control_and_expression (OP_AND, $lhs, $rhs); }
	|	expr[lhs] OROP[operator] expr[rhs]
			{ $$ = parser->action->flow_control_or_expression ($operator, $lhs, $rhs); }
	|	listexpr %prec PREC_LOW

Usage of "virtual method table" allows to install different rule handlers (eg: PPI) as well as it will help with obsoleted feature maintenance, for example moving them into dynamically loaded plugins (when feature will be necessary) - plugin loader will just update table with proper implementations.

unify actions to use "named parameters"

Example:

expr:	expr[lhs] ANDOP expr[rhs]
			{ $$ = parser->action->flow_control_and_expression (& (struct expr) {
				.operator = OP_AND, .lhs = $lhs, .rhs = $rhs
			} ); }
	|	expr[lhs] OROP[operator] expr[rhs]
			{ $$ = parser->action->flow_control_or_expression (& (struct expr) {
				.operator = $operator, .lhs = $lhs, .rhs = $rhs
			} ); }
	|	listexpr %prec PREC_LOW

Usage of this pattern proved valuable for me in modernization of complex code (with help of variadic macros - requires C99)

%{
#define EXPR_ACTION(Type, ...) action->Type( & (struct expr) { .operator = 0, .lhs = NULL, .rhs = NULL, __ARGS__ }
#define FLOW_CONTROL_AND_EXPRESSION(First, ...) EXPR_ACTION (flow_control_and_expression, First, __ARGS__ }
#define FLOW_CONTROL_OR_EXPRESSION(First, ...) EXPR_ACTION (flow_control_or_expression, First, __ARGS__ }
}%
expr:	expr[lhs] ANDOP expr[rhs]
			{ $$ = parser->FLOW_CONTROL_AND_EXPRESSION(.operator = OP_AND, .lhs = $lhs, .rhs = $rhs); }
	|	expr[lhs] OROP[operator] expr[rhs]
			{ $$ = parser->FLOW_CONTROL_OR_EXPRESSION(.operator = $operator, .lhs = $lhs, .rhs = $rhs); }
	|	listexpr %prec PREC_LOW

Misc "end game" goals

  • single grammar definition for any perl version (v5, v7, vX ...)
  • get rid of toke.c, keyword.c; replace them with as simple as possible (f)lex
  • generate language support tools (mvp: emacs perl-mode, PPI tokenizer as parser's virtual method table)
@rabbiveesh
Copy link

hey, are you actively working on this? I highly support the whole plan that you have here, and would love to help in whatever way I can.
I think even just to rewrite the parser using a GLR (which sounds like it would work) could go a long way for tooling, even if it wouldn't be merged right away upstream.

@happy-barney
Copy link
Author

Hi @rabbiveesh, thanks for support, and yes, I'd like to work on it (starting with pr #18036). Next steps include move of parser logic from lexer into parser and naming of alternatives and actions (as much as possible). More will come but it's premature to plan.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment