-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathexample_greenspun_test.go
184 lines (157 loc) · 4.32 KB
/
example_greenspun_test.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
package hm
import (
"fmt"
"log"
"strings"
"github.com/pkg/errors"
)
const digits = "0123456789"
type TyperExpression interface {
Expression
Typer
}
type λ struct {
name string
body Expression
}
func (n λ) Name() string { return n.name }
func (n λ) Body() Expression { return n.body }
func (n λ) IsLambda() bool { return true }
type lit string
func (n lit) Name() string { return string(n) }
func (n lit) Body() Expression { return n }
func (n lit) Type() Type {
switch {
case strings.ContainsAny(digits, string(n)) && strings.ContainsAny(digits, string(n[0])):
return Float
case string(n) == "true" || string(n) == "false":
return Bool
default:
return nil
}
}
func (n lit) IsLit() bool { return true }
func (n lit) IsLambda() bool { return true }
type app struct {
f Expression
arg Expression
}
func (n app) Fn() Expression { return n.f }
func (n app) Body() Expression { return n.arg }
func (n app) Arg() Expression { return n.arg }
type let struct {
name string
def Expression
in Expression
}
func (n let) Name() string { return n.name }
func (n let) Def() Expression { return n.def }
func (n let) Body() Expression { return n.in }
type letrec struct {
name string
def Expression
in Expression
}
func (n letrec) Name() string { return n.name }
func (n letrec) Def() Expression { return n.def }
func (n letrec) Body() Expression { return n.in }
func (n letrec) Children() []Expression { return []Expression{n.def, n.in} }
func (n letrec) IsRecursive() bool { return true }
type prim byte
const (
Float prim = iota
Bool
)
// implement Type
func (t prim) Name() string { return t.String() }
func (t prim) Apply(Subs) Substitutable { return t }
func (t prim) FreeTypeVar() TypeVarSet { return nil }
func (t prim) Normalize(TypeVarSet, TypeVarSet) (Type, error) { return t, nil }
func (t prim) Types() Types { return nil }
func (t prim) Eq(other Type) bool {
if ot, ok := other.(prim); ok {
return ot == t
}
return false
}
func (t prim) Format(s fmt.State, c rune) { fmt.Fprintf(s, t.String()) }
func (t prim) String() string {
switch t {
case Float:
return "Float"
case Bool:
return "Bool"
}
return "HELP"
}
//Phillip Greenspun's tenth law says:
// "Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp."
//
// So let's implement a half-arsed lisp (Or rather, an AST that can optionally be executed upon if you write the correct interpreter)!
func Example_greenspun() {
// haskell envy in a greenspun's tenth law example function!
//
// We'll assume the following is the "input" code
// let fac n = if n == 0 then 1 else n * fac (n - 1) in fac 5
// and what we have is the AST
fac := letrec{
"fac",
λ{
"n",
app{
app{
app{
lit("if"),
app{
lit("isZero"),
lit("n"),
},
},
lit("1"),
},
app{
app{lit("mul"), lit("n")},
app{lit("fac"), app{lit("--"), lit("n")}},
},
},
},
app{lit("fac"), lit("5")},
}
// but first, let's start with something simple:
// let x = 3 in x+5
simple := let{
"x",
lit("3"),
app{
app{
lit("+"),
lit("5"),
},
lit("x"),
},
}
env := SimpleEnv{
"--": &Scheme{tvs: TypeVarSet{'a'}, t: NewFnType(TypeVariable('a'), TypeVariable('a'))},
"if": &Scheme{tvs: TypeVarSet{'a'}, t: NewFnType(Bool, TypeVariable('a'), TypeVariable('a'), TypeVariable('a'))},
"isZero": &Scheme{t: NewFnType(Float, Bool)},
"mul": &Scheme{t: NewFnType(Float, Float, Float)},
"+": &Scheme{tvs: TypeVarSet{'a'}, t: NewFnType(TypeVariable('a'), TypeVariable('a'), TypeVariable('a'))},
}
var scheme *Scheme
var err error
scheme, err = Infer(env, simple)
if err != nil {
log.Printf("%+v", errors.Cause(err))
}
simpleType, ok := scheme.Type()
fmt.Printf("simple Type: %v | isMonoType: %v | err: %v\n", simpleType, ok, err)
scheme, err = Infer(env, fac)
if err != nil {
log.Printf("%+v", errors.Cause(err))
}
facType, ok := scheme.Type()
fmt.Printf("fac Type: %v | isMonoType: %v | err: %v", facType, ok, err)
// Output:
// simple Type: Float | isMonoType: true | err: <nil>
// fac Type: Float | isMonoType: true | err: <nil>
}