package parse
|
|
|
|
import (
|
|
"fmt"
|
|
"go/ast"
|
|
"strings"
|
|
|
|
"github.com/tinylib/msgp/gen"
|
|
)
|
|
|
|
const linePrefix = "//msgp:"
|
|
|
|
// func(args, fileset)
|
|
type directive func([]string, *FileSet) error
|
|
|
|
// func(passName, args, printer)
|
|
type passDirective func(gen.Method, []string, *gen.Printer) error
|
|
|
|
// map of all recognized directives
|
|
//
|
|
// to add a directive, define a func([]string, *FileSet) error
|
|
// and then add it to this list.
|
|
var directives = map[string]directive{
|
|
"shim": applyShim,
|
|
"ignore": ignore,
|
|
"tuple": astuple,
|
|
}
|
|
|
|
var passDirectives = map[string]passDirective{
|
|
"ignore": passignore,
|
|
}
|
|
|
|
func passignore(m gen.Method, text []string, p *gen.Printer) error {
|
|
pushstate(m.String())
|
|
for _, a := range text {
|
|
p.ApplyDirective(m, gen.IgnoreTypename(a))
|
|
infof("ignoring %s\n", a)
|
|
}
|
|
popstate()
|
|
return nil
|
|
}
|
|
|
|
// find all comment lines that begin with //msgp:
|
|
func yieldComments(c []*ast.CommentGroup) []string {
|
|
var out []string
|
|
for _, cg := range c {
|
|
for _, line := range cg.List {
|
|
if strings.HasPrefix(line.Text, linePrefix) {
|
|
out = append(out, strings.TrimPrefix(line.Text, linePrefix))
|
|
}
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
//msgp:shim {Type} as:{Newtype} using:{toFunc/fromFunc} mode:{Mode}
|
|
func applyShim(text []string, f *FileSet) error {
|
|
if len(text) < 4 || len(text) > 5 {
|
|
return fmt.Errorf("shim directive should have 3 or 4 arguments; found %d", len(text)-1)
|
|
}
|
|
|
|
name := text[1]
|
|
be := gen.Ident(strings.TrimPrefix(strings.TrimSpace(text[2]), "as:")) // parse as::{base}
|
|
if name[0] == '*' {
|
|
name = name[1:]
|
|
be.Needsref(true)
|
|
}
|
|
be.Alias(name)
|
|
|
|
usestr := strings.TrimPrefix(strings.TrimSpace(text[3]), "using:") // parse using::{method/method}
|
|
|
|
methods := strings.Split(usestr, "/")
|
|
if len(methods) != 2 {
|
|
return fmt.Errorf("expected 2 using::{} methods; found %d (%q)", len(methods), text[3])
|
|
}
|
|
|
|
be.ShimToBase = methods[0]
|
|
be.ShimFromBase = methods[1]
|
|
|
|
if len(text) == 5 {
|
|
modestr := strings.TrimPrefix(strings.TrimSpace(text[4]), "mode:") // parse mode::{mode}
|
|
switch modestr {
|
|
case "cast":
|
|
be.ShimMode = gen.Cast
|
|
case "convert":
|
|
be.ShimMode = gen.Convert
|
|
default:
|
|
return fmt.Errorf("invalid shim mode; found %s, expected 'cast' or 'convert", modestr)
|
|
}
|
|
}
|
|
|
|
infof("%s -> %s\n", name, be.Value.String())
|
|
f.findShim(name, be)
|
|
|
|
return nil
|
|
}
|
|
|
|
//msgp:ignore {TypeA} {TypeB}...
|
|
func ignore(text []string, f *FileSet) error {
|
|
if len(text) < 2 {
|
|
return nil
|
|
}
|
|
for _, item := range text[1:] {
|
|
name := strings.TrimSpace(item)
|
|
if _, ok := f.Identities[name]; ok {
|
|
delete(f.Identities, name)
|
|
infof("ignoring %s\n", name)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
//msgp:tuple {TypeA} {TypeB}...
|
|
func astuple(text []string, f *FileSet) error {
|
|
if len(text) < 2 {
|
|
return nil
|
|
}
|
|
for _, item := range text[1:] {
|
|
name := strings.TrimSpace(item)
|
|
if el, ok := f.Identities[name]; ok {
|
|
if st, ok := el.(*gen.Struct); ok {
|
|
st.AsTuple = true
|
|
infoln(name)
|
|
} else {
|
|
warnf("%s: only structs can be tuples\n", name)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|