first pass at a "builder" interface
This commit is contained in:
6
ast.go
6
ast.go
@@ -25,7 +25,11 @@ type Instruction struct {
|
|||||||
|
|
||||||
// File represents an assembly file.
|
// File represents an assembly file.
|
||||||
type File struct {
|
type File struct {
|
||||||
functions []*Function
|
Functions []*Function
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFile() *File {
|
||||||
|
return &File{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function represents an assembly function.
|
// Function represents an assembly function.
|
||||||
|
|||||||
65
build/context.go
Normal file
65
build/context.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package build
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/mmcloughlin/avo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Context struct {
|
||||||
|
file *avo.File
|
||||||
|
function *avo.Function
|
||||||
|
errs []error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewContext() *Context {
|
||||||
|
return &Context{
|
||||||
|
file: avo.NewFile(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:generate avogen -output zinstructions.go build
|
||||||
|
|
||||||
|
func (c *Context) TEXT(name string) {
|
||||||
|
c.function = avo.NewFunction(name)
|
||||||
|
c.file.Functions = append(c.file.Functions, c.function)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) Instruction(i avo.Instruction) {
|
||||||
|
if c.function == nil {
|
||||||
|
c.AddErrorMessage("no active function")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.function.AddInstruction(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) AddError(err error) {
|
||||||
|
c.errs = append(c.errs, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) AddErrorMessage(msg string) {
|
||||||
|
c.AddError(errors.New(msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) Result() (*avo.File, []error) {
|
||||||
|
return c.file, c.errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) Generate(wout, werr io.Writer) {
|
||||||
|
diag := log.New(werr, "", 0)
|
||||||
|
|
||||||
|
f, errs := c.Result()
|
||||||
|
if errs != nil {
|
||||||
|
for _, err := range errs {
|
||||||
|
diag.Println(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
p := avo.NewGoPrinter(wout)
|
||||||
|
if err := p.Print(f); err != nil {
|
||||||
|
diag.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
9
build/global.go
Normal file
9
build/global.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package build
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
// ctx provides a global build context.
|
||||||
|
var ctx = NewContext()
|
||||||
|
|
||||||
|
func TEXT(name string) { ctx.TEXT(name) }
|
||||||
|
func EOF() { ctx.Generate(os.Stdout, os.Stderr) }
|
||||||
9838
build/zinstructions.go
Normal file
9838
build/zinstructions.go
Normal file
File diff suppressed because it is too large
Load Diff
13
examples/add/asm.go
Normal file
13
examples/add/asm.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/mmcloughlin/avo/build"
|
||||||
|
"github.com/mmcloughlin/avo/reg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
TEXT("add")
|
||||||
|
ADDQ(reg.R8, reg.R11)
|
||||||
|
RET()
|
||||||
|
EOF()
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ var generators = map[string]gen.Builder{
|
|||||||
"godatatest": gen.NewGoDataTest,
|
"godatatest": gen.NewGoDataTest,
|
||||||
"ctors": gen.NewCtors,
|
"ctors": gen.NewCtors,
|
||||||
"ctorstest": gen.NewCtorsTest,
|
"ctorstest": gen.NewCtorsTest,
|
||||||
|
"build": gen.NewBuild,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command-line flags.
|
// Command-line flags.
|
||||||
|
|||||||
42
internal/gen/build.go
Normal file
42
internal/gen/build.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package gen
|
||||||
|
|
||||||
|
import "github.com/mmcloughlin/avo/internal/inst"
|
||||||
|
|
||||||
|
type build struct {
|
||||||
|
cfg Config
|
||||||
|
printer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBuild(cfg Config) Interface {
|
||||||
|
return GoFmt(&build{cfg: cfg})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *build) Generate(is []inst.Instruction) ([]byte, error) {
|
||||||
|
b.Printf("// %s\n\n", b.cfg.GeneratedWarning())
|
||||||
|
b.Printf("package build\n\n")
|
||||||
|
|
||||||
|
b.Printf("import (\n")
|
||||||
|
b.Printf("\t\"%s\"\n", pkg)
|
||||||
|
b.Printf("\t\"%s/x86\"\n", pkg)
|
||||||
|
b.Printf(")\n\n")
|
||||||
|
|
||||||
|
for _, i := range is {
|
||||||
|
b.instruction(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.Result()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *build) instruction(i inst.Instruction) {
|
||||||
|
s := params(i)
|
||||||
|
|
||||||
|
// Context method.
|
||||||
|
b.Printf("func (c *Context) %s(%s) {\n", i.Opcode, s.ParameterList())
|
||||||
|
b.Printf("if inst, err := x86.%s(%s); err == nil", i.Opcode, s.Arguments())
|
||||||
|
b.Printf(" { c.Instruction(*inst) }")
|
||||||
|
b.Printf(" else { c.AddError(err) }\n")
|
||||||
|
b.Printf("}\n")
|
||||||
|
|
||||||
|
// Global version.
|
||||||
|
b.Printf("func %s(%s) { ctx.%s(%s) }\n\n", i.Opcode, s.ParameterList(), i.Opcode, s.Arguments())
|
||||||
|
}
|
||||||
@@ -3,8 +3,6 @@ package gen
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
@@ -103,86 +101,6 @@ func (c *ctors) checkargs(i inst.Instruction, s signature) {
|
|||||||
c.Printf("}\n")
|
c.Printf("}\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// signature provides access to details about the signature of an instruction function.
|
|
||||||
type signature interface {
|
|
||||||
ParameterList() string
|
|
||||||
ParameterName(int) string
|
|
||||||
ParameterSlice() string
|
|
||||||
Length() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// argslist is the signature for a function with the given named parameters.
|
|
||||||
type argslist []string
|
|
||||||
|
|
||||||
func (a argslist) ParameterList() string { return strings.Join(a, ", ") + " avo.Operand" }
|
|
||||||
func (a argslist) ParameterName(i int) string { return a[i] }
|
|
||||||
func (a argslist) ParameterSlice() string {
|
|
||||||
return fmt.Sprintf("[]avo.Operand{%s}", strings.Join(a, ", "))
|
|
||||||
}
|
|
||||||
func (a argslist) Length() string { return strconv.Itoa(len(a)) }
|
|
||||||
|
|
||||||
// variadic is the signature for a variadic function.
|
|
||||||
type variadic struct {
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v variadic) ParameterList() string { return v.name + " ...avo.Operand" }
|
|
||||||
func (v variadic) ParameterName(i int) string { return fmt.Sprintf("%s[%d]", v.name, i) }
|
|
||||||
func (v variadic) ParameterSlice() string { return v.name }
|
|
||||||
func (v variadic) Length() string { return fmt.Sprintf("len(%s)", v.name) }
|
|
||||||
|
|
||||||
// niladic is the signature for a function with no arguments.
|
|
||||||
type niladic struct{}
|
|
||||||
|
|
||||||
func (n niladic) ParameterList() string { return "" }
|
|
||||||
func (n niladic) ParameterName(i int) string { panic("niladic function has no parameters") }
|
|
||||||
func (n niladic) ParameterSlice() string { return "nil" }
|
|
||||||
func (n niladic) Length() string { return "0" }
|
|
||||||
|
|
||||||
// params generates the function parameters and a function.
|
|
||||||
func params(i inst.Instruction) signature {
|
|
||||||
// Handle the case of forms with multiple arities.
|
|
||||||
switch {
|
|
||||||
case i.IsVariadic():
|
|
||||||
return variadic{name: "ops"}
|
|
||||||
case i.IsNiladic():
|
|
||||||
return niladic{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate nice-looking variable names.
|
|
||||||
n := i.Arity()
|
|
||||||
ops := make([]string, n)
|
|
||||||
count := map[string]int{}
|
|
||||||
for j := 0; j < n; j++ {
|
|
||||||
// Collect unique lowercase bytes from first characters of operand types.
|
|
||||||
s := map[byte]bool{}
|
|
||||||
for _, f := range i.Forms {
|
|
||||||
c := f.Operands[j].Type[0]
|
|
||||||
if 'a' <= c && c <= 'z' {
|
|
||||||
s[c] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Operand name is the sorted bytes.
|
|
||||||
var b []byte
|
|
||||||
for c := range s {
|
|
||||||
b = append(b, c)
|
|
||||||
}
|
|
||||||
sort.Slice(b, func(i, j int) bool { return b[i] < b[j] })
|
|
||||||
name := string(b)
|
|
||||||
|
|
||||||
// Append a counter if we've seen it already.
|
|
||||||
m := count[name]
|
|
||||||
count[name]++
|
|
||||||
if m > 0 {
|
|
||||||
name += strconv.Itoa(m)
|
|
||||||
}
|
|
||||||
ops[j] = name
|
|
||||||
}
|
|
||||||
|
|
||||||
return argslist(ops)
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkername returns the name of the function that checks an operand of type t.
|
// checkername returns the name of the function that checks an operand of type t.
|
||||||
func checkername(t string) string {
|
func checkername(t string) string {
|
||||||
return "operand.Is" + strings.Title(t)
|
return "operand.Is" + strings.Title(t)
|
||||||
|
|||||||
@@ -9,5 +9,6 @@ func TestBuilderInterfaces(t *testing.T) {
|
|||||||
NewGoDataTest,
|
NewGoDataTest,
|
||||||
NewCtors,
|
NewCtors,
|
||||||
NewCtorsTest,
|
NewCtorsTest,
|
||||||
|
NewBuild,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
94
internal/gen/signature.go
Normal file
94
internal/gen/signature.go
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
package gen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mmcloughlin/avo/internal/inst"
|
||||||
|
)
|
||||||
|
|
||||||
|
// signature provides access to details about the signature of an instruction function.
|
||||||
|
type signature interface {
|
||||||
|
ParameterList() string
|
||||||
|
Arguments() string
|
||||||
|
ParameterName(int) string
|
||||||
|
ParameterSlice() string
|
||||||
|
Length() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// argslist is the signature for a function with the given named parameters.
|
||||||
|
type argslist []string
|
||||||
|
|
||||||
|
func (a argslist) ParameterList() string { return strings.Join(a, ", ") + " avo.Operand" }
|
||||||
|
func (a argslist) Arguments() string { return strings.Join(a, ", ") }
|
||||||
|
func (a argslist) ParameterName(i int) string { return a[i] }
|
||||||
|
func (a argslist) ParameterSlice() string {
|
||||||
|
return fmt.Sprintf("[]avo.Operand{%s}", strings.Join(a, ", "))
|
||||||
|
}
|
||||||
|
func (a argslist) Length() string { return strconv.Itoa(len(a)) }
|
||||||
|
|
||||||
|
// variadic is the signature for a variadic function.
|
||||||
|
type variadic struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v variadic) ParameterList() string { return v.name + " ...avo.Operand" }
|
||||||
|
func (v variadic) Arguments() string { return v.name + "..." }
|
||||||
|
func (v variadic) ParameterName(i int) string { return fmt.Sprintf("%s[%d]", v.name, i) }
|
||||||
|
func (v variadic) ParameterSlice() string { return v.name }
|
||||||
|
func (v variadic) Length() string { return fmt.Sprintf("len(%s)", v.name) }
|
||||||
|
|
||||||
|
// niladic is the signature for a function with no arguments.
|
||||||
|
type niladic struct{}
|
||||||
|
|
||||||
|
func (n niladic) ParameterList() string { return "" }
|
||||||
|
func (n niladic) Arguments() string { return "" }
|
||||||
|
func (n niladic) ParameterName(i int) string { panic("niladic function has no parameters") }
|
||||||
|
func (n niladic) ParameterSlice() string { return "nil" }
|
||||||
|
func (n niladic) Length() string { return "0" }
|
||||||
|
|
||||||
|
// params generates the function parameters and a function.
|
||||||
|
func params(i inst.Instruction) signature {
|
||||||
|
// Handle the case of forms with multiple arities.
|
||||||
|
switch {
|
||||||
|
case i.IsVariadic():
|
||||||
|
return variadic{name: "ops"}
|
||||||
|
case i.IsNiladic():
|
||||||
|
return niladic{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate nice-looking variable names.
|
||||||
|
n := i.Arity()
|
||||||
|
ops := make([]string, n)
|
||||||
|
count := map[string]int{}
|
||||||
|
for j := 0; j < n; j++ {
|
||||||
|
// Collect unique lowercase bytes from first characters of operand types.
|
||||||
|
s := map[byte]bool{}
|
||||||
|
for _, f := range i.Forms {
|
||||||
|
c := f.Operands[j].Type[0]
|
||||||
|
if 'a' <= c && c <= 'z' {
|
||||||
|
s[c] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Operand name is the sorted bytes.
|
||||||
|
var b []byte
|
||||||
|
for c := range s {
|
||||||
|
b = append(b, c)
|
||||||
|
}
|
||||||
|
sort.Slice(b, func(i, j int) bool { return b[i] < b[j] })
|
||||||
|
name := string(b)
|
||||||
|
|
||||||
|
// Append a counter if we've seen it already.
|
||||||
|
m := count[name]
|
||||||
|
count[name]++
|
||||||
|
if m > 0 {
|
||||||
|
name += strconv.Itoa(m)
|
||||||
|
}
|
||||||
|
ops[j] = name
|
||||||
|
}
|
||||||
|
|
||||||
|
return argslist(ops)
|
||||||
|
}
|
||||||
@@ -34,7 +34,7 @@ func (p *GoPrinter) SetGeneratedBy(by string) {
|
|||||||
func (p *GoPrinter) Print(f *File) error {
|
func (p *GoPrinter) Print(f *File) error {
|
||||||
p.header()
|
p.header()
|
||||||
|
|
||||||
for _, fn := range f.functions {
|
for _, fn := range f.Functions {
|
||||||
p.function(fn)
|
p.function(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user