add check command
This commit is contained in:
parent
9f541e527a
commit
ab3032e868
1
cmd/treerack/bar_test.txt
Normal file
1
cmd/treerack/bar_test.txt
Normal file
@ -0,0 +1 @@
|
||||
bar
|
@ -1,5 +1,47 @@
|
||||
package main
|
||||
|
||||
func check([]string) int {
|
||||
type checkOptions struct {
|
||||
command *commandOptions
|
||||
syntax *fileOptions
|
||||
input *fileOptions
|
||||
}
|
||||
|
||||
func check(args []string) int {
|
||||
var o checkOptions
|
||||
o.command = initOptions(checkUsage, checkExample, 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)
|
||||
|
||||
if o.command.checkHelp() {
|
||||
return 0
|
||||
}
|
||||
|
||||
if code := o.command.parseArgs(); code != 0 {
|
||||
return code
|
||||
}
|
||||
|
||||
o.input.positional = o.command.flagSet.Args()
|
||||
input, code := o.input.open()
|
||||
if code != 0 {
|
||||
return code
|
||||
}
|
||||
|
||||
s, code := o.syntax.openSyntax()
|
||||
if code != 0 {
|
||||
return code
|
||||
}
|
||||
|
||||
_, err := s.Parse(input)
|
||||
if err != nil {
|
||||
stderr(err)
|
||||
return -1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
@ -1 +1,196 @@
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
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,
|
||||
},
|
||||
},
|
||||
|
||||
mainTest{
|
||||
title: "invalid flag",
|
||||
args: []string{
|
||||
"treerack", "check", "-foo",
|
||||
},
|
||||
exit: -1,
|
||||
stderr: []string{
|
||||
"-syntax",
|
||||
"-syntax-string",
|
||||
"-input",
|
||||
"-input-string",
|
||||
},
|
||||
},
|
||||
|
||||
mainTest{
|
||||
title: "multiple syntaxes",
|
||||
args: []string{
|
||||
"treerack", "check", "-syntax", "foo.treerack", "-syntax-string", `foo = "bar"`, "-input-string", "bar",
|
||||
},
|
||||
exit: -1,
|
||||
stderr: []string{
|
||||
"only one syntax",
|
||||
"-syntax",
|
||||
"-syntax-string",
|
||||
"-input",
|
||||
"-input-string",
|
||||
},
|
||||
},
|
||||
|
||||
mainTest{
|
||||
title: "multiple inputs",
|
||||
args: []string{
|
||||
"treerack", "check", "-syntax-string", `foo = "bar"`, "-input", "foo.txt", "-input-string", "bar",
|
||||
},
|
||||
exit: -1,
|
||||
stderr: []string{
|
||||
"only one input",
|
||||
"-syntax",
|
||||
"-syntax-string",
|
||||
"-input",
|
||||
"-input-string",
|
||||
},
|
||||
},
|
||||
|
||||
mainTest{
|
||||
title: "multiple inputs, positional",
|
||||
args: []string{
|
||||
"treerack", "check", "-syntax-string", `foo = "bar"`, "foo.txt", "bar.txt",
|
||||
},
|
||||
exit: -1,
|
||||
stderr: []string{
|
||||
"only one input",
|
||||
"-syntax",
|
||||
"-syntax-string",
|
||||
"-input",
|
||||
"-input-string",
|
||||
},
|
||||
},
|
||||
|
||||
mainTest{
|
||||
title: "multiple inputs, positional and explicit file",
|
||||
args: []string{
|
||||
"treerack", "check", "-syntax-string", `foo = "bar"`, "-input", "foo.txt", "bar.txt",
|
||||
},
|
||||
exit: -1,
|
||||
stderr: []string{
|
||||
"only one input",
|
||||
"-syntax",
|
||||
"-syntax-string",
|
||||
"-input",
|
||||
"-input-string",
|
||||
},
|
||||
},
|
||||
|
||||
mainTest{
|
||||
title: "no syntax",
|
||||
args: []string{
|
||||
"treerack", "check", "-input-string", "foo",
|
||||
},
|
||||
exit: -1,
|
||||
stderr: []string{
|
||||
"missing syntax",
|
||||
"-syntax",
|
||||
"-syntax-string",
|
||||
"-input",
|
||||
"-input-string",
|
||||
},
|
||||
},
|
||||
|
||||
mainTest{
|
||||
title: "no input",
|
||||
args: []string{
|
||||
"treerack", "check", "-syntax-string", `foo = "bar"`,
|
||||
},
|
||||
exit: -1,
|
||||
stderr: []string{
|
||||
"missing input",
|
||||
"-syntax",
|
||||
"-syntax-string",
|
||||
"-input",
|
||||
"-input-string",
|
||||
},
|
||||
},
|
||||
|
||||
mainTest{
|
||||
title: "invalid syntax",
|
||||
args: []string{
|
||||
"treerack", "check", "-syntax-string", "foo", "-input-string", "foo",
|
||||
},
|
||||
exit: -1,
|
||||
stderr: []string{
|
||||
"parse failed",
|
||||
},
|
||||
},
|
||||
|
||||
mainTest{
|
||||
title: "syntax file open fails",
|
||||
args: []string{
|
||||
"treerack", "check", "-syntax", "noexist.treerack", "-input-string", "foo",
|
||||
},
|
||||
exit: -1,
|
||||
stderr: []string{
|
||||
"file",
|
||||
},
|
||||
},
|
||||
|
||||
mainTest{
|
||||
title: "input file open fails",
|
||||
args: []string{
|
||||
"treerack", "check", "-syntax-string", `foo = "bar"`, "-input", "noexist.txt",
|
||||
},
|
||||
exit: -1,
|
||||
stderr: []string{
|
||||
"file",
|
||||
},
|
||||
},
|
||||
|
||||
mainTest{
|
||||
title: "syntax as file",
|
||||
args: []string{
|
||||
"treerack", "check", "-syntax", "foo_test.treerack", "-input-string", "bar",
|
||||
},
|
||||
},
|
||||
|
||||
mainTest{
|
||||
title: "syntax as string",
|
||||
args: []string{
|
||||
"treerack", "check", "-syntax-string", `foo = "bar"`, "-input-string", "bar",
|
||||
},
|
||||
},
|
||||
|
||||
mainTest{
|
||||
title: "input as stdin",
|
||||
args: []string{
|
||||
"treerack", "check", "-syntax-string", `foo = "bar"`,
|
||||
},
|
||||
stdin: "bar",
|
||||
},
|
||||
|
||||
mainTest{
|
||||
title: "input as file",
|
||||
args: []string{
|
||||
"treerack", "check", "-syntax-string", `foo = "bar"`, "-input", "bar_test.txt",
|
||||
},
|
||||
},
|
||||
|
||||
mainTest{
|
||||
title: "input as string",
|
||||
args: []string{
|
||||
"treerack", "check", "-syntax-string", `foo = "bar"`, "-input-string", "bar",
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
package main
|
||||
|
||||
type checkOptions struct {
|
||||
type checkSyntaxOptions struct {
|
||||
command *commandOptions
|
||||
syntax *fileOptions
|
||||
}
|
||||
|
||||
func checkSyntax(args []string) int {
|
||||
var o checkOptions
|
||||
var o checkSyntaxOptions
|
||||
o.command = initOptions(checkSyntaxUsage, checkSyntaxExample, args)
|
||||
o.syntax = &fileOptions{flagSet: o.command.flagSet}
|
||||
o.syntax = &fileOptions{typ: "syntax", flagSet: o.command.flagSet}
|
||||
|
||||
o.command.flagSet.StringVar(&o.syntax.inline, "syntax-string", "", syntaxStringUsage)
|
||||
o.command.flagSet.StringVar(&o.syntax.fileName, "syntax", "", syntaxFileUsage)
|
||||
@ -22,6 +22,6 @@ func checkSyntax(args []string) int {
|
||||
}
|
||||
|
||||
o.syntax.positional = o.command.flagSet.Args()
|
||||
_, code := openSyntax(o.syntax)
|
||||
_, code := o.syntax.openSyntax()
|
||||
return code
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ func TestCheckSyntax(t *testing.T) {
|
||||
},
|
||||
exit: -1,
|
||||
stderr: []string{
|
||||
"only one",
|
||||
"only one syntax",
|
||||
"-syntax",
|
||||
"-syntax-string",
|
||||
},
|
||||
@ -50,7 +50,7 @@ func TestCheckSyntax(t *testing.T) {
|
||||
},
|
||||
exit: -1,
|
||||
stderr: []string{
|
||||
"only one",
|
||||
"only one syntax",
|
||||
"-syntax",
|
||||
"-syntax-string",
|
||||
},
|
||||
@ -63,7 +63,7 @@ func TestCheckSyntax(t *testing.T) {
|
||||
},
|
||||
exit: -1,
|
||||
stderr: []string{
|
||||
"only one",
|
||||
"only one syntax",
|
||||
"-syntax",
|
||||
"-syntax-string",
|
||||
},
|
||||
@ -76,7 +76,7 @@ func TestCheckSyntax(t *testing.T) {
|
||||
},
|
||||
exit: -1,
|
||||
stderr: []string{
|
||||
"missing syntax input",
|
||||
"missing syntax",
|
||||
"-syntax",
|
||||
"-syntax-string",
|
||||
},
|
||||
@ -115,14 +115,14 @@ func TestCheckSyntax(t *testing.T) {
|
||||
mainTest{
|
||||
title: "syntax as file",
|
||||
args: []string{
|
||||
"treerack", "generate", "-syntax", "foo_test.treerack",
|
||||
"treerack", "check-syntax", "-syntax", "foo_test.treerack",
|
||||
},
|
||||
},
|
||||
|
||||
mainTest{
|
||||
title: "syntax as string",
|
||||
args: []string{
|
||||
"treerack", "generate", "-syntax-string", `foo = "bar"`,
|
||||
"treerack", "check-syntax", "-syntax-string", `foo = "bar"`,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
@ -12,27 +12,47 @@ help prints the current help
|
||||
See more details about a particular command by calling:
|
||||
treerack <command> -help`
|
||||
|
||||
const docRef = "See more documentation about the definition syntax and the parser output at https://github.com/aryszka/treerack."
|
||||
const docRef = `See more documentation about the definition syntax and the parser output at
|
||||
https://github.com/aryszka/treerack.`
|
||||
|
||||
const syntaxFileUsage = "path to the syntax file in treerack format"
|
||||
|
||||
const syntaxStringUsage = "inline syntax in treerack format"
|
||||
|
||||
const inputFileUsage = "path to the input to be parsed"
|
||||
|
||||
const inputStringUsage = "inline input string to be parsed"
|
||||
|
||||
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 it as a separate package`
|
||||
const exportUsage = `when the export flag is set, the generated code will have exported symbols to allow using
|
||||
it as a separate package.`
|
||||
|
||||
const parseUsage = `'treerack parse' 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. If it was successfully parsed, it prints the resulting abstract syntax tree (AST) in JSON format.`
|
||||
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.
|
||||
It returns non-zero exit code and prints the problem if the provided syntax is not valid or the intput cannot be
|
||||
parsed against it.`
|
||||
|
||||
const checkExample = `Example:
|
||||
treerack check -syntax example.treerack foo.example`
|
||||
|
||||
const parseUsage = `'treerack parse' 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.
|
||||
If it was successfully parsed, it prints the resulting abstract syntax tree (AST) in JSON format.`
|
||||
|
||||
const parseExample = `Example:
|
||||
treerack parse -syntax example.treerack foo.example`
|
||||
|
||||
const checkSyntaxUsage = `'treerack check-syntax' takes a syntax description from the standard input, or a file, or inline string, and validates it to check whether it represents a valid syntax. It returns with non-zero exit code and prints the problem if the syntax is not valid.`
|
||||
const checkSyntaxUsage = `'treerack check-syntax' takes a syntax description from the standard input, or a file,
|
||||
or inline string, and validates it to check whether it represents a valid syntax. It returns with non-zero exit
|
||||
code and prints the problem if the syntax is not valid.`
|
||||
|
||||
const checkSyntaxExample = `Example:
|
||||
treerack check-syntax example.treerack`
|
||||
|
||||
const generateUsage = `'treerack generate' takes a syntax description from the standard input, or a file, or inline string, and generates parser code implementing the described syntax. It prints the parser code to the standard output.`
|
||||
const generateUsage = `'treerack generate' takes a syntax description from the standard input, or a file, or
|
||||
inline string, and generates parser code implementing the described syntax. It prints the parser code to the
|
||||
standard output.`
|
||||
|
||||
const generateExample = `Example:
|
||||
treerack generate example.treerack > parser.go`
|
||||
|
@ -12,7 +12,7 @@ type generateOptions struct {
|
||||
func generate(args []string) int {
|
||||
var o generateOptions
|
||||
o.command = initOptions(generateUsage, generateExample, args)
|
||||
o.syntax = &fileOptions{flagSet: o.command.flagSet}
|
||||
o.syntax = &fileOptions{typ: "syntax", flagSet: o.command.flagSet}
|
||||
|
||||
o.command.flagSet.BoolVar(&o.export, "export", false, exportUsage)
|
||||
o.command.flagSet.StringVar(&o.packageName, "package-name", "", packageNameUsage)
|
||||
@ -28,7 +28,7 @@ func generate(args []string) int {
|
||||
}
|
||||
|
||||
o.syntax.positional = o.command.flagSet.Args()
|
||||
s, code := openSyntax(o.syntax)
|
||||
s, code := o.syntax.openSyntax()
|
||||
if code != 0 {
|
||||
return code
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ func TestGenerate(t *testing.T) {
|
||||
},
|
||||
exit: -1,
|
||||
stderr: []string{
|
||||
"only one",
|
||||
"only one syntax",
|
||||
"-export",
|
||||
"-package-name",
|
||||
"-syntax",
|
||||
@ -56,7 +56,7 @@ func TestGenerate(t *testing.T) {
|
||||
},
|
||||
exit: -1,
|
||||
stderr: []string{
|
||||
"only one",
|
||||
"only one syntax",
|
||||
"-export",
|
||||
"-package-name",
|
||||
"-syntax",
|
||||
@ -71,7 +71,7 @@ func TestGenerate(t *testing.T) {
|
||||
},
|
||||
exit: -1,
|
||||
stderr: []string{
|
||||
"only one",
|
||||
"only one syntax",
|
||||
"-export",
|
||||
"-package-name",
|
||||
"-syntax",
|
||||
@ -86,7 +86,7 @@ func TestGenerate(t *testing.T) {
|
||||
},
|
||||
exit: -1,
|
||||
stderr: []string{
|
||||
"missing syntax input",
|
||||
"missing syntax",
|
||||
"-export",
|
||||
"-package-name",
|
||||
"-syntax",
|
||||
|
@ -12,41 +12,42 @@ import (
|
||||
)
|
||||
|
||||
type fileOptions struct {
|
||||
typ string
|
||||
inline string
|
||||
fileName string
|
||||
positional []string
|
||||
flagSet *flag.FlagSet
|
||||
}
|
||||
|
||||
func multipleSyntaxesError(fs *flag.FlagSet) {
|
||||
stderr("only one of syntax file or syntax string is allowed")
|
||||
func (o *fileOptions) multipleInputsError() {
|
||||
stderr("only one", o.typ, "is allowed")
|
||||
stderr()
|
||||
stderr("Options:")
|
||||
fs.PrintDefaults()
|
||||
o.flagSet.PrintDefaults()
|
||||
}
|
||||
|
||||
func missingSyntaxError(fs *flag.FlagSet) {
|
||||
stderr("missing syntax input")
|
||||
func (o *fileOptions) missingInputError() {
|
||||
stderr("missing", o.typ)
|
||||
stderr()
|
||||
stderr("Options:")
|
||||
fs.PrintDefaults()
|
||||
o.flagSet.PrintDefaults()
|
||||
}
|
||||
|
||||
func getSource(options *fileOptions) (hasInput bool, fileName string, syntax string, code int) {
|
||||
if len(options.positional) > 1 {
|
||||
multipleSyntaxesError(options.flagSet)
|
||||
func (o *fileOptions) getSource() (hasInput bool, fileName string, inline string, code int) {
|
||||
if len(o.positional) > 1 {
|
||||
o.multipleInputsError()
|
||||
code = -1
|
||||
return
|
||||
}
|
||||
|
||||
hasPositional := len(options.positional) == 1
|
||||
hasFile := options.fileName != ""
|
||||
hasSyntax := options.inline != ""
|
||||
hasPositional := len(o.positional) == 1
|
||||
hasFile := o.fileName != ""
|
||||
hasInline := o.inline != ""
|
||||
|
||||
var has bool
|
||||
for _, h := range []bool{hasPositional, hasFile, hasSyntax} {
|
||||
for _, h := range []bool{hasPositional, hasFile, hasInline} {
|
||||
if h && has {
|
||||
multipleSyntaxesError(options.flagSet)
|
||||
o.multipleInputsError()
|
||||
code = -1
|
||||
return
|
||||
}
|
||||
@ -56,20 +57,20 @@ func getSource(options *fileOptions) (hasInput bool, fileName string, syntax str
|
||||
|
||||
switch {
|
||||
case hasPositional:
|
||||
fileName = options.positional[0]
|
||||
fileName = o.positional[0]
|
||||
return
|
||||
case hasFile:
|
||||
fileName = options.fileName
|
||||
fileName = o.fileName
|
||||
return
|
||||
case hasSyntax:
|
||||
syntax = options.inline
|
||||
case hasInline:
|
||||
inline = o.inline
|
||||
return
|
||||
}
|
||||
|
||||
// check input last to allow explicit syntax in non-TTY environments:
|
||||
// check input last to allow explicit input in non-TTY environments:
|
||||
hasInput = isTest && rin != nil || !isTest && !terminal.IsTerminal(0)
|
||||
if !hasInput {
|
||||
missingSyntaxError(options.flagSet)
|
||||
o.missingInputError()
|
||||
code = -1
|
||||
return
|
||||
}
|
||||
@ -77,8 +78,8 @@ func getSource(options *fileOptions) (hasInput bool, fileName string, syntax str
|
||||
return
|
||||
}
|
||||
|
||||
func open(options *fileOptions) (io.ReadCloser, int) {
|
||||
hasInput, fileName, syntax, code := getSource(options)
|
||||
func (o *fileOptions) open() (io.ReadCloser, int) {
|
||||
hasInput, fileName, inline, code := o.getSource()
|
||||
if code != 0 {
|
||||
return nil, code
|
||||
}
|
||||
@ -95,14 +96,14 @@ func open(options *fileOptions) (io.ReadCloser, int) {
|
||||
|
||||
r = f
|
||||
} else {
|
||||
r = ioutil.NopCloser(bytes.NewBufferString(syntax))
|
||||
r = ioutil.NopCloser(bytes.NewBufferString(inline))
|
||||
}
|
||||
|
||||
return r, 0
|
||||
}
|
||||
|
||||
func openSyntax(options *fileOptions) (*treerack.Syntax, int) {
|
||||
input, code := open(options)
|
||||
func (o *fileOptions) openSyntax() (*treerack.Syntax, int) {
|
||||
input, code := o.open()
|
||||
if code != 0 {
|
||||
return nil, code
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ test explicit input priority
|
||||
take the last
|
||||
test error report on invalid flag
|
||||
input name: may be just dropped because completely controlled by the client
|
||||
input name needed in command to differentiate between syntax and input in check and parse subcommands
|
||||
|
||||
[generator]
|
||||
allchars: can have char sequence
|
||||
|
1156
self/self.go
1156
self/self.go
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user