add check command

This commit is contained in:
Arpad Ryszka 2018-01-08 23:07:05 +01:00
parent 9f541e527a
commit ab3032e868
11 changed files with 885 additions and 625 deletions

View File

@ -0,0 +1 @@
bar

View File

@ -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
}

View File

@ -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",
},
},
)
}

View File

@ -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
}

View File

@ -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"`,
},
},
)

View File

@ -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`

View File

@ -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
}

View File

@ -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",

View File

@ -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
}

View File

@ -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

File diff suppressed because it is too large Load Diff