Skip to content

Commit 6a7d3de

Browse files
authored
cmd/addchain: templated output generation (#127)
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
1 parent 49956ae commit 6a7d3de

38 files changed

+2550
-32
lines changed

.golangci.yml

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ linters:
1515
- maligned
1616
- nlreturn
1717
- paralleltest
18+
- prealloc
1819
- predeclared
1920
- revive
2021
- testpackage

README.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ generators.
2121
* Generic optimization methods eliminate redundant operations
2222
* Simple domain-specific language for addition chain computations
2323
* Command-line interface or library
24+
* Code generation and templated output support
2425

2526
## Table of Contents
2627

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

108109

109-
See [full results listing](doc/results.md) for more detail and results for
110-
less common exponents.
110+
See [full results listing](doc/results.md) for more detail and
111+
results for less common exponents.
111112

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

166+
Next, you can [generate code from this addition chain](doc/gen.md).
167+
165168
### Library
166169

167170
Install:

acc/printer/printer.go

+9
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ import (
1111
"github.com/mmcloughlin/addchain/internal/print"
1212
)
1313

14+
// String prints the AST and returns resulting string.
15+
func String(n interface{}) (string, error) {
16+
b, err := Bytes(n)
17+
if err != nil {
18+
return "", err
19+
}
20+
return string(b), nil
21+
}
22+
1423
// Bytes prints the AST and returns resulting bytes.
1524
func Bytes(n interface{}) ([]byte, error) {
1625
var buf bytes.Buffer

cmd/addchain/gen.go

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"errors"
6+
"flag"
7+
"fmt"
8+
"io/ioutil"
9+
"strings"
10+
11+
"github.com/google/subcommands"
12+
13+
"github.com/mmcloughlin/addchain/acc/parse"
14+
"github.com/mmcloughlin/addchain/acc/pass"
15+
"github.com/mmcloughlin/addchain/internal/cli"
16+
"github.com/mmcloughlin/addchain/internal/gen"
17+
)
18+
19+
// generate subcommand.
20+
type generate struct {
21+
cli.Command
22+
23+
typ string
24+
tmpl string
25+
output string
26+
}
27+
28+
func (*generate) Name() string { return "gen" }
29+
func (*generate) Synopsis() string { return "generate output from an addition chain program" }
30+
func (*generate) Usage() string {
31+
return `Usage: gen [-type <name>] [-tmpl <file>] [-out <file>] [<filename>]
32+
33+
Generate output from an addition chain program.
34+
35+
`
36+
}
37+
38+
func (cmd *generate) SetFlags(f *flag.FlagSet) {
39+
defaulttype := "listing"
40+
if !gen.IsBuiltinTemplate(defaulttype) {
41+
panic("bad default template")
42+
}
43+
f.StringVar(&cmd.typ, "type", defaulttype, fmt.Sprintf("`name` of a builtin template (%s)", strings.Join(gen.BuiltinTemplateNames(), ",")))
44+
f.StringVar(&cmd.tmpl, "tmpl", "", "template `file` (overrides type)")
45+
f.StringVar(&cmd.output, "out", "", "output `file` (default stdout)")
46+
}
47+
48+
func (cmd *generate) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) (status subcommands.ExitStatus) {
49+
// Read input.
50+
input, r, err := cli.OpenInput(f.Arg(0))
51+
if err != nil {
52+
return cmd.Error(err)
53+
}
54+
defer cmd.CheckClose(&status, r)
55+
56+
// Parse to a syntax tree.
57+
s, err := parse.Reader(input, r)
58+
if err != nil {
59+
return cmd.Error(err)
60+
}
61+
62+
// Prepare template data.
63+
cfg := gen.Config{
64+
Allocator: pass.Allocator{
65+
Input: "x",
66+
Output: "z",
67+
Format: "t%d",
68+
},
69+
}
70+
71+
data, err := gen.PrepareData(cfg, s)
72+
if err != nil {
73+
return cmd.Error(err)
74+
}
75+
76+
// Load template.
77+
tmpl, err := cmd.LoadTemplate()
78+
if err != nil {
79+
return cmd.Error(err)
80+
}
81+
82+
// Open output.
83+
_, w, err := cli.OpenOutput(cmd.output)
84+
if err != nil {
85+
return cmd.Error(err)
86+
}
87+
defer cmd.CheckClose(&status, w)
88+
89+
// Generate.
90+
if err := gen.Generate(w, tmpl, data); err != nil {
91+
return cmd.Error(err)
92+
}
93+
94+
return subcommands.ExitSuccess
95+
}
96+
97+
func (cmd *generate) LoadTemplate() (string, error) {
98+
// Explicit filename has precedence.
99+
if cmd.tmpl != "" {
100+
b, err := ioutil.ReadFile(cmd.tmpl)
101+
if err != nil {
102+
return "", err
103+
}
104+
return string(b), nil
105+
}
106+
107+
// Lookup type name in builtin templates.
108+
if cmd.typ == "" {
109+
return "", errors.New("no builtin template specified")
110+
}
111+
112+
s, err := gen.BuiltinTemplate(cmd.typ)
113+
if err != nil {
114+
return "", err
115+
}
116+
117+
return s, nil
118+
}

cmd/addchain/main.go

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ func main() {
1818
subcommands.Register(&search{Command: base}, "")
1919
subcommands.Register(&eval{Command: base}, "")
2020
subcommands.Register(&format{Command: base}, "")
21+
subcommands.Register(&generate{Command: base}, "")
2122

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

0 commit comments

Comments
 (0)