182 lines
2.9 KiB
Go
182 lines
2.9 KiB
Go
package ast
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/cevaris/ordered_map"
|
|
)
|
|
|
|
type AST struct {
|
|
Tag Tag
|
|
Val interface{}
|
|
}
|
|
|
|
type Tag int8
|
|
|
|
const (
|
|
Symbol Tag = iota
|
|
List
|
|
String
|
|
Num
|
|
Bool
|
|
Fn
|
|
Char
|
|
Table
|
|
Prim
|
|
Quoted
|
|
)
|
|
|
|
func (tag Tag) String() string {
|
|
names := []string{
|
|
"Symbol",
|
|
"List",
|
|
"String",
|
|
"Num",
|
|
"Bool",
|
|
"Fn",
|
|
"Char",
|
|
"Table",
|
|
"Prim",
|
|
"Quoted",
|
|
}
|
|
|
|
return names[tag]
|
|
}
|
|
|
|
func (ast *AST) Type() *AST {
|
|
names := []string{
|
|
"sym",
|
|
"cons",
|
|
"string",
|
|
"num",
|
|
"bool",
|
|
"fn",
|
|
"char",
|
|
"table",
|
|
"fn",
|
|
"cons",
|
|
}
|
|
|
|
if ast.Tag == Quoted {
|
|
return ast.Val.(*AST).Type()
|
|
}
|
|
|
|
name := names[ast.Tag]
|
|
if ast.Tag == Num {
|
|
val := ast.Val.(float64)
|
|
if val == float64(int64(val)) {
|
|
name = "int"
|
|
}
|
|
}
|
|
|
|
res := AST{Symbol, name}
|
|
return &res
|
|
}
|
|
|
|
func (ast *AST) Pretty() string {
|
|
if ast.Tag == List {
|
|
var agg []string
|
|
for _, elem := range ast.Val.([]*AST) {
|
|
agg = append(agg, elem.Pretty())
|
|
}
|
|
return "(" + strings.Join(agg, " ") + ")"
|
|
} else if ast.Tag == Quoted {
|
|
return "'" + ast.Val.(*AST).Pretty()
|
|
} else if ast.Tag == Fn {
|
|
val := ast.Val.(Func)
|
|
var agg []string
|
|
for _, elem := range val.Params {
|
|
agg = append(agg, elem)
|
|
}
|
|
rest := ""
|
|
if val.HasRest() {
|
|
rest = " . " + *val.Rest
|
|
}
|
|
// TODO: opt
|
|
opt := ""
|
|
iter := val.Opt.IterFunc()
|
|
for kv, ok := iter(); ok; kv, ok = iter() {
|
|
opt += "(o " + kv.Key.(string) + " " + kv.Value.(*AST).Pretty() + ")"
|
|
}
|
|
if val.HasOpt() && (val.HasRest() || len(agg) != 0) {
|
|
opt = " " + opt
|
|
}
|
|
body := val.Body.Pretty()
|
|
return "(fn (" + strings.Join(agg, " ") + rest + opt + ") " + body + ")"
|
|
} else if ast.Tag == Char {
|
|
return "#" + string(ast.Val.(rune))
|
|
} else if ast.Tag == Table {
|
|
var agg []string
|
|
|
|
for k, v := range ast.Val.(map[*AST]*AST) {
|
|
agg = append(agg, "("+k.Pretty()+" . "+v.Pretty()+")")
|
|
}
|
|
|
|
return "#hash(" + strings.Join(agg, " ") + ")"
|
|
} else if ast.Tag == Prim {
|
|
return "#<procedure: " + ast.Val.(Primitive).Name + ">"
|
|
}
|
|
return fmt.Sprintf("%v", ast.Val)
|
|
}
|
|
|
|
func (ast *AST) String() string {
|
|
switch ast.Tag {
|
|
case List, Quoted, Fn, Table, Prim:
|
|
return ast.Pretty()
|
|
case Char:
|
|
return string(ast.Val.(rune))
|
|
}
|
|
return fmt.Sprintf("%v", ast.Val)
|
|
}
|
|
|
|
type Env struct {
|
|
parent *Env
|
|
Values map[string]*AST
|
|
}
|
|
|
|
type PrimFn func([]*AST) (*AST, error)
|
|
|
|
type Primitive struct {
|
|
Name string
|
|
Fn PrimFn
|
|
}
|
|
|
|
func NewEnv(parent *Env) Env {
|
|
return Env{parent, make(map[string]*AST)}
|
|
}
|
|
|
|
func ParentEnv() Env {
|
|
return NewEnv(nil)
|
|
}
|
|
|
|
func (e *Env) Lookup(elem string) (*AST, error) {
|
|
res, ok := e.Values[elem]
|
|
|
|
if !ok {
|
|
if e.parent == nil {
|
|
return nil, fmt.Errorf("Symbol not found: %s", elem)
|
|
}
|
|
|
|
return e.parent.Lookup(elem)
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
type Func struct {
|
|
Params []string
|
|
Rest *string
|
|
Opt *ordered_map.OrderedMap
|
|
Body *AST
|
|
Env Env
|
|
}
|
|
|
|
func (f *Func) HasRest() bool {
|
|
return f.Rest != nil
|
|
}
|
|
|
|
func (f *Func) HasOpt() bool {
|
|
return f.Opt.Len() != 0
|
|
}
|