Skip to content

Commit f9af04d

Browse files
committed
Add new features and tests
- negative numbers - parse floating point numbers - strings - more native functions (including car, cdr, and cons (but not a real cons)) - lots more tests (83% coverage)
1 parent cf648a7 commit f9af04d

File tree

3 files changed

+363
-105
lines changed

3 files changed

+363
-105
lines changed

lispy.go

Lines changed: 144 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bufio"
55
"errors"
66
"fmt"
7+
"io"
78
"log"
89
"math"
910
"os"
@@ -12,12 +13,17 @@ import (
1213
"strings"
1314
)
1415

15-
type Token int
16-
type Number float64
17-
type Atom string
16+
// Item is an Atom, Number, String, or Function
1817
type Item interface{}
18+
19+
// ItemList is a fundamental data type.
1920
type ItemList []Item
2021

22+
type Number float64
23+
type Atom string
24+
type String string
25+
type NativeFunc func(...Item) (Item, error)
26+
2127
var ErrorEOF = errors.New("End of File")
2228

2329
func (items ItemList) String() string {
@@ -32,17 +38,23 @@ func (items ItemList) String() string {
3238
return s
3339
}
3440

41+
func (s String) String() string {
42+
return fmt.Sprintf("\"%s\"", string(s))
43+
}
44+
3545
func read(s *Scanner) (Item, error) {
3646
tok, lit := s.Scan()
3747
if tok == WS {
3848
tok, lit = s.Scan()
3949
}
40-
// fmt.Println("scan:", tok, lit)
50+
//log.Println("scan:", tok, lit)
4151
switch tok {
4252
case LEFT_PAREN:
4353
return readList(s)
4454
case ATOM:
4555
return Atom(lit), nil
56+
case QUOTE:
57+
return readQuote(s)
4658
case NUMBER:
4759
v, err := strconv.ParseFloat(lit, 64)
4860
if err != nil {
@@ -53,10 +65,21 @@ func read(s *Scanner) (Item, error) {
5365
return nil, nil
5466
case EOF:
5567
return nil, ErrorEOF
68+
case STRING:
69+
return String(lit), nil
5670
}
5771
return nil, errors.New("Malformed input")
5872
}
5973

74+
func readQuote(s *Scanner) (Item, error) {
75+
l := ItemList{Atom("quote")}
76+
c, err := read(s)
77+
if err != nil {
78+
return nil, fmt.Errorf("Failed to complete list: %v\n", err)
79+
}
80+
return append(l, c), nil
81+
}
82+
6083
func readList(s *Scanner) (Item, error) {
6184
var l ItemList
6285
for {
@@ -110,6 +133,8 @@ func eval(expr Item, env *Env) (interface{}, error) {
110133
}
111134
case Number:
112135
return e, nil
136+
case String:
137+
return e, nil
113138
case ItemList:
114139
switch car, _ := e[0].(Atom); car {
115140
case "quote":
@@ -147,14 +172,12 @@ func eval(expr Item, env *Env) (interface{}, error) {
147172
default:
148173
proc, err := eval(e[0], env)
149174
if err != nil {
150-
log.Println("Error1", err)
151-
return nil, err
175+
return nil, fmt.Errorf("undefined-function: %v", err)
152176
}
153177
args := make(ItemList, len(e)-1)
154178
for i, a := range e[1:] {
155179
args[i], err = eval(a, env)
156180
if err != nil {
157-
log.Println("Error2", err)
158181
return nil, err
159182
}
160183
}
@@ -185,7 +208,7 @@ func evalLambda(expr ItemList, env *Env) (interface{}, error) {
185208
case Atom:
186209
l.params = append(l.params, p)
187210
case []interface{}:
188-
log.Fatal("combo param not supported:", x)
211+
return nil, fmt.Errorf("combo param not supported: %v", x)
189212
}
190213

191214
}
@@ -195,35 +218,39 @@ func evalLambda(expr ItemList, env *Env) (interface{}, error) {
195218
return &l, nil
196219

197220
}
221+
198222
func apply(proc Item, args ItemList, env *Env) (Item, error) {
199223
// log.Printf("apply: %v args: %v\n", proc, args)
200224
switch f := proc.(type) {
201-
case func(...Item) Item:
202-
return f(args...), nil
225+
case NativeFunc:
226+
return f(args...)
203227
case *Lambda:
204228
if len(f.params) != len(args) {
205-
log.Fatalf("parameter mismatch %v != %v", f.params, args)
229+
return nil, fmt.Errorf("parameter mismatch %v != %v", f.params, args)
206230
}
207231
for i, p := range f.params {
208232
f.envt.vars[p] = args[i]
209233
}
210234
return eval(f.body, f.envt)
211235
default:
212-
log.Fatalf("apply to a non function: %#v", proc)
236+
return nil, fmt.Errorf("apply to a non function: %#v", proc)
213237
}
214238
return nil, nil
215239
}
216240

217-
func repl(in string, env *Env) (interface{}, error) {
218-
s := NewScanner(strings.NewReader(in))
241+
func replReader(in io.Reader, env *Env) (interface{}, error) {
242+
s := NewScanner(in)
219243
var result interface{}
220244
for {
221245
var err error
222246
expr, err := read(s)
223247
if err != nil {
248+
if err == ErrorEOF {
249+
return result, nil
250+
}
224251
return result, err
225252
}
226-
//fmt.Println(expr)
253+
// fmt.Println(expr)
227254

228255
result, err = eval(expr, env)
229256
if err != nil {
@@ -232,38 +259,69 @@ func repl(in string, env *Env) (interface{}, error) {
232259
}
233260
}
234261

262+
func repl(in string, env *Env) (interface{}, error) {
263+
return replReader(strings.NewReader(in), env)
264+
}
265+
235266
func replCLI(env *Env) {
236-
reader := (bufio.NewReader(os.Stdin))
267+
reader := bufio.NewReader(os.Stdin)
237268
for {
238-
fmt.Print(">>> ")
269+
fmt.Print("* ")
239270
text, _ := reader.ReadString('\n')
240271
result, err := repl(text, env)
241272
if err != nil && err != ErrorEOF {
242-
fmt.Println("New Error:", err)
273+
fmt.Println("Error:", err)
274+
} else {
275+
fmt.Println(result)
243276
}
244-
fmt.Println("===>", result)
245277
}
246278
}
247279

248-
var defaultEnv *Env
249-
250-
func ApplyNumeric(f func(Number, Number) Number) func(a ...Item) Item {
251-
return func(a ...Item) Item {
280+
func ApplyNumeric(f func(Number, Number) Number) NativeFunc {
281+
return func(a ...Item) (Item, error) {
252282
v, ok := a[0].(Number)
253283
if !ok {
254-
log.Fatalf("Not a number: %v", a[0])
284+
return nil, fmt.Errorf("Not a number: %v", a[0])
255285
}
256286
for _, n := range a[1:] {
257287
i, ok := n.(Number)
258288
if !ok {
259-
log.Fatalf("Not a number: %v", a[0])
289+
return nil, fmt.Errorf("Not a number: %v", n)
260290
}
261291
v = f(v, i)
262292
}
263-
return v
293+
return v, nil
294+
}
295+
}
296+
297+
func ApplyNumericBool(f func(Number, Number) bool) NativeFunc {
298+
return func(a ...Item) (Item, error) {
299+
var ret bool
300+
v, ok := a[0].(Number)
301+
if !ok {
302+
return nil, fmt.Errorf("Not a number: %v", a[0])
303+
}
304+
for _, n := range a[1:] {
305+
i, ok := n.(Number)
306+
if !ok {
307+
return nil, fmt.Errorf("Not a number: %v", n)
308+
}
309+
ret = f(v, i)
310+
v = i
311+
if !ret {
312+
return ret, nil
313+
}
314+
}
315+
return Item(ret), nil
264316
}
265317
}
266318

319+
func isItemList(i Item) bool {
320+
if _, ok := i.(ItemList); ok {
321+
return true
322+
}
323+
return false
324+
}
267325
func DefaultEnv() *Env {
268326
return &Env{
269327
Binding{
@@ -279,40 +337,71 @@ func DefaultEnv() *Env {
279337
"-": ApplyNumeric(func(x, y Number) Number {
280338
return x - y
281339
}),
282-
"<=": func(a ...Item) Item {
283-
return a[0].(Number) <= a[1].(Number)
284-
},
285-
"equal?": func(a ...Item) Item {
286-
return reflect.DeepEqual(a[0], a[1])
287-
},
340+
"<": ApplyNumericBool(func(x, y Number) bool {
341+
return x < y
342+
}),
343+
"<=": ApplyNumericBool(func(x, y Number) bool {
344+
return x <= y
345+
}),
346+
">": ApplyNumericBool(func(x, y Number) bool {
347+
return x > y
348+
}),
349+
">=": ApplyNumericBool(func(x, y Number) bool {
350+
return x >= y
351+
}),
352+
"=": ApplyNumericBool(func(x, y Number) bool {
353+
return x == y
354+
}),
355+
"number?": NativeFunc(func(a ...Item) (Item, error) {
356+
if len(a) > 1 {
357+
return nil, fmt.Errorf("Too many arguments for number?: %v", a)
358+
}
359+
if _, ok := a[0].(Number); ok {
360+
return Item(true), nil
361+
}
362+
return Item(false), nil
363+
}),
364+
"car": NativeFunc(func(a ...Item) (Item, error) {
365+
if !isItemList(a[0]) {
366+
return nil, fmt.Errorf("Not a list: %#v", a[0])
367+
}
368+
il := a[0].(ItemList)
369+
if len(il) == 0 {
370+
return []ItemList{}, nil
371+
}
372+
return a[0].(ItemList)[0], nil
373+
}),
374+
"cdr": NativeFunc(func(a ...Item) (Item, error) {
375+
if !isItemList(a[0]) {
376+
return nil, fmt.Errorf("Not a list: %#v", a[0])
377+
}
378+
il := a[0].(ItemList)
379+
if len(il) < 2 {
380+
return []ItemList{}, nil
381+
}
382+
return il[1:], nil
383+
}),
384+
"cons": NativeFunc(func(a ...Item) (Item, error) {
385+
if len(a) != 2 {
386+
return nil, fmt.Errorf("wrong number of arguments given to cons")
387+
}
388+
l := ItemList{a[0]}
389+
if il, ok := a[1].(ItemList); ok {
390+
l = append(l, il...)
391+
} else {
392+
l = append(l, a[1])
393+
}
394+
return l, nil
395+
}),
396+
"equal?": NativeFunc(func(a ...Item) (Item, error) {
397+
return reflect.DeepEqual(a[0], a[1]), nil
398+
}),
288399
"pi": Number(math.Pi),
289400
},
290401
nil,
291402
}
292403
}
293-
func init() {
294-
defaultEnv = DefaultEnv()
295-
}
296404

297405
func main() {
298-
env := defaultEnv
299-
/*
300-
repl("(define r 10)\n(define n 12)", env)
301-
repl("(begin (define r 10)\n(define n 12))", env)
302-
repl("(define radius (* pi (* r r)))", env)
303-
repl("(if (<= 4 2) (* 10 2))", env)
304-
repl("(if (<= 4 2) (* 10 2) (+ 1 2))", env)
305-
repl("(/ radius 10)", env)
306-
repl("(quote (1 1))", env)
307-
repl("123", env)
308-
repl("(lambda () (+ 1 1))", env)
309-
repl("((lambda () (+ 1 1)))", env)
310-
repl("((lambda () (+ 1 1)))", env)
311-
repl("(define foo (begin (define count 0) (lambda () (set! count (+ count 1)))))", env)
312-
repl("(foo) (foo)", env)
313-
repl("(foo) (foo)", env)
314-
repl("(define plus (lambda (a b) (+ a b)))", env)
315-
repl("(define counter (lambda (n) (lambda () (set! n (+ n 1)))))", env)
316-
*/
317-
replCLI(env)
406+
replCLI(DefaultEnv())
318407
}

0 commit comments

Comments
 (0)