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

Lines changed: 85 additions & 12 deletions
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

Lines changed: 85 additions & 0 deletions
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

Lines changed: 7 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy