mirror of
https://github.com/RoBaertschi/tt.git
synced 2025-04-18 23:13:29 +00:00
begin binary expressions
This commit is contained in:
parent
3c3c1fc880
commit
1e68083aad
@ -53,6 +53,10 @@ type Opcode string
|
||||
const (
|
||||
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)
|
||||
}
|
||||
|
36
ast/ast.go
36
ast/ast.go
@ -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)
|
||||
}
|
||||
|
26
ast/binaryoperator_string.go
Normal file
26
ast/binaryoperator_string.go
Normal 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]]
|
||||
}
|
15
cmd/cmd.go
15
cmd/cmd.go
@ -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'})
|
||||
|
||||
|
6
go.sum
Normal file
6
go.sum
Normal 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=
|
@ -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
|
||||
|
@ -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}
|
||||
}
|
||||
|
17
tast/tast.go
17
tast/tast.go
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
21
ttir/emit.go
21
ttir/emit.go
@ -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")
|
||||
}
|
||||
|
23
ttir/ttir.go
23
ttir/ttir.go
@ -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() {}
|
||||
|
@ -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")
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user