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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 ',':

View File

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

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))",
},
{
"3 + 4 * 5 == 3 * 1 + 4 * 5",
"((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))",

View File

@@ -27,8 +27,10 @@ const (
ASTERISK = "*"
SLASH = "/"
LT = "<"
GT = ">"
LT = "<"
LTE = "<="
GT = ">"
GTE = ">="
EQ = "=="
NOT_EQ = "!="

View File

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

View File

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