Created
July 4, 2015 07:36
-
-
Save artpej/6f25363e790aef09eb43 to your computer and use it in GitHub Desktop.
Golo parser combinator adventure...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module parser | |
struct Context = {current, rest} | |
function any = { | |
return |reader| { | |
if reader is null or reader: isEmpty() { | |
return null | |
} | |
return Context(charValue(reader: charAt(0)), reader: substring(1)) | |
} | |
} | |
function whitespaces = { | |
return star(oneof(" \t\n\r")) | |
} | |
function chartest = |predicate| { | |
return |reader| { | |
let c = any()(reader) | |
if c != null and predicate(c: current()) { | |
return c | |
} | |
return null | |
} | |
} | |
function matchchar = |char| { | |
return chartest(|x| -> x is char) | |
} | |
function oneof = |target| { | |
return chartest(|x| { | |
return target: contains(x+"") | |
}) | |
} | |
function matchstr = |target| { | |
if target is null or target: isEmpty() { | |
return |reader| -> Context("", reader) | |
} | |
return |reader| { | |
let c = matchchar(target: charAt(0))(reader) | |
if c != null { | |
let cs = matchstr(target:substring(1))(c:rest()) | |
if cs != null { | |
return Context(c:current() + cs: current() , cs: rest()) | |
} | |
} | |
return null | |
} | |
} | |
function optional = |parserfn| { | |
return |reader| { | |
let c = parserfn(reader) | |
if c != null { | |
return c | |
} | |
return Context("",reader) | |
} | |
} | |
function plus = |parser| { | |
return |reader| { | |
let c = parser(reader) | |
if c != null { | |
let cs = star(parser)(c: rest()) | |
return Context(c: current() + cs: current(), cs: rest()) | |
} | |
return null | |
} | |
} | |
function star = |parser| { | |
return optional(plus(parser)) | |
} | |
function alt = |parsers...| { | |
return |reader| { | |
foreach p in parsers { | |
let result = p(reader) | |
if result != null { | |
return result | |
} | |
} | |
} | |
} | |
function sequence = |parsers...| { | |
return |reader| { | |
var parsed = "" | |
var rest = reader | |
foreach p in parsers { | |
let result = p(rest) | |
if result != null { | |
rest = result: rest() | |
parsed = parsed + result: current() | |
} else { | |
return null | |
} | |
} | |
return Context(parsed, rest) | |
} | |
} | |
function between = |parser, left, right| { | |
return |reader| { | |
let lres = matchchr(left)(reader) | |
if lres != null { | |
let pres = parser(lres: rest()) | |
if pres != null { | |
let rres = matchchr(right)(pres: rest()) | |
if rres != null { | |
return Restult( left + pres: current() + right , rres: rest()) | |
} | |
} | |
} | |
return null | |
} | |
} | |
function main = |args| { | |
println(any()("abcd")) | |
println(chartest(|x| -> x is 'a')("abc")) | |
let alpha = oneof("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") | |
let loweralpha = oneof("abcdefghijklmnopqrstuvwxyz") | |
let upperalpha = oneof("ABCDEFGHIJKLMNOPQRSTUVWXYZ") | |
let digit = oneof("0123456789") | |
let hexdigit = oneof("0123456789abcdefABCDEF") | |
let whitespace = oneof(" \t\n\r") | |
println(whitespace(" a b c ")) | |
let matchwhile = matchstr("while") | |
println(matchwhile("while true{}")) | |
let optional_matchwhile = optional(matchwhile) | |
println(optional_matchwhile("foo")) | |
let optrepeat_while = star(matchwhile) | |
println(optrepeat_while("whilewhilewhile")) | |
let repeat_while = plus(matchwhile) | |
println(repeat_while("foo")) | |
println(repeat_while("while foo")) | |
let iforwhileorfor = alt(matchstr("if"), matchstr("while"), matchstr("for")) | |
println(iforwhileorfor("if")) | |
println(iforwhileorfor("while")) | |
println(iforwhileorfor("for")) | |
println(iforwhileorfor("foo")) | |
let whileToken = sequence(whitespaces(), matchstr("while") , whitespaces()) | |
let conditional = oneof("><=") | |
let colonToken = matchchar(':') | |
let codeBlock = alt(matchstr("if"), matchstr("for")) | |
let whileStmt = sequence(whileToken, conditional, colonToken, codeBlock) | |
println(whileStmt(" while <:if")) | |
println(whileStmt("while>:if")) | |
println(whileStmt("while>:for")) | |
println(whileStmt("while:for")) | |
let betweenparens = |p| -> between(p,"(",")") | |
let betweenbrackets = |p| -> between(p,"[","]") | |
let betweencurlies = |p| -> between(p,"{","}") | |
} |
another one...
if you wrap all your (unary) functions and lambdas with something like
function nullable = |func| -> |arg| -> match {
when arg is null then null
otherwise func(arg)
}
(e.g. with a decorator) along with elvis operator and orIfNull
, you can get rid of a lot of your tests, and make all the stuff even more readable
(btw, this decorator should be included in the stdlib, it's in my pipe)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
:) welcome to the dark side…