bitwise operators and boolean operators
This commit is contained in:
16
code/code.go
16
code/code.go
@@ -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{}},
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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: `
|
||||||
return fn(b) {
|
fn(a) {
|
||||||
return a + b
|
return fn(b) {
|
||||||
}
|
return a + b
|
||||||
}
|
}
|
||||||
`,
|
}
|
||||||
expectedConstants: []interface{}{
|
`,
|
||||||
[]code.Instructions{
|
constants: []interface{}{
|
||||||
code.Make(code.OpGetFree, 0),
|
Instructions("0000 OpGetFree 0\n0002 OpGetLocal 0\n0004 OpAdd\n0005 OpReturn\n"),
|
||||||
code.Make(code.OpGetLocal, 0),
|
Instructions("0000 OpGetLocal 0\n0002 OpClosure 0 1\n0006 OpReturn\n"),
|
||||||
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),
|
||||||
|
|||||||
@@ -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
|
||||||
return &object.Integer{Value: -value}
|
switch operator {
|
||||||
|
case "!":
|
||||||
|
return FALSE
|
||||||
|
case "~":
|
||||||
|
return &object.Integer{Value: ^value}
|
||||||
|
case "-":
|
||||||
|
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 "<=":
|
||||||
|
|||||||
@@ -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
19
examples/fizzbuzz.monkey
Normal 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
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
|||||||
@@ -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, ""},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,22 +28,28 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var precedences = map[token.TokenType]int{
|
var precedences = map[token.TokenType]int{
|
||||||
token.BIND: ASSIGN,
|
token.OR: OR,
|
||||||
token.ASSIGN: ASSIGN,
|
token.AND: AND,
|
||||||
token.EQ: EQUALS,
|
token.NOT: NOT,
|
||||||
token.NOT_EQ: EQUALS,
|
token.BIND: ASSIGN,
|
||||||
token.LT: LESSGREATER,
|
token.ASSIGN: ASSIGN,
|
||||||
token.GT: LESSGREATER,
|
token.EQ: EQUALS,
|
||||||
token.LTE: LESSGREATER,
|
token.NOT_EQ: EQUALS,
|
||||||
token.GTE: LESSGREATER,
|
token.LT: LESSGREATER,
|
||||||
token.PLUS: SUM,
|
token.GT: LESSGREATER,
|
||||||
token.MINUS: SUM,
|
token.LTE: LESSGREATER,
|
||||||
token.SLASH: PRODUCT,
|
token.GTE: LESSGREATER,
|
||||||
token.ASTERISK: PRODUCT,
|
token.BITWISE_OR: BITWISE_OR,
|
||||||
token.PERCENT: PRODUCT,
|
token.BITWISE_XOR: BITWISE_XOR,
|
||||||
token.LPAREN: CALL,
|
token.BITWISE_AND: BITWISE_AND,
|
||||||
token.LBRACKET: INDEX,
|
token.PLUS: SUM,
|
||||||
token.DOT: INDEX,
|
token.MINUS: SUM,
|
||||||
|
token.DIVIDE: PRODUCT,
|
||||||
|
token.MULTIPLY: PRODUCT,
|
||||||
|
token.MODULO: PRODUCT,
|
||||||
|
token.LPAREN: CALL,
|
||||||
|
token.LBRACKET: INDEX,
|
||||||
|
token.DOT: INDEX,
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@@ -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()
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 = "<="
|
||||||
|
|||||||
64
vm/vm.go
64
vm/vm.go
@@ -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 {
|
||||||
|
|||||||
@@ -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},
|
||||||
|
|||||||
Reference in New Issue
Block a user