add parse command

This commit is contained in:
Arpad Ryszka 2018-01-09 00:21:35 +01:00
parent ab3032e868
commit 736741915c
12 changed files with 1182 additions and 1011 deletions

View File

@ -18,7 +18,7 @@ func check(args []string) int {
o.command.flagSet.StringVar(&o.input.inline, "input-string", "", inputStringUsage) o.command.flagSet.StringVar(&o.input.inline, "input-string", "", inputStringUsage)
o.command.flagSet.StringVar(&o.input.fileName, "input", "", inputFileUsage) o.command.flagSet.StringVar(&o.input.fileName, "input", "", inputFileUsage)
if o.command.checkHelp() { if o.command.help() {
return 0 return 0
} }
@ -26,13 +26,13 @@ func check(args []string) int {
return code return code
} }
o.input.positional = o.command.flagSet.Args() s, code := o.syntax.openSyntax()
input, code := o.input.open()
if code != 0 { if code != 0 {
return code return code
} }
s, code := o.syntax.openSyntax() o.input.positional = o.command.flagSet.Args()
input, code := o.input.open()
if code != 0 { if code != 0 {
return code return code
} }

View File

@ -2,25 +2,8 @@ package main
import "testing" import "testing"
func TestCheck(t *testing.T) { var checkFailureTests = []mainTest{
runMainTest(t, {
mainTest{
title: "help",
args: []string{
"treerack", "check", "-help",
},
stdout: []string{
checkUsage,
"-syntax",
"-syntax-string",
"-input",
"-input-string",
checkExample,
docRef,
},
},
mainTest{
title: "invalid flag", title: "invalid flag",
args: []string{ args: []string{
"treerack", "check", "-foo", "treerack", "check", "-foo",
@ -34,7 +17,7 @@ func TestCheck(t *testing.T) {
}, },
}, },
mainTest{ {
title: "multiple syntaxes", title: "multiple syntaxes",
args: []string{ args: []string{
"treerack", "check", "-syntax", "foo.treerack", "-syntax-string", `foo = "bar"`, "-input-string", "bar", "treerack", "check", "-syntax", "foo.treerack", "-syntax-string", `foo = "bar"`, "-input-string", "bar",
@ -49,7 +32,7 @@ func TestCheck(t *testing.T) {
}, },
}, },
mainTest{ {
title: "multiple inputs", title: "multiple inputs",
args: []string{ args: []string{
"treerack", "check", "-syntax-string", `foo = "bar"`, "-input", "foo.txt", "-input-string", "bar", "treerack", "check", "-syntax-string", `foo = "bar"`, "-input", "foo.txt", "-input-string", "bar",
@ -64,7 +47,7 @@ func TestCheck(t *testing.T) {
}, },
}, },
mainTest{ {
title: "multiple inputs, positional", title: "multiple inputs, positional",
args: []string{ args: []string{
"treerack", "check", "-syntax-string", `foo = "bar"`, "foo.txt", "bar.txt", "treerack", "check", "-syntax-string", `foo = "bar"`, "foo.txt", "bar.txt",
@ -79,7 +62,7 @@ func TestCheck(t *testing.T) {
}, },
}, },
mainTest{ {
title: "multiple inputs, positional and explicit file", title: "multiple inputs, positional and explicit file",
args: []string{ args: []string{
"treerack", "check", "-syntax-string", `foo = "bar"`, "-input", "foo.txt", "bar.txt", "treerack", "check", "-syntax-string", `foo = "bar"`, "-input", "foo.txt", "bar.txt",
@ -94,7 +77,7 @@ func TestCheck(t *testing.T) {
}, },
}, },
mainTest{ {
title: "no syntax", title: "no syntax",
args: []string{ args: []string{
"treerack", "check", "-input-string", "foo", "treerack", "check", "-input-string", "foo",
@ -109,7 +92,7 @@ func TestCheck(t *testing.T) {
}, },
}, },
mainTest{ {
title: "no input", title: "no input",
args: []string{ args: []string{
"treerack", "check", "-syntax-string", `foo = "bar"`, "treerack", "check", "-syntax-string", `foo = "bar"`,
@ -124,7 +107,7 @@ func TestCheck(t *testing.T) {
}, },
}, },
mainTest{ {
title: "invalid syntax", title: "invalid syntax",
args: []string{ args: []string{
"treerack", "check", "-syntax-string", "foo", "-input-string", "foo", "treerack", "check", "-syntax-string", "foo", "-input-string", "foo",
@ -135,7 +118,7 @@ func TestCheck(t *testing.T) {
}, },
}, },
mainTest{ {
title: "syntax file open fails", title: "syntax file open fails",
args: []string{ args: []string{
"treerack", "check", "-syntax", "noexist.treerack", "-input-string", "foo", "treerack", "check", "-syntax", "noexist.treerack", "-input-string", "foo",
@ -146,7 +129,7 @@ func TestCheck(t *testing.T) {
}, },
}, },
mainTest{ {
title: "input file open fails", title: "input file open fails",
args: []string{ args: []string{
"treerack", "check", "-syntax-string", `foo = "bar"`, "-input", "noexist.txt", "treerack", "check", "-syntax-string", `foo = "bar"`, "-input", "noexist.txt",
@ -157,21 +140,34 @@ func TestCheck(t *testing.T) {
}, },
}, },
mainTest{ {
title: "invalid input",
args: []string{
"treerack", "check", "-syntax-string", `foo = "bar"`, "-input-string", "foo",
},
exit: -1,
stderr: []string{
"parse failed",
},
},
}
var checkTests = []mainTest{
{
title: "syntax as file", title: "syntax as file",
args: []string{ args: []string{
"treerack", "check", "-syntax", "foo_test.treerack", "-input-string", "bar", "treerack", "check", "-syntax", "foo_test.treerack", "-input-string", "bar",
}, },
}, },
mainTest{ {
title: "syntax as string", title: "syntax as string",
args: []string{ args: []string{
"treerack", "check", "-syntax-string", `foo = "bar"`, "-input-string", "bar", "treerack", "check", "-syntax-string", `foo = "bar"`, "-input-string", "bar",
}, },
}, },
mainTest{ {
title: "input as stdin", title: "input as stdin",
args: []string{ args: []string{
"treerack", "check", "-syntax-string", `foo = "bar"`, "treerack", "check", "-syntax-string", `foo = "bar"`,
@ -179,18 +175,38 @@ func TestCheck(t *testing.T) {
stdin: "bar", stdin: "bar",
}, },
mainTest{ {
title: "input as file", title: "input as file",
args: []string{ args: []string{
"treerack", "check", "-syntax-string", `foo = "bar"`, "-input", "bar_test.txt", "treerack", "check", "-syntax-string", `foo = "bar"`, "-input", "bar_test.txt",
}, },
}, },
mainTest{ {
title: "input as string", title: "input as string",
args: []string{ args: []string{
"treerack", "check", "-syntax-string", `foo = "bar"`, "-input-string", "bar", "treerack", "check", "-syntax-string", `foo = "bar"`, "-input-string", "bar",
}, },
}, },
) }
func TestCheck(t *testing.T) {
runMainTest(t, mainTest{
title: "help",
args: []string{
"treerack", "check", "-help",
},
stdout: []string{
checkUsage,
"-syntax",
"-syntax-string",
"-input",
"-input-string",
checkExample,
docRef,
},
})
runMainTest(t, checkFailureTests...)
runMainTest(t, checkTests...)
} }

View File

@ -13,7 +13,7 @@ func checkSyntax(args []string) int {
o.command.flagSet.StringVar(&o.syntax.inline, "syntax-string", "", syntaxStringUsage) o.command.flagSet.StringVar(&o.syntax.inline, "syntax-string", "", syntaxStringUsage)
o.command.flagSet.StringVar(&o.syntax.fileName, "syntax", "", syntaxFileUsage) o.command.flagSet.StringVar(&o.syntax.fileName, "syntax", "", syntaxFileUsage)
if o.command.checkHelp() { if o.command.help() {
return 0 return 0
} }

View File

@ -2,23 +2,8 @@ package main
import "testing" import "testing"
func TestCheckSyntax(t *testing.T) { var checkSyntaxFailureTests = []mainTest{
runMainTest(t, {
mainTest{
title: "help",
args: []string{
"treerack", "check-syntax", "-help",
},
stdout: []string{
checkSyntaxUsage,
"-syntax",
"-syntax-string",
checkSyntaxExample,
docRef,
},
},
mainTest{
title: "invalid flag", title: "invalid flag",
args: []string{ args: []string{
"treerack", "check-syntax", "-foo", "treerack", "check-syntax", "-foo",
@ -30,7 +15,7 @@ func TestCheckSyntax(t *testing.T) {
}, },
}, },
mainTest{ {
title: "multiple inputs", title: "multiple inputs",
args: []string{ args: []string{
"treerack", "check-syntax", "-syntax", "foo.treerack", "-syntax-string", `foo = "bar"`, "treerack", "check-syntax", "-syntax", "foo.treerack", "-syntax-string", `foo = "bar"`,
@ -43,7 +28,7 @@ func TestCheckSyntax(t *testing.T) {
}, },
}, },
mainTest{ {
title: "multiple inputs, positional", title: "multiple inputs, positional",
args: []string{ args: []string{
"treerack", "check-syntax", "foo.treerack", "bar.treerack", "treerack", "check-syntax", "foo.treerack", "bar.treerack",
@ -56,7 +41,7 @@ func TestCheckSyntax(t *testing.T) {
}, },
}, },
mainTest{ {
title: "multiple inputs, positional and explicit file", title: "multiple inputs, positional and explicit file",
args: []string{ args: []string{
"treerack", "check-syntax", "-syntax", "foo.treerack", "bar.treerack", "treerack", "check-syntax", "-syntax", "foo.treerack", "bar.treerack",
@ -69,7 +54,7 @@ func TestCheckSyntax(t *testing.T) {
}, },
}, },
mainTest{ {
title: "no input", title: "no input",
args: []string{ args: []string{
"treerack", "check-syntax", "treerack", "check-syntax",
@ -82,7 +67,7 @@ func TestCheckSyntax(t *testing.T) {
}, },
}, },
mainTest{ {
title: "invalid input", title: "invalid input",
args: []string{ args: []string{
"treerack", "check-syntax", "-syntax-string", "foo", "treerack", "check-syntax", "-syntax-string", "foo",
@ -93,7 +78,7 @@ func TestCheckSyntax(t *testing.T) {
}, },
}, },
mainTest{ {
title: "file open fails", title: "file open fails",
args: []string{ args: []string{
"treerack", "check-syntax", "-syntax", "noexist.treerack", "treerack", "check-syntax", "-syntax", "noexist.treerack",
@ -103,8 +88,10 @@ func TestCheckSyntax(t *testing.T) {
"file", "file",
}, },
}, },
}
mainTest{ var checkSyntaxTests = []mainTest{
{
title: "syntax as stdin", title: "syntax as stdin",
args: []string{ args: []string{
"treerack", "check-syntax", "treerack", "check-syntax",
@ -112,18 +99,36 @@ func TestCheckSyntax(t *testing.T) {
stdin: `foo = "bar"`, stdin: `foo = "bar"`,
}, },
mainTest{ {
title: "syntax as file", title: "syntax as file",
args: []string{ args: []string{
"treerack", "check-syntax", "-syntax", "foo_test.treerack", "treerack", "check-syntax", "-syntax", "foo_test.treerack",
}, },
}, },
mainTest{ {
title: "syntax as string", title: "syntax as string",
args: []string{ args: []string{
"treerack", "check-syntax", "-syntax-string", `foo = "bar"`, "treerack", "check-syntax", "-syntax-string", `foo = "bar"`,
}, },
}, },
) }
func TestCheckSyntax(t *testing.T) {
runMainTest(t, mainTest{
title: "help",
args: []string{
"treerack", "check-syntax", "-help",
},
stdout: []string{
checkSyntaxUsage,
"-syntax",
"-syntax-string",
checkSyntaxExample,
docRef,
},
})
runMainTest(t, checkSyntaxFailureTests...)
runMainTest(t, checkSyntaxTests...)
} }

View File

@ -28,6 +28,10 @@ const packageNameUsage = `package name of the generated Go code`
const exportUsage = `when the export flag is set, the generated code will have exported symbols to allow using const exportUsage = `when the export flag is set, the generated code will have exported symbols to allow using
it as a separate package.` it as a separate package.`
const prettyUsage = `when the pretty flag is set, the AST will be pretty printed`
const indentUsage = `string used for indentation of the printed AST`
const checkUsage = `'treerack check' takes a syntax description from a file or inline string, an arbitrary piece const checkUsage = `'treerack check' takes a syntax description from a file or inline string, an arbitrary piece
of text from the standard input, or a file, or inline string, and parses the input text with the defined syntax. of text from the standard input, or a file, or inline string, and parses the input text with the defined syntax.
It returns non-zero exit code and prints the problem if the provided syntax is not valid or the intput cannot be It returns non-zero exit code and prints the problem if the provided syntax is not valid or the intput cannot be

View File

@ -19,7 +19,7 @@ func generate(args []string) int {
o.command.flagSet.StringVar(&o.syntax.inline, "syntax-string", "", syntaxStringUsage) o.command.flagSet.StringVar(&o.syntax.inline, "syntax-string", "", syntaxStringUsage)
o.command.flagSet.StringVar(&o.syntax.fileName, "syntax", "", syntaxFileUsage) o.command.flagSet.StringVar(&o.syntax.fileName, "syntax", "", syntaxFileUsage)
if o.command.checkHelp() { if o.command.help() {
return 0 return 0
} }

View File

@ -2,121 +2,10 @@ package main
import "testing" import "testing"
func TestGenerate(t *testing.T) { var generateFailureTests = convertTests("generate", checkSyntaxFailureTests)
runMainTest(t,
mainTest{
title: "help",
args: []string{
"treerack", "generate", "-help",
},
stdout: []string{
generateUsage,
"-export",
"-package-name",
"-syntax",
"-syntax-string",
generateExample,
docRef,
},
},
mainTest{ var generateTests = []mainTest{
title: "invalid flag", {
args: []string{
"treerack", "generate", "-foo",
},
exit: -1,
stderr: []string{
"-export",
"-package-name",
"-syntax",
"-syntax-string",
},
},
mainTest{
title: "multiple inputs",
args: []string{
"treerack", "generate", "-syntax", "foo.treerack", "-syntax-string", `foo = "bar"`,
},
exit: -1,
stderr: []string{
"only one syntax",
"-export",
"-package-name",
"-syntax",
"-syntax-string",
},
},
mainTest{
title: "multiple inputs, positional",
args: []string{
"treerack", "generate", "foo.treerack", "bar.treerack",
},
exit: -1,
stderr: []string{
"only one syntax",
"-export",
"-package-name",
"-syntax",
"-syntax-string",
},
},
mainTest{
title: "multiple inputs, positional and explicit file",
args: []string{
"treerack", "generate", "-syntax", "foo.treerack", "bar.treerack",
},
exit: -1,
stderr: []string{
"only one syntax",
"-export",
"-package-name",
"-syntax",
"-syntax-string",
},
},
mainTest{
title: "no input",
args: []string{
"treerack", "generate",
},
exit: -1,
stderr: []string{
"missing syntax",
"-export",
"-package-name",
"-syntax",
"-syntax-string",
},
},
mainTest{
title: "invalid input",
args: []string{
"treerack", "generate", "-syntax-string", "foo",
},
exit: -1,
stderr: []string{
"parse failed",
},
},
mainTest{
title: "file open fails",
args: []string{
"treerack", "generate", "-syntax", "noexist.treerack",
},
exit: -1,
stderr: []string{
"file",
},
},
mainTest{
title: "failing output", title: "failing output",
args: []string{ args: []string{
"treerack", "generate", "-syntax-string", `foo = "bar"`, "treerack", "generate", "-syntax-string", `foo = "bar"`,
@ -125,7 +14,7 @@ func TestGenerate(t *testing.T) {
exit: -1, exit: -1,
}, },
mainTest{ {
title: "syntax as stdin", title: "syntax as stdin",
args: []string{ args: []string{
"treerack", "generate", "-export", "-package-name", "foo", "treerack", "generate", "-export", "-package-name", "foo",
@ -137,7 +26,7 @@ func TestGenerate(t *testing.T) {
}, },
}, },
mainTest{ {
title: "syntax as file", title: "syntax as file",
args: []string{ args: []string{
"treerack", "generate", "-export", "-package-name", "foo", "-syntax", "foo_test.treerack", "treerack", "generate", "-export", "-package-name", "foo", "-syntax", "foo_test.treerack",
@ -148,7 +37,7 @@ func TestGenerate(t *testing.T) {
}, },
}, },
mainTest{ {
title: "syntax as string", title: "syntax as string",
args: []string{ args: []string{
"treerack", "generate", "-export", "-package-name", "foo", "-syntax-string", `foo = "bar"`, "treerack", "generate", "-export", "-package-name", "foo", "-syntax-string", `foo = "bar"`,
@ -158,5 +47,25 @@ func TestGenerate(t *testing.T) {
"func Parse", "func Parse",
}, },
}, },
) }
func TestGenerate(t *testing.T) {
runMainTest(t, mainTest{
title: "help",
args: []string{
"treerack", "generate", "-help",
},
stdout: []string{
generateUsage,
"-syntax",
"-syntax-string",
"-export",
"-package-name",
generateExample,
docRef,
},
})
runMainTest(t, generateFailureTests...)
runMainTest(t, generateTests...)
} }

View File

@ -32,6 +32,23 @@ func init() {
isTest = true isTest = true
} }
func convertTest(cmd string, t mainTest) mainTest {
args := make([]string, len(t.args))
copy(args, t.args)
args[1] = cmd
t.args = args
return t
}
func convertTests(cmd string, t []mainTest) []mainTest {
tt := make([]mainTest, len(t))
for i := range t {
tt[i] = convertTest(cmd, t[i])
}
return tt
}
func mockArgs(args ...string) (reset func()) { func mockArgs(args ...string) (reset func()) {
original := os.Args original := os.Args
os.Args = args os.Args = args

View File

@ -38,7 +38,7 @@ func (o *commandOptions) parseArgs() (exit int) {
return return
} }
func (o *commandOptions) help() { func (o *commandOptions) printHelp() {
stdout(o.usage) stdout(o.usage)
stdout() stdout()
@ -52,11 +52,11 @@ func (o *commandOptions) help() {
stdout(docRef) stdout(docRef)
} }
func (o *commandOptions) checkHelp() bool { func (o *commandOptions) help() bool {
if len(o.args) == 0 || o.args[0] != "-help" { if len(o.args) == 0 || o.args[0] != "-help" {
return false return false
} }
o.help() o.printHelp()
return true return true
} }

View File

@ -1,5 +1,103 @@
package main package main
import (
"encoding/json"
"github.com/aryszka/treerack"
)
type parseOptions struct {
command *commandOptions
syntax *fileOptions
input *fileOptions
pretty bool
indent string
}
type node struct {
Name string `json:"name"`
From int `json:"from"`
To int `json:"to"`
Text string `json:"text,omitempty"`
Nodes []*node `json:"nodes,omitempty"`
}
func mapNode(n *treerack.Node) *node {
var nn node
nn.Name = n.Name
nn.From = n.From
nn.To = n.To
if len(n.Nodes) == 0 {
nn.Text = n.Text()
return &nn
}
for i := range n.Nodes {
nn.Nodes = append(nn.Nodes, mapNode(n.Nodes[i]))
}
return &nn
}
func parse(args []string) int { func parse(args []string) int {
var o parseOptions
o.command = initOptions(parseUsage, parseExample, args)
o.syntax = &fileOptions{typ: "syntax", flagSet: o.command.flagSet}
o.input = &fileOptions{typ: "input", flagSet: o.command.flagSet}
o.command.flagSet.StringVar(&o.syntax.inline, "syntax-string", "", syntaxStringUsage)
o.command.flagSet.StringVar(&o.syntax.fileName, "syntax", "", syntaxFileUsage)
o.command.flagSet.StringVar(&o.input.inline, "input-string", "", inputStringUsage)
o.command.flagSet.StringVar(&o.input.fileName, "input", "", inputFileUsage)
o.command.flagSet.BoolVar(&o.pretty, "pretty", false, prettyUsage)
o.command.flagSet.StringVar(&o.indent, "indent", "", indentUsage)
if o.command.help() {
return 0
}
if code := o.command.parseArgs(); code != 0 {
return code
}
s, code := o.syntax.openSyntax()
if code != 0 {
return code
}
o.input.positional = o.command.flagSet.Args()
input, code := o.input.open()
if code != 0 {
return code
}
n, err := s.Parse(input)
if err != nil {
stderr(err)
return -1
}
nn := mapNode(n)
marshal := json.Marshal
if o.pretty || o.indent != "" {
if o.indent == "" {
o.indent = " "
}
marshal = func(n interface{}) ([]byte, error) {
return json.MarshalIndent(n, "", o.indent)
}
}
b, err := marshal(nn)
if err != nil {
stderr(err)
}
stdout(string(b))
return 0 return 0
} }

122
cmd/treerack/parse_test.go Normal file
View File

@ -0,0 +1,122 @@
package main
import "testing"
var parseFailureTests = convertTests("parse", checkFailureTests)
var parseTests = []mainTest{
{
title: "syntax as file",
args: []string{
"treerack", "parse", "-syntax", "foo_test.treerack", "-input-string", "bar",
},
stdout: []string{
`"name":"foo"`,
},
},
{
title: "syntax as string",
args: []string{
"treerack", "parse", "-syntax-string", `foo = "bar"`, "-input-string", "bar",
},
stdout: []string{
`"name":"foo"`,
},
},
{
title: "input as stdin",
args: []string{
"treerack", "parse", "-syntax-string", `foo = "bar"`,
},
stdin: "bar",
stdout: []string{
`"name":"foo"`,
},
},
{
title: "input as file",
args: []string{
"treerack", "parse", "-syntax-string", `foo = "bar"`, "-input", "bar_test.txt",
},
stdout: []string{
`"name":"foo"`,
},
},
{
title: "input as string",
args: []string{
"treerack", "parse", "-syntax-string", `foo = "bar"`, "-input-string", "bar",
},
stdout: []string{
`"name":"foo"`,
},
},
{
title: "pretty",
args: []string{
"treerack", "parse", "-syntax-string", `foo = "bar"`, "-input-string", "bar", "-pretty",
},
stdout: []string{
` "name": "foo"`,
},
},
{
title: "pretty and indent",
args: []string{
"treerack", "parse", "-syntax-string", `foo = "bar"`, "-input-string", "bar", "-pretty", "-indent", "xx",
},
stdout: []string{
`xx"name": "foo"`,
},
},
{
title: "indent without pretty",
args: []string{
"treerack", "parse", "-syntax-string", `foo = "bar"`, "-input-string", "bar", "-pretty", "-indent", "xx",
},
stdout: []string{
`xx"name": "foo"`,
},
},
{
title: "with child nodes",
args: []string{
"treerack", "parse", "-syntax-string", `foo = "bar"; doc = foo`, "-input-string", "bar",
},
stdout: []string{
`"nodes":[`,
`"text":"bar"`,
},
},
}
func TestParse(t *testing.T) {
runMainTest(t, mainTest{
title: "help",
args: []string{
"treerack", "parse", "-help",
},
stdout: []string{
parseUsage,
"-syntax",
"-syntax-string",
"-input",
"-input-string",
"-pretty",
"-indent",
parseExample,
docRef,
},
})
runMainTest(t, parseFailureTests...)
runMainTest(t, parseTests...)
}

File diff suppressed because it is too large Load Diff