add generate command
This commit is contained in:
parent
db2022d46d
commit
9e25538b94
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
|||||||
*.out
|
*.out
|
||||||
.coverprofile
|
.coverprofile
|
||||||
codecov
|
codecov
|
||||||
|
cmd/treerack/treerack
|
||||||
|
14
Makefile
14
Makefile
@ -13,6 +13,10 @@ imports: $(SOURCES)
|
|||||||
|
|
||||||
build: $(SOURCES)
|
build: $(SOURCES)
|
||||||
go build
|
go build
|
||||||
|
go build -o cmd/treerack/treerack ./cmd/treerack
|
||||||
|
|
||||||
|
install: $(SOURCES)
|
||||||
|
go install ./cmd/treerack
|
||||||
|
|
||||||
head: $(SOURCES)
|
head: $(SOURCES)
|
||||||
go run scripts/createhead.go -- \
|
go run scripts/createhead.go -- \
|
||||||
@ -26,15 +30,15 @@ head: $(SOURCES)
|
|||||||
syntaxhead.go \
|
syntaxhead.go \
|
||||||
> head.go
|
> head.go
|
||||||
|
|
||||||
generate: $(SOURCES) $(PARSERS) head
|
generate: $(SOURCES) $(PARSERS) head install
|
||||||
go run scripts/boot.go < syntax.treerack > self/self.go.next
|
treerack generate -export -package-name self < syntax.treerack > self/self.go.next
|
||||||
@mv self/self.go{.next,}
|
@mv self/self.go{.next,}
|
||||||
@gofmt -s -w self/self.go
|
@gofmt -s -w self/self.go
|
||||||
|
|
||||||
regenerate: $(SOURCES) $(PARSERS) head
|
regenerate: $(SOURCES) $(PARSERS) head install
|
||||||
go run scripts/boot.go < syntax.treerack > self/self.go.next
|
treerack generate -export -package-name self < syntax.treerack > self/self.go.next
|
||||||
@mv self/self.go{.next,}
|
@mv self/self.go{.next,}
|
||||||
go run scripts/boot.go < syntax.treerack > self/self.go.next
|
treerack generate -export -package-name self < syntax.treerack > self/self.go.next
|
||||||
@mv self/self.go{.next,}
|
@mv self/self.go{.next,}
|
||||||
@gofmt -s -w self/self.go
|
@gofmt -s -w self/self.go
|
||||||
|
|
||||||
|
25
cmd/treerack/doc.go
Normal file
25
cmd/treerack/doc.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
const summary = `treerack - parser generator - https://github.com/aryszka/treerack`
|
||||||
|
|
||||||
|
const commandsHelp = `Available commands:
|
||||||
|
generate generates a parser from a syntax definition
|
||||||
|
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 syntaxFileUsage = "path to the syntax file in treerack format"
|
||||||
|
|
||||||
|
const syntaxStringUsage = "inline syntax in treerack format"
|
||||||
|
|
||||||
|
const packageNameUsage = `package name of the generated 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 generateUsage = `treerack generate takes a syntax description from the standard input, or a file, or inline string, and generates code implementing a parser. It prints the parser code to the standard output.`
|
||||||
|
|
||||||
|
const generateExample = `Example:
|
||||||
|
treerack generate < syntax.treerack > parser.go`
|
125
cmd/treerack/generate.go
Normal file
125
cmd/treerack/generate.go
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"flag"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/aryszka/treerack"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type generateOptions struct {
|
||||||
|
syntax string
|
||||||
|
syntaxFile string
|
||||||
|
packageName string
|
||||||
|
export bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var isTest bool
|
||||||
|
|
||||||
|
func flagSet(o *generateOptions, output io.Writer) *flag.FlagSet {
|
||||||
|
fs := flag.NewFlagSet("", flag.ContinueOnError)
|
||||||
|
fs.Usage = func() {}
|
||||||
|
fs.SetOutput(output)
|
||||||
|
fs.StringVar(&o.syntax, "syntax-string", "", syntaxStringUsage)
|
||||||
|
fs.StringVar(&o.syntaxFile, "syntax", "", syntaxFileUsage)
|
||||||
|
fs.StringVar(&o.packageName, "package-name", "", packageNameUsage)
|
||||||
|
fs.BoolVar(&o.export, "export", false, exportUsage)
|
||||||
|
return fs
|
||||||
|
}
|
||||||
|
|
||||||
|
func helpGenerate() {
|
||||||
|
stdout(generateUsage)
|
||||||
|
stdout()
|
||||||
|
stdout("Options:")
|
||||||
|
fs := flagSet(&generateOptions{}, os.Stdout)
|
||||||
|
fs.PrintDefaults()
|
||||||
|
stdout()
|
||||||
|
stdout(generateExample)
|
||||||
|
}
|
||||||
|
|
||||||
|
func flagError(fs *flag.FlagSet) {
|
||||||
|
stderr()
|
||||||
|
stderr("Options:")
|
||||||
|
fs.PrintDefaults()
|
||||||
|
}
|
||||||
|
|
||||||
|
func multipleInputsError(fs *flag.FlagSet) {
|
||||||
|
stderr("only one of syntax file or syntax string is allowed")
|
||||||
|
stderr()
|
||||||
|
stderr("Options:")
|
||||||
|
fs.PrintDefaults()
|
||||||
|
}
|
||||||
|
|
||||||
|
func noInputError(fs *flag.FlagSet) {
|
||||||
|
stderr("missing syntax input")
|
||||||
|
stderr()
|
||||||
|
stderr("Options:")
|
||||||
|
fs.PrintDefaults()
|
||||||
|
}
|
||||||
|
|
||||||
|
func generate(args []string) int {
|
||||||
|
if len(args) > 0 && args[0] == "-help" {
|
||||||
|
helpGenerate()
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var options generateOptions
|
||||||
|
fs := flagSet(&options, os.Stderr)
|
||||||
|
if err := fs.Parse(args); err != nil {
|
||||||
|
flagError(fs)
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.syntaxFile != "" && options.syntax != "" {
|
||||||
|
multipleInputsError(fs)
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasInput bool
|
||||||
|
if options.syntaxFile == "" && options.syntax == "" {
|
||||||
|
fdint := int(os.Stdin.Fd())
|
||||||
|
hasInput = !isTest && !terminal.IsTerminal(fdint)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasInput && options.syntaxFile == "" && options.syntax == "" {
|
||||||
|
noInputError(fs)
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
var input io.Reader
|
||||||
|
if hasInput {
|
||||||
|
input = os.Stdin
|
||||||
|
} else if options.syntaxFile != "" {
|
||||||
|
f, err := os.Open(options.syntaxFile)
|
||||||
|
if err != nil {
|
||||||
|
stderr(err)
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
defer f.Close()
|
||||||
|
input = f
|
||||||
|
} else if options.syntax != "" {
|
||||||
|
input = bytes.NewBufferString(options.syntax)
|
||||||
|
}
|
||||||
|
|
||||||
|
s := &treerack.Syntax{}
|
||||||
|
if err := s.ReadSyntax(input); err != nil {
|
||||||
|
stderr(err)
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
var goptions treerack.GeneratorOptions
|
||||||
|
goptions.PackageName = options.packageName
|
||||||
|
goptions.Export = options.export
|
||||||
|
|
||||||
|
if err := s.Generate(goptions, os.Stdout); err != nil {
|
||||||
|
stderr(err)
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
37
cmd/treerack/main.go
Normal file
37
cmd/treerack/main.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func mainHelp() {
|
||||||
|
stdout(summary)
|
||||||
|
stdout()
|
||||||
|
stdout(commandsHelp)
|
||||||
|
stdout()
|
||||||
|
stdout(docRef)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if len(os.Args) == 1 {
|
||||||
|
stderr("missing command")
|
||||||
|
stderr()
|
||||||
|
stderr(commandsHelp)
|
||||||
|
stdout()
|
||||||
|
stdout(docRef)
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch os.Args[1] {
|
||||||
|
case "generate":
|
||||||
|
exit := generate(os.Args[2:])
|
||||||
|
os.Exit(exit)
|
||||||
|
case "help", "-help":
|
||||||
|
mainHelp()
|
||||||
|
default:
|
||||||
|
stderr("invalid command")
|
||||||
|
stderr()
|
||||||
|
stderr(commandsHelp)
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
}
|
14
cmd/treerack/out.go
Normal file
14
cmd/treerack/out.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func stderr(a ...interface{}) {
|
||||||
|
fmt.Fprintln(os.Stderr, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func stdout(a ...interface{}) {
|
||||||
|
fmt.Fprintln(os.Stderr, a...)
|
||||||
|
}
|
@ -1,20 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/aryszka/treerack"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
s := &treerack.Syntax{}
|
|
||||||
|
|
||||||
if err := s.ReadSyntax(os.Stdin); err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.Generate(treerack.GeneratorOptions{PackageName: "self"}, os.Stdout); err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
}
|
|
1154
self/self.go
1154
self/self.go
File diff suppressed because it is too large
Load Diff
12
syntax.go
12
syntax.go
@ -28,6 +28,7 @@ type Syntax struct {
|
|||||||
|
|
||||||
type GeneratorOptions struct {
|
type GeneratorOptions struct {
|
||||||
PackageName string
|
PackageName string
|
||||||
|
Export bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// applied in a non-type-checked way
|
// applied in a non-type-checked way
|
||||||
@ -329,7 +330,12 @@ func (s *Syntax) Generate(o GeneratorOptions, w io.Writer) error {
|
|||||||
fprintln()
|
fprintln()
|
||||||
fprintln()
|
fprintln()
|
||||||
|
|
||||||
fprint(`func Parse(r io.Reader) (*Node, error) {`)
|
if o.Export {
|
||||||
|
fprint(`func Parse(r io.Reader) (*Node, error) {`)
|
||||||
|
} else {
|
||||||
|
fprint(`func parse(r io.Reader) (*Node, error) {`)
|
||||||
|
}
|
||||||
|
|
||||||
fprintln()
|
fprintln()
|
||||||
|
|
||||||
done := make(map[string]bool)
|
done := make(map[string]bool)
|
||||||
@ -344,7 +350,7 @@ func (s *Syntax) Generate(o GeneratorOptions, w io.Writer) error {
|
|||||||
|
|
||||||
fprintln()
|
fprintln()
|
||||||
fprintln()
|
fprintln()
|
||||||
fprintf(`return parse(r, &p%d, &b%d)`, s.parser.nodeID(), s.builder.nodeID())
|
fprintf(`return parseInput(r, &p%d, &b%d)`, s.parser.nodeID(), s.builder.nodeID())
|
||||||
fprintln()
|
fprintln()
|
||||||
fprint(`}`)
|
fprint(`}`)
|
||||||
fprintln()
|
fprintln()
|
||||||
@ -357,5 +363,5 @@ func (s *Syntax) Parse(r io.Reader) (*Node, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return parse(r, s.parser, s.builder)
|
return parseInput(r, s.parser, s.builder)
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ func (pe *ParseError) Error() string {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parse(r io.Reader, p parser, b builder) (*Node, error) {
|
func parseInput(r io.Reader, p parser, b builder) (*Node, error) {
|
||||||
c := newContext(bufio.NewReader(r))
|
c := newContext(bufio.NewReader(r))
|
||||||
p.parse(c)
|
p.parse(c)
|
||||||
if c.readErr != nil {
|
if c.readErr != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user