Miksilo logo Miksilo

BiGrammar is Miksilo’s metalanguage for everything syntax related. Its main strength is that it defines both a parser and a printer at the same time. Operators in BiGrammar will often have an equivalent effect on both the parser and the printer, although some are asymmetrical. In this article we’ll go through code examples showcasing the most important operators. Afterwards you will comfortably read BiGrammar code.

Parsing & printing

Here is a grammar for a common while loop:

"while" ~ expression.inParenthesis.as(Condition) ~~ "{" %
        statement.manyVertical.indent(2).as(Body) %
        "}" asNode While

The grammars for expression and statement have been left out for brevity.

The operators have the following meaning:

With the BiGrammar defined above, we can parse the following program (the ugly formatting is intentional):

while (i){
  i--; x += 2;
}

which yields this AST:

Shape: While
Condition: 
  Shape: Variable
  Name: i
Body:
  - Shape: Decrement
    Target: i
  - Shape: PlusEquals
    Target: x
    Value: 
      Shape: Constant
      Value: 2  

Also using the above grammar, we can then pretty print the AST to get:

while(i) {
  i--;
  x += 2;
}

Choice, ignore and value

The following grammar maps “yes” and “no” strings to their corresponding boolean values:

"yes" ~> value(true) | "no" ~> value(false)

Regex and map

The following grammar maps an integer to its string representation:

new RegexGrammar("""-?\d+""".r).map[String, Int](
  afterParsing = digits => Integer.parseInt(digits),
  beforePrinting = int => int.toString
)

Next

Now that you’re comfortable reading BiGrammar, continue to see how BiGrammar supports modular language design.