aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHimbeer <himbeer@disroot.org>2024-09-05 13:26:48 +0200
committerHimbeer <himbeer@disroot.org>2024-09-05 13:35:38 +0200
commitfdfcd6d8d0fe79138a2e5bf66ae7c476c8db9c75 (patch)
treea8f252fdc893038eaef50e0771c8065ea16c0d51
parent413e267d0ed01c426002ec6488f05a5d7a619e5a (diff)
Switch to signature-based extern function declarations
-rw-r--r--GRAMMAR.txt7
-rw-r--r--expression.go22
-rw-r--r--generate.go20
-rw-r--r--parse.go189
4 files changed, 181 insertions, 57 deletions
diff --git a/GRAMMAR.txt b/GRAMMAR.txt
index d11aa3c..e1d94d3 100644
--- a/GRAMMAR.txt
+++ b/GRAMMAR.txt
@@ -3,11 +3,14 @@
// SPDX-License-Identifier: CC-BY-NC-SA-4.0
root -> toplevel*
-toplevel -> function
+toplevel -> function | externfunc
-function -> ( "export" | "extern" )? "func" IDENTIFIER "(" param? ")" IDENTIFIER block?
+function -> "export"? "func" IDENTIFIER "(" param? ")" IDENTIFIER block?
param -> IDENTIFIER IDENTIFIER ( "," param )?
+externfunc -> "extern" "func" IDENTIFIER "(" signature? ")" IDENTIFIER ";"
+signature -> IDENTIFIER ( "," signature )?
+
block -> "{" ( statement )* "}"
statement -> ( return | declaration | assignment ) ";"
diff --git a/expression.go b/expression.go
index 6ab30c2..76cb16b 100644
--- a/expression.go
+++ b/expression.go
@@ -41,7 +41,6 @@ func (l linkage) String() string {
type functionExpr struct {
link linkage
- external bool
name string
params *paramExpr
returnType string
@@ -77,6 +76,27 @@ func (p *paramExpr) String() string {
return s
}
+type externFuncExpr struct {
+ name string
+ params *signatureExpr
+ returnType string
+ ln int
+}
+
+func (e *externFuncExpr) markExpr() {}
+
+func (e *externFuncExpr) line() int { return e.ln }
+
+type signatureExpr struct {
+ typ string
+ next *signatureExpr
+ ln int
+}
+
+func (s *signatureExpr) markExpr() {}
+
+func (s *signatureExpr) line() int { return s.ln }
+
type blockExpr struct {
stmts []statementExpr
ln int
diff --git a/generate.go b/generate.go
index a5ddf7f..f09ec42 100644
--- a/generate.go
+++ b/generate.go
@@ -74,6 +74,8 @@ func generateToplevel(toplevel expression, w io.Writer) error {
switch expr := toplevel.(type) {
case *functionExpr:
return generateFunction(expr, w)
+ case *externFuncExpr:
+ return generateExternFunc(expr, w)
}
return invalidToplevel{got: toplevel}
@@ -81,7 +83,10 @@ func generateToplevel(toplevel expression, w io.Writer) error {
func generateFunction(function *functionExpr, w io.Writer) error {
if ok, _ := isDeclared(function.name, true); ok {
- return errAlreadyDeclared{name: function.name, line: function.line()}
+ return errAlreadyDeclared{
+ name: function.name,
+ line: function.line(),
+ }
}
resetRegs()
@@ -91,10 +96,6 @@ func generateFunction(function *functionExpr, w io.Writer) error {
funcs[currentFunc] = struct{}{}
- if function.external {
- return nil
- }
-
localConsts[currentFunc] = map[string]string{}
localMuts[currentFunc] = map[string]struct{}{}
@@ -113,6 +114,15 @@ func generateFunction(function *functionExpr, w io.Writer) error {
return nil
}
+func generateExternFunc(e *externFuncExpr, w io.Writer) error {
+ if ok, _ := isDeclared(e.name, true); ok {
+ return errAlreadyDeclared{name: e.name, line: e.line()}
+ }
+
+ funcs[e.name] = struct{}{}
+ return nil
+}
+
func generateBlock(blk *blockExpr, w io.Writer) error {
for _, stmt := range blk.stmts {
if err := generateStatement(stmt, w); err != nil {
diff --git a/parse.go b/parse.go
index d2f9990..4983c65 100644
--- a/parse.go
+++ b/parse.go
@@ -72,7 +72,13 @@ func parse(toks *tokens, root chan<- *rootExpr, errs chan<- error) {
func parseRoot(toks *tokens, errs chan<- error) *rootExpr {
toplevels := make([]expression, 0)
for !toks.eof() {
- if toplevel := parseToplevel(toks, errs); toplevel != nil {
+ toplevel, err := parseToplevel(toks)
+ if err != nil {
+ errs <- err
+ continue
+ }
+
+ if toplevel != nil {
toplevels = append(toplevels, toplevel)
}
}
@@ -82,114 +88,103 @@ func parseRoot(toks *tokens, errs chan<- error) *rootExpr {
}
}
-func parseToplevel(toks *tokens, errs chan<- error) expression {
- function := parseFunction(toks, errs)
- if function == nil {
- errs <- expectedToplevel{got: toks.current()}
- return nil
+func parseToplevel(toks *tokens) (expression, error) {
+ function, err := parseFunction(toks)
+ if err != nil {
+ return nil, err
+ }
+ if function != nil {
+ return function, nil
+ }
+
+ externFunc, err := parseExternFunc(toks)
+ if err != nil {
+ return nil, err
+ }
+ if externFunc != nil {
+ return externFunc, nil
}
- return function
+ return nil, expectedToplevel{got: toks.current()}
}
-func parseFunction(toks *tokens, errs chan<- error) *functionExpr {
+func parseFunction(toks *tokens) (*functionExpr, error) {
+ link := exportLinkage
+
exported, err := toks.match(export)
if err != nil {
- errs <- err
- return nil
+ return nil, err
}
line := toks.current().line
- var external bool
if !exported {
toks.unreadToken()
-
- external, err = toks.match(extern)
- if err != nil {
- errs <- err
- return nil
- }
- if !external {
- toks.unreadToken()
- }
- }
-
- var link linkage
- switch {
- case exported:
- link = exportLinkage
+ link = defaultLinkage
}
ok, err := toks.match(function)
if err != nil {
- errs <- err
- return nil
+ return nil, err
}
if !ok {
- return nil
+ toks.unreadToken()
+ if exported {
+ toks.unreadToken()
+ }
+ return nil, nil
}
nameTok, ok := toks.consumeToken()
if !ok {
- errs <- unexpectedEOF
- return nil
+ return nil, unexpectedEOF
}
if nameTok.kind != identifier {
- errs <- expected{want: identifier, got: nameTok}
- return nil
+ return nil, expected{want: identifier, got: nameTok}
}
if err := toks.mustMatch(lparen); err != nil {
- errs <- err
- return nil
+ return nil, err
}
var params *paramExpr
ok, err = toks.match(rparen)
if err != nil {
- errs <- err
- return nil
+ return nil, err
}
if !ok {
toks.unreadToken()
params, err = parseParam(toks)
if err != nil {
- errs <- err
- return nil
+ return nil, err
}
if err := toks.mustMatch(rparen); err != nil {
- errs <- err
- return nil
+ return nil, err
}
}
returnTypeTok, ok := toks.consumeToken()
if !ok {
- errs <- unexpectedEOF
- return nil
+ return nil, unexpectedEOF
}
if returnTypeTok.kind != identifier {
- errs <- expected{want: identifier, got: returnTypeTok}
- return nil
+ return nil, expected{want: identifier, got: returnTypeTok}
}
blk, err := parseBlock(toks)
if err != nil {
- errs <- err
- return nil
+ return nil, err
}
return &functionExpr{
link: link,
- external: external,
name: nameTok.value,
params: params,
returnType: returnTypeTok.value,
blk: blk,
ln: line,
- }
+ }, nil
}
func parseParam(toks *tokens) (*paramExpr, error) {
@@ -231,6 +226,102 @@ func parseParam(toks *tokens) (*paramExpr, error) {
}, nil
}
+func parseExternFunc(toks *tokens) (*externFuncExpr, error) {
+ ok, err := toks.match(extern)
+ if err != nil {
+ return nil, err
+ }
+ if !ok {
+ toks.unreadToken()
+ return nil, nil
+ }
+ line := toks.current().line
+
+ if err := toks.mustMatch(function); err != nil {
+ return nil, err
+ }
+
+ nameTok, ok := toks.consumeToken()
+ if !ok {
+ return nil, unexpectedEOF
+ }
+ if nameTok.kind != identifier {
+ return nil, expected{want: identifier, got: nameTok}
+ }
+
+ if err := toks.mustMatch(lparen); err != nil {
+ return nil, err
+ }
+
+ var params *signatureExpr
+
+ ok, err = toks.match(rparen)
+ if err != nil {
+ return nil, err
+ }
+ if !ok {
+ toks.unreadToken()
+
+ params, err = parseSignature(toks)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := toks.mustMatch(rparen); err != nil {
+ return nil, err
+ }
+ }
+
+ returnTypeTok, ok := toks.consumeToken()
+ if !ok {
+ return nil, unexpectedEOF
+ }
+ if returnTypeTok.kind != identifier {
+ return nil, expected{want: identifier, got: returnTypeTok}
+ }
+
+ if err := toks.mustMatch(semicolon); err != nil {
+ return nil, err
+ }
+ return &externFuncExpr{
+ name: nameTok.value,
+ params: params,
+ returnType: returnTypeTok.value,
+ ln: line,
+ }, nil
+}
+
+func parseSignature(toks *tokens) (*signatureExpr, error) {
+ typeTok, ok := toks.consumeToken()
+ if !ok {
+ return nil, unexpectedEOF
+ }
+ if typeTok.kind != identifier {
+ return nil, expected{want: identifier, got: typeTok}
+ }
+
+ var next *signatureExpr
+
+ ok, err := toks.match(comma)
+ if err != nil {
+ return nil, err
+ }
+ if ok {
+ next, err = parseSignature(toks)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ toks.unreadToken()
+ }
+
+ return &signatureExpr{
+ typ: typeTok.value,
+ next: next,
+ ln: typeTok.line,
+ }, nil
+}
+
func parseBlock(toks *tokens) (*blockExpr, error) {
if err := toks.mustMatch(lbrace); err != nil {
return nil, err