Skip to content

Commit de0c835

Browse files
committed
rewrite lexer
Inspired by Rob Pike's talk http://cuddle.googlecode.com/hg/talk/lex.html
1 parent 771c8de commit de0c835

File tree

3 files changed

+293
-54
lines changed

3 files changed

+293
-54
lines changed

lispy.go

Lines changed: 51 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,23 @@ type Atom string
2424
type String string
2525
type InternalFunc func(...Item) (Item, error)
2626

27-
var Nil = Atom("NIL")
27+
type Tokenizer interface {
28+
NextItem() *TokenItem
29+
}
30+
31+
var T, Nil *Symbol
32+
33+
func (a Atom) ToUpper() Atom {
34+
return Atom(strings.ToUpper(string(a)))
35+
}
36+
37+
func init() {
38+
Nil = internSymbol(Atom("NIL"))
39+
Nil.Bind("NIL")
40+
41+
T = internSymbol(Atom("t"))
42+
T.Bind("T")
43+
}
2844

2945
var ErrorEOF = errors.New("End of File")
3046

@@ -41,51 +57,53 @@ func (items ItemList) String() string {
4157
}
4258

4359
func (s String) String() string {
44-
return fmt.Sprintf("\"%s\"", string(s))
60+
return fmt.Sprintf(`"%s"`, string(s))
4561
}
4662

47-
func read(s *Scanner) (Item, error) {
48-
tok, lit := s.Scan()
49-
if tok == WS {
50-
tok, lit = s.Scan()
63+
func read(l Tokenizer) (Item, error) {
64+
t := l.NextItem()
65+
if t.token == WS {
66+
t = l.NextItem()
5167
}
52-
//log.Println("scan:", tok, lit)
53-
switch tok {
68+
//log.Printf("scan: %v\n", t)
69+
switch t.token {
5470
case LEFT_PAREN:
55-
return readList(s)
71+
return readList(l)
72+
case RIGHT_PAREN:
73+
return nil, nil
5674
case ATOM:
57-
return Atom(lit), nil
75+
return Atom(t.lit), nil
5876
case QUOTE:
59-
return readQuote(s)
77+
return readQuote(l)
6078
case NUMBER:
61-
v, err := strconv.ParseFloat(lit, 64)
79+
v, err := strconv.ParseFloat(t.lit, 64)
6280
if err != nil {
63-
log.Println("Number fail:", err)
81+
log.Fatal("Number fail:", err)
6482
}
6583
return Number(v), nil
66-
case RIGHT_PAREN:
67-
return nil, nil
6884
case EOF:
6985
return nil, ErrorEOF
7086
case STRING:
71-
return String(lit), nil
87+
return String(t.lit), nil
88+
case ILLEGAL:
89+
return nil, errors.New(t.lit)
7290
}
7391
return nil, errors.New("Malformed input")
7492
}
7593

76-
func readQuote(s *Scanner) (Item, error) {
94+
func readQuote(lex Tokenizer) (Item, error) {
7795
l := ItemList{Atom("quote")}
78-
c, err := read(s)
96+
c, err := read(lex)
7997
if err != nil {
8098
return nil, fmt.Errorf("Failed to complete list: %v\n", err)
8199
}
82100
return append(l, c), nil
83101
}
84102

85-
func readList(s *Scanner) (Item, error) {
103+
func readList(lex Tokenizer) (Item, error) {
86104
var l ItemList
87105
for {
88-
c, err := read(s)
106+
c, err := read(lex)
89107
if err != nil {
90108
return nil, fmt.Errorf("Failed to complete list: %v\n", err)
91109
}
@@ -124,7 +142,7 @@ func (env *Env) Find(a Atom) *Env {
124142
}
125143

126144
func eval(expr Item, env *Env) (interface{}, error) {
127-
// fmt.Println(expr)
145+
// log.Println(expr)
128146
switch e := expr.(type) {
129147
case Atom:
130148
v, ok := env.Find(e).vars[e]
@@ -136,6 +154,9 @@ func eval(expr Item, env *Env) (interface{}, error) {
136154
case Number, String:
137155
return e, nil
138156
case ItemList:
157+
if len(e) == 0 {
158+
return Nil, nil
159+
}
139160
switch car, _ := e[0].(Atom); car {
140161
case "quote":
141162
return e[1], nil
@@ -238,18 +259,21 @@ func apply(proc Item, args ItemList, env *Env) (Item, error) {
238259
}
239260

240261
func replReader(in io.Reader, env *Env) (interface{}, error) {
241-
s := NewScanner(in)
262+
// l := NewScanner(in)
263+
buf := make([]byte, 1024)
264+
n, _ := in.Read(buf)
265+
l := NewLexer("lispy", string(buf[:n]))
242266
var result interface{}
243267
for {
244268
var err error
245-
expr, err := read(s)
269+
expr, err := read(l)
270+
//log.Println(expr, err)
246271
if err != nil {
247272
if err == ErrorEOF {
248273
return result, nil
249274
}
250275
return result, err
251276
}
252-
// fmt.Println(expr)
253277

254278
result, err = eval(expr, env)
255279
if err != nil {
@@ -265,7 +289,7 @@ func repl(in string, env *Env) (interface{}, error) {
265289
func replCLI(env *Env) {
266290
reader := bufio.NewReader(os.Stdin)
267291
for {
268-
fmt.Print("* ")
292+
fmt.Print("-> ")
269293
text, _ := reader.ReadString('\n')
270294
result, err := repl(text, env)
271295
if err != nil && err != ErrorEOF {
@@ -320,7 +344,7 @@ func isTrue(i Item) bool {
320344
return b
321345
}
322346
if a, ok := i.(Atom); ok {
323-
if a == Nil {
347+
if internSymbol(a) == Nil {
324348
return false
325349
}
326350
return true
@@ -425,5 +449,6 @@ func DefaultEnv() *Env {
425449
}
426450

427451
func main() {
452+
log.SetFlags(log.LstdFlags | log.Lshortfile)
428453
replCLI(DefaultEnv())
429454
}

repl_test.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,25 @@ import (
99
)
1010

1111
func TestRepl(t *testing.T) {
12+
1213
Convey("repl basic testing", t, func() {
14+
env := DefaultEnv()
15+
val, err := repl("", env)
16+
So(err, ShouldBeNil)
17+
So(val, ShouldBeNil)
18+
19+
val, err = repl("()", env)
20+
So(err, ShouldBeNil)
21+
So(fmt.Sprintf("%v", val), ShouldEqual, "NIL")
22+
23+
val, err = repl("123", env)
24+
So(err, ShouldBeNil)
25+
So(val, ShouldEqual, 123)
26+
27+
return
28+
})
29+
30+
Convey("testing", t, func() {
1331
env := DefaultEnv()
1432
Convey("Use unset variable n", func() {
1533
val, err := repl("(+ n n)", env)
@@ -35,7 +53,7 @@ func TestRepl(t *testing.T) {
3553
Convey("If statements", func() {
3654
val, err := repl("(if (<= 4 2) (* 10 2))", env)
3755
So(err, ShouldBeNil)
38-
So(val, ShouldEqual, "NIL")
56+
So(fmt.Sprintf("%v", val), ShouldEqual, "NIL")
3957

4058
val, err = repl("(if (< 4 2) (* 10 2) (+ 1 2))", env)
4159
So(err, ShouldBeNil)

0 commit comments

Comments
 (0)