482 lines
13 KiB
Go
482 lines
13 KiB
Go
package manifest
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"aego/format/aetext"
|
|
"aego/script/abi"
|
|
)
|
|
|
|
type extra struct {
|
|
params []string
|
|
example string
|
|
doc string
|
|
loc string
|
|
}
|
|
|
|
func Encode(m *Manifest) []byte {
|
|
root := &aetext.Node{Head: "manifest"}
|
|
root.Attrs = append(root.Attrs, aetext.Field{Name: "version", Value: aetext.Int(int64(m.Version))})
|
|
root.Fields = append(root.Fields, aetext.Field{Name: "abi", Value: aetext.String(hex.EncodeToString(m.ABIHash[:]))})
|
|
for _, mod := range m.Modules {
|
|
n := &aetext.Node{Head: "module", Label: mod.Path}
|
|
for _, f := range mod.Funcs {
|
|
n.Blocks = append(n.Blocks, callable("func", f.Name, f.Sig, extra{f.Params, f.Example, f.Doc, f.Loc}))
|
|
}
|
|
for _, c := range mod.Consts {
|
|
n.Blocks = append(n.Blocks, constant(c))
|
|
}
|
|
root.Blocks = append(root.Blocks, n)
|
|
}
|
|
for _, e := range m.Enums {
|
|
n := &aetext.Node{Head: "enum", Label: e.Name}
|
|
n.Attrs = append(n.Attrs, aetext.Field{Name: "module", Value: aetext.String(e.Module)})
|
|
text(n, "doc", e.Doc)
|
|
for _, mem := range e.Members {
|
|
b := &aetext.Node{Head: "member", Label: mem.Name}
|
|
b.Attrs = append(b.Attrs, aetext.Field{Name: "value", Value: aetext.Int(mem.Value)})
|
|
n.Blocks = append(n.Blocks, b)
|
|
}
|
|
root.Blocks = append(root.Blocks, n)
|
|
}
|
|
for _, s := range m.Structs {
|
|
n := &aetext.Node{Head: "struct", Label: s.Name}
|
|
n.Attrs = append(n.Attrs,
|
|
aetext.Field{Name: "module", Value: aetext.String(s.Module)},
|
|
aetext.Field{Name: "slots", Value: aetext.Int(int64(s.Slots))},
|
|
)
|
|
text(n, "doc", s.Doc)
|
|
for _, f := range s.Fields {
|
|
b := &aetext.Node{Head: "field", Label: f.Name}
|
|
b.Fields = append(b.Fields, aetext.Field{Name: "type", Value: aetext.String(f.Type.String())})
|
|
n.Blocks = append(n.Blocks, b)
|
|
}
|
|
root.Blocks = append(root.Blocks, n)
|
|
}
|
|
for _, h := range m.Handles {
|
|
n := &aetext.Node{Head: "handle", Label: h.Name}
|
|
n.Attrs = append(n.Attrs, aetext.Field{Name: "await", Value: aetext.Bool(h.Await)})
|
|
text(n, "doc", h.Doc)
|
|
root.Blocks = append(root.Blocks, n)
|
|
}
|
|
for _, e := range m.Events {
|
|
params := make([]string, len(e.Params))
|
|
for i, p := range e.Params {
|
|
params[i] = p.String()
|
|
}
|
|
n := &aetext.Node{Head: "event", Label: e.Name}
|
|
n.Fields = append(n.Fields, aetext.Field{Name: "params", Value: strList(params)})
|
|
if len(e.Names) > 0 {
|
|
n.Fields = append(n.Fields, aetext.Field{Name: "names", Value: strList(e.Names)})
|
|
}
|
|
text(n, "doc", e.Doc)
|
|
root.Blocks = append(root.Blocks, n)
|
|
}
|
|
for _, me := range m.Methods {
|
|
n := callable("method", me.Name, me.Sig, extra{me.Params, me.Example, me.Doc, me.Loc})
|
|
n.Attrs = append(n.Attrs, aetext.Field{Name: "recv", Value: aetext.String(me.Recv.String())})
|
|
root.Blocks = append(root.Blocks, n)
|
|
}
|
|
for _, cm := range m.CompMethods {
|
|
n := callable("cmethod", cm.Name, cm.Sig, extra{cm.Params, cm.Example, cm.Doc, cm.Loc})
|
|
n.Attrs = append(n.Attrs, aetext.Field{Name: "comp", Value: aetext.String(cm.Comp)})
|
|
root.Blocks = append(root.Blocks, n)
|
|
}
|
|
for _, hm := range m.HandleMethods {
|
|
n := callable("hmethod", hm.Name, hm.Sig, extra{hm.Params, hm.Example, hm.Doc, hm.Loc})
|
|
n.Attrs = append(n.Attrs, aetext.Field{Name: "handle", Value: aetext.String(hm.Handle)})
|
|
root.Blocks = append(root.Blocks, n)
|
|
}
|
|
m.encodeSignals(root)
|
|
for _, c := range m.Components {
|
|
n := &aetext.Node{Head: "component", Label: c.Name}
|
|
text(n, "short", c.Short)
|
|
text(n, "category", c.Category)
|
|
text(n, "doc", c.Doc)
|
|
for _, f := range c.Fields {
|
|
b := &aetext.Node{Head: "field", Label: f.Name}
|
|
b.Fields = append(b.Fields,
|
|
aetext.Field{Name: "type", Value: aetext.String(f.Type.String())},
|
|
aetext.Field{Name: "readonly", Value: aetext.Bool(f.ReadOnly)},
|
|
)
|
|
text(b, "doc", f.Doc)
|
|
n.Blocks = append(n.Blocks, b)
|
|
}
|
|
root.Blocks = append(root.Blocks, n)
|
|
}
|
|
return aetext.Write(root)
|
|
}
|
|
|
|
func text(n *aetext.Node, name, v string) {
|
|
if v != "" {
|
|
n.Fields = append(n.Fields, aetext.Field{Name: name, Value: aetext.String(v)})
|
|
}
|
|
}
|
|
|
|
func strList(list []string) aetext.Value {
|
|
out := make([]aetext.Value, len(list))
|
|
for i, s := range list {
|
|
out[i] = aetext.String(s)
|
|
}
|
|
return aetext.List(out...)
|
|
}
|
|
|
|
func callable(head, name string, s abi.Signature, x extra) *aetext.Node {
|
|
n := &aetext.Node{Head: head, Label: name}
|
|
params := make([]string, len(s.Params))
|
|
for i, p := range s.Params {
|
|
params[i] = p.String()
|
|
}
|
|
n.Fields = append(n.Fields,
|
|
aetext.Field{Name: "params", Value: strList(params)},
|
|
aetext.Field{Name: "result", Value: aetext.String(s.Result.String())},
|
|
)
|
|
if len(x.params) > 0 {
|
|
n.Fields = append(n.Fields, aetext.Field{Name: "names", Value: strList(x.params)})
|
|
}
|
|
text(n, "doc", x.doc)
|
|
text(n, "example", x.example)
|
|
text(n, "loc", x.loc)
|
|
return n
|
|
}
|
|
|
|
func constant(c Const) *aetext.Node {
|
|
n := &aetext.Node{Head: "const", Label: c.Name}
|
|
n.Fields = append(n.Fields,
|
|
aetext.Field{Name: "type", Value: aetext.String(c.Val.Type.String())},
|
|
aetext.Field{Name: "int", Value: aetext.Int(c.Val.Int)},
|
|
aetext.Field{Name: "float", Value: aetext.Float64(c.Val.Float)},
|
|
aetext.Field{Name: "bool", Value: aetext.Bool(c.Val.Bool)},
|
|
aetext.Field{Name: "str", Value: aetext.String(c.Val.Str)},
|
|
)
|
|
text(n, "doc", c.Doc)
|
|
return n
|
|
}
|
|
|
|
func str(n *aetext.Node, name string) string {
|
|
if v, ok := n.Field(name); ok {
|
|
return v.Str
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func list(n *aetext.Node, name string) []string {
|
|
v, ok := n.Field(name)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
out := make([]string, len(v.List))
|
|
for i, s := range v.List {
|
|
out[i] = s.Str
|
|
}
|
|
return out
|
|
}
|
|
|
|
func typeRef(s string) (abi.TypeRef, error) {
|
|
t, ok := abi.Parse(s)
|
|
if !ok {
|
|
return abi.TypeRef{}, fmt.Errorf("manifest: malformed type %q", s)
|
|
}
|
|
return t, nil
|
|
}
|
|
|
|
func readSig(n *aetext.Node) (abi.Signature, error) {
|
|
var s abi.Signature
|
|
for _, p := range list(n, "params") {
|
|
t, err := typeRef(p)
|
|
if err != nil {
|
|
return s, err
|
|
}
|
|
s.Params = append(s.Params, t)
|
|
}
|
|
res := str(n, "result")
|
|
if res == "" {
|
|
res = "void"
|
|
}
|
|
t, err := typeRef(res)
|
|
s.Result = t
|
|
return s, err
|
|
}
|
|
|
|
func Decode(b []byte) (*Manifest, error) {
|
|
doc, err := aetext.Parse(b, "manifest.aetext")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if doc.Head != "manifest" {
|
|
return nil, errors.New("manifest: not a bindings manifest")
|
|
}
|
|
m := &Manifest{Version: Version}
|
|
if v, ok := doc.Attr("version"); ok {
|
|
m.Version = uint32(v.AsInt())
|
|
}
|
|
if m.Version == 0 || m.Version > Version {
|
|
return nil, fmt.Errorf("manifest: version %d is not supported", m.Version)
|
|
}
|
|
if v, ok := doc.Field("abi"); ok {
|
|
h, err := hex.DecodeString(v.Str)
|
|
if err != nil || len(h) != len(m.ABIHash) {
|
|
return nil, errors.New("manifest: malformed abi hash")
|
|
}
|
|
copy(m.ABIHash[:], h)
|
|
}
|
|
for _, n := range doc.Blocks {
|
|
var err error
|
|
switch n.Head {
|
|
case "module":
|
|
err = m.readModule(n)
|
|
case "enum":
|
|
m.readEnum(n)
|
|
case "struct":
|
|
err = m.readStruct(n)
|
|
case "handle":
|
|
m.readHandle(n)
|
|
case "event":
|
|
err = m.readEvent(n)
|
|
case "method":
|
|
err = m.readMethod(n)
|
|
case "cmethod":
|
|
err = m.readCompMethod(n)
|
|
case "hmethod":
|
|
err = m.readHandleMethod(n)
|
|
case "csignal", "hsignal":
|
|
err = m.readSignal(n)
|
|
case "component":
|
|
err = m.readComponent(n)
|
|
default:
|
|
err = fmt.Errorf("manifest: unknown block %s", n.Head)
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if _, err := m.Table(); err != nil {
|
|
return nil, err
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
func (m *Manifest) readModule(n *aetext.Node) error {
|
|
mod := Module{Path: n.Label}
|
|
for _, b := range n.Blocks {
|
|
switch b.Head {
|
|
case "func":
|
|
sig, err := readSig(b)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
mod.Funcs = append(mod.Funcs, Func{
|
|
Name: b.Label, Sig: sig, Params: list(b, "names"), Example: str(b, "example"),
|
|
Doc: str(b, "doc"), Loc: str(b, "loc"),
|
|
})
|
|
case "const":
|
|
t, err := typeRef(str(b, "type"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c := Const{Name: b.Label, Doc: str(b, "doc"), Val: abi.Const{Type: t, Str: str(b, "str")}}
|
|
if v, ok := b.Field("int"); ok {
|
|
c.Val.Int = v.AsInt()
|
|
}
|
|
if v, ok := b.Field("float"); ok {
|
|
c.Val.Float = v.AsFloat()
|
|
}
|
|
if v, ok := b.Field("bool"); ok {
|
|
c.Val.Bool = v.Bool
|
|
}
|
|
mod.Consts = append(mod.Consts, c)
|
|
}
|
|
}
|
|
m.Modules = append(m.Modules, mod)
|
|
return nil
|
|
}
|
|
|
|
func (m *Manifest) readEnum(n *aetext.Node) {
|
|
e := Enum{Name: n.Label, Doc: str(n, "doc")}
|
|
if v, ok := n.Attr("module"); ok {
|
|
e.Module = v.Str
|
|
}
|
|
n.EachBlock("member", func(b *aetext.Node) {
|
|
mem := abi.EnumMember{Name: b.Label}
|
|
if v, ok := b.Attr("value"); ok {
|
|
mem.Value = v.AsInt()
|
|
}
|
|
e.Members = append(e.Members, mem)
|
|
})
|
|
m.Enums = append(m.Enums, e)
|
|
}
|
|
|
|
func (m *Manifest) readStruct(n *aetext.Node) error {
|
|
s := Struct{Name: n.Label, Doc: str(n, "doc")}
|
|
if v, ok := n.Attr("module"); ok {
|
|
s.Module = v.Str
|
|
}
|
|
if v, ok := n.Attr("slots"); ok {
|
|
s.Slots = int(v.AsInt())
|
|
}
|
|
var ferr error
|
|
n.EachBlock("field", func(b *aetext.Node) {
|
|
t, err := typeRef(str(b, "type"))
|
|
if err != nil {
|
|
ferr = err
|
|
return
|
|
}
|
|
s.Fields = append(s.Fields, abi.StructField{Name: b.Label, Type: t})
|
|
})
|
|
m.Structs = append(m.Structs, s)
|
|
return ferr
|
|
}
|
|
|
|
func (m *Manifest) readEvent(n *aetext.Node) error {
|
|
e := Event{Name: n.Label, Doc: str(n, "doc"), Names: list(n, "names")}
|
|
for _, p := range list(n, "params") {
|
|
t, err := typeRef(p)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
e.Params = append(e.Params, t)
|
|
}
|
|
m.Events = append(m.Events, e)
|
|
return nil
|
|
}
|
|
|
|
func (m *Manifest) readMethod(n *aetext.Node) error {
|
|
recv := ""
|
|
if v, ok := n.Attr("recv"); ok {
|
|
recv = v.Str
|
|
}
|
|
kind, ok := abi.KindOf(recv)
|
|
if !ok {
|
|
return fmt.Errorf("manifest: unknown receiver %q", recv)
|
|
}
|
|
sig, err := readSig(n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
m.Methods = append(m.Methods, Method{
|
|
Recv: kind, Name: n.Label, Sig: sig, Params: list(n, "names"), Example: str(n, "example"),
|
|
Doc: str(n, "doc"), Loc: str(n, "loc"),
|
|
})
|
|
return nil
|
|
}
|
|
|
|
func (m *Manifest) readCompMethod(n *aetext.Node) error {
|
|
comp := ""
|
|
if v, ok := n.Attr("comp"); ok {
|
|
comp = v.Str
|
|
}
|
|
if comp == "" {
|
|
return fmt.Errorf("manifest: component method %s has no component", n.Label)
|
|
}
|
|
sig, err := readSig(n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
m.CompMethods = append(m.CompMethods, CompMethod{
|
|
Comp: comp, Name: n.Label, Sig: sig, Params: list(n, "names"), Example: str(n, "example"),
|
|
Doc: str(n, "doc"), Loc: str(n, "loc"),
|
|
})
|
|
return nil
|
|
}
|
|
|
|
func (m *Manifest) readHandle(n *aetext.Node) {
|
|
h := HandleType{Name: n.Label, Doc: str(n, "doc")}
|
|
if v, ok := n.Attr("await"); ok {
|
|
h.Await = v.Bool
|
|
}
|
|
m.Handles = append(m.Handles, h)
|
|
}
|
|
|
|
func (m *Manifest) readHandleMethod(n *aetext.Node) error {
|
|
handle := ""
|
|
if v, ok := n.Attr("handle"); ok {
|
|
handle = v.Str
|
|
}
|
|
if handle == "" {
|
|
return fmt.Errorf("manifest: handle method %s has no handle type", n.Label)
|
|
}
|
|
sig, err := readSig(n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
m.HandleMethods = append(m.HandleMethods, HandleMethod{
|
|
Handle: handle, Name: n.Label, Sig: sig, Params: list(n, "names"), Example: str(n, "example"),
|
|
Doc: str(n, "doc"), Loc: str(n, "loc"),
|
|
})
|
|
return nil
|
|
}
|
|
|
|
func (m *Manifest) readComponent(n *aetext.Node) error {
|
|
c := Component{Name: n.Label, Short: str(n, "short"), Category: str(n, "category"), Doc: str(n, "doc")}
|
|
var ferr error
|
|
n.EachBlock("field", func(b *aetext.Node) {
|
|
t, err := typeRef(str(b, "type"))
|
|
if err != nil {
|
|
ferr = err
|
|
return
|
|
}
|
|
f := Field{Name: b.Label, Type: t, Doc: str(b, "doc")}
|
|
if v, ok := b.Field("readonly"); ok {
|
|
f.ReadOnly = v.Bool
|
|
}
|
|
c.Fields = append(c.Fields, f)
|
|
})
|
|
m.Components = append(m.Components, c)
|
|
return ferr
|
|
}
|
|
|
|
func signalBlock(head, label, attr, owner string, params []abi.TypeRef, names []string, doc string) *aetext.Node {
|
|
n := &aetext.Node{Head: head, Label: label}
|
|
n.Attrs = append(n.Attrs, aetext.Field{Name: attr, Value: aetext.String(owner)})
|
|
ps := make([]string, len(params))
|
|
for i, p := range params {
|
|
ps[i] = p.String()
|
|
}
|
|
n.Fields = append(n.Fields, aetext.Field{Name: "params", Value: strList(ps)})
|
|
if len(names) > 0 {
|
|
n.Fields = append(n.Fields, aetext.Field{Name: "names", Value: strList(names)})
|
|
}
|
|
text(n, "doc", doc)
|
|
return n
|
|
}
|
|
|
|
func (m *Manifest) encodeSignals(root *aetext.Node) {
|
|
for _, s := range m.CompSignals {
|
|
root.Blocks = append(root.Blocks, signalBlock("csignal", s.Name, "comp", s.Comp, s.Params, s.Names, s.Doc))
|
|
}
|
|
for _, s := range m.HandleSignals {
|
|
root.Blocks = append(root.Blocks, signalBlock("hsignal", s.Name, "handle", s.Handle, s.Params, s.Names, s.Doc))
|
|
}
|
|
}
|
|
|
|
func (m *Manifest) readSignal(n *aetext.Node) error {
|
|
var ps []abi.TypeRef
|
|
for _, s := range list(n, "params") {
|
|
t, ok := abi.Parse(s)
|
|
if !ok {
|
|
return fmt.Errorf("manifest: signal %s has a malformed parameter type %q", n.Label, s)
|
|
}
|
|
ps = append(ps, t)
|
|
}
|
|
attr := "comp"
|
|
if n.Head == "hsignal" {
|
|
attr = "handle"
|
|
}
|
|
owner := ""
|
|
if v, ok := n.Attr(attr); ok {
|
|
owner = v.Str
|
|
}
|
|
if owner == "" {
|
|
return fmt.Errorf("manifest: signal %s has no %s", n.Label, attr)
|
|
}
|
|
names, doc := list(n, "names"), str(n, "doc")
|
|
if n.Head == "hsignal" {
|
|
m.HandleSignals = append(m.HandleSignals, HandleSignal{Handle: owner, Name: n.Label, Params: ps, Names: names, Doc: doc})
|
|
return nil
|
|
}
|
|
m.CompSignals = append(m.CompSignals, CompSignal{Comp: owner, Name: n.Label, Params: ps, Names: names, Doc: doc})
|
|
return nil
|
|
}
|