Files
avo/build/context.go

190 lines
4.8 KiB
Go
Raw Normal View History

2018-11-30 20:43:31 -08:00
package build
import (
"errors"
2018-12-17 21:20:21 -08:00
"go/types"
2018-11-30 20:43:31 -08:00
"github.com/mmcloughlin/avo"
"github.com/mmcloughlin/avo/buildtags"
"github.com/mmcloughlin/avo/gotypes"
2018-12-21 00:30:59 -08:00
"github.com/mmcloughlin/avo/operand"
2018-12-03 20:40:43 -08:00
"github.com/mmcloughlin/avo/reg"
"golang.org/x/tools/go/packages"
2018-11-30 20:43:31 -08:00
)
2019-01-05 00:11:13 -08:00
// Context maintains state for incrementally building an avo File.
2018-11-30 20:43:31 -08:00
type Context struct {
pkg *packages.Package
2018-11-30 20:43:31 -08:00
file *avo.File
function *avo.Function
2018-12-27 11:57:46 -08:00
global *avo.Global
2018-11-30 20:43:31 -08:00
errs []error
2018-12-03 20:40:43 -08:00
reg.Collection
2018-11-30 20:43:31 -08:00
}
2019-01-05 00:11:13 -08:00
// NewContext initializes an empty build Context.
2018-11-30 20:43:31 -08:00
func NewContext() *Context {
return &Context{
2018-12-03 20:40:43 -08:00
file: avo.NewFile(),
Collection: *reg.NewCollection(),
2018-11-30 20:43:31 -08:00
}
}
2019-01-05 00:11:13 -08:00
// Package sets the package the generated file will belong to. Required to be able to reference types in the package.
func (c *Context) Package(path string) {
cfg := &packages.Config{
Mode: packages.LoadTypes,
}
pkgs, err := packages.Load(cfg, path)
if err != nil {
2019-01-05 16:49:51 -08:00
c.adderror(err)
return
}
pkg := pkgs[0]
if len(pkg.Errors) > 0 {
for _, err := range pkg.Errors {
2019-01-05 16:49:51 -08:00
c.adderror(err)
}
return
}
c.pkg = pkg
}
2019-01-05 00:11:13 -08:00
// Constraints sets build constraints for the file.
func (c *Context) Constraints(t buildtags.ConstraintsConvertable) {
cs := t.ToConstraints()
if err := cs.Validate(); err != nil {
2019-01-05 16:49:51 -08:00
c.adderror(err)
return
}
c.file.Constraints = cs
}
2019-01-05 00:11:13 -08:00
// Constraint appends a constraint to the file's build constraints.
func (c *Context) Constraint(t buildtags.ConstraintConvertable) {
c.Constraints(append(c.file.Constraints, t.ToConstraint()))
}
2019-01-05 16:49:51 -08:00
// ConstraintExpr appends a constraint to the file's build constraints. The
2019-01-05 00:11:13 -08:00
// constraint to add is parsed from the given expression. The expression should
// look the same as the content following "// +build " in regular build
// constraint comments.
func (c *Context) ConstraintExpr(expr string) {
constraint, err := buildtags.ParseConstraint(expr)
if err != nil {
2019-01-05 16:49:51 -08:00
c.adderror(err)
return
}
c.Constraint(constraint)
}
2019-01-05 00:11:13 -08:00
// Function starts building a new function with the given name.
2018-11-30 20:58:51 -08:00
func (c *Context) Function(name string) {
2018-11-30 20:43:31 -08:00
c.function = avo.NewFunction(name)
2018-12-27 11:57:46 -08:00
c.file.AddSection(c.function)
2018-11-30 20:43:31 -08:00
}
2019-01-05 00:11:13 -08:00
// Doc sets documentation comment lines for the currently active function.
2018-12-27 23:01:27 -08:00
func (c *Context) Doc(lines ...string) {
c.activefunc().Doc = lines
}
2019-01-05 00:11:13 -08:00
// Attributes sets function attributes for the currently active function.
func (c *Context) Attributes(a avo.Attribute) {
c.activefunc().Attributes = a
}
2019-01-05 00:11:13 -08:00
// Signature sets the signature for the currently active function.
func (c *Context) Signature(s *gotypes.Signature) {
c.activefunc().SetSignature(s)
}
2019-01-05 00:11:13 -08:00
// SignatureExpr parses the signature expression and sets it as the active function's signature.
func (c *Context) SignatureExpr(expr string) {
2018-12-17 21:20:21 -08:00
s, err := gotypes.ParseSignatureInPackage(c.types(), expr)
if err != nil {
2019-01-05 16:49:51 -08:00
c.adderror(err)
return
}
c.Signature(s)
}
2018-12-17 21:20:21 -08:00
func (c *Context) types() *types.Package {
if c.pkg == nil {
return nil
}
return c.pkg.Types
}
2019-01-05 00:11:13 -08:00
// AllocLocal allocates size bytes in the stack of the currently active function.
// Returns a reference to the base pointer for the newly allocated region.
2018-12-21 00:30:59 -08:00
func (c *Context) AllocLocal(size int) operand.Mem {
return c.activefunc().AllocLocal(size)
}
2019-01-05 00:11:13 -08:00
// Instruction adds an instruction to the active function.
2018-12-02 23:59:29 -08:00
func (c *Context) Instruction(i *avo.Instruction) {
c.activefunc().AddNode(i)
2018-11-30 21:37:17 -08:00
}
2019-01-05 00:11:13 -08:00
// Label adds a label to the active function.
2018-11-30 21:37:17 -08:00
func (c *Context) Label(l avo.Label) {
c.activefunc().AddLabel(l)
2018-11-30 21:37:17 -08:00
}
func (c *Context) activefunc() *avo.Function {
2018-11-30 20:43:31 -08:00
if c.function == nil {
2019-01-05 16:49:51 -08:00
c.adderrormessage("no active function")
return avo.NewFunction("")
2018-11-30 20:43:31 -08:00
}
return c.function
2018-11-30 20:43:31 -08:00
}
2018-11-30 20:58:51 -08:00
//go:generate avogen -output zinstructions.go build
2019-01-05 16:49:51 -08:00
// StaticGlobal adds a new static data section to the file and returns a pointer to it.
2018-12-27 11:57:46 -08:00
func (c *Context) StaticGlobal(name string) operand.Mem {
c.global = avo.NewStaticGlobal(name)
c.file.AddSection(c.global)
return c.global.Base()
}
2019-01-05 16:49:51 -08:00
// DataAttributes sets the attributes on the current active global data section.
func (c *Context) DataAttributes(a avo.Attribute) {
c.activeglobal().Attributes = a
}
2019-01-05 16:49:51 -08:00
// AddDatum adds constant v at offset to the current active global data section.
2018-12-27 11:57:46 -08:00
func (c *Context) AddDatum(offset int, v operand.Constant) {
if err := c.activeglobal().AddDatum(avo.NewDatum(offset, v)); err != nil {
2019-01-05 16:49:51 -08:00
c.adderror(err)
2018-12-27 11:57:46 -08:00
}
}
2019-01-05 16:49:51 -08:00
// AppendDatum appends a constant to the current active global data section.
2018-12-27 11:57:46 -08:00
func (c *Context) AppendDatum(v operand.Constant) {
c.activeglobal().Append(v)
}
func (c *Context) activeglobal() *avo.Global {
if c.global == nil {
2019-01-05 16:49:51 -08:00
c.adderrormessage("no active global")
2018-12-27 11:57:46 -08:00
return avo.NewStaticGlobal("")
}
return c.global
}
2019-01-05 16:49:51 -08:00
func (c *Context) adderror(err error) {
e := exterr(err)
c.errs = append(c.errs, e)
2018-11-30 20:43:31 -08:00
}
2019-01-05 16:49:51 -08:00
func (c *Context) adderrormessage(msg string) {
c.adderror(errors.New(msg))
2018-11-30 20:43:31 -08:00
}
2019-01-05 16:49:51 -08:00
// Result returns the built file and any accumulated errors.
2018-11-30 20:43:31 -08:00
func (c *Context) Result() (*avo.File, []error) {
return c.file, c.errs
}