0

I want to create a grammar parsing some commands. Most is working flawless but the "if(condition,then-value,else-value)" is not working together with "out" command to show some value. It works fine in case the output-command is outside the if-command:

out(if(1,42,43))

→ output and return 42 as expected OK

But at the moment the output-command is inside then- and else-part (which is required to be more intuitive) it fails:

if(1,out(42),out(43))

→ still return only 42 as expected OK, but the output function is called twice with 42 and 43

I'm working under C with the peg/leg parser generator here The problem is also reproducible with PEG.js online parser generator here when using the following very much simplified grammar:

Expression
  = Int
  / "if(" cond:Expression "," ok:Expression "," nok:Expression ")" { return cond?ok:nok; }
  / "out(" num:Expression ")" { window.alert(num); return num;}

Int = [0-9]+ { return parseInt(text(), 10); }

The "window.alert()" is only a placeholder for the needed output function, but for this problem it acts the same. It looks like the scanner have to match the full if-command with then- and else-value until the closing bracket ")". So it matches both out-commands and they both execute the defined function - which is not what I expect.

Is there a way in peg/leg to match some characters but suppress execution of the according function under some circumstances?

(I've already experimented with "&" predicate element without success)

(Maybe left-recursion vs. right-recursion could help here, but used peg/leg-generator seems to only supports right-recursion)

Cœur
  • 37,241
  • 25
  • 195
  • 267
Achim
  • 442
  • 1
  • 3
  • 13

2 Answers2

2

Is there a way in peg/leg to match some characters but suppress execution of the according function under some circumstances?

I'm not familiar with the tools in question, but it would surprise me if this were possible. And even if it were, you'd run into a similar problem when implementing loops: now you'd need to execute the action multiple times.

What you need is for your actions to not directly execute the code, but return something that can be used to execute it.

The usual way that interpreters work is that the parser produces some sort of representation of the source code (such as bytecode or an AST), which is then executed as a separate step.

The simplest (but perhaps not cleanest) way to make your parser work without changing too much would be to just wrap all your actions in 0-argument functions. You could then call the functions returned by the sub-expressions if and only if you want them to be executed. And to implement loops, you could then simply call the functions multiple times.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • Wow, this would lead to a complete redesign of my grammar. For now I have 20+ functions implemented inside the grammar behind the pattern as {}. Most are very simple. Creating a bytecode tree and executing them would be indeed a valid solution - currently I don't have another choice. Thanks Achim – Achim Nov 14 '17 at 16:08
0

An solution could be using a predicate expression "& {expression}" (not to be confused by predicate element "& element")

Expression
  = Function
  
Function
  = Int
  / "if(" IfCond "," ok:Function "," nok:FunctionDisabled ")" { return ok; }
  / "if(" FunctionDisabled "," ok:FunctionDisabled "," nok:Function ")" { return nok; }
  / "out(" num:Function ")" { window.alert("Out:"+num); return num;}
 
FunctionDisabled
  = Int
  / "if(" IfCond "," ok:FunctionDisabled "," nok:FunctionDisabled ")" { return ok; }
  / "if(" FunctionDisabled "," ok:FunctionDisabled "," nok:FunctionDisabled ")" { return nok; }

  / "out(" num:FunctionDisabled ")" { return num;}

IfCond
  = cond:FunctionDisabled   &{ return cond; }
                   
Int = [0-9]+ { return parseInt(text(), 10); }

The idea is to define the out() twice, once really doing something and a second time disabled without output. The condition of the if-command is evaluated using the code inside {}, so if the condition is false, the whole expression match failes.

Visible drawback is the redundant definition of the if-command for then and else and recursive disabled

Achim
  • 442
  • 1
  • 3
  • 13
  • In addition to the code duplication (which will become increasingly annoying as the grammar grows larger), note that this approach won't help with loops. This also doesn't seem to quite match the desired behaviour as your if-conditions now no longer have their side-effects executed (which I assume you did on purpose to prevent them from being executed twice). – sepp2k Nov 16 '17 at 11:53