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
const (
Mov Opcode = "mov"
Ret Opcode = "ret"
Mov Opcode = "mov"
Ret Opcode = "ret"
Add Opcode = "add"
Sub Opcode = "sub"
Imull Opcode = "imul"
Idiv Opcode = "idiv"
)
type Instruction struct {
@ -113,3 +117,9 @@ type Imm int64
func (i Imm) OperandString(size OperandSize) string {
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) TokenLiteral() 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
}
// Prefix writer writes a prefix befor a call to an other writer
// This Prefix is written befor each new line
type prefixWriter struct {
// Prefix writer writes a prefix before each new line from another io.Writer
type PrefixWriter struct {
output io.Writer
outputPrefix []byte
outputPrefixWritten bool
}
func NewPrefixWriter(output io.Writer, prefix []byte) *prefixWriter {
return &prefixWriter{
func NewPrefixWriter(output io.Writer, prefix []byte) *PrefixWriter {
return &PrefixWriter{
output: output,
outputPrefix: prefix,
}
}
func NewPrefixWriterString(output io.Writer, prefix string) *prefixWriter {
return &prefixWriter{
func NewPrefixWriterString(output io.Writer, prefix string) *PrefixWriter {
return &PrefixWriter{
output: output,
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'})

3
go.mod
View File

@ -1,3 +1,6 @@
module robaertschi.xyz/robaertschi/tt
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)
case ')':
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:
tok.Literal = ""
tok.Type = token.Eof

View File

@ -12,11 +12,18 @@ import (
type precedence int
const (
LOWEST precedence = iota
SUM
PRODUCT
PrecLowest precedence = iota
PrecSum
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 prefixParseFn func() 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.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()
@ -77,10 +88,14 @@ func (p *Parser) peekTokenIs(tt token.TokenType) bool {
}
func getPrecedence(tt token.TokenType) precedence {
switch tt {
default:
return LOWEST
if prec, ok := precedences[tt]; ok {
return prec
}
return PrecLowest
}
func (p *Parser) curPrecedence() precedence {
return getPrecedence(p.curToken.Type)
}
func (p *Parser) peekPrecedence() precedence {
@ -161,7 +176,7 @@ func (p *Parser) parseDeclaration() ast.Declaration {
}
p.nextToken()
expr := p.parseExpression(LOWEST)
expr := p.parseExpression(PrecLowest)
if !p.expectPeek(token.Semicolon) {
return nil
}
@ -212,3 +227,26 @@ func (p *Parser) parseIntegerExpression() ast.Expression {
int.Value = value
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"
"strings"
"robaertschi.xyz/robaertschi/tt/ast"
"robaertschi.xyz/robaertschi/tt/token"
"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) 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"
Int TokenType = "INT"
Semicolon = ";"
Equal = "="
OpenParen = "("
CloseParen = ")"
Semicolon TokenType = ";"
Equal TokenType = "="
OpenParen TokenType = "("
CloseParen TokenType = ")"
Plus TokenType = "+"
Minus TokenType = "-"
Asterisk TokenType = "*"
Slash TokenType = "/"
// Keywords
Fn = "FN"
Fn TokenType = "FN"
)
func LookupKeyword(literal string) TokenType {

View File

@ -1,6 +1,17 @@
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 {
functions := make([]Function, 0)
@ -29,6 +40,14 @@ func emitExpression(expr tast.Expression) (Operand, []Instruction) {
switch expr := expr.(type) {
case *tast.IntegerExpression:
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")
}

View File

@ -3,6 +3,8 @@ package ttir
import (
"fmt"
"strings"
"robaertschi.xyz/robaertschi/tt/ast"
)
type Program struct {
@ -38,6 +40,18 @@ func (r *Ret) String() string {
}
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 {
String() string
operand()
@ -51,3 +65,12 @@ func (c *Constant) String() string {
return fmt.Sprintf("%d", c.Value)
}
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/tast"
"robaertschi.xyz/robaertschi/tt/token"
"robaertschi.xyz/robaertschi/tt/types"
)
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
case *ast.ErrorExpression:
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")
}