From 1bff92c52b27b9a516599e172fe9852c3d99be38 Mon Sep 17 00:00:00 2001 From: Marc Vertes Date: Tue, 2 Apr 2024 11:27:13 +0200 Subject: chore: add linters and some lint fixes (#8) * chore: add linters and some lint fixes Configure some golangci-lint linters to get the code quality right. Apply the first fixes. Next step will be to add github actions to run lint and tests in github CI. * chore: more lint, fixed comments and variable names. no semantic change. * chore: add Makefile This makefile is intended to be used as a local substitute to github actions. --- parser/compiler.go | 18 ++++-- parser/decl.go | 42 ++++++------- parser/expr.go | 68 ++++++++++----------- parser/interpreter.go | 3 + parser/parse.go | 161 ++++++++++++++++++++++++++------------------------ parser/symbol.go | 1 + parser/tokens.go | 21 ++++--- parser/type.go | 33 ++++++----- 8 files changed, 185 insertions(+), 162 deletions(-) (limited to 'parser') diff --git a/parser/compiler.go b/parser/compiler.go index 57e176f..7a90597 100644 --- a/parser/compiler.go +++ b/parser/compiler.go @@ -12,6 +12,7 @@ import ( "github.com/mvertes/parscan/vm" ) +// Compiler represents the state of a compiler. type Compiler struct { *Parser vm.Code // produced code, to fill VM with @@ -21,6 +22,7 @@ type Compiler struct { strings map[string]int // locations of strings in Data } +// NewCompiler returns a new compiler state for a given scanner. func NewCompiler(scanner *scanner.Scanner) *Compiler { return &Compiler{ Parser: &Parser{Scanner: scanner, symbols: initUniverse(), framelen: map[string]int{}, labelCount: map[string]int{}}, @@ -29,6 +31,7 @@ func NewCompiler(scanner *scanner.Scanner) *Compiler { } } +// AddSym adds a new named value to the compiler symbol table, and returns its index in memory. func (c *Compiler) AddSym(name string, value vm.Value) int { p := len(c.Data) c.Data = append(c.Data, value) @@ -36,6 +39,7 @@ func (c *Compiler) AddSym(name string, value vm.Value) int { return p } +// Codegen generates vm code from parsed tokens. func (c *Compiler) Codegen(tokens Tokens) (err error) { log.Println("Codegen tokens:", tokens) fixList := Tokens{} // list of tokens to fix after we gathered all necessary information @@ -46,7 +50,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { pop := func() *symbol { l := len(stack) - 1; s := stack[l]; stack = stack[:l]; return s } for i, t := range tokens { - switch t.Id { + switch t.Tok { case lang.Int: n, err := strconv.Atoi(t.Str) if err != nil { @@ -145,7 +149,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { case lang.Assign: st := tokens[i-1] - if st.Id == lang.Period || st.Id == lang.Index { + if st.Tok == lang.Period || st.Tok == lang.Index { emit(int64(t.Pos), vm.Vassign) break } @@ -182,7 +186,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { case lang.Ident: if i < len(tokens)-1 { - switch t1 := tokens[i+1]; t1.Id { + switch t1 := tokens[i+1]; t1.Tok { case lang.Define, lang.Assign, lang.Colon: continue } @@ -317,9 +321,10 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { return err } -func arithmeticOpType(s1, s2 *symbol) *vm.Type { return symtype(s1) } -func booleanOpType(s1, s2 *symbol) *vm.Type { return vm.TypeOf(true) } +func arithmeticOpType(s1, _ *symbol) *vm.Type { return symtype(s1) } +func booleanOpType(_, _ *symbol) *vm.Type { return vm.TypeOf(true) } +// PrintCode pretty prints the generated code in compiler. func (c *Compiler) PrintCode() { labels := map[int][]string{} // labels indexed by code location data := map[int]string{} // data indexed by frame location @@ -379,6 +384,7 @@ func (e entry) String() string { return e.name } +// PrintData pretty prints the generated global data symbols in compiler. func (c *Compiler) PrintData() { dict := c.symbolsByIndex() @@ -400,10 +406,12 @@ func (c *Compiler) symbolsByIndex() map[int]entry { return dict } +// Dump represents the state of a data dump. type Dump struct { Values []*DumpValue } +// DumpValue is a value of a dump state. type DumpValue struct { Index int Name string diff --git a/parser/decl.go b/parser/decl.go index 7638b75..b1cd13b 100644 --- a/parser/decl.go +++ b/parser/decl.go @@ -15,11 +15,11 @@ import ( var nilValue = vm.ValueOf(nil) -func (p *Parser) ParseConst(in Tokens) (out Tokens, err error) { +func (p *Parser) parseConst(in Tokens) (out Tokens, err error) { if len(in) < 2 { return out, errors.New("missing expression") } - if in[1].Id != lang.ParenBlock { + if in[1].Tok != lang.ParenBlock { return p.parseConstLine(in[1:]) } if in, err = p.Scan(in[1].Block(), false); err != nil { @@ -53,7 +53,7 @@ func (p *Parser) parseConstLine(in Tokens) (out Tokens, err error) { } var vars []string if _, vars, err = p.parseParamTypes(decl, parseTypeVar); err != nil { - if errors.Is(err, MissingTypeErr) { + if errors.Is(err, ErrMissingType) { for _, lt := range decl.Split(lang.Comma) { vars = append(vars, lt[0].Str) name := strings.TrimPrefix(p.scope+"/"+lt[0].Str, "/") @@ -68,7 +68,7 @@ func (p *Parser) parseConstLine(in Tokens) (out Tokens, err error) { values = nil } for i, v := range values { - if v, err = p.ParseExpr(v); err != nil { + if v, err = p.parseExpr(v); err != nil { return out, err } cval, _, err := p.evalConstExpr(v) @@ -95,7 +95,7 @@ func (p *Parser) evalConstExpr(in Tokens) (cval constant.Value, length int, err return nil, 0, errors.New("missing argument") } t := in[l] - id := t.Id + id := t.Tok switch { case id.IsBinaryOp(): op1, l1, err := p.evalConstExpr(in[:l]) @@ -163,7 +163,7 @@ func constValue(c constant.Value) any { return nil } -var gotok = map[lang.TokenId]token.Token{ +var gotok = map[lang.Token]token.Token{ lang.Char: token.CHAR, lang.Imag: token.IMAG, lang.Int: token.INT, @@ -191,14 +191,14 @@ var gotok = map[lang.TokenId]token.Token{ lang.Not: token.NOT, } -func (p *Parser) ParseImport(in Tokens) (out Tokens, err error) { +func (p *Parser) parseImports(in Tokens) (out Tokens, err error) { if p.fname != "" { return out, errors.New("unexpected import") } if len(in) < 2 { return out, errors.New("missing expression") } - if in[1].Id != lang.ParenBlock { + if in[1].Tok != lang.ParenBlock { return p.parseImportLine(in[1:]) } if in, err = p.Scan(in[1].Block(), false); err != nil { @@ -219,7 +219,7 @@ func (p *Parser) parseImportLine(in Tokens) (out Tokens, err error) { if l != 1 && l != 2 { return out, errors.New("invalid number of arguments") } - if in[l-1].Id != lang.String { + if in[l-1].Tok != lang.String { return out, fmt.Errorf("invalid argument %v", in[0]) } pp := in[l-1].Block() @@ -248,11 +248,11 @@ func (p *Parser) parseImportLine(in Tokens) (out Tokens, err error) { return out, err } -func (p *Parser) ParseType(in Tokens) (out Tokens, err error) { +func (p *Parser) parseType(in Tokens) (out Tokens, err error) { if len(in) < 2 { - return out, MissingTypeErr + return out, ErrMissingType } - if in[1].Id != lang.ParenBlock { + if in[1].Tok != lang.ParenBlock { return p.parseTypeLine(in[1:]) } if in, err = p.Scan(in[1].Block(), false); err != nil { @@ -270,12 +270,12 @@ func (p *Parser) ParseType(in Tokens) (out Tokens, err error) { func (p *Parser) parseTypeLine(in Tokens) (out Tokens, err error) { if len(in) < 2 { - return out, MissingTypeErr + return out, ErrMissingType } - if in[0].Id != lang.Ident { + if in[0].Tok != lang.Ident { return out, errors.New("not an ident") } - isAlias := in[1].Id == lang.Assign + isAlias := in[1].Tok == lang.Assign toks := in[1:] if isAlias { toks = toks[1:] @@ -288,11 +288,11 @@ func (p *Parser) parseTypeLine(in Tokens) (out Tokens, err error) { return out, err } -func (p *Parser) ParseVar(in Tokens) (out Tokens, err error) { +func (p *Parser) parseVar(in Tokens) (out Tokens, err error) { if len(in) < 2 { return out, errors.New("missing expression") } - if in[1].Id != lang.ParenBlock { + if in[1].Tok != lang.ParenBlock { return p.parseVarLine(in[1:]) } if in, err = p.Scan(in[1].Block(), false); err != nil { @@ -316,7 +316,7 @@ func (p *Parser) parseVarLine(in Tokens) (out Tokens, err error) { } var vars []string if _, vars, err = p.parseParamTypes(decl, parseTypeVar); err != nil { - if errors.Is(err, MissingTypeErr) { + if errors.Is(err, ErrMissingType) { for _, lt := range decl.Split(lang.Comma) { vars = append(vars, lt[0].Str) name := strings.TrimPrefix(p.scope+"/"+lt[0].Str, "/") @@ -336,13 +336,13 @@ func (p *Parser) parseVarLine(in Tokens) (out Tokens, err error) { values = nil } for i, v := range values { - if v, err = p.ParseExpr(v); err != nil { + if v, err = p.parseExpr(v); err != nil { return out, err } out = append(out, v...) out = append(out, - scanner.Token{Id: lang.Ident, Str: vars[i]}, - scanner.Token{Id: lang.Assign}) + scanner.Token{Tok: lang.Ident, Str: vars[i]}, + scanner.Token{Tok: lang.Assign}) } return out, err } diff --git a/parser/expr.go b/parser/expr.go index 4145240..9e96e42 100644 --- a/parser/expr.go +++ b/parser/expr.go @@ -9,33 +9,33 @@ import ( "github.com/mvertes/parscan/scanner" ) -func (p *Parser) ParseExpr(in Tokens) (out Tokens, err error) { +func (p *Parser) parseExpr(in Tokens) (out Tokens, err error) { log.Println("ParseExpr in:", in) var ops, selectors Tokens var vl int - var selectorId string + var selectorIndex string // // Process tokens from last to first, the goal is to reorder the tokens in // a stack machine processing order, so it can be directly interpreted. // - if len(in) > 1 && in[0].Id == lang.Func { + if len(in) > 1 && in[0].Tok == lang.Func { // Function as value (i.e closure). - if out, err = p.ParseFunc(in); err != nil { + if out, err = p.parseFunc(in); err != nil { return out, err } // Get function label and use it as a symbol ident. fid := out[1] - fid.Id = lang.Ident + fid.Tok = lang.Ident out = append(out, fid) return out, err } for i := len(in) - 1; i >= 0; i-- { t := in[i] // temporary assumptions: binary operators, returning 1 value - switch t.Id { + switch t.Tok { case lang.Ident: - if i > 0 && in[i-1].Id == lang.Period { - selectorId = t.Str + if i > 0 && in[i-1].Tok == lang.Period { + selectorIndex = t.Str continue } // resolve symbol if not a selector rhs. @@ -48,16 +48,16 @@ func (p *Parser) ParseExpr(in Tokens) (out Tokens, err error) { out = append(out, t) vl++ case lang.Period: - t.Str += selectorId + t.Str += selectorIndex selectors = append(Tokens{t}, selectors...) continue case lang.Int, lang.String: out = append(out, t) vl++ case lang.Define, lang.Add, lang.Sub, lang.Assign, lang.Equal, lang.Greater, lang.Less, lang.Mul, lang.Land, lang.Lor, lang.Shl, lang.Shr, lang.Not, lang.And: - if i == 0 || in[i-1].Id.IsOperator() { + if i == 0 || in[i-1].Tok.IsOperator() { // An operator preceded by an operator or no token is unary. - t.Id = lang.UnaryOp[t.Id] + t.Tok = lang.UnaryOp[t.Tok] j := len(out) - 1 l := out[j] if p.precedence(l) > 0 { @@ -73,7 +73,7 @@ func (p *Parser) ParseExpr(in Tokens) (out Tokens, err error) { case lang.ParenBlock: // If the previous token is an arithmetic, logic or assign operator then // this parenthesis block is an enclosed expr, otherwise a call expr. - if i == 0 || in[i-1].Id.IsOperator() { + if i == 0 || in[i-1].Tok.IsOperator() { out = append(out, t) vl++ break @@ -83,15 +83,15 @@ func (p *Parser) ParseExpr(in Tokens) (out Tokens, err error) { // func call: push args and func address then call out = append(out, t) vl++ - ops = append(ops, scanner.Token{Id: lang.Call, Pos: t.Pos, Beg: p.numItems(t.Block(), lang.Comma)}) + ops = append(ops, scanner.Token{Tok: lang.Call, Pos: t.Pos, Beg: p.numItems(t.Block(), lang.Comma)}) case lang.BracketBlock: out = append(out, t) vl++ - ops = append(ops, scanner.Token{Id: lang.Index, Pos: t.Pos}) + ops = append(ops, scanner.Token{Tok: lang.Index, Pos: t.Pos}) case lang.Comment: return out, nil default: - return nil, fmt.Errorf("expression not supported yet: %v: %q", t.Id, t.Str) + return nil, fmt.Errorf("expression not supported yet: %v: %q", t.Tok, t.Str) } if len(selectors) > 0 { out = append(out, selectors...) @@ -115,13 +115,13 @@ func (p *Parser) ParseExpr(in Tokens) (out Tokens, err error) { log.Println("ParseExpr out:", out, "vl:", vl, "ops:", ops) // A logical operator (&&, ||) involves additional control flow operations. - if out, err = p.ParseLogical(out); err != nil { + if out, err = p.parseLogical(out); err != nil { return out, err } - if l := len(out) - 1; l >= 0 && (out[l].Id == lang.Define || out[l].Id == lang.Assign) { + if l := len(out) - 1; l >= 0 && (out[l].Tok == lang.Define || out[l].Tok == lang.Assign) { // Handle the assignment of a logical expression. s1 := p.subExprLen(out[:l]) - head, err := p.ParseLogical(out[:l-s1]) + head, err := p.parseLogical(out[:l-s1]) if err != nil { return out, err } @@ -132,9 +132,9 @@ func (p *Parser) ParseExpr(in Tokens) (out Tokens, err error) { for i := len(out) - 1; i >= 0; i-- { t := out[i] var toks Tokens - switch t.Id { + switch t.Tok { case lang.ParenBlock, lang.BracketBlock: - if toks, err = p.ParseExprStr(t.Block()); err != nil { + if toks, err = p.parseExprStr(t.Block()); err != nil { return out, err } default: @@ -151,13 +151,13 @@ func (p *Parser) ParseExpr(in Tokens) (out Tokens, err error) { return out, err } -func (p *Parser) ParseExprStr(s string) (tokens Tokens, err error) { +func (p *Parser) parseExprStr(s string) (tokens Tokens, err error) { if tokens, err = p.Scan(s, false); err != nil { return } var result Tokens for _, sub := range tokens.Split(lang.Comma) { - toks, err := p.ParseExpr(sub) + toks, err := p.parseExpr(sub) if err != nil { return result, err } @@ -166,34 +166,34 @@ func (p *Parser) ParseExprStr(s string) (tokens Tokens, err error) { return result, err } -// ParseLogical handles logical expressions with control flow (&& and ||) by +// parseLogical handles logical expressions with control flow (&& and ||) by // ensuring the left hand side is evaluated unconditionally first, then the // right hand side can be skipped or not by inserting a conditional jump and label. // If the last token is not a logical operator then the function is idempotent. -func (p *Parser) ParseLogical(in Tokens) (out Tokens, err error) { +func (p *Parser) parseLogical(in Tokens) (out Tokens, err error) { l := len(in) - 1 - if l < 0 || !in[l].Id.IsLogicalOp() { + if l < 0 || !in[l].Tok.IsLogicalOp() { return in, nil } xp := strconv.Itoa(p.labelCount[p.scope]) p.labelCount[p.scope]++ rhsIndex := p.subExprLen(in[:l]) - lhs, err := p.ParseLogical(in[l-rhsIndex : l]) + lhs, err := p.parseLogical(in[l-rhsIndex : l]) if err != nil { return out, err } - rhs, err := p.ParseLogical(in[:l-rhsIndex]) + rhs, err := p.parseLogical(in[:l-rhsIndex]) if err != nil { return out, err } out = append(out, lhs...) - if in[l].Id == lang.Lor { - out = append(out, scanner.Token{Id: lang.JumpSetTrue, Str: p.scope + "x" + xp}) + if in[l].Tok == lang.Lor { + out = append(out, scanner.Token{Tok: lang.JumpSetTrue, Str: p.scope + "x" + xp}) } else { - out = append(out, scanner.Token{Id: lang.JumpSetFalse, Str: p.scope + "x" + xp}) + out = append(out, scanner.Token{Tok: lang.JumpSetFalse, Str: p.scope + "x" + xp}) } out = append(out, rhs...) - out = append(out, scanner.Token{Id: lang.Label, Str: p.scope + "x" + xp}) + out = append(out, scanner.Token{Tok: lang.Label, Str: p.scope + "x" + xp}) return out, err } @@ -201,7 +201,7 @@ func (p *Parser) ParseLogical(in Tokens) (out Tokens, err error) { func (p *Parser) subExprLen(in Tokens) int { l := len(in) - 1 last := in[l] - switch last.Id { + switch last.Tok { case lang.Int, lang.Float, lang.String, lang.Char, lang.Ident, lang.ParenBlock, lang.BracketBlock: return 1 case lang.Call: @@ -209,11 +209,11 @@ func (p *Parser) subExprLen(in Tokens) int { return 1 + s1 + p.subExprLen(in[:l-s1]) // TODO: add selector and index operators when ready } - if last.Id.IsBinaryOp() { + if last.Tok.IsBinaryOp() { s1 := p.subExprLen(in[:l]) return 1 + s1 + p.subExprLen(in[:l-s1]) } - if last.Id.IsUnaryOp() { + if last.Tok.IsUnaryOp() { return 1 + p.subExprLen(in[:l]) } return 0 // should not occur. TODO: diplay some error here. diff --git a/parser/interpreter.go b/parser/interpreter.go index d820416..e6ac95c 100644 --- a/parser/interpreter.go +++ b/parser/interpreter.go @@ -9,15 +9,18 @@ import ( const debug = true +// Interpreter represents the state of an interpreter. type Interpreter struct { *Compiler *vm.Machine } +// NewInterpreter returns a new interpreter state. func NewInterpreter(s *scanner.Scanner) *Interpreter { return &Interpreter{NewCompiler(s), &vm.Machine{}} } +// Eval interprets a src program and return the last produced value if any, or an error. func (i *Interpreter) Eval(src string) (res reflect.Value, err error) { codeOffset := len(i.Code) dataOffset := 0 diff --git a/parser/parse.go b/parser/parse.go index ffcb8e2..bd19d81 100644 --- a/parser/parse.go +++ b/parser/parse.go @@ -1,3 +1,4 @@ +// Package parser implements a parser and compiler. package parser import ( @@ -11,6 +12,7 @@ import ( "github.com/mvertes/parscan/scanner" ) +// Parser represents the state of a parser. type Parser struct { *scanner.Scanner @@ -27,28 +29,30 @@ type Parser struct { clonum int // closure instance number } +// Scan performs lexical analysis on s and returns Tokens or an error. func (p *Parser) Scan(s string, endSemi bool) (Tokens, error) { return p.Scanner.Scan(s, endSemi) } +// Parse performs syntax analysis on s and return Tokens or an error. func (p *Parser) Parse(src string) (out Tokens, err error) { log.Printf("Parse src: %#v\n", src) in, err := p.Scan(src, true) if err != nil { return out, err } - return p.ParseStmts(in) + return p.parseStmts(in) } -func (p *Parser) ParseStmts(in Tokens) (out Tokens, err error) { +func (p *Parser) parseStmts(in Tokens) (out Tokens, err error) { for len(in) > 0 { endstmt := in.Index(lang.Semicolon) if endstmt == -1 { return out, scanner.ErrBlock } // Skip over simple init statements for some tokens (if, for, ...) - if lang.HasInit[in[0].Id] { - for in[endstmt-1].Id != lang.BraceBlock { + if lang.HasInit[in[0].Tok] { + for in[endstmt-1].Tok != lang.BraceBlock { e2 := in[endstmt+1:].Index(lang.Semicolon) if e2 == -1 { return out, scanner.ErrBlock @@ -56,7 +60,7 @@ func (p *Parser) ParseStmts(in Tokens) (out Tokens, err error) { endstmt += 1 + e2 } } - o, err := p.ParseStmt(in[:endstmt]) + o, err := p.parseStmt(in[:endstmt]) if err != nil { return out, err } @@ -66,58 +70,58 @@ func (p *Parser) ParseStmts(in Tokens) (out Tokens, err error) { return out, err } -func (p *Parser) ParseStmt(in Tokens) (out Tokens, err error) { +func (p *Parser) parseStmt(in Tokens) (out Tokens, err error) { if len(in) == 0 { return nil, nil } log.Println("ParseStmt in:", in) - switch t := in[0]; t.Id { + switch t := in[0]; t.Tok { case lang.Break: - return p.ParseBreak(in) + return p.parseBreak(in) case lang.Continue: - return p.ParseContinue(in) + return p.parseContinue(in) case lang.Const: - return p.ParseConst(in) + return p.parseConst(in) case lang.For: - return p.ParseFor(in) + return p.parseFor(in) case lang.Func: - return p.ParseFunc(in) + return p.parseFunc(in) case lang.Defer, lang.Go, lang.Fallthrough, lang.Select: - return out, fmt.Errorf("not yet implemented: %v", t.Id) + return out, fmt.Errorf("not yet implemented: %v", t.Tok) case lang.Goto: - return p.ParseGoto(in) + return p.parseGoto(in) case lang.If: - return p.ParseIf(in) + return p.parseIf(in) case lang.Import: - return p.ParseImport(in) + return p.parseImports(in) case lang.Package: // TODO: support packages return out, err case lang.Return: - return p.ParseReturn(in) + return p.parseReturn(in) case lang.Switch: - return p.ParseSwitch(in) + return p.parseSwitch(in) case lang.Type: - return p.ParseType(in) + return p.parseType(in) case lang.Var: - return p.ParseVar(in) + return p.parseVar(in) case lang.Ident: - if len(in) == 2 && in[1].Id == lang.Colon { - return p.ParseLabel(in) + if len(in) == 2 && in[1].Tok == lang.Colon { + return p.parseLabel(in) } fallthrough default: - return p.ParseExpr(in) + return p.parseExpr(in) } } -func (p *Parser) ParseBreak(in Tokens) (out Tokens, err error) { +func (p *Parser) parseBreak(in Tokens) (out Tokens, err error) { var label string switch len(in) { case 1: label = p.breakLabel case 2: - if in[1].Id != lang.Ident { + if in[1].Tok != lang.Ident { return nil, fmt.Errorf("invalid break statement") } // TODO: check validity of user provided label @@ -125,17 +129,17 @@ func (p *Parser) ParseBreak(in Tokens) (out Tokens, err error) { default: return nil, fmt.Errorf("invalid break statement") } - out = Tokens{{Id: lang.Goto, Str: label}} + out = Tokens{{Tok: lang.Goto, Str: label}} return out, err } -func (p *Parser) ParseContinue(in Tokens) (out Tokens, err error) { +func (p *Parser) parseContinue(in Tokens) (out Tokens, err error) { var label string switch len(in) { case 1: label = p.continueLabel case 2: - if in[1].Id != lang.Ident { + if in[1].Tok != lang.Ident { return nil, fmt.Errorf("invalid continue statement") } // TODO: check validity of user provided label @@ -143,19 +147,19 @@ func (p *Parser) ParseContinue(in Tokens) (out Tokens, err error) { default: return nil, fmt.Errorf("invalid continue statement") } - out = Tokens{{Id: lang.Goto, Str: label}} + out = Tokens{{Tok: lang.Goto, Str: label}} return out, err } -func (p *Parser) ParseGoto(in Tokens) (out Tokens, err error) { - if len(in) != 2 || in[1].Id != lang.Ident { +func (p *Parser) parseGoto(in Tokens) (out Tokens, err error) { + if len(in) != 2 || in[1].Tok != lang.Ident { return nil, fmt.Errorf("invalid goto statement") } // TODO: check validity of user provided label - return Tokens{{Id: lang.Goto, Str: p.funcScope + "/" + in[1].Str}}, nil + return Tokens{{Tok: lang.Goto, Str: p.funcScope + "/" + in[1].Str}}, nil } -func (p *Parser) ParseFor(in Tokens) (out Tokens, err error) { +func (p *Parser) parseFor(in Tokens) (out Tokens, err error) { // TODO: detect invalid code. fc := strconv.Itoa(p.labelCount[p.scope]) p.labelCount[p.scope]++ @@ -177,42 +181,42 @@ func (p *Parser) ParseFor(in Tokens) (out Tokens, err error) { p.popScope() }() if len(init) > 0 { - if init, err = p.ParseStmt(init); err != nil { + if init, err = p.parseStmt(init); err != nil { return nil, err } out = init } - out = append(out, scanner.Token{Id: lang.Label, Str: p.scope + "b"}) + out = append(out, scanner.Token{Tok: lang.Label, Str: p.scope + "b"}) if len(cond) > 0 { - if cond, err = p.ParseExpr(cond); err != nil { + if cond, err = p.parseExpr(cond); err != nil { return nil, err } out = append(out, cond...) - out = append(out, scanner.Token{Id: lang.JumpFalse, Str: p.scope + "e"}) + out = append(out, scanner.Token{Tok: lang.JumpFalse, Str: p.scope + "e"}) } if body, err = p.Parse(in[len(in)-1].Block()); err != nil { return nil, err } out = append(out, body...) if len(post) > 0 { - if post, err = p.ParseStmt(post); err != nil { + if post, err = p.parseStmt(post); err != nil { return nil, err } out = append(out, post...) } out = append(out, - scanner.Token{Id: lang.Goto, Str: p.scope + "b"}, - scanner.Token{Id: lang.Label, Str: p.scope + "e"}) + scanner.Token{Tok: lang.Goto, Str: p.scope + "b"}, + scanner.Token{Tok: lang.Label, Str: p.scope + "e"}) return out, err } -func (p *Parser) ParseFunc(in Tokens) (out Tokens, err error) { +func (p *Parser) parseFunc(in Tokens) (out Tokens, err error) { // TODO: handle anonymous functions (no function name) // TODO: handle receiver (methods) // TODO: handle parametric types (generics) // TODO: handle variadic parameters var fname string - if in[1].Id == lang.Ident { + if in[1].Tok == lang.Ident { fname = in[1].Str } else { fname = "#f" + strconv.Itoa(p.clonum) @@ -237,8 +241,9 @@ func (p *Parser) ParseFunc(in Tokens) (out Tokens, err error) { }() out = Tokens{ - {Id: lang.Goto, Str: fname + "_end"}, // Skip function definition. - {Id: lang.Label, Pos: in[0].Pos, Str: fname}} + {Tok: lang.Goto, Str: fname + "_end"}, // Skip function definition. + {Tok: lang.Label, Pos: in[0].Pos, Str: fname}, + } bi := in.Index(lang.BraceBlock) if bi < 0 { @@ -257,23 +262,23 @@ func (p *Parser) ParseFunc(in Tokens) (out Tokens, err error) { return out, err } if l := p.framelen[p.funcScope] - 1; l > 0 { - out = append(out, scanner.Token{Id: lang.Grow, Beg: l}) + out = append(out, scanner.Token{Tok: lang.Grow, Beg: l}) } out = append(out, toks...) - if out[len(out)-1].Id != lang.Return { - // Ensure that a return statment is always added at end of function. + if out[len(out)-1].Tok != lang.Return { + // Ensure that a return statement is always added at end of function. // TODO: detect missing or wrong returns. - x, err := p.ParseReturn(nil) + x, err := p.parseReturn(nil) if err != nil { return out, err } out = append(out, x...) } - out = append(out, scanner.Token{Id: lang.Label, Str: fname + "_end"}) + out = append(out, scanner.Token{Tok: lang.Label, Str: fname + "_end"}) return out, err } -func (p *Parser) ParseIf(in Tokens) (out Tokens, err error) { +func (p *Parser) parseIf(in Tokens) (out Tokens, err error) { label := "if" + strconv.Itoa(p.labelCount[p.scope]) p.labelCount[p.scope]++ p.pushScope(label) @@ -282,7 +287,7 @@ func (p *Parser) ParseIf(in Tokens) (out Tokens, err error) { // get the destination labels already computed when jumps are set. for sc, i := 0, len(in)-1; i > 0; sc++ { ssc := strconv.Itoa(sc) - if in[i].Id != lang.BraceBlock { + if in[i].Tok != lang.BraceBlock { return nil, fmt.Errorf("expected '{', got %v", in[i]) } pre, err := p.Parse(in[i].Block()) @@ -290,13 +295,13 @@ func (p *Parser) ParseIf(in Tokens) (out Tokens, err error) { return nil, err } if sc > 0 { - pre = append(pre, scanner.Token{Id: lang.Goto, Str: p.scope + "e0"}) + pre = append(pre, scanner.Token{Tok: lang.Goto, Str: p.scope + "e0"}) } - pre = append(pre, scanner.Token{Id: lang.Label, Str: p.scope + "e" + ssc}) + pre = append(pre, scanner.Token{Tok: lang.Label, Str: p.scope + "e" + ssc}) out = append(pre, out...) i-- - if in[i].Id == lang.Else { // Step over final 'else'. + if in[i].Tok == lang.Else { // Step over final 'else'. i-- continue } @@ -311,26 +316,26 @@ func (p *Parser) ParseIf(in Tokens) (out Tokens, err error) { cond = initcond[ii+1:] } if len(init) > 0 { - if init, err = p.ParseStmt(init); err != nil { + if init, err = p.parseStmt(init); err != nil { return nil, err } pre = append(pre, init...) } - if cond, err = p.ParseExpr(cond); err != nil { + if cond, err = p.parseExpr(cond); err != nil { return nil, err } pre = append(pre, cond...) - pre = append(pre, scanner.Token{Id: lang.JumpFalse, Str: p.scope + "e" + ssc}) + pre = append(pre, scanner.Token{Tok: lang.JumpFalse, Str: p.scope + "e" + ssc}) out = append(pre, out...) i = ifp - if i > 1 && in[i].Id == lang.If && in[i-1].Id == lang.Else { // Step over 'else if'. + if i > 1 && in[i].Tok == lang.If && in[i-1].Tok == lang.Else { // Step over 'else if'. i -= 2 } } return out, err } -func (p *Parser) ParseSwitch(in Tokens) (out Tokens, err error) { +func (p *Parser) parseSwitch(in Tokens) (out Tokens, err error) { var init, cond, clauses Tokens initcond := in[1 : len(in)-1] if ii := initcond.Index(lang.Semicolon); ii < 0 { @@ -349,14 +354,14 @@ func (p *Parser) ParseSwitch(in Tokens) (out Tokens, err error) { p.popScope() }() if len(init) > 0 { - if init, err = p.ParseStmt(init); err != nil { + if init, err = p.parseStmt(init); err != nil { return nil, err } out = init } condSwitch := false if len(cond) > 0 { - if cond, err = p.ParseExpr(cond); err != nil { + if cond, err = p.parseExpr(cond); err != nil { return nil, err } out = append(out, cond...) @@ -368,7 +373,7 @@ func (p *Parser) ParseSwitch(in Tokens) (out Tokens, err error) { // Make sure that the default clause is the last. lsc := len(sc) - 1 for i, cl := range sc { - if cl[1].Id == lang.Colon && i != lsc { + if cl[1].Tok == lang.Colon && i != lsc { sc[i], sc[lsc] = sc[lsc], sc[i] break } @@ -376,30 +381,30 @@ func (p *Parser) ParseSwitch(in Tokens) (out Tokens, err error) { // Process each clause. nc := len(sc) - 1 for i, cl := range sc { - co, err := p.ParseCaseClause(cl, i, nc, condSwitch) + co, err := p.parseCaseClause(cl, i, nc, condSwitch) if err != nil { return nil, err } out = append(out, co...) } - out = append(out, scanner.Token{Id: lang.Label, Str: p.breakLabel}) + out = append(out, scanner.Token{Tok: lang.Label, Str: p.breakLabel}) return out, err } -func (p *Parser) ParseCaseClause(in Tokens, index, max int, condSwitch bool) (out Tokens, err error) { - in = append(in, scanner.Token{Id: lang.Semicolon}) // Force a ';' at the end of body clause. +func (p *Parser) parseCaseClause(in Tokens, index, max int, condSwitch bool) (out Tokens, err error) { + in = append(in, scanner.Token{Tok: lang.Semicolon}) // Force a ';' at the end of body clause. var conds, body Tokens tl := in.Split(lang.Colon) if len(tl) != 2 { return nil, errors.New("invalid case clause") } conds = tl[0][1:] - if body, err = p.ParseStmts(tl[1]); err != nil { + if body, err = p.parseStmts(tl[1]); err != nil { return out, err } lcond := conds.Split(lang.Comma) for i, cond := range lcond { - if cond, err = p.ParseExpr(cond); err != nil { + if cond, err = p.parseExpr(cond); err != nil { return out, err } txt := fmt.Sprintf("%sc%d.%d", p.scope, index, i) @@ -413,33 +418,33 @@ func (p *Parser) ParseCaseClause(in Tokens, index, max int, condSwitch bool) (ou } else { next = fmt.Sprintf("%sc%d.%d", p.scope, index, i+1) } - out = append(out, scanner.Token{Id: lang.Label, Str: txt}) + out = append(out, scanner.Token{Tok: lang.Label, Str: txt}) if len(cond) > 0 { out = append(out, cond...) if condSwitch { - out = append(out, scanner.Token{Id: lang.EqualSet}) + out = append(out, scanner.Token{Tok: lang.EqualSet}) } - out = append(out, scanner.Token{Id: lang.JumpFalse, Str: next}) + out = append(out, scanner.Token{Tok: lang.JumpFalse, Str: next}) } out = append(out, body...) if i != len(lcond)-1 || index != max { - out = append(out, scanner.Token{Id: lang.Goto, Str: p.scope + "e"}) + out = append(out, scanner.Token{Tok: lang.Goto, Str: p.scope + "e"}) } } return out, err } -func (p *Parser) ParseLabel(in Tokens) (out Tokens, err error) { - return Tokens{{Id: lang.Label, Str: p.funcScope + "/" + in[0].Str}}, nil +func (p *Parser) parseLabel(in Tokens) (out Tokens, err error) { + return Tokens{{Tok: lang.Label, Str: p.funcScope + "/" + in[0].Str}}, nil } -func (p *Parser) ParseReturn(in Tokens) (out Tokens, err error) { +func (p *Parser) parseReturn(in Tokens) (out Tokens, err error) { if l := len(in); l > 1 { - if out, err = p.ParseExpr(in[1:]); err != nil { + if out, err = p.parseExpr(in[1:]); err != nil { return out, err } } else if l == 0 { - in = Tokens{{Id: lang.Return}} // Implicit return in functions with no return parameters. + in = Tokens{{Tok: lang.Return}} // Implicit return in functions with no return parameters. } // TODO: the function symbol should be already present in the parser context. @@ -451,7 +456,7 @@ func (p *Parser) ParseReturn(in Tokens) (out Tokens, err error) { return out, err } -func (p *Parser) numItems(s string, sep lang.TokenId) int { +func (p *Parser) numItems(s string, sep lang.Token) int { tokens, err := p.Scan(s, false) if err != nil { return -1 diff --git a/parser/symbol.go b/parser/symbol.go index c8d89db..499e121 100644 --- a/parser/symbol.go +++ b/parser/symbol.go @@ -40,6 +40,7 @@ func symtype(s *symbol) *vm.Type { return vm.TypeOf(s.value) } +// AddSym add a new named value at memory position i in the parser symbol table. func (p *Parser) AddSym(i int, name string, v vm.Value) { p.addSym(i, name, v, symValue, nil, false) } diff --git a/parser/tokens.go b/parser/tokens.go index 51085d9..78467c6 100644 --- a/parser/tokens.go +++ b/parser/tokens.go @@ -5,6 +5,7 @@ import ( "github.com/mvertes/parscan/scanner" ) +// Tokens represents slice of tokens. type Tokens []scanner.Token func (toks Tokens) String() (s string) { @@ -14,27 +15,30 @@ func (toks Tokens) String() (s string) { return s } -func (toks Tokens) Index(id lang.TokenId) int { +// Index returns the index in toks of the first matching tok, or -1. +func (toks Tokens) Index(tok lang.Token) int { for i, t := range toks { - if t.Id == id { + if t.Tok == tok { return i } } return -1 } -func (toks Tokens) LastIndex(id lang.TokenId) int { +// LastIndex returns the index in toks of the last matching tok, or -1. +func (toks Tokens) LastIndex(tok lang.Token) int { for i := len(toks) - 1; i >= 0; i-- { - if toks[i].Id == id { + if toks[i].Tok == tok { return i } } return -1 } -func (toks Tokens) Split(id lang.TokenId) (result []Tokens) { +// Split returns a slice of token arrays, separated by tok. +func (toks Tokens) Split(tok lang.Token) (result []Tokens) { for { - i := toks.Index(id) + i := toks.Index(tok) if i < 0 { return append(result, toks) } @@ -43,9 +47,10 @@ func (toks Tokens) Split(id lang.TokenId) (result []Tokens) { } } -func (toks Tokens) SplitStart(id lang.TokenId) (result []Tokens) { +// SplitStart is similar to Split, except the first token in toks is skipped. +func (toks Tokens) SplitStart(tok lang.Token) (result []Tokens) { for { - i := toks[1:].Index(id) + i := toks[1:].Index(tok) if i < 0 { return append(result, toks) } diff --git a/parser/type.go b/parser/type.go index 16390e9..b9ab0f9 100644 --- a/parser/type.go +++ b/parser/type.go @@ -18,17 +18,18 @@ const ( parseTypeType ) +// Type parsing error definitions. var ( - InvalidTypeErr = errors.New("invalid type") - MissingTypeErr = errors.New("missing type") - SyntaxErr = errors.New("syntax error") - TypeNotImplementedErr = errors.New("not implemented") + ErrInvalidType = errors.New("invalid type") + ErrMissingType = errors.New("missing type") + ErrSyntax = errors.New("syntax error") + ErrTypeNotImplemented = errors.New("not implemented") ) // ParseTypeExpr parses a list of tokens defining a type expresssion and returns // the corresponding runtime type or an error. func (p *Parser) ParseTypeExpr(in Tokens) (typ *vm.Type, err error) { - switch in[0].Id { + switch in[0].Tok { case lang.BracketBlock: typ, err := p.ParseTypeExpr(in[1:]) if err != nil { @@ -65,11 +66,11 @@ func (p *Parser) ParseTypeExpr(in Tokens) (typ *vm.Type, err error) { var out Tokens var indexArgs int switch l, in1 := len(in), in[1]; { - case l >= 4 && in1.Id == lang.ParenBlock && in[2].Id == lang.Ident: + case l >= 4 && in1.Tok == lang.ParenBlock && in[2].Tok == lang.Ident: indexArgs, out = 3, in[4:] - case l >= 3 && in1.Id == lang.Ident: + case l >= 3 && in1.Tok == lang.Ident: indexArgs, out = 2, in[3:] - case l >= 2 && in1.Id == lang.ParenBlock: + case l >= 2 && in1.Tok == lang.ParenBlock: indexArgs, out = 1, in[2:] default: return nil, fmt.Errorf("invalid func signature") @@ -86,7 +87,7 @@ func (p *Parser) ParseTypeExpr(in Tokens) (typ *vm.Type, err error) { return nil, err } // Output parameters may be empty, or enclosed or not by parenthesis. - if len(out) == 1 && out[0].Id == lang.ParenBlock { + if len(out) == 1 && out[0].Tok == lang.ParenBlock { if out, err = p.Scan(out[0].Block(), false); err != nil { return nil, err } @@ -101,13 +102,13 @@ func (p *Parser) ParseTypeExpr(in Tokens) (typ *vm.Type, err error) { // TODO: selector expression (pkg.type) s, _, ok := p.getSym(in[0].Str, p.scope) if !ok || s.kind != symType { - return nil, fmt.Errorf("%w: %s", InvalidTypeErr, in[0].Str) + return nil, fmt.Errorf("%w: %s", ErrInvalidType, in[0].Str) } return s.Type, nil case lang.Struct: - if len(in) != 2 || in[1].Id != lang.BraceBlock { - return nil, fmt.Errorf("%w: %v", SyntaxErr, in) + if len(in) != 2 || in[1].Tok != lang.BraceBlock { + return nil, fmt.Errorf("%w: %v", ErrSyntax, in) } if in, err = p.Scan(in[1].Block(), false); err != nil { return nil, err @@ -126,7 +127,7 @@ func (p *Parser) ParseTypeExpr(in Tokens) (typ *vm.Type, err error) { return vm.StructOf(fields), nil default: - return nil, fmt.Errorf("%w: %v", TypeNotImplementedErr, in[0].Name()) + return nil, fmt.Errorf("%w: %v", ErrTypeNotImplemented, in[0].Name()) } } @@ -147,9 +148,9 @@ func (p *Parser) parseParamTypes(in Tokens, flag typeFlag) (types []*vm.Type, va t = t[1:] if len(t) == 0 { if len(types) == 0 { - return nil, nil, MissingTypeErr + return nil, nil, ErrMissingType } - // Type was ommitted, apply the previous one from the right. + // Type was omitted, apply the previous one from the right. types = append([]*vm.Type{types[0]}, types...) p.addSymVar(i, param, types[0], flag, local) vars = append(vars, param) @@ -189,7 +190,7 @@ func (p *Parser) addSymVar(index int, name string, typ *vm.Type, flag typeFlag, // hasFirstParam returns true if the first token of a list is a parameter name. func (p *Parser) hasFirstParam(in Tokens) bool { - if in[0].Id != lang.Ident { + if in[0].Tok != lang.Ident { return false } s, _, ok := p.getSym(in[0].Str, p.scope) -- cgit v1.2.3