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
}
BONUS: Here are the syntax diagrams for the phrase categories of the language above: