You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

130 lines
2.9 KiB

  1. package parse
  2. import (
  3. "fmt"
  4. "go/ast"
  5. "strings"
  6. "github.com/tinylib/msgp/gen"
  7. )
  8. const linePrefix = "//msgp:"
  9. // func(args, fileset)
  10. type directive func([]string, *FileSet) error
  11. // func(passName, args, printer)
  12. type passDirective func(gen.Method, []string, *gen.Printer) error
  13. // map of all recognized directives
  14. //
  15. // to add a directive, define a func([]string, *FileSet) error
  16. // and then add it to this list.
  17. var directives = map[string]directive{
  18. "shim": applyShim,
  19. "ignore": ignore,
  20. "tuple": astuple,
  21. }
  22. var passDirectives = map[string]passDirective{
  23. "ignore": passignore,
  24. }
  25. func passignore(m gen.Method, text []string, p *gen.Printer) error {
  26. pushstate(m.String())
  27. for _, a := range text {
  28. p.ApplyDirective(m, gen.IgnoreTypename(a))
  29. infof("ignoring %s\n", a)
  30. }
  31. popstate()
  32. return nil
  33. }
  34. // find all comment lines that begin with //msgp:
  35. func yieldComments(c []*ast.CommentGroup) []string {
  36. var out []string
  37. for _, cg := range c {
  38. for _, line := range cg.List {
  39. if strings.HasPrefix(line.Text, linePrefix) {
  40. out = append(out, strings.TrimPrefix(line.Text, linePrefix))
  41. }
  42. }
  43. }
  44. return out
  45. }
  46. //msgp:shim {Type} as:{Newtype} using:{toFunc/fromFunc} mode:{Mode}
  47. func applyShim(text []string, f *FileSet) error {
  48. if len(text) < 4 || len(text) > 5 {
  49. return fmt.Errorf("shim directive should have 3 or 4 arguments; found %d", len(text)-1)
  50. }
  51. name := text[1]
  52. be := gen.Ident(strings.TrimPrefix(strings.TrimSpace(text[2]), "as:")) // parse as::{base}
  53. if name[0] == '*' {
  54. name = name[1:]
  55. be.Needsref(true)
  56. }
  57. be.Alias(name)
  58. usestr := strings.TrimPrefix(strings.TrimSpace(text[3]), "using:") // parse using::{method/method}
  59. methods := strings.Split(usestr, "/")
  60. if len(methods) != 2 {
  61. return fmt.Errorf("expected 2 using::{} methods; found %d (%q)", len(methods), text[3])
  62. }
  63. be.ShimToBase = methods[0]
  64. be.ShimFromBase = methods[1]
  65. if len(text) == 5 {
  66. modestr := strings.TrimPrefix(strings.TrimSpace(text[4]), "mode:") // parse mode::{mode}
  67. switch modestr {
  68. case "cast":
  69. be.ShimMode = gen.Cast
  70. case "convert":
  71. be.ShimMode = gen.Convert
  72. default:
  73. return fmt.Errorf("invalid shim mode; found %s, expected 'cast' or 'convert", modestr)
  74. }
  75. }
  76. infof("%s -> %s\n", name, be.Value.String())
  77. f.findShim(name, be)
  78. return nil
  79. }
  80. //msgp:ignore {TypeA} {TypeB}...
  81. func ignore(text []string, f *FileSet) error {
  82. if len(text) < 2 {
  83. return nil
  84. }
  85. for _, item := range text[1:] {
  86. name := strings.TrimSpace(item)
  87. if _, ok := f.Identities[name]; ok {
  88. delete(f.Identities, name)
  89. infof("ignoring %s\n", name)
  90. }
  91. }
  92. return nil
  93. }
  94. //msgp:tuple {TypeA} {TypeB}...
  95. func astuple(text []string, f *FileSet) error {
  96. if len(text) < 2 {
  97. return nil
  98. }
  99. for _, item := range text[1:] {
  100. name := strings.TrimSpace(item)
  101. if el, ok := f.Identities[name]; ok {
  102. if st, ok := el.(*gen.Struct); ok {
  103. st.AsTuple = true
  104. infoln(name)
  105. } else {
  106. warnf("%s: only structs can be tuples\n", name)
  107. }
  108. }
  109. }
  110. return nil
  111. }