// avoid this problem: https://groups.google.com/forum/#!topic/coffeescript/or4vC69SQ-M // indentation handling courtesy of http://www.coyotos.org/pipermail/bitc-dev/2009-February/001510.html ometa CS <: Parser { space = ' ', newline = '\n', comment = '#' (~newline char)*, emptyLine = space* newline | space* comment newline, spacing = space | newline | comment, // iApply takes a rule and indentation. The rule must take indentation as an argument. // This is really just a shorter way to use `_applyWithArgs` // Plain `apply` ought to take optional arguments, which would obviate the need for this rule. iApply :r :i = _applyWithArgs(r, i):x -> x, // iListOf takes a rule, a delimit rule and indentation. // Both rules must take indentation as an argument. // Note that iListOf accepts an empty list, but ensuring indentation in case of newlines/emptyLines. iListOf :r :d :i = iApply(r,i):x ( iApply(d,i) iApply(r,i) )*:xs -> [x].concat(xs) | (emptyLine+ matchInset(i))? space* -> [], inset :i = matchInset(i) space+:ss -> i.concat(ss), matchInset :i = seq(i), block :i = inset(i):newi expr(newi):e emptyLine indentedExpr(newi)+:es -> ['block', [e].concat(es)] | inset(i):newi expr(newi):e -> ['block', [e]], expr :i = expr2(i):x space* -> x, expr2 :i = function(i) | call(i) | assignment(i) | string | id, indentedExpr :i = emptyLine* matchInset(i) expr(i):e -> e, stringChar :quote = char:c ?(c != quote) -> c | '\\':bs char:c ?(c == quote) -> [bs, c].join(''), // TODO: interpolation! string = '"' stringChar('"')*:cs '"' -> ['string', cs.join('')] | '\'' stringChar('\'')*:cs '\'' -> ['string', cs.join('')], keyword = "if" | "for" | "continue" | "break", // TODO: finish list idStartChar = letter | '_' | '$', idChar = idStartChar | number, id = ~keyword idStartChar:start idChar*:idcs -> ['id', [start].concat(idcs).join('')], assignment :i = id:id "=" space* expr(i):e -> ['assign', id, e] | id:id "=" emptyLine+ matchInset(i) space* expr(i):e -> ['assign', id, e], function :i = "->" emptyLine+ block(i):x -> ['function', x], argList :i = iListOf('expr', 'argDelim', i):xs -> xs, argDelim :i = ",", call :i = id:f "(" space* argList(i):xs ','? ")" -> [#call, f, xs] | id:f space+ argList(i):xs ','? -> [#call, f, xs] | id:f '(' space* ')' -> [#call, f, []], begin = indentedExpr([])+:xs emptyLine* ~anything -> xs } CS.matchAll(" -> f one,two,three,four five( ) ", "begin") test.matchAll("s,d,f", 'expr') ometa L { p = 'a' p:x 'a' -> ['a', x, 'a'] | 'a' -> 'a', start = p:x end -> x } L.matchAll("aaa", 'start') // [a, a, a] L.matchAll("aaaaa", 'start') // match failed L.matchAll("aaaaaaa", 'start') // [a, [a, [a, a, a], a], a] L.matchAll("aaaaaaaaaaaaaaa", 'start') // [a, [a, [a, [a, [a, [a, [a, a, a], a], a], a], a], a], a]