generate.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /*
  2. * ECAL
  3. *
  4. * Copyright 2020 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the MIT
  7. * License, If a copy of the MIT License was not distributed with this
  8. * file, You can obtain one at https://opensource.org/licenses/MIT.
  9. */
  10. package main
  11. import (
  12. "bytes"
  13. "flag"
  14. "fmt"
  15. "go/importer"
  16. "go/token"
  17. "go/types"
  18. "io/ioutil"
  19. "os"
  20. "path/filepath"
  21. "sort"
  22. "unicode"
  23. )
  24. //go:generate echo Generating ECAL stdlib from Go functions ...
  25. //go:generate go run devt.de/krotik/ecal/stdlib/generate $PWD/stdlib
  26. /*
  27. Stdlib candidates modules:
  28. go list std | grep -v internal | grep -v '\.' | grep -v unsafe | grep -v syscall
  29. */
  30. var pkgNames = map[string][]string{
  31. "math": {"Pi"},
  32. "fmt": {"Println", "Sprint"},
  33. }
  34. var filename = filepath.Join(os.Args[1], "stdlib_gen.go")
  35. var stderrPrint = fmt.Println
  36. var stdoutPrint = fmt.Println
  37. func main() {
  38. var err error
  39. var outbuf bytes.Buffer
  40. flag.Parse()
  41. // Make sure pkgNames is sorted
  42. var importList []string
  43. for pkgName, names := range pkgNames {
  44. sort.Strings(names)
  45. importList = append(importList, pkgName)
  46. }
  47. sort.Strings(importList)
  48. outbuf.WriteString(`
  49. // Code generated by ecal/stdlib/generate; DO NOT EDIT.
  50. package stdlib
  51. `)
  52. outbuf.WriteString("import (\n")
  53. for _, pkgName := range importList {
  54. outbuf.WriteString(fmt.Sprintf("\t\"%v\"\n", pkgName))
  55. }
  56. outbuf.WriteString(` "reflect"
  57. )
  58. `)
  59. outbuf.WriteString(`/*
  60. genStdlib contains all generated stdlib constructs.
  61. */
  62. `)
  63. outbuf.WriteString("var genStdlib = map[interface{}]interface{}{\n")
  64. for _, pkgName := range importList {
  65. outbuf.WriteString(fmt.Sprintf("\t\"%v-const\" : %vConstMap,\n", pkgName, pkgName))
  66. outbuf.WriteString(fmt.Sprintf("\t\"%v-func\" : %vFuncMap,\n", pkgName, pkgName))
  67. }
  68. outbuf.WriteString("}\n\n")
  69. for _, pkgName := range importList {
  70. var pkg *types.Package
  71. pkgSymbols := pkgNames[pkgName]
  72. if err == nil {
  73. pkg, err = importer.ForCompiler(token.NewFileSet(), "source", nil).Import(pkgName)
  74. if err == nil {
  75. stdoutPrint("Generating adapter functions for", pkg)
  76. scope := pkg.Scope()
  77. // Write constants
  78. outbuf.WriteString(fmt.Sprintf(`/*
  79. %vConstMap contains the mapping of stdlib %v constants.
  80. */
  81. var %vConstMap = map[interface{}]interface{}{
  82. `, pkgName, pkgName, pkgName))
  83. for _, name := range scope.Names() {
  84. if !containsSymbol(pkgSymbols, name) {
  85. continue
  86. }
  87. switch obj := scope.Lookup(name).(type) {
  88. case *types.Const:
  89. if unicode.IsUpper([]rune(name)[0]) {
  90. line := fmt.Sprintf(` "%v": %v.%v,
  91. `, name, pkgName, obj.Name())
  92. if basicType, ok := obj.Type().(*types.Basic); ok {
  93. // Convert number constants so they can be used in calculations
  94. switch basicType.Kind() {
  95. case types.Int,
  96. types.Int8,
  97. types.Int16,
  98. types.Int32,
  99. types.Int64,
  100. types.Uint,
  101. types.Uint8,
  102. types.Uint16,
  103. types.Uint32,
  104. types.Uint64,
  105. types.Uintptr,
  106. types.Float32,
  107. types.UntypedInt,
  108. types.UntypedFloat:
  109. line = fmt.Sprintf(` "%v": float64(%v.%v),
  110. `, name, pkgName, obj.Name())
  111. }
  112. }
  113. outbuf.WriteString(line)
  114. }
  115. }
  116. }
  117. outbuf.WriteString("}\n\n")
  118. // Write functions
  119. outbuf.WriteString(fmt.Sprintf(`/*
  120. %vFuncMap contains the mapping of stdlib %v functions.
  121. */
  122. var %vFuncMap = map[interface{}]interface{}{
  123. `, pkgName, pkgName, pkgName))
  124. for _, name := range scope.Names() {
  125. if !containsSymbol(pkgSymbols, name) {
  126. continue
  127. }
  128. switch obj := scope.Lookup(name).(type) {
  129. case *types.Func:
  130. if unicode.IsUpper([]rune(name)[0]) {
  131. outbuf.WriteString(
  132. fmt.Sprintf(` "%v": &ECALFunctionAdapter{reflect.ValueOf(%v)},
  133. `, name, obj.FullName()))
  134. }
  135. }
  136. }
  137. outbuf.WriteString("}\n\n")
  138. }
  139. }
  140. }
  141. if err == nil {
  142. err = ioutil.WriteFile(filename, outbuf.Bytes(), 0644)
  143. }
  144. if err != nil {
  145. stderrPrint("Error:", err)
  146. }
  147. }
  148. func containsSymbol(symbols []string, item string) bool {
  149. i := sort.SearchStrings(symbols, item)
  150. return i < len(symbols) && symbols[i] == item
  151. }