added new equality operators

This commit is contained in:
Robin Bärtschi 2025-01-27 10:08:23 +01:00
parent 5b46794539
commit bc554cdedf
10 changed files with 181 additions and 26 deletions

View File

@ -73,6 +73,10 @@ type CondCode string
const (
Equal CondCode = "e"
NotEqual CondCode = "ne"
Greater CondCode = "g"
GreaterEqual CondCode = "ge"
Less CondCode = "l"
LessEqual CondCode = "le"
)
type Opcode string

View File

@ -124,6 +124,10 @@ func expectOperand(t *testing.T, expected Operand, actual Operand) {
}
}
func trim(s string) string {
return strings.Trim(s, " \n\t")
}
func TestOperands(t *testing.T) {
var op Operand
@ -146,6 +150,7 @@ func TestCodegen(t *testing.T) {
Functions: []ttir.Function{
{
Name: "main",
HasReturnValue: true,
Instructions: []ttir.Instruction{
&ttir.Ret{Op: &ttir.Constant{Value: 0}},
},
@ -154,6 +159,7 @@ func TestCodegen(t *testing.T) {
}
expectedProgram := Program{
Functions: []Function{
{
Name: "main",
@ -167,6 +173,7 @@ func TestCodegen(t *testing.T) {
Opcode: Ret,
},
},
HasReturnValue: true,
},
},
}
@ -176,8 +183,8 @@ func TestCodegen(t *testing.T) {
actual := actualProgram.Emit()
expected := basicTest
if strings.Trim(actual, " \n\t") != strings.Trim(expected, " \n\t") {
t.Errorf("Expected program to be:\n>>%s<<\nbut got:\n>>%s<<\n", expected, actual)
if trim(actual) != trim(expected) {
t.Errorf("Expected program to be:\n>>%s<<\nbut got:\n>>%s<<\n", trim(expected), trim(actual))
}
}
@ -198,12 +205,63 @@ func TestBinary(t *testing.T) {
},
&ttir.Ret{Op: &ttir.Var{Value: "temp.1"}},
},
HasReturnValue: true,
},
},
}
actual := CgProgram(program).Emit()
if strings.Trim(actual, " \n\t") != strings.Trim(binaryTest, " \n\t") {
t.Errorf("Expected program to be:\n>>%s<<\nbut got:\n>>%s<<\n", binaryTest, actual)
if trim(actual) != trim(binaryTest) {
t.Errorf("Expected program to be:\n>>%s<<\nbut got:\n>>%s<<\n", trim(binaryTest), trim(actual))
}
}
//go:embed equality_test.txt
var equalityTest string
// There was once a bug with how the cmp instructions were generated, this check should fail if it happens again
func TestEqualityOperators(t *testing.T) {
program := ttir.Program{
Functions: []ttir.Function{
{
Name: "main",
HasReturnValue: false,
Instructions: []ttir.Instruction{
&ttir.Binary{
Lhs: &ttir.Constant{Value: 5},
Rhs: &ttir.Constant{Value: 4},
Dst: &ttir.Var{Value: "temp.1"},
Operator: ast.LessThanEqual,
},
&ttir.Binary{
Lhs: &ttir.Constant{Value: 5},
Rhs: &ttir.Constant{Value: 4},
Dst: &ttir.Var{Value: "temp.2"},
Operator: ast.LessThan,
},
&ttir.Binary{
Lhs: &ttir.Constant{Value: 5},
Rhs: &ttir.Constant{Value: 4},
Dst: &ttir.Var{Value: "temp.3"},
Operator: ast.GreaterThanEqual,
},
&ttir.Binary{
Lhs: &ttir.Constant{Value: 5},
Rhs: &ttir.Constant{Value: 4},
Dst: &ttir.Var{Value: "temp.4"},
Operator: ast.GreaterThan,
},
&ttir.Ret{},
},
},
},
}
actual := CgProgram(&program).Emit()
actualTrimmed := trim(actual)
expectedTrimmed := trim(actualTrimmed)
if expectedTrimmed != actualTrimmed {
t.Errorf("Expected program to be:\n>>%s<<\nbut got:\n>>%s<<\n", expectedTrimmed, actualTrimmed)
}
}

View File

@ -81,7 +81,7 @@ func cgInstruction(i ttir.Instruction) []Instruction {
func cgBinary(b *ttir.Binary) []Instruction {
switch b.Operator {
case ast.Equal, ast.NotEqual:
case ast.Equal, ast.NotEqual, ast.GreaterThan, ast.GreaterThanEqual, ast.LessThan, ast.LessThanEqual:
var condCode CondCode
switch b.Operator {
@ -89,6 +89,14 @@ func cgBinary(b *ttir.Binary) []Instruction {
condCode = Equal
case ast.NotEqual:
condCode = NotEqual
case ast.GreaterThan:
condCode = Greater
case ast.GreaterThanEqual:
condCode = GreaterEqual
case ast.LessThan:
condCode = Less
case ast.LessThanEqual:
condCode = LessEqual
}
return []Instruction{
@ -278,7 +286,7 @@ func fixupInstruction(i Instruction) []Instruction {
&SimpleInstruction{
Opcode: Cmp,
Lhs: Register(R11),
Rhs: i.Lhs,
Rhs: i.Rhs,
},
}
}

View File

@ -0,0 +1,33 @@
format ELF64 executable
segment readable executable
entry _start
_start:
call main
mov rdi, 0
mov rax, 60
syscall
main:
push rbp
mov rbp, rsp
add rsp, -16
mov r11, 5
cmp r11, 4
mov qword [rsp -4], 0
setle byte [rsp -4]
mov r11, 5
cmp r11, 4
mov qword [rsp -8], 0
setl byte [rsp -8]
mov r11, 5
cmp r11, 4
mov qword [rsp -12], 0
setge byte [rsp -12]
mov r11, 5
cmp r11, 4
mov qword [rsp -16], 0
setg byte [rsp -16]
mov rsp, rbp
pop rbp
ret

View File

@ -93,10 +93,14 @@ const (
Divide
Equal
NotEqual
LessThan
LessThanEqual
GreaterThan
GreaterThanEqual
)
func (bo BinaryOperator) IsBooleanOperator() bool {
return bo == Equal || bo == NotEqual
return bo == Equal || bo == NotEqual || bo == LessThan || bo == LessThanEqual || bo == GreaterThan || bo == GreaterThanEqual
}
func (bo BinaryOperator) SymbolString() string {
@ -113,6 +117,14 @@ func (bo BinaryOperator) SymbolString() string {
return "=="
case NotEqual:
return "!="
case LessThan:
return "<"
case LessThanEqual:
return "<="
case GreaterThan:
return ">"
case GreaterThanEqual:
return ">="
}
return "<INVALID BINARY OPERATOR>"
}

View File

@ -3,7 +3,6 @@
## Syntax
```tt
// Return type is i64
fn main() = {
let i = 34;

View File

@ -75,6 +75,26 @@ func (l *Lexer) NextToken() token.Token {
return tok
}
tok = l.newToken(token.Equal)
case '<':
if l.peekByte() == '=' {
pos := l.position
l.readChar()
l.readChar()
tok.Type = token.LessThanEqual
tok.Literal = l.input[pos:l.position]
return tok
}
tok = l.newToken(token.LessThan)
case '>':
if l.peekByte() == '=' {
pos := l.position
l.readChar()
l.readChar()
tok.Type = token.GreaterThanEqual
tok.Literal = l.input[pos:l.position]
return tok
}
tok = l.newToken(token.GreaterThan)
case '(':
tok = l.newToken(token.OpenParen)
case ')':

View File

@ -25,6 +25,10 @@ var precedences = map[token.TokenType]precedence{
token.Slash: PrecProduct,
token.DoubleEqual: PrecComparison,
token.NotEqual: PrecComparison,
token.GreaterThan: PrecComparison,
token.GreaterThanEqual: PrecComparison,
token.LessThan: PrecComparison,
token.LessThanEqual: PrecComparison,
}
type ErrorCallback func(token.Token, string, ...any)
@ -60,6 +64,10 @@ func New(l *lexer.Lexer) *Parser {
p.registerInfixFn(token.Slash, p.parseBinaryExpression)
p.registerInfixFn(token.DoubleEqual, p.parseBinaryExpression)
p.registerInfixFn(token.NotEqual, p.parseBinaryExpression)
p.registerInfixFn(token.GreaterThan, p.parseBinaryExpression)
p.registerInfixFn(token.GreaterThanEqual, p.parseBinaryExpression)
p.registerInfixFn(token.LessThan, p.parseBinaryExpression)
p.registerInfixFn(token.LessThanEqual, p.parseBinaryExpression)
p.nextToken()
p.nextToken()
@ -270,6 +278,14 @@ func (p *Parser) parseBinaryExpression(lhs ast.Expression) ast.Expression {
op = ast.Equal
case token.NotEqual:
op = ast.NotEqual
case token.LessThan:
op = ast.LessThan
case token.LessThanEqual:
op = ast.LessThanEqual
case token.GreaterThan:
op = ast.GreaterThan
case token.GreaterThanEqual:
op = ast.GreaterThanEqual
default:
return p.exprError(p.curToken, "invalid token for binary expression %s", p.curToken.Type)
}

View File

@ -1,5 +1,6 @@
fn main() = {
3 + 3;
3 * 43 * 34 / 34;
3
5 <= 4;
5 < 4;
5 >= 4;
5 > 4;
};

View File

@ -42,6 +42,10 @@ const (
Slash TokenType = "/"
DoubleEqual TokenType = "=="
NotEqual TokenType = "!="
LessThan TokenType = "<"
LessThanEqual TokenType = "<="
GreaterThan TokenType = ">"
GreaterThanEqual TokenType = ">="
// Keywords
Fn TokenType = "FN"