summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Vertes <mvertes@free.fr>2023-11-15 22:38:46 +0100
committerMarc Vertes <mvertes@free.fr>2023-11-15 22:38:46 +0100
commita79e558d825c5b777c95c5e098b01391ee36781e (patch)
tree977e5131eee46197a6e72377a168df23ca356948
parenta4d7fb2da6a8390b818dae8d07391c7d76e365e9 (diff)
parser: parse struct type declarations
Recursive structs and embedded structs are not supported yet.
-rw-r--r--lang/golang/go.go2
-rw-r--r--parser/README.md2
-rw-r--r--parser/decl.go8
-rw-r--r--parser/expr.go2
-rw-r--r--parser/interpreter_test.go12
-rw-r--r--parser/type.go32
6 files changed, 45 insertions, 13 deletions
diff --git a/lang/golang/go.go b/lang/golang/go.go
index 44cbd3a..26af4f3 100644
--- a/lang/golang/go.go
+++ b/lang/golang/go.go
@@ -74,6 +74,8 @@ var GoSpec = &lang.Spec{
"*": {TokenId: lang.Mul, Precedence: 1},
"/": {TokenId: lang.Quo, Precedence: 1},
"%": {TokenId: lang.Rem, Precedence: 1},
+ "<<": {TokenId: lang.Shl, Precedence: 1},
+ ">>": {TokenId: lang.Shr, Precedence: 1},
"+": {TokenId: lang.Add, Precedence: 2},
"-": {TokenId: lang.Sub, Precedence: 2},
"=": {TokenId: lang.Assign, Precedence: 6},
diff --git a/parser/README.md b/parser/README.md
index 6f3b6dd..3fbbf61 100644
--- a/parser/README.md
+++ b/parser/README.md
@@ -40,7 +40,7 @@ Go language support:
- [x] function types
- [ ] variadic functions
- [ ] pointers
-- [ ] structures
+- [x] structures
- [ ] embedded structures
- [ ] recursive structures
- [ ] interfaces
diff --git a/parser/decl.go b/parser/decl.go
index ba68da0..782b57a 100644
--- a/parser/decl.go
+++ b/parser/decl.go
@@ -48,7 +48,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, missingTypeError) {
+ if errors.Is(err, MissingTypeErr) {
for _, lt := range decl.Split(lang.Comma) {
vars = append(vars, lt[0].Str)
// TODO: compute type from rhs
@@ -188,7 +188,7 @@ var gotok = map[lang.TokenId]token.Token{
func (p *Parser) ParseType(in Tokens) (out Tokens, err error) {
if len(in) < 2 {
- return out, missingTypeError
+ return out, MissingTypeErr
}
if in[1].Id != lang.ParenBlock {
return p.parseTypeLine(in[1:])
@@ -208,7 +208,7 @@ 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, missingTypeError
+ return out, MissingTypeErr
}
if in[0].Id != lang.Ident {
return out, errors.New("not an ident")
@@ -254,7 +254,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, missingTypeError) {
+ if errors.Is(err, MissingTypeErr) {
for _, lt := range decl.Split(lang.Comma) {
vars = append(vars, lt[0].Str)
// TODO: compute type from rhs
diff --git a/parser/expr.go b/parser/expr.go
index d626e9e..2cd19c6 100644
--- a/parser/expr.go
+++ b/parser/expr.go
@@ -34,7 +34,7 @@ func (p *Parser) ParseExpr(in Tokens) (out Tokens, err error) {
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:
+ 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:
if vl < 2 {
ops = append(ops, t)
break
diff --git a/parser/interpreter_test.go b/parser/interpreter_test.go
index 7ac1212..ac8dfc4 100644
--- a/parser/interpreter_test.go
+++ b/parser/interpreter_test.go
@@ -174,19 +174,23 @@ func TestConst(t *testing.T) {
run(t, []etest{
{src: "const a = 1+2; a", res: "3"},
{src: "const a, b = 1, 2; a+b", res: "3"},
+ {src: "const huge = 1 << 100; const four = huge >> 98; four", res: "4"},
{src: src0 + "c", res: "2"},
})
}
func TestType(t *testing.T) {
+ src0 := `type(
+ I int
+ S string
+)
+`
run(t, []etest{
{src: "type t int; var a t = 1; a", res: "1"},
{src: "type t = int; var a t = 1; a", res: "1"},
- {src: `type (
- I int
- S string
-); var s S = "xx"; s`, res: "xx"},
+ {src: src0 + `var s S = "xx"; s`, res: "xx"},
+ {src: "type T struct {a string; b, c int}; var t T; t", res: "{ 0 0}"},
})
}
diff --git a/parser/type.go b/parser/type.go
index 41bcfe1..f39acea 100644
--- a/parser/type.go
+++ b/parser/type.go
@@ -19,7 +19,10 @@ const (
)
var (
- missingTypeError = errors.New("Missing type")
+ InvalidTypeErr = errors.New("invalid type")
+ MissingTypeErr = errors.New("missing type")
+ SyntaxErr = errors.New("syntax error")
+ TypeNotImplementedErr = errors.New("not implemented")
)
// ParseTypeExpr parses a list of tokens defining a type expresssion and returns
@@ -69,9 +72,32 @@ func (p *Parser) ParseTypeExpr(in Tokens) (typ reflect.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("invalid type %s", in[0].Str)
+ return nil, fmt.Errorf("%w: %s", InvalidTypeErr, 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 in, err = p.Scan(in[1].Block(), false); err != nil {
+ return nil, err
+ }
+ var fields []reflect.StructField
+ for _, lt := range in.Split(lang.Semicolon) {
+ types, names, err := p.parseParamTypes(lt, parseTypeType)
+ if err != nil {
+ return nil, err
+ }
+ for i, name := range names {
+ fields = append(fields, reflect.StructField{Name: "X" + name, Type: types[i]})
+ // TODO: handle embedded fields
+ }
+ }
+ return reflect.StructOf(fields), nil
+
+ default:
+ return nil, fmt.Errorf("%w: %v", TypeNotImplementedErr, in[0].Name())
}
return typ, err
}
@@ -93,7 +119,7 @@ func (p *Parser) parseParamTypes(in Tokens, flag typeFlag) (types []reflect.Type
t = t[1:]
if len(t) == 0 {
if len(types) == 0 {
- return nil, nil, missingTypeError
+ return nil, nil, MissingTypeErr
}
// Type was ommitted, apply the previous one from the right.
types = append([]reflect.Type{types[0]}, types...)