|
8 | 8 | we can use ``Row``
|
9 | 9 |
|
10 | 10 | """
|
11 |
| - |
| 11 | +from typing import Optional, Union |
12 | 12 |
|
13 | 13 | from mathics.builtin.base import BinaryOperator, Builtin, Operator
|
14 | 14 | from mathics.builtin.box.layout import GridBox, RowBox, to_boxes
|
15 | 15 | from mathics.builtin.lists import list_boxes
|
16 | 16 | from mathics.builtin.makeboxes import MakeBoxes
|
17 | 17 | from mathics.builtin.options import options_to_rules
|
18 |
| -from mathics.core.atoms import Real, String |
| 18 | +from mathics.core.atoms import Integer, Integer1, Real, String |
| 19 | +from mathics.core.convert.op import operator_to_ascii, operator_to_unicode |
| 20 | +from mathics.core.element import BaseElement |
19 | 21 | from mathics.core.expression import Expression
|
20 | 22 | from mathics.core.list import ListExpression
|
21 |
| -from mathics.core.symbols import Symbol |
22 |
| -from mathics.core.systemsymbols import SymbolMakeBoxes |
23 |
| -from mathics.eval.makeboxes import format_element |
24 |
| - |
| 23 | +from mathics.core.symbols import Atom, Symbol |
| 24 | +from mathics.core.systemsymbols import ( |
| 25 | + SymbolFullForm, |
| 26 | + SymbolInfix, |
| 27 | + SymbolInputForm, |
| 28 | + SymbolLeft, |
| 29 | + SymbolMakeBoxes, |
| 30 | + SymbolNone, |
| 31 | + SymbolOutputForm, |
| 32 | + SymbolRight, |
| 33 | +) |
| 34 | +from mathics.eval.makeboxes import format_element, make_boxes_infix |
| 35 | + |
| 36 | +SymbolNonAssociative = Symbol("System`NonAssociative") |
25 | 37 | SymbolSubscriptBox = Symbol("System`SubscriptBox")
|
26 | 38 |
|
27 | 39 |
|
@@ -145,11 +157,178 @@ class Infix(Builtin):
|
145 | 157 | """
|
146 | 158 |
|
147 | 159 | messages = {
|
148 |
| - "normal": "Nonatomic expression expected at position `1`", |
| 160 | + "argb": "Infix called with `1` arguments; between 1 and 4 arguments are expected.", |
| 161 | + "group": "Infix::group: Grouping specification `1` is not NonAssociative, None, Left, or Right.", |
149 | 162 | "intm": "Machine-sized integer expected at position 3 in `1`",
|
| 163 | + "normal": "Nonatomic expression expected at position `1`", |
150 | 164 | }
|
151 | 165 | summary_text = "infix form"
|
152 | 166 |
|
| 167 | + # the right rule should be |
| 168 | + # mbexpression:MakeBoxes[Infix[___], form] |
| 169 | + |
| 170 | + def eval_infix_1(self, expr: Expression, form: Symbol, evaluation): |
| 171 | + """MakeBoxes[Infix[expr_], |
| 172 | + form:StandardForm|TraditionalForm|OutputForm|InputForm]""" |
| 173 | + return self.do_eval_infix(expr, String("~"), form, evaluation) |
| 174 | + |
| 175 | + def eval_infix_2( |
| 176 | + self, expr: Expression, operator: BaseElement, form: Symbol, evaluation |
| 177 | + ): |
| 178 | + """MakeBoxes[Infix[expr_, operator_], |
| 179 | + form:StandardForm|TraditionalForm|OutputForm|InputForm]""" |
| 180 | + return self.do_eval_infix(expr, operator, form, evaluation) |
| 181 | + |
| 182 | + def eval_infix_3( |
| 183 | + self, |
| 184 | + expr: Expression, |
| 185 | + operator: BaseElement, |
| 186 | + precedence: BaseElement, |
| 187 | + form: Symbol, |
| 188 | + evaluation, |
| 189 | + ): |
| 190 | + """MakeBoxes[Infix[expr_, operator_, precedence_], |
| 191 | + form:StandardForm|TraditionalForm|OutputForm|InputForm]""" |
| 192 | + |
| 193 | + if not isinstance(precedence, Integer): |
| 194 | + evaluation.message( |
| 195 | + "Infix", |
| 196 | + "intm", |
| 197 | + Expression( |
| 198 | + SymbolFullForm, Expression(SymbolInfix, expr, operator, precedence) |
| 199 | + ), |
| 200 | + ) |
| 201 | + return |
| 202 | + |
| 203 | + return self.do_eval_infix(expr, operator, form, evaluation, precedence.value) |
| 204 | + |
| 205 | + def eval_infix_4( |
| 206 | + self, |
| 207 | + expr: Expression, |
| 208 | + operator: BaseElement, |
| 209 | + precedence: BaseElement, |
| 210 | + grouping: BaseElement, |
| 211 | + form: Symbol, |
| 212 | + evaluation, |
| 213 | + ): |
| 214 | + """MakeBoxes[Infix[expr_, operator_, precedence_, grouping_], |
| 215 | + form:StandardForm|TraditionalForm|OutputForm|InputForm]""" |
| 216 | + if not isinstance(precedence, Integer): |
| 217 | + fullform_expr = Expression( |
| 218 | + SymbolFullForm, |
| 219 | + Expression(SymbolInfix, expr, operator, precedence, grouping), |
| 220 | + ) |
| 221 | + evaluation.message( |
| 222 | + "Infix", |
| 223 | + "intm", |
| 224 | + Expression( |
| 225 | + SymbolFullForm, |
| 226 | + Expression(SymbolInfix, expr, operator, precedence, grouping), |
| 227 | + ), |
| 228 | + ) |
| 229 | + return fullform_expr |
| 230 | + |
| 231 | + if grouping is SymbolNone: |
| 232 | + return self.do_eval_infix( |
| 233 | + expr, operator, form, evaluation, precedence.value |
| 234 | + ) |
| 235 | + if grouping in (SymbolNonAssociative, SymbolLeft, SymbolRight): |
| 236 | + return self.do_eval_infix( |
| 237 | + expr, operator, form, evaluation, precedence.value, grouping.get_name() |
| 238 | + ) |
| 239 | + |
| 240 | + evaluation.message("Infix", "argb", grouping) |
| 241 | + return Expression( |
| 242 | + SymbolFullForm, |
| 243 | + Expression(SymbolInfix, expr, operator, precedence, grouping), |
| 244 | + ) |
| 245 | + |
| 246 | + def eval_infix_default(self, parms, form: Symbol, evaluation): |
| 247 | + """MakeBoxes[Infix[parms___], |
| 248 | + form:StandardForm|TraditionalForm|OutputForm|InputForm]""" |
| 249 | + evaluation.message("Infix", "argb", Integer(len(parms.get_sequence()))) |
| 250 | + return None |
| 251 | + |
| 252 | + def do_eval_infix( |
| 253 | + self, |
| 254 | + expr: Expression, |
| 255 | + operator: BaseElement, |
| 256 | + form: Symbol, |
| 257 | + evaluation, |
| 258 | + precedence_value: Optional[int] = None, |
| 259 | + grouping: Optional[str] = None, |
| 260 | + ): |
| 261 | + """Implements MakeBoxes[Infix[...]]""" |
| 262 | + |
| 263 | + ## FIXME: this should go into a some formatter. |
| 264 | + def format_operator(operator) -> Union[String, BaseElement]: |
| 265 | + """ |
| 266 | + Format infix operator `operator`. To do this outside parameter form is used. |
| 267 | + Sometimes no changes are made and operator is returned unchanged. |
| 268 | +
|
| 269 | + This function probably should be rewritten be more scalable across other forms |
| 270 | + and moved to a module that contiaing similar formatting routines. |
| 271 | + """ |
| 272 | + if not isinstance(operator, String): |
| 273 | + return MakeBoxes(operator, form) |
| 274 | + |
| 275 | + op_str = operator.value |
| 276 | + |
| 277 | + # FIXME: performing a check using the operator symbol representation feels a bit |
| 278 | + # fragile. The operator name seems more straightforward and more robust. |
| 279 | + if form == SymbolInputForm and op_str in ["*", "^", " "]: |
| 280 | + return operator |
| 281 | + elif ( |
| 282 | + form in (SymbolInputForm, SymbolOutputForm) |
| 283 | + and not op_str.startswith(" ") |
| 284 | + and not op_str.endswith(" ") |
| 285 | + ): |
| 286 | + # FIXME: Again, testing on specific forms is fragile and not scalable. |
| 287 | + op = String(" " + op_str + " ") |
| 288 | + return op |
| 289 | + return operator |
| 290 | + |
| 291 | + if isinstance(expr, Atom): |
| 292 | + evaluation.message("Infix", "normal", Integer1) |
| 293 | + return None |
| 294 | + |
| 295 | + elements = expr.elements |
| 296 | + if len(elements) > 1: |
| 297 | + if operator.has_form("List", len(elements) - 1): |
| 298 | + operator = [format_operator(op) for op in operator.elements] |
| 299 | + return make_boxes_infix( |
| 300 | + elements, operator, precedence_value, grouping, form |
| 301 | + ) |
| 302 | + else: |
| 303 | + encoding_rule = evaluation.definitions.get_ownvalue( |
| 304 | + "$CharacterEncoding" |
| 305 | + ) |
| 306 | + encoding = ( |
| 307 | + "UTF8" if encoding_rule is None else encoding_rule.replace.value |
| 308 | + ) |
| 309 | + op_str = ( |
| 310 | + operator.value |
| 311 | + if isinstance(operator, String) |
| 312 | + else operator.short_name |
| 313 | + ) |
| 314 | + if encoding == "ASCII": |
| 315 | + operator = format_operator( |
| 316 | + String(operator_to_ascii.get(op_str, op_str)) |
| 317 | + ) |
| 318 | + else: |
| 319 | + operator = format_operator( |
| 320 | + String(operator_to_unicode.get(op_str, op_str)) |
| 321 | + ) |
| 322 | + |
| 323 | + return make_boxes_infix( |
| 324 | + elements, operator, precedence_value, grouping, form |
| 325 | + ) |
| 326 | + |
| 327 | + elif len(elements) == 1: |
| 328 | + return MakeBoxes(elements[0], form) |
| 329 | + else: |
| 330 | + return MakeBoxes(expr, form) |
| 331 | + |
153 | 332 |
|
154 | 333 | class Left(Builtin):
|
155 | 334 | """
|
|
0 commit comments