summaryrefslogtreecommitdiffstats
path: root/tools/glsl_preproc/macros.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/glsl_preproc/macros.py')
-rw-r--r--tools/glsl_preproc/macros.py105
1 files changed, 105 insertions, 0 deletions
diff --git a/tools/glsl_preproc/macros.py b/tools/glsl_preproc/macros.py
new file mode 100644
index 0000000..2ba7e21
--- /dev/null
+++ b/tools/glsl_preproc/macros.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+
+import re
+
+from variables import Var
+from templates import *
+from statement import *
+
+PATTERN_PRAGMA = re.compile(flags=re.VERBOSE, pattern=r'''
+\s*\#\s*pragma\s+ # '#pragma'
+(?P<pragma>(?: # pragma name
+ GLSL[PHF]?
+))\s*
+(?P<rest>.*)$ # rest of line (pragma body)
+''')
+
+# Represents a single #pragma macro
+class Macro(object):
+ PRAGMAS = {
+ 'GLSL': 'SH_BUF_BODY',
+ 'GLSLP': 'SH_BUF_PRELUDE',
+ 'GLSLH': 'SH_BUF_HEADER',
+ 'GLSLF': 'SH_BUF_FOOTER',
+ }
+
+ def __init__(self, linenr=0, type='GLSL'):
+ self.linenr = linenr
+ self.buf = Macro.PRAGMAS[type]
+ self.name = '_glsl_' + str(linenr)
+ self.body = [] # list of statements
+ self.last = None # previous GLSLBlock (if unterminated)
+ self.vars = VarSet()
+
+ def needs_single_line(self):
+ if not self.body:
+ return False
+ prev = self.body[-1]
+ return isinstance(prev, BlockStart) and not prev.multiline
+
+ def push_line(self, line):
+ self.vars.merge(line.vars)
+
+ if isinstance(line, GLSLLine):
+ if self.last:
+ self.last.append(line)
+ elif self.needs_single_line():
+ self.body.append(GLSLBlock(line))
+ else:
+ # start new GLSL block
+ self.last = GLSLBlock(line)
+ self.body.append(self.last)
+ else:
+ self.body.append(line)
+ self.last = None
+
+ def render_struct(self):
+ return STRUCT_TEMPLATE.render(macro=self)
+
+ def render_call(self):
+ return CALL_TEMPLATE.render(macro=self)
+
+ def render_fun(self):
+ return FUNCTION_TEMPLATE.render(macro=self, Var=Var)
+
+ # yields output lines
+ @staticmethod
+ def process_file(lines, strip=False):
+ macro = None
+ macros = []
+
+ for linenr, line_orig in enumerate(lines, start=1):
+ line = line_orig.rstrip()
+
+ # Strip leading spaces, due to C indent. Skip first pragma line.
+ if macro and leading_spaces is None:
+ leading_spaces = len(line) - len(line.lstrip())
+
+ # check for start of macro
+ if not macro:
+ leading_spaces = None
+ if result := re.match(PATTERN_PRAGMA, line):
+ macro = Macro(linenr, type=result['pragma'])
+ line = result['rest'] # strip pragma prefix
+
+ if macro:
+ if leading_spaces:
+ line = re.sub(f'^\s{{1,{leading_spaces}}}', '', line)
+ if more_lines := line.endswith('\\'):
+ line = line[:-1]
+ if statement := Statement.parse(line, strip=strip, linenr=linenr):
+ macro.push_line(statement)
+ if more_lines:
+ continue # stay in macro
+ else:
+ yield macro.render_call()
+ yield '#line {}\n'.format(linenr + 1)
+ macros.append(macro)
+ macro = None
+ else:
+ yield line_orig
+
+ if macros:
+ yield '\n// Auto-generated template functions:'
+ for macro in macros:
+ yield macro.render_fun()