Skip to content

Commit 267ab7e

Browse files
committed
add participle example
1 parent dbd0cb3 commit 267ab7e

File tree

8 files changed

+419
-0
lines changed

8 files changed

+419
-0
lines changed

go.mod

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module github.com/quasilyte/parsing-in-go
2+
3+
go 1.16
4+
5+
require github.com/alecthomas/participle/v2 v2.0.0-alpha6 // indirect

go.sum

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
github.com/alecthomas/participle/v2 v2.0.0-alpha6 h1:6IeFBBLWi0xcTk4ModH9UKkLBYf5l5OzaYkJOjZW1rg=
2+
github.com/alecthomas/participle/v2 v2.0.0-alpha6/go.mod h1:Z1zPLDbcGsVsBYsThKXY00i84575bN/nMczzIrU4rWU=
3+
github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
4+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
7+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
8+
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
9+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
10+
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

participle/convert.go

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package main
2+
3+
import (
4+
"github.com/quasilyte/parsing-in-go/phpdoc"
5+
)
6+
7+
type converter struct{}
8+
9+
func (conv *converter) Convert(expr *UnionExpr) phpdoc.Type {
10+
return conv.convertUnion(expr)
11+
}
12+
13+
func (conv *converter) convertPrefix(expr *PrefixExpr) phpdoc.Type {
14+
result := conv.convertPrimary(expr.Right)
15+
if len(expr.Ops) != 0 {
16+
for _, prefix := range expr.Ops {
17+
switch prefix.Tok {
18+
case "?":
19+
result = &phpdoc.NullableType{Elem: result}
20+
}
21+
}
22+
}
23+
// if expr.Postfix != nil {
24+
25+
// }
26+
return result
27+
}
28+
29+
func (conv *converter) convertPostfix(expr *PostfixExpr) phpdoc.Type {
30+
result := conv.convertPrefix(expr.Left)
31+
if len(expr.Ops) != 0 {
32+
for _, postfix := range expr.Ops {
33+
switch postfix.Tok {
34+
case "[]":
35+
result = &phpdoc.ArrayType{Elem: result}
36+
case "?":
37+
result = &phpdoc.OptionalKeyType{Elem: result}
38+
}
39+
}
40+
}
41+
return result
42+
}
43+
44+
func (conv *converter) convertUnion(expr *UnionExpr) phpdoc.Type {
45+
left := conv.convertIntersection(expr.Left)
46+
if expr.Right != nil {
47+
right := conv.convertUnion(expr.Right)
48+
return &phpdoc.UnionType{X: left, Y: right}
49+
}
50+
return left
51+
}
52+
53+
func (conv *converter) convertIntersection(expr *IntersectionExpr) phpdoc.Type {
54+
left := conv.convertPostfix(expr.Left)
55+
if expr.Right != nil {
56+
right := conv.convertIntersection(expr.Right)
57+
return &phpdoc.IntersectionType{X: left, Y: right}
58+
}
59+
return left
60+
}
61+
62+
func (conv *converter) convertPrimary(expr *PrimaryExpr) phpdoc.Type {
63+
if expr.TypeName != nil {
64+
if expr.TypeName.Primitive != nil {
65+
return &phpdoc.PrimitiveTypeName{Name: *expr.TypeName.Primitive}
66+
}
67+
return conv.convertClassName(expr.TypeName.Class, nil)
68+
}
69+
if expr.Parens != nil {
70+
return conv.Convert(expr.Parens)
71+
}
72+
return nil
73+
}
74+
75+
func (conv *converter) convertClassName(name *ClassNameExpr, parts []string) phpdoc.Type {
76+
parts = append(parts, name.Part)
77+
if name.Next == nil {
78+
return &phpdoc.TypeName{Parts: parts}
79+
}
80+
return conv.convertClassName(name.Next, parts)
81+
}

participle/main copy.go

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
//+build ignore
2+
// +build ignore
3+
4+
package main
5+
6+
import (
7+
"encoding/json"
8+
"flag"
9+
"os"
10+
11+
"github.com/alecthomas/participle/v2"
12+
)
13+
14+
// TODO: read http://www.craftinginterpreters.com/parsing-expressions.html
15+
16+
// int
17+
// Foo
18+
// A\B
19+
// A|B
20+
// int[]
21+
// ?int
22+
// ?int[]
23+
// (A|B)[]
24+
// ?(A|B)[]
25+
// A|B[]
26+
27+
// Note: using something like `@@ "|" @@` will cause infinite recursion.
28+
29+
type TypeExpr struct {
30+
Left *TypeExprTerm `@@`
31+
Right *TypeExprOp `@@*`
32+
}
33+
34+
type TypeExprTerm struct {
35+
TypeName *TypeNameExpr `@@`
36+
Nullable *TypeExpr `| "?" @@`
37+
Parens *TypeExpr `| "(" @@ ")"`
38+
}
39+
40+
type TypeExprOp struct {
41+
PostfixOp *PostfixOp `@@`
42+
InfixOp *InfixOp `| @@`
43+
}
44+
45+
type PostfixOp struct {
46+
Op string `@("[" "]" | "?")`
47+
Next *PostfixOp "@@?"
48+
}
49+
50+
type IntersectionExpr struct {
51+
}
52+
53+
type InfixOp struct {
54+
IntersectionExpr *TypeExpr `("&" @@)`
55+
UnionExpr *TypeExpr `| ("|" @@)`
56+
57+
// Op string `@("|" | "&")`
58+
// Expr *TypeExpr `@@`
59+
}
60+
61+
type TypeNameExpr struct {
62+
Primitive *string `@("int"|"float"|"string"|"bool")`
63+
Name *NameExpr `| @@`
64+
}
65+
66+
type NameExpr struct {
67+
Part string `@Ident`
68+
Next *NameExpr `("\\" @@)?`
69+
}
70+
71+
type ArrayTypeExpr struct {
72+
Elem *TypeExpr `@@ "[" "]"`
73+
}
74+
75+
func main() {
76+
flag.Parse()
77+
78+
parser := participle.MustBuild(&TypeExpr{})
79+
var result TypeExpr
80+
if err := parser.ParseString("", flag.Args()[0], &result); err != nil {
81+
panic(err)
82+
}
83+
84+
encoder := json.NewEncoder(os.Stdout)
85+
encoder.SetIndent("", " ")
86+
if err := encoder.Encode(result); err != nil {
87+
panic(err)
88+
}
89+
90+
// var conv converter
91+
// ast := conv.Convert(&result)
92+
// fmt.Printf("%#v\n", ast)
93+
// fmt.Printf("%s\n", ast)
94+
}

participle/main.go

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"flag"
6+
"fmt"
7+
"os"
8+
9+
"github.com/alecthomas/participle/v2"
10+
)
11+
12+
// Note: using something like `@@ "|" @@` will cause infinite recursion.
13+
// Note: this "grammar" produces inefficient AST, so it's impractical for big
14+
// inputs, but OK for phpdoc types
15+
16+
type PrimaryExpr struct {
17+
TypeName *TypeNameExpr `@@`
18+
Parens *UnionExpr `| "(" @@ ")"`
19+
}
20+
21+
type TypeNameExpr struct {
22+
Primitive *string `@("null"|"false"|"int"|"float"|"string"|"bool")`
23+
Class *ClassNameExpr `| @@`
24+
}
25+
26+
type ClassNameExpr struct {
27+
Part string `@Ident`
28+
Next *ClassNameExpr `("\\" @@)?`
29+
}
30+
31+
type PrefixExpr struct {
32+
Ops []*PrefixOp `@@*`
33+
Right *PrimaryExpr `@@`
34+
}
35+
36+
type PrefixOp struct {
37+
Tok string `@("?")`
38+
}
39+
40+
type PostfixExpr struct {
41+
Left *PrefixExpr `@@`
42+
Ops []*PostfixOp `@@*`
43+
}
44+
45+
type PostfixOp struct {
46+
Tok string `@("[" "]" | "?")`
47+
}
48+
49+
type IntersectionExpr struct {
50+
Left *PostfixExpr `@@`
51+
Right *IntersectionExpr `("&" @@)?`
52+
}
53+
54+
type UnionExpr struct {
55+
Left *IntersectionExpr `@@`
56+
Right *UnionExpr `("|" @@)?`
57+
}
58+
59+
func main() {
60+
flag.Parse()
61+
62+
parser := participle.MustBuild(&UnionExpr{})
63+
var result UnionExpr
64+
if err := parser.ParseString("", flag.Args()[0], &result); err != nil {
65+
panic(err)
66+
}
67+
68+
encoder := json.NewEncoder(os.Stdout)
69+
encoder.SetIndent("", " ")
70+
if err := encoder.Encode(result); err != nil {
71+
panic(err)
72+
}
73+
74+
var conv converter
75+
ast := conv.Convert(&result)
76+
fmt.Printf("%#v\n", ast)
77+
fmt.Printf("%s\n", ast)
78+
}

participle/main_test.go

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package main
2+
3+
import (
4+
"testing"
5+
6+
"github.com/alecthomas/participle/v2"
7+
"github.com/quasilyte/parsing-in-go/phpdoc"
8+
"github.com/quasilyte/parsing-in-go/phpdoctest"
9+
)
10+
11+
type parserWrapper struct {
12+
parser *participle.Parser
13+
}
14+
15+
func (p *parserWrapper) Parse(s string) (phpdoc.Type, error) {
16+
var result UnionExpr
17+
if err := p.parser.ParseString("", s, &result); err != nil {
18+
return nil, err
19+
}
20+
var conv converter
21+
return conv.Convert(&result), nil
22+
}
23+
24+
func TestMain(t *testing.T) {
25+
parser := &parserWrapper{
26+
parser: participle.MustBuild(&UnionExpr{}),
27+
}
28+
phpdoctest.Run(t, parser)
29+
}

phpdoc/phpdoc.go

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package phpdoc
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
type Type interface {
9+
String() string
10+
}
11+
12+
type PrimitiveTypeName struct {
13+
Name string
14+
}
15+
16+
type TypeName struct {
17+
Parts []string
18+
}
19+
20+
type NullableType struct {
21+
Elem Type
22+
}
23+
24+
type OptionalKeyType struct {
25+
Elem Type
26+
}
27+
28+
type ArrayType struct {
29+
Elem Type
30+
}
31+
32+
type UnionType struct {
33+
X Type
34+
Y Type
35+
}
36+
37+
type IntersectionType struct {
38+
X Type
39+
Y Type
40+
}
41+
42+
func (typ *PrimitiveTypeName) String() string { return typ.Name }
43+
func (typ *TypeName) String() string { return strings.Join(typ.Parts, `\`) }
44+
func (typ *NullableType) String() string { return "?(" + typ.Elem.String() + ")" }
45+
func (typ *OptionalKeyType) String() string { return "(" + typ.Elem.String() + ")?" }
46+
func (typ *ArrayType) String() string { return "(" + typ.Elem.String() + ")[]" }
47+
func (typ *UnionType) String() string { return fmt.Sprintf("(%s)|(%s)", typ.X, typ.Y) }
48+
func (typ *IntersectionType) String() string { return fmt.Sprintf("(%s)&(%s)", typ.X, typ.Y) }

0 commit comments

Comments
 (0)