begin binary expressions

This commit is contained in:
Robin Bärtschi 2025-01-22 18:21:30 +01:00
parent 3c3c1fc880
commit 1e68083aad
14 changed files with 228 additions and 24 deletions

View File

@ -51,8 +51,12 @@ func (f *Function) Emit() string {
type Opcode string type Opcode string
const ( const (
Mov Opcode = "mov" Mov Opcode = "mov"
Ret Opcode = "ret" Ret Opcode = "ret"
Add Opcode = "add"
Sub Opcode = "sub"
Imull Opcode = "imul"
Idiv Opcode = "idiv"
) )
type Instruction struct { type Instruction struct {
@ -113,3 +117,9 @@ type Imm int64
func (i Imm) OperandString(size OperandSize) string { func (i Imm) OperandString(size OperandSize) string {
return fmt.Sprintf("%d", i) return fmt.Sprintf("%d", i)
} }
type Stack int64
func (s Stack) OperandString(size OperandSize) string {
return fmt.Sprintf("rbp(%d)", s)
}

View File

@ -73,3 +73,39 @@ type IntegerExpression struct {
func (ie *IntegerExpression) expressionNode() {} func (ie *IntegerExpression) expressionNode() {}
func (ie *IntegerExpression) TokenLiteral() string { return ie.Token.Literal } func (ie *IntegerExpression) TokenLiteral() string { return ie.Token.Literal }
func (ie *IntegerExpression) String() string { return ie.Token.Literal } func (ie *IntegerExpression) String() string { return ie.Token.Literal }
//go:generate stringer -type=BinaryOperator
type BinaryOperator int
const (
Add BinaryOperator = iota
Subtract
Multiply
Divide
)
func (bo BinaryOperator) SymbolString() string {
switch bo {
case Add:
return "+"
case Subtract:
return "-"
case Multiply:
return "*"
case Divide:
return "/"
}
return "<INVALID BINARY OPERATOR>"
}
type BinaryExpression struct {
Token token.Token // The operator
Lhs, Rhs Expression
Operator BinaryOperator
}
func (be *BinaryExpression) expressionNode() {}
func (be *BinaryExpression) TokenLiteral() string { return be.Token.Literal }
func (be *BinaryExpression) String() string {
return fmt.Sprintf("%s %s %s", be.Lhs, be.Operator, be.Rhs)
}

View File

@ -0,0 +1,26 @@
// Code generated by "stringer -type=BinaryOperator"; DO NOT EDIT.
package ast
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[Add-0]
_ = x[Subtract-1]
_ = x[Multiply-2]
_ = x[Divide-3]
}
const _BinaryOperator_name = "AddSubtractMultiplyDivide"
var _BinaryOperator_index = [...]uint8{0, 3, 11, 19, 25}
func (i BinaryOperator) String() string {
if i < 0 || i >= BinaryOperator(len(_BinaryOperator_index)-1) {
return "BinaryOperator(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _BinaryOperator_name[_BinaryOperator_index[i]:_BinaryOperator_index[i+1]]
}

View File

@ -23,29 +23,28 @@ type Arguments struct {
OnlyEmitAsm bool OnlyEmitAsm bool
} }
// Prefix writer writes a prefix befor a call to an other writer // Prefix writer writes a prefix before each new line from another io.Writer
// This Prefix is written befor each new line type PrefixWriter struct {
type prefixWriter struct {
output io.Writer output io.Writer
outputPrefix []byte outputPrefix []byte
outputPrefixWritten bool outputPrefixWritten bool
} }
func NewPrefixWriter(output io.Writer, prefix []byte) *prefixWriter { func NewPrefixWriter(output io.Writer, prefix []byte) *PrefixWriter {
return &prefixWriter{ return &PrefixWriter{
output: output, output: output,
outputPrefix: prefix, outputPrefix: prefix,
} }
} }
func NewPrefixWriterString(output io.Writer, prefix string) *prefixWriter { func NewPrefixWriterString(output io.Writer, prefix string) *PrefixWriter {
return &prefixWriter{ return &PrefixWriter{
output: output, output: output,
outputPrefix: []byte(prefix), outputPrefix: []byte(prefix),
} }
} }
func (w *prefixWriter) Write(p []byte) (n int, err error) { func (w *PrefixWriter) Write(p []byte) (n int, err error) {
toWrites := bytes.SplitAfter(p, []byte{'\n'}) toWrites := bytes.SplitAfter(p, []byte{'\n'})

3
go.mod
View File

@ -1,3 +1,6 @@
module robaertschi.xyz/robaertschi/tt module robaertschi.xyz/robaertschi/tt
go 1.23.4 go 1.23.4
require (
)

6
go.sum Normal file
View File

@ -0,0 +1,6 @@
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=

View File

@ -71,6 +71,14 @@ func (l *Lexer) NextToken() token.Token {
tok = l.newToken(token.OpenParen) tok = l.newToken(token.OpenParen)
case ')': case ')':
tok = l.newToken(token.CloseParen) tok = l.newToken(token.CloseParen)
case '+':
tok = l.newToken(token.Plus)
case '-':
tok = l.newToken(token.Minus)
case '*':
tok = l.newToken(token.Asterisk)
case '/':
tok = l.newToken(token.Slash)
case -1: case -1:
tok.Literal = "" tok.Literal = ""
tok.Type = token.Eof tok.Type = token.Eof

View File

@ -12,11 +12,18 @@ import (
type precedence int type precedence int
const ( const (
LOWEST precedence = iota PrecLowest precedence = iota
SUM PrecSum
PRODUCT PrecProduct
) )
var precedences = map[token.TokenType]precedence{
token.Plus: PrecSum,
token.Minus: PrecSum,
token.Asterisk: PrecProduct,
token.Slash: PrecProduct,
}
type ErrorCallback func(token.Token, string, ...any) type ErrorCallback func(token.Token, string, ...any)
type prefixParseFn func() ast.Expression type prefixParseFn func() ast.Expression
type infixParseFn func(ast.Expression) ast.Expression type infixParseFn func(ast.Expression) ast.Expression
@ -40,6 +47,10 @@ func New(l *lexer.Lexer) *Parser {
p.registerPrefixFn(token.Int, p.parseIntegerExpression) p.registerPrefixFn(token.Int, p.parseIntegerExpression)
p.infixParseFns = make(map[token.TokenType]infixParseFn) p.infixParseFns = make(map[token.TokenType]infixParseFn)
p.registerInfixFn(token.Plus, p.parseBinaryExpression)
p.registerInfixFn(token.Minus, p.parseBinaryExpression)
p.registerInfixFn(token.Asterisk, p.parseBinaryExpression)
p.registerInfixFn(token.Slash, p.parseBinaryExpression)
p.nextToken() p.nextToken()
p.nextToken() p.nextToken()
@ -77,10 +88,14 @@ func (p *Parser) peekTokenIs(tt token.TokenType) bool {
} }
func getPrecedence(tt token.TokenType) precedence { func getPrecedence(tt token.TokenType) precedence {
switch tt { if prec, ok := precedences[tt]; ok {
default: return prec
return LOWEST
} }
return PrecLowest
}
func (p *Parser) curPrecedence() precedence {
return getPrecedence(p.curToken.Type)
} }
func (p *Parser) peekPrecedence() precedence { func (p *Parser) peekPrecedence() precedence {
@ -161,7 +176,7 @@ func (p *Parser) parseDeclaration() ast.Declaration {
} }
p.nextToken() p.nextToken()
expr := p.parseExpression(LOWEST) expr := p.parseExpression(PrecLowest)
if !p.expectPeek(token.Semicolon) { if !p.expectPeek(token.Semicolon) {
return nil return nil
} }
@ -212,3 +227,26 @@ func (p *Parser) parseIntegerExpression() ast.Expression {
int.Value = value int.Value = value
return int return int
} }
func (p *Parser) parseBinaryExpression(lhs ast.Expression) ast.Expression {
var op ast.BinaryOperator
switch p.curToken.Type {
case token.Plus:
op = ast.Add
case token.Minus:
op = ast.Subtract
case token.Asterisk:
op = ast.Multiply
case token.Slash:
op = ast.Divide
default:
return p.exprError(p.curToken, "invalid token for binary expression %s", p.curToken.Type)
}
tok := p.curToken
precedence := p.curPrecedence()
p.nextToken()
rhs := p.parseExpression(precedence)
return &ast.BinaryExpression{Lhs: lhs, Rhs: rhs, Operator: op, Token: tok}
}

View File

@ -8,6 +8,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"robaertschi.xyz/robaertschi/tt/ast"
"robaertschi.xyz/robaertschi/tt/token" "robaertschi.xyz/robaertschi/tt/token"
"robaertschi.xyz/robaertschi/tt/types" "robaertschi.xyz/robaertschi/tt/types"
) )
@ -74,3 +75,19 @@ func (ie *IntegerExpression) Type() types.Type {
} }
func (ie *IntegerExpression) TokenLiteral() string { return ie.Token.Literal } func (ie *IntegerExpression) TokenLiteral() string { return ie.Token.Literal }
func (ie *IntegerExpression) String() string { return ie.Token.Literal } func (ie *IntegerExpression) String() string { return ie.Token.Literal }
type BinaryExpression struct {
Token token.Token // The operator
Lhs, Rhs Expression
Operator ast.BinaryOperator
ResultType types.Type
}
func (be *BinaryExpression) expressionNode() {}
func (be *BinaryExpression) Type() types.Type {
return be.ResultType
}
func (be *BinaryExpression) TokenLiteral() string { return be.Token.Literal }
func (be *BinaryExpression) String() string {
return fmt.Sprintf("%s %s %s", be.Lhs, be.Operator, be.Rhs)
}

View File

@ -1 +1 @@
fn main() = 0; fn main() = 35 + 34;

View File

@ -26,13 +26,17 @@ const (
Ident TokenType = "IDENT" Ident TokenType = "IDENT"
Int TokenType = "INT" Int TokenType = "INT"
Semicolon = ";" Semicolon TokenType = ";"
Equal = "=" Equal TokenType = "="
OpenParen = "(" OpenParen TokenType = "("
CloseParen = ")" CloseParen TokenType = ")"
Plus TokenType = "+"
Minus TokenType = "-"
Asterisk TokenType = "*"
Slash TokenType = "/"
// Keywords // Keywords
Fn = "FN" Fn TokenType = "FN"
) )
func LookupKeyword(literal string) TokenType { func LookupKeyword(literal string) TokenType {

View File

@ -1,6 +1,17 @@
package ttir package ttir
import "robaertschi.xyz/robaertschi/tt/tast" import (
"fmt"
"robaertschi.xyz/robaertschi/tt/tast"
)
var uniqueId int64
func temp() string {
uniqueId += 1
return fmt.Sprintf("temp.%d", uniqueId)
}
func EmitProgram(program *tast.Program) *Program { func EmitProgram(program *tast.Program) *Program {
functions := make([]Function, 0) functions := make([]Function, 0)
@ -29,6 +40,14 @@ func emitExpression(expr tast.Expression) (Operand, []Instruction) {
switch expr := expr.(type) { switch expr := expr.(type) {
case *tast.IntegerExpression: case *tast.IntegerExpression:
return &Constant{Value: expr.Value}, []Instruction{} return &Constant{Value: expr.Value}, []Instruction{}
case *tast.BinaryExpression:
lhsDst, instructions := emitExpression(expr.Lhs)
rhsDst, rhsInstructions := emitExpression(expr.Rhs)
instructions = append(instructions, rhsInstructions...)
dst := &Var{Value: temp()}
instructions = append(instructions, &Binary{Operator: expr.Operator, Lhs: lhsDst, Rhs: rhsDst, Dst: dst})
return dst, instructions
} }
panic("unhandled tast.Expression case in ir emitter") panic("unhandled tast.Expression case in ir emitter")
} }

View File

@ -3,6 +3,8 @@ package ttir
import ( import (
"fmt" "fmt"
"strings" "strings"
"robaertschi.xyz/robaertschi/tt/ast"
) )
type Program struct { type Program struct {
@ -38,6 +40,18 @@ func (r *Ret) String() string {
} }
func (r *Ret) instruction() {} func (r *Ret) instruction() {}
type Binary struct {
Operator ast.BinaryOperator
Lhs Operand
Rhs Operand
Dst Operand
}
func (b *Binary) String() string {
return fmt.Sprintf("%s = %s %s, %s", b.Dst, b.Operator, b.Lhs, b.Rhs)
}
func (b *Binary) instruction() {}
type Operand interface { type Operand interface {
String() string String() string
operand() operand()
@ -51,3 +65,12 @@ func (c *Constant) String() string {
return fmt.Sprintf("%d", c.Value) return fmt.Sprintf("%d", c.Value)
} }
func (c *Constant) operand() {} func (c *Constant) operand() {}
type Var struct {
Value string
}
func (v *Var) String() string {
return v.Value
}
func (v *Var) operand() {}

View File

@ -7,6 +7,7 @@ import (
"robaertschi.xyz/robaertschi/tt/ast" "robaertschi.xyz/robaertschi/tt/ast"
"robaertschi.xyz/robaertschi/tt/tast" "robaertschi.xyz/robaertschi/tt/tast"
"robaertschi.xyz/robaertschi/tt/token" "robaertschi.xyz/robaertschi/tt/token"
"robaertschi.xyz/robaertschi/tt/types"
) )
type Checker struct { type Checker struct {
@ -66,6 +67,20 @@ func (c *Checker) checkExpression(expr ast.Expression) (tast.Expression, error)
return &tast.IntegerExpression{Token: expr.Token, Value: expr.Value}, nil return &tast.IntegerExpression{Token: expr.Token, Value: expr.Value}, nil
case *ast.ErrorExpression: case *ast.ErrorExpression:
return nil, c.error(expr.InvalidToken, "invalid expression") return nil, c.error(expr.InvalidToken, "invalid expression")
case *ast.BinaryExpression:
lhs, lhsErr := c.checkExpression(expr.Lhs)
rhs, rhsErr := c.checkExpression(expr.Rhs)
var operandErr error
var resultType types.Type
if lhsErr == nil && rhsErr == nil {
if !lhs.Type().IsSameType(rhs.Type()) {
operandErr = fmt.Errorf("the lhs of the expression does not have the same type then the rhs, lhs=%q, rhs=%q", lhs.Type(), rhs.Type())
} else {
resultType = lhs.Type()
}
}
return &tast.BinaryExpression{Lhs: lhs, Rhs: rhs, Operator: expr.Operator, Token: expr.Token, ResultType: resultType}, errors.Join(lhsErr, rhsErr, operandErr)
} }
return nil, fmt.Errorf("unhandled expression in type checker") return nil, fmt.Errorf("unhandled expression in type checker")
} }