Skip to content

Commit b8b5f4e

Browse files
committed
compiler: implement comparators
1 parent 67b9193 commit b8b5f4e

File tree

4 files changed

+299
-7
lines changed

4 files changed

+299
-7
lines changed

compile/compile.go

+54-1
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,60 @@ func (c *compiler) Expr(expr ast.Expr) {
428428
// Left Expr
429429
// Ops []CmpOp
430430
// Comparators []Expr
431-
panic("FIXME compile: Compare not implemented")
431+
if len(node.Ops) != len(node.Comparators) {
432+
panic("compile: Unequal Ops and Comparators in Compare")
433+
}
434+
if len(node.Ops) == 0 {
435+
panic("compile: No Ops or Comparators in Compare")
436+
}
437+
c.Expr(node.Left)
438+
label := new(Label)
439+
for i := range node.Ops {
440+
last := i == len(node.Ops)-1
441+
c.Expr(node.Comparators[i])
442+
if !last {
443+
c.Op(vm.DUP_TOP)
444+
c.Op(vm.ROT_THREE)
445+
}
446+
op := node.Ops[i]
447+
var arg uint32
448+
switch op {
449+
case ast.Eq:
450+
arg = vm.PyCmp_EQ
451+
case ast.NotEq:
452+
arg = vm.PyCmp_NE
453+
case ast.Lt:
454+
arg = vm.PyCmp_LT
455+
case ast.LtE:
456+
arg = vm.PyCmp_LE
457+
case ast.Gt:
458+
arg = vm.PyCmp_GT
459+
case ast.GtE:
460+
arg = vm.PyCmp_GE
461+
case ast.Is:
462+
arg = vm.PyCmp_IS
463+
case ast.IsNot:
464+
arg = vm.PyCmp_IS_NOT
465+
case ast.In:
466+
arg = vm.PyCmp_IN
467+
case ast.NotIn:
468+
arg = vm.PyCmp_NOT_IN
469+
default:
470+
panic("compile: Unknown OpArg")
471+
}
472+
c.OpArg(vm.COMPARE_OP, arg)
473+
if !last {
474+
c.Jump(vm.JUMP_IF_FALSE_OR_POP, label)
475+
}
476+
}
477+
if len(node.Ops) > 1 {
478+
endLabel := new(Label)
479+
c.Jump(vm.JUMP_FORWARD, endLabel)
480+
c.Label(label)
481+
c.Op(vm.ROT_TWO)
482+
c.Op(vm.POP_TOP)
483+
c.Label(endLabel)
484+
}
432485
case *ast.Call:
433486
// Func Expr
434487
// Args []Expr

compile/compile_data_test.go

+226-5
Original file line numberDiff line numberDiff line change
@@ -505,21 +505,242 @@ var compileTestData = []struct {
505505
Firstlineno: 1,
506506
Lnotab: "",
507507
}, " 1 0 LOAD_CONST 0 (1)\n 3 LOAD_CONST 1 ('2')\n 6 BINARY_ADD\n 7 LOAD_CONST 2 ('3')\n 10 BINARY_MULTIPLY\n 11 RETURN_VALUE\n"},
508-
{"(\"true\" if 1+\"2\" else \"false\")+\"a\"", "eval", py.Code{
508+
{"(a if b else c)+0", "eval", py.Code{
509509
Argcount: 0,
510510
Kwonlyargcount: 0,
511511
Nlocals: 0,
512512
Stacksize: 2,
513513
Flags: 64,
514-
Code: "\x64\x00\x00\x64\x01\x00\x17\x72\x10\x00\x64\x02\x00\x6e\x03\x00\x64\x03\x00\x64\x04\x00\x17\x53",
515-
Consts: []py.Object{py.Int(1), py.String("2"), py.String("true"), py.String("false"), py.String("a")},
516-
Names: []string{},
514+
Code: "\x65\x00\x00\x72\x0c\x00\x65\x01\x00\x6e\x03\x00\x65\x02\x00\x64\x00\x00\x17\x53",
515+
Consts: []py.Object{py.Int(0)},
516+
Names: []string{"b", "a", "c"},
517+
Varnames: []string{},
518+
Freevars: []string{},
519+
Cellvars: []string{},
520+
Filename: "<string>",
521+
Name: "<module>",
522+
Firstlineno: 1,
523+
Lnotab: "",
524+
}, " 1 0 LOAD_NAME 0 (b)\n 3 POP_JUMP_IF_FALSE 12\n 6 LOAD_NAME 1 (a)\n 9 JUMP_FORWARD 3 (to 15)\n >> 12 LOAD_NAME 2 (c)\n >> 15 LOAD_CONST 0 (0)\n 18 BINARY_ADD\n 19 RETURN_VALUE\n"},
525+
{"a == b", "eval", py.Code{
526+
Argcount: 0,
527+
Kwonlyargcount: 0,
528+
Nlocals: 0,
529+
Stacksize: 2,
530+
Flags: 64,
531+
Code: "\x65\x00\x00\x65\x01\x00\x6b\x02\x00\x53",
532+
Consts: []py.Object{},
533+
Names: []string{"a", "b"},
534+
Varnames: []string{},
535+
Freevars: []string{},
536+
Cellvars: []string{},
537+
Filename: "<string>",
538+
Name: "<module>",
539+
Firstlineno: 1,
540+
Lnotab: "",
541+
}, " 1 0 LOAD_NAME 0 (a)\n 3 LOAD_NAME 1 (b)\n 6 COMPARE_OP 2 (==)\n 9 RETURN_VALUE\n"},
542+
{"a != b", "eval", py.Code{
543+
Argcount: 0,
544+
Kwonlyargcount: 0,
545+
Nlocals: 0,
546+
Stacksize: 2,
547+
Flags: 64,
548+
Code: "\x65\x00\x00\x65\x01\x00\x6b\x03\x00\x53",
549+
Consts: []py.Object{},
550+
Names: []string{"a", "b"},
551+
Varnames: []string{},
552+
Freevars: []string{},
553+
Cellvars: []string{},
554+
Filename: "<string>",
555+
Name: "<module>",
556+
Firstlineno: 1,
557+
Lnotab: "",
558+
}, " 1 0 LOAD_NAME 0 (a)\n 3 LOAD_NAME 1 (b)\n 6 COMPARE_OP 3 (!=)\n 9 RETURN_VALUE\n"},
559+
{"a < b", "eval", py.Code{
560+
Argcount: 0,
561+
Kwonlyargcount: 0,
562+
Nlocals: 0,
563+
Stacksize: 2,
564+
Flags: 64,
565+
Code: "\x65\x00\x00\x65\x01\x00\x6b\x00\x00\x53",
566+
Consts: []py.Object{},
567+
Names: []string{"a", "b"},
568+
Varnames: []string{},
569+
Freevars: []string{},
570+
Cellvars: []string{},
571+
Filename: "<string>",
572+
Name: "<module>",
573+
Firstlineno: 1,
574+
Lnotab: "",
575+
}, " 1 0 LOAD_NAME 0 (a)\n 3 LOAD_NAME 1 (b)\n 6 COMPARE_OP 0 (<)\n 9 RETURN_VALUE\n"},
576+
{"a <= b", "eval", py.Code{
577+
Argcount: 0,
578+
Kwonlyargcount: 0,
579+
Nlocals: 0,
580+
Stacksize: 2,
581+
Flags: 64,
582+
Code: "\x65\x00\x00\x65\x01\x00\x6b\x01\x00\x53",
583+
Consts: []py.Object{},
584+
Names: []string{"a", "b"},
585+
Varnames: []string{},
586+
Freevars: []string{},
587+
Cellvars: []string{},
588+
Filename: "<string>",
589+
Name: "<module>",
590+
Firstlineno: 1,
591+
Lnotab: "",
592+
}, " 1 0 LOAD_NAME 0 (a)\n 3 LOAD_NAME 1 (b)\n 6 COMPARE_OP 1 (<=)\n 9 RETURN_VALUE\n"},
593+
{"a > b", "eval", py.Code{
594+
Argcount: 0,
595+
Kwonlyargcount: 0,
596+
Nlocals: 0,
597+
Stacksize: 2,
598+
Flags: 64,
599+
Code: "\x65\x00\x00\x65\x01\x00\x6b\x04\x00\x53",
600+
Consts: []py.Object{},
601+
Names: []string{"a", "b"},
602+
Varnames: []string{},
603+
Freevars: []string{},
604+
Cellvars: []string{},
605+
Filename: "<string>",
606+
Name: "<module>",
607+
Firstlineno: 1,
608+
Lnotab: "",
609+
}, " 1 0 LOAD_NAME 0 (a)\n 3 LOAD_NAME 1 (b)\n 6 COMPARE_OP 4 (>)\n 9 RETURN_VALUE\n"},
610+
{"a >= b", "eval", py.Code{
611+
Argcount: 0,
612+
Kwonlyargcount: 0,
613+
Nlocals: 0,
614+
Stacksize: 2,
615+
Flags: 64,
616+
Code: "\x65\x00\x00\x65\x01\x00\x6b\x05\x00\x53",
617+
Consts: []py.Object{},
618+
Names: []string{"a", "b"},
619+
Varnames: []string{},
620+
Freevars: []string{},
621+
Cellvars: []string{},
622+
Filename: "<string>",
623+
Name: "<module>",
624+
Firstlineno: 1,
625+
Lnotab: "",
626+
}, " 1 0 LOAD_NAME 0 (a)\n 3 LOAD_NAME 1 (b)\n 6 COMPARE_OP 5 (>=)\n 9 RETURN_VALUE\n"},
627+
{"a is b", "eval", py.Code{
628+
Argcount: 0,
629+
Kwonlyargcount: 0,
630+
Nlocals: 0,
631+
Stacksize: 2,
632+
Flags: 64,
633+
Code: "\x65\x00\x00\x65\x01\x00\x6b\x08\x00\x53",
634+
Consts: []py.Object{},
635+
Names: []string{"a", "b"},
636+
Varnames: []string{},
637+
Freevars: []string{},
638+
Cellvars: []string{},
639+
Filename: "<string>",
640+
Name: "<module>",
641+
Firstlineno: 1,
642+
Lnotab: "",
643+
}, " 1 0 LOAD_NAME 0 (a)\n 3 LOAD_NAME 1 (b)\n 6 COMPARE_OP 8 (is)\n 9 RETURN_VALUE\n"},
644+
{"a is not b", "eval", py.Code{
645+
Argcount: 0,
646+
Kwonlyargcount: 0,
647+
Nlocals: 0,
648+
Stacksize: 2,
649+
Flags: 64,
650+
Code: "\x65\x00\x00\x65\x01\x00\x6b\x09\x00\x53",
651+
Consts: []py.Object{},
652+
Names: []string{"a", "b"},
653+
Varnames: []string{},
654+
Freevars: []string{},
655+
Cellvars: []string{},
656+
Filename: "<string>",
657+
Name: "<module>",
658+
Firstlineno: 1,
659+
Lnotab: "",
660+
}, " 1 0 LOAD_NAME 0 (a)\n 3 LOAD_NAME 1 (b)\n 6 COMPARE_OP 9 (is not)\n 9 RETURN_VALUE\n"},
661+
{"a in b", "eval", py.Code{
662+
Argcount: 0,
663+
Kwonlyargcount: 0,
664+
Nlocals: 0,
665+
Stacksize: 2,
666+
Flags: 64,
667+
Code: "\x65\x00\x00\x65\x01\x00\x6b\x06\x00\x53",
668+
Consts: []py.Object{},
669+
Names: []string{"a", "b"},
670+
Varnames: []string{},
671+
Freevars: []string{},
672+
Cellvars: []string{},
673+
Filename: "<string>",
674+
Name: "<module>",
675+
Firstlineno: 1,
676+
Lnotab: "",
677+
}, " 1 0 LOAD_NAME 0 (a)\n 3 LOAD_NAME 1 (b)\n 6 COMPARE_OP 6 (in)\n 9 RETURN_VALUE\n"},
678+
{"a not in b", "eval", py.Code{
679+
Argcount: 0,
680+
Kwonlyargcount: 0,
681+
Nlocals: 0,
682+
Stacksize: 2,
683+
Flags: 64,
684+
Code: "\x65\x00\x00\x65\x01\x00\x6b\x07\x00\x53",
685+
Consts: []py.Object{},
686+
Names: []string{"a", "b"},
687+
Varnames: []string{},
688+
Freevars: []string{},
689+
Cellvars: []string{},
690+
Filename: "<string>",
691+
Name: "<module>",
692+
Firstlineno: 1,
693+
Lnotab: "",
694+
}, " 1 0 LOAD_NAME 0 (a)\n 3 LOAD_NAME 1 (b)\n 6 COMPARE_OP 7 (not in)\n 9 RETURN_VALUE\n"},
695+
{"(a < b < c)+0", "eval", py.Code{
696+
Argcount: 0,
697+
Kwonlyargcount: 0,
698+
Nlocals: 0,
699+
Stacksize: 3,
700+
Flags: 64,
701+
Code: "\x65\x00\x00\x65\x01\x00\x04\x03\x6b\x00\x00\x6f\x17\x00\x65\x02\x00\x6b\x00\x00\x6e\x02\x00\x02\x01\x64\x00\x00\x17\x53",
702+
Consts: []py.Object{py.Int(0)},
703+
Names: []string{"a", "b", "c"},
704+
Varnames: []string{},
705+
Freevars: []string{},
706+
Cellvars: []string{},
707+
Filename: "<string>",
708+
Name: "<module>",
709+
Firstlineno: 1,
710+
Lnotab: "",
711+
}, " 1 0 LOAD_NAME 0 (a)\n 3 LOAD_NAME 1 (b)\n 6 DUP_TOP\n 7 ROT_THREE\n 8 COMPARE_OP 0 (<)\n 11 JUMP_IF_FALSE_OR_POP 23\n 14 LOAD_NAME 2 (c)\n 17 COMPARE_OP 0 (<)\n 20 JUMP_FORWARD 2 (to 25)\n >> 23 ROT_TWO\n 24 POP_TOP\n >> 25 LOAD_CONST 0 (0)\n 28 BINARY_ADD\n 29 RETURN_VALUE\n"},
712+
{"(a < b < c < d)+0", "eval", py.Code{
713+
Argcount: 0,
714+
Kwonlyargcount: 0,
715+
Nlocals: 0,
716+
Stacksize: 4,
717+
Flags: 64,
718+
Code: "\x65\x00\x00\x65\x01\x00\x04\x03\x6b\x00\x00\x6f\x22\x00\x65\x02\x00\x04\x03\x6b\x00\x00\x6f\x22\x00\x65\x03\x00\x6b\x00\x00\x6e\x02\x00\x02\x01\x64\x00\x00\x17\x53",
719+
Consts: []py.Object{py.Int(0)},
720+
Names: []string{"a", "b", "c", "d"},
721+
Varnames: []string{},
722+
Freevars: []string{},
723+
Cellvars: []string{},
724+
Filename: "<string>",
725+
Name: "<module>",
726+
Firstlineno: 1,
727+
Lnotab: "",
728+
}, " 1 0 LOAD_NAME 0 (a)\n 3 LOAD_NAME 1 (b)\n 6 DUP_TOP\n 7 ROT_THREE\n 8 COMPARE_OP 0 (<)\n 11 JUMP_IF_FALSE_OR_POP 34\n 14 LOAD_NAME 2 (c)\n 17 DUP_TOP\n 18 ROT_THREE\n 19 COMPARE_OP 0 (<)\n 22 JUMP_IF_FALSE_OR_POP 34\n 25 LOAD_NAME 3 (d)\n 28 COMPARE_OP 0 (<)\n 31 JUMP_FORWARD 2 (to 36)\n >> 34 ROT_TWO\n 35 POP_TOP\n >> 36 LOAD_CONST 0 (0)\n 39 BINARY_ADD\n 40 RETURN_VALUE\n"},
729+
{"(a < b < c < d < e)+0", "eval", py.Code{
730+
Argcount: 0,
731+
Kwonlyargcount: 0,
732+
Nlocals: 0,
733+
Stacksize: 5,
734+
Flags: 64,
735+
Code: "\x65\x00\x00\x65\x01\x00\x04\x03\x6b\x00\x00\x6f\x2d\x00\x65\x02\x00\x04\x03\x6b\x00\x00\x6f\x2d\x00\x65\x03\x00\x04\x03\x6b\x00\x00\x6f\x2d\x00\x65\x04\x00\x6b\x00\x00\x6e\x02\x00\x02\x01\x64\x00\x00\x17\x53",
736+
Consts: []py.Object{py.Int(0)},
737+
Names: []string{"a", "b", "c", "d", "e"},
517738
Varnames: []string{},
518739
Freevars: []string{},
519740
Cellvars: []string{},
520741
Filename: "<string>",
521742
Name: "<module>",
522743
Firstlineno: 1,
523744
Lnotab: "",
524-
}, " 1 0 LOAD_CONST 0 (1)\n 3 LOAD_CONST 1 ('2')\n 6 BINARY_ADD\n 7 POP_JUMP_IF_FALSE 16\n 10 LOAD_CONST 2 ('true')\n 13 JUMP_FORWARD 3 (to 19)\n >> 16 LOAD_CONST 3 ('false')\n >> 19 LOAD_CONST 4 ('a')\n 22 BINARY_ADD\n 23 RETURN_VALUE\n"},
745+
}, " 1 0 LOAD_NAME 0 (a)\n 3 LOAD_NAME 1 (b)\n 6 DUP_TOP\n 7 ROT_THREE\n 8 COMPARE_OP 0 (<)\n 11 JUMP_IF_FALSE_OR_POP 45\n 14 LOAD_NAME 2 (c)\n 17 DUP_TOP\n 18 ROT_THREE\n 19 COMPARE_OP 0 (<)\n 22 JUMP_IF_FALSE_OR_POP 45\n 25 LOAD_NAME 3 (d)\n 28 DUP_TOP\n 29 ROT_THREE\n 30 COMPARE_OP 0 (<)\n 33 JUMP_IF_FALSE_OR_POP 45\n 36 LOAD_NAME 4 (e)\n 39 COMPARE_OP 0 (<)\n 42 JUMP_FORWARD 2 (to 47)\n >> 45 ROT_TWO\n 46 POP_TOP\n >> 47 LOAD_CONST 0 (0)\n 50 BINARY_ADD\n 51 RETURN_VALUE\n"},
525746
}

compile/instructions.go

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import (
44
"github.com/ncw/gpython/vm"
55
)
66

7+
// FIXME detect if label is not in the instruction stream by setting
8+
// Pos to 0xFFFF say by default, ie we made a label but forgot to add
9+
// it. Or have a bool (Inserted) say.
10+
711
// Resolved or unresolved instruction stream
812
type Instructions []Instruction
913

compile/make_compile_test.py

+15-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,21 @@
4343
('''"1"+("2"*"3")''', "eval"),
4444
('''(1+"2")*"3"''', "eval"),
4545
# If expression
46-
('''("true" if 1+"2" else "false")+"a"''', "eval"),
46+
('''(a if b else c)+0''', "eval"),
47+
# Compare
48+
('''a == b''', "eval"),
49+
('''a != b''', "eval"),
50+
('''a < b''', "eval"),
51+
('''a <= b''', "eval"),
52+
('''a > b''', "eval"),
53+
('''a >= b''', "eval"),
54+
('''a is b''', "eval"),
55+
('''a is not b''', "eval"),
56+
('''a in b''', "eval"),
57+
('''a not in b''', "eval"),
58+
('''(a < b < c)+0''', "eval"),
59+
('''(a < b < c < d)+0''', "eval"),
60+
('''(a < b < c < d < e)+0''', "eval"),
4761

4862
]
4963

0 commit comments

Comments
 (0)