aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TYPE_SYSTEM.md5
-rw-r--r--decl.go14
-rw-r--r--expression.go14
-rw-r--r--generate.go26
-rw-r--r--generate_error.go18
-rw-r--r--lex.go2
-rw-r--r--parse.go37
-rw-r--r--token.go3
8 files changed, 107 insertions, 12 deletions
diff --git a/TYPE_SYSTEM.md b/TYPE_SYSTEM.md
index 56baa91..fb45232 100644
--- a/TYPE_SYSTEM.md
+++ b/TYPE_SYSTEM.md
@@ -17,8 +17,9 @@ SPDX-License-Identifier: CC-BY-NC-SA-4.0
# Declaration and mutability
-* Variables and constants need to be declared before being used
+* Variables 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
+* Constants cannot be changed, but mutables can
+* Local mutables are allocated on the stack, local constants are QBE temporaries
diff --git a/decl.go b/decl.go
index 179acb2..67d641f 100644
--- a/decl.go
+++ b/decl.go
@@ -8,7 +8,7 @@ var funcs = map[string]struct{}{}
var localConsts = map[string]map[string]string{}
var localMuts = map[string]map[string]struct{}{}
-func isDeclared(name string, toplevel bool) bool {
+func isDeclared(name string, toplevel bool) (ok bool, mutable bool) {
if toplevel {
return isDeclaredToplevel(name)
}
@@ -16,13 +16,13 @@ func isDeclared(name string, toplevel bool) bool {
return isDeclaredLocal(currentFunc, name)
}
-func isDeclaredToplevel(name string) bool {
- _, ok := funcs[name]
- return ok
+func isDeclaredToplevel(name string) (ok bool, mutable bool) {
+ _, ok = funcs[name]
+ return
}
-func isDeclaredLocal(function string, local string) bool {
+func isDeclaredLocal(function string, local string) (ok bool, mutable bool) {
_, constant := localConsts[function][local]
- _, mutable := localMuts[function][local]
- return constant || mutable
+ _, mutable = localMuts[function][local]
+ return constant || mutable, mutable
}
diff --git a/expression.go b/expression.go
index 7693f79..285cac3 100644
--- a/expression.go
+++ b/expression.go
@@ -134,6 +134,20 @@ func (m *mutStmt) markDecl() {}
func (m *mutStmt) line() int { return m.ln }
+type assignStmt struct {
+ name string
+ value exprExpr
+ ln int
+}
+
+func (a *assignStmt) markExpr() {}
+
+func (a *assignStmt) markStmt() {}
+
+func (a *assignStmt) markDecl() {}
+
+func (a *assignStmt) line() int { return a.ln }
+
type exprExpr interface {
expression
markExprExpr()
diff --git a/generate.go b/generate.go
index 511ce05..187ec8f 100644
--- a/generate.go
+++ b/generate.go
@@ -80,7 +80,7 @@ func generateToplevel(toplevel expression, w io.Writer) error {
}
func generateFunction(function *functionExpr, w io.Writer) error {
- if isDeclared(function.name, true) {
+ if ok, _ := isDeclared(function.name, true); ok {
return errAlreadyDeclared{name: function.name, line: function.line()}
}
@@ -126,6 +126,8 @@ func generateStatement(stmt statementExpr, w io.Writer) error {
return generateConstStmt(s, false, w)
case *mutStmt:
return generateMutStmt(s, false, w)
+ case *assignStmt:
+ return generateAssignStmt(s, w)
}
return nil
@@ -143,7 +145,7 @@ func generateReturnStmt(stmt *returnStmt, w io.Writer) error {
}
func generateConstStmt(stmt *constStmt, toplevel bool, w io.Writer) error {
- if isDeclared(stmt.name, toplevel) {
+ if ok, _ := isDeclared(stmt.name, toplevel); ok {
return errAlreadyDeclared{name: stmt.name, line: stmt.line()}
}
@@ -159,7 +161,7 @@ func generateConstStmt(stmt *constStmt, toplevel bool, w io.Writer) error {
}
func generateMutStmt(stmt *mutStmt, toplevel bool, w io.Writer) error {
- if isDeclared(stmt.name, toplevel) {
+ if ok, _ := isDeclared(stmt.name, toplevel); ok {
return errAlreadyDeclared{name: stmt.name, line: stmt.line()}
}
@@ -175,6 +177,24 @@ func generateMutStmt(stmt *mutStmt, toplevel bool, w io.Writer) error {
return nil
}
+func generateAssignStmt(stmt *assignStmt, w io.Writer) error {
+ ok, mutable := isDeclared(stmt.name, false)
+ if !ok {
+ return errUndeclared{name: stmt.name, line: stmt.line()}
+ }
+ if !mutable {
+ return errImmutable{name: stmt.name, line: stmt.line()}
+ }
+
+ value, err := stmt.value.generate(w)
+ if err != nil {
+ return err
+ }
+
+ fmt.Fprintf(w, "storew %s, %%%s\n", value, stmt.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
index 7884151..8dfd964 100644
--- a/generate_error.go
+++ b/generate_error.go
@@ -14,3 +14,21 @@ type errAlreadyDeclared struct {
func (e errAlreadyDeclared) Error() string {
return fmt.Sprintf("%d: redeclaration of %q\n", e.line, e.name)
}
+
+type errUndeclared struct {
+ name string
+ line int
+}
+
+func (e errUndeclared) Error() string {
+ return fmt.Sprintf("%d: undeclared variable %q\n", e.line, e.name)
+}
+
+type errImmutable struct {
+ name string
+ line int
+}
+
+func (e errImmutable) Error() string {
+ return fmt.Sprintf("%d: cannot assign to constant %q\n", e.line, e.name)
+}
diff --git a/lex.go b/lex.go
index 7450888..961291a 100644
--- a/lex.go
+++ b/lex.go
@@ -300,6 +300,8 @@ func addKeywordOrIdentifier(name string, line int) token {
return token{kind: constKeyword, line: line}
case "mut":
return token{kind: mut, line: line}
+ case "assign":
+ return token{kind: assign, line: line}
default:
return token{kind: identifier, value: name, line: line}
}
diff --git a/parse.go b/parse.go
index 4704ed3..a9c7b9d 100644
--- a/parse.go
+++ b/parse.go
@@ -326,6 +326,14 @@ func parseDeclaration(toks *tokens) (declStmt, error) {
return mutable, nil
}
+ assignment, err := parseAssignStmt(toks)
+ if err != nil {
+ return nil, err
+ }
+ if assignment != nil {
+ return assignment, nil
+ }
+
return nil, nil
}
@@ -387,6 +395,35 @@ func parseMutStmt(toks *tokens) (*mutStmt, error) {
return &mutStmt{name: nameTok.value, initial: expr, ln: line}, nil
}
+func parseAssignStmt(toks *tokens) (*assignStmt, error) {
+ ok, err := toks.match(assign)
+ 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 &assignStmt{name: nameTok.value, value: expr, ln: line}, nil
+}
+
func parseExpression(toks *tokens) (exprExpr, error) {
return parseEquality(toks)
}
diff --git a/token.go b/token.go
index 20d4727..14d23a1 100644
--- a/token.go
+++ b/token.go
@@ -104,6 +104,8 @@ func (k tokenKind) String() string {
return "const"
case mut:
return "mut"
+ case assign:
+ return "assign"
}
return "invalid token"
@@ -159,6 +161,7 @@ const (
ret
constKeyword
mut
+ assign
)
type token struct {