main.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. package main
  2. import (
  3. "bufio"
  4. "flag"
  5. "fmt"
  6. "io"
  7. "os"
  8. "regexp"
  9. "strings"
  10. )
  11. // Current Version
  12. const (
  13. PROGNAME = "glapi2go"
  14. VMAJOR = 0
  15. VMINOR = 1
  16. )
  17. // Command line options
  18. var (
  19. oVersion = flag.Bool("version", false, "Show version and exits")
  20. oLoader = flag.String("loader", "loader.c", "Name for the generated C loader file")
  21. )
  22. const (
  23. fileGLAPIC = "glapi.c"
  24. fileGLAPIH = "glapi.h"
  25. fileGLPARAMH = "glparam.h"
  26. fileCONSTS = "consts.go"
  27. glMaxVersion = "GL_VERSION_3_3"
  28. )
  29. // Maps OpenGL types to Go
  30. var mapCType2Go = map[string]string{
  31. "GLenum": "uint",
  32. "GLfloat": "float32",
  33. "GLchar": "byte",
  34. "GLbyte": "byte",
  35. "GLboolean": "bool",
  36. "GLshort": "int16",
  37. "GLushort": "uint16",
  38. "GLint": "int",
  39. "GLint64": "int64",
  40. "GLsizei": "int",
  41. "GLbitfield": "uint",
  42. "GLdouble": "float64",
  43. "GLuint": "uint",
  44. "GLuint64": "uint64",
  45. "GLubyte": "byte",
  46. "GLintptr": "uintptr",
  47. "GLsizeiptr": "uintptr",
  48. "GLsync": "unsafe.Pointer",
  49. }
  50. func main() {
  51. // Parse command line parameters
  52. flag.Usage = usage
  53. flag.Parse()
  54. // Print version and exits
  55. if *oVersion == true {
  56. fmt.Fprintf(os.Stderr, "%s v%d.%d\n", PROGNAME, VMAJOR, VMINOR)
  57. return
  58. }
  59. // Checks for input header file
  60. if len(flag.Args()) == 0 {
  61. usage()
  62. return
  63. }
  64. fname := flag.Args()[0]
  65. // Open input header file
  66. fin, err := os.Open(fname)
  67. if err != nil {
  68. abort(err)
  69. }
  70. // Parses the header and builds GLHeader struct
  71. // with all the information necessary to expand all templates.
  72. var glh GLHeader
  73. err = parser(fin, &glh)
  74. if err != nil {
  75. abort(err)
  76. }
  77. // Generates glapi.c
  78. err = genFile(templGLAPIC, &glh, fileGLAPIC, false)
  79. if err != nil {
  80. abort(err)
  81. }
  82. // Generates glapi.h
  83. err = genFile(templGLAPIH, &glh, fileGLAPIH, false)
  84. if err != nil {
  85. abort(err)
  86. }
  87. // Generates consts.go
  88. err = genFile(templCONSTS, &glh, fileCONSTS, true)
  89. if err != nil {
  90. abort(err)
  91. }
  92. }
  93. // parser parses the header file and builds the Template structure
  94. func parser(fheader io.Reader, h *GLHeader) error {
  95. // Regex to parser #endif line to detect end of definitions for
  96. // specific OpenGL version: ex:"#endif /* GL_VERSION_3_3 */"
  97. rexEndif := regexp.MustCompile(`#endif\s+/\*\s+(\w+)\s+\*/`)
  98. // Regex to parse define line, capturing name (1) and value (2)
  99. rexDefine := regexp.MustCompile(`#define\s+(\w+)\s+(\w+)`)
  100. // Regex to parse function definition line,
  101. // capturing return value (1), function name (2) and parameters (3)
  102. rexApi := regexp.MustCompile(`GLAPI\s+(\w+)\s+APIENTRY\s+(\w+)\s+\((.*)\)`)
  103. h.Defines = make([]GLDefine, 0)
  104. h.Funcs = make([]GLFunc, 0)
  105. bufin := bufio.NewReader(fheader)
  106. maxLength := 0
  107. for {
  108. // Reads next line and abort on error (not EOF)
  109. line, err := bufin.ReadString('\n')
  110. if err != nil && err != io.EOF {
  111. return err
  112. }
  113. // Checks for "#endif" identifying end of definitions for specified
  114. // OpenGL version
  115. res := rexEndif.FindStringSubmatch(line)
  116. if len(res) > 0 {
  117. if res[1] == glMaxVersion {
  118. break
  119. }
  120. }
  121. // Checks for "#define" of GL constants
  122. res = rexDefine.FindStringSubmatch(line)
  123. if len(res) >= 3 {
  124. dname := res[1]
  125. if strings.HasPrefix(dname, "GL_") {
  126. h.Defines = append(h.Defines, GLDefine{
  127. Name: gldef2go(res[1]),
  128. Value: glval2go(res[2]),
  129. })
  130. }
  131. }
  132. // Checks for function declaration
  133. res = rexApi.FindStringSubmatch(line)
  134. if len(res) >= 2 {
  135. var f GLFunc
  136. f.Rtype = res[1]
  137. f.Ptype = "PFN" + strings.ToUpper(res[2]) + "PROC"
  138. f.Fname = res[2]
  139. f.FnameGo = glfname2go(res[2])
  140. f.Pname = "p" + f.Fname
  141. f.Rtype = res[1]
  142. f.CParams = res[3]
  143. err := parseParams(res[3], &f)
  144. if err != nil {
  145. return err
  146. }
  147. h.Funcs = append(h.Funcs, f)
  148. if len(f.Ptype) > maxLength {
  149. maxLength = len(f.Ptype)
  150. }
  151. }
  152. // If EOF ends of parsing.
  153. if err == io.EOF {
  154. break
  155. }
  156. }
  157. // Sets spacer string
  158. for i := 0; i < len(h.Funcs); i++ {
  159. h.Funcs[i].Spacer = strings.Repeat(" ", maxLength-len(h.Funcs[i].Ptype)+1)
  160. }
  161. return nil
  162. }
  163. // parseParams receives a string with the declaration of the parameters of a C function
  164. // and parses it into an array of GLParam types with are then saved in the specified
  165. // GLfunc object.
  166. func parseParams(gparams string, f *GLFunc) error {
  167. params := strings.Split(gparams, ",")
  168. res := make([]GLParam, 0)
  169. args := make([]string, 0)
  170. goParams := make([]string, 0)
  171. for _, tn := range params {
  172. parts := strings.Split(strings.TrimSpace(tn), " ")
  173. var qualif string
  174. var name string
  175. var ctype string
  176. switch len(parts) {
  177. case 1:
  178. ctype = parts[0]
  179. if ctype != "void" {
  180. panic("Should be void but is:" + ctype)
  181. }
  182. continue
  183. case 2:
  184. ctype = parts[0]
  185. name = parts[1]
  186. case 3:
  187. qualif = parts[0]
  188. ctype = parts[1]
  189. name = parts[2]
  190. default:
  191. return fmt.Errorf("Invalid parameter:[%s]", tn)
  192. }
  193. arg := getArgName(name)
  194. args = append(args, arg)
  195. res = append(res, GLParam{Qualif: qualif, CType: ctype, Arg: arg, Name: name})
  196. // Go parameter
  197. goarg, gotype := gltypearg2go(ctype, name)
  198. goParams = append(goParams, goarg+" "+gotype)
  199. }
  200. f.Args = strings.Join(args, ", ")
  201. f.Params = res
  202. f.GoParams = strings.Join(goParams, ", ")
  203. return nil
  204. }
  205. // getArgName remove qualifiers and array brackets from the argument
  206. // returning only the argument name. Ex: *const*indices -> indices
  207. func getArgName(arg string) string {
  208. if strings.HasPrefix(arg, "*const*") {
  209. return strings.TrimPrefix(arg, "*const*")
  210. }
  211. if strings.HasPrefix(arg, "**") {
  212. return strings.TrimPrefix(arg, "**")
  213. }
  214. if strings.HasPrefix(arg, "*") {
  215. return strings.TrimPrefix(arg, "*")
  216. }
  217. // Checks for array index: [?]
  218. aidx := strings.Index(arg, "[")
  219. if aidx > 0 {
  220. return arg[:aidx]
  221. }
  222. return arg
  223. }
  224. // glfname2go converts the name of an OpenGL C function to Go
  225. func glfname2go(glfname string) string {
  226. if strings.HasPrefix(glfname, "gl") {
  227. return strings.TrimPrefix(glfname, "gl")
  228. }
  229. return glfname
  230. }
  231. // gldef2go converts a name such as GL_LINE_LOOP to LINE_LOOP
  232. func gldef2go(gldef string) string {
  233. return strings.TrimPrefix(gldef, "GL_")
  234. }
  235. // glval2go converts a C OpenGL value to a Go value
  236. func glval2go(glval string) string {
  237. val := glval
  238. if strings.HasSuffix(val, "u") {
  239. val = strings.TrimSuffix(val, "u")
  240. }
  241. if strings.HasSuffix(val, "ull") {
  242. val = strings.TrimSuffix(val, "ull")
  243. }
  244. return val
  245. }
  246. // gltypearg2go converts a C OpenGL function type/argument to a Go argument/type
  247. // GLfloat *param -> param *float32
  248. // GLuint type -> ptype uint
  249. // void *pixels -> pixels unsafe.Pointer
  250. // void **params -> params *unsafe.Pointer
  251. func gltypearg2go(gltype, glarg string) (goarg string, gotype string) {
  252. // Replace parameter names using Go keywords
  253. gokeys := []string{"type", "func"}
  254. for _, k := range gokeys {
  255. if strings.HasSuffix(glarg, k) {
  256. glarg = strings.TrimSuffix(glarg, k) + "p" + k
  257. break
  258. }
  259. }
  260. if gltype == "void" {
  261. gotype = "unsafe.Pointer"
  262. if strings.HasPrefix(glarg, "**") {
  263. goarg = strings.TrimPrefix(glarg, "**")
  264. gotype = "*" + gotype
  265. return goarg, gotype
  266. }
  267. if strings.HasPrefix(glarg, "*") {
  268. goarg = strings.TrimPrefix(glarg, "*")
  269. return goarg, gotype
  270. }
  271. return "???", "???"
  272. }
  273. goarg = glarg
  274. gotype = mapCType2Go[gltype]
  275. if strings.HasPrefix(glarg, "*") {
  276. gotype = "*" + gotype
  277. goarg = strings.TrimPrefix(goarg, "*")
  278. }
  279. return goarg, gotype
  280. }
  281. //
  282. // Shows application usage
  283. //
  284. func usage() {
  285. fmt.Fprintf(os.Stderr, "%s v%d.%d\n", PROGNAME, VMAJOR, VMINOR)
  286. fmt.Fprintf(os.Stderr, "usage:%s [options] <glheader>\n", strings.ToLower(PROGNAME))
  287. flag.PrintDefaults()
  288. os.Exit(2)
  289. }
  290. func abort(err error) {
  291. fmt.Fprintf(os.Stderr, "%s\n", err)
  292. os.Exit(1)
  293. }