Skip to content

Commit e5d1bdb

Browse files
committed
Revise API for position tracking
1 parent 620049d commit e5d1bdb

20 files changed

Lines changed: 198 additions & 48 deletions

File tree

examples/symbolic_logic/gries_schneider/test_gs.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env python3
22
# -*- coding: utf-8 -*-
33

4+
from mathics_scanner.location import ContainerKind
45

56
from mathics.core.definitions import Definitions
67
from mathics.core.evaluation import Evaluation
@@ -13,5 +14,8 @@
1314
for i in range(0, 4):
1415
evaluation = Evaluation(definitions=definitions, catch_interrupt=False)
1516

16-
expr = parse(definitions, MathicsSingleLineFeeder(f"<< GS{i}.m"))
17+
expr = parse(
18+
definitions,
19+
MathicsSingleLineFeeder(f"<< GS{i}.m", "<test_gs>", ContainerKind.STRING),
20+
)
1721
expr.evaluate(evaluation)

mathics/builtin/testing_expressions/string_tests.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import re
66

77
from mathics_scanner import SingleLineFeeder, SyntaxError
8+
from mathics_scanner.location import ContainerKind
89

910
from mathics.builtin.atomic.strings import anchor_pattern
1011
from mathics.core.atoms import Integer1, String
@@ -278,7 +279,7 @@ def eval(self, string, evaluation: Evaluation):
278279
)
279280
return
280281

281-
feeder = SingleLineFeeder(string.value)
282+
feeder = SingleLineFeeder(string.value, "<SyntaxQ>", ContainerKind.STRING)
282283
try:
283284
parser.parse(feeder)
284285
except SyntaxError:

mathics/builtin/trace.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,24 @@
2222
from time import time
2323
from typing import Callable
2424

25+
import mathics_scanner.location
26+
2527
import mathics.eval.tracing
2628
from mathics.core.attributes import A_HOLD_ALL, A_HOLD_ALL_COMPLETE, A_PROTECTED
27-
from mathics.core.builtin import Builtin
29+
from mathics.core.builtin import Builtin, Predefined
2830
from mathics.core.convert.python import from_bool, from_python
2931
from mathics.core.definitions import Definitions
3032
from mathics.core.evaluation import Evaluation
3133
from mathics.core.expression import Expression
3234
from mathics.core.list import ListExpression
3335
from mathics.core.rules import FunctionApplyRule
34-
from mathics.core.symbols import SymbolFalse, SymbolNull, SymbolTrue, strip_context
36+
from mathics.core.symbols import (
37+
Symbol,
38+
SymbolFalse,
39+
SymbolNull,
40+
SymbolTrue,
41+
strip_context,
42+
)
3543

3644

3745
def traced_apply_function(
@@ -493,3 +501,33 @@ def eval(self, expr: Expression, evaluation: Evaluation):
493501
else:
494502
result = expr.evaluate(evaluation)
495503
return ListExpression(result, profile_result)
504+
505+
506+
class TrackLocations(Predefined):
507+
r"""## <url>:TrackLocations native symbol:</url>
508+
509+
<dl>
510+
<dt>'$TrackLocations'
511+
<dd>specifies whether we should track \
512+
source-text location information during evaluation. This \
513+
can be helpful in debugging when there is a failure.
514+
</dl>
515+
"""
516+
517+
name = "$TrackLocations"
518+
messages = {"bool": "`1` should be True or False."}
519+
520+
summary_text = "track source-text locations in evaluation"
521+
522+
def evaluate(self, evaluation: Evaluation) -> Symbol:
523+
print(mathics_scanner.position.MATHICS3_PATHS)
524+
return from_bool(mathics_scanner.position.TRACK_LOCATIONS)
525+
526+
def eval_set(self, value, evaluation):
527+
"""Set[$TrackLocations, value_]"""
528+
if value is SymbolTrue or value is SymbolFalse:
529+
evaluation.definitions.set_ownvalue("System`$TrackLocations", value)
530+
mathics.core.parser.parser.TRACK_LOCATIONS = value.to_python()
531+
else:
532+
evaluation.message("$TrackLocations", "bool", value)
533+
return value

mathics/core/builtin.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,15 @@ def get_functions(self, prefix="eval", is_pymodule=False):
484484
definition_class = (
485485
PyMathicsDefinitions() if is_pymodule else SystemDefinitions()
486486
)
487-
pattern = parse_builtin_rule(pattern, definition_class)
487+
488+
# Passing the function parameter is in a way
489+
# redundant, because creating FunctionApplyRule has
490+
# access to the function and sets the postion this
491+
# way. But revised afte the dust has settled and
492+
# we have a very good idea of what is desirable and useful.
493+
pattern = parse_builtin_rule(
494+
pattern, definition_class, location=function
495+
)
488496
if unavailable_function:
489497
function = unavailable_function
490498
if attrs:

mathics/core/definitions.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
from mathics.core.attributes import A_NO_ATTRIBUTES
1919
from mathics.core.convert.expression import to_mathics_list
2020
from mathics.core.element import BaseElement, fully_qualified_symbol_name
21-
from mathics.core.load_builtin import definition_contribute, mathics3_builtins_modules
2221
from mathics.core.rules import BaseRule, Rule
2322
from mathics.core.symbols import Atom, Symbol, strip_context
2423
from mathics.core.util import canonic_filename
@@ -1066,6 +1065,10 @@ def load_builtin_definitions(
10661065
"""
10671066
Load definitions from Builtin classes, autoload files and extension modules.
10681067
"""
1068+
from mathics.core.load_builtin import (
1069+
definition_contribute,
1070+
mathics3_builtins_modules,
1071+
)
10691072
from mathics.eval.files_io.files import get_file_time
10701073
from mathics.eval.pymathics import PyMathicsLoadException, load_pymathics_module
10711074
from mathics.session import autoload_files

mathics/core/expression.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import math
55
from bisect import bisect_left
66
from itertools import chain
7+
from types import MethodType
78
from typing import (
89
Any,
910
Callable,
@@ -18,6 +19,7 @@
1819
)
1920

2021
import sympy
22+
from mathics_scanner.location import SourceRange
2123

2224
from mathics.core.atoms import Integer1, String
2325
from mathics.core.attributes import (
@@ -275,6 +277,7 @@ class Expression(BaseElement, NumericOperators, EvalMixin):
275277
elements_properties: Optional[ElementsProperties]
276278
options: Optional[Dict[str, Any]]
277279
pattern_sequence: bool
280+
location: Optional[Union[SourceRange, MethodType]]
278281

279282
def __init__(
280283
self,
@@ -300,6 +303,7 @@ def __init__(
300303

301304
self._sequences = None
302305
self._cache = None
306+
self.location = None
303307

304308
# self.copy creates this
305309
self.original: Optional[Expression] = None

mathics/core/parser/convert.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from mathics.core.atoms import Integer, MachineReal, PrecisionReal, Rational, String
1212
from mathics.core.convert.expression import to_expression, to_mathics_list
13+
from mathics.core.element import BaseElement
1314
from mathics.core.number import RECONSTRUCT_MACHINE_PRECISION_DIGITS
1415
from mathics.core.parser.ast import (
1516
Filename as AST_Filename,
@@ -189,7 +190,7 @@ class Converter(GenericConverter):
189190
def __init__(self):
190191
self.definitions = None
191192

192-
def convert(self, node, definitions):
193+
def convert(self, node, definitions) -> BaseElement:
193194
self.definitions = definitions
194195
result = self.do_convert(node)
195196
self.definitions = None

mathics/core/parser/parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
# FIXME: should rework so we don't have to do this
5050
# We have the character name ImaginaryI and ImaginaryJ, but we should
5151
# have the *operator* name, "I".
52-
special_symbols["\uF74F"] = special_symbols["\uF74E"] = "I"
52+
special_symbols["\uf74f"] = special_symbols["\uf74e"] = "I"
5353

5454

5555
# An operator precedence value that will ensure that whatever operator

mathics/core/parser/util.py

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
# -*- coding: utf-8 -*-
22

3-
from typing import Any, FrozenSet, Tuple
3+
from typing import FrozenSet, Optional, Tuple
44

55
from mathics_scanner.feed import LineFeeder
6+
from mathics_scanner.location import ContainerKind, SourceRange
67

8+
from mathics.core.definitions import Definitions
9+
from mathics.core.element import BaseElement
710
from mathics.core.parser.convert import convert
811
from mathics.core.parser.feed import MathicsSingleLineFeeder
912
from mathics.core.parser.parser import Parser
@@ -12,7 +15,7 @@
1215
parser = Parser()
1316

1417

15-
def parse(definitions, feeder: LineFeeder) -> Any:
18+
def parse(definitions, feeder: LineFeeder) -> BaseElement:
1619
"""
1720
Parse input (from the frontend, -e, input files, ToExpression etc).
1821
Look up symbols according to the Definitions instance supplied.
@@ -22,7 +25,9 @@ def parse(definitions, feeder: LineFeeder) -> Any:
2225
return parse_returning_code(definitions, feeder)[0]
2326

2427

25-
def parse_incrementally_by_line(definitions, feeder: LineFeeder) -> Any:
28+
def parse_incrementally_by_line(
29+
definitions: Definitions, feeder: LineFeeder
30+
) -> Optional[BaseElement]:
2631
"""Parse input incrementally by line. This is in contrast to parse() or
2732
parser_returning_code(), which parse the *entire*
2833
input which could be many line.
@@ -50,19 +55,40 @@ def parse_incrementally_by_line(definitions, feeder: LineFeeder) -> Any:
5055
return convert(ast, definitions)
5156

5257

53-
def parse_returning_code(definitions, feeder: LineFeeder) -> Tuple[Any, str]:
54-
"""
55-
Parse input (from the frontend, -e, input files, ToExpression etc).
58+
def parse_returning_code(
59+
definitions: Definitions, feeder: LineFeeder
60+
) -> Tuple[BaseElement, str]:
61+
"""Parse input (from the frontend, -e, input files, ToExpression etc).
5662
Look up symbols according to the Definitions instance supplied.
5763
58-
Feeder must implement the feed and empty methods, see core/parser/feed.py.
64+
``feeder`` must implement the ``feed()`` and ``empty()``
65+
methods. See the mathics_scanner.feed module.
66+
5967
"""
68+
from mathics.core.expression import Expression
69+
6070
ast = parser.parse(feeder)
61-
source_code = parser.tokeniser.code if hasattr(parser.tokeniser, "code") else ""
62-
if ast is not None:
63-
return convert(ast, definitions), source_code
64-
else:
65-
return None, source_code
71+
72+
# For expressions which make their way to
73+
# FunctionApplyRule, saving a position here is
74+
# extraneous because teh FunctionApplyRule an get
75+
# the position. But deal with this redundancy
76+
# after the dust settles, and we have experience
77+
# on what is desired.
78+
location = (
79+
feeder.container
80+
if feeder.container_kind == ContainerKind.PYTHON
81+
else SourceRange(0, parser.tokeniser.pos, feeder.container_index)
82+
)
83+
source_text = parser.tokeniser.source_text
84+
85+
if ast is None:
86+
return None, source_text
87+
88+
converted = convert(ast, definitions)
89+
if isinstance(converted, Expression):
90+
converted.location = location
91+
return converted, source_text
6692

6793

6894
class SystemDefinitions:
@@ -111,9 +137,12 @@ def lookup_name(self, name):
111137
return ensure_context(name, context)
112138

113139

114-
def parse_builtin_rule(string, definitions=SystemDefinitions()):
140+
def parse_builtin_rule(string, definitions=SystemDefinitions(), location=None):
115141
"""
116142
Parse rules specified in builtin docstrings/attributes. Every symbol
117143
in the input is created in the System` context.
118144
"""
119-
return parse(definitions, MathicsSingleLineFeeder(string, "<builtin_rules>"))
145+
return parse(
146+
definitions,
147+
MathicsSingleLineFeeder(string, location, ContainerKind.PYTHON),
148+
)

mathics/core/pattern.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,10 @@ def get_match_count(self, vars_dict: Optional[dict] = None) -> Tuple[int, int]:
399399
"""The number of matches"""
400400
return (1, 1)
401401

402+
@property
403+
def short_name(self) -> str:
404+
return self.atom.short_name
405+
402406

403407
# class StopGenerator_ExpressionPattern_match(StopGenerator):
404408
# pass
@@ -421,6 +425,7 @@ def __init__(
421425
evaluation: Optional[Evaluation] = None,
422426
):
423427
self.expr = expr
428+
self.location = expr.location if hasattr(expr, "location") else None
424429
head = expr.head
425430
if attributes is None and evaluation:
426431
attributes = head.get_attributes(evaluation.definitions)

0 commit comments

Comments
 (0)