from enum import Enum
from hdlConvertorAst.hdlAst import HdlModuleDec, HdlOp
from hdlConvertorAst.hdlAst._expr import HdlOpType, HdlValueId, HdlValueInt
from hdlConvertorAst.py_ver_compatibility import is_str
from hdlConvertorAst.to.hdlUtils import AutoIndentingStream, iter_with_last
from hdlConvertorAst.to.hdl_ast_visitor import HdlAstVisitor
# https://www.geeksforgeeks.org/operator-precedence-and-associativity-in-c/
# http://www.euroelectronica.ro/7-operators/
# https://gist.github.com/kputnam/5625856
# https://github.com/kaitai-io/kaitai_struct/issues/69
[docs]class ASSOCIATIVITY(Enum):
L_TO_R = "L_TO_R"
R_TO_L = "R_TO_L"
NONE = "NONE"
ASSIGN_OPERATORS_SYMBOLS_C = {
HdlOpType.ASSIGN: ' = ',
HdlOpType.PLUS_ASSIGN: ' += ',
HdlOpType.MINUS_ASSIGN: ' -= ',
HdlOpType.MUL_ASSIGN: ' *= ',
HdlOpType.DIV_ASSIGN: ' /= ',
HdlOpType.MOD_ASSIGN: ' %= ',
HdlOpType.AND_ASSIGN: ' &= ',
HdlOpType.OR_ASSIGN: ' |= ',
HdlOpType.XOR_ASSIGN: ' ^= ',
HdlOpType.SHIFT_LEFT_ASSIGN: ' <<= ',
HdlOpType.SHIFT_RIGHT_ASSIGN: ' >>= ',
}
[docs]class ToHdlCommon(HdlAstVisitor):
INDENT_STEP = " "
ALL_UNARY_OPS = {
getattr(HdlOpType, name) for name in dir(HdlOpType)
if name.endswith("_UNARY")
}
GENERIC_UNARY_OPS = {
HdlOpType.PLUS_UNARY: "+",
HdlOpType.MINUS_UNARY: "-",
}
GENERIC_UNARY_OPS_POSTFIX = {}
GENERIC_BIN_OPS = {
HdlOpType.ADD: " + ",
HdlOpType.SUB: " - ",
HdlOpType.MUL: " * ",
HdlOpType.LT: " < ",
HdlOpType.LE: " <= ",
HdlOpType.GT: " > ",
HdlOpType.GE: " >= ",
HdlOpType.DOT: ".",
}
[docs] def __init__(self, out_stream):
super(ToHdlCommon, self).__init__()
self.out = AutoIndentingStream(out_stream, self.INDENT_STEP)
[docs] def visit_doc(self, obj, line_comment_prefix):
"""
Format doc as line comments
:type line_comment_prefix: str
"""
doc = obj.doc
if doc is not None:
doc = doc.split("\n")
w = self.out.write
for last, d in iter_with_last(doc):
if last and d == "":
break
w(line_comment_prefix)
w(d.replace('\r', ''))
w("\n")
[docs] def _precedence_of_expr(self, o):
"""
:type o: iHdlExpr
"""
# not id or value
if not isinstance(o, HdlOp):
return (-1, ASSOCIATIVITY.NONE, None)
return self.OP_PRECEDENCE[o.fn] + (o.fn,)
[docs] def visit_HdlOp(self, op):
"""
:type op: HdlOp
"""
o = op.fn
w = self.out.write
argc = len(op.ops)
if argc == 1:
op_str = self.GENERIC_UNARY_OPS.get(o, None)
if op_str is not None:
w(op_str)
self._visit_operand(op.ops[0], 0, op, False, False)
return
op_str = self.GENERIC_UNARY_OPS_POSTFIX.get(o, None)
if op_str is not None:
self._visit_operand(op.ops[0], 0, op, False, False)
w(op_str)
return
if argc == 2:
op_str = self.GENERIC_BIN_OPS.get(o, None)
if op_str is not None:
return self._visit_bin_op(op, op_str)
if o == HdlOpType.INDEX:
return self._visit_operator_index(op)
elif o == HdlOpType.CALL or o == HdlOpType.PARAMETRIZATION:
return self.visit_operator_call(op)
else:
raise NotImplementedError(
"Do not know how to convert %s argc:%d" % (o, argc))
[docs] def visit_iHdlExpr(self, o):
"""
:type o: iHdlExpr
"""
w = self.out.write
if isinstance(o, HdlValueId):
w(o.val)
return
elif is_str(o):
w('"%s"' % o.replace("\n", "\\\n"))
return
elif isinstance(o, HdlValueInt):
self.visit_HdlValueInt(o)
return
elif isinstance(o, HdlOp):
self.visit_HdlOp(o)
return
elif isinstance(o, float):
w("%e" % o)
else:
raise NotImplementedError(
"Do not know how to convert %r" % (o))
[docs] def _visit_operand_parentheses_extra_check(
self,
op_my, precedence_my, asoc_my,
op_parent, precedence_parent, asoc_parent,
left, right):
if op_my in self.ALL_UNARY_OPS and op_parent in self.ALL_UNARY_OPS:
return True
return False
[docs] def _visit_operand(self, operand, i,
parent,
expr_requires_parenthesis,
cancel_parenthesis):
"""
:type operand: iHdlExpr
:type i: int
:type parent: HdlOp
:type expr_requires_parenthesis: bool
:type cancel_parenthesis: bool
"""
use_parenthesis = False
if not cancel_parenthesis:
# resolve if the parenthesis are required
precedence_my, asoc_my, op_my = self._precedence_of_expr(operand)
if precedence_my >= 0: # if this is an expression
if expr_requires_parenthesis or asoc_my is ASSOCIATIVITY.NONE:
use_parenthesis = True
else:
precedence_parent, asoc_parent = self.OP_PRECEDENCE[parent.fn]
right = None
left = None
argc = len(parent.ops)
assert argc, parent
if argc == 1:
if asoc_parent == ASSOCIATIVITY.L_TO_R:
# post fix
left = parent.ops[0]
else:
assert asoc_parent == ASSOCIATIVITY.R_TO_L, asoc_parent
right = parent.ops[0]
else:
if i == 0:
right = parent.ops[1]
else:
left = parent.ops[i - 1]
if argc > i + 2:
right = parent.ops[i + 1]
if self._visit_operand_parentheses_extra_check(
op_my, precedence_my, asoc_my, parent.fn,
precedence_parent, asoc_parent, left, right):
use_parenthesis = True
else:
if left is not None: # "operand" is on right side of parent operator
# same precedence -> parenthesis on right (this) if it is expression
# a + (b + c)
# a + b + c = (a + b) + c
# right with lower precedence -> parenthesis for right not required
# a + b * c = a + (b * c)
# right with higher precedence -> parenthesis for right
# a * (b + c)
if precedence_my > precedence_parent:
use_parenthesis = True
elif precedence_my == precedence_parent:
use_parenthesis = argc != 1 or asoc_parent != ASSOCIATIVITY.L_TO_R
if not use_parenthesis and right is not None:
# "operand" is on left side of parent operator
# if op_my == parent.fn:
# right_prec, _, right_op = self._precedence_of_expr(right)
# if right_op == op_my:
# # right and left with same precedence -> parenthesis on both sides
# # (a + b) + (c + d)
# use_parenthesis = True
if precedence_my > precedence_parent:
# left with higher precedence -> parenthesis for left
# (a + b) * c
# a + b + c + d = (a + b) + c + d
# = ((a + b) + c) + d
use_parenthesis = True
w = self.out.write
if use_parenthesis:
w("(")
self.visit_iHdlExpr(operand)
if use_parenthesis:
w(")")
[docs] def _visit_bin_op(self, operator, op_str,
expr_requires_parenthesis=False,
cancel_parenthesis=False):
"""
:type operator: HdlOp
:type op_str: str
"""
op0, op1 = operator.ops
self._visit_operand(op0, 0, operator, expr_requires_parenthesis,
cancel_parenthesis)
self.out.write(op_str)
self._visit_operand(op1, 1, operator, expr_requires_parenthesis,
cancel_parenthesis)
[docs] def _visit_operator_index(self, operator):
"""
:type operator: HdlOp
"""
op0, op1 = operator.ops
self._visit_operand(op0, 0, operator, False, False)
w = self.out.write
w("[")
self._visit_operand(op1, 1, operator, False, True)
w("]")
[docs] def visit_operator_call(self, o):
"""
:type operator: HdlOp
"""
self._visit_operand(o.ops[0], 0, o, False, False)
w = self.out.write
w("(")
for is_last, (o_i, _o) in iter_with_last(enumerate(o.ops[1:])):
self._visit_operand(_o, o_i, o, False, True)
if not is_last:
w(", ")
w(")")
[docs] def visit_HdlFunctionDef(self, o):
"""
:type o: HdlFunctionDef
"""
raise TypeError("does not support HdlFunctionDef", self, o)
[docs] def visit_HdlStmProcess(self, o):
"""
:type proc: HdlStmProcess
"""
raise TypeError("does not support HdlStmProcess", self, o)
[docs] def visit_HdlStmBlock(self, o):
"""
:type o: HdlStmBlock
"""
raise TypeError("does not support HdlStmBlock", self, o)
[docs] def visit_HdlStmIf(self, o):
"""
:type o: HdlStmIf
"""
raise TypeError("does not support HdlStmIf", self, o)
[docs] def visit_HdlStmCase(self, o):
"""
:type o: HdlStmCase
"""
raise TypeError("does not support HdlStmCase", self, o)
[docs] def visit_HdlStmWait(self, o):
"""
:type o: HdlStmWait
"""
raise TypeError("does not support HdlStmWait", self, o)
[docs] def visit_HdlStmFor(self, o):
"""
:type o: HdlStmFor
"""
raise TypeError("does not support HdlStmFor", self, o)
[docs] def visit_HdlStmForIn(self, o):
"""
:type o: HdlStmForIn
"""
raise TypeError("does not support HdlStmForIn", self, o)
[docs] def visit_HdlStmWhile(self, o):
"""
:type o: HdlStmWhile
"""
raise TypeError("does not support HdlStmWhile", self, o)
[docs] def visit_HdlStmAssign(self, o):
"""
:type o: HdlStmAssign
"""
raise TypeError("does not support HdlStmAssign", self, o)
[docs] def visit_HdlStmRepeat(self, o):
"""
:type o: HdlStmRepeat
"""
raise TypeError("does not support HdlStmRepeat", self, o)
[docs] def visit_HdlStmReturn(self, o):
"""
:type o: HdlStmReturn
"""
raise TypeError("does not support HdlStmReturn", self, o)
[docs] def visit_HdlStmContinue(self, o):
"""
:type o: HdlStmContinue
"""
raise TypeError("does not support HdlStmContinue", self, o)
[docs] def visit_HdlStmBreak(self, o):
"""
:type o: HdlStmBreak
"""
raise TypeError("does not support HdlStmBreak", self, o)