Skip to content

Commit 1b88218

Browse files
committed
0.36.0
1 parent c01ec68 commit 1b88218

7 files changed

Lines changed: 131 additions & 37 deletions

File tree

changelog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## 0.36.0
4+
5+
- Page now (after ***much*** pain) has closures
6+
- Lists literals now draw items from parent stacks (e.g. `1 2 3 [pop]` is now valid)
7+
38
## 0.35.1
49

510
- 'ge' was misnamed as 'gt'

examples/closures.pg

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/fun {
2+
1 dict begin
3+
/a 10 def
4+
{a ==}
5+
end
6+
} def
7+
8+
fun
9+
dup ==
10+
exec

page.nimble

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Package
22

3-
version = "0.35.1"
3+
version = "0.36.0"
44
author = "Nuclear Pasta"
55
description = "A PostScript-like language."
66
license = "Apache-2.0"

src/builtins.nim

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,9 @@ Takes a function P and executes it.
353353

354354
s.check(f.args)
355355

356+
ps.closure = f.closure
357+
defer: ps.closure = nil
358+
356359
if f.ptype == ptLiteral:
357360
evalValues(s, ps, f.values)
358361
else:
@@ -1107,7 +1110,7 @@ S -> V
11071110
Retrieves the value bound to a symbol S.
11081111
""", @[("S", tSymbol)]):
11091112
let name = s.pop().strv
1110-
s.push(s.get(name))
1113+
s.push(s.get(name, cast[State](ps.closure)))
11111114

11121115
addS("load?",
11131116
"""

src/interpreter.nim

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export value
2323
type Interpreter* = ref object
2424
rand: Rand
2525
state: State
26+
closure: ptr ClosureState
2627

2728

2829
proc codeEvaler(g: GlobalState, file, text: string): State
@@ -50,12 +51,30 @@ func pstate(self: Interpreter): ProcState =
5051

5152
proc exec*(self: Interpreter, nodes: openArray[Node])
5253

54+
proc runf(self: Interpreter, v: Value) =
55+
self.state.check(v.args)
56+
57+
let
58+
prevClos = self.closure
59+
ps = self.pstate
60+
61+
defer:
62+
self.closure = prevClos
63+
64+
ps.closure = v.closure
65+
self.closure = cast[ptr ClosureState](v.closure)
66+
67+
if v.ptype == ptLiteral:
68+
evalValues(self.state, ps, v.values)
69+
else:
70+
v.run(cast[pointer](self.state), ps)
71+
5372
proc runDeferred*(self: Interpreter) =
5473
self.state.deferred.add(@[])
5574
defer: discard self.state.deferred.pop()
5675

5776
for p in self.state.deferred[^2]:
58-
p.run(cast[pointer](self.state), self.pstate)
77+
self.runf(p)
5978

6079
proc exec(self: Interpreter, n: Node) =
6180
logger.logd("DEFERRED: " & $self.state.deferred)
@@ -76,29 +95,23 @@ proc exec(self: Interpreter, n: Node) =
7695
of nList:
7796
var i = newInterpreter(self.state.dicts)
7897
i.state.g = self.state.g
98+
i.state.parent = self.state
7999
i.exec(n.nodes)
80100
self.state.push(newList(i.state.stack))
81101
of nProc:
82-
self.state.push(newProcedure(n.nodes))
102+
let
103+
p = newProcedure(n.nodes)
104+
clos = self.state.closure()
105+
106+
p.closure = cast[pointer](clos)
107+
108+
self.state.push(p)
83109
of nWord:
84110
logger.logdv("Found word with value '" & n.tok.lit & "'")
85-
let v = self.state.get(n.tok.lit)
111+
let v = self.state.get(n.tok.lit, self.closure.stateFromClosure())
86112

87113
if v.typ == tProcedure:
88-
logger.logdv("Word is a function")
89-
logger.logdv(fmt"As debug: `{v.debug()}`")
90-
91-
logger.logdv("Checking function arguments")
92-
self.state.check(v.args)
93-
94-
logger.logdv("Executing function")
95-
96-
let ps = self.pstate
97-
98-
if v.ptype == ptLiteral:
99-
evalValues(self.state, ps, v.values)
100-
else:
101-
v.run(cast[pointer](self.state), ps)
114+
self.runf(v)
102115
else:
103116
logger.logdv("Word is not a function")
104117
self.state.push(v)
@@ -113,20 +126,17 @@ proc exec(self: Interpreter, n: Node) =
113126
discard self.state.dend(self.pstate)
114127

115128
if v.typ == tProcedure and not literal:
116-
self.state.check(v.args)
117-
118-
let ps = self.pstate
119-
120-
if v.ptype == ptLiteral:
121-
evalValues(self.state, ps, v.values)
122-
else:
123-
v.run(cast[pointer](self.state), ps)
129+
self.runf(v)
124130
else:
125131
self.state.push(v)
126132

127133
proc exec*(self: Interpreter, nodes: openArray[Node]) =
128134
if self.state.nodeRunner == nil:
129-
self.state.nodeRunner = proc(nodes: seq[Node]) = self.exec(nodes)
135+
self.state.nodeRunner = proc(nodes: seq[Node], closure: pointer) =
136+
let prevClos = self.closure
137+
defer: self.closure = prevClos
138+
self.closure = cast[ptr ClosureState](closure)
139+
self.exec(nodes)
130140

131141
for n in nodes:
132142
try:

src/state.nim

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,13 @@ type
2626
importCache*: TableRef[string, ImportCacheEntry]
2727
cleanup*: seq[proc()]
2828

29+
ClosureState* = object
30+
g*: GlobalState
31+
dicts*: seq[Dict]
32+
2933
State* = ref object
3034
g*: GlobalState
35+
parent*: State
3136
dictMin: int
3237
dicts: seq[Dict]
3338
stack: seq[Value]
@@ -50,6 +55,12 @@ func copy*(dict: Dict): Dict =
5055
for (k, v) in dict.pairs:
5156
result[k] = v
5257

58+
func pairSeq*(dict: Dict): seq[(string, Value)] =
59+
result = newSeqOfCap[(string, Value)](dict.len)
60+
61+
for p in dict.pairs:
62+
result.add(p)
63+
5364

5465
func newGlobalState*(exe, file: string, args: seq[string]): GlobalState =
5566
new result
@@ -59,8 +70,17 @@ func newGlobalState*(exe, file: string, args: seq[string]): GlobalState =
5970
result.importCache = newTable[string, ImportCacheEntry](0)
6071

6172

62-
func newState*(dictMin: int, dicts: varargs[Dict]): State =
73+
func stateFromClosure*(clos: ptr ClosureState): State =
74+
if clos == nil:
75+
return nil
76+
6377
new result
78+
result.g = clos[].g
79+
result.dicts = clos[].dicts
80+
81+
func newState*(dictMin: int, dicts: varargs[Dict], parent: State = nil): State =
82+
new result
83+
result.parent = parent
6484
result.dictMin = dictMin
6585
result.dicts = newSeqOfCap[Dict](varargsLen(dicts))
6686
result.deferred = newSeqOfCap[seq[Value]](varargsLen(dicts))
@@ -78,6 +98,8 @@ proc doCleanup*(self: State) =
7898
for f in self.g.cleanup:
7999
f()
80100

101+
self.g.cleanup = @[]
102+
81103
func dicts*(self: State): seq[Dict] =
82104
self.dicts
83105

@@ -88,6 +110,18 @@ func dbegin*(self: State, dict: Dict) =
88110
self.dicts.add(dict)
89111
self.deferred.add(@[])
90112

113+
proc closure*(self: State): ptr ClosureState =
114+
let clos = allocZ(ClosureState)
115+
clos[].g = self.g
116+
clos[].dicts = newSeqOfCap[Dict](self.dicts.len)
117+
118+
for d in self.dicts:
119+
clos[].dicts.add(d)
120+
121+
self.g.cleanup.add(proc() = dealloc(clos))
122+
123+
clos
124+
91125
func dbegin*(self: State, size: int) =
92126
self.dbegin(newDict(size))
93127

@@ -110,11 +144,14 @@ func has*(self: State, name: string): bool =
110144
proc set*(self: State, name: string, val: Value) =
111145
self.dicts[^1][name] = val
112146

113-
proc get*(self: State, name: string): Value =
147+
proc get*(self: State, name: string, closure: State = nil): Value =
114148
for d in self.dicts.rev:
115149
if d.hasKey(name):
116150
return d[name]
117151

152+
if closure != nil:
153+
return closure.get(name)
154+
118155
raise newPgError(fmt"Undefined symbol '{name}'")
119156

120157
proc unset*(self: State, name: string): bool =
@@ -160,6 +197,9 @@ proc push*(self: State, val: Value) =
160197

161198
func pop*(self: State): Value =
162199
if self.stack.len == 0:
200+
if self.parent != nil:
201+
return self.parent.pop()
202+
163203
raise newPgError("stack underflow")
164204

165205
self.stack.pop()
@@ -174,14 +214,38 @@ func peek*(self: State, ind: BackwardsIndex): Value =
174214
self.peek(self.stack.len - int(ind))
175215

176216
proc check*(self: State, args: ProcArgs) =
177-
if self.stack.len < args.len:
178-
raise newPgError(fmt"Expected {args.len} items on the stack but found {self.stack.len} items instead")
217+
if args.len == 0:
218+
return
219+
220+
var
221+
srcState = self
222+
ind = args.len - 1
223+
stackLen = 0
224+
stack = newSeq[Value](args.len)
225+
226+
while true:
227+
if srcState.stack.len > 0:
228+
for i in countdown(srcState.stack.len - 1, 0, 1):
229+
if ind < 0:
230+
break
231+
232+
stack[ind] = srcState.stack[i]
233+
dec ind
234+
inc stackLen
235+
236+
if ind < 0 or srcState.parent == nil:
237+
break
238+
239+
srcState = srcState.parent
240+
241+
if stackLen < args.len:
242+
raise newPgError(fmt"Expected {args.len} items on the stack but found {stackLen} items instead")
179243

180-
var i = self.stack.len - 1
244+
var i = stack.len - 1
181245

182246
for pst in args:
183-
if self.stack[i] isnot pst.typ:
184-
raise newPgError(fmt"Expected type {pst.typ} for argument {pst.name} at stack position {i + 1}, but found type {self.stack[i].typ} instead")
247+
if stack[i] isnot pst.typ:
248+
raise newPgError(fmt"Expected type {pst.typ} for argument {pst.name} at stack position {i + 1}, but found type {stack[i].typ} instead")
185249

186250
dec i
187251

src/value.nim

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,13 @@ type
3333
tProcedure = 0b100000000,
3434
tExtitem = 0b1000000000
3535

36-
Runner* = proc(nodes: seq[Node])
36+
Runner* = proc(nodes: seq[Node], closure: pointer)
3737

3838
ProcState* = ref object
3939
r*: Runner
4040
rand*: Rand
4141
deferred*: seq[seq[Value]]
42+
closure*: pointer
4243

4344
NativeProc* = proc(s: pointer, ps: ProcState)
4445
ProcArgs* = seq[tuple[name: string, typ: Type]]
@@ -72,6 +73,7 @@ type
7273
of tProcedure:
7374
args*: ProcArgs
7475
lit*: bool
76+
closure*: pointer
7577
case ptype: ProcType
7678
of ptNative:
7779
native: NativeProc
@@ -301,7 +303,7 @@ proc run*(self: Value, s: pointer, ps: ProcState) =
301303
self.native(s, ps)
302304
elif self.ptype == ptComposite:
303305
if self.nodes.len > 0:
304-
ps.r(self.nodes)
306+
ps.r(self.nodes, ps.closure)
305307
elif self.ptype == ptLiteral:
306308
panic(fmt"Literal procedures cannot be executed via 'run'")
307309
else:

0 commit comments

Comments
 (0)