incomp-flame-post/code/code_gen/code_gen.py
ignis 962704e5f7
All checks were successful
CI Test Suite / run-tests (push) Successful in 1m14s
Deploy Documentation / build-and-deploy (push) Successful in 53s
refactor: resolve code smells ranked 1 to 6 (unused variables, local imports, wildcard imports, empty routines, pointer warning)
2026-06-02 17:27:48 +00:00

206 lines
5.9 KiB
Python

import os
import sys
import argparse
import datetime
import subprocess as sp
from lark import Lark, Token
from jinja2 import Template
from post import Stage1, Stage2, Stage3, Stage4
from resources.m_template import mod_form
parser = argparse.ArgumentParser()
parser.add_argument("term_file", help="name of file containing postprocessing term spec")
parser.add_argument("-b", "--build-info", action="store_true", help="print build_info instead of code")
parser.add_argument("-x", "--print-latex", action="store_true", help="print latex equation instead of code")
parser.add_argument("-p", "--print-report", action="store_true", help="print code generation report instead of code")
args = parser.parse_args()
calc_grammar = """
?varlist: "[" [NAME ("," NAME)*] "]"
?start: statement*
?statement: NAME [ attr_list ] "=" sum -> assign_var
| avg "{" [NAME ("," NAME)*] "}" -> assign_avg_var
| varlist
attr_list: "(" [attr_pair ("," attr_pair)*] ")"
?attr_pair: NAME "=" BOOL
| NAME "=" INT
| NAME "=" ESCAPED_STRING
?sum: product
| sum "+" product -> add
| sum "-" product -> sub
?product: atom
| product "*" atom -> mul
| product "/" atom -> div
?atom: NUMBER -> number
| "-" atom -> neg
| NAME -> var
| NAME "'" -> fluc
| "$" NAME -> env
| "(" sum ")" -> paren
| inlinefunc "(" sum ")" -> icall
| mathfunc "(" sum ("," sum)* ")" -> fcall
| derivative "(" NAME ")" -> dnx
avg: "avg" [NAME]
?inlinefunc: "sqr" -> sqr
| "pow3" -> pow3
?mathfunc: "log" -> log
| "exp" -> exp
| "sqrt" -> sqrt
| "abs" -> abs
| "rxn_rate" -> rxn_rate
| "$" NAME -> udf
?derivative: "ddx" -> ddx
| "d2dx" -> d2dx
| "ddy" -> ddy
| "d2dy" -> d2dy
| "ddz" -> ddz
| "d2dz" -> d2dz
%import common.CNAME -> NAME
%import common.NUMBER
%import common.ESCAPED_STRING
%import common.INT
%import common.WS
BOOL: "true" | "false"
COMMENT: /#.*/
%ignore COMMENT
%ignore WS
"""
def tok_to_bool(tok):
"Convert the value of `tok` from string to bool, while maintaining line number & column."
# tok.type == 'BOOL'
return Token.new_borrow_pos(tok.type, tok.value == "true", tok)
def tok_to_int(tok):
"Convert the value of `tok` from string to int, while maintaining line number & column."
# tok.type == 'INT'
return Token.new_borrow_pos(tok.type, int(tok), tok)
def tok_to_str(tok):
"Convert the value of `tok` from string to string, while maintaining line number & column."
# tok.type == 'ESCAPED_STRING'
return Token.new_borrow_pos(tok.type, tok.value.strip('"'), tok)
class VersionInfo(object):
def __init__ (self, input_raw):
bd = 'build date: {}'
bb = 'build base: {}'
self.build_date = bd.format(datetime.datetime.today().ctime())
try:
self.build_base = bb.format(sp.check_output(["git", "rev-parse", "HEAD"]).strip())
except sp.CalledProcessError:
self.build_base = "None"
try:
sp.check_output("git diff --exit-code".split())
sp.check_output("git diff --cached --exit-code".split())
self.off_base = ""
except sp.CalledProcessError:
self.off_base = "built with changes not commited"
tinfo = '''
================================================================================
************************************ terms *************************************
--------------------------------------------------------------------------------
{}
================================================================================
'''
self.term_info = tinfo.format(input_raw)
def fparams (self):
return "\n".join([self.build_date, self.build_base, self.off_base, self.term_info])
def test(terms_raw, report=False, latex=False):
"""Compiles the post-processing term specifications from DSL into targets.
This compiles the Lark AST through all 4 pipeline stages (Stage 1 to 4)
and outputs the resulting Fortran source code via Jinja2, a LaTeX equation
dictionary, or a code generation IR report.
Args:
terms_raw (str): The raw string contents of the DSL terms specification input file.
report (bool): If True, generates and saves the intermediate code generation report.
latex (bool): If True, prints LaTeX equations of the averaged terms to stdout.
"""
parser = Lark(calc_grammar,
parser='lalr',
lexer_callbacks =
{
'ESCAPED_STRING': tok_to_str,
'INT': tok_to_int,
'BOOL': tok_to_bool
}
)
tree = parser.parse(terms_raw)
ir1 = Stage1(tree)
ir2 = Stage2(ir1)
ir3 = Stage3(ir2)
ir4 = Stage4(ir3)
cdict = ir4.print_program()
if report:
ir4.save_ir()
elif latex:
print("{")
for avg in ir4.averaged.values():
print('"{}" : '.format(avg.name))
# print r"\begin{equation}"
print('r"${}$" ,'.format(avg.latex))
# print r"\end{equation}"
print("}")
else:
print(Template(mod_form).render(**cdict))
def build_info(terms_raw):
"""Generates and prints build information for the current code generation run.
Args:
terms_raw (str): The raw string contents of the DSL terms specification input file.
"""
vinfo = VersionInfo(terms_raw)
print(vinfo.fparams())
if __name__ == '__main__':
with open(args.term_file) as inputfile:
terms_raw = ((inputfile.read()))
if args.build_info:
build_info(terms_raw)
else:
test(terms_raw, report=args.print_report, latex=args.print_latex)