bitwise operators and boolean operators
Some checks failed
Test / build (push) Waiting to run
Build / build (push) Has been cancelled

This commit is contained in:
Chuck Smith
2024-03-23 10:00:02 -04:00
parent cbb430b47d
commit ef8c8f8f04
13 changed files with 427 additions and 188 deletions

View File

@@ -26,6 +26,13 @@ const (
OpMul OpMul
OpDiv OpDiv
OpMod OpMod
OpOr
OpAnd
OpNot
OpBitwiseOR
OpBitwiseXOR
OpBitwiseAND
OpBitwiseNOT
OpTrue OpTrue
OpFalse OpFalse
OpEqual OpEqual
@@ -33,7 +40,6 @@ const (
OpGreaterThan OpGreaterThan
OpGreaterThanEqual OpGreaterThanEqual
OpMinus OpMinus
OpBang
OpJumpNotTruthy OpJumpNotTruthy
OpJump OpJump
OpNull OpNull
@@ -71,6 +77,13 @@ var definitions = map[Opcode]*Definition{
OpMul: {"OpMul", []int{}}, OpMul: {"OpMul", []int{}},
OpDiv: {"OpDiv", []int{}}, OpDiv: {"OpDiv", []int{}},
OpMod: {"OpMod", []int{}}, OpMod: {"OpMod", []int{}},
OpOr: {"OpOr", []int{}},
OpAnd: {"OpAnd", []int{}},
OpNot: {"OpNot", []int{}},
OpBitwiseOR: {"OpBitwiseOR", []int{}},
OpBitwiseXOR: {"OpBitwiseXOR", []int{}},
OpBitwiseAND: {"OpBitwiseAND", []int{}},
OpBitwiseNOT: {"OpBitwiseNOT", []int{}},
OpTrue: {"OpTrue", []int{}}, OpTrue: {"OpTrue", []int{}},
OpFalse: {"OpFalse", []int{}}, OpFalse: {"OpFalse", []int{}},
OpEqual: {"OpEqual", []int{}}, OpEqual: {"OpEqual", []int{}},
@@ -78,7 +91,6 @@ var definitions = map[Opcode]*Definition{
OpGreaterThan: {"OpGreaterThan", []int{}}, OpGreaterThan: {"OpGreaterThan", []int{}},
OpGreaterThanEqual: {"OpGreaterThanEqual", []int{}}, OpGreaterThanEqual: {"OpGreaterThanEqual", []int{}},
OpMinus: {"OpMinus", []int{}}, OpMinus: {"OpMinus", []int{}},
OpBang: {"OpBang", []int{}},
OpJumpNotTruthy: {"OpJumpNotTruthy", []int{2}}, OpJumpNotTruthy: {"OpJumpNotTruthy", []int{2}},
OpJump: {"OpJump", []int{2}}, OpJump: {"OpJump", []int{2}},
OpNull: {"OpNull", []int{}}, OpNull: {"OpNull", []int{}},

View File

@@ -141,6 +141,16 @@ func (c *Compiler) Compile(node ast.Node) error {
c.emit(code.OpDiv) c.emit(code.OpDiv)
case "%": case "%":
c.emit(code.OpMod) c.emit(code.OpMod)
case "|":
c.emit(code.OpBitwiseOR)
case "^":
c.emit(code.OpBitwiseXOR)
case "&":
c.emit(code.OpBitwiseAND)
case "||":
c.emit(code.OpOr)
case "&&":
c.emit(code.OpAnd)
case ">": case ">":
c.emit(code.OpGreaterThan) c.emit(code.OpGreaterThan)
case ">=": case ">=":
@@ -177,7 +187,9 @@ func (c *Compiler) Compile(node ast.Node) error {
switch node.Operator { switch node.Operator {
case "!": case "!":
c.emit(code.OpBang) c.emit(code.OpNot)
case "~":
c.emit(code.OpBitwiseNOT)
case "-": case "-":
c.emit(code.OpMinus) c.emit(code.OpMinus)
default: default:

View File

@@ -96,6 +96,38 @@ func TestIntegerArithmetic(t *testing.T) {
code.Make(code.OpPop), code.Make(code.OpPop),
}, },
}, },
{
input: "5 | 2",
expectedConstants: []interface{}{5, 2},
expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 0),
code.Make(code.OpConstant, 1),
code.Make(code.OpBitwiseOR),
code.Make(code.OpPop),
},
},
{
input: "5 ^ 2",
expectedConstants: []interface{}{5, 2},
expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 0),
code.Make(code.OpConstant, 1),
code.Make(code.OpBitwiseXOR),
code.Make(code.OpPop),
},
},
{
input: "5 & 2",
expectedConstants: []interface{}{5, 2},
expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 0),
code.Make(code.OpConstant, 1),
code.Make(code.OpBitwiseAND),
code.Make(code.OpPop),
},
},
} }
runCompilerTests(t, tests) runCompilerTests(t, tests)
@@ -207,12 +239,32 @@ func TestBooleanExpressions(t *testing.T) {
code.Make(code.OpPop), code.Make(code.OpPop),
}, },
}, },
{
input: "true && false",
expectedConstants: []interface{}{},
expectedInstructions: []code.Instructions{
code.Make(code.OpTrue),
code.Make(code.OpFalse),
code.Make(code.OpAnd),
code.Make(code.OpPop),
},
},
{
input: "true || false",
expectedConstants: []interface{}{},
expectedInstructions: []code.Instructions{
code.Make(code.OpTrue),
code.Make(code.OpFalse),
code.Make(code.OpOr),
code.Make(code.OpPop),
},
},
{ {
input: "!true", input: "!true",
expectedConstants: []interface{}{}, expectedConstants: []interface{}{},
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
code.Make(code.OpTrue), code.Make(code.OpTrue),
code.Make(code.OpBang), code.Make(code.OpNot),
code.Make(code.OpPop), code.Make(code.OpPop),
}, },
}, },
@@ -228,20 +280,20 @@ func TestConditionals(t *testing.T) {
if (true) { 10 }; 3333; if (true) { 10 }; 3333;
`, `,
constants: []interface{}{10, 3333}, constants: []interface{}{10, 3333},
instructions: "0000 LoadTrue\n0001 JumpIfFalse 10\n0004 LoadConstant 0\n0007 Jump 11\n0010 LoadNull\n0011 Pop\n0012 LoadConstant 1\n0015 Pop\n", instructions: "0000 OpTrue\n0001 OpJumpNotTruthy 10\n0004 OpConstant 0\n0007 OpJump 11\n0010 OpNull\n0011 OpPop\n0012 OpConstant 1\n0015 OpPop\n",
}, { }, {
input: ` input: `
if (true) { 10 } else { 20 }; 3333; if (true) { 10 } else { 20 }; 3333;
`, `,
constants: []interface{}{10, 20, 3333}, constants: []interface{}{10, 20, 3333},
instructions: "0000 LoadTrue\n0001 JumpIfFalse 10\n0004 LoadConstant 0\n0007 Jump 13\n0010 LoadConstant 1\n0013 Pop\n0014 LoadConstant 2\n0017 Pop\n", instructions: "0000 OpTrue\n0001 OpJumpNotTruthy 10\n0004 OpConstant 0\n0007 OpJump 13\n0010 OpConstant 1\n0013 OpPop\n0014 OpConstant 2\n0017 OpPop\n",
}, },
{ {
input: ` input: `
let x = 0; if (true) { x = 1; }; if (false) { x = 2; } x := 0; if (true) { x = 1; }; if (false) { x = 2; }
`, `,
constants: []interface{}{0, 1, 2}, constants: []interface{}{0, 1, 2},
instructions: "0000 LoadConstant 0\n0003 BindGlobal 0\n0006 Pop\n0007 LoadTrue\n0008 JumpIfFalse 20\n0011 LoadConstant 1\n0014 AssignGlobal 0\n0017 Jump 21\n0020 LoadNull\n0021 Pop\n0022 LoadFalse\n0023 JumpIfFalse 35\n0026 LoadConstant 2\n0029 AssignGlobal 0\n0032 Jump 36\n0035 LoadNull\n0036 Pop\n", instructions: "0000 OpConstant 0\n0003 OpSetGlobal 0\n0006 OpPop\n0007 OpTrue\n0008 OpJumpNotTruthy 20\n0011 OpConstant 1\n0014 OpAssignGlobal 0\n0017 OpJump 21\n0020 OpNull\n0021 OpPop\n0022 OpFalse\n0023 OpJumpNotTruthy 35\n0026 OpConstant 2\n0029 OpAssignGlobal 0\n0032 OpJump 36\n0035 OpNull\n0036 OpPop\n",
}, },
} }
@@ -255,7 +307,8 @@ func TestGlobalBindExpressions(t *testing.T) {
one := 1; one := 1;
two := 2; two := 2;
`, `,
instructions: "0000 LoadConstant 0\n0003 BindGlobal 0\n0006 Pop\n0007 LoadConstant 1\n0010 BindGlobal 1\n0013 Pop\n", constants: []interface{}{1, 2},
instructions: "0000 OpConstant 0\n0003 OpSetGlobal 0\n0006 OpPop\n0007 OpConstant 1\n0010 OpSetGlobal 1\n0013 OpPop\n",
}, },
{ {
input: ` input: `
@@ -263,7 +316,7 @@ func TestGlobalBindExpressions(t *testing.T) {
one; one;
`, `,
constants: []interface{}{1}, constants: []interface{}{1},
instructions: "0000 LoadConstant 0\n0003 BindGlobal 0\n0006 Pop\n0007 LoadGlobal 0\n0010 Pop\n", instructions: "0000 OpConstant 0\n0003 OpSetGlobal 0\n0006 OpPop\n0007 OpGetGlobal 0\n0010 OpPop\n",
}, },
{ {
input: ` input: `
@@ -272,7 +325,7 @@ func TestGlobalBindExpressions(t *testing.T) {
two; two;
`, `,
constants: []interface{}{1}, constants: []interface{}{1},
instructions: "0000 LoadConstant 0\n0003 BindGlobal 0\n0006 Pop\n0007 LoadGlobal 0\n0010 BindGlobal 1\n0013 Pop\n0014 LoadGlobal 1\n0017 Pop\n", instructions: "0000 OpConstant 0\n0003 OpSetGlobal 0\n0006 OpPop\n0007 OpGetGlobal 0\n0010 OpSetGlobal 1\n0013 OpPop\n0014 OpGetGlobal 1\n0017 OpPop\n",
}, },
} }
@@ -490,31 +543,20 @@ func TestFunctionsWithoutReturn(t *testing.T) {
} }
func TestClosures(t *testing.T) { func TestClosures(t *testing.T) {
tests := []compilerTestCase{ tests := []compilerTestCase2{
{ {
input: `fn(a) { input: `
fn(a) {
return fn(b) { return fn(b) {
return a + b return a + b
} }
} }
`, `,
expectedConstants: []interface{}{ constants: []interface{}{
[]code.Instructions{ Instructions("0000 OpGetFree 0\n0002 OpGetLocal 0\n0004 OpAdd\n0005 OpReturn\n"),
code.Make(code.OpGetFree, 0), Instructions("0000 OpGetLocal 0\n0002 OpClosure 0 1\n0006 OpReturn\n"),
code.Make(code.OpGetLocal, 0),
code.Make(code.OpAdd),
code.Make(code.OpReturn),
},
[]code.Instructions{
code.Make(code.OpGetLocal, 0),
code.Make(code.OpClosure, 0, 1),
code.Make(code.OpReturn),
},
},
expectedInstructions: []code.Instructions{
code.Make(code.OpClosure, 1, 0),
code.Make(code.OpPop),
}, },
instructions: "0000 OpClosure 1 0\n0004 OpPop\n",
}, },
{ {
input: ` input: `
@@ -526,93 +568,45 @@ func TestClosures(t *testing.T) {
} }
}; };
`, `,
expectedConstants: []interface{}{ constants: []interface{}{
[]code.Instructions{ Instructions("0000 OpGetFree 0\n0002 OpGetFree 1\n0004 OpAdd\n0005 OpGetLocal 0\n0007 OpAdd\n0008 OpReturn\n"),
code.Make(code.OpGetFree, 0), Instructions("0000 OpGetFree 0\n0002 OpGetLocal 0\n0004 OpClosure 0 2\n0008 OpReturn\n"),
code.Make(code.OpGetFree, 1), Instructions("0000 OpGetLocal 0\n0002 OpClosure 1 1\n0006 OpReturn\n"),
code.Make(code.OpAdd),
code.Make(code.OpGetLocal, 0),
code.Make(code.OpAdd),
code.Make(code.OpReturn),
},
[]code.Instructions{
code.Make(code.OpGetFree, 0),
code.Make(code.OpGetLocal, 0),
code.Make(code.OpClosure, 0, 2),
code.Make(code.OpReturn),
},
[]code.Instructions{
code.Make(code.OpGetLocal, 0),
code.Make(code.OpClosure, 1, 1),
code.Make(code.OpReturn),
},
},
expectedInstructions: []code.Instructions{
code.Make(code.OpClosure, 2, 0),
code.Make(code.OpPop),
}, },
instructions: "0000 OpClosure 2 0\n0004 OpPop\n",
}, },
{ {
input: ` input: `
let global = 55; global := 55;
fn() { fn() {
let a = 66; a := 66;
return fn() { return fn() {
let b = 77; b := 77;
return fn() { return fn() {
let c = 88; c := 88;
return global + a + b + c; return global + a + b + c;
} }
} }
} }
`, `,
expectedConstants: []interface{}{ constants: []interface{}{
55, 55,
66, 66,
77, 77,
88, 88,
[]code.Instructions{ Instructions("0000 OpConstant 3\n0003 OpSetLocal 0\n0005 OpPop\n0006 OpGetGlobal 0\n0009 OpGetFree 0\n0011 OpAdd\n0012 OpGetFree 1\n0014 OpAdd\n0015 OpGetLocal 0\n0017 OpAdd\n0018 OpReturn\n"),
code.Make(code.OpConstant, 3), Instructions("0000 OpConstant 2\n0003 OpSetLocal 0\n0005 OpPop\n0006 OpGetFree 0\n0008 OpGetLocal 0\n0010 OpClosure 4 2\n0014 OpReturn\n"),
code.Make(code.OpSetLocal, 0), Instructions("0000 OpConstant 1\n0003 OpSetLocal 0\n0005 OpPop\n0006 OpGetLocal 0\n0008 OpClosure 5 1\n0012 OpReturn\n"),
code.Make(code.OpGetGlobal, 0),
code.Make(code.OpGetFree, 0),
code.Make(code.OpAdd),
code.Make(code.OpGetFree, 1),
code.Make(code.OpAdd),
code.Make(code.OpGetLocal, 0),
code.Make(code.OpAdd),
code.Make(code.OpReturn),
},
[]code.Instructions{
code.Make(code.OpConstant, 2),
code.Make(code.OpSetLocal, 0),
code.Make(code.OpGetFree, 0),
code.Make(code.OpGetLocal, 0),
code.Make(code.OpClosure, 4, 2),
code.Make(code.OpReturn),
},
[]code.Instructions{
code.Make(code.OpConstant, 1),
code.Make(code.OpSetLocal, 0),
code.Make(code.OpGetLocal, 0),
code.Make(code.OpClosure, 5, 1),
code.Make(code.OpReturn),
},
},
expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 0),
code.Make(code.OpSetGlobal, 0),
code.Make(code.OpClosure, 6, 0),
code.Make(code.OpPop),
}, },
instructions: "0000 OpConstant 0\n0003 OpSetGlobal 0\n0006 OpPop\n0007 OpClosure 6 0\n0011 OpPop\n",
}, },
} }
runCompilerTests(t, tests) runCompilerTests2(t, tests)
} }
func TestCompilerScopes(t *testing.T) { func TestCompilerScopes(t *testing.T) {
@@ -667,9 +661,9 @@ func TestFunctionCalls(t *testing.T) {
input: `fn() { return 24 }();`, input: `fn() { return 24 }();`,
constants: []interface{}{ constants: []interface{}{
24, 24,
Instructions("0000 LoadConstant 0\n0003 Return\n"), Instructions("0000 OpConstant 0\n0003 OpReturn\n"),
}, },
instructions: "0000 MakeClosure 1 0\n0004 Call 0\n0006 Pop\n", instructions: "0000 OpClosure 1 0\n0004 OpCall 0\n0006 OpPop\n",
}, },
{ {
input: ` input: `
@@ -678,9 +672,9 @@ func TestFunctionCalls(t *testing.T) {
`, `,
constants: []interface{}{ constants: []interface{}{
24, 24,
Instructions("0000 SetSelf 0\n0002 LoadConstant 0\n0005 Return\n"), Instructions("0000 OpConstant 0\n0003 OpReturn\n"),
}, },
instructions: "0000 LoadGlobal 0\n0003 MakeClosure 1 1\n0007 BindGlobal 0\n0010 Pop\n0011 LoadGlobal 0\n0014 Call 0\n0016 Pop\n", instructions: "0000 OpClosure 1 0\n0004 OpSetGlobal 0\n0007 OpPop\n0008 OpGetGlobal 0\n0011 OpCall 0\n0013 OpPop\n",
}, },
{ {
input: ` input: `
@@ -688,10 +682,10 @@ func TestFunctionCalls(t *testing.T) {
oneArg(24); oneArg(24);
`, `,
constants: []interface{}{ constants: []interface{}{
Instructions("0000 SetSelf 0\n0002 LoadLocal 0\n0004 Return\n"), Instructions("0000 OpGetLocal 0\n0002 OpReturn\n"),
24, 24,
}, },
instructions: "0000 LoadGlobal 0\n0003 MakeClosure 0 1\n0007 BindGlobal 0\n0010 Pop\n0011 LoadGlobal 0\n0014 LoadConstant 1\n0017 Call 1\n0019 Pop\n", instructions: "0000 OpClosure 0 0\n0004 OpSetGlobal 0\n0007 OpPop\n0008 OpGetGlobal 0\n0011 OpConstant 1\n0014 OpCall 1\n0016 OpPop\n",
}, },
{ {
input: ` input: `
@@ -699,12 +693,12 @@ func TestFunctionCalls(t *testing.T) {
manyArg(24, 25, 26); manyArg(24, 25, 26);
`, `,
constants: []interface{}{ constants: []interface{}{
Instructions("0000 SetSelf 0\n0002 LoadLocal 0\n0004 Pop\n0005 LoadLocal 1\n0007 Pop\n0008 LoadLocal 2\n0010 Return\n"), Instructions("0000 OpGetLocal 0\n0002 OpPop\n0003 OpGetLocal 1\n0005 OpPop\n0006 OpGetLocal 2\n0008 OpReturn\n"),
24, 24,
25, 25,
26, 26,
}, },
instructions: "0000 LoadGlobal 0\n0003 MakeClosure 0 1\n0007 BindGlobal 0\n0010 Pop\n0011 LoadGlobal 0\n0014 LoadConstant 1\n0017 LoadConstant 2\n0020 LoadConstant 3\n0023 Call 3\n0025 Pop\n", instructions: "0000 OpClosure 0 0\n0004 OpSetGlobal 0\n0007 OpPop\n0008 OpGetGlobal 0\n0011 OpConstant 1\n0014 OpConstant 2\n0017 OpConstant 3\n0020 OpCall 3\n0022 OpPop\n",
}, },
} }
@@ -715,11 +709,11 @@ func TestAssignmentExpressions(t *testing.T) {
tests := []compilerTestCase2{ tests := []compilerTestCase2{
{ {
input: ` input: `
let x = 1 x := 1
x = 2 x = 2
`, `,
constants: []interface{}{1, 2}, constants: []interface{}{1, 2},
instructions: "0000 OpConstant 0\n0003 OpSetGlobal 0\n0006 OpConstant 1\n0009 OpAssignGlobal 0\n0012 OpPop\n", instructions: "0000 OpConstant 0\n0003 OpSetGlobal 0\n0006 OpPop\n0007 OpConstant 1\n0010 OpAssignGlobal 0\n0013 OpPop\n",
}, },
} }
@@ -730,7 +724,7 @@ func TestAssignmentStatementScopes(t *testing.T) {
tests := []compilerTestCase{ tests := []compilerTestCase{
{ {
input: ` input: `
let num = 0; num := 0;
fn() { num = 55; } fn() { num = 55; }
`, `,
expectedConstants: []interface{}{ expectedConstants: []interface{}{
@@ -746,13 +740,14 @@ func TestAssignmentStatementScopes(t *testing.T) {
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 0), code.Make(code.OpConstant, 0),
code.Make(code.OpSetGlobal, 0), code.Make(code.OpSetGlobal, 0),
code.Make(code.OpPop),
code.Make(code.OpClosure, 2, 0), code.Make(code.OpClosure, 2, 0),
code.Make(code.OpPop), code.Make(code.OpPop),
}, },
}, },
{ {
input: ` input: `
fn() { let num = 0; num = 55; } fn() { num := 0; num = 55; }
`, `,
expectedConstants: []interface{}{ expectedConstants: []interface{}{
0, 0,
@@ -760,6 +755,7 @@ func TestAssignmentStatementScopes(t *testing.T) {
[]code.Instructions{ []code.Instructions{
code.Make(code.OpConstant, 0), code.Make(code.OpConstant, 0),
code.Make(code.OpSetLocal, 0), code.Make(code.OpSetLocal, 0),
code.Make(code.OpPop),
code.Make(code.OpConstant, 1), code.Make(code.OpConstant, 1),
code.Make(code.OpAssignLocal, 0), code.Make(code.OpAssignLocal, 0),
code.Make(code.OpNull), code.Make(code.OpNull),
@@ -780,7 +776,7 @@ func TestLetStatementScopes(t *testing.T) {
tests := []compilerTestCase{ tests := []compilerTestCase{
{ {
input: ` input: `
let num = 55; num := 55;
fn() { return num } fn() { return num }
`, `,
expectedConstants: []interface{}{ expectedConstants: []interface{}{
@@ -793,6 +789,7 @@ func TestLetStatementScopes(t *testing.T) {
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 0), code.Make(code.OpConstant, 0),
code.Make(code.OpSetGlobal, 0), code.Make(code.OpSetGlobal, 0),
code.Make(code.OpPop),
code.Make(code.OpClosure, 1, 0), code.Make(code.OpClosure, 1, 0),
code.Make(code.OpPop), code.Make(code.OpPop),
}, },
@@ -800,7 +797,7 @@ func TestLetStatementScopes(t *testing.T) {
{ {
input: ` input: `
fn() { fn() {
let num = 55; num := 55;
return num return num
} }
`, `,
@@ -809,6 +806,7 @@ func TestLetStatementScopes(t *testing.T) {
[]code.Instructions{ []code.Instructions{
code.Make(code.OpConstant, 0), code.Make(code.OpConstant, 0),
code.Make(code.OpSetLocal, 0), code.Make(code.OpSetLocal, 0),
code.Make(code.OpPop),
code.Make(code.OpGetLocal, 0), code.Make(code.OpGetLocal, 0),
code.Make(code.OpReturn), code.Make(code.OpReturn),
}, },
@@ -821,8 +819,8 @@ func TestLetStatementScopes(t *testing.T) {
{ {
input: ` input: `
fn() { fn() {
let a = 55; a := 55;
let b = 77; b := 77;
return a + b return a + b
} }
`, `,
@@ -832,8 +830,10 @@ func TestLetStatementScopes(t *testing.T) {
[]code.Instructions{ []code.Instructions{
code.Make(code.OpConstant, 0), code.Make(code.OpConstant, 0),
code.Make(code.OpSetLocal, 0), code.Make(code.OpSetLocal, 0),
code.Make(code.OpPop),
code.Make(code.OpConstant, 1), code.Make(code.OpConstant, 1),
code.Make(code.OpSetLocal, 1), code.Make(code.OpSetLocal, 1),
code.Make(code.OpPop),
code.Make(code.OpGetLocal, 0), code.Make(code.OpGetLocal, 0),
code.Make(code.OpGetLocal, 1), code.Make(code.OpGetLocal, 1),
code.Make(code.OpAdd), code.Make(code.OpAdd),
@@ -847,8 +847,8 @@ func TestLetStatementScopes(t *testing.T) {
}, },
{ {
input: ` input: `
let a = 0; a := 0;
let a = a + 1; a := a + 1;
`, `,
expectedConstants: []interface{}{ expectedConstants: []interface{}{
0, 0,
@@ -857,10 +857,12 @@ func TestLetStatementScopes(t *testing.T) {
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 0), code.Make(code.OpConstant, 0),
code.Make(code.OpSetGlobal, 0), code.Make(code.OpSetGlobal, 0),
code.Make(code.OpPop),
code.Make(code.OpGetGlobal, 0), code.Make(code.OpGetGlobal, 0),
code.Make(code.OpConstant, 1), code.Make(code.OpConstant, 1),
code.Make(code.OpAdd), code.Make(code.OpAdd),
code.Make(code.OpSetGlobal, 0), code.Make(code.OpSetGlobal, 0),
code.Make(code.OpPop),
}, },
}, },
} }
@@ -912,7 +914,7 @@ func TestRecursiveFunctions(t *testing.T) {
tests := []compilerTestCase{ tests := []compilerTestCase{
{ {
input: ` input: `
let countDown = fn(x) { return countDown(x - 1); }; countDown := fn(x) { return countDown(x - 1); };
countDown(1); countDown(1);
`, `,
expectedConstants: []interface{}{ expectedConstants: []interface{}{
@@ -930,6 +932,7 @@ func TestRecursiveFunctions(t *testing.T) {
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
code.Make(code.OpClosure, 1, 0), code.Make(code.OpClosure, 1, 0),
code.Make(code.OpSetGlobal, 0), code.Make(code.OpSetGlobal, 0),
code.Make(code.OpPop),
code.Make(code.OpGetGlobal, 0), code.Make(code.OpGetGlobal, 0),
code.Make(code.OpConstant, 2), code.Make(code.OpConstant, 2),
code.Make(code.OpCall, 1), code.Make(code.OpCall, 1),
@@ -938,8 +941,8 @@ func TestRecursiveFunctions(t *testing.T) {
}, },
{ {
input: ` input: `
let wrapper = fn() { wrapper := fn() {
let countDown = fn(x) { return countDown(x - 1); }; countDown := fn(x) { return countDown(x - 1); };
return countDown(1); return countDown(1);
}; };
wrapper(); wrapper();
@@ -958,6 +961,7 @@ func TestRecursiveFunctions(t *testing.T) {
[]code.Instructions{ []code.Instructions{
code.Make(code.OpClosure, 1, 0), code.Make(code.OpClosure, 1, 0),
code.Make(code.OpSetLocal, 0), code.Make(code.OpSetLocal, 0),
code.Make(code.OpPop),
code.Make(code.OpGetLocal, 0), code.Make(code.OpGetLocal, 0),
code.Make(code.OpConstant, 2), code.Make(code.OpConstant, 2),
code.Make(code.OpCall, 1), code.Make(code.OpCall, 1),
@@ -967,6 +971,7 @@ func TestRecursiveFunctions(t *testing.T) {
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
code.Make(code.OpClosure, 3, 0), code.Make(code.OpClosure, 3, 0),
code.Make(code.OpSetGlobal, 0), code.Make(code.OpSetGlobal, 0),
code.Make(code.OpPop),
code.Make(code.OpGetGlobal, 0), code.Make(code.OpGetGlobal, 0),
code.Make(code.OpCall, 0), code.Make(code.OpCall, 0),
code.Make(code.OpPop), code.Make(code.OpPop),

View File

@@ -256,15 +256,22 @@ func nativeBoolToBooleanObject(input bool) object.Object {
func evalPrefixExpression(operator string, right object.Object) object.Object { func evalPrefixExpression(operator string, right object.Object) object.Object {
switch operator { switch operator {
case "!": case "!":
return evalBangOperatorExpression(right) if right.Type() == object.BOOLEAN_OBJ {
case "-": return evalBooleanPrefixOperatorExpression(operator, right)
return evalMinusPrefixOperatorExpression(right) }
return evalIntegerPrefixOperatorExpression(operator, right)
case "~", "-":
return evalIntegerPrefixOperatorExpression(operator, right)
default: default:
return newError("unknown operator: %s%s", operator, right.Type()) return newError("unknown operator: %s%s", operator, right.Type())
} }
} }
func evalBangOperatorExpression(right object.Object) object.Object { func evalBooleanPrefixOperatorExpression(operator string, right object.Object) object.Object {
if right.Type() != object.BOOLEAN_OBJ {
return newError("unknown operator: %s%s", operator, right.Type())
}
switch right { switch right {
case TRUE: case TRUE:
return FALSE return FALSE
@@ -277,17 +284,28 @@ func evalBangOperatorExpression(right object.Object) object.Object {
} }
} }
func evalMinusPrefixOperatorExpression(right object.Object) object.Object { func evalIntegerPrefixOperatorExpression(operator string, right object.Object) object.Object {
if right.Type() != object.INTEGER_OBJ { if right.Type() != object.INTEGER_OBJ {
return newError("unknown operator: -%s", right.Type()) return newError("unknown operator: -%s", right.Type())
} }
value := right.(*object.Integer).Value value := right.(*object.Integer).Value
switch operator {
case "!":
return FALSE
case "~":
return &object.Integer{Value: ^value}
case "-":
return &object.Integer{Value: -value} return &object.Integer{Value: -value}
default:
return newError("unknown operator: %s", operator)
}
} }
func evalInfixExpression(operator string, left, right object.Object) object.Object { func evalInfixExpression(operator string, left, right object.Object) object.Object {
switch { switch {
case left.Type() == object.BOOLEAN_OBJ && right.Type() == object.BOOLEAN_OBJ:
return evalBooleanInfixExpression(operator, left, right)
case left.Type() == object.INTEGER_OBJ && right.Type() == object.INTEGER_OBJ: case left.Type() == object.INTEGER_OBJ && right.Type() == object.INTEGER_OBJ:
return evalIntegerInfixExpression(operator, left, right) return evalIntegerInfixExpression(operator, left, right)
case left.Type() == object.STRING_OBJ && right.Type() == object.STRING_OBJ: case left.Type() == object.STRING_OBJ && right.Type() == object.STRING_OBJ:
@@ -303,6 +321,24 @@ func evalInfixExpression(operator string, left, right object.Object) object.Obje
} }
} }
func evalBooleanInfixExpression(operator string, left, right object.Object) object.Object {
leftVal := left.(*object.Boolean).Value
rightVal := right.(*object.Boolean).Value
switch operator {
case "==":
return nativeBoolToBooleanObject(left == right)
case "!=":
return nativeBoolToBooleanObject(leftVal != rightVal)
case "&&":
return &object.Boolean{Value: leftVal && rightVal}
case "||":
return &object.Boolean{Value: leftVal || rightVal}
default:
return newError("unknown operator: %s %s %s", left.Type(), operator, right.Type())
}
}
func evalStringInfixExpression(operator string, left object.Object, right object.Object) object.Object { func evalStringInfixExpression(operator string, left object.Object, right object.Object) object.Object {
leftVal := left.(*object.String).Value leftVal := left.(*object.String).Value
rightVal := right.(*object.String).Value rightVal := right.(*object.String).Value
@@ -338,6 +374,12 @@ func evalIntegerInfixExpression(operator string, left, right object.Object) obje
return &object.Integer{Value: leftVal / rightVal} return &object.Integer{Value: leftVal / rightVal}
case "%": case "%":
return &object.Integer{Value: leftVal % rightVal} return &object.Integer{Value: leftVal % rightVal}
case "|":
return &object.Integer{Value: leftVal | rightVal}
case "^":
return &object.Integer{Value: leftVal ^ rightVal}
case "&":
return &object.Integer{Value: leftVal & rightVal}
case "<": case "<":
return nativeBoolToBooleanObject(leftVal < rightVal) return nativeBoolToBooleanObject(leftVal < rightVal)
case "<=": case "<=":

View File

@@ -13,7 +13,7 @@ import (
func TestEvalIntegerExpression(t *testing.T) { func TestEvalIntegerExpression(t *testing.T) {
tests := []struct { tests := []struct {
input string input string
expected int64 expected interface{}
}{ }{
{"5", 5}, {"5", 5},
{"10", 10}, {"10", 10},
@@ -30,12 +30,21 @@ func TestEvalIntegerExpression(t *testing.T) {
{"3 * 3 * 3 + 10", 37}, {"3 * 3 * 3 + 10", 37},
{"3 * (3 * 3) + 10", 37}, {"3 * (3 * 3) + 10", 37},
{"(5 + 10 * 2 + 15 / 3) * 2 + -10", 50}, {"(5 + 10 * 2 + 15 / 3) * 2 + -10", 50},
{"!1", false},
{"~1", -2},
{"5 % 2", 1}, {"5 % 2", 1},
{"1 | 2", 3},
{"2 ^ 4", 6},
{"3 & 6", 2},
} }
for _, tt := range tests { for _, tt := range tests {
evaluated := testEval(tt.input) evaluated := testEval(tt.input)
testIntegerObject(t, evaluated, tt.expected) if expected, ok := tt.expected.(int64); ok {
testIntegerObject(t, evaluated, expected)
} else if expected, ok := tt.expected.(bool); ok {
testBooleanObject(t, evaluated, expected)
}
} }
} }
@@ -46,6 +55,16 @@ func TestEvalBooleanExpression(t *testing.T) {
}{ }{
{"true", true}, {"true", true},
{"false", false}, {"false", false},
{"!true", false},
{"!false", true},
{"true && true", true},
{"false && true", false},
{"true && false", false},
{"false && false", false},
{"true || true", true},
{"false || true", true},
{"true || false", true},
{"false || false", false},
{"1 < 2", true}, {"1 < 2", true},
{"1 > 2", false}, {"1 > 2", false},
{"1 < 1", false}, {"1 < 1", false},
@@ -78,25 +97,6 @@ func TestEvalBooleanExpression(t *testing.T) {
} }
} }
func TestBangOperator(t *testing.T) {
tests := []struct {
input string
expected bool
}{
{"!true", false},
{"!false", true},
{"!5", false},
{"!!true", true},
{"!!false", false},
{"!!5", true},
}
for _, tt := range tests {
evaluated := testEval(tt.input)
testBooleanObject(t, evaluated, tt.expected)
}
}
func TestIfElseExpression(t *testing.T) { func TestIfElseExpression(t *testing.T) {
tests := []struct { tests := []struct {
input string input string

19
examples/fizzbuzz.monkey Normal file
View File

@@ -0,0 +1,19 @@
#!./monkey-lang
test := fn(n) {
if (n % 15 == 0) {
return "FizzBuzz"
} else if (n % 5 == 0) {
return "Buzz"
} else if (n % 3 == 0) {
return "Fizz"
} else {
return str(n)
}
}
n := 1
while (n <= 100) {
print(test(n))
n = n + 1
}

View File

@@ -74,7 +74,7 @@ func (l *Lexer) NextToken() token.Token {
Literal: literal, Literal: literal,
} }
} else { } else {
tok = newToken(token.BANG, l.ch) tok = newToken(token.NOT, l.ch)
} }
case '/': case '/':
@@ -83,12 +83,34 @@ func (l *Lexer) NextToken() token.Token {
tok.Type = token.COMMENT tok.Type = token.COMMENT
tok.Literal = l.readLine() tok.Literal = l.readLine()
} else { } else {
tok = newToken(token.SLASH, l.ch) tok = newToken(token.DIVIDE, l.ch)
} }
case '*': case '*':
tok = newToken(token.ASTERISK, l.ch) tok = newToken(token.MULTIPLY, l.ch)
case '%': case '%':
tok = newToken(token.PERCENT, l.ch) tok = newToken(token.MODULO, l.ch)
case '&':
if l.peekChar() == '&' {
ch := l.ch
l.readChar()
literal := string(ch) + string(l.ch)
tok = token.Token{Type: token.AND, Literal: literal}
} else {
tok = newToken(token.BITWISE_AND, l.ch)
}
case '|':
if l.peekChar() == '|' {
ch := l.ch
l.readChar()
literal := string(ch) + string(l.ch)
tok = token.Token{Type: token.OR, Literal: literal}
} else {
tok = newToken(token.BITWISE_OR, l.ch)
}
case '^':
tok = newToken(token.BITWISE_XOR, l.ch)
case '~':
tok = newToken(token.BITWISE_NOT, l.ch)
case '<': case '<':
if l.peekChar() == '=' { if l.peekChar() == '=' {
l.readChar() l.readChar()

View File

@@ -33,6 +33,8 @@ func TestNextToken(t *testing.T) {
[1, 2]; [1, 2];
{"foo": "bar"} {"foo": "bar"}
d.foo d.foo
&|^~
!&&||
` `
tests := []struct { tests := []struct {
@@ -74,10 +76,10 @@ func TestNextToken(t *testing.T) {
{token.RPAREN, ")"}, {token.RPAREN, ")"},
{token.SEMICOLON, ";"}, {token.SEMICOLON, ";"},
{token.BANG, "!"}, {token.NOT, "!"},
{token.MINUS, "-"}, {token.MINUS, "-"},
{token.SLASH, "/"}, {token.DIVIDE, "/"},
{token.ASTERISK, "*"}, {token.MULTIPLY, "*"},
{token.INT, "5"}, {token.INT, "5"},
{token.SEMICOLON, ";"}, {token.SEMICOLON, ";"},
{token.INT, "5"}, {token.INT, "5"},
@@ -137,6 +139,13 @@ func TestNextToken(t *testing.T) {
{token.IDENT, "d"}, {token.IDENT, "d"},
{token.DOT, "."}, {token.DOT, "."},
{token.IDENT, "foo"}, {token.IDENT, "foo"},
{token.BitwiseAND, "&"},
{token.BitwiseOR, "|"},
{token.BitwiseXOR, "^"},
{token.BitwiseNOT, "~"},
{token.NOT, "!"},
{token.AND, "&&"},
{token.OR, "||"},
{token.EOF, ""}, {token.EOF, ""},
} }

View File

@@ -11,9 +11,15 @@ import (
const ( const (
_ int = iota _ int = iota
LOWEST LOWEST
OR
AND
NOT
ASSIGN // := or = ASSIGN // := or =
EQUALS // == EQUALS // ==
LESSGREATER // > or < LESSGREATER // > or <
BITWISE_OR // |
BITWISE_XOR // ^
BITWISE_AND // &
SUM // + or - SUM // + or -
PRODUCT // * or / or % PRODUCT // * or / or %
PREFIX // -X or !X PREFIX // -X or !X
@@ -22,6 +28,9 @@ const (
) )
var precedences = map[token.TokenType]int{ var precedences = map[token.TokenType]int{
token.OR: OR,
token.AND: AND,
token.NOT: NOT,
token.BIND: ASSIGN, token.BIND: ASSIGN,
token.ASSIGN: ASSIGN, token.ASSIGN: ASSIGN,
token.EQ: EQUALS, token.EQ: EQUALS,
@@ -30,11 +39,14 @@ var precedences = map[token.TokenType]int{
token.GT: LESSGREATER, token.GT: LESSGREATER,
token.LTE: LESSGREATER, token.LTE: LESSGREATER,
token.GTE: LESSGREATER, token.GTE: LESSGREATER,
token.BITWISE_OR: BITWISE_OR,
token.BITWISE_XOR: BITWISE_XOR,
token.BITWISE_AND: BITWISE_AND,
token.PLUS: SUM, token.PLUS: SUM,
token.MINUS: SUM, token.MINUS: SUM,
token.SLASH: PRODUCT, token.DIVIDE: PRODUCT,
token.ASTERISK: PRODUCT, token.MULTIPLY: PRODUCT,
token.PERCENT: PRODUCT, token.MODULO: PRODUCT,
token.LPAREN: CALL, token.LPAREN: CALL,
token.LBRACKET: INDEX, token.LBRACKET: INDEX,
token.DOT: INDEX, token.DOT: INDEX,
@@ -65,7 +77,6 @@ func New(l *lexer.Lexer) *Parser {
p.prefixParseFns = make(map[token.TokenType]prefixParseFn) p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
p.registerPrefix(token.IDENT, p.parseIdentifier) p.registerPrefix(token.IDENT, p.parseIdentifier)
p.registerPrefix(token.INT, p.parseIntegerLiteral) p.registerPrefix(token.INT, p.parseIntegerLiteral)
p.registerPrefix(token.BANG, p.parsePrefixExpression)
p.registerPrefix(token.MINUS, p.parsePrefixExpression) p.registerPrefix(token.MINUS, p.parsePrefixExpression)
p.registerPrefix(token.TRUE, p.parseBoolean) p.registerPrefix(token.TRUE, p.parseBoolean)
p.registerPrefix(token.FALSE, p.parseBoolean) p.registerPrefix(token.FALSE, p.parseBoolean)
@@ -81,9 +92,9 @@ func New(l *lexer.Lexer) *Parser {
p.infixParseFns = make(map[token.TokenType]infixParseFn) p.infixParseFns = make(map[token.TokenType]infixParseFn)
p.registerInfix(token.PLUS, p.parseInfixExpression) p.registerInfix(token.PLUS, p.parseInfixExpression)
p.registerInfix(token.MINUS, p.parseInfixExpression) p.registerInfix(token.MINUS, p.parseInfixExpression)
p.registerInfix(token.SLASH, p.parseInfixExpression) p.registerInfix(token.DIVIDE, p.parseInfixExpression)
p.registerInfix(token.ASTERISK, p.parseInfixExpression) p.registerInfix(token.MULTIPLY, p.parseInfixExpression)
p.registerInfix(token.PERCENT, p.parseInfixExpression) p.registerInfix(token.MODULO, p.parseInfixExpression)
p.registerInfix(token.EQ, p.parseInfixExpression) p.registerInfix(token.EQ, p.parseInfixExpression)
p.registerInfix(token.NOT_EQ, p.parseInfixExpression) p.registerInfix(token.NOT_EQ, p.parseInfixExpression)
p.registerInfix(token.LT, p.parseInfixExpression) p.registerInfix(token.LT, p.parseInfixExpression)
@@ -96,6 +107,15 @@ func New(l *lexer.Lexer) *Parser {
p.registerInfix(token.ASSIGN, p.parseAssignmentExpression) p.registerInfix(token.ASSIGN, p.parseAssignmentExpression)
p.registerInfix(token.DOT, p.parseSelectorExpression) p.registerInfix(token.DOT, p.parseSelectorExpression)
p.registerPrefix(token.BITWISE_NOT, p.parsePrefixExpression)
p.registerInfix(token.BITWISE_OR, p.parseInfixExpression)
p.registerInfix(token.BITWISE_XOR, p.parseInfixExpression)
p.registerInfix(token.BITWISE_AND, p.parseInfixExpression)
p.registerPrefix(token.NOT, p.parsePrefixExpression)
p.registerInfix(token.OR, p.parseInfixExpression)
p.registerInfix(token.AND, p.parseInfixExpression)
// Read two tokens, so curToken and peekToken are both set // Read two tokens, so curToken and peekToken are both set
p.nextToken() p.nextToken()
p.nextToken() p.nextToken()

View File

@@ -138,6 +138,7 @@ func TestParsingPrefixExpressions(t *testing.T) {
{"-foobar;", "-", "foobar"}, {"-foobar;", "-", "foobar"},
{"!true;", "!", true}, {"!true;", "!", true},
{"!false;", "!", false}, {"!false;", "!", false},
{"~5;", "~", 5},
} }
for _, tt := range prefixTests { for _, tt := range prefixTests {

View File

@@ -24,10 +24,31 @@ const (
ASSIGN = "=" ASSIGN = "="
PLUS = "+" PLUS = "+"
MINUS = "-" MINUS = "-"
BANG = "!" MULTIPLY = "*"
ASTERISK = "*" DIVIDE = "/"
SLASH = "/" MODULO = "%"
PERCENT = "%"
// Bitwise / Logical operators
// BITWISE_AND AND
BITWISE_AND = "&"
// BITWISE_OR OR
BITWISE_OR = "|"
// BITWISE_XOR XOR
BITWISE_XOR = "^"
// BITWISE_NOT NOT
BITWISE_NOT = "~"
//
// Comparision operators
//
// NOT the not operator
NOT = "!"
// AND the and operator
AND = "&&"
// OR the or operator
OR = "||"
LT = "<" LT = "<"
LTE = "<=" LTE = "<="

View File

@@ -112,7 +112,8 @@ func (vm *VM) Run() error {
return err return err
} }
case code.OpAdd, code.OpSub, code.OpMul, code.OpDiv, code.OpMod: case code.OpAdd, code.OpSub, code.OpMul, code.OpDiv, code.OpMod, code.OpOr,
code.OpAnd, code.OpBitwiseOR, code.OpBitwiseXOR, code.OpBitwiseAND:
err := vm.executeBinaryOperation(op) err := vm.executeBinaryOperation(op)
if err != nil { if err != nil {
return err return err
@@ -139,8 +140,14 @@ func (vm *VM) Run() error {
return err return err
} }
case code.OpBang: case code.OpNot:
err := vm.executeBangOperator() err := vm.executeNotOperator()
if err != nil {
return err
}
case code.OpBitwiseNOT:
err := vm.executeBitwiseNotOperator()
if err != nil { if err != nil {
return err return err
} }
@@ -516,18 +523,38 @@ func (vm *VM) executeBinaryOperation(op code.Opcode) error {
left := vm.pop() left := vm.pop()
leftType := left.Type() leftType := left.Type()
rightRight := right.Type() rightType := right.Type()
switch { switch {
case leftType == object.INTEGER_OBJ && rightRight == object.INTEGER_OBJ: case leftType == object.BOOLEAN_OBJ && rightType == object.BOOLEAN_OBJ:
return vm.executeBinaryBooleanOperation(op, left, right)
case leftType == object.INTEGER_OBJ && rightType == object.INTEGER_OBJ:
return vm.executeBinaryIntegerOperation(op, left, right) return vm.executeBinaryIntegerOperation(op, left, right)
case leftType == object.STRING_OBJ && rightRight == object.STRING_OBJ: case leftType == object.STRING_OBJ && rightType == object.STRING_OBJ:
return vm.executeBinaryStringOperation(op, left, right) return vm.executeBinaryStringOperation(op, left, right)
default: default:
return fmt.Errorf("unsupported types for binary operation: %s %s", leftType, rightRight) return fmt.Errorf("unsupported types for binary operation: %s %s", leftType, rightType)
} }
} }
func (vm *VM) executeBinaryBooleanOperation(op code.Opcode, left, right object.Object) error {
leftValue := left.(*object.Boolean).Value
rightValue := right.(*object.Boolean).Value
var result bool
switch op {
case code.OpOr:
result = leftValue || rightValue
case code.OpAnd:
result = leftValue && rightValue
default:
return fmt.Errorf("unknown boolean operator: %d", op)
}
return vm.push(&object.Boolean{Value: result})
}
func (vm *VM) executeBinaryIntegerOperation(op code.Opcode, left, right object.Object) error { func (vm *VM) executeBinaryIntegerOperation(op code.Opcode, left, right object.Object) error {
leftValue := left.(*object.Integer).Value leftValue := left.(*object.Integer).Value
rightValue := right.(*object.Integer).Value rightValue := right.(*object.Integer).Value
@@ -545,6 +572,12 @@ func (vm *VM) executeBinaryIntegerOperation(op code.Opcode, left, right object.O
result = leftValue / rightValue result = leftValue / rightValue
case code.OpMod: case code.OpMod:
result = leftValue % rightValue result = leftValue % rightValue
case code.OpBitwiseOR:
result = leftValue | rightValue
case code.OpBitwiseXOR:
result = leftValue ^ rightValue
case code.OpBitwiseAND:
result = leftValue & rightValue
default: default:
return fmt.Errorf("unknown integer operator: %d", op) return fmt.Errorf("unknown integer operator: %d", op)
@@ -604,7 +637,15 @@ func (vm *VM) executeIntegerComparison(op code.Opcode, left, right object.Object
} }
} }
func (vm *VM) executeBangOperator() error { func (vm *VM) executeBitwiseNotOperator() error {
operand := vm.pop()
if i, ok := operand.(*object.Integer); ok {
return vm.push(&object.Integer{Value: ^i.Value})
}
return fmt.Errorf("expected int got=%T", operand)
}
func (vm *VM) executeNotOperator() error {
operand := vm.pop() operand := vm.pop()
switch operand { switch operand {
@@ -622,12 +663,11 @@ func (vm *VM) executeBangOperator() error {
func (vm *VM) executeMinusOperator() error { func (vm *VM) executeMinusOperator() error {
operand := vm.pop() operand := vm.pop()
if operand.Type() != object.INTEGER_OBJ { if i, ok := operand.(*object.Integer); ok {
return fmt.Errorf("unsupported type for negation: %s", operand.Type()) return vm.push(&object.Integer{Value: -i.Value})
} }
value := operand.(*object.Integer).Value return fmt.Errorf("expected int got=%T", operand)
return vm.push(&object.Integer{Value: -value})
} }
func (vm *VM) executeCall(numArgs int) error { func (vm *VM) executeCall(numArgs int) error {

View File

@@ -224,7 +224,12 @@ func TestIntegerArithmetic(t *testing.T) {
{"-10", -10}, {"-10", -10},
{"-50 + 100 + -50", 0}, {"-50 + 100 + -50", 0},
{"(5 + 10 * 2 + 15 / 3) * 2 + -10", 50}, {"(5 + 10 * 2 + 15 / 3) * 2 + -10", 50},
{"!1", false},
{"~1", -2},
{"5 % 2", 1}, {"5 % 2", 1},
{"1 | 2", 3},
{"2 ^ 4", 6},
{"3 & 6", 2},
} }
runVmTests(t, tests) runVmTests(t, tests)
@@ -237,6 +242,37 @@ func TestBooleanExpressions(t *testing.T) {
{"null", nil}, {"null", nil},
{"!true", false}, {"!true", false},
{"!false", true}, {"!false", true},
{"true && true", true},
{"false && true", false},
{"true && false", false},
{"false && false", false},
{"true || true", true},
{"false || true", true},
{"true || false", true},
{"false || false", false},
{"1 < 2", true},
{"1 > 2", false},
{"1 < 1", false},
{"1 > 1", false},
{"1 == 1", true},
{"1 != 1", false},
{"1 == 2", false},
{"1 != 2", true},
{"true == true", true},
{"false == false", true},
{"true == false", false},
{"true != false", true},
{"false != true", true},
{"(1 < 2) == true", true},
{"(1 < 2) == false", false},
{"(1 > 2) == true", false},
{"(1 > 2) == false", true},
{"(1 <= 2) == true", true},
{"(1 <= 2) == false", false},
{"(1 >= 2) == true", false},
{"(1 >= 2) == false", true},
{"!true", false},
{"!false", true},
{"!5", false}, {"!5", false},
{"!!true", true}, {"!!true", true},
{"!!false", false}, {"!!false", false},