1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
|
package api
import (
"bytes"
"fmt"
"sort"
"strconv"
"strings"
"text/tabwriter"
"github.com/mmcloughlin/avo/internal/inst"
)
// Function represents a function that constructs some collection of
// instruction forms.
type Function struct {
Instruction inst.Instruction
Suffixes inst.Suffixes
inst.Forms
}
// Name returns the function name.
func (f *Function) Name() string {
return f.opcodesuffix("_")
}
// Opcode returns the full Go opcode of the instruction built by this function. Includes any suffixes.
func (f *Function) Opcode() string {
return f.opcodesuffix(".")
}
func (f *Function) opcodesuffix(sep string) string {
n := f.Instruction.Opcode
for _, suffix := range f.Suffixes {
n += sep
n += suffix.String()
}
return n
}
// HasSuffix reports whether the function has the provided suffix.
func (f *Function) HasSuffix(suffix inst.Suffix) bool {
for _, s := range f.Suffixes {
if s == suffix {
return true
}
}
return false
}
// Summary returns a summary of the instruction this function constructs.
func (f *Function) Summary() string {
summary := f.Instruction.Summary
if len(f.Suffixes) > 0 {
summary += " (" + strings.Join(f.Suffixes.Summaries(), ", ") + ")"
}
return summary
}
// Doc returns the function document comment as a list of lines.
func (f *Function) Doc() []string {
lines := []string{
fmt.Sprintf("%s: %s.", f.Name(), f.Summary()),
"",
"Forms:",
"",
}
// Write a table of instruction forms.
buf := bytes.NewBuffer(nil)
w := tabwriter.NewWriter(buf, 0, 0, 1, ' ', 0)
for _, form := range f.Forms {
row := f.Opcode() + "\t" + strings.Join(form.Signature(), "\t") + "\n"
fmt.Fprint(w, row)
}
w.Flush()
tbl := strings.TrimSpace(buf.String())
for _, line := range strings.Split(tbl, "\n") {
lines = append(lines, "\t"+line)
}
return lines
}
// Signature of the function. Derived from the instruction forms generated by this function.
func (f *Function) Signature() Signature {
// Handle the case of forms with multiple arities.
switch {
case f.IsVariadic():
return variadic{name: "ops"}
case f.IsNiladic():
return niladic{}
}
// Generate nice-looking variable names.
n := f.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 _, form := range f.Forms {
c := form.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)
}
// InstructionFunctions builds the list of all functions for a given
// instruction.
func InstructionFunctions(i inst.Instruction) []*Function {
// One function for each possible suffix combination.
bysuffix := map[string]*Function{}
for _, f := range i.Forms {
for _, suffixes := range f.SupportedSuffixes() {
k := suffixes.String()
if _, ok := bysuffix[k]; !ok {
bysuffix[k] = &Function{
Instruction: i,
Suffixes: suffixes,
}
}
bysuffix[k].Forms = append(bysuffix[k].Forms, f)
}
}
// Convert to a sorted slice.
var fns []*Function
for _, fn := range bysuffix {
fns = append(fns, fn)
}
SortFunctions(fns)
return fns
}
// InstructionsFunctions builds all functions for a list of instructions.
func InstructionsFunctions(is []inst.Instruction) []*Function {
var all []*Function
for _, i := range is {
fns := InstructionFunctions(i)
all = append(all, fns...)
}
SortFunctions(all)
return all
}
// SortFunctions sorts a list of functions by name.
func SortFunctions(fns []*Function) {
sort.Slice(fns, func(i, j int) bool {
return fns[i].Name() < fns[j].Name()
})
}
|