aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--GRAMMAR.txt8
-rw-r--r--TYPE_SYSTEM.md24
-rw-r--r--decl.go25
-rw-r--r--expression.go33
-rw-r--r--generate.go38
-rw-r--r--generate_error.go16
-rw-r--r--lex.go4
-rw-r--r--parse.go107
-rw-r--r--token.go6
9 files changed, 255 insertions, 6 deletions
diff --git a/GRAMMAR.txt b/GRAMMAR.txt
index 9e30c24..4882ff8 100644
--- a/GRAMMAR.txt
+++ b/GRAMMAR.txt
@@ -10,9 +10,15 @@ param -> IDENTIFIER IDENTIFIER ( "," param )?
block -> "{" ( statement )* "}"
-statement -> ( return ) ";"
+statement -> ( return | declaration | assignment ) ";"
return -> "return" expression
+declaration -> constant | mutable
+constant -> "const" IDENTIFIER "=" expression
+mutable -> "mut" IDENTIFIER "=" expression
+
+assignment -> "assign" IDENTIFIER "=" expression
+
expression -> equality
equality -> comparison ( ( "==" | "!=" ) comparison )*
comparison -> term ( ( "<" | "<=" | ">" | ">=" ) term )?
diff --git a/TYPE_SYSTEM.md b/TYPE_SYSTEM.md
new file mode 100644
index 0000000..56baa91
--- /dev/null
+++ b/TYPE_SYSTEM.md
@@ -0,0 +1,24 @@
+<!--
+SPDX-FileCopyrightText: 2024 Himbeer <himbeer@disroot.org>
+
+SPDX-License-Identifier: CC-BY-NC-SA-4.0
+-->
+
+# Primitive types
+
+* int8, uint8 -> QBE b, w
+* int16, uint16 -> QBE h, w
+* int32, uint32 -> QBE w, w
+* int64, uint64 -> QBE l, l
+
+# Coercion
+
+* Integer widening
+
+# Declaration and mutability
+
+* Variables and constants need to be declared before being used
+* All declarations (including globals and implicit declarations from imports) must be above their first use
+* Redeclarations ("shadowing") are forbidden
+* Variables may be changed using the "assign" statement
+* Constants cannot be changed
diff --git a/decl.go b/decl.go
new file mode 100644
index 0000000..32adbae
--- /dev/null
+++ b/decl.go
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: 2024 Himbeer <himbeer@disroot.org>
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package main
+
+import (
+ "fmt"
+)
+
+type mutInfo struct {
+ id string
+ nameIndex int
+}
+
+func (m mutInfo) name() string {
+ return fmt.Sprintf("%%%s%d", m.id, m.nameIndex-1)
+}
+
+func (m *mutInfo) nextName() string {
+ defer func() { m.nameIndex++ }()
+ return fmt.Sprintf("%%%s%d", m.id, m.nameIndex)
+}
+
+var locals = map[string]map[string]mutInfo{}
diff --git a/expression.go b/expression.go
index c62918c..7693f79 100644
--- a/expression.go
+++ b/expression.go
@@ -101,6 +101,39 @@ func (r *returnStmt) markStmt() {}
func (r *returnStmt) line() int { return r.ln }
+type declStmt interface {
+ statementExpr
+ markDecl()
+}
+
+type constStmt struct {
+ name string
+ initial exprExpr
+ ln int
+}
+
+func (c *constStmt) markExpr() {}
+
+func (c *constStmt) markStmt() {}
+
+func (c *constStmt) markDecl() {}
+
+func (c *constStmt) line() int { return c.ln }
+
+type mutStmt struct {
+ name string
+ initial exprExpr
+ ln int
+}
+
+func (m *mutStmt) markExpr() {}
+
+func (m *mutStmt) markStmt() {}
+
+func (m *mutStmt) markDecl() {}
+
+func (m *mutStmt) line() int { return m.ln }
+
type exprExpr interface {
expression
markExprExpr()
diff --git a/generate.go b/generate.go
index 94f577d..432bd25 100644
--- a/generate.go
+++ b/generate.go
@@ -11,6 +11,8 @@ import (
"strings"
)
+var currentFunc string
+
var funcRegIndex int
func allocReg() string {
@@ -78,6 +80,8 @@ func generateToplevel(toplevel expression, w io.Writer) error {
func generateFunction(function *functionExpr, w io.Writer) error {
resetRegs()
+ currentFunc = function.name
+ locals[function.name] = map[string]mutInfo{}
if function.link != defaultLinkage {
fmt.Fprintf(w, "%s ", function.link)
@@ -108,6 +112,10 @@ func generateStatement(stmt statementExpr, w io.Writer) error {
switch s := stmt.(type) {
case *returnStmt:
return generateReturnStmt(s, w)
+ case *constStmt:
+ return generateConstStmt(s, false, w)
+ case *mutStmt:
+ return generateMutStmt(s, false, w)
}
return nil
@@ -124,6 +132,36 @@ func generateReturnStmt(stmt *returnStmt, w io.Writer) error {
return nil
}
+func generateConstStmt(stmt *constStmt, toplevel bool, w io.Writer) error {
+ value, err := stmt.initial.generate(w)
+ if err != nil {
+ return err
+ }
+
+ fmt.Fprintf(w, "%%%s =w add %s, 0\n", stmt.name, value)
+ return nil
+}
+
+func generateMutStmt(stmt *mutStmt, toplevel bool, w io.Writer) error {
+ value, err := stmt.initial.generate(w)
+ if err != nil {
+ return err
+ }
+
+ if _, ok := locals[currentFunc][stmt.name]; ok {
+ return errAlreadyDeclared{name: stmt.name, line: stmt.line()}
+ }
+
+ mutable := mutInfo{
+ id: stmt.name,
+ }
+ locals[currentFunc][stmt.name] = mutable
+
+ fmt.Fprintf(w, "%s =l alloc4 8\n", mutable.nextName())
+ fmt.Fprintf(w, "storew %s, %s\n", value, mutable.name())
+ return nil
+}
+
func (e *equalityExpr) generate(w io.Writer) (string, error) {
lhs, err := e.lhs.generate(w)
if err != nil {
diff --git a/generate_error.go b/generate_error.go
new file mode 100644
index 0000000..7884151
--- /dev/null
+++ b/generate_error.go
@@ -0,0 +1,16 @@
+// SPDX-FileCopyrightText: 2024 Himbeer <himbeer@disroot.org>
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package main
+
+import "fmt"
+
+type errAlreadyDeclared struct {
+ name string
+ line int
+}
+
+func (e errAlreadyDeclared) Error() string {
+ return fmt.Sprintf("%d: redeclaration of %q\n", e.line, e.name)
+}
diff --git a/lex.go b/lex.go
index 6b76953..7450888 100644
--- a/lex.go
+++ b/lex.go
@@ -296,6 +296,10 @@ func addKeywordOrIdentifier(name string, line int) token {
return token{kind: function, line: line}
case "return":
return token{kind: ret, line: line}
+ case "const":
+ return token{kind: constKeyword, line: line}
+ case "mut":
+ return token{kind: mut, line: line}
default:
return token{kind: identifier, value: name, line: line}
}
diff --git a/parse.go b/parse.go
index 5261dfe..fa01c52 100644
--- a/parse.go
+++ b/parse.go
@@ -236,7 +236,6 @@ func parseBlock(toks *tokens) (*blockExpr, error) {
return nil, err
}
if stmt == nil {
- expectStmt = true
break
}
@@ -255,18 +254,38 @@ func parseBlock(toks *tokens) (*blockExpr, error) {
}
func parseStatement(toks *tokens) (statementExpr, error) {
+ stmt, err := parseStatementContents(toks)
+ if err != nil {
+ return nil, err
+ }
+ if stmt == nil {
+ return nil, nil
+ }
+
+ if err := toks.mustMatch(semicolon); err != nil {
+ return nil, err
+ }
+
+ return stmt, nil
+}
+
+func parseStatementContents(toks *tokens) (statementExpr, error) {
stmt, err := parseReturnStmt(toks)
if err != nil {
return nil, err
}
if stmt != nil {
- if err := toks.mustMatch(semicolon); err != nil {
- return nil, err
- }
-
return stmt, nil
}
+ decl, err := parseDeclaration(toks)
+ if err != nil {
+ return nil, err
+ }
+ if decl != nil {
+ return decl, nil
+ }
+
return nil, nil
}
@@ -288,6 +307,84 @@ func parseReturnStmt(toks *tokens) (*returnStmt, error) {
return &returnStmt{value: expr, ln: toks.current().line}, nil
}
+func parseDeclaration(toks *tokens) (declStmt, error) {
+ constant, err := parseConstStmt(toks)
+ if err != nil {
+ return nil, err
+ }
+ if constant != nil {
+ return constant, nil
+ }
+
+ mutable, err := parseMutStmt(toks)
+ if err != nil {
+ return nil, err
+ }
+ if mutable != nil {
+ return mutable, nil
+ }
+
+ return nil, nil
+}
+
+func parseConstStmt(toks *tokens) (*constStmt, error) {
+ ok, err := toks.match(constKeyword)
+ if err != nil {
+ return nil, err
+ }
+ if !ok {
+ toks.unreadToken()
+ return nil, nil
+ }
+
+ line := toks.current().line
+
+ nameTok, ok := toks.consumeToken()
+ if !ok {
+ return nil, unexpectedEOF
+ }
+
+ if err := toks.mustMatch(equals); err != nil {
+ return nil, err
+ }
+
+ expr, err := parseExpression(toks)
+ if err != nil {
+ return nil, err
+ }
+
+ return &constStmt{name: nameTok.value, initial: expr, ln: line}, nil
+}
+
+func parseMutStmt(toks *tokens) (*mutStmt, error) {
+ ok, err := toks.match(mut)
+ if err != nil {
+ return nil, err
+ }
+ if !ok {
+ toks.unreadToken()
+ return nil, nil
+ }
+
+ line := toks.current().line
+
+ nameTok, ok := toks.consumeToken()
+ if !ok {
+ return nil, unexpectedEOF
+ }
+
+ if err := toks.mustMatch(equals); err != nil {
+ return nil, err
+ }
+
+ expr, err := parseExpression(toks)
+ if err != nil {
+ return nil, err
+ }
+
+ return &mutStmt{name: nameTok.value, initial: expr, ln: line}, nil
+}
+
func parseExpression(toks *tokens) (exprExpr, error) {
return parseEquality(toks)
}
diff --git a/token.go b/token.go
index d4a5d33..20d4727 100644
--- a/token.go
+++ b/token.go
@@ -100,6 +100,10 @@ func (k tokenKind) String() string {
return "func"
case ret:
return "return"
+ case constKeyword:
+ return "const"
+ case mut:
+ return "mut"
}
return "invalid token"
@@ -153,6 +157,8 @@ const (
export
function
ret
+ constKeyword
+ mut
)
type token struct {