From fb0fc0e8af8375a5e99a064910c43a8f23f2b18e Mon Sep 17 00:00:00 2001 From: Robert Read Date: Mon, 7 Sep 2015 19:08:28 -0700 Subject: [PATCH] Major refactorings - New data type Null with single value Empty - Don't use nils in cons lists anymore (should die if nil is discovered) - Parse dotted pair syntax '(a . b) - reimplement cons, car, cdr with single return value and return error as a Data. Experiemental to see if it makes sense to have an Error type or stick with multi-value returns from eval. - Ignore ;; comments - implement let --- eval.go | 356 +++++++++++++++++++++++++++++++++------------------ main.go | 52 ++++++++ pair.go | 142 ++++++++++---------- repl_test.go | 34 ++++- scanner.go | 24 ++++ 5 files changed, 413 insertions(+), 195 deletions(-) diff --git a/eval.go b/eval.go index c1aeb1e..8fbc093 100644 --- a/eval.go +++ b/eval.go @@ -1,7 +1,6 @@ package main import ( - "bufio" "errors" "fmt" "io" @@ -16,14 +15,15 @@ import ( // Data is a fundamental type (symbol, boolean, string, number, pair, func) type Data interface { - String() string + // temporarily removed this as error handling is integrated into Data + // String() string } type Symbol string type Boolean bool type Number float64 type String string -type InternalFunc func(*Pair) (Data, error) +type InternalFunc func(Data) (Data, error) func (sym Symbol) String() string { return string(sym) @@ -34,7 +34,7 @@ func (num Number) String() string { } func (fun InternalFunc) String() string { - return fmt.Sprintf("native-func '%v'", ((func(*Pair) (Data, error))(fun))) + return fmt.Sprintf("native-func '%v'", ((func(Data) (Data, error))(fun))) } func (b Boolean) String() string { @@ -49,6 +49,21 @@ func (s String) String() string { return fmt.Sprintf(`"%s"`, string(s)) } +type Null byte + +var Empty Null = 0xfe // Null is nothing so it can be anything + +func (n Null) String() string { + return "()" +} + +func nullp(d Data) Boolean { + if v, ok := d.(Null); ok { + return v == Empty + } + return false +} + type Tokenizer interface { NextItem() *TokenItem } @@ -56,13 +71,15 @@ type Tokenizer interface { var ( T = Boolean(true) False = Boolean(false) - Nil = (*Pair)(nil) ) var ErrorEOF = errors.New("End of File") func read(l Tokenizer) (Data, error) { t := l.NextItem() + if t == nil { + return nil, ErrorEOF + } if t.token == WS { t = l.NextItem() } @@ -90,6 +107,8 @@ func read(l Tokenizer) (Data, error) { return T, nil case FALSE: return False, nil + case DOT: + return _dot, nil case ILLEGAL: return nil, errors.New(t.lit) } @@ -101,40 +120,58 @@ func readQuote(lex Tokenizer) (Data, error) { if err != nil { return nil, fmt.Errorf("Failed to complete list: %v\n", err) } - return cons(internSymbol("quote"), cons(c, Nil)), nil + return cons(internSymbol("quote"), cons(c, Empty)), nil } -func readList(lex Tokenizer) (Data, error) { - var l []Data - for { - c, err := read(lex) +func readList2(lex Tokenizer) (Data, error) { + c, err := read(lex) + if err != nil { + return nil, fmt.Errorf("Failed to complete list: %v\n", err) + } + if c == nil { + return Empty, nil + } + + // handle (a b . c) but (a b . c d) is an error. + if c == _dot { + last, err := read(lex) if err != nil { - return nil, fmt.Errorf("Failed to complete list: %v\n", err) + return nil, err } - if c == nil { - break + end, _ := read(lex) + if end != nil { + return nil, fmt.Errorf("More than one object follows .") } - l = append(l, c) + + log.Printf("last %v", last) + return last, nil + } + + rest, err := readList2(lex) + if err != nil { + return nil, err } - return list2cons(l), nil + return cons(c, rest), nil } -func list2cons(l []Data) *Pair { - p := Nil - for i := len(l) - 1; i >= 0; i-- { - p = cons(l[i], p) +func readList(lex Tokenizer) (Data, error) { + li, err := readList2(lex) + if err != nil { + return nil, err } - return p + return li, nil } var ( _quote = internSymbol("quote") _define = internSymbol("define") + _dot = internSymbol("::dot::") _set = internSymbol("set!") _if = internSymbol("if") _begin = internSymbol("begin") _quit = internSymbol("quit") _lambda = internSymbol("lambda") + _let = internSymbol("let") _vars = internSymbol(":vars") _ok = internSymbol("ok") ) @@ -142,16 +179,17 @@ var ( func eval(expr Data, env *Env) (Data, error) { log.Printf("eval: %T: %v\n", expr, expr) switch e := expr.(type) { + case Boolean: + return e, nil case Symbol: return env.FindVar(e) case Number: return e, nil case String: return e, nil + case Null: + return e, nil case *Pair: - if e == nil { - return Nil, nil - } c, _ := getSymbol(car(e)) /* non-Symbols fall through to default */ switch c { @@ -169,7 +207,10 @@ func eval(expr Data, env *Env) (Data, error) { // Return value of define is undefined return _ok, nil case _set: - d := cadr(e).(Symbol) + d, err := getSymbol(cadr(e)) + if err != nil { + return nil, err + } val, err := eval(caddr(e), env) if err != nil { return nil, err @@ -183,35 +224,23 @@ func eval(expr Data, env *Env) (Data, error) { } else if listLen(e) > 3 { return eval(cadddr(e), env) } - return Nil, nil - case _begin: - var v Data - e, err := getPair(cdr(e)) + return Empty, nil + case _let: + letExpr, err := let(cdr(e), env) if err != nil { return nil, err } - for e != Nil { - log.Printf("begin: %v", e) - v, err = eval(car(e), env) - if err != nil { - return nil, err - } - e, err = listNext(e) - if err != nil { - return nil, err - } - - } - return v, nil - + return eval(letExpr, env) + case _begin: + return evalSequential(cdr(e), env) case _quit: os.Exit(0) case _lambda: - params, err := getPair(cadr(e)) + params, err := getList(cadr(e)) if err != nil { return nil, fmt.Errorf("bad params: %v", err) } - body, err := getPair(cddr(e)) + body, err := getList(cddr(e)) if err != nil { return nil, fmt.Errorf("bad body: %v", err) } @@ -234,12 +263,36 @@ func eval(expr Data, env *Env) (Data, error) { return apply(proc, args, env) } case nil: - log.Errorln("parsed a nil?") - return Nil, nil + log.Fatal("parsed a nil?") + return nil, nil } return nil, fmt.Errorf("Unparsable expression: %v", expr) } +func evalSequential(d Data, env *Env) (Data, error) { + var v Data + e, err := getPair(d) + if err != nil { + return nil, err + } + for !nullp(e) { + log.Printf("begin: %v", e) + v, err = eval(car(e), env) + if err != nil { + return nil, err + } + if nullp(cdr(e)) { + break + } + e, err = listNext(e) + if err != nil { + return nil, err + } + + } + return v, nil + +} func definition(defn *Pair, env *Env) error { var value Data var name Symbol @@ -278,7 +331,7 @@ func definition(defn *Pair, env *Env) error { type Lambda struct { params []Symbol - body []Data + body Data envt *Env } @@ -286,60 +339,105 @@ func (l *Lambda) String() string { return fmt.Sprintf("", l, l.body) } -func evalLambda(params *Pair, body *Pair, env *Env) (Data, error) { +func evalLambda(params Data, body Data, env *Env) (Data, error) { l := Lambda{} - for params != nil { + for params != Empty { var err error - switch p := car(params).(type) { + p, err := getPair(params) + if err != nil { + return nil, fmt.Errorf("not a pair here %v", params) + } + switch arg := car(p).(type) { case Symbol: - l.params = append(l.params, p) + l.params = append(l.params, arg) case *Pair: - return nil, fmt.Errorf("combo param not supported: %v", p) + return nil, fmt.Errorf("combo param not supported: %v", arg) } - d := cdr(params) - if d == nil { - break - } - if params, err = getPair(d); err != nil { - return nil, fmt.Errorf("not a pair here %v", d) - } - - } - for { - if body == nil { - break - } - l.body = append(l.body, car(body)) - body, _ = listNext(body) + params = cdr(p) } + l.body = body l.envt = NewEnv(env) return &l, nil } -func evalArgs(next Data, env *Env) (*Pair, error) { +func evalArgs(next Data, env *Env) (Data, error) { + if nullp(next) { + return Empty, nil + } e, err := getPair(next) if err != nil { return nil, err } - if e == nil { - return Nil, nil - } - val, err := eval(car(e), env) if err != nil { return nil, err } - rest, err := evalArgs(cdr(e), env) + if nullp(cdr(e)) { + return cons(val, Empty), nil + } else { + rest, err := evalArgs(cdr(e), env) + if err != nil { + return nil, err + } + return cons(val, rest), nil + } +} + +func getError(d Data) error { + v, ok := d.(error) + if ok { + return v + } + return nil +} + +func _map(f func(Data) Data, d Data) Data { + if nullp(d) { + return Empty + } + lst, err := getPair(d) if err != nil { + return err + } + if nullp(lst) { + return Empty + } + e := f(car(lst)) + if err := getError(e); err != nil { + return err + } + rest := cdr(lst) + if err := getError(rest); err != nil { + return err + } + return cons(e, _map(f, rest)) +} + +func let(expr Data, env *Env) (Data, error) { + arguments := _map(car, car(expr)) + if err := getError(arguments); err != nil { + return nil, err + } + values := _map(cadr, car(expr)) + if err := getError(values); err != nil { return nil, err } - return cons(val, rest), nil + + body := cdr(expr) + if err := getError(body); err != nil { + return nil, err + } + + result := cons(cons(_lambda, cons(arguments, body)), values) + log.Printf("lambda %v", result) + + return eval(result, env) } -func apply(proc Data, args *Pair, env *Env) (Data, error) { +func apply(proc Data, args Data, env *Env) (Data, error) { // log.Printf("apply: %v args: %v\n", proc, args) switch f := proc.(type) { case InternalFunc: @@ -348,23 +446,22 @@ func apply(proc Data, args *Pair, env *Env) (Data, error) { if len(f.params) != listLen(args) { return nil, fmt.Errorf("parameter mismatch %v != %v", f.params, args) } - for _, p := range f.params { + for _, name := range f.params { var err error - f.envt.Bind(p, car(args)) - args, err = listNext(args) + p, err := getPair(args) if err != nil { return nil, err } - } - var result Data - var err error - for _, expr := range f.body { - result, err = eval(expr, f.envt) - if err != nil { + f.envt.Bind(name, car(p)) + if nullp(cdr(p)) { + break + } + args = cdr(args) + if err, ok := args.(error); ok { return nil, err } } - return result, nil + return evalSequential(f.body, f.envt) default: return nil, fmt.Errorf("apply to a non function: %#v %v", proc, args) } @@ -398,32 +495,24 @@ func repl(in string, env *Env) (Data, error) { return replReader(strings.NewReader(in), env) } -func replCLI(env *Env) { - reader := bufio.NewReader(os.Stdin) - for { - fmt.Print("-> ") - text, err := reader.ReadString('\n') - if err != nil { - fmt.Println("\nbye!") - os.Exit(0) +func ApplyNumeric(f func(Number, Number) Number) InternalFunc { + return func(d Data) (Data, error) { + if nullp(d) { + return 0, nil } - result, err := repl(text, env) - if err != nil && err != ErrorEOF { - fmt.Println("Error:", err) - } else { - fmt.Println(result) + a, err := getPair(d) + if err != nil { + return nil, err } - } -} - -func ApplyNumeric(f func(Number, Number) Number) InternalFunc { - return func(a *Pair) (Data, error) { v, ok := car(a).(Number) if !ok { return nil, fmt.Errorf("Not a number: %v", car(a)) } for { var err error + if nullp(cdr(a)) { + break + } a, err = listNext(a) if err != nil { return nil, err @@ -442,14 +531,24 @@ func ApplyNumeric(f func(Number, Number) Number) InternalFunc { } func ApplyNumericBool(f func(Number, Number) Boolean) InternalFunc { - return func(a *Pair) (Data, error) { + return func(d Data) (Data, error) { var ret Boolean + if nullp(d) { + return 0, nil + } + a, err := getPair(d) + if err != nil { + return nil, err + } v, ok := car(a).(Number) if !ok { return nil, fmt.Errorf("Not a number: %v", car(a)) } for { var err error + if nullp(cdr(a)) { + break + } a, err = listNext(a) if err != nil { return nil, err @@ -472,7 +571,7 @@ func ApplyNumericBool(f func(Number, Number) Boolean) InternalFunc { } func Apply1(f func(Data) (Data, error)) InternalFunc { - return func(args *Pair) (Data, error) { + return func(args Data) (Data, error) { if listLen(args) != 1 { return nil, fmt.Errorf("Expected 1 arguments, received %d", listLen(args)) } @@ -481,30 +580,35 @@ func Apply1(f func(Data) (Data, error)) InternalFunc { } func Apply2(f func(Data, Data) (Data, error)) InternalFunc { - return func(args *Pair) (Data, error) { + return func(args Data) (Data, error) { if listLen(args) != 2 { return nil, fmt.Errorf("Expected 2 arguments, received %d", listLen(args)) } - return f(car(args), cadr(args)) + + a, err := getPair(args) + if err != nil { + return nil, err + } + return f(car(a), cadr(a)) } } func isTrue(i Data) Boolean { + log.Printf("isTrue %T %v", i, i) if b, ok := i.(Boolean); ok { return b } - if a, ok := i.(Symbol); ok { - if a == internSymbol("nil") { - return false - } + if _, ok := i.(Symbol); ok { return true } if n, ok := i.(Number); ok { return !(n == 0) } - if il, ok := i.(*Pair); ok { - /* FIXME: empty list if false, like Lisp */ - return listLen(il) > 0 + if _, ok := i.(*Pair); ok { + return true + } + if _, ok := i.(Null); ok { + return true } if _, ok := i.(String); ok { return true @@ -548,20 +652,29 @@ func DefaultEnv() *Env { env.BindName("=", ApplyNumericBool(func(x, y Number) Boolean { return x == y })) - env.BindName("number?", InternalFunc(func(a *Pair) (Data, error) { - if listLen(a) > 1 { - return nil, fmt.Errorf("Too many arguments for number?: %v", a) + env.BindName("number?", InternalFunc(func(args Data) (Data, error) { + if listLen(args) > 1 { + return nil, fmt.Errorf("Too many arguments for number?: %v", args) + } + a, err := getPair(args) + if err != nil { + return nil, err } if _, ok := car(a).(Number); ok { return T, nil } return False, nil })) - env.BindName("equal?", InternalFunc(func(args *Pair) (Data, error) { + env.BindName("equal?", InternalFunc(func(args Data) (Data, error) { if listLen(args) != 2 { return nil, fmt.Errorf("wrong number of arguments given to equal?") } - return Boolean(reflect.DeepEqual(car(args), cadr(args))), nil + a, err := getPair(args) + if err != nil { + return nil, err + } + log.Printf("%v", a) + return Boolean(reflect.DeepEqual(car(a), cadr(a))), nil })) env.BindName("pi", Number(math.Pi)) env.BindName("cons", Apply2(_cons)) @@ -569,7 +682,6 @@ func DefaultEnv() *Env { env.BindName("cdr", Apply1(_cdr)) env.BindName("null?", Apply1(_nullp)) env.BindName("pair?", Apply1(_pairp)) - env.Bind(internSymbol("nil"), nil) return env } @@ -580,7 +692,7 @@ func _cons(a Data, b Data) (Data, error) { func _car(a Data) (Data, error) { p, err := getPair(a) if err != nil { - return Nil, fmt.Errorf("car received: %v", err) + return nil, fmt.Errorf("car received: %v", err) } return car(p), nil } @@ -588,7 +700,7 @@ func _car(a Data) (Data, error) { func _cdr(a Data) (Data, error) { p, err := getPair(a) if err != nil { - return Nil, fmt.Errorf("cdr received: %v", err) + return nil, fmt.Errorf("cdr received: %v", err) } return cdr(p), nil } diff --git a/main.go b/main.go index 62fe607..c3e2111 100644 --- a/main.go +++ b/main.go @@ -1,12 +1,64 @@ package main import ( + "bytes" "flag" + "fmt" + "io" "io/ioutil" + "unicode" + "github.com/bobappleyard/readline" "github.com/rread/rsi/log" ) +func validSexp(s string) bool { + var parens int + var notEmpty bool + for _, c := range s { + switch c { + case '(': + parens++ + case ')': + parens-- + } + if notEmpty || !unicode.IsSpace(c) { + notEmpty = true + } + } + return notEmpty && parens == 0 +} + +func replCLI(env *Env) { + defer fmt.Println("\nbye!") + counter := readline.HistorySize() + for { + buf := bytes.Buffer{} + prompt := fmt.Sprintf("[%d]-> ", counter) + for { + l, err := readline.String(prompt) + if err == io.EOF { + return + } + buf.WriteString(l) + if validSexp(buf.String()) { + break + } + buf.WriteString("\n") + prompt = ": " + } + result, err := repl(buf.String(), env) + if err != nil && err != ErrorEOF { + fmt.Println("Error:", err) + } else { + fmt.Println(result) + } + readline.AddHistory(buf.String()) + counter++ + + } +} + func main() { debug := flag.Bool("debug", false, "Enable debugging") flag.Parse() diff --git a/pair.go b/pair.go index c0d7934..8689e1d 100644 --- a/pair.go +++ b/pair.go @@ -15,7 +15,7 @@ func (p *Pair) String() string { ret := "(" for { ret += fmt.Sprintf("%v", p.car) - if p.cdr == Nil { + if nullp(p.cdr) { break } if pairp(p.cdr) { @@ -32,114 +32,122 @@ func (p *Pair) String() string { return ret } -func getPair(d Data) (*Pair, error) { - if d == Nil { - return Nil, nil +func getList(d Data) (Data, error) { + if nullp(d) { + return Empty, nil } - if p, ok := d.(*Pair); ok { - return p, nil + switch v := d.(type) { + case *Pair: + return v, nil + case error: + return nil, v + default: + return nil, fmt.Errorf("%v: value is not a pair", d) + } - return Nil, fmt.Errorf("%v: value is not a pair", d) } -func cons(car, cdr Data) *Pair { - return &Pair{car, cdr} -} +func getPair(d Data) (*Pair, error) { + if nullp(d) { + return nil, fmt.Errorf("%v: value is not a pair", d) + } + switch v := d.(type) { + case *Pair: + return v, nil + case error: + return nil, v + default: + return nil, fmt.Errorf("%v: value is not a pair", d) -func car(l *Pair) Data { - if l == nil { - return Nil } - return l.car } -func cdr(l *Pair) Data { - if l == nil { - return Nil +func cons(car, cdr Data) Data { + if getError(car) != nil { + return car } - return l.cdr + if getError(cdr) != nil { + return cdr + } + return &Pair{car, cdr} } -func cddr(l *Pair) Data { - d := cdr(l) - if p, ok := d.(*Pair); ok { - return cdr(p) +func cdr(d Data) Data { + l, err := getPair(d) + if err != nil { + return err } - log.Fatalf("pair expected: %v", l) - return Nil + return l.cdr } -func cadr(l *Pair) Data { - d := cdr(l) - if p, ok := d.(*Pair); ok { - return car(p) +func car(d Data) Data { + l, err := getPair(d) + if err != nil { + return err } - log.Fatalf("pair expected: %v", l) - return Nil + return l.car } -func caddr(l *Pair) Data { - d := cdr(l) - if p, ok := d.(*Pair); ok { - d := cdr(p) - if p, ok := d.(*Pair); ok { - return car(p) - } - } +func cadr(d Data) Data { + return car(cdr(d)) +} - log.Fatalf("pair expected: %v", l) - return Nil +func cddr(d Data) Data { + return cdr(cdr(d)) } -func cadddr(l *Pair) Data { - d := cdr(l) - if p, ok := d.(*Pair); ok { - d := cdr(p) - if p, ok := d.(*Pair); ok { - d := cdr(p) - if p, ok := d.(*Pair); ok { - return car(p) - } - } - } - log.Fatal("pair expected") - return Nil +func caddr(l Data) Data { + return car(cdr(cdr(l))) } -func nullp(p *Pair) Boolean { - return p == Nil +func cadddr(l Data) Data { + return car(cdr(cdr(cdr(l)))) } func pairp(d Data) Boolean { _, ok := d.(*Pair) if !ok { - log.Printf("consp %T %v %v", d, d, ok) + log.Printf("pairp %T %v %v", d, d, ok) } return Boolean(ok) } -func listNext(l *Pair) (*Pair, error) { - d := cdr(l) +func listNext(d Data) (*Pair, error) { + l, err := getPair(d) + if err != nil { + return nil, err + } + d = cdr(l) return getPair(d) } -func listLen(p *Pair) int { - var i int - if p == Nil { - return i +func listLen(d Data) int { + if nullp(d) { + return 0 } + var i int for { i++ - d := cdr(p) - if d == Nil { + d = cdr(d) + if nullp(d) { break } - - var ok bool - if p, ok = d.(*Pair); !ok { + if !pairp(d) { log.Fatal("expecting a list") } } return i } + +func reverse(d Data) Data { + var li Data = Empty + for { + if nullp(d) { + break + } + li = cons(car(d), li) + d = cdr(d) + } + return li +} diff --git a/repl_test.go b/repl_test.go index 8f96722..5744a7c 100644 --- a/repl_test.go +++ b/repl_test.go @@ -26,7 +26,7 @@ func TestRepl(t *testing.T) { val, err = repl(" () ", env) So(err, ShouldBeNil) - So(val, ShouldEqual, Nil) + So(val, ShouldEqual, Empty) val, err = repl("123", env) So(err, ShouldBeNil) @@ -44,6 +44,14 @@ func TestRepl(t *testing.T) { So(err, ShouldBeNil) So(val, ShouldEqual, 456) + val, err = repl(".123 ", env) + So(err, ShouldBeNil) + So(val, ShouldEqual, 0.123) + + val, err = repl("'(a . b)", env) + So(err, ShouldBeNil) + So(S(val), ShouldEqual, "(A . B)") + val, err = repl(`"bad string`, env) So(err.Error(), ShouldContainSubstring, "unterminated string") @@ -87,7 +95,7 @@ func TestRepl(t *testing.T) { Convey("null?", func() { val, err := repl("(null? '())", env) So(err, ShouldBeNil) - So(val, ShouldEqual, T) + So(val, ShouldEqual, False) val, err = repl("(null? 123)", env) So(err, ShouldBeNil) @@ -129,7 +137,7 @@ func TestRepl(t *testing.T) { Convey("If statements", func() { val, err := repl("(if (<= 4 2) (* 10 2))", env) So(err, ShouldBeNil) - So(S(val), ShouldEqual, "") + So(val, ShouldEqual, Empty) val, err = repl("(if (< 4 2) (* 10 2) (+ 1 2))", env) So(err, ShouldBeNil) @@ -149,7 +157,7 @@ func TestRepl(t *testing.T) { val, err = repl("(if '() 'a 'b)", env) So(err, ShouldBeNil) - So(S(val), ShouldEqual, ("B")) + So(S(val), ShouldEqual, ("A")) val, err = repl("(if 0 'a 'b)", env) So(err, ShouldBeNil) @@ -348,7 +356,12 @@ func TestRepl(t *testing.T) { val, err = repl("(cdr (cdr (cdr (cons 1 '(2 3)))))", env) So(err, ShouldBeNil) - So(val, ShouldResemble, Nil) + So(val, ShouldResemble, Empty) + }) + + Convey("Test let statements", func() { + s(env, "(let ((a 1) (b 2)) (+ a b))", Number(3), nil) + s(env, "(let ((a 3) (b 2)) (* a b))", Number(6), nil) }) Convey("factorial 10", func() { @@ -363,13 +376,22 @@ func TestRepl(t *testing.T) { }) } +func s(env *Env, exp string, value Data, err error) { + Convey(fmt.Sprintf("%v => %v", exp, value), func() { + val, err := repl(exp, env) + So(err, ShouldEqual, err) + So(val, ShouldEqual, value) + }) +} + func TestEndofFile(t *testing.T) { env := DefaultEnv() Convey("fail when given imbalanced parens", t, func() { _, err := repl("(define counter (lambda (n) (lambda () (set! n (+ n 1))))", env) if err == nil { t.Fail() + } else { + So(err.Error(), ShouldContainSubstring, "End of File") } - So(err.Error(), ShouldContainSubstring, "End of File") }) } diff --git a/scanner.go b/scanner.go index 4612e9f..c0d8480 100644 --- a/scanner.go +++ b/scanner.go @@ -13,10 +13,12 @@ const ( ILLEGAL Token = iota EOF WS + COMMENT SYMBOL NUMBER LEFT_PAREN RIGHT_PAREN + DOT QUOTE DQUOTE STRING @@ -34,6 +36,8 @@ func (t Token) String() string { return "EOF" case WS: return "WS" + case COMMENT: + return "COMMENT" case SYMBOL: return "SYMBOL" case NUMBER: @@ -42,6 +46,8 @@ func (t Token) String() string { return "LEFT_PAREN" case RIGHT_PAREN: return "RIGHT_PAREN" + case DOT: + return "DOT" case QUOTE: return "QUOTE" case DQUOTE: @@ -196,6 +202,8 @@ func lexBase(l *Lexer) stateFn { return nil case isWhitespace(ch): l.ignore() + case ch == ';': + return lexComment case ch == '(': l.emit(LEFT_PAREN) return lexBase @@ -213,6 +221,8 @@ func lexBase(l *Lexer) stateFn { case ch == '#': l.ignore() return lexHash + case ch == '.': + return lexDot case isNumber(ch): return lexNumber case isSymbol(ch): @@ -233,6 +243,12 @@ func symbolOrNumber(l *Lexer) stateFn { return lexSymbol } +func lexComment(l *Lexer) stateFn { + for l.next() != '\n' { + } + l.ignore() + return lexBase +} func lexNumber(l *Lexer) stateFn { l.acceptRun("0123456789.") l.emit(NUMBER) @@ -251,6 +267,14 @@ func lexHash(l *Lexer) stateFn { return lexBase } +func lexDot(l *Lexer) stateFn { + if isWhitespace(l.peek()) { + l.emit(DOT) + return lexBase + } + return lexNumber +} + func lexSymbol(l *Lexer) stateFn { l.acceptRunFn(isSymbol) l.emit(SYMBOL)