Skip to content

Commit

Permalink
cmd/addchain: templated output generation (#127)
Browse files Browse the repository at this point in the history
Provides support for templated output based on addition chain programs.
The new `addchain gen` subcommand allows for templated output, including some
built-in templates.

The new feature has full documentation, auto-generated as usual to keep in
sync:

* Full working example of fp25519 inversion
* Template input data and functions
* Example output for all builtin templates

Fixes #94
  • Loading branch information
mmcloughlin committed Oct 27, 2021
1 parent 49956ae commit 6a7d3de
Show file tree
Hide file tree
Showing 38 changed files with 2,550 additions and 32 deletions.
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ linters:
- maligned
- nlreturn
- paralleltest
- prealloc
- predeclared
- revive
- testpackage
Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ generators.
* Generic optimization methods eliminate redundant operations
* Simple domain-specific language for addition chain computations
* Command-line interface or library
* Code generation and templated output support

## Table of Contents

Expand Down Expand Up @@ -106,8 +107,8 @@ delta from the library result.
| [secp256k1 (Bitcoin) Scalar Inversion](doc/results.md#secp256k1-bitcoin-scalar-inversion) | 293 | 290 | +3 |


See [full results listing](doc/results.md) for more detail and results for
less common exponents.
See [full results listing](doc/results.md) for more detail and
results for less common exponents.

These results demonstrate that `addchain` is competitive with hand-optimized
chains, often with equivalent or better performance. Even when `addchain` is
Expand Down Expand Up @@ -162,6 +163,8 @@ x250 = x240 << 10 + x10
return (x250 << 2 + 1) << 3 + _11
```

Next, you can [generate code from this addition chain](doc/gen.md).

### Library

Install:
Expand Down
9 changes: 9 additions & 0 deletions acc/printer/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ import (
"github.com/mmcloughlin/addchain/internal/print"
)

// String prints the AST and returns resulting string.
func String(n interface{}) (string, error) {
b, err := Bytes(n)
if err != nil {
return "", err
}
return string(b), nil
}

// Bytes prints the AST and returns resulting bytes.
func Bytes(n interface{}) ([]byte, error) {
var buf bytes.Buffer
Expand Down
118 changes: 118 additions & 0 deletions cmd/addchain/gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package main

import (
"context"
"errors"
"flag"
"fmt"
"io/ioutil"
"strings"

"github.com/google/subcommands"

"github.com/mmcloughlin/addchain/acc/parse"
"github.com/mmcloughlin/addchain/acc/pass"
"github.com/mmcloughlin/addchain/internal/cli"
"github.com/mmcloughlin/addchain/internal/gen"
)

// generate subcommand.
type generate struct {
cli.Command

typ string
tmpl string
output string
}

func (*generate) Name() string { return "gen" }
func (*generate) Synopsis() string { return "generate output from an addition chain program" }
func (*generate) Usage() string {
return `Usage: gen [-type <name>] [-tmpl <file>] [-out <file>] [<filename>]
Generate output from an addition chain program.
`
}

func (cmd *generate) SetFlags(f *flag.FlagSet) {
defaulttype := "listing"
if !gen.IsBuiltinTemplate(defaulttype) {
panic("bad default template")
}
f.StringVar(&cmd.typ, "type", defaulttype, fmt.Sprintf("`name` of a builtin template (%s)", strings.Join(gen.BuiltinTemplateNames(), ",")))
f.StringVar(&cmd.tmpl, "tmpl", "", "template `file` (overrides type)")
f.StringVar(&cmd.output, "out", "", "output `file` (default stdout)")
}

func (cmd *generate) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) (status subcommands.ExitStatus) {
// Read input.
input, r, err := cli.OpenInput(f.Arg(0))
if err != nil {
return cmd.Error(err)
}
defer cmd.CheckClose(&status, r)

// Parse to a syntax tree.
s, err := parse.Reader(input, r)
if err != nil {
return cmd.Error(err)
}

// Prepare template data.
cfg := gen.Config{
Allocator: pass.Allocator{
Input: "x",
Output: "z",
Format: "t%d",
},
}

data, err := gen.PrepareData(cfg, s)
if err != nil {
return cmd.Error(err)
}

// Load template.
tmpl, err := cmd.LoadTemplate()
if err != nil {
return cmd.Error(err)
}

// Open output.
_, w, err := cli.OpenOutput(cmd.output)
if err != nil {
return cmd.Error(err)
}
defer cmd.CheckClose(&status, w)

// Generate.
if err := gen.Generate(w, tmpl, data); err != nil {
return cmd.Error(err)
}

return subcommands.ExitSuccess
}

func (cmd *generate) LoadTemplate() (string, error) {
// Explicit filename has precedence.
if cmd.tmpl != "" {
b, err := ioutil.ReadFile(cmd.tmpl)
if err != nil {
return "", err
}
return string(b), nil
}

// Lookup type name in builtin templates.
if cmd.typ == "" {
return "", errors.New("no builtin template specified")
}

s, err := gen.BuiltinTemplate(cmd.typ)
if err != nil {
return "", err
}

return s, nil
}
1 change: 1 addition & 0 deletions cmd/addchain/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func main() {
subcommands.Register(&search{Command: base}, "")
subcommands.Register(&eval{Command: base}, "")
subcommands.Register(&format{Command: base}, "")
subcommands.Register(&generate{Command: base}, "")

if meta.Meta.BuildVersion != "" {
subcommands.Register(&version{version: meta.Meta.BuildVersion, Command: base}, "")
Expand Down
Loading

0 comments on commit 6a7d3de

Please sign in to comment.