From 5d729197f820848c43a5bc6f23c2ef88e9707c0a Mon Sep 17 00:00:00 2001 From: NotLe0n Date: Mon, 3 Feb 2025 22:31:28 +0100 Subject: [PATCH] added const parsing --- src/ast/declarations.go | 21 ++++++ src/ast/helper_visitor.go | 8 ++ src/ast/printer.go | 9 +++ src/ast/visitor.go | 14 ++++ src/compiler/compiler.go | 4 + src/ddperror/messages.go | 2 +- src/parser/declarations.go | 78 +++++++++++++++++--- src/parser/parser.go | 2 + src/parser/resolver/resolver.go | 17 +++++ src/parser/typechecker/assignable_checker.go | 1 - src/parser/typechecker/typechecker.go | 10 +++ src/token/token_types.go | 3 + 12 files changed, 157 insertions(+), 12 deletions(-) diff --git a/src/ast/declarations.go b/src/ast/declarations.go index 8bc64470..ebc23da2 100644 --- a/src/ast/declarations.go +++ b/src/ast/declarations.go @@ -14,6 +14,17 @@ type ( Mod *Module } + ConstDecl struct { + Range token.Range + Mod *Module // the module in which the variable was declared + CommentTok *token.Token // optional comment (also contained in ast.Comments) + Type ddptypes.Type // type of the variable + NameTok token.Token // identifier name + Val Expression + IsPublic bool // wether the function is marked with öffentliche + IsExternVisible bool // wether the variable is marked as extern visible + } + VarDecl struct { Range token.Range CommentTok *token.Token // optional comment (also contained in ast.Comments) @@ -92,6 +103,7 @@ type ( ) func (decl *BadDecl) node() {} +func (decl *ConstDecl) node() {} func (decl *VarDecl) node() {} func (decl *FuncDecl) node() {} func (decl *FuncDef) node() {} @@ -100,6 +112,7 @@ func (decl *TypeAliasDecl) node() {} func (decl *TypeDefDecl) node() {} func (decl *BadDecl) String() string { return "BadDecl" } +func (decl *ConstDecl) String() string { return "ConstDecl" } func (decl *VarDecl) String() string { return "VarDecl" } func (decl *FuncDecl) String() string { return "FuncDecl" } func (decl *FuncDef) String() string { return "FuncDef" } @@ -108,6 +121,7 @@ func (decl *TypeAliasDecl) String() string { return "TypeAliasDecl" } func (decl *TypeDefDecl) String() string { return "TypeDefDecl" } func (decl *BadDecl) Token() token.Token { return decl.Tok } +func (decl *ConstDecl) Token() token.Token { return decl.NameTok } func (decl *VarDecl) Token() token.Token { return decl.NameTok } func (decl *FuncDecl) Token() token.Token { return decl.Tok } func (decl *FuncDef) Token() token.Token { return decl.Tok } @@ -116,6 +130,7 @@ func (decl *TypeAliasDecl) Token() token.Token { return decl.Tok } func (decl *TypeDefDecl) Token() token.Token { return decl.Tok } func (decl *BadDecl) GetRange() token.Range { return decl.Err.Range } +func (decl *ConstDecl) GetRange() token.Range { return decl.Range } func (decl *VarDecl) GetRange() token.Range { return decl.Range } func (decl *FuncDecl) GetRange() token.Range { return decl.Range } func (decl *FuncDef) GetRange() token.Range { return decl.Range } @@ -124,6 +139,7 @@ func (decl *TypeAliasDecl) GetRange() token.Range { return decl.Range } func (decl *TypeDefDecl) GetRange() token.Range { return decl.Range } func (decl *BadDecl) Accept(visitor FullVisitor) VisitResult { return visitor.VisitBadDecl(decl) } +func (decl *ConstDecl) Accept(visitor FullVisitor) VisitResult { return visitor.VisitConstDecl(decl) } func (decl *VarDecl) Accept(visitor FullVisitor) VisitResult { return visitor.VisitVarDecl(decl) } func (decl *FuncDecl) Accept(visitor FullVisitor) VisitResult { return visitor.VisitFuncDecl(decl) } func (decl *FuncDef) Accept(visitor FullVisitor) VisitResult { return visitor.VisitFuncDef(decl) } @@ -137,6 +153,7 @@ func (decl *TypeDefDecl) Accept(visitor FullVisitor) VisitResult { } func (decl *BadDecl) declarationNode() {} +func (decl *ConstDecl) declarationNode() {} func (decl *VarDecl) declarationNode() {} func (decl *FuncDecl) declarationNode() {} func (decl *FuncDef) statementNode() {} @@ -145,6 +162,7 @@ func (decl *TypeAliasDecl) declarationNode() {} func (decl *TypeDefDecl) declarationNode() {} func (decl *BadDecl) Name() string { return "" } +func (decl *ConstDecl) Name() string { return decl.NameTok.Literal } func (decl *VarDecl) Name() string { return decl.NameTok.Literal } func (decl *FuncDecl) Name() string { return decl.NameTok.Literal } func (decl *StructDecl) Name() string { return decl.NameTok.Literal } @@ -152,6 +170,7 @@ func (decl *TypeAliasDecl) Name() string { return decl.NameTok.Literal } func (decl *TypeDefDecl) Name() string { return decl.NameTok.Literal } func (decl *BadDecl) Public() bool { return false } +func (decl *ConstDecl) Public() bool { return decl.IsPublic } func (decl *VarDecl) Public() bool { return decl.IsPublic } func (decl *FuncDecl) Public() bool { return decl.IsPublic } func (decl *StructDecl) Public() bool { return decl.IsPublic } @@ -159,6 +178,7 @@ func (decl *TypeAliasDecl) Public() bool { return decl.IsPublic } func (decl *TypeDefDecl) Public() bool { return decl.IsPublic } func (decl *BadDecl) Comment() *token.Token { return nil } +func (decl *ConstDecl) Comment() *token.Token { return decl.CommentTok } func (decl *VarDecl) Comment() *token.Token { return decl.CommentTok } func (decl *FuncDecl) Comment() *token.Token { return decl.CommentTok } func (decl *StructDecl) Comment() *token.Token { return decl.CommentTok } @@ -166,6 +186,7 @@ func (decl *TypeAliasDecl) Comment() *token.Token { return decl.CommentTok } func (decl *TypeDefDecl) Comment() *token.Token { return decl.CommentTok } func (decl *BadDecl) Module() *Module { return decl.Mod } +func (decl *ConstDecl) Module() *Module { return decl.Mod } func (decl *VarDecl) Module() *Module { return decl.Mod } func (decl *FuncDecl) Module() *Module { return decl.Mod } func (decl *StructDecl) Module() *Module { return decl.Mod } diff --git a/src/ast/helper_visitor.go b/src/ast/helper_visitor.go index e3db355e..1144ebbe 100644 --- a/src/ast/helper_visitor.go +++ b/src/ast/helper_visitor.go @@ -172,6 +172,14 @@ func (h *helperVisitor) VisitBadDecl(decl *BadDecl) VisitResult { return VisitRecurse } +func (h *helperVisitor) VisitConstDecl(decl *ConstDecl) VisitResult { + result := VisitRecurse + if vis, ok := h.actualVisitor.(ConstDeclVisitor); ok { + result = vis.VisitConstDecl(decl) + } + return h.visitChildren(result, decl.Val) +} + func (h *helperVisitor) VisitVarDecl(decl *VarDecl) VisitResult { result := VisitRecurse if vis, ok := h.actualVisitor.(VarDeclVisitor); ok { diff --git a/src/ast/printer.go b/src/ast/printer.go index 5be570ae..b206ed1d 100644 --- a/src/ast/printer.go +++ b/src/ast/printer.go @@ -74,6 +74,15 @@ func (pr *printer) VisitBadDecl(decl *BadDecl) VisitResult { return VisitRecurse } +func (pr *printer) VisitConstDecl(decl *ConstDecl) VisitResult { + msg := fmt.Sprintf("ConstDecl[%s: %s]", decl.Name(), decl.Type) + if decl.CommentTok != nil { + msg += fmt.Sprintf(commentFmt, strings.Trim(decl.CommentTok.Literal, commentCutset), pr.currentIdent, " ") + } + pr.parenthesizeNode(msg, decl.Val) + return VisitRecurse +} + func (pr *printer) VisitVarDecl(decl *VarDecl) VisitResult { msg := fmt.Sprintf("VarDecl[%s: %s]", decl.Name(), decl.Type) if decl.CommentTok != nil { diff --git a/src/ast/visitor.go b/src/ast/visitor.go index 5e54b45a..c7898c64 100644 --- a/src/ast/visitor.go +++ b/src/ast/visitor.go @@ -25,6 +25,7 @@ type FullVisitor interface { */ BadDeclVisitor + ConstDeclVisitor VarDeclVisitor FuncDeclVisitor FuncDefVisitor @@ -80,6 +81,10 @@ type ( Visitor VisitBadDecl(*BadDecl) VisitResult } + ConstDeclVisitor interface { + Visitor + VisitConstDecl(*ConstDecl) VisitResult + } VarDeclVisitor interface { Visitor VisitVarDecl(*VarDecl) VisitResult @@ -247,6 +252,15 @@ func (f BadDeclVisitorFunc) VisitBadDecl(stmt *BadDecl) VisitResult { return f(stmt) } +type ConstDeclVisitorFunc func(*ConstDecl) VisitResult + +var _ ConstDeclVisitor = (ConstDeclVisitorFunc)(nil) + +func (ConstDeclVisitorFunc) Visitor() {} +func (f ConstDeclVisitorFunc) VisitConstDecl(stmt *ConstDecl) VisitResult { + return f(stmt) +} + type VarDeclVisitorFunc func(*VarDecl) VisitResult var _ VarDeclVisitor = (VarDeclVisitorFunc)(nil) diff --git a/src/compiler/compiler.go b/src/compiler/compiler.go index 1622a701..60e8ba87 100644 --- a/src/compiler/compiler.go +++ b/src/compiler/compiler.go @@ -456,6 +456,10 @@ func (c *compiler) VisitBadDecl(d *ast.BadDecl) ast.VisitResult { return ast.VisitRecurse } +func (c *compiler) VisitConstDecl(d *ast.ConstDecl) ast.VisitResult { + return ast.VisitRecurse +} + func (c *compiler) VisitVarDecl(d *ast.VarDecl) ast.VisitResult { // allocate the variable on the function call frame // all local variables are allocated in the first basic block of the function they are within diff --git a/src/ddperror/messages.go b/src/ddperror/messages.go index 81ff91b1..3b53fb6a 100644 --- a/src/ddperror/messages.go +++ b/src/ddperror/messages.go @@ -24,7 +24,7 @@ func MsgGotExpected(got any, expected ...any) string { } func MsgNameAlreadyExists(name string) string { - return fmt.Sprintf("Der Name %s steht bereits für eine Variable, Funktion oder Struktur", name) + return fmt.Sprintf("Der Name %s steht bereits für eine Variable, Konstante, Funktion oder Struktur", name) } func MsgAliasAlreadyExists(alias, name string, isFunc bool) string { diff --git a/src/parser/declarations.go b/src/parser/declarations.go index e1376374..25822b4a 100644 --- a/src/parser/declarations.go +++ b/src/parser/declarations.go @@ -66,6 +66,71 @@ func (p *parser) assignRhs(withComma bool) ast.Expression { return expr } +func (p *parser) matchExternSichtbar() bool { + if p.matchAny(token.COMMA) || p.matchAny(token.EXTERN) { + if p.previous().Type == token.COMMA { + p.consumeSeq(token.EXTERN) + } + p.consumeSeq(token.SICHTBARE) + return true + } + return false +} + +func (p *parser) constDeclaration(startDepth int) ast.Declaration { + begin := p.peekN(startDepth) // Die + if begin.Type != token.DIE { + p.err(ddperror.SYN_GENDER_MISMATCH, begin.Range, fmt.Sprintf("Falscher Artikel, meintest du %s?", "Die")) + } + + comment := p.parseDeclComment(begin.Range) + + isPublic := p.peekN(startDepth+1).Type == token.OEFFENTLICHE || p.peekN(startDepth+1).Type == token.OEFFENTLICHEN + isExternVisible := isPublic && p.matchExternSichtbar() + + p.consumeSeq(token.KONSTANTE) + + // we need a name, so bailout if none is provided + if !p.consumeSeq(token.IDENTIFIER) { + return &ast.BadDecl{ + Err: ddperror.Error{ + Range: token.NewRange(p.peekN(-2), p.peek()), + File: p.module.FileName, + Msg: "Es wurde ein Name für die Konstante erwartet", + }, + Tok: *p.peek(), + Mod: p.module, + } + } + name := p.previous() + + p.consumeSeq(token.IST) + + expr := p.assignRhs(false) // TODO: add support for lists "N Mal x" + p.consumeSeq(token.DOT) + + switch expr := expr.(type) { + case *ast.IntLit, *ast.FloatLit, *ast.BoolLit, *ast.StringLit, *ast.CharLit, *ast.ListLit: + default: + p.err(ddperror.SYN_EXPECTED_LITERAL, expr.GetRange(), "Es wurde ein Literal erwartet aber ein Ausdruck gefunden") + } + + // prefer trailing comments as long as they are on the same line + if trailingComment := p.commentAfterPos(p.previous().Range.End); trailingComment != nil && trailingComment.Range.Start.Line == p.previous().Range.End.Line { + comment = trailingComment + } + + return &ast.ConstDecl{ + Range: token.NewRange(begin, p.previous()), + Mod: p.module, + CommentTok: comment, + IsPublic: isPublic, + IsExternVisible: isExternVisible, + NameTok: *name, + Val: expr, + } +} + // parses a variable declaration // startDepth is the int passed to p.peekN(n) to get to the DER/DIE token of the declaration // isField indicates that this declaration should be parsed as a struct field @@ -74,15 +139,7 @@ func (p *parser) varDeclaration(startDepth int, isField bool) ast.Declaration { comment := p.parseDeclComment(begin.Range) isPublic := p.peekN(startDepth+1).Type == token.OEFFENTLICHE || p.peekN(startDepth+1).Type == token.OEFFENTLICHEN - - isExternVisible := false - if isPublic && p.matchAny(token.COMMA) || p.matchAny(token.EXTERN) { - if p.previous().Type == token.COMMA { - p.consumeSeq(token.EXTERN) - } - p.consumeSeq(token.SICHTBARE) - isExternVisible = true - } + isExternVisible := isPublic && p.matchExternSichtbar() type_start := p.peek() typ := p.parseType() @@ -122,7 +179,7 @@ func (p *parser) varDeclaration(startDepth int, isField bool) ast.Declaration { Err: ddperror.Error{ Range: token.NewRange(p.peekN(-2), p.peek()), File: p.module.FileName, - Msg: "Es wurde ein Variablen Name erwartet", + Msg: "Es wurde ein Name für die Variable erwartet", }, Tok: *p.peek(), Mod: p.module, @@ -158,6 +215,7 @@ func (p *parser) varDeclaration(startDepth int, isField bool) ast.Declaration { if !isField { p.consumeSeq(token.DOT) } + // prefer trailing comments as long as they are on the same line if trailingComment := p.commentAfterPos(p.previous().Range.End); trailingComment != nil && trailingComment.Range.Start.Line == p.previous().Range.End.Line { comment = trailingComment diff --git a/src/parser/parser.go b/src/parser/parser.go index 7ed914ec..d7da388b 100644 --- a/src/parser/parser.go +++ b/src/parser/parser.go @@ -185,6 +185,8 @@ func (p *parser) declaration() ast.Statement { case token.FUNKTION: p.advance() return p.funcDeclaration(n - 1) + case token.KONSTANTE: + return &ast.DeclStmt{Decl: p.constDeclaration(n)} default: return &ast.DeclStmt{Decl: p.varDeclaration(n, false)} } diff --git a/src/parser/resolver/resolver.go b/src/parser/resolver/resolver.go index e9ebabf3..491da993 100644 --- a/src/parser/resolver/resolver.go +++ b/src/parser/resolver/resolver.go @@ -81,6 +81,23 @@ func (r *Resolver) VisitBadDecl(decl *ast.BadDecl) ast.VisitResult { return ast.VisitRecurse } +func (r *Resolver) VisitConstDecl(decl *ast.ConstDecl) ast.VisitResult { + r.visit(decl.Val) + + // insert the variable into the current scope (SymbolTable) + if existed := r.CurrentTable.InsertDecl(decl.Name(), decl); existed { + r.err(ddperror.SEM_NAME_ALREADY_DEFINED, decl.NameTok.Range, ddperror.MsgNameAlreadyExists(decl.Name())) // variables may only be declared once in the same scope + } + + if decl.Public() && !ast.IsGlobalScope(r.CurrentTable) { + r.err(ddperror.SEM_NON_GLOBAL_PUBLIC_DECL, decl.NameTok.Range, "Nur globale Konstante können öffentlich sein") + } else if _, alreadyExists := r.Module.PublicDecls[decl.Name()]; decl.IsPublic && !alreadyExists { // insert the variable int othe public module decls + r.Module.PublicDecls[decl.Name()] = decl + } + + return ast.VisitRecurse +} + func (r *Resolver) VisitVarDecl(decl *ast.VarDecl) ast.VisitResult { r.visit(decl.InitVal) // resolve the initial value // insert the variable into the current scope (SymbolTable) diff --git a/src/parser/typechecker/assignable_checker.go b/src/parser/typechecker/assignable_checker.go index 0d16cf7c..a14ed937 100644 --- a/src/parser/typechecker/assignable_checker.go +++ b/src/parser/typechecker/assignable_checker.go @@ -19,7 +19,6 @@ func isAssignable(expr ast.Expression) (ast.Assigneable, bool) { default: return nil, false } - return nil, false } func isBinaryExprAssignable(expr *ast.BinaryExpr) (ast.Assigneable, bool) { diff --git a/src/parser/typechecker/typechecker.go b/src/parser/typechecker/typechecker.go index cf3086dd..61602768 100644 --- a/src/parser/typechecker/typechecker.go +++ b/src/parser/typechecker/typechecker.go @@ -103,6 +103,16 @@ func (t *Typechecker) VisitBadDecl(decl *ast.BadDecl) ast.VisitResult { return ast.VisitRecurse } +func (t *Typechecker) VisitConstDecl(decl *ast.ConstDecl) ast.VisitResult { + decl.Type = t.Evaluate(decl.Val) + + if decl.Public() && !IsPublicType(decl.Type, t.CurrentTable) { + t.err(ddperror.SEM_BAD_PUBLIC_MODIFIER, decl.Range, "Der Typ einer öffentlichen Konstante muss ebenfalls öffentlich sein") + } + + return ast.VisitRecurse +} + func (t *Typechecker) VisitVarDecl(decl *ast.VarDecl) ast.VisitResult { initialType := t.Evaluate(decl.InitVal) decl.InitType = initialType diff --git a/src/token/token_types.go b/src/token/token_types.go index 86b5bc0f..6beac64b 100644 --- a/src/token/token_types.go +++ b/src/token/token_types.go @@ -158,6 +158,7 @@ const ( ALLE MODULE INDEX + KONSTANTE DOT // . COMMA // , @@ -325,6 +326,7 @@ var tokenStrings = [...]string{ ALLE: "alle", MODULE: "Module", INDEX: "Index", + KONSTANTE: "Konstante", DOT: ".", COMMA: ",", @@ -497,6 +499,7 @@ var KeywordMap = map[string]TokenType{ "alle": ALLE, "Module": MODULE, "Index": INDEX, + "Konstante": KONSTANTE, } func KeywordToTokenType(keyword string) TokenType {