diff --git a/code/code.go b/code/code.go index 24d0e4b..8cc0229 100644 --- a/code/code.go +++ b/code/code.go @@ -33,6 +33,8 @@ const ( OpBitwiseXOR OpBitwiseAND OpBitwiseNOT + OpLeftShift + OpRightShift OpTrue OpFalse OpEqual @@ -84,6 +86,8 @@ var definitions = map[Opcode]*Definition{ OpBitwiseXOR: {"OpBitwiseXOR", []int{}}, OpBitwiseAND: {"OpBitwiseAND", []int{}}, OpBitwiseNOT: {"OpBitwiseNOT", []int{}}, + OpLeftShift: {"OpLeftShift", []int{}}, + OpRightShift: {"OpRightShift", []int{}}, OpTrue: {"OpTrue", []int{}}, OpFalse: {"OpFalse", []int{}}, OpEqual: {"OpEqual", []int{}}, diff --git a/compiler/compiler.go b/compiler/compiler.go index f3b823b..dbdedd0 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -148,6 +148,10 @@ func (c *Compiler) Compile(node ast.Node) error { c.emit(code.OpBitwiseXOR) case "&": c.emit(code.OpBitwiseAND) + case "<<": + c.emit(code.OpLeftShift) + case ">>": + c.emit(code.OpRightShift) case "||": c.emit(code.OpOr) case "&&": diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go index f030019..35c25e9 100644 --- a/compiler/compiler_test.go +++ b/compiler/compiler_test.go @@ -87,6 +87,26 @@ func TestIntegerArithmetic(t *testing.T) { code.Make(code.OpPop), }, }, + { + input: "1 << 2", + expectedConstants: []interface{}{1, 2}, + expectedInstructions: []code.Instructions{ + code.Make(code.OpConstant, 0), + code.Make(code.OpConstant, 1), + code.Make(code.OpLeftShift), + code.Make(code.OpPop), + }, + }, + { + input: "4 >> 2", + expectedConstants: []interface{}{4, 2}, + expectedInstructions: []code.Instructions{ + code.Make(code.OpConstant, 0), + code.Make(code.OpConstant, 1), + code.Make(code.OpRightShift), + code.Make(code.OpPop), + }, + }, { input: "-1", expectedConstants: []interface{}{1}, diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go index a4c27eb..a31f7fe 100644 --- a/evaluator/evaluator.go +++ b/evaluator/evaluator.go @@ -434,6 +434,10 @@ func evalIntegerInfixExpression(operator string, left, right object.Object) obje return &object.Integer{Value: leftVal ^ rightVal} case "&": return &object.Integer{Value: leftVal & rightVal} + case "<<": + return &object.Integer{Value: leftVal << uint64(rightVal)} + case ">>": + return &object.Integer{Value: leftVal >> uint64(rightVal)} case "<": return nativeBoolToBooleanObject(leftVal < rightVal) case "<=": diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go index 51347fa..40837ef 100644 --- a/evaluator/evaluator_test.go +++ b/evaluator/evaluator_test.go @@ -38,6 +38,8 @@ func TestEvalExpressions(t *testing.T) { {"3 & 6", 2}, {`" " * 4`, " "}, {`4 * " "`, " "}, + {"1 << 2", 4}, + {"4 >> 2", 1}, } for _, tt := range tests { diff --git a/lexer/lexer.go b/lexer/lexer.go index 80cb710..2753ae8 100644 --- a/lexer/lexer.go +++ b/lexer/lexer.go @@ -116,6 +116,11 @@ func (l *Lexer) NextToken() token.Token { l.readChar() tok = newToken(token.LTE, l.ch) tok.Literal = "<=" + } else if l.peekChar() == '<' { + ch := l.ch + l.readChar() + literal := string(ch) + string(l.ch) + tok = token.Token{Type: token.LEFT_SHIFT, Literal: literal} } else { tok = newToken(token.LT, l.ch) } @@ -124,6 +129,11 @@ func (l *Lexer) NextToken() token.Token { l.readChar() tok = newToken(token.GTE, l.ch) tok.Literal = ">=" + } else if l.peekChar() == '>' { + ch := l.ch + l.readChar() + literal := string(ch) + string(l.ch) + tok = token.Token{Type: token.RIGHT_SHIFT, Literal: literal} } else { tok = newToken(token.GT, l.ch) } diff --git a/lexer/lexer_test.go b/lexer/lexer_test.go index c841ad6..1393cd3 100644 --- a/lexer/lexer_test.go +++ b/lexer/lexer_test.go @@ -35,6 +35,7 @@ func TestNextToken(t *testing.T) { d.foo &|^~ !&&|| + <<>> ` tests := []struct { @@ -146,6 +147,8 @@ func TestNextToken(t *testing.T) { {token.NOT, "!"}, {token.AND, "&&"}, {token.OR, "||"}, + {token.LEFT_SHIFT, "<<"}, + {token.RIGHT_SHIFT, ">>"}, {token.EOF, ""}, } diff --git a/parser/parser.go b/parser/parser.go index 52f6d01..1fd0b64 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -14,17 +14,18 @@ const ( OR AND NOT - ASSIGN // := or = - EQUALS // == - LESSGREATER // > or < - BITWISE_OR // | - BITWISE_XOR // ^ - BITWISE_AND // & - SUM // + or - - PRODUCT // * or / or % - PREFIX // -X or !X - CALL // myFunction(X) - INDEX // array[index] + ASSIGN // := or = + EQUALS // == + LESSGREATER // > or < + BITWISE_OR // | + BITWISE_XOR // ^ + BITWISE_AND // & + BITWISE_SHIFT // << or >> + SUM // + or - + PRODUCT // * or / or % + PREFIX // -X or !X + CALL // myFunction(X) + INDEX // array[index] ) var precedences = map[token.TokenType]int{ @@ -42,6 +43,8 @@ var precedences = map[token.TokenType]int{ token.BITWISE_OR: BITWISE_OR, token.BITWISE_XOR: BITWISE_XOR, token.BITWISE_AND: BITWISE_AND, + token.LEFT_SHIFT: BITWISE_SHIFT, + token.RIGHT_SHIFT: BITWISE_SHIFT, token.PLUS: SUM, token.MINUS: SUM, token.DIVIDE: PRODUCT, @@ -112,6 +115,9 @@ func New(l *lexer.Lexer) *Parser { p.registerInfix(token.BITWISE_XOR, p.parseInfixExpression) p.registerInfix(token.BITWISE_AND, p.parseInfixExpression) + p.registerInfix(token.LEFT_SHIFT, p.parseInfixExpression) + p.registerInfix(token.RIGHT_SHIFT, p.parseInfixExpression) + p.registerPrefix(token.NOT, p.parsePrefixExpression) p.registerInfix(token.OR, p.parseInfixExpression) p.registerInfix(token.AND, p.parseInfixExpression) diff --git a/token/token.go b/token/token.go index 4b36d9f..66ad737 100644 --- a/token/token.go +++ b/token/token.go @@ -38,6 +38,10 @@ const ( BITWISE_XOR = "^" // BITWISE_NOT NOT BITWISE_NOT = "~" + // LeftShift + LEFT_SHIFT = "<<" + // RightShift + RIGHT_SHIFT = ">>" // // Comparision operators diff --git a/vm/vm.go b/vm/vm.go index d1a9841..b501980 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -114,7 +114,8 @@ func (vm *VM) Run() error { } case code.OpAdd, code.OpSub, code.OpMul, code.OpDiv, code.OpMod, code.OpOr, - code.OpAnd, code.OpBitwiseOR, code.OpBitwiseXOR, code.OpBitwiseAND: + code.OpAnd, code.OpBitwiseOR, code.OpBitwiseXOR, code.OpBitwiseAND, + code.OpLeftShift, code.OpRightShift: err := vm.executeBinaryOperation(op) if err != nil { return err @@ -631,6 +632,10 @@ func (vm *VM) executeBinaryIntegerOperation(op code.Opcode, left, right object.O result = leftValue ^ rightValue case code.OpBitwiseAND: result = leftValue & rightValue + case code.OpLeftShift: + result = leftValue << uint64(rightValue) + case code.OpRightShift: + result = leftValue >> uint64(rightValue) default: return fmt.Errorf("unknown integer operator: %d", op) diff --git a/vm/vm_test.go b/vm/vm_test.go index 169ccbc..fa9b323 100644 --- a/vm/vm_test.go +++ b/vm/vm_test.go @@ -230,6 +230,8 @@ func TestIntegerArithmetic(t *testing.T) { {"1 | 2", 3}, {"2 ^ 4", 6}, {"3 & 6", 2}, + {"1 << 2", 4}, + {"4 >> 2", 1}, } runVmTests(t, tests)