diff --git a/architecture.md b/architecture.md index 245aca6..8adea3d 100644 --- a/architecture.md +++ b/architecture.md @@ -7,7 +7,7 @@ tt Programming Language Backend Architecture # Architecture -AST --> Type Checking --> TAST --> IR Emission --> TTIR --> Codegen --> TTASM --> Emit --> FASM -> Binary +AST --> Type Checking --> TAST --> IR Emission --> TTIR --> Codegen --> ASM --> Emit --> FASM -> Binary TTIR: TT Intermediate Representation is the Representation that the AST gets turned into. This will be mostly be used for optimissing and abstracting away from Assembly TAST: Typed Ast diff --git a/asm/amd64/amd64.go b/asm/amd64/amd64.go index 1a9d2df..527942a 100644 --- a/asm/amd64/amd64.go +++ b/asm/amd64/amd64.go @@ -5,6 +5,15 @@ import ( "strings" ) +var callConvArgs map[int]Register = map[int]Register{ + 1: DI, + 2: SI, + 3: DX, + 4: CX, + 5: R8, + 6: R9, +} + type Program struct { Functions []Function MainFunction *Function @@ -169,6 +178,12 @@ type Register int const ( AX Register = iota + CX + DX + DI + SI + R8 + R9 R10 R11 diff --git a/ast/ast.go b/ast/ast.go index aaa1fb0..8e86db8 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -52,17 +52,35 @@ func (p *Program) String() string { return builder.String() } +type Type string + +type Argument struct { + Name string + Type Type +} + type FunctionDeclaration struct { Token token.Token // The token.FN Body Expression Name string + Args []Argument +} + +func ArgsToString(args []Argument) string { + var b strings.Builder + + for _, arg := range args { + b.WriteString(fmt.Sprintf("%s %s,", arg.Name, arg.Type)) + } + + return b.String() } func (fd *FunctionDeclaration) declarationNode() {} func (fd *FunctionDeclaration) TokenLiteral() string { return fd.Token.Literal } func (fd *FunctionDeclaration) Tok() token.Token { return fd.Token } func (fd *FunctionDeclaration) String() string { - return fmt.Sprintf("fn %v() = %v;", fd.Name, fd.Body.String()) + return fmt.Sprintf("fn %v(%v) = %v;", fd.Name, ArgsToString(fd.Args), fd.Body.String()) } // Represents a Expression that we failed to parse @@ -209,7 +227,7 @@ func (ie *IfExpression) String() string { type VariableDeclaration struct { Token token.Token // The Identifier token InitializingExpression Expression - Type string + Type Type Identifier string } diff --git a/lexer/lexer.go b/lexer/lexer.go index 9e3095a..1e50dbc 100644 --- a/lexer/lexer.go +++ b/lexer/lexer.go @@ -63,6 +63,8 @@ func (l *Lexer) NextToken() token.Token { tok.Loc = l.loc() switch l.ch { + case ',': + tok = l.newToken(token.Comma) case ';': tok = l.newToken(token.Semicolon) case ':': diff --git a/parser/parser.go b/parser/parser.go index 7b6c56c..fea2462 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -178,6 +178,38 @@ func (p *Parser) ParseProgram() *ast.Program { } } +func (p *Parser) parseType() (t ast.Type, ok bool) { + if ok, _ := p.expect(token.Ident); !ok { + return "", false + } + + return ast.Type(p.curToken.Literal), true +} + +func (p *Parser) parseArgumentList() ([]ast.Argument, bool) { + args := []ast.Argument{} + + for p.peekTokenIs(token.Ident) { + p.nextToken() + name := p.curToken.Literal + p.nextToken() + t, ok := p.parseType() + + if !ok { + return args, false + } + + args = append(args, ast.Argument{Type: t, Name: name}) + + if !p.peekTokenIs(token.Comma) { + break + } + p.nextToken() + } + + return args, true +} + func (p *Parser) parseDeclaration() ast.Declaration { if ok, _ := p.expect(token.Fn); !ok { return nil @@ -191,6 +223,13 @@ func (p *Parser) parseDeclaration() ast.Declaration { if ok, _ := p.expectPeek(token.OpenParen); !ok { return nil } + + args, ok := p.parseArgumentList() + + if !ok { + return nil + } + if ok, _ := p.expectPeek(token.CloseParen); !ok { return nil } @@ -208,6 +247,7 @@ func (p *Parser) parseDeclaration() ast.Declaration { Token: tok, Name: name, Body: expr, + Args: args, } } @@ -371,7 +411,7 @@ func (p *Parser) parseVariableDeclaration() ast.Expression { if p.peekTokenIs(token.Ident) { p.nextToken() - variable.Type = p.curToken.Literal + variable.Type = ast.Type(p.curToken.Literal) } if ok, errExpr := p.expectPeek(token.Equal); !ok { diff --git a/tast/tast.go b/tast/tast.go index eea1203..d0a079b 100644 --- a/tast/tast.go +++ b/tast/tast.go @@ -52,10 +52,16 @@ func (p *Program) String() string { return builder.String() } +type Argument struct { + Name string + Type types.Type +} + type FunctionDeclaration struct { Token token.Token // The token.FN Body Expression Name string + Args []Argument ReturnType types.Type } diff --git a/test.tt b/test.tt index 8bce7bd..8d848d6 100644 --- a/test.tt +++ b/test.tt @@ -1,9 +1,13 @@ fn main() = { hi := 4; - if hi == 4 in + if hi == 4 { + test2 := 3; hi = 0 - else hi = 1; + } + else { + hi = 1 + }; - hi + test2 }; diff --git a/token/token.go b/token/token.go index 111638a..eb236b4 100644 --- a/token/token.go +++ b/token/token.go @@ -33,6 +33,7 @@ const ( Semicolon TokenType = ";" Colon TokenType = ":" + Comma TokenType = "," Equal TokenType = "=" OpenParen TokenType = "(" CloseParen TokenType = ")" diff --git a/typechecker/check.go b/typechecker/check.go index d3f5acc..35f73d0 100644 --- a/typechecker/check.go +++ b/typechecker/check.go @@ -26,6 +26,11 @@ func (c *Checker) error(t token.Token, format string, args ...any) error { } func (c *Checker) CheckProgram(program *ast.Program) (*tast.Program, error) { + _, err := VarResolve(program) + if err != nil { + return nil, err + } + newProgram, err := c.inferTypes(program) if err != nil { return nil, err diff --git a/typechecker/infer.go b/typechecker/infer.go index a8fa78a..930b334 100644 --- a/typechecker/infer.go +++ b/typechecker/infer.go @@ -30,6 +30,13 @@ func (c *Checker) inferDeclaration(decl ast.Declaration) (tast.Declaration, erro switch decl := decl.(type) { case *ast.FunctionDeclaration: vars := make(Variables) + for _, arg := range decl.Args { + t, ok := types.From(arg.Type) + if !ok { + return nil, c.error(decl.Token, "could not find the type %q for argument %q", arg.Type, arg.Name) + } + vars[arg.Name] = t + } body, err := c.inferExpression(vars, decl.Body) c.functionVariables[decl.Name] = vars diff --git a/typechecker/variable_resolution.go b/typechecker/variable_resolution.go index 9e74e3e..7af48e3 100644 --- a/typechecker/variable_resolution.go +++ b/typechecker/variable_resolution.go @@ -29,17 +29,13 @@ func copyScope(s *Scope) Scope { newVars[k] = Var{Name: v.Name, FromCurrentScope: false} } - return Scope{Variables: newVars} + return Scope{Variables: newVars, UniqueId: s.UniqueId} } func (s *Scope) Get(name string) (Var, bool) { v, ok := s.Variables[name] - if ok { - return v, true - } - - return Var{}, false + return v, ok } func (s *Scope) Set(name string, uniqName string) { @@ -59,6 +55,18 @@ func (s *Scope) HasInCurrent(name string) bool { return v.FromCurrentScope } +func (s *Scope) Uniq(name string) string { + uniqName := fmt.Sprintf("%s.%d", name, s.UniqueId) + s.UniqueId += 1 + return uniqName +} + +func (s *Scope) SetUniq(name string) string { + uniq := s.Uniq(name) + s.Set(name, uniq) + return uniq +} + func VarResolve(p *ast.Program) (map[string]Scope, error) { functionToScope := make(map[string]Scope) @@ -66,6 +74,9 @@ func VarResolve(p *ast.Program) (map[string]Scope, error) { switch d := d.(type) { case *ast.FunctionDeclaration: s := Scope{Variables: make(map[string]Var)} + for _, arg := range d.Args { + s.SetUniq(arg.Name) + } err := VarResolveExpr(&s, d.Body) functionToScope[d.Name] = s if err != nil { @@ -116,25 +127,25 @@ func VarResolveExpr(s *Scope, e ast.Expression) error { return err } - err = VarResolveExpr(s, e.Then) + thenS := copyScope(s) + err = VarResolveExpr(&thenS, e.Then) if err != nil { return err } + elseS := copyScope(s) if e.Else != nil { - err = VarResolveExpr(s, e.Else) + err = VarResolveExpr(&elseS, e.Else) if err != nil { return err } } case *ast.VariableDeclaration: if s.HasInCurrent(e.Identifier) { - return errorf(e.Token, "variable %q redifinded", e.Identifier) + return errorf(e.Token, "variable %q redefined", e.Identifier) } - uniqName := fmt.Sprintf("%s.%d", e.Identifier, s.UniqueId) - s.UniqueId += 1 - s.Set(e.Identifier, uniqName) + s.SetUniq(e.Identifier) case *ast.VariableReference: v, ok := s.Get(e.Identifier) if !ok { diff --git a/types/types.go b/types/types.go index 94cd447..6aff11e 100644 --- a/types/types.go +++ b/types/types.go @@ -53,7 +53,7 @@ func New(id int64, name string) Type { return typeId } -func From(name string) (Type, bool) { - t, ok := types[name] +func From(name ast.Type) (Type, bool) { + t, ok := types[string(name)] return t, ok } diff --git a/update.md b/update.md new file mode 100644 index 0000000..09c0f2d --- /dev/null +++ b/update.md @@ -0,0 +1,15 @@ +# What to check if you updated or added something + +- [x] Lexer +- [x] Parser + - [x] AST + - [x] TAST +- [x] Type Checker + - [x] Variable Resolution + - [x] Infer + - [x] Check +- [ ] TTIR +- [ ] TTIR Emit +- [ ] Backends + - [ ] Qbe + - [ ] Fasm