Skip to content

Instantly share code, notes, and snippets.

@artpej
Created July 4, 2015 07:36
Show Gist options
  • Save artpej/6f25363e790aef09eb43 to your computer and use it in GitHub Desktop.
Save artpej/6f25363e790aef09eb43 to your computer and use it in GitHub Desktop.
Golo parser combinator adventure...
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,"{","}")
}
@yloiseau
Copy link

yloiseau commented Jul 4, 2015

in the same way, as a matter of style you could write

function any = -> |reader| -> match {
  when reader is null or reader: isEmpty() then null
  otherwise Context(charValue(reader: charAt(0)), reader: substring(1))
}

function oneof = |target| -> chartest(|x| -> target: contains(x+""))

which I found more readable...

@artpej
Copy link
Author

artpej commented Jul 4, 2015

I've mainly done this changes before watching your comments... Damn! i begin to think just like you....

@yloiseau
Copy link

yloiseau commented Jul 4, 2015

:) welcome to the dark side…

@yloiseau
Copy link

yloiseau commented Jul 4, 2015

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