A fast implementation of the Nix expression language
Revisión | 322ce0897c89057b54726d1d10da3deaa281ee57 (tree) |
---|---|
Tiempo | 2024-06-13 13:01:55 |
Autor | Corbin <cds@corb...> |
Commiter | Corbin |
gmachine: Flesh out actual operations.
This is going to be a supercombinator machine, so I need to rethink the
current compilation scheme.
@@ -2,9 +2,15 @@ | ||
2 | 2 | # We will implement graph reduction by compiling an expression into bytecode |
3 | 3 | # which, when evaluated, builds the graph node representing that expression. |
4 | 4 | |
5 | -CONST, OUTER, PICK = range(3) | |
5 | +# Quirks: | |
6 | +# 0) The instruction at PC 0 is always UNWIND, to ease EVAL. | |
7 | + | |
8 | +from heap import HeapApp | |
9 | + | |
10 | +OUTER, CONST, LOCAL, ARG, PRIM, MKAP, SLIDE, COND, EVAL, UNWIND = range(10) | |
6 | 11 | |
7 | 12 | class StackUnderflow(Exception): pass |
13 | +class Overapplied(Exception): pass | |
8 | 14 | |
9 | 15 | class G(object): |
10 | 16 | _immutable_attrs_ = "code[*]", "consts[*]" |
@@ -14,25 +20,52 @@ class G(object): | ||
14 | 20 | |
15 | 21 | def run(self, outers): |
16 | 22 | stack = [] |
17 | - pc = 0 | |
23 | + dump = [] | |
24 | + # Quirk (0) | |
25 | + pc = 1 | |
18 | 26 | while pc < len(self.code): |
19 | 27 | inst = self.code[pc] |
20 | 28 | pc += 1 |
21 | - if inst == CONST: | |
22 | - stack.append(self.consts[self.code[pc]]) | |
23 | - pc += 1 | |
24 | - elif inst == OUTER: | |
29 | + if inst == OUTER: | |
25 | 30 | stack.append(outers[self.code[pc]]) |
26 | 31 | pc += 1 |
27 | - elif inst == PICK: | |
28 | - depth = self.code[pc] | |
32 | + elif inst == CONST: | |
33 | + stack.append(self.consts[self.code[pc]]) | |
34 | + pc += 1 | |
35 | + elif inst == LOCAL: | |
36 | + i = len(stack) - self.code[pc] | |
29 | 37 | pc += 1 |
30 | - i = len(stack) - depth | |
31 | - if i < 0: raise StackUnderflow() | |
32 | 38 | stack.append(stack[i]) |
39 | + elif inst == ARG: | |
40 | + i = len(stack) - self.code[pc] | |
41 | + pc += 1 | |
42 | + node = stack[i] | |
43 | + if not isinstance(node, HeapApp): raise Overapplied() | |
44 | + stack.append(node.f) | |
45 | + elif inst == PRIM: assert False | |
46 | + elif inst == MKAP: stack.append(HeapApp(stack.pop(), stack.pop())) | |
47 | + elif inst == SLIDE: | |
48 | + top = stack.pop() | |
49 | + stop = len(stack) - self.code[pc] | |
50 | + pc += 1 | |
51 | + if stop < 0: raise StackUnderflow() | |
52 | + del stack[:stop] | |
53 | + stack.append(top) | |
54 | + elif inst == COND: assert False | |
55 | + elif inst == EVAL: | |
56 | + top = stack.pop() | |
57 | + dump.append((stack, pc)) | |
58 | + stack = [top] | |
59 | + pc = 0 | |
60 | + elif inst == UNWIND: | |
61 | + while isinstance(stack[-1], HeapApp): stack.append(stack[-1].f) | |
62 | + # XXX supercombinators | |
63 | + if dump: stack, pc = dump.pop() | |
64 | + else: return stack.pop() | |
33 | 65 | else: |
34 | 66 | print "Unknown bytecode:", inst |
35 | 67 | assert False |
68 | + # XXX possible? | |
36 | 69 | try: return stack.pop() |
37 | 70 | except IndexError: raise StackUnderflow() |
38 | 71 |
@@ -44,7 +77,8 @@ class MissingVar(Exception): | ||
44 | 77 | class Compiler(object): |
45 | 78 | def __init__(self, scope): |
46 | 79 | self.scope = scope |
47 | - self.code = [] | |
80 | + # Quirk (0) | |
81 | + self.code = [UNWIND] | |
48 | 82 | self.consts = [] |
49 | 83 | self.locals = [] |
50 | 84 |
@@ -62,9 +96,21 @@ class Compiler(object): | ||
62 | 96 | self.code.append(CONST) |
63 | 97 | self.code.append(i) |
64 | 98 | |
65 | - def pick(self, i): | |
99 | + def local(self, i): | |
100 | + assert i >= 0, "implementation error" | |
101 | + self.code.append(LOCAL) | |
102 | + self.code.append(i) | |
103 | + | |
104 | + def arg(self, i): | |
105 | + assert i >= 0, "implementation error" | |
106 | + self.code.append(ARG) | |
107 | + self.code.append(i) | |
108 | + | |
109 | + def mkAp(self): self.code.append(MKAP) | |
110 | + | |
111 | + def slide(self, i): | |
66 | 112 | assert i >= 0, "implementation error" |
67 | - self.code.append(PICK) | |
113 | + self.code.append(SLIDE) | |
68 | 114 | self.code.append(i) |
69 | 115 | |
70 | 116 | def finish(self): return G(self.code[:], self.consts[:]) |
@@ -36,25 +36,25 @@ class HeapObject(object): | ||
36 | 36 | self.__class__.__name__) |
37 | 37 | |
38 | 38 | class HeapTrue(HeapObject): |
39 | - _immutable_ = True | |
39 | + # _immutable_ = True | |
40 | 40 | def asStr(self): return "true" |
41 | 41 | def unwrapBool(self): return True |
42 | 42 | true = HeapTrue() |
43 | 43 | |
44 | 44 | class HeapFalse(HeapObject): |
45 | - _immutable_ = True | |
45 | + # _immutable_ = True | |
46 | 46 | def asStr(self): return "false" |
47 | 47 | def unwrapBool(self): return False |
48 | 48 | false = HeapFalse() |
49 | 49 | |
50 | 50 | class HeapInt(HeapObject): |
51 | - _immutable_ = True | |
51 | + # _immutable_ = True | |
52 | 52 | def __init__(self, i): self.i = i |
53 | 53 | def asStr(self): return str(self.i) |
54 | 54 | def unwrapInt(self): return self.i |
55 | 55 | |
56 | 56 | class HeapStr(HeapObject): |
57 | - _immutable_ = True | |
57 | + # _immutable_ = True | |
58 | 58 | def __init__(self, s): self.s = s |
59 | 59 | def asStr(self): return self.s |
60 | 60 | def unwrapStr(self): return self.s |
@@ -2,7 +2,7 @@ import sys | ||
2 | 2 | |
3 | 3 | from gmachine import Compiler |
4 | 4 | from heap import defaultScope, InfiniteLoop |
5 | -from parser import parse, MissingVar, NotAnExpr, ParseError | |
5 | +from parser import parse, NotAnExpr, ParseError | |
6 | 6 | |
7 | 7 | def entryPoint(argv): |
8 | 8 | if len(argv) < 2: |
@@ -33,10 +33,6 @@ def entryPoint(argv): | ||
33 | 33 | print "Error while compiling" |
34 | 34 | print "Input syntax was not an expr, but:", e.cls |
35 | 35 | return 1 |
36 | - except MissingVar as e: | |
37 | - print "Error while compiling" | |
38 | - print "Name was missing:", e.name | |
39 | - return 1 | |
40 | 36 | |
41 | 37 | obj = g.run(defaultScope.values()) |
42 | 38 | print "Heap object:", obj |
@@ -161,12 +161,12 @@ class SelectBox(EBox): | ||
161 | 161 | class VarBox(EBox): |
162 | 162 | def __init__(self, name): self.name = name |
163 | 163 | def pretty(self): return self.name |
164 | - def compile(self, compiler): | |
165 | - # XXX should probably be a compiler method | |
166 | - if self.name in compiler.locals: | |
167 | - compiler.pick(len(compiler.locals) - compiler.locals.index(self.name)) | |
168 | - elif self.name in compiler.scope: return compiler.outer(self.name) | |
169 | - else: raise MissingVar(self.name) | |
164 | + # def compile(self, compiler): | |
165 | + # # XXX should probably be a compiler method | |
166 | + # if self.name in compiler.locals: | |
167 | + # compiler.pick(len(compiler.locals) - compiler.locals.index(self.name)) | |
168 | + # elif self.name in compiler.scope: return compiler.outer(self.name) | |
169 | + # else: raise MissingVar(self.name) | |
170 | 170 | |
171 | 171 | class AppBox(EBox): |
172 | 172 | def __init__(self, func, arg): |