use(require(“atomy”)) use(require(“grammar”))
parser(Parser):
%%: def(trim-leading(str, n)): unless(n > 0): return(str) str gsub!(r"\n {0,#{n}}", "\n") def(word(l, x)): w = Atomy Grammar AST Word new(x) w line = l w %atomy = Atomy Grammar rule(line): { current-line } rule(column): { current-column } rule(comment): "{-" in-multi rule(in-multi): [ /"[^\-\{\}]*"/ "-}" /"[^\-\{\}]*"/ "{-" in-multi /"[^\-\{\}]*"/ "-}" /"[^\-\{\}]*"/ /"[-{}]"/ in-multi ] rule(content(s)): comment? c=(chunk(s) | escaped) comment? { c } rule(insignificant): (<(/"[^\\\{\}]+"/)> | "\\" <(/"[\\\{\}]"/)>) { text } rule(chunk(s)): [ l=(line) chunk=(insignificant+) c=((&"}" | comment)?) { text = chunk join when(c): text rstrip! trim-leading(text, s) Atomy Grammar AST StringLiteral new(text) } nested ] rule(escaped): [ l=(line) "\\" n=(%atomy(identifier)) as=(argument+) { `((~word(l, n))(~*as)) } l=(line) "\\" n=(%atomy(identifier)) { word(l, n) } l=(line) "\\" "(" e=(%atomy(expression)) ")" { e } ] rule(leading): [ &(/"\n+"/ b=(column) /"\s+"/ a=(column)) { a - b } { 0 } ] rule(nested): l=(line) "{" s=(leading) cs=(content(s)*) "}" { when(cs[0] is-a?(Atomy Grammar AST StringLiteral)): cs[0] value sub!(r"^\n", "") cs match: []: Atomy Grammar AST StringLiteral new("") [x]: x _: `[~*cs] } rule(atomy): "[" e=(%atomy(expression)) "]" { e } rule(argument): nested | atomy rule(root): l=(line) cs=(content(0)*) !_ { setup = Array[] definitions = Array[] filter = [c]: c match: (`use(~_) | `require(~_)): setup << c 'nil `(def(~x): ~*y): definitions << c 'nil `let(~x, ~y): definitions << `(~x = ~y) 'nil `[~*cs]: `[~*(cs map &filter)] _: c body = cs map &filter Atomy Grammar AST Sequence new([ 'use(require("atomy")) 'use(require("anatomy/base")) `(do: ~*setup ~*definitions def(doc): decode(~*body)) ]) }
Parser singleton:
def(parse-string(str)): p = new(str) unless(p parse): p raise-error p result def(parse-file(path)): File open(path, "r") [f]: parse-string(f read)