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