diff --git a/code/code_gen/code_gen.py b/code/code_gen/code_gen.py index 2b10cf4..18864d1 100644 --- a/code/code_gen/code_gen.py +++ b/code/code_gen/code_gen.py @@ -6,7 +6,8 @@ import argparse 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("-p", "--print_report", action="store_true", help="print code generation report 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() from lark import Lark, Visitor, Transformer, v_args, Token @@ -20,14 +21,15 @@ calc_grammar = """ ?start: statement* - ?statement: NAME [ "(" [attr_list] ")" ] "=" sum -> assign_var + ?statement: NAME [ attr_list ] "=" sum -> assign_var | avg "{" [NAME ("," NAME)*] "}" -> assign_avg_var | varlist - ?attr_list: attr_pair ("," attr_pair)* + attr_list: "(" [attr_pair ("," attr_pair)*] ")" ?attr_pair: NAME "=" BOOL | NAME "=" INT + | NAME "=" ESCAPED_STRING ?sum: product | sum "+" product -> add @@ -42,7 +44,7 @@ calc_grammar = """ | NAME -> var | NAME "'" -> fluc | "$" NAME -> env - | "(" sum ")" + | "(" sum ")" -> paren | inlinefunc "(" sum ")" -> icall | mathfunc "(" sum ("," sum)* ")" -> fcall | derivative "(" NAME ")" -> dnx @@ -68,6 +70,7 @@ calc_grammar = """ %import common.CNAME -> NAME %import common.NUMBER + %import common.ESCAPED_STRING %import common.INT %import common.WS @@ -89,6 +92,11 @@ def tok_to_int(tok): # 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): @@ -127,12 +135,13 @@ class VersionInfo(object): -def test(terms_raw, report=False): +def test(terms_raw, report=False, latex=False): parser = Lark(calc_grammar, parser='lalr', lexer_callbacks = { + 'ESCAPED_STRING': tok_to_str, 'INT': tok_to_int, 'BOOL': tok_to_bool } @@ -150,10 +159,18 @@ def test(terms_raw, report=False): cdict = ir4.print_program() - if not report: - print mod_form.format( cdict ) - else: + if report: ir4.save_ir() + elif latex: + print "{" + for avg in ir4.averaged.values(): + print "'{}' : ".format(avg.name) + # print r"\begin{equation}" + print "'${}$' ,".format(avg.latex) + # print r"\end{equation}" + print "}" + else: + print mod_form.format( cdict ) @@ -173,4 +190,4 @@ if __name__ == '__main__': if args.build_info: build_info(terms_raw) else: - test(terms_raw, args.print_report) + test(terms_raw, report=args.print_report, latex=args.print_latex) diff --git a/code/code_gen/post.py b/code/code_gen/post.py index 5ed3939..eacce7f 100644 --- a/code/code_gen/post.py +++ b/code/code_gen/post.py @@ -1,4 +1,5 @@ from lark import Lark, Visitor, Transformer, v_args, Token +import warnings class CollectDefinitions(Visitor): @@ -70,6 +71,120 @@ class ExpInspector(Visitor): self.deriv.add((op.data, v.value)) +@v_args(inline=True) # Affects the signatures of the methods +class ExpToLatex(Transformer): + + def __init__(self, fdict): + self.fdict = fdict + + def arithmatic_rooted(self, name): + try: + exproot = self.fdict[name].exp.data + except AttributeError: + exproot = "something_11fasq2afa3rfzsaerqw23" + + return ((exproot == "add") or (exproot == "sub") or + (exproot == "mul") or (exproot == "div")) + + def parenthise(self, name): + try: + latex = self.fdict[name].latex + latex_given = self.fdict[name].latex_given + except KeyError: + warnings.warn(name + " is not found") + latex = r"\mathrm{{{}}}".format(name) + latex_given = None + + if self.arithmatic_rooted(name) and (latex_given is None): + latex = "(" + latex + ")" + + return latex + + def number(self, numeral): + return numeral + + def env(self, name): + return r"\mathrm{{{}}}".format(name.value) + + def paren(self, name): + return "({})".format(str(name)) + + def var(self, name): + return self.parenthise(name.value) + + def fluc(self, name): + return self.parenthise(name.value)+ "''" + + def dnx (self, partial, b): + fmt = r"\partial_{{{}}}" + coord = partial.data[-1] + op = fmt.format(coord + coord if len(partial.data) > 3 else coord) + + signature = "{}_{}".format(partial.data, b.value) + + try: + eq = self.fdict[signature].latex + except KeyError: + eq = op + self.parenthise(b.value) + warnings.warn(signature + " is not found: " + eq) + + return eq + + def icall (self, a, b): + if a.data == "sqr": + fcode = "({0})^2".format(b) + elif a.data == "pow3": + fcode = "({0})^3".format(b) + else: + fcode = "({0})".format(b) + return fcode + + def fcall (self, *args): + a = args[0] + b = ", ".join(args[1:]) + if a == 'sqrt': + fcode = r"\sqrt{{{}}}".format(b) + return fcode + elif a == 'abs': + fcode = r"\left| {} \right|".format(b) + return fcode + elif a.startswith("\\"): + fcode = r"{}{{({})}}".format(a, b) + return fcode + else: + fcode = r"\mathrm{{{}}}({})".format(a, b) + return fcode + + def neg(self, b): + fcode = "(-{})".format(b) + return fcode + + def add(self, a, b): + fcode = "{} + {}".format(a, b) + return fcode + + def sub(self, a, b): + fcode = "{} - {}".format(a, b) + return fcode + + def mul(self, a, b): + fcode = "{} {}".format(a, b) + return fcode + + def div(self, a, b): + fcode = "{} / {}".format(a, b) + return fcode + + log = lambda self : "\log" + exp = lambda self : "\exp" + sqrt = lambda self : "sqrt" + abs = lambda self : "abs" + rxn_rate = lambda self : "\omega" + udf = lambda self, a : a.value + + + + @v_args(inline=True) # Affects the signatures of the methods class ExpToCode(Transformer): @@ -82,6 +197,9 @@ class ExpToCode(Transformer): def env(self, name): return name.value + def paren(self, name): + return "({})".format(str(name)) + def var(self, name): try: arrname = self.fdict[name.value].array @@ -307,12 +425,13 @@ class Field (FieldBase): self.fluc, self.dep, self.derivs = ExpInspector.inspect(exp) self.comment = self.name + " = " + ExpToCode(self.fdict).transform(self.exp) - self.exporter = None - try: - if self.attr["export"]: - self.exporter = FieldExporter(self.name, self.attr, self) - except KeyError: - pass + self.latex_given = self.attr.get("latex") + if self.latex_given is None: + self.latex = ExpToLatex(self.fdict).transform(self.exp) + else: + self.latex = self.latex_given + + self.exporter = self.attr.get("export") ''' for a in self.dep: @@ -402,6 +521,8 @@ class PrimaryField (FieldBase): super(PrimaryField,self).__init__(name, fdict) self.derivs = set([]) self.prime = True + self.latex = name + self.latex_given = None def code (self, alloc=None): return "! {} is read from file".format(self.name) @@ -425,6 +546,12 @@ class DerivedField (FieldBase): self.v = v self.dep = set([v]) + fmt = r"\partial_{{{}}}" + coord = op[-1] + partial = fmt.format(coord + coord if len(op) > 3 else coord) + + self.latex = partial + "(" + fdict[v].latex + ")" + def code (self, alloc=None): self.array = alloc[self.name] if alloc else self.name varray = alloc[self.v] if alloc else self.v @@ -451,6 +578,8 @@ class AveragedField (FieldBase): tfield = fdict[tgt] self.fset = tfield.checkFluctuation() + self.latex = r"\langle {} \rangle".format(tfield.latex) + if not self.fset: self.tgt = tgt self.dep.add(tgt) @@ -463,6 +592,8 @@ class AveragedField (FieldBase): if w: self.w = fdict[w] self.dep.add(w) + self.latex += ("_{{{}}}".format(w)) + def code (self, alloc=None):