Skip to content

Commit 3bfe3bf

Browse files
committed
strparse implementation
1 parent eefc00e commit 3bfe3bf

File tree

2 files changed

+103
-0
lines changed

2 files changed

+103
-0
lines changed

strparse.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Package strparse provides convenience wrappers around `go/parser` for simple
2+
// expression, statement and declaretion parsing from string.
3+
//
4+
// Can be used to construct AST nodes using source syntax.
5+
package strparse
6+
7+
import (
8+
"go/ast"
9+
"go/parser"
10+
"go/token"
11+
)
12+
13+
var (
14+
// BadExpr is returned as a parse result for malformed expressions.
15+
// Should be treated as constant or readonly variable.
16+
BadExpr = &ast.BadExpr{}
17+
18+
// BadStmt is returned as a parse result for malformed statmenents.
19+
// Should be treated as constant or readonly variable.
20+
BadStmt = &ast.BadStmt{}
21+
22+
// BadDecl is returned as a parse result for malformed declarations.
23+
// Should be treated as constant or readonly variable.
24+
BadDecl = &ast.BadDecl{}
25+
)
26+
27+
// Expr parses single expression node from s.
28+
// In case of parse error, BadExpr is returned.
29+
func Expr(s string) ast.Expr {
30+
node, err := parser.ParseExpr(s)
31+
if err != nil {
32+
return BadExpr
33+
}
34+
return node
35+
}
36+
37+
// Stmt parses single statement node from s.
38+
// In case of parse error, BadStmt is returned.
39+
func Stmt(s string) ast.Stmt {
40+
node, err := parser.ParseFile(token.NewFileSet(), "", "package main;func main() {"+s+"}", 0)
41+
if err != nil {
42+
return BadStmt
43+
}
44+
return node.Decls[0].(*ast.FuncDecl).Body.List[0]
45+
}
46+
47+
// Decl parses single declaration node from s.
48+
// In case of parse error, BadDecl is returned.
49+
func Decl(s string) ast.Decl {
50+
node, err := parser.ParseFile(token.NewFileSet(), "", "package main;"+s, 0)
51+
if err != nil {
52+
return BadDecl
53+
}
54+
return node.Decls[0]
55+
}

strparse_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package strparse
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"go/ast"
7+
"go/format"
8+
"go/token"
9+
"testing"
10+
)
11+
12+
func equalPrintedForm(x ast.Node, y string) bool {
13+
var buf bytes.Buffer
14+
if err := format.Node(&buf, token.NewFileSet(), x); err != nil {
15+
panic(fmt.Sprintf("format error: %v", err))
16+
}
17+
b, err := format.Source([]byte(y))
18+
if err != nil {
19+
panic(fmt.Sprintf("format error: %v", err))
20+
}
21+
xString := buf.String()
22+
yString := string(b)
23+
return xString == yString
24+
}
25+
26+
func TestParseExpr(t *testing.T) {
27+
const input = `1 * (2 << x)/s[1].f()(^y)`
28+
expr := Expr(input)
29+
if !equalPrintedForm(expr, input) {
30+
t.Error("expr printed form not match")
31+
}
32+
}
33+
34+
func TestParseStmt(t *testing.T) {
35+
const input = `for i := 0; i < len(xs); i++ { fmt.Println(i) }`
36+
expr := Stmt(input)
37+
if !equalPrintedForm(expr, input) {
38+
t.Error("stmt printed form not match")
39+
}
40+
}
41+
42+
func TestParseDecl(t *testing.T) {
43+
const input = `var (x int = 19; a, b = f(); c, d = "1", "2"+"3")`
44+
expr := Decl(input)
45+
if !equalPrintedForm(expr, input) {
46+
t.Error("decl printed form not match")
47+
}
48+
}

0 commit comments

Comments
 (0)