Instantly share code, notes, and snippets.

@yukirin /ils.go
Last active Jan 23, 2016

Embed
What would you like to do?
((Pythonで) 書く (Lisp) インタプリタ) をGoで書いてみる
package main
import (
"bufio"
"fmt"
"os"
"reflect"
"strconv"
"strings"
)
type expr interface{}
type symbol string
type number float64
type list []expr
type proc func(...expr) expr
type environ struct {
vars map[symbol]expr
outer *environ
}
func (e environ) find(s symbol) environ {
if _, ok := e.vars[s]; ok {
return e
}
return e.outer.find(s)
}
func lex(s string) *[]string {
s = strings.Replace(strings.Replace(s, "(", " ( ", -1), ")", " ) ", -1)
tokens := strings.Fields(s)
return &tokens
}
func parse(ps *[]string) expr {
if len(*ps) == 0 {
panic("unexpected EOF while reading")
}
token := (*ps)[0]
*ps = (*ps)[1:]
if "(" == token {
var l list
for (*ps)[0] != ")" {
l = append(l, parse(ps))
}
*ps = (*ps)[1:]
return l
} else if ")" == token {
panic("unepected )")
}
return atom(token)
}
func atom(s string) expr {
if f, err := strconv.ParseFloat(s, 64); err == nil {
return number(f)
}
return symbol(s)
}
func eval(e expr, env environ) expr {
switch v := e.(type) {
case number:
return v
case symbol:
return env.find(v).vars[v]
}
v := e.(list)
switch v[0].(symbol) {
case "quote":
return v[1]
case "if":
test, conseq, alt := v[1], v[2], v[3]
if eval(test, env).(bool) {
return eval(conseq, env)
}
return eval(alt, env)
case "set!":
sym := v[1].(symbol)
val := eval(v[2], env)
env.find(sym).vars[sym] = val
return val
case "define":
sym := v[1].(symbol)
env.vars[sym] = eval(v[2], env)
return sym
case "lambda":
vars, exp := v[1], v[2]
l := vars.(list)
return proc(func(es ...expr) expr {
m := make(map[symbol]expr)
for i, val := range l {
m[val.(symbol)] = es[i]
}
return eval(exp, environ{m, &env})
})
case "begin":
var val expr
for _, exp := range v[1:] {
val = eval(exp, env)
}
return val
default:
var exps list
for _, exp := range v {
exps = append(exps, eval(exp, env))
}
f := exps[0].(proc)
return f(exps[1:]...)
}
}
func repl(prompt string) error {
env := environ{
vars: make(map[symbol]expr),
outer: nil,
}
env = addGlobals(env)
sc := bufio.NewScanner(os.Stdin)
fmt.Print(prompt)
for sc.Scan() {
v := eval(parse(lex(sc.Text())), env)
fmt.Println(String(v))
fmt.Print(prompt)
}
return sc.Err()
}
func addGlobals(env environ) environ {
procs := map[symbol]proc{
"+": add, "-": sub, "*": mul,
"/": div, "not": not, ">": gt,
"<": lt, ">=": ge, "<=": le,
"=": eq, "equal?": eq, "length": length,
"cons": cons, "car": car, "cdr": cdr,
"append": add, "list": newList, "list?": isList,
"null?": isNull, "symbol?": isSymbol,
}
for k, v := range procs {
env.vars[k] = v
}
return env
}
// String convert expr to string
func String(e expr) string {
switch v := e.(type) {
case list:
var a []string
for _, value := range v {
a = append(a, String(value))
}
return "(" + strings.Join(a, " ") + ")"
default:
return fmt.Sprint(v)
}
}
func add(es ...expr) expr { return es[0].(number) + es[1].(number) }
func sub(es ...expr) expr { return es[0].(number) - es[1].(number) }
func mul(es ...expr) expr { return es[0].(number) * es[1].(number) }
func div(es ...expr) expr { return es[0].(number) / es[1].(number) }
func not(es ...expr) expr { return !es[0].(bool) }
func gt(es ...expr) expr { return es[0].(number) > es[1].(number) }
func lt(es ...expr) expr { return es[0].(number) < es[1].(number) }
func ge(es ...expr) expr { return !lt(es...).(bool) }
func le(es ...expr) expr { return !gt(es...).(bool) }
func eq(es ...expr) expr { return reflect.DeepEqual(es[0], es[1]) }
func length(es ...expr) expr { return number(len(es[0].(list))) }
func car(es ...expr) expr { return es[0].(list)[0] }
func cdr(es ...expr) expr { return es[0].(list)[1:] }
func newList(es ...expr) expr { return list(es) }
func isList(es ...expr) expr { _, ok := es[0].(list); return ok }
func isNull(es ...expr) expr { return len(es[0].(list)) == 0 }
func isSymbol(es ...expr) expr { _, ok := es[0].(symbol); return ok }
func cons(es ...expr) expr { return append(list{es[0]}, es[1].(list)...) }
func main() {
if err := repl("lis.go> "); err != nil {
fmt.Println(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment