all: make a ton of features work
This commit is contained in:
161
ast/ast.go
161
ast/ast.go
@@ -1,44 +1,155 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type AST struct {
|
||||
Tag Tag
|
||||
Val interface{}
|
||||
Tag Tag
|
||||
Val interface{}
|
||||
}
|
||||
|
||||
type Tag int8
|
||||
|
||||
const (
|
||||
Symbol Tag = iota
|
||||
List
|
||||
String
|
||||
Num
|
||||
Bool
|
||||
Symbol Tag = iota
|
||||
List
|
||||
String
|
||||
Num
|
||||
Bool
|
||||
Fn
|
||||
Char
|
||||
Table
|
||||
Prim
|
||||
Quoted
|
||||
)
|
||||
|
||||
func (tag Tag) String() string {
|
||||
names := []string{
|
||||
"Symbol",
|
||||
"List",
|
||||
"String",
|
||||
"Num",
|
||||
"Bool",
|
||||
}
|
||||
names := []string{
|
||||
"Symbol",
|
||||
"List",
|
||||
"String",
|
||||
"Num",
|
||||
"Bool",
|
||||
"Fn",
|
||||
"Char",
|
||||
"Table",
|
||||
"Prim",
|
||||
"Quoted",
|
||||
}
|
||||
|
||||
return names[tag]
|
||||
return names[tag]
|
||||
}
|
||||
|
||||
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, " ") + ")"
|
||||
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()
|
||||
}
|
||||
return fmt.Sprintf("%v", ast.Val)
|
||||
|
||||
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)
|
||||
}
|
||||
opt := ""
|
||||
if val.HasOpt() {
|
||||
opt = " . " + *val.Opt
|
||||
}
|
||||
body := val.Body.Pretty()
|
||||
return "(fn (" + strings.Join(agg, " ") + 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)
|
||||
}
|
||||
|
||||
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
|
||||
Opt *string
|
||||
Body *AST
|
||||
Env Env
|
||||
}
|
||||
|
||||
func (f *Func) HasOpt() bool {
|
||||
return f.Opt != nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user