diff options
| author | Marc Vertes <mvertes@free.fr> | 2024-07-18 12:56:29 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-07-18 12:56:29 +0200 |
| commit | dabd9e5eb81bbc9aeaeb32fb3e3ce83eef258a77 (patch) | |
| tree | 32be6a4cecf83487dd6a91e8a9aa1da709840b0f /parser | |
| parent | 1a2b2cb565ebf701f43012e1fce5552398d622a9 (diff) | |
fix (parser): don't panic if assign of define untyped value (#10)
* fix (parser): don't panic if assign of define untyped value
In case of defining or assigning to untyped value, the type has
to be taken from the source value instead of the target value.
The vm test coverage has also been slightly improved.
* fix and simplify Token.Name()
* improve parser errors
Diffstat (limited to 'parser')
| -rw-r--r-- | parser/compiler.go | 25 | ||||
| -rw-r--r-- | parser/expr.go | 2 | ||||
| -rw-r--r-- | parser/interpreter_test.go | 2 | ||||
| -rw-r--r-- | parser/parse.go | 25 | ||||
| -rw-r--r-- | parser/type.go | 16 |
5 files changed, 46 insertions, 24 deletions
diff --git a/parser/compiler.go b/parser/compiler.go index 3b9a32d..05462eb 100644 --- a/parser/compiler.go +++ b/parser/compiler.go @@ -61,13 +61,14 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { case lang.String: s := t.Block() + v := vm.Value{Data: reflect.ValueOf(s), Type: vm.TypeOf(s)} i, ok := c.strings[s] if !ok { i = len(c.Data) - c.Data = append(c.Data, vm.ValueOf(s)) + c.Data = append(c.Data, v) c.strings[s] = i } - push(&symbol{kind: symConst, value: vm.ValueOf(s)}) + push(&symbol{kind: symConst, value: v}) emit(int64(t.Pos), vm.Dup, int64(i)) case lang.Add: @@ -144,7 +145,11 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { // TODO: support assignment to local, composite objects. st := tokens[i-1] l := len(c.Data) - typ := pop().Type + d := pop() + typ := d.Type + if typ == nil { + typ = d.value.Type + } v := vm.NewValue(typ) c.addSym(l, st.Str, v, symVar, typ, false) c.Data = append(c.Data, v) @@ -160,7 +165,11 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { if !ok { return fmt.Errorf("symbol not found: %s", st.Str) } - typ := pop().Type + d := pop() + typ := d.Type + if typ == nil { + typ = d.value.Type + } if s.Type == nil { s.Type = typ s.value = vm.NewValue(typ) @@ -259,7 +268,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { } else { i = s.value.Data.Int() - int64(len(c.Code)) } - emit(int64(t.Pos), vm.JumpSetTrue, int64(i)) + emit(int64(t.Pos), vm.JumpSetTrue, i) case lang.Goto: var i int64 @@ -431,17 +440,17 @@ type DumpValue struct { // This design choice allows the Virtual Machine (VM) to evolve its memory management strategies // without compromising backward compatibility with dumps generated by previous versions. func (c *Compiler) Dump() *Dump { - var dv []*DumpValue dict := c.symbolsByIndex() + dv := make([]*DumpValue, len(c.Data)) for i, d := range c.Data { e := dict[i] - dv = append(dv, &DumpValue{ + dv[i] = &DumpValue{ Index: e.index, Name: e.name, Kind: int(e.kind), Type: e.Type.Name, Value: d.Data.Interface(), - }) + } } return &Dump{Values: dv} diff --git a/parser/expr.go b/parser/expr.go index cf6ee74..1efb45e 100644 --- a/parser/expr.go +++ b/parser/expr.go @@ -110,7 +110,7 @@ func (p *Parser) parseExpr(in Tokens) (out Tokens, err error) { case lang.Comment: return out, nil default: - return nil, fmt.Errorf("expression not supported yet: %v: %q", t.Tok, t.Str) + return nil, fmt.Errorf("invalid expression: %v: %q", t.Tok, t.Str) } if len(selectors) > 0 { out = append(out, selectors...) diff --git a/parser/interpreter_test.go b/parser/interpreter_test.go index a6ad246..314f35b 100644 --- a/parser/interpreter_test.go +++ b/parser/interpreter_test.go @@ -69,6 +69,7 @@ func TestExpr(t *testing.T) { {src: "-2 + 5", res: "3"}, {src: "5 + -2", res: "3"}, {src: "!false", res: "true"}, + {src: `a := "hello"`, res: "hello"}, }) } @@ -233,6 +234,7 @@ func TestVar(t *testing.T) { {src: "var a, b int = 2, 5; a+b", res: "7"}, {src: "var x = 5; x", res: "5"}, {src: "var a = 1; func f() int { var a, b int = 3, 4; return a+b}; a+f()", res: "8"}, + {src: `var a = "hello"; a`, res: "hello"}, {src: `var ( a, b int = 4+1, 3 c = 8 diff --git a/parser/parse.go b/parser/parse.go index add1600..537b68e 100644 --- a/parser/parse.go +++ b/parser/parse.go @@ -29,6 +29,15 @@ type Parser struct { clonum int // closure instance number } +// Parser errors. +var ( + ErrBody = errors.New("missign body") + ErrBreak = errors.New("invalid break statement") + ErrContinue = errors.New("invalid continue statement") + ErrFor = errors.New("invalid for statement") + ErrGoto = errors.New("invalid goto statement") +) + // 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) @@ -36,11 +45,11 @@ func (p *Parser) Scan(s string, endSemi bool) (Tokens, error) { // 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 } + log.Printf("Parse src: %#v\n", src) return p.parseStmts(in) } @@ -122,12 +131,12 @@ func (p *Parser) parseBreak(in Tokens) (out Tokens, err error) { label = p.breakLabel case 2: if in[1].Tok != lang.Ident { - return nil, fmt.Errorf("invalid break statement") + return nil, ErrBreak } // TODO: check validity of user provided label label = in[1].Str default: - return nil, fmt.Errorf("invalid break statement") + return nil, ErrBreak } out = Tokens{{Tok: lang.Goto, Str: label}} return out, err @@ -140,12 +149,12 @@ func (p *Parser) parseContinue(in Tokens) (out Tokens, err error) { label = p.continueLabel case 2: if in[1].Tok != lang.Ident { - return nil, fmt.Errorf("invalid continue statement") + return nil, ErrContinue } // TODO: check validity of user provided label label = in[1].Str default: - return nil, fmt.Errorf("invalid continue statement") + return nil, ErrContinue } out = Tokens{{Tok: lang.Goto, Str: label}} return out, err @@ -153,7 +162,7 @@ func (p *Parser) parseContinue(in Tokens) (out Tokens, err error) { 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") + return nil, ErrGoto } // TODO: check validity of user provided label return Tokens{{Tok: lang.Goto, Str: p.funcScope + "/" + in[1].Str}}, nil @@ -171,7 +180,7 @@ func (p *Parser) parseFor(in Tokens) (out Tokens, err error) { case 3: init, cond, post = pre[0], pre[1], pre[2] default: - return nil, fmt.Errorf("invalild for statement") + return nil, ErrFor } breakLabel, continueLabel := p.breakLabel, p.continueLabel p.pushScope("for" + fc) @@ -247,7 +256,7 @@ func (p *Parser) parseFunc(in Tokens) (out Tokens, err error) { bi := in.Index(lang.BraceBlock) if bi < 0 { - return out, fmt.Errorf("no function body") + return out, ErrBody } typ, err := p.parseTypeExpr(in[:bi]) if err != nil { diff --git a/parser/type.go b/parser/type.go index ba60e8f..e25431a 100644 --- a/parser/type.go +++ b/parser/type.go @@ -20,10 +20,12 @@ const ( // Type parsing error definitions. var ( - ErrInvalidType = errors.New("invalid type") - ErrMissingType = errors.New("missing type") - ErrSyntax = errors.New("syntax error") - ErrTypeNotImplemented = errors.New("not implemented") + ErrFuncType = errors.New("invalid function type") + ErrInvalidType = errors.New("invalid type") + ErrMissingType = errors.New("missing type") + ErrSize = errors.New("invalid size") + ErrSyntax = errors.New("syntax error") + ErrNotImplemented = errors.New("not implemented") ) func (p *Parser) parseTypeExpr(in Tokens) (typ *vm.Type, err error) { @@ -44,7 +46,7 @@ func (p *Parser) parseTypeExpr(in Tokens) (typ *vm.Type, err error) { } size, ok := constValue(cval).(int) if !ok { - return nil, fmt.Errorf("invalid size") + return nil, ErrSize } return vm.ArrayOf(size, typ), nil } @@ -71,7 +73,7 @@ func (p *Parser) parseTypeExpr(in Tokens) (typ *vm.Type, err error) { case l >= 2 && in1.Tok == lang.ParenBlock: indexArgs, out = 1, in[2:] default: - return nil, fmt.Errorf("invalid func signature") + return nil, ErrFuncType } // We can now parse function input and output parameter types. @@ -125,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", ErrTypeNotImplemented, in[0].Name()) + return nil, fmt.Errorf("%w: %v", ErrNotImplemented, in[0].Name()) } } |
