mirror of
https://github.com/RoBaertschi/tt.git
synced 2025-04-16 05:53:30 +00:00
added new equality operators
This commit is contained in:
parent
5b46794539
commit
bc554cdedf
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
33
asm/amd64/equality_test.txt
Normal file
33
asm/amd64/equality_test.txt
Normal 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
|
||||
|
||||
|
14
ast/ast.go
14
ast/ast.go
@ -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>"
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
## Syntax
|
||||
|
||||
```tt
|
||||
|
||||
// Return type is i64
|
||||
fn main() = {
|
||||
let i = 34;
|
||||
|
@ -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 ')':
|
||||
|
@ -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)
|
||||
}
|
||||
|
7
test.tt
7
test.tt
@ -1,5 +1,6 @@
|
||||
fn main() = {
|
||||
3 + 3;
|
||||
3 * 43 * 34 / 34;
|
||||
3
|
||||
5 <= 4;
|
||||
5 < 4;
|
||||
5 >= 4;
|
||||
5 > 4;
|
||||
};
|
||||
|
@ -42,6 +42,10 @@ const (
|
||||
Slash TokenType = "/"
|
||||
DoubleEqual TokenType = "=="
|
||||
NotEqual TokenType = "!="
|
||||
LessThan TokenType = "<"
|
||||
LessThanEqual TokenType = "<="
|
||||
GreaterThan TokenType = ">"
|
||||
GreaterThanEqual TokenType = ">="
|
||||
|
||||
// Keywords
|
||||
Fn TokenType = "FN"
|
||||
|
Loading…
x
Reference in New Issue
Block a user