diff options
-rw-r--r-- | GRAMMAR.txt | 7 | ||||
-rw-r--r-- | expression.go | 22 | ||||
-rw-r--r-- | generate.go | 20 | ||||
-rw-r--r-- | parse.go | 189 |
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 { @@ -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 |