Ohm Exercises:
import * as ohm from "ohm-js" const grammars = { canadianPostalCode: String.raw` code = alpha digit alpha " " digit alpha digit alpha = "A".."C" | "E" | "G".."H" | "J".."N" | "P" | "R".."T" | "V".."Z" `, visa: String.raw` card = "4" d d d d d d d d d d d d (d d d)? d = digit `, masterCard: String.raw` card = "5" "1".."5" d d d12 --fives | "222" "1".."9" d12 --range2221_2229 | "22" "3".."9" d d12 --range2230_2299 | "2" "3".."6" d d d12 --range2300_2699 | "27" "0".."1" d d d12 --range2700_2719 | "2720" d12 --range2720 d12 = d d d d d d d d d d d d d = digit `, notThreeEndingInOO: String.raw` ok = long | threeWithNoFinalO | threeWithNoPenultimateO | two | char? long = char char char char+ threeWithNoFinalO = char char nonOChar threeWithNoPenultimateO = char nonOChar char two = char char char = "A".."Z" | "a".."z" nonOChar = ~"o" ~"O" char `, divisibleBy16: String.raw` ok = (~("0000" end) ("0"|"1"))* "0000" -- nonzero | "0" "0"? "0"? "0"? -- zero `, eightThroughThirtyTwo: String.raw` ok = "1".."2" digit -- tenToTwentyNine | "3" "0".."2" -- thirtyToThirtyTwo | "8".."9" -- eightToNine `, notPythonPycharmPyc: String.raw` word = ~("python" end) ~("pycharm" end) ~("pyc" end) letter* `, restrictedFloats: String.raw` float = digit+ frac? exp frac = "." digit+ exp = ("E" | "e") ("+" | "-")? digit digit? digit? `, palindromes2358: String.raw` pal2358 = pal8 | pal5 | pal3 | pal2 pal8 = "a" pal6 "a" | "b" pal6 "b" | "c" pal6 "c" pal6 = "a" pal4 "a" | "b" pal4 "b" | "c" pal4 "c" pal5 = "a" pal3 "a" | "b" pal3 "b" | "c" pal3 "c" pal4 = "a" pal2 "a" | "b" pal2 "b" | "c" pal2 "c" pal3 = "a" pal1 "a" | "b" pal1 "b" | "c" pal1 "c" pal2 = "aa" | "bb" | "cc" pal1 = "a" | "b" | "c" `, pythonStringLiterals: String.raw` stringliteral = stringprefix? (longstring | shortstring) stringprefix = "r" | "u" | "R" | "U" | "f" | "F" | "fr" | "Fr" | "fR" | "FR" | "rf" | "rF" | "Rf" | "RF" shortstring = "'" shortstringitem_s* "'" | "\"" shortstringitem_d* "\"" longstring = "'''" longstringitem_s* "'''" | "\"\"\"" longstringitem_d* "\"\"\"" shortstringitem_s = shortstringchar_s | stringescapeseq shortstringitem_d = shortstringchar_d | stringescapeseq longstringitem_s = longstringchar_s | stringescapeseq longstringitem_d = longstringchar_d | stringescapeseq shortstringchar_s = ~"\n" ~"\\" ~"'" any shortstringchar_d = ~"\n" ~"\\" ~"\"" any longstringchar_s = ~"\\" ~"'''" any longstringchar_d = ~"\\" ~"\"\"\"" any stringescapeseq = "\\" any `, } export function matches(name, string) { const grammar = `G {${grammars[name]}}` return ohm.grammar(grammar).match(string).succeeded() }
An Ohm Grammar for a Mystery Language:
Mystery { Program = FunDecl* Exp Exp = Exp1 if Exp1 else Exp --conditional | Exp1 Exp1 = Exp1 ("+" | "-") Exp2 --additive | Exp2 Exp2 = Exp2 ("*" | "/") Exp3 --multiplicative | Exp3 Exp3 = "-" Exp4 --negation | Exp4 Exp4 = Exp5 "!" --factorial | Exp5 Exp5 = num | str | Call | id | "(" Exp ")" --parens FunDecl = func id Params Body Params = "(" ListOf<id, ","> ")" Body = NonemptyListOf<Exp, ";"> endkw Call = id Args Args = "[" ListOf<Exp, ","> "]" id = ~keyword (letter | "@") idchar* idchar = alnum | "_" | "@" | "$" endkw = "end" ~idchar func = "func" ~idchar if = "if" ~idchar else = "else" ~idchar keyword = endkw | func | if | else num = digit+ ("." digit+)? (("E" | "e") ("+" | "-")? digit+)? str = "\"" char* "\"" char = ~("\"" | "\\") any --direct | "\\" escape --escaped escape = "'" | "\"" | "\\" | "n" | "u{" hexes "}" --codepoint hexes = h h? h? h? h? h? h = hexDigit space += "--" (~"\n" any)* --comment }
Here are the syntax diagrams for the phrase categories of the language above: