Skip to content
This repository was archived by the owner on Dec 12, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,44 @@ CRLF = CR LF / LF
7. Alternative

`lowest`


## Automation
It is possible to specify a configuration using a YAML file to automate the code generation. To install the command:

```abnf
go install github.com/elimity-com/abnf/cmd/abnf@latest
abnf generate
```

The generate command expects to find an `abnf.yml` file in its working directory. This YAML file defines and controls
the code generation. To specify a different YAML file:

```abnf
abnf -f ./path/to/foo.yml generate
```

Creation of an empty YAML file is done by invoking the `init` function.

### Generating an empty YAML file
```abnf
abnf init
```

The `abnf.yml` file is written out in the current working directory. To specify a different location to write out
the file, use the same `-f` flag:

```abnf
abnf -f ./path/to/foo.yml init
```

#### Code Generation Configuration Properties
- **version**: must be defined as "1"
- **spec**: is the path to the ABNF specification file, e.g. `./testdata/core.abnf`
- **gofile**: the name of the Go file to generated, e.g. `core_abnf.go`
- **package**: the name of the package for the Go file generated, e.g. `core`
- **output**: the output path where the package (folder) and Go file are written, e.g. `.`
- **generate**: the type of generation, {`operators`, `alternatives`}
- **verbose**: displays additional debugging information

The `generate` option either invokes `GenerateABNFAsOperators` or `GenerateABNFAsAlternatives`
10 changes: 10 additions & 0 deletions cmd/abnf/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package main

import (
"github.com/elimity-com/abnf/internal/cmd"
"os"
)

func main() {
os.Exit(cmd.Do(os.Args[1:], os.Stdin, os.Stdout, os.Stderr))
}
7 changes: 7 additions & 0 deletions core.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
version: "1"
spec: "./testdata/core.abnf"
gofile: "core_abnf.go"
package: "core"
output: "."
verbose: false
generate: "operators"
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ go 1.15

require (
github.com/di-wu/regen v1.0.0
github.com/spf13/cobra v1.5.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/text v0.3.3
gopkg.in/yaml.v2 v2.4.0 // indirect
)
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/di-wu/regen v1.0.0 h1:OsCF5dbrBS3vdStC4Bc6VVAnw4QN4wDJv1PigwBERhU=
github.com/di-wu/regen v1.0.0/go.mod h1:+KqJmSeFLlFKds2W13YGscFiDLGuvLRAYrhBOYf0+/w=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
103 changes: 103 additions & 0 deletions internal/cmd/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package cmd

import (
"context"
"fmt"
"github.com/elimity-com/abnf/internal/config"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"gopkg.in/yaml.v2"
"io"
"os"
"path/filepath"
)

const Version = "v0.1"

func Do(args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) int {
rootCmd := &cobra.Command{Use: "abnf", SilenceUsage: true}
rootCmd.PersistentFlags().StringP("file", "f", "", "specify an alternate config file (default: abnf.yml)")

rootCmd.AddCommand(genCmd)
rootCmd.AddCommand(initCmd)
rootCmd.AddCommand(versionCmd)

rootCmd.SetArgs(args)
rootCmd.SetIn(stdin)
rootCmd.SetOut(stdout)
rootCmd.SetErr(stderr)

ctx := context.Background()

if err := rootCmd.ExecuteContext(ctx); err == nil {
return 0
}
return 1
}

var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the abnf version number",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("%s\n", Version)
},
}

var initCmd = &cobra.Command{
Use: "init",
Short: "Create an empty abnf.yml settings file",
RunE: func(cmd *cobra.Command, args []string) error {
file := "abnf.yml"
if f := cmd.Flag("file"); f != nil && f.Changed {
file = f.Value.String()
if file == "" {
return fmt.Errorf("file argument is empty")
}
}
if _, err := os.Stat(file); !os.IsNotExist(err) {
return nil
}
blob, err := yaml.Marshal(config.Config{Version: "1", VerboseFlag: false})
if err != nil {
return err
}
return os.WriteFile(file, blob, 0644)
},
}

var genCmd = &cobra.Command{
Use: "generate",
Short: "Generate Go code from an ABNF specification file",
Run: func(cmd *cobra.Command, args []string) {
stderr := cmd.ErrOrStderr()
dir, name := getConfigPath(stderr, cmd.Flag("file"))

err := Generate(dir, name, stderr)
if err != nil {
os.Exit(1)
}
},
}

func getConfigPath(stderr io.Writer, f *pflag.Flag) (string, string) {
if f != nil && f.Changed {
file := f.Value.String()
if file == "" {
fmt.Fprintln(stderr, "error parsing config: file argument is empty")
os.Exit(1)
}
abspath, err := filepath.Abs(file)
if err != nil {
fmt.Fprintf(stderr, "error parsing config: absolute file path lookup failed: %s\n", err)
os.Exit(1)
}
return filepath.Dir(abspath), filepath.Base(abspath)
} else {
wd, err := os.Getwd()
if err != nil {
fmt.Fprintln(stderr, "error parsing abnf.json: file does not exist")
os.Exit(1)
}
return wd, ""
}
}
67 changes: 67 additions & 0 deletions internal/cmd/generate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package cmd

import (
"bytes"
"github.com/elimity-com/abnf"
"github.com/elimity-com/abnf/internal/config"
"io"
"log"
"os"
"path/filepath"
)

func Generate(dir, filename string, stderr io.Writer) error {
configPath, configuration, err := config.ReadConfig(stderr, dir, filename)
if err != nil {
return err
}

if configuration.VerboseFlag {
log.Printf("Config Path: %v, Spec: %v, Package: %v, Output: %v", configPath, configuration.SpecFile, configuration.PackageName, configuration.OutputPath)
}

rawABNF, err := os.ReadFile(configuration.SpecFile)
if err != nil {
log.Fatalf("Error %v", err)
}

g := abnf.CodeGenerator{
PackageName: configuration.PackageName,
RawABNF: rawABNF,
}
buf := new(bytes.Buffer)

if configuration.Generation == "alternatives" {
g.GenerateABNFAsAlternatives(buf)
} else {
g.GenerateABNFAsOperators(buf)
}

assembledOutputPath := filepath.Join(configuration.OutputPath, configuration.PackageName)

if configuration.VerboseFlag {
log.Printf("Assembled output path: %v", assembledOutputPath)
}

err = os.MkdirAll(assembledOutputPath, 0775)
if err != nil {
log.Fatalf("Unable to make assembled output path directory structure: %v", err)
}

assembledFilePath := filepath.Join(assembledOutputPath, configuration.GoFileName)
if configuration.VerboseFlag {
log.Printf("Assembled Go file path: %v", assembledFilePath)
}

err = os.WriteFile(assembledFilePath, buf.Bytes(), 0644)
if err != nil {
log.Fatalf("Error writing Go source code to file: %v", err)
}

if configuration.VerboseFlag {
log.Printf("%v ABNF specification successfully processed", configuration.SpecFile)
log.Printf("Output Directory: %v", configuration.OutputPath)
log.Printf("Go Code %v/%v", configuration.PackageName, configuration.GoFileName)
}
return nil
}
Loading