Greater than and Less Than
This commit is contained in:
68
code/code.go
68
code/code.go
@@ -22,6 +22,7 @@ const (
|
|||||||
OpEqual
|
OpEqual
|
||||||
OpNotEqual
|
OpNotEqual
|
||||||
OpGreaterThan
|
OpGreaterThan
|
||||||
|
OpGreaterThanEqual
|
||||||
OpMinus
|
OpMinus
|
||||||
OpBang
|
OpBang
|
||||||
OpJumpNotTruthy
|
OpJumpNotTruthy
|
||||||
@@ -52,39 +53,40 @@ type Definition struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var definitions = map[Opcode]*Definition{
|
var definitions = map[Opcode]*Definition{
|
||||||
OpConstant: {"OpConstant", []int{2}},
|
OpConstant: {"OpConstant", []int{2}},
|
||||||
OpAssignGlobal: {"OpAssignGlobal", []int{2}},
|
OpAssignGlobal: {"OpAssignGlobal", []int{2}},
|
||||||
OpAssignLocal: {"OpAssignLocal", []int{1}},
|
OpAssignLocal: {"OpAssignLocal", []int{1}},
|
||||||
OpAdd: {"OpAdd", []int{}},
|
OpAdd: {"OpAdd", []int{}},
|
||||||
OpPop: {"OpPop", []int{}},
|
OpPop: {"OpPop", []int{}},
|
||||||
OpSub: {"OpSub", []int{}},
|
OpSub: {"OpSub", []int{}},
|
||||||
OpMul: {"OpMul", []int{}},
|
OpMul: {"OpMul", []int{}},
|
||||||
OpDiv: {"OpDiv", []int{}},
|
OpDiv: {"OpDiv", []int{}},
|
||||||
OpTrue: {"OpTrue", []int{}},
|
OpTrue: {"OpTrue", []int{}},
|
||||||
OpFalse: {"OpFalse", []int{}},
|
OpFalse: {"OpFalse", []int{}},
|
||||||
OpEqual: {"OpEqual", []int{}},
|
OpEqual: {"OpEqual", []int{}},
|
||||||
OpNotEqual: {"OpNotEqual", []int{}},
|
OpNotEqual: {"OpNotEqual", []int{}},
|
||||||
OpGreaterThan: {"OpGreaterThan", []int{}},
|
OpGreaterThan: {"OpGreaterThan", []int{}},
|
||||||
OpMinus: {"OpMinus", []int{}},
|
OpGreaterThanEqual: {"OpGreaterThanEqual", []int{}},
|
||||||
OpBang: {"OpBang", []int{}},
|
OpMinus: {"OpMinus", []int{}},
|
||||||
OpJumpNotTruthy: {"OpJumpNotTruthy", []int{2}},
|
OpBang: {"OpBang", []int{}},
|
||||||
OpJump: {"OpJump", []int{2}},
|
OpJumpNotTruthy: {"OpJumpNotTruthy", []int{2}},
|
||||||
OpNull: {"OpNull", []int{}},
|
OpJump: {"OpJump", []int{2}},
|
||||||
OpGetGlobal: {"OpGetGlobal", []int{2}},
|
OpNull: {"OpNull", []int{}},
|
||||||
OpSetGlobal: {"OpSetGlobal", []int{2}},
|
OpGetGlobal: {"OpGetGlobal", []int{2}},
|
||||||
OpArray: {"OpArray", []int{2}},
|
OpSetGlobal: {"OpSetGlobal", []int{2}},
|
||||||
OpHash: {"OpHash", []int{2}},
|
OpArray: {"OpArray", []int{2}},
|
||||||
OpIndex: {"OpIndex", []int{}},
|
OpHash: {"OpHash", []int{2}},
|
||||||
OpCall: {"OpCall", []int{1}},
|
OpIndex: {"OpIndex", []int{}},
|
||||||
OpReturnValue: {"OpReturnValue", []int{}},
|
OpCall: {"OpCall", []int{1}},
|
||||||
OpReturn: {"OpReturn", []int{}},
|
OpReturnValue: {"OpReturnValue", []int{}},
|
||||||
OpGetLocal: {"OpGetLocal", []int{1}},
|
OpReturn: {"OpReturn", []int{}},
|
||||||
OpSetLocal: {"OpSetLocal", []int{1}},
|
OpGetLocal: {"OpGetLocal", []int{1}},
|
||||||
OpGetBuiltin: {"OpGetBuiltin", []int{1}},
|
OpSetLocal: {"OpSetLocal", []int{1}},
|
||||||
OpClosure: {"OpClosure", []int{2, 1}},
|
OpGetBuiltin: {"OpGetBuiltin", []int{1}},
|
||||||
OpGetFree: {"OpGetFree", []int{1}},
|
OpClosure: {"OpClosure", []int{2, 1}},
|
||||||
OpCurrentClosure: {"OpCurrentClosure", []int{}},
|
OpGetFree: {"OpGetFree", []int{1}},
|
||||||
OpNoop: {"OpNoop", []int{}},
|
OpCurrentClosure: {"OpCurrentClosure", []int{}},
|
||||||
|
OpNoop: {"OpNoop", []int{}},
|
||||||
}
|
}
|
||||||
|
|
||||||
func Lookup(op byte) (*Definition, error) {
|
func Lookup(op byte) (*Definition, error) {
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case *ast.InfixExpression:
|
case *ast.InfixExpression:
|
||||||
if node.Operator == "<" {
|
if node.Operator == "<" || node.Operator == "<=" {
|
||||||
err := c.Compile(node.Right)
|
err := c.Compile(node.Right)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -91,7 +91,11 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.emit(code.OpGreaterThan)
|
if node.Operator == "<=" {
|
||||||
|
c.emit(code.OpGreaterThanEqual)
|
||||||
|
} else {
|
||||||
|
c.emit(code.OpGreaterThan)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,6 +120,8 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||||||
c.emit(code.OpDiv)
|
c.emit(code.OpDiv)
|
||||||
case ">":
|
case ">":
|
||||||
c.emit(code.OpGreaterThan)
|
c.emit(code.OpGreaterThan)
|
||||||
|
case ">=":
|
||||||
|
c.emit(code.OpGreaterThanEqual)
|
||||||
case "==":
|
case "==":
|
||||||
c.emit(code.OpEqual)
|
c.emit(code.OpEqual)
|
||||||
case "!=":
|
case "!=":
|
||||||
|
|||||||
@@ -120,6 +120,26 @@ func TestBooleanExpressions(t *testing.T) {
|
|||||||
code.Make(code.OpPop),
|
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.OpGreaterThanEqual),
|
||||||
|
code.Make(code.OpPop),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "1 <= 2",
|
||||||
|
expectedConstants: []interface{}{2, 1},
|
||||||
|
expectedInstructions: []code.Instructions{
|
||||||
|
code.Make(code.OpConstant, 0),
|
||||||
|
code.Make(code.OpConstant, 1),
|
||||||
|
code.Make(code.OpGreaterThanEqual),
|
||||||
|
code.Make(code.OpPop),
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
input: "1 == 2",
|
input: "1 == 2",
|
||||||
expectedConstants: []interface{}{1, 2},
|
expectedConstants: []interface{}{1, 2},
|
||||||
|
|||||||
@@ -296,8 +296,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 nativeBoolToBooleanObject(leftVal < rightVal)
|
return nativeBoolToBooleanObject(leftVal < rightVal)
|
||||||
|
case "<=":
|
||||||
|
return nativeBoolToBooleanObject(leftVal <= rightVal)
|
||||||
case ">":
|
case ">":
|
||||||
return nativeBoolToBooleanObject(leftVal > rightVal)
|
return nativeBoolToBooleanObject(leftVal > rightVal)
|
||||||
|
case ">=":
|
||||||
|
return nativeBoolToBooleanObject(leftVal >= rightVal)
|
||||||
case "==":
|
case "==":
|
||||||
return nativeBoolToBooleanObject(leftVal == rightVal)
|
return nativeBoolToBooleanObject(leftVal == rightVal)
|
||||||
case "!=":
|
case "!=":
|
||||||
|
|||||||
@@ -62,6 +62,10 @@ func TestEvalBooleanExpression(t *testing.T) {
|
|||||||
{"(1 < 2) == false", false},
|
{"(1 < 2) == false", false},
|
||||||
{"(1 > 2) == true", false},
|
{"(1 > 2) == true", false},
|
||||||
{"(1 > 2) == false", true},
|
{"(1 > 2) == false", true},
|
||||||
|
{"(1 <= 2) == true", true},
|
||||||
|
{"(1 <= 2) == false", false},
|
||||||
|
{"(1 >= 2) == true", false},
|
||||||
|
{"(1 >= 2) == false", true},
|
||||||
{`"a" == "a"`, true},
|
{`"a" == "a"`, true},
|
||||||
{`"a" < "b"`, true},
|
{`"a" < "b"`, true},
|
||||||
{`"abc" == "abc"`, true},
|
{`"abc" == "abc"`, true},
|
||||||
|
|||||||
@@ -84,9 +84,21 @@ func (l *Lexer) NextToken() token.Token {
|
|||||||
case '*':
|
case '*':
|
||||||
tok = newToken(token.ASTERISK, l.ch)
|
tok = newToken(token.ASTERISK, l.ch)
|
||||||
case '<':
|
case '<':
|
||||||
tok = newToken(token.LT, l.ch)
|
if l.peekChar() == '=' {
|
||||||
|
l.readChar()
|
||||||
|
tok = newToken(token.LTE, l.ch)
|
||||||
|
tok.Literal = "<="
|
||||||
|
} else {
|
||||||
|
tok = newToken(token.LT, l.ch)
|
||||||
|
}
|
||||||
case '>':
|
case '>':
|
||||||
tok = newToken(token.GT, l.ch)
|
if l.peekChar() == '=' {
|
||||||
|
l.readChar()
|
||||||
|
tok = newToken(token.GTE, l.ch)
|
||||||
|
tok.Literal = ">="
|
||||||
|
} else {
|
||||||
|
tok = newToken(token.GT, l.ch)
|
||||||
|
}
|
||||||
case ';':
|
case ';':
|
||||||
tok = newToken(token.SEMICOLON, l.ch)
|
tok = newToken(token.SEMICOLON, l.ch)
|
||||||
case ',':
|
case ',':
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ var precedences = map[token.TokenType]int{
|
|||||||
token.NOT_EQ: EQUALS,
|
token.NOT_EQ: EQUALS,
|
||||||
token.LT: LESSGREATER,
|
token.LT: LESSGREATER,
|
||||||
token.GT: LESSGREATER,
|
token.GT: LESSGREATER,
|
||||||
|
token.LTE: LESSGREATER,
|
||||||
|
token.GTE: LESSGREATER,
|
||||||
token.PLUS: SUM,
|
token.PLUS: SUM,
|
||||||
token.MINUS: SUM,
|
token.MINUS: SUM,
|
||||||
token.SLASH: PRODUCT,
|
token.SLASH: PRODUCT,
|
||||||
@@ -78,7 +80,9 @@ func New(l *lexer.Lexer) *Parser {
|
|||||||
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)
|
||||||
|
p.registerInfix(token.LTE, p.parseInfixExpression)
|
||||||
p.registerInfix(token.GT, p.parseInfixExpression)
|
p.registerInfix(token.GT, p.parseInfixExpression)
|
||||||
|
p.registerInfix(token.GTE, p.parseInfixExpression)
|
||||||
p.registerInfix(token.LPAREN, p.parseCallExpression)
|
p.registerInfix(token.LPAREN, p.parseCallExpression)
|
||||||
p.registerInfix(token.LBRACKET, p.parseIndexExpression)
|
p.registerInfix(token.LBRACKET, p.parseIndexExpression)
|
||||||
|
|
||||||
|
|||||||
@@ -285,6 +285,14 @@ func TestOperatorPrecedenceParsing(t *testing.T) {
|
|||||||
"5 < 4 != 3 > 4",
|
"5 < 4 != 3 > 4",
|
||||||
"((5 < 4) != (3 > 4))",
|
"((5 < 4) != (3 > 4))",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"5 >= 4 == 3 <= 4",
|
||||||
|
"((5 >= 4) == (3 <= 4))",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"5 <= 4 != 3 >= 4",
|
||||||
|
"((5 <= 4) != (3 >= 4))",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"3 + 4 * 5 == 3 * 1 + 4 * 5",
|
"3 + 4 * 5 == 3 * 1 + 4 * 5",
|
||||||
"((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))",
|
"((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))",
|
||||||
|
|||||||
@@ -27,8 +27,10 @@ const (
|
|||||||
ASTERISK = "*"
|
ASTERISK = "*"
|
||||||
SLASH = "/"
|
SLASH = "/"
|
||||||
|
|
||||||
LT = "<"
|
LT = "<"
|
||||||
GT = ">"
|
LTE = "<="
|
||||||
|
GT = ">"
|
||||||
|
GTE = ">="
|
||||||
|
|
||||||
EQ = "=="
|
EQ = "=="
|
||||||
NOT_EQ = "!="
|
NOT_EQ = "!="
|
||||||
|
|||||||
15
vm/vm.go
15
vm/vm.go
@@ -83,11 +83,14 @@ func (vm *VM) Run() error {
|
|||||||
|
|
||||||
if vm.Debug {
|
if vm.Debug {
|
||||||
log.Printf(
|
log.Printf(
|
||||||
"%-20s %-20s\n",
|
"%-25s %-20s\n",
|
||||||
strings.Split(ins[ip:].String(), "\n")[0],
|
fmt.Sprintf(
|
||||||
|
"%04d %s", ip,
|
||||||
|
strings.Split(ins[ip:].String(), "\n")[0][4:],
|
||||||
|
),
|
||||||
fmt.Sprintf(
|
fmt.Sprintf(
|
||||||
"[ip=%02d fp=%02d, sp=%02d]",
|
"[ip=%02d fp=%02d, sp=%02d]",
|
||||||
ip, vm.sp, vm.framesIndex-1,
|
ip, vm.framesIndex-1, vm.sp,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -130,7 +133,7 @@ func (vm *VM) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case code.OpEqual, code.OpNotEqual, code.OpGreaterThan:
|
case code.OpEqual, code.OpNotEqual, code.OpGreaterThan, code.OpGreaterThanEqual:
|
||||||
err := vm.executeComparison(op)
|
err := vm.executeComparison(op)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -512,6 +515,8 @@ func (vm *VM) executeIntegerComparison(op code.Opcode, left, right object.Object
|
|||||||
return vm.push(nativeBoolToBooleanObject(rightValue != leftValue))
|
return vm.push(nativeBoolToBooleanObject(rightValue != leftValue))
|
||||||
case code.OpGreaterThan:
|
case code.OpGreaterThan:
|
||||||
return vm.push(nativeBoolToBooleanObject(leftValue > rightValue))
|
return vm.push(nativeBoolToBooleanObject(leftValue > rightValue))
|
||||||
|
case code.OpGreaterThanEqual:
|
||||||
|
return vm.push(nativeBoolToBooleanObject(leftValue >= rightValue))
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown operator: %d", op)
|
return fmt.Errorf("unknown operator: %d", op)
|
||||||
}
|
}
|
||||||
@@ -628,6 +633,8 @@ func (vm *VM) executeStringComparison(op code.Opcode, left, right object.Object)
|
|||||||
return vm.push(nativeBoolToBooleanObject(rightValue != leftValue))
|
return vm.push(nativeBoolToBooleanObject(rightValue != leftValue))
|
||||||
case code.OpGreaterThan:
|
case code.OpGreaterThan:
|
||||||
return vm.push(nativeBoolToBooleanObject(leftValue > rightValue))
|
return vm.push(nativeBoolToBooleanObject(leftValue > rightValue))
|
||||||
|
case code.OpGreaterThanEqual:
|
||||||
|
return vm.push(nativeBoolToBooleanObject(leftValue >= rightValue))
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown operator: %d", op)
|
return fmt.Errorf("unknown operator: %d", op)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -209,6 +209,10 @@ func TestIntegerArithmetic(t *testing.T) {
|
|||||||
{"(1 < 2) == false", false},
|
{"(1 < 2) == false", false},
|
||||||
{"(1 > 2) == true", false},
|
{"(1 > 2) == true", false},
|
||||||
{"(1 > 2) == false", true},
|
{"(1 > 2) == false", true},
|
||||||
|
{"(1 <= 2) == true", true},
|
||||||
|
{"(1 <= 2) == false", false},
|
||||||
|
{"(1 >= 2) == true", false},
|
||||||
|
{"(1 >= 2) == false", true},
|
||||||
{"-5", -5},
|
{"-5", -5},
|
||||||
{"-10", -10},
|
{"-10", -10},
|
||||||
{"-50 + 100 + -50", 0},
|
{"-50 + 100 + -50", 0},
|
||||||
|
|||||||
Reference in New Issue
Block a user