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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
|
# Create a variable slot.
# A slot also functions as a variable reference, one kind of reference.
# References support some of the following fields:
# - Get: Get value
# - GetC: Get value, and clear the slot (variable only)
# - SetN: Define value
# - SetU: Change value
# - SetQ: Set value and return 0 if possible, and return 1 if not
# - GetF: Get corresponding field from namespace π©
MakeVar β { program π name:
vβ@ # Value
Get β !β"Runtime: Variable referenced before definition"
SetU β !β"β©: Variable modified before definition"
SetN β {
Get β© {π:v}
(SetU β© {vβ©π©}) π©
}
SetQ β 0βSetN
GetC β {
r β Getπ©
Getβ©SetUβ©SetNβ©!β"Internal error: Variable used after clear"
r
}
GetF β {program π©.Field name}
}
# Other kinds of references, and reference utilities
ref β {
# Constant-matching reference, like π2: in a header
Matcher β {π const:
SetQ β constΛ β’ β’
}
# Array destructuring β¨a,b,cβ©β
Array β {π arr:
Get β {π©.Get@}Β¨ arrΛ
# Common code for all setter functions
# π¨Sπ© sets reference π¨ to π©, and e indicates error handling
al β β¨Β΄ {β¨alβ©:al;0}Β¨ arr
_set_ β {S _π£_ e:
Err β {(eβΎ": "βΎπ©)!eβ‘@ β β¨1β©} # eβ‘@ indicates SetQ, which can't error
c β (eβ‘@) β {π½}βΏ{π½β1} # GetF or Get in F can error
# Get field for reference π¨ from namespace, if possible
F β {β¨GβGetFβ©ππ©:(Gπ©).Get@ ; !Err"Cannot extract non-name from namespace"}
{
0=β’Typeπ© ? Errβal "Can't use alias in list destructuring"
arr β‘ββ’βΆβ¨Errβ"Target and value shapes don't match", SΒ¨β© π© ;
6=β’Typeπ© ? (β’ S Fβπ©)_cΒ¨ arr ;
Err "Multiple targets but atomic value"
}
}
SetN β {π¨.SetNπ©}_set_"β"
SetU β {π¨.SetUπ©}_set_"β©"
SetQ β β¨Β΄ {π¨.SetQπ©}_set_@
# Create a merged reference array based on this one
Merge β {π:
Split β {
"[β¦]β: Value must have rank 1 or more" ! 1β€=π©
<Λ π©
}
Get β > Get
SetNβΏSetUβΏSetQ β {πβSplit}Β¨ SetNβΏSetUβΏSetQ
}
}
# Alias, like βvals in β¨aβΏbβvalsβ©β
# It behaves like β¨aβΏbβ©β except when destructuring a namespace (GetF)
Alias β {envβΏname π r:
SetNβΏSetUβΏSetQ β r
GetF β {env.program π©.Field name}
al β 1
}
# Destructuring placeholder Β·
not β { SetUβSetNββ’ β SetQβ0Λ }
}
# Create an environment: essentially, a list of variable slots and a
# reference to the parent environment.
MakeEnv β { πβ¨
p # Parent environment
v # Total number of slots (special names plus named variables)
n # ID numbers of named variables
e # Which named variables are exported
β©:
ns β v-β n # Number of special names
parent β p
program β p.program # Determines the meaning of ID numbers
vars β programβΈMakeVarΒ¨ (nsβ₯Β―1) βΎ n # Variables
# Return a namespace for this environment.
# A namespace is represented as a namespace with one field, Field.
# π¨ ns.Field π© returns the value of the field with ID π© in program π¨.
MakeNS β {π€
v β @ β£Β΄Β¨ n ββ(eβΈ/) nsβvars # Lookup table
Field β {π¨πi:
cross β π¨ { π¨1ββ‘π© ? β’ ; β π©.names β ββπ¨.names } program
(Cross i) β v
}
}
}
# Return a function that reads the variable from slot s at depth d.
# The input is taken from the bytecode stream.
VO β { dβπ@, sβπ@, sβΒ·{π©.vars}{π©.parent}βd }
# Namespace from environment
Namespace β {π©.MakeNS@}
# Read field π¨ from program π©, where π¨ is the environment and index
GetField β { eβΏi π π©:
"Key lookup in non-namespace" ! 6=β’Typeπ©
(e.program π©.Field i).Get @
}
# Constants
nothing β {β} # Used when π¨ is Β·
skipMark β {β} # Indicates body aborted instead of returning
# Execution stack: every body evaluation makes one of these
MakeStack β {
s β π© # Stack (a list)
cont β 1 # Whether to continue execution
rslt β skipMark # Result: skipMark to abort current body
Push β {sβΎβ©<π©} # Push a value
Pop β {tβ-π© β (sβΛβ©t) β’ β½tβs} # Pop π© values; return as list
Peek β {π:Β―1βs} # Return but don't pop top value
Ret β {rsltβ©π© β contβ©0 β "Internal compiler error: Wrong stack size"!π¨β₯β s}
Skip β {π: contβ©0} # Exit with no return value
}
# All the opcodes
# Each one is a function that takes the next-opcode function so it can
# read values from the bytecode stream. It returns a function to be
# called on the stack s and environment e at evaluation time.
ops β ((!β"Unknown opcode")Λβ£Β΄β’)Β¨ βΛ β> β¨
# Constants and drop
0βΏ{iβπ@ β {sπe: s.Push iβe.program.consts } }
1βΏ{iβπ@ β {sπe: s.Push e {ππ©}Λ iβe.program.blocks } }
6βΏ( {sπe: s.Pop 1 }Λ)
# Returns
7βΏ( {sπe: 0 s.Ret βs.Pop 1 }Λ)
8βΏ( {sπe: 1 s.Ret Namespace e }Λ)
# Arrays
11βΏ{iβπ@ β {sπe: s.Push β½s.Pop i } }
12βΏ{iβπ@ β {sπe: s.Push ref.Array β½s.Pop i } }
13βΏ{iβπ@ β {sπe: s.Push > β½s.Pop i } }
14βΏ{iβπ@ β {sπe: s.Push {π©.Merge@} ref.Array β½s.Pop i } }
# Application
16βΏ( {sπe: s.Push { fβΏx: F x } s.Pop 2 }Λ)
17βΏ( {sπe: s.Push { wβΏfβΏx: w F x } s.Pop 3 }Λ)
20βΏ( {sπe: s.Push { gβΏh: G H } s.Pop 2 }Λ)
21βΏ( {sπe: s.Push { fβΏgβΏh: F G H } s.Pop 3 }Λ)
26βΏ( {sπe: s.Push { fβΏm : F _m } s.Pop 2 }Λ)
27βΏ( {sπe: s.Push { fβΏmβΏg: F _m_ g } s.Pop 3 }Λ)
# Application with Nothing
18βΏ( {sπe: s.Push { fβΏx: Fβ(nothingβΈβ’) x } s.Pop 2 }Λ) # Like 16
19βΏ( {sπe: s.Push { wβΏfβΏx: (nothingβ’wΛ)βΆβ¨F,wΛβΈFβ©β(nothingβΈβ’) x } s.Pop 3 }Λ) # Like 17
23βΏ( {sπe: s.Push { fβΏgβΏh: {fβ‘nothing?G H;F G H} } s.Pop 3 }Λ) # Like 21
22βΏ( {sπe: "Left argument required" ! nothingβ’s.Peek@ }Λ)
# Variables
32βΏ{vβVOπ©β {sπe: s.Push (V e).Get @ } }
34βΏ{vβVOπ©β {sπe: s.Push (V e).GetC@ } }
33βΏ{vβVOπ©β {sπe: s.Push V e } }
# Headers
42βΏ( {sπe: {0:s.Skip@; 1:@; π:!"Predicate value must be 0 or 1"} βs.Pop 1 }Λ)
43βΏ( {sπe: s.Push ref.Matcher βs.Pop 1 }Λ)
44βΏ( {sπe: s.Push ref.not }Λ)
# Assignment
47βΏ( {sπe: s.Skipββ’{rβΏ v: r.SetQ v } s.Pop 2 }Λ) # r:
48βΏ( {sπe: s.Push { rβΏ v: r.SetNβΈβ’ v } s.Pop 2 }Λ) # r βv
49βΏ( {sπe: s.Push { rβΏ v: r.SetUβΈβ’ v } s.Pop 2 }Λ) # r β©v
50βΏ( {sπe: s.Push { rβΏfβΏx: r.SetUβΈβ’ (r.Get@)F x } s.Pop 3 }Λ) # r Fβ©x
51βΏ( {sπe: s.Push { rβΏf : r.SetUβΈβ’ F r.Get@ } s.Pop 2 }Λ) # r Fβ©
# Namespaces
64βΏ{iβπ@ β {sπe: s.Push eβΏi GetField βs.Pop 1 } }
66βΏ{iβπ@ β {sπe: s.Push eβΏi ref.Alias βs.Pop 1 } }
β©
# Evaluate a body
RunBC β { bcβΏposβΏenv: # bytecode, starting position, environment
Next β {π: (pos+β©1) β’ posβbc }
stack β MakeStack β¨β©
Step β {π:
op β (Next@) β ops
op β© Op next
stack Op env
stack.cont # Changes to 0 on return or abort
}
_while_ β {π½βπΎβπ½_π£_πΎβπ½βπΎπ©}
Step _while_ β’ 1
stack.rslt
}
# Evaluate a program, given the compiler output
{ VM bcβΏconstsβΏblockInfoβΏbodyInfoβΏlocβΏtoken:
bodies β {startβΏvarsβΏnamesβΏexport:
# Called when the body is evaluated
{parent π args:
env β MakeEnv parentβΏvarsβΏnamesβΏexport
(β’ {π©.SetN π¨}Β¨ β βenv.varsΛ) args # Initialize arguments
RunBC bcβΏstartβΏenv
}
}Β¨ bodyInfo
blocks β {typeβΏimmβΏbody:
# Handle operands
inner β imm β type β β¨
2β₯β¨{πn: N β¨β©}β©
{πn: {dβN π£βΏπ βπ¨Dπ©}}βΏ{πn: {N π£βΏπ }}
{πn: {dβN π£βΏπβΏπβπ¨Dπ©}}βΏ{πn: {N π£βΏπβΏπ}}
β©
# Handle arguments
outer β imm β β¨
{
mβΏd: {πv: {M π€βΏπ©βΏnothingβΎv;D π€βΏπ©βΏπ¨βΎv}} ;
β¨bβ©: {πv: {B π€βΏπ©βΏ(π¨β£nothing)βΎv}}
}
β
β©
# Assemble bodies
nmc β "No matching case"
Then β {first π next: {skipMarkβ’rβπ¨Firstπ© ? r ; π¨Nextπ©}}
run β {
1=β’Type π© ?
β¨(π©βbodies) Then {!βnmc}β©
;
"Internal compiler error: Invalid body indices" ! 1==π©
! (β π©) β‘ 2-imm
e β {imm ? β¨nmcβ© ;
(0=β Β¨π©) nmcββ£Β¨ "Left argument "βΈβΎΒ¨β¨"not allowed","required"β©
}π©
π© ThenΒ΄β(ββbodies)Λβ{!βπ©}Β¨ e
} body
{π parent:
Inner Outer {parentΛ π β’}Β¨ run
}
}Β¨ blockInfo
program β {
constsβconsts
blocksβblocks
namesβ0β2βtoken
}
(βblocks){π½} {programβprogram}
}
|