add generate command
This commit is contained in:
parent
db2022d46d
commit
9e25538b94
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
||||
*.out
|
||||
.coverprofile
|
||||
codecov
|
||||
cmd/treerack/treerack
|
||||
|
14
Makefile
14
Makefile
@ -13,6 +13,10 @@ imports: $(SOURCES)
|
||||
|
||||
build: $(SOURCES)
|
||||
go build
|
||||
go build -o cmd/treerack/treerack ./cmd/treerack
|
||||
|
||||
install: $(SOURCES)
|
||||
go install ./cmd/treerack
|
||||
|
||||
head: $(SOURCES)
|
||||
go run scripts/createhead.go -- \
|
||||
@ -26,15 +30,15 @@ head: $(SOURCES)
|
||||
syntaxhead.go \
|
||||
> head.go
|
||||
|
||||
generate: $(SOURCES) $(PARSERS) head
|
||||
go run scripts/boot.go < syntax.treerack > self/self.go.next
|
||||
generate: $(SOURCES) $(PARSERS) head install
|
||||
treerack generate -export -package-name self < syntax.treerack > self/self.go.next
|
||||
@mv self/self.go{.next,}
|
||||
@gofmt -s -w self/self.go
|
||||
|
||||
regenerate: $(SOURCES) $(PARSERS) head
|
||||
go run scripts/boot.go < syntax.treerack > self/self.go.next
|
||||
regenerate: $(SOURCES) $(PARSERS) head install
|
||||
treerack generate -export -package-name self < syntax.treerack > 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,}
|
||||
@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 {
|
||||
PackageName string
|
||||
Export bool
|
||||
}
|
||||
|
||||
// applied in a non-type-checked way
|
||||
@ -329,7 +330,12 @@ func (s *Syntax) Generate(o GeneratorOptions, w io.Writer) error {
|
||||
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()
|
||||
|
||||
done := make(map[string]bool)
|
||||
@ -344,7 +350,7 @@ func (s *Syntax) Generate(o GeneratorOptions, w io.Writer) error {
|
||||
|
||||
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()
|
||||
fprint(`}`)
|
||||
fprintln()
|
||||
@ -357,5 +363,5 @@ func (s *Syntax) Parse(r io.Reader) (*Node, error) {
|
||||
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))
|
||||
p.parse(c)
|
||||
if c.readErr != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user