Greater than and Less Than
Some checks failed
Build / build (push) Failing after 1m12s
Test / build (push) Failing after 11m29s

This commit is contained in:
Chuck Smith
2024-03-18 16:17:13 -04:00
parent d454572870
commit b47a39e1b2
11 changed files with 116 additions and 43 deletions

View File

@@ -22,6 +22,7 @@ const (
OpEqual OpEqual
OpNotEqual OpNotEqual
OpGreaterThan OpGreaterThan
OpGreaterThanEqual
OpMinus OpMinus
OpBang OpBang
OpJumpNotTruthy OpJumpNotTruthy
@@ -65,6 +66,7 @@ var definitions = map[Opcode]*Definition{
OpEqual: {"OpEqual", []int{}}, OpEqual: {"OpEqual", []int{}},
OpNotEqual: {"OpNotEqual", []int{}}, OpNotEqual: {"OpNotEqual", []int{}},
OpGreaterThan: {"OpGreaterThan", []int{}}, OpGreaterThan: {"OpGreaterThan", []int{}},
OpGreaterThanEqual: {"OpGreaterThanEqual", []int{}},
OpMinus: {"OpMinus", []int{}}, OpMinus: {"OpMinus", []int{}},
OpBang: {"OpBang", []int{}}, OpBang: {"OpBang", []int{}},
OpJumpNotTruthy: {"OpJumpNotTruthy", []int{2}}, OpJumpNotTruthy: {"OpJumpNotTruthy", []int{2}},

View File

@@ -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
} }
if node.Operator == "<=" {
c.emit(code.OpGreaterThanEqual)
} else {
c.emit(code.OpGreaterThan) 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 "!=":

View File

@@ -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},

View File

@@ -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 "!=":

View File

@@ -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},

View File

@@ -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 '<':
if l.peekChar() == '=' {
l.readChar()
tok = newToken(token.LTE, l.ch)
tok.Literal = "<="
} else {
tok = newToken(token.LT, l.ch) tok = newToken(token.LT, l.ch)
}
case '>': case '>':
if l.peekChar() == '=' {
l.readChar()
tok = newToken(token.GTE, l.ch)
tok.Literal = ">="
} else {
tok = newToken(token.GT, l.ch) tok = newToken(token.GT, l.ch)
}
case ';': case ';':
tok = newToken(token.SEMICOLON, l.ch) tok = newToken(token.SEMICOLON, l.ch)
case ',': case ',':

View File

@@ -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)

View File

@@ -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)))",

View File

@@ -28,7 +28,9 @@ const (
SLASH = "/" SLASH = "/"
LT = "<" LT = "<"
LTE = "<="
GT = ">" GT = ">"
GTE = ">="
EQ = "==" EQ = "=="
NOT_EQ = "!=" NOT_EQ = "!="

View File

@@ -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)
} }

View File

@@ -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},