main.go 7.3 KB

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