verify parse trees pl0x

master
Jordan Orelli 10 years ago
parent 636a30b963
commit 876794d694

@ -41,8 +41,10 @@ func (t tokenType) String() string {
return "t_object_separator" return "t_object_separator"
case t_object_end: case t_object_end:
return "t_object_end" return "t_object_end"
case t_number: case t_real_number:
return "t_number" return "t_real_number"
case t_imaginary_number:
return "t_imaginary_number"
default: default:
panic(fmt.Sprintf("unknown token type: %v", t)) panic(fmt.Sprintf("unknown token type: %v", t))
} }
@ -62,7 +64,8 @@ const (
t_object_start // { t_object_start // {
t_object_end // } t_object_end // }
t_object_separator // : t_object_separator // :
t_number // a number t_real_number // a number
t_imaginary_number // an imaginary number
) )
type stateFn func(*lexer) stateFn type stateFn func(*lexer) stateFn
@ -321,13 +324,17 @@ func lexNumber(l *lexer) stateFn {
l.accept("+-") l.accept("+-")
l.acceptRun("0123456789") l.acceptRun("0123456789")
} }
l.accept("i") imaginary := l.accept("i")
r := l.next() r := l.next()
if isAlphaNumeric(r) { if isAlphaNumeric(r) {
return lexErrorf("unexpected alphanum in lexNumber: %c", r) return lexErrorf("unexpected alphanum in lexNumber: %c", r)
} }
l.unread(r) l.unread(r)
l.emit(t_number) if imaginary {
l.emit(t_imaginary_number)
} else {
l.emit(t_real_number)
}
return lexRoot return lexRoot
} }

@ -64,38 +64,39 @@ var primitivesTests = []struct {
{t_list_separator, ","}, {t_list_separator, ","},
{t_object_end, "}"}, {t_object_end, "}"},
}}, }},
{`0`, []token{{t_number, "0"}}}, {`0`, []token{{t_real_number, "0"}}},
{`-0`, []token{{t_number, "-0"}}}, {`-0`, []token{{t_real_number, "-0"}}},
{`+0`, []token{{t_number, "+0"}}}, {`+0`, []token{{t_real_number, "+0"}}},
{`+125`, []token{{t_number, "+125"}}}, {`+125`, []token{{t_real_number, "+125"}}},
{`-125`, []token{{t_number, "-125"}}}, {`-125`, []token{{t_real_number, "-125"}}},
{`.0`, []token{{t_number, ".0"}}}, {`.0`, []token{{t_real_number, ".0"}}},
{`15`, []token{{t_number, "15"}}}, {`15`, []token{{t_real_number, "15"}}},
{`0x0`, []token{{t_number, "0x0"}}}, {`0x0`, []token{{t_real_number, "0x0"}}},
{`0xa`, []token{{t_number, "0xa"}}}, {`0xa`, []token{{t_real_number, "0xa"}}},
{`0xc0dea5cf`, []token{{t_number, "0xc0dea5cf"}}}, {`0xc0dea5cf`, []token{{t_real_number, "0xc0dea5cf"}}},
{`12.345`, []token{{t_number, "12.345"}}}, {`12.345`, []token{{t_real_number, "12.345"}}},
{`12.345 name`, []token{{t_number, "12.345"}, {t_name, "name"}}}, {`12.345 name`, []token{{t_real_number, "12.345"}, {t_name, "name"}}},
{`[12.345]`, []token{ {`[12.345]`, []token{
{t_list_start, "["}, {t_list_start, "["},
{t_number, "12.345"}, {t_real_number, "12.345"},
{t_list_end, "]"}, {t_list_end, "]"},
}}, }},
{`[1, 2, 3]`, []token{ {`[1, 2, 3]`, []token{
{t_list_start, "["}, {t_list_start, "["},
{t_number, "1"}, {t_real_number, "1"},
{t_list_separator, ","}, {t_list_separator, ","},
{t_number, "2"}, {t_real_number, "2"},
{t_list_separator, ","}, {t_list_separator, ","},
{t_number, "3"}, {t_real_number, "3"},
{t_list_end, "]"}, {t_list_end, "]"},
}}, }},
// an imaginary number generates two lexemes; one for its real component, {`1i`, []token{{t_imaginary_number, "1i"}}},
// a complex number generates two lexemes; one for its real component,
// and one for its imaginary component. // and one for its imaginary component.
{`1+2i`, []token{{t_number, "1"}, {t_number, "+2i"}}}, {`1+2i`, []token{{t_real_number, "1"}, {t_imaginary_number, "+2i"}}},
{`1e9`, []token{{t_number, "1e9"}}}, {`1e9`, []token{{t_real_number, "1e9"}}},
{`1e+9`, []token{{t_number, "1e+9"}}}, {`1e+9`, []token{{t_real_number, "1e+9"}}},
{`1E-9`, []token{{t_number, "1E-9"}}}, {`1E-9`, []token{{t_real_number, "1E-9"}}},
} }
func TestLexPrimities(t *testing.T) { func TestLexPrimities(t *testing.T) {

@ -40,8 +40,7 @@ func (n *rootNode) parse(p *parser) error {
case t_eof: case t_eof:
return nil return nil
case t_comment: case t_comment:
shit := commentNode(t.s) n.addChild(&commentNode{t.s})
n.addChild(&shit)
case t_name: case t_name:
nn := &assignmentNode{name: t.s} nn := &assignmentNode{name: t.s}
if err := nn.parse(p); err != nil { if err := nn.parse(p); err != nil {
@ -74,18 +73,20 @@ func (n *rootNode) String() string {
return buf.String() return buf.String()
} }
type commentNode string type commentNode struct {
body string
}
func (n commentNode) Type() nodeType { func (n *commentNode) Type() nodeType {
return n_comment return n_comment
} }
func (n commentNode) parse(p *parser) error { func (n *commentNode) parse(p *parser) error {
return nil return nil
} }
func (n commentNode) String() string { func (n *commentNode) String() string {
return fmt.Sprintf("{comment: %s}", string(n)) return fmt.Sprintf("{comment: %s}", n.body)
} }
type assignmentNode struct { type assignmentNode struct {
@ -93,7 +94,7 @@ type assignmentNode struct {
value interface{} value interface{}
} }
func (n assignmentNode) Type() nodeType { func (n *assignmentNode) Type() nodeType {
return n_assignment return n_assignment
} }
@ -118,44 +119,8 @@ func (n *assignmentNode) parse(p *parser) error {
} }
func (n *assignmentNode) String() string { func (n *assignmentNode) String() string {
return fmt.Sprintf("{assign: name=%s, val=%s}", n.name, n.value) return fmt.Sprintf("{assign: name=%s, val=%v}", n.name, n.value)
}
type list struct {
head *listElem
tail *listElem
}
func (l list) String() string {
var buf bytes.Buffer
buf.WriteString("[")
for e := l.head; e != nil; e = e.next {
fmt.Fprintf(&buf, "%v, ", e.value)
}
if buf.Len() > 1 {
buf.Truncate(buf.Len() - 2)
}
buf.WriteString("]")
return buf.String()
}
func (l *list) append(v interface{}) {
e := listElem{value: v}
if l.head == nil {
l.head = &e
}
if l.tail != nil {
l.tail.next = &e
e.prev = l.tail
}
l.tail = &e
}
type listElem struct {
value interface{}
prev *listElem
next *listElem
} }
type list []interface{}
type object map[string]interface{} type object map[string]interface{}

@ -3,6 +3,7 @@ package main
import ( import (
"fmt" "fmt"
"io" "io"
"strconv"
) )
const () const ()
@ -78,8 +79,11 @@ func (p *parser) parseValue() (interface{}, error) {
return nil, fmt.Errorf("parse error: unexpected eof when looking for value") return nil, fmt.Errorf("parse error: unexpected eof when looking for value")
case t_string: case t_string:
return t.s, nil return t.s, nil
case t_real_number, t_imaginary_number:
p.unread(t)
return p.number()
case t_list_start: case t_list_start:
return p.parseList(new(list)) return p.parseList(make(list, 0, 4))
case t_object_start: case t_object_start:
return p.parseObject(make(object)) return p.parseObject(make(object))
default: default:
@ -88,7 +92,7 @@ func (p *parser) parseValue() (interface{}, error) {
} }
} }
func (p *parser) parseList(l *list) (*list, error) { func (p *parser) parseList(l list) (list, error) {
if p.peek().t == t_list_end { if p.peek().t == t_list_end {
p.next() p.next()
return l, nil return l, nil
@ -97,7 +101,7 @@ func (p *parser) parseList(l *list) (*list, error) {
if v, err := p.parseValue(); err != nil { if v, err := p.parseValue(); err != nil {
return nil, err return nil, err
} else { } else {
l.append(v) l = append(l, v)
} }
switch t := p.next(); t.t { switch t := p.next(); t.t {
@ -139,3 +143,31 @@ func (p *parser) parseObject(obj object) (object, error) {
return nil, fmt.Errorf("parse error: unexpected %v token while scanning for object", t.t) return nil, fmt.Errorf("parse error: unexpected %v token while scanning for object", t.t)
} }
} }
func (p *parser) number() (interface{}, error) {
t := p.next()
if t.t != t_real_number {
return nil, fmt.Errorf("unexpected %s token while parsing number", t.t)
}
if p.peek().t == t_imaginary_number {
var c complex128
s := t.s + p.next().s
if _, err := fmt.Sscan(s, &c); err != nil {
return nil, fmt.Errorf("ungood imaginary number format %s: %s", s, err)
}
return c, nil
}
i, err := strconv.ParseInt(t.s, 0, 64)
if err == nil {
return int(i), nil
}
f, err := strconv.ParseFloat(t.s, 64)
if err == nil {
return f, nil
}
return nil, fmt.Errorf("this token broke the number parser: %s", t)
}

@ -1,6 +1,7 @@
package main package main
import ( import (
"reflect"
"strings" "strings"
"testing" "testing"
) )
@ -16,7 +17,7 @@ var parseTests = []parseTest{
source: `# just a comment`, source: `# just a comment`,
root: &rootNode{ root: &rootNode{
children: []node{ children: []node{
commentNode(" just a comment"), &commentNode{" just a comment"},
}, },
}, },
}, },
@ -24,46 +25,65 @@ var parseTests = []parseTest{
source: `name = "jordan"`, source: `name = "jordan"`,
root: &rootNode{ root: &rootNode{
children: []node{ children: []node{
&assignmentNode{ &assignmentNode{"name", "jordan"},
name: "name",
value: "jordan",
},
}, },
}, },
}, },
{ {
source: ` source: `
first_name = "jordan" hostname = "jordanorelli.com"
last_name = "orelli" port = 9000
freq = 1e9
duty = 0.2
neg = -2
neg2 = -2.3
imag = 1+2i
`, `,
root: &rootNode{}, root: &rootNode{
children: []node{
&assignmentNode{"hostname", "jordanorelli.com"},
&assignmentNode{"port", 9000},
&assignmentNode{"freq", 1e9},
&assignmentNode{"duty", 0.2},
&assignmentNode{"neg", -2},
&assignmentNode{"neg2", -2.3},
&assignmentNode{"imag", 1 + 2i},
},
}, },
{
source: `
# personal info
first_name = "jordan"
last_name = "orelli"
`,
root: &rootNode{},
}, },
{ {
source: ` source: `
first_name = "jordan" # yep, that's my name first_name = "jordan" # yep, that's my name
last_name = "orelli" # comments should be able to follow other shit last_name = "orelli" # comments should be able to follow other shit
`, `,
root: &rootNode{}, root: &rootNode{
children: []node{
&assignmentNode{"first_name", "jordan"},
&commentNode{" yep, that's my name"},
&assignmentNode{"last_name", "orelli"},
&commentNode{" comments should be able to follow other shit"},
},
},
}, },
{ {
source: ` source: `
heroes = ["lina", "cm"] heroes = ["lina", "cm"]
`, `,
root: &rootNode{}, root: &rootNode{
children: []node{
&assignmentNode{"heroes", list{"lina", "cm"}},
},
},
}, },
{ {
source: ` source: `
nested = [["one", "two"], ["three", "four"]] nested = [["one", "two"], ["three", "four"]]
`, `,
root: &rootNode{}, root: &rootNode{
children: []node{
&assignmentNode{"nested", list{list{"one", "two"}, list{"three", "four"}}},
},
},
}, },
{ {
source: ` source: `
@ -72,28 +92,46 @@ var parseTests = []parseTest{
["three", "four"], ["three", "four"],
] ]
`, `,
root: &rootNode{}, root: &rootNode{
children: []node{
&assignmentNode{"nested", list{list{"one", "two"}, list{"three", "four"}}},
},
},
}, },
{ {
source: ` source: `
admin = {first_name: "jordan", last_name: "orelli"} admin = {first_name: "jordan", last_name: "orelli"}
`, `,
root: &rootNode{}, root: &rootNode{
children: []node{
&assignmentNode{"admin", object{
"first_name": "jordan",
"last_name": "orelli",
}},
},
},
}, },
{ {
source: ` source: `
http = { http = {
port: "9000", port: 9000,
routes: "/path/to/some/file", routes: "/path/to/some/file",
} }
`, `,
root: &rootNode{}, root: &rootNode{
children: []node{
&assignmentNode{"http", object{
"port": 9000,
"routes": "/path/to/some/file",
}},
},
},
}, },
} }
type parseTest struct { type parseTest struct {
source string source string
root *rootNode root node
} }
func (p *parseTest) run(t *testing.T) { func (p *parseTest) run(t *testing.T) {
@ -103,10 +141,11 @@ func (p *parseTest) run(t *testing.T) {
t.Errorf("parse error: %v", err) t.Errorf("parse error: %v", err)
return return
} }
if n.Type() != n_root { if !reflect.DeepEqual(p.root, n) {
t.Errorf("we expected a root node object, but instead we got: %s", n.Type()) t.Errorf("trees are not equal. expected:\n%v\nsaw:\n%v", p.root, n)
} else {
t.Logf("OK trees are equal: %v = %v", p.root, n)
} }
t.Logf("output: %v", n)
} }
func TestParse(t *testing.T) { func TestParse(t *testing.T) {

Loading…
Cancel
Save