def _print_Function(self, e): """Print a Function object. :param e: The expression. :rtype : bce.dom.mathml.all.Base :return: The printed MathML object. """ assert isinstance(e, _sympy.Function) # Check the function. fn_object = _mexp_function.find_sympy_function(e.func.__name__) if fn_object is None: raise RuntimeError("Unsupported function: \"%s\"." % e.func.__name__) if fn_object.get_argument_count() != len(e.args): raise RuntimeError("Argument count mismatch.") # Build the node. node = _mathml.RowComponent() node.append_object(_mathml.TextComponent( fn_object.get_function_name())) node.append_object( _mathml.OperatorComponent(_mathml.OPERATOR_LEFT_PARENTHESIS)) for arg_id in range(0, len(e.args)): arg_value = e.args[arg_id] node.append_object(self.doprint(arg_value)) if arg_id + 1 != len(e.args): node.append_object( _mathml.OperatorComponent(_mathml.OPERATOR_SEPARATOR)) node.append_object( _mathml.OperatorComponent(_mathml.OPERATOR_RIGHT_PARENTHESIS)) return node
def _print_super_electronic( charge, mexp_parser, mexp_protected_header_enabled=False, mexp_protected_header_prefix="X" ): """Print electronic charge value. :type mexp_parser: bce.parser.interface.mexp_parser.MathExpressionParserInterface :type mexp_protected_header_enabled: bool :type mexp_protected_header_prefix: str :param charge: The charge number. :param mexp_parser: The math expression parser. :param mexp_protected_header_enabled: Whether the MEXP protected headers are enabled. :param mexp_protected_header_prefix: The prefix of the MEXP protected headers. :rtype : bce.dom.mathml.all.Base :return: The printed MathML node. """ # Print the positivity part. if charge.is_negative: charge = -charge positivity = _mathml.OperatorComponent(_mathml.OPERATOR_MINUS) else: positivity = _mathml.OperatorComponent(_mathml.OPERATOR_PLUS) # Simplify. charge = charge.simplify() if charge == _math_constant.ONE: return positivity else: # Initialize a row component to contain the printing result. r = _mathml.RowComponent() # Print the charge part. r.append_object(_print_operand( charge, True, mexp_parser, mexp_protected_header_enabled=mexp_protected_header_enabled, mexp_protected_header_prefix=mexp_protected_header_prefix )) # Add the positivity flag. r.append_object(positivity) return r
def _print_operand( value, need_wrapping, mexp_parser, mexp_protected_header_enabled=False, mexp_protected_header_prefix="X" ): """Print an operand. :type need_wrapping: bool :type mexp_parser: bce.parser.interface.mexp_parser.MathExpressionParserInterface :type mexp_protected_header_enabled: bool :type mexp_protected_header_prefix: str :param value: The operand value. :param need_wrapping: Set to True if you need to wrap the expression when it is neither an integer nor a symbol. :param mexp_parser: The math expression parser. :param mexp_protected_header_enabled: Whether the MEXP protected headers are enabled. :param mexp_protected_header_prefix: The prefix of the MEXP protected headers. :rtype : bce.dom.mathml.all.Base :return: The printed MathML node. """ # Simplify. value = value.simplify() if value.is_Integer: return _mathml.NumberComponent(str(value)) else: if need_wrapping and not (value.is_Integer or value.is_Symbol): # Use a pair of parentheses to wrap the printed expression. r = _mathml.RowComponent() r.append_object(_mathml.OperatorComponent(_mathml.OPERATOR_LEFT_PARENTHESIS)) r.append_object(mexp_parser.print_out( value, printer_type=_interface_printer.PRINTER_TYPE_MATHML, protected_header_enabled=mexp_protected_header_enabled, protected_header_prefix=mexp_protected_header_prefix )) r.append_object(_mathml.OperatorComponent(_mathml.OPERATOR_RIGHT_PARENTHESIS)) return r else: return mexp_parser.print_out( value, printer_type=_interface_printer.PRINTER_TYPE_MATHML, protected_header_enabled=mexp_protected_header_enabled, protected_header_prefix=mexp_protected_header_prefix )
def print_ast( root_node, mexp_parser, mexp_protected_header_enabled=False, mexp_protected_header_prefix="X" ): """Print an AST to BCE expression. :type root_node: bce.parser.ast.molecule.ASTNodeHydrateGroup | bce.parser.ast.molecule.ASTNodeMolecule :type mexp_parser: bce.parser.interface.mexp_parser.MathExpressionParserInterface :type mexp_protected_header_enabled: bool :type mexp_protected_header_prefix: str :param root_node: The root node of the AST. :param mexp_parser: The math expression parser. :param mexp_protected_header_enabled: Whether the MEXP protected headers are enabled. :param mexp_protected_header_prefix: The prefix of the MEXP protected headers. :rtype : bce.dom.mathml.all.Base :return: The printed expression. """ # Get the printing order. work_order = _ml_ast_bfs.do_bfs(root_node, True) # Initialize the printed result container. printed = {} for work_node in work_order: if work_node.is_hydrate_group(): assert isinstance(work_node, _ml_ast_base.ASTNodeHydrateGroup) # Initialize a row component to contain the printing result. build = _mathml.RowComponent() # Print the prefix number part. pfx = work_node.get_prefix_number().simplify() if pfx != _math_constant.ONE: build.append_object(_print_operand( pfx, True, mexp_parser, mexp_protected_header_enabled=mexp_protected_header_enabled, mexp_protected_header_prefix=mexp_protected_header_prefix )) build.append_object(_mathml.OperatorComponent(_mathml.OPERATOR_LEFT_PARENTHESIS)) surround = True else: surround = False # Print children nodes. build.append_object(printed[id(work_node[0])]) for child_id in range(1, len(work_node)): build.append_object(_mathml.OperatorComponent(_mathml.OPERATOR_DOT)) build.append_object(printed[id(work_node[child_id])]) # Complete the surrounding parentheses if the flag was marked. if surround: build.append_object(_mathml.OperatorComponent(_mathml.OPERATOR_RIGHT_PARENTHESIS)) # Save printing result. printed[id(work_node)] = build elif work_node.is_molecule(): assert isinstance(work_node, _ml_ast_base.ASTNodeMolecule) # Initialize a row component to contain the printing result. build = _mathml.RowComponent() # Print the prefix number part. pfx = work_node.get_prefix_number().simplify() if pfx != _math_constant.ONE: build.append_object(_print_operand( pfx, True, mexp_parser, mexp_protected_header_enabled=mexp_protected_header_enabled, mexp_protected_header_prefix=mexp_protected_header_prefix )) # Print children nodes. for child_id in range(0, len(work_node)): build.append_object(printed[id(work_node[child_id])]) el_charge = work_node.get_electronic_count().simplify() if not el_charge.is_zero: if len(work_node) == 0: build.append_object(_mathml.SuperComponent( _mathml.TextComponent("e"), _print_super_electronic( el_charge, mexp_parser, mexp_protected_header_enabled=mexp_protected_header_enabled, mexp_protected_header_prefix=mexp_protected_header_prefix ) )) else: # Find the innermost row component. innermost = build while innermost[-1].is_row(): innermost = innermost[-1] # Fetch the last item. last_item = innermost[-1] # Add the electronic. if last_item.is_sub(): assert isinstance(last_item, _mathml.SubComponent) last_item = _mathml.SubAndSuperComponent( last_item.get_main_object(), last_item.get_sub_object(), _print_super_electronic( el_charge, mexp_parser, mexp_protected_header_enabled=mexp_protected_header_enabled, mexp_protected_header_prefix=mexp_protected_header_prefix ) ) else: last_item = _mathml.SuperComponent( last_item, _print_super_electronic( el_charge, mexp_parser, mexp_protected_header_enabled=mexp_protected_header_enabled, mexp_protected_header_prefix=mexp_protected_header_prefix ) ) # Save the modified item. innermost[-1] = last_item # Save printing result. printed[id(work_node)] = build elif work_node.is_atom(): assert isinstance(work_node, _ml_ast_base.ASTNodeAtom) # Print and save the result. printed[id(work_node)] = _print_suffix( _mathml.TextComponent(work_node.get_atom_symbol()), work_node, mexp_parser, mexp_protected_header_enabled=mexp_protected_header_enabled, mexp_protected_header_prefix=mexp_protected_header_prefix ) elif work_node.is_parenthesis(): assert isinstance(work_node, _ml_ast_base.ASTNodeParenthesisWrapper) # Initialize a row component to contain the printing result. build = _mathml.RowComponent() # Print. build.append_object(_mathml.OperatorComponent(_mathml.OPERATOR_LEFT_PARENTHESIS)) build.append_object(printed[id(work_node.get_inner_node())]) build.append_object(_print_suffix( _mathml.OperatorComponent(_mathml.OPERATOR_RIGHT_PARENTHESIS), work_node, mexp_parser, mexp_protected_header_enabled=mexp_protected_header_enabled, mexp_protected_header_prefix=mexp_protected_header_prefix )) # Save printing result. printed[id(work_node)] = build elif work_node.is_abbreviation(): assert isinstance(work_node, _ml_ast_base.ASTNodeAbbreviation) # Print and save the result. printed[id(work_node)] = _print_suffix( _mathml.TextComponent("[%s]" % work_node.get_abbreviation_symbol()), work_node, mexp_parser, mexp_protected_header_enabled=mexp_protected_header_enabled, mexp_protected_header_prefix=mexp_protected_header_prefix ) else: raise RuntimeError("BUG: Unhandled AST node type.") # Post process - add status. post_process = printed[id(root_node)] if root_node.get_status() is not None: if not post_process.is_row(): tmp = _mathml.RowComponent() tmp.append_object(post_process) post_process = tmp post_process.append_object(_mathml.OperatorComponent(_mathml.OPERATOR_LEFT_PARENTHESIS)) if root_node.get_status() == _ml_ast_base.STATUS_GAS: post_process.append_object(_mathml.TextComponent("g")) elif root_node.get_status() == _ml_ast_base.STATUS_LIQUID: post_process.append_object(_mathml.TextComponent("l")) elif root_node.get_status() == _ml_ast_base.STATUS_SOLID: post_process.append_object(_mathml.TextComponent("s")) elif root_node.get_status() == _ml_ast_base.STATUS_AQUEOUS: post_process.append_object(_mathml.TextComponent("aq")) else: raise RuntimeError("BUG: No such status.") post_process.append_object(_mathml.OperatorComponent(_mathml.OPERATOR_RIGHT_PARENTHESIS)) return printed[id(root_node)]
def print_cexp(cexp_object, molecule_parser, mexp_parser, mexp_protected_header_enabled=False, mexp_protected_header_prefix="X"): """Print the chemical equation. :type cexp_object: bce.parser.interface.cexp_parser.ChemicalEquation :type molecule_parser: bce.parser.interface.molecule_parser.MoleculeParserInterface :type mexp_parser: bce.parser.interface.mexp_parser.MathExpressionParserInterface :type mexp_protected_header_enabled: bool :type mexp_protected_header_prefix: str :param cexp_object: The chemical equation object. :param molecule_parser: The molecule parser. :param mexp_parser: The math expression parser. :param mexp_protected_header_enabled: Whether the MEXP protected headers are enabled. :param mexp_protected_header_prefix: The prefix of the MEXP protected headers. :rtype : bce.dom.mathml.all.Base :return: The printed MathML. """ assert cexp_object.get_left_item_count( ) != 0 and cexp_object.get_right_item_count() != 0 # Initialize an empty CE expression. r = _mathml.RowComponent() # Process items on left side. for idx in range(0, cexp_object.get_left_item_count()): # Get the item. item = cexp_object.get_left_item(idx) # Insert operator. if item.is_operator_plus(): if len(r) != 0: r.append_object( _mathml.OperatorComponent(_mathml.OPERATOR_PLUS)) if item.is_operator_minus(): r.append_object(_mathml.OperatorComponent(_mathml.OPERATOR_MINUS)) # Get the AST root node. ast_root = item.get_molecule_ast() # Backup the prefix number. origin_coefficient = ast_root.get_prefix_number() # Set the prefix to the balanced coefficient. ast_root.set_prefix_number(item.get_coefficient() * origin_coefficient) # Print the molecule. r.append_object( molecule_parser.print_out( ast_root, mexp_parser, mexp_protected_header_enabled=mexp_protected_header_enabled, mexp_protected_header_prefix=mexp_protected_header_prefix, printer_type=_interface_printer.PRINTER_TYPE_MATHML)) # Restore the prefix number. ast_root.set_prefix_number(origin_coefficient) # Insert '='. r.append_object(_mathml.OperatorComponent(_mathml.OPERATOR_EQUAL)) # Mark whether processing molecule is the first molecule on right side. r_is_first = True # Process items on right side. for idx in range(0, cexp_object.get_right_item_count()): # Get the item. item = cexp_object.get_right_item(idx) # Insert operator. if item.is_operator_plus(): if not r_is_first: r.append_object( _mathml.OperatorComponent(_mathml.OPERATOR_PLUS)) if item.is_operator_minus(): r.append_object(_mathml.OperatorComponent(_mathml.OPERATOR_MINUS)) # Get the AST root node. ast_root = item.get_molecule_ast() # Backup the prefix number. origin_coefficient = ast_root.get_prefix_number() # Set the prefix to the balanced coefficient. ast_root.set_prefix_number(item.get_coefficient() * origin_coefficient) # Print the molecule. r.append_object( molecule_parser.print_out( ast_root, mexp_parser, mexp_protected_header_enabled=mexp_protected_header_enabled, mexp_protected_header_prefix=mexp_protected_header_prefix, printer_type=_interface_printer.PRINTER_TYPE_MATHML)) # Restore the prefix number. ast_root.set_prefix_number(origin_coefficient) # Switch off the mark. r_is_first = False return r
def _print_Mul(self, expr): """Print a Mul object. :param expr: The expression. :rtype : bce.dom.mathml.all.Base :return: The printed MathML object. """ assert isinstance(expr, _sympy.Mul) # noinspection PyProtectedMember if _coeff_isneg(expr): x = _mathml.RowComponent() x.append_object(_mathml.OperatorComponent(_mathml.OPERATOR_MINUS)) x.append_object(self._print(-expr)) return x PREC = _sympy_precedence.precedence(expr) from sympy.simplify import fraction numer, denom = fraction(expr) if denom is not _sympy.S.One: return _mathml.FractionComponent(self._print(numer), self._print(denom)) coeff, terms = expr.as_coeff_mul() if coeff is _sympy.S.One and len(terms) == 1: # Since the negative coefficient has been handled, I don't # thing a coeff of 1 can remain if _sympy_precedence.precedence(terms[0]) < PREC: # Return the argument with parentheses around. tmp_node = _mathml.RowComponent() tmp_node.append_object( _mathml.OperatorComponent( _mathml.OPERATOR_LEFT_PARENTHESIS)) tmp_node.append_object(self._print(terms[0])) tmp_node.append_object( _mathml.OperatorComponent( _mathml.OPERATOR_RIGHT_PARENTHESIS)) return tmp_node else: # Return the argument only. return self._print(terms[0]) if self.order != "old": # noinspection PyProtectedMember terms = _sympy.Mul._from_args(terms).as_ordered_factors() # Build result row element(node). x = _mathml.RowComponent() if coeff != 1: if _sympy_precedence.precedence(coeff) < PREC: # Insert the coefficient number with parentheses around. x.append_object( _mathml.OperatorComponent( _mathml.OPERATOR_LEFT_PARENTHESIS)) x.append_object(self._print(coeff)) x.append_object( _mathml.OperatorComponent( _mathml.OPERATOR_RIGHT_PARENTHESIS)) else: # Insert the coefficient number only. x.append_object(self._print(coeff)) # Insert a multiply operator. if not terms[0].is_Symbol: x.append_object( _mathml.OperatorComponent(_mathml.OPERATOR_MULTIPLY)) terms_len = len(terms) for term_id in range(0, terms_len): cur_term = terms[term_id] if _sympy_precedence.precedence(cur_term) < PREC: x.append_object( _mathml.OperatorComponent( _mathml.OPERATOR_LEFT_PARENTHESIS)) x.append_object(self._print(cur_term)) x.append_object( _mathml.OperatorComponent( _mathml.OPERATOR_RIGHT_PARENTHESIS)) else: x.append_object(self._print(cur_term)) if term_id + 1 != terms_len and not cur_term.is_Symbol: x.append_object( _mathml.OperatorComponent(_mathml.OPERATOR_MULTIPLY)) return x
def _print_Pow(self, e): """Print a Pow object. :param e: The expression. :rtype : bce.dom.mathml.all.Base :return: The printed MathML object. """ assert isinstance(e, _sympy.Pow) PREC = _sympy_precedence.precedence(e) if e.exp.is_Rational and e.exp.p == 1: # If the exponent is like {1/x}, do SQRT operation if x is 2, otherwise, do # root operation. printed_base = self._print(e.base) if e.exp.q != 2: # Do root operation. root = _mathml.RootComponent( printed_base, _mathml.NumberComponent(str(e.exp.q))) else: # Do SQRT operation. root = _mathml.SquareRootComponent(printed_base) return root if e.exp.is_negative: if e.exp.is_Integer and e.exp == _sympy.Integer(-1): final_node = _mathml.FractionComponent( _mathml.NumberComponent("1"), self._print(e.base)) else: # frac{1, base ^ |exp|} neg_exp = -e.exp # Get node for the base. if _sympy_precedence.precedence(e.base) < PREC: base_node = _mathml.RowComponent() base_node.append_object( _mathml.OperatorComponent( _mathml.OPERATOR_LEFT_PARENTHESIS)) base_node.append_object(self._print(e.base)) base_node.append_object( _mathml.OperatorComponent( _mathml.OPERATOR_RIGHT_PARENTHESIS)) else: base_node = self._print(e.base) # Get node for the exponent. if _sympy_precedence.precedence(neg_exp) < PREC: exp_node = _mathml.RowComponent() exp_node.append_object( _mathml.OperatorComponent( _mathml.OPERATOR_LEFT_PARENTHESIS)) exp_node.append_object(self._print(neg_exp)) exp_node.append_object( _mathml.OperatorComponent( _mathml.OPERATOR_RIGHT_PARENTHESIS)) else: exp_node = neg_exp final_node = _mathml.FractionComponent( _mathml.NumberComponent("1"), _mathml.SuperComponent(base_node, exp_node)) return final_node # Get node for the base. if _sympy_precedence.precedence(e.base) < PREC: base_node = _mathml.RowComponent() base_node.append_object( _mathml.OperatorComponent(_mathml.OPERATOR_LEFT_PARENTHESIS)) base_node.append_object(self._print(e.base)) base_node.append_object( _mathml.OperatorComponent(_mathml.OPERATOR_RIGHT_PARENTHESIS)) else: base_node = self._print(e.base) return _mathml.SuperComponent(base_node, self._print(e.exp))
def _print_Add(self, expr, order=None): """Print a Add object. :param expr: The expression. :rtype : bce.dom.mathml.all.Base :return: The printed MathML object. """ assert isinstance(expr, _sympy.Add) args = self._as_ordered_terms(expr, order=order) PREC = _sympy_precedence.precedence(expr) dt = _mathml.RowComponent() args_len = len(args) # Iterator each part. for arg_id in range(0, args_len): cur_arg = args[arg_id] if cur_arg.is_negative: # Get the negative number. neg_arg = -cur_arg # Get the precedence. CUR_PREC = _sympy_precedence.precedence(neg_arg) # Add a '-' operator. dt.append_object( _mathml.OperatorComponent(_mathml.OPERATOR_MINUS)) # noinspection PyProtectedMember if CUR_PREC < PREC or (_coeff_isneg(neg_arg) and arg_id != 0): # Insert the argument with parentheses around. dt.append_object( _mathml.OperatorComponent( _mathml.OPERATOR_LEFT_PARENTHESIS)) dt.append_object(self._print(neg_arg)) dt.append_object( _mathml.OperatorComponent( _mathml.OPERATOR_RIGHT_PARENTHESIS)) else: # Insert the argument only. dt.append_object(self._print(neg_arg)) else: # Add a '+' operator if the argument is not the first one. if arg_id != 0: dt.append_object( _mathml.OperatorComponent(_mathml.OPERATOR_PLUS)) # Get the precedence. CUR_PREC = _sympy_precedence.precedence(cur_arg) # noinspection PyProtectedMember if CUR_PREC < PREC or (_coeff_isneg(cur_arg) and arg_id != 0): # Insert the argument with parentheses around. dt.append_object( _mathml.OperatorComponent( _mathml.OPERATOR_LEFT_PARENTHESIS)) dt.append_object(self._print(cur_arg)) dt.append_object( _mathml.OperatorComponent( _mathml.OPERATOR_RIGHT_PARENTHESIS)) else: # Insert the argument only. dt.append_object(self._print(cur_arg)) return dt