Skip to content

Commit 2ece17f

Browse files
committed
compiler: Bool Ops, Labels, N pass assembly, Jump resolution
1 parent d84b599 commit 2ece17f

File tree

3 files changed

+177
-12
lines changed

3 files changed

+177
-12
lines changed

compile/compile.go

+85-12
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,30 @@ func (c *compiler) Op(op byte) {
155155
c.OpCodes.Add(&Op{Op: op})
156156
}
157157

158+
// Inserts an existing label
159+
func (c *compiler) Label(Dest *Label) {
160+
c.OpCodes.Add(Dest)
161+
}
162+
163+
// Inserts and creates a label
164+
func (c *compiler) NewLabel() *Label {
165+
Dest := new(Label)
166+
c.OpCodes.Add(Dest)
167+
return Dest
168+
}
169+
170+
// Compiles a jump instruction
171+
func (c *compiler) Jump(Op byte, Dest *Label) {
172+
switch Op {
173+
case vm.JUMP_IF_FALSE_OR_POP, vm.JUMP_IF_TRUE_OR_POP, vm.JUMP_ABSOLUTE, vm.POP_JUMP_IF_FALSE, vm.POP_JUMP_IF_TRUE: // Absolute
174+
c.OpCodes.Add(&JumpAbs{OpArg: OpArg{Op: Op}, Dest: Dest})
175+
case vm.JUMP_FORWARD: // Relative
176+
panic("FIXME JUMP_FORWARD NOT implemented")
177+
default:
178+
panic("Jump called with non jump instruction")
179+
}
180+
}
181+
158182
// Compile statements
159183
func (c *compiler) compileStmts(stmts []ast.Stmt) {
160184
for _, stmt := range stmts {
@@ -265,8 +289,23 @@ func (c *compiler) compileExpr(expr ast.Expr) {
265289
case *ast.BoolOp:
266290
// Op BoolOpNumber
267291
// Values []Expr
268-
_ = node
269-
panic("FIXME not implemented")
292+
var op byte
293+
switch node.Op {
294+
case ast.And:
295+
op = vm.JUMP_IF_FALSE_OR_POP
296+
case ast.Or:
297+
op = vm.JUMP_IF_TRUE_OR_POP
298+
default:
299+
panic("Unknown BoolOp")
300+
}
301+
label := new(Label)
302+
for i, e := range node.Values {
303+
c.compileExpr(e)
304+
if i != len(node.Values)-1 {
305+
c.Jump(op, label)
306+
}
307+
}
308+
c.Label(label)
270309
case *ast.BinOp:
271310
// Left Expr
272311
// Op OperatorNumber
@@ -425,8 +464,35 @@ func (is *Instructions) Add(i Instruction) {
425464
*is = append(*is, i)
426465
}
427466

428-
// Assembler the instructions into an Opcode string
467+
// Do a pass of assembly
468+
//
469+
// Returns a boolean as to whether the stream changed
470+
func (is Instructions) Pass(pass int) bool {
471+
addr := uint32(0)
472+
changed := pass == 0
473+
for _, i := range is {
474+
i.SetPos(addr)
475+
if pass > 0 {
476+
// Only resolve addresses on 2nd pass
477+
if resolver, ok := i.(Resolver); ok {
478+
changed = changed || resolver.Resolve()
479+
}
480+
}
481+
addr += i.Size()
482+
}
483+
return changed
484+
}
485+
486+
// Assemble the instructions into an Opcode string
429487
func (is Instructions) Assemble() string {
488+
for i := 0; i < 10; i++ {
489+
changed := is.Pass(i)
490+
if !changed {
491+
goto done
492+
}
493+
}
494+
panic("Failed to assemble after 10 passes")
495+
done:
430496
out := make([]byte, 0, 3*len(is))
431497
for _, i := range is {
432498
out = append(out, i.Output()...)
@@ -437,10 +503,14 @@ func (is Instructions) Assemble() string {
437503
type Instruction interface {
438504
Pos() uint32
439505
SetPos(uint32)
440-
Size() int
506+
Size() uint32
441507
Output() []byte
442508
}
443509

510+
type Resolver interface {
511+
Resolve() bool
512+
}
513+
444514
// Position
445515
type pos uint32
446516

@@ -461,7 +531,7 @@ type Op struct {
461531
}
462532

463533
// Uses 1 byte in the output stream
464-
func (o *Op) Size() int {
534+
func (o *Op) Size() uint32 {
465535
return 1
466536
}
467537

@@ -478,7 +548,7 @@ type OpArg struct {
478548
}
479549

480550
// Uses 1 byte in the output stream
481-
func (o *OpArg) Size() int {
551+
func (o *OpArg) Size() uint32 {
482552
if o.Arg <= 0xFFFF {
483553
return 3 // Op Arg1 Arg2
484554
} else {
@@ -501,7 +571,7 @@ type Label struct {
501571
}
502572

503573
// Uses 0 bytes in the output stream
504-
func (o *Label) Size() int {
574+
func (o *Label) Size() uint32 {
505575
return 0
506576
}
507577

@@ -517,15 +587,18 @@ type JumpAbs struct {
517587
Dest *Label
518588
}
519589

520-
// FIXME need changed flags?
521-
522590
// Set the Arg from the Jump Label
523-
func (o *JumpAbs) Resolve() {
524-
o.OpArg.Arg = o.Dest.Pos()
591+
//
592+
// Returns a changed flag
593+
func (o *JumpAbs) Resolve() bool {
594+
newPos := o.Dest.Pos()
595+
changed := o.OpArg.Arg == newPos
596+
o.OpArg.Arg = newPos
597+
return changed
525598
}
526599

527600
// Bytes used in the output stream
528-
func (o *JumpAbs) Size() int {
601+
func (o *JumpAbs) Size() uint32 {
529602
o.Resolve()
530603
return o.OpArg.Size()
531604
}

compile/compile_data_test.go

+85
Original file line numberDiff line numberDiff line change
@@ -318,4 +318,89 @@ var compileTestData = []struct {
318318
Firstlineno: 1,
319319
Lnotab: "",
320320
}, " 1 0 LOAD_CONST 0 ('a')\n 3 UNARY_NEGATIVE\n 4 RETURN_VALUE\n"},
321+
{"1 and 2", "eval", py.Code{
322+
Argcount: 0,
323+
Kwonlyargcount: 0,
324+
Nlocals: 0,
325+
Stacksize: 2,
326+
Flags: 64,
327+
Code: "\x64\x00\x00\x6f\x09\x00\x64\x01\x00\x53",
328+
Consts: []py.Object{py.Int(1), py.Int(2)},
329+
Names: []string{},
330+
Varnames: []string{},
331+
Freevars: []string{},
332+
Cellvars: []string{},
333+
Filename: "<string>",
334+
Name: "<module>",
335+
Firstlineno: 1,
336+
Lnotab: "",
337+
}, " 1 0 LOAD_CONST 0 (1)\n 3 JUMP_IF_FALSE_OR_POP 9\n 6 LOAD_CONST 1 (2)\n >> 9 RETURN_VALUE\n"},
338+
{"1 and 2 and 3 and 4", "eval", py.Code{
339+
Argcount: 0,
340+
Kwonlyargcount: 0,
341+
Nlocals: 0,
342+
Stacksize: 4,
343+
Flags: 64,
344+
Code: "\x64\x00\x00\x6f\x15\x00\x64\x01\x00\x6f\x15\x00\x64\x02\x00\x6f\x15\x00\x64\x03\x00\x53",
345+
Consts: []py.Object{py.Int(1), py.Int(2), py.Int(3), py.Int(4)},
346+
Names: []string{},
347+
Varnames: []string{},
348+
Freevars: []string{},
349+
Cellvars: []string{},
350+
Filename: "<string>",
351+
Name: "<module>",
352+
Firstlineno: 1,
353+
Lnotab: "",
354+
}, " 1 0 LOAD_CONST 0 (1)\n 3 JUMP_IF_FALSE_OR_POP 21\n 6 LOAD_CONST 1 (2)\n 9 JUMP_IF_FALSE_OR_POP 21\n 12 LOAD_CONST 2 (3)\n 15 JUMP_IF_FALSE_OR_POP 21\n 18 LOAD_CONST 3 (4)\n >> 21 RETURN_VALUE\n"},
355+
{"1 and 2", "eval", py.Code{
356+
Argcount: 0,
357+
Kwonlyargcount: 0,
358+
Nlocals: 0,
359+
Stacksize: 2,
360+
Flags: 64,
361+
Code: "\x64\x00\x00\x6f\x09\x00\x64\x01\x00\x53",
362+
Consts: []py.Object{py.Int(1), py.Int(2)},
363+
Names: []string{},
364+
Varnames: []string{},
365+
Freevars: []string{},
366+
Cellvars: []string{},
367+
Filename: "<string>",
368+
Name: "<module>",
369+
Firstlineno: 1,
370+
Lnotab: "",
371+
}, " 1 0 LOAD_CONST 0 (1)\n 3 JUMP_IF_FALSE_OR_POP 9\n 6 LOAD_CONST 1 (2)\n >> 9 RETURN_VALUE\n"},
372+
{"1 or 2", "eval", py.Code{
373+
Argcount: 0,
374+
Kwonlyargcount: 0,
375+
Nlocals: 0,
376+
Stacksize: 2,
377+
Flags: 64,
378+
Code: "\x64\x00\x00\x70\x09\x00\x64\x01\x00\x53",
379+
Consts: []py.Object{py.Int(1), py.Int(2)},
380+
Names: []string{},
381+
Varnames: []string{},
382+
Freevars: []string{},
383+
Cellvars: []string{},
384+
Filename: "<string>",
385+
Name: "<module>",
386+
Firstlineno: 1,
387+
Lnotab: "",
388+
}, " 1 0 LOAD_CONST 0 (1)\n 3 JUMP_IF_TRUE_OR_POP 9\n 6 LOAD_CONST 1 (2)\n >> 9 RETURN_VALUE\n"},
389+
{"1 or 2 or 3 or 4", "eval", py.Code{
390+
Argcount: 0,
391+
Kwonlyargcount: 0,
392+
Nlocals: 0,
393+
Stacksize: 4,
394+
Flags: 64,
395+
Code: "\x64\x00\x00\x70\x15\x00\x64\x01\x00\x70\x15\x00\x64\x02\x00\x70\x15\x00\x64\x03\x00\x53",
396+
Consts: []py.Object{py.Int(1), py.Int(2), py.Int(3), py.Int(4)},
397+
Names: []string{},
398+
Varnames: []string{},
399+
Freevars: []string{},
400+
Cellvars: []string{},
401+
Filename: "<string>",
402+
Name: "<module>",
403+
Firstlineno: 1,
404+
Lnotab: "",
405+
}, " 1 0 LOAD_CONST 0 (1)\n 3 JUMP_IF_TRUE_OR_POP 21\n 6 LOAD_CONST 1 (2)\n 9 JUMP_IF_TRUE_OR_POP 21\n 12 LOAD_CONST 2 (3)\n 15 JUMP_IF_TRUE_OR_POP 21\n 18 LOAD_CONST 3 (4)\n >> 21 RETURN_VALUE\n"},
321406
}

compile/make_compile_test.py

+7
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@
2929
('''not "a"''', "eval"),
3030
('''+"a"''', "eval"),
3131
('''-"a"''', "eval"),
32+
# Bool Ops
33+
('''1 and 2''', "eval"),
34+
('''1 and 2 and 3 and 4''', "eval"),
35+
('''1 and 2''', "eval"),
36+
('''1 or 2''', "eval"),
37+
('''1 or 2 or 3 or 4''', "eval"),
38+
3239
]
3340

3441
def string(s):

0 commit comments

Comments
 (0)