Gopar - The Golang Parser that Needs a Better Name
05 Oct 2018A while back I built a PEG (Parsing Expression Grammer) parser in golang. I wasn’t blogging at the time, so the idea slipped under the radar. Here’s a link to the codebase.
And here I am using the parser API to build a JSON parser. If you’ve ever dealt with parsers, I think this reads pretty clearly!
digit := OneOfChars("0123456789").Rename("Digit")
char := OneOfChars(" \t\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~!@#$%^&*()_+`-={}|[]\\:;'<>?,./'").Rename("Char")
// numbers are a sequence of digits optionally with a '.' and then some
// more digits
number := Seq(
OneOrMoreOf(digit),
ZeroOrOneOf(Seq(
S("."),
OneOrMoreOf(digit),
)),
).Rename("Number")
// strings are a bunch of characters surrounded by " (I'm lazy, I did
// include ' strings and quote characters in strings)
str := Seq(
S("\""),
OneOrMoreOf(char),
S("\""),
).Rename("JsonString")
// values can be strings or numbers or Objects or Lists ... hey wait,
// we haven't defined Objects or Lists yet. No problem, `P(string)`
// creates a placeholderRule that will later be patched with the rule it
// names
value := OneOf(
str,
number,
P("Object"),
P("List"),
).Rename("Value")
// a list is, well, a list of values
list := Seq(
S("["),
ZeroOrOneOf(Seq(
value,
ZeroOrMoreOf(Seq(
S(","),
value,
)),
)),
S("]"),
).Rename("List")
// keyVal has a string key and a value val
keyVal := Seq(
str,
S(":"),
value,
).Rename("KeyValue")
// an object is a bunch of keyVal pairs
object := Seq(
S("{"),
ZeroOrOneOf(Seq(
keyVal,
ZeroOrMoreOf(Seq(
S(","),
keyVal,
)),
)),
S("}"),
).Rename("Object")
// an object is a bunch of keyVal pairs
err := Patch(object,list)
if err != nil {
t.Fatal(err)
}
//The big test: nested lists and dicts some which are empty
expectNoErr(t, object, `{"apple":"red","banana":[1,2],"coconut":{"a":1,"b":[],"c":{}}}`)
expectErr(t, object, `{"apple":"red","banana":[1,2],"coconut":{"a":1,"b":[],"c":{}}`,
"error at offset 61 in rule Object>'}'. EOF")