-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLispGo.go
123 lines (102 loc) · 3.21 KB
/
LispGo.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// LispGo is a toy lisp interpreter writtn in Go, in the spirit of http://norvig.com/lispy.html and
// https://maryrosecook.com/blog/post/little-lisp-interpreter
package main
import (
"bufio"
"fmt"
"os"
"regexp"
"strconv"
"strings"
)
func main() {
breadr := bufio.NewReader(os.Stdin) // buffered reader to read console input
prompt := "glisop>>" // console prompt
// run REPL-style until keyboard interrupt
for {
// show prompt
fmt.Print(prompt)
in, _ := breadr.ReadString('\n')
if strings.TrimSpace(in) == "" {
// no input - continue showing prompt
} else {
// input!! - evaluate
fmt.Printf("%s\n", parse(in)) // echo parsed input
}
}
}
// split input statement into tokens (names, numbers and parentheses)
func tokenize(input string) []string {
input = strings.TrimSpace(input)
// prepare regexps to match open/close parentheses
r_spc_op, err := regexp.Compile(`\(`) // replace all opening parentheses with [opening parentheses with spaces around them]
r_spc_cl, err := regexp.Compile(`\)`) // replace all closing parentheses with [closing parentheses with spaces around them]
// error in regexp?
if err != nil {
fmt.Printf("Regexp error in tokenizer: %v\n", err)
return nil
}
// perform replacement using prepared regexps and return result of splitting on whitespace
input = r_spc_op.ReplaceAllString(r_spc_cl.ReplaceAllString(input, " ) "), " ( ")
return strings.Split(input, " ")
}
// parenthesize() takes the tokens produced by tokenize() and produces a nested array that mimics the structure of the Lisp code
func parenthesize(tokens []string, list []*category) []*category {
if list == nil {
return parenthesize(tokens, []*category{})
} else {
token := strings.TrimSpace(tokens[0])
tokens = tokens[1:]
if token == "" {
// return list.pop();
// return list[len(list)-1]
if len(list) > 0 {
return list[len(list)-1:]
} else {
return nil
}
} else if token == "(" {
// list.push(parenthesize(input, []));
// return parenthesize(input, list);
list = append(list, parenthesize(tokens, []*category{})...)
return parenthesize(tokens, list)
} else if token == ")" {
return list
} /*else {
return parenthesize(tokens, append(list, categorize(token)))
} */
return parenthesize(tokens, append(list, categorize(token)))
}
}
func categorize(token string) *category {
_, err := strconv.ParseFloat(strings.TrimSpace(token), 64)
if err == nil {
// number
// return { type:'literal', value: parseFloat(input) };
return &category{"literal", token}
} else {
tok_runes := []rune(token)
if tok_runes[0] == '"' && tok_runes[len(tok_runes)-1] == '"' {
// return { type:'literal', value: input.slice(1, -1) };
return &category{"literal", string(tok_runes[1 : len(tok_runes)-1])}
} else {
// return { type:'identifier', value: input };
return &category{"identifier", token}
}
}
}
func parse(input string) []*category {
return parenthesize(tokenize(input), nil)
/*cats := parenthesize(tokenize(input), nil)
for _, c := range cats {
c.toString()
}
fmt.Print("\n")*/
}
type category struct {
typ string // type
val string // value
}
func (cat *category) toString() string {
return fmt.Sprintf("[type: %s, value: %s] ", cat.typ, cat.val)
}