pitybas 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pitybas-0.1.0/LICENSE +19 -0
- pitybas-0.1.0/PKG-INFO +67 -0
- pitybas-0.1.0/README.md +38 -0
- pitybas-0.1.0/pyproject.toml +52 -0
- pitybas-0.1.0/setup.cfg +4 -0
- pitybas-0.1.0/src/pitybas/__init__.py +1 -0
- pitybas-0.1.0/src/pitybas/__main__.py +4 -0
- pitybas-0.1.0/src/pitybas/cli.py +68 -0
- pitybas-0.1.0/src/pitybas/common.py +41 -0
- pitybas-0.1.0/src/pitybas/expression.py +285 -0
- pitybas-0.1.0/src/pitybas/interpret.py +269 -0
- pitybas-0.1.0/src/pitybas/io/__init__.py +0 -0
- pitybas-0.1.0/src/pitybas/io/simple.py +71 -0
- pitybas-0.1.0/src/pitybas/io/vt100.py +286 -0
- pitybas-0.1.0/src/pitybas/parse.py +367 -0
- pitybas-0.1.0/src/pitybas/tokens.py +1356 -0
- pitybas-0.1.0/src/pitybas.egg-info/PKG-INFO +67 -0
- pitybas-0.1.0/src/pitybas.egg-info/SOURCES.txt +31 -0
- pitybas-0.1.0/src/pitybas.egg-info/dependency_links.txt +1 -0
- pitybas-0.1.0/src/pitybas.egg-info/entry_points.txt +2 -0
- pitybas-0.1.0/src/pitybas.egg-info/requires.txt +3 -0
- pitybas-0.1.0/src/pitybas.egg-info/top_level.txt +1 -0
- pitybas-0.1.0/tests/test_bas_programs.py +56 -0
- pitybas-0.1.0/tests/test_cli.py +184 -0
- pitybas-0.1.0/tests/test_common.py +30 -0
- pitybas-0.1.0/tests/test_control_flow.py +76 -0
- pitybas-0.1.0/tests/test_expressions.py +68 -0
- pitybas-0.1.0/tests/test_interpreter_introspection.py +101 -0
- pitybas-0.1.0/tests/test_io.py +61 -0
- pitybas-0.1.0/tests/test_io_simple.py +131 -0
- pitybas-0.1.0/tests/test_known_bugs.py +136 -0
- pitybas-0.1.0/tests/test_parser.py +76 -0
- pitybas-0.1.0/tests/test_variables.py +73 -0
pitybas-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2014 Ryan Hileman
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
|
11
|
+
all copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
+
THE SOFTWARE.
|
pitybas-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pitybas
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A TI-BASIC interpreter written in Python
|
|
5
|
+
Author: Ryan Hileman
|
|
6
|
+
Maintainer-email: Thomas Roten <tsr@thomasroten.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/tsroten/pitybas
|
|
9
|
+
Project-URL: Repository, https://github.com/tsroten/pitybas
|
|
10
|
+
Project-URL: Issues, https://github.com/tsroten/pitybas/issues
|
|
11
|
+
Keywords: ti-basic,interpreter,calculator,ti-83,ti-84
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Software Development :: Interpreters
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Provides-Extra: test
|
|
27
|
+
Requires-Dist: pytest; extra == "test"
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
pitybas
|
|
31
|
+
=======
|
|
32
|
+
A working TI-BASIC interpreter, written in Python.
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
pip install pitybas
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
Use `pb -i vt100` to run programs which need a working home screen.
|
|
41
|
+
|
|
42
|
+
If you run `pb` with no filename, it launches an interactive shell.
|
|
43
|
+
|
|
44
|
+
Usage: pb [options] filename
|
|
45
|
+
|
|
46
|
+
Options:
|
|
47
|
+
-h, --help show this help message and exit
|
|
48
|
+
-a, --ast parse, print ast, and quit
|
|
49
|
+
-d, --dump dump variables in stacktrace
|
|
50
|
+
-s, --stacktrace always stacktrace
|
|
51
|
+
-v, --verbose verbose output
|
|
52
|
+
-i IO, --io=IO select an IO system: simple (default), vt100
|
|
53
|
+
|
|
54
|
+
You can also run it as a module without installing the console script:
|
|
55
|
+
|
|
56
|
+
python -m pitybas -i vt100
|
|
57
|
+
|
|
58
|
+
## Known Limitations
|
|
59
|
+
|
|
60
|
+
- **Graph screen functions are not supported.** Commands that draw to the TI-83/84 graph screen (e.g. `Circle`, `Line`, `DrawF`) are not implemented. Programs that use them will fail or produce no output.
|
|
61
|
+
|
|
62
|
+
## Development
|
|
63
|
+
|
|
64
|
+
Clone the repository and install it in editable mode with the test extras:
|
|
65
|
+
|
|
66
|
+
pip install -e .[test]
|
|
67
|
+
pytest
|
pitybas-0.1.0/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
pitybas
|
|
2
|
+
=======
|
|
3
|
+
A working TI-BASIC interpreter, written in Python.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
pip install pitybas
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
Use `pb -i vt100` to run programs which need a working home screen.
|
|
12
|
+
|
|
13
|
+
If you run `pb` with no filename, it launches an interactive shell.
|
|
14
|
+
|
|
15
|
+
Usage: pb [options] filename
|
|
16
|
+
|
|
17
|
+
Options:
|
|
18
|
+
-h, --help show this help message and exit
|
|
19
|
+
-a, --ast parse, print ast, and quit
|
|
20
|
+
-d, --dump dump variables in stacktrace
|
|
21
|
+
-s, --stacktrace always stacktrace
|
|
22
|
+
-v, --verbose verbose output
|
|
23
|
+
-i IO, --io=IO select an IO system: simple (default), vt100
|
|
24
|
+
|
|
25
|
+
You can also run it as a module without installing the console script:
|
|
26
|
+
|
|
27
|
+
python -m pitybas -i vt100
|
|
28
|
+
|
|
29
|
+
## Known Limitations
|
|
30
|
+
|
|
31
|
+
- **Graph screen functions are not supported.** Commands that draw to the TI-83/84 graph screen (e.g. `Circle`, `Line`, `DrawF`) are not implemented. Programs that use them will fail or produce no output.
|
|
32
|
+
|
|
33
|
+
## Development
|
|
34
|
+
|
|
35
|
+
Clone the repository and install it in editable mode with the test extras:
|
|
36
|
+
|
|
37
|
+
pip install -e .[test]
|
|
38
|
+
pytest
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pitybas"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "A TI-BASIC interpreter written in Python"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Ryan Hileman" },
|
|
14
|
+
]
|
|
15
|
+
maintainers = [
|
|
16
|
+
{ name = "Thomas Roten", email = "tsr@thomasroten.com" },
|
|
17
|
+
]
|
|
18
|
+
requires-python = ">=3.9"
|
|
19
|
+
keywords = ["ti-basic", "interpreter", "calculator", "ti-83", "ti-84"]
|
|
20
|
+
classifiers = [
|
|
21
|
+
"Development Status :: 4 - Beta",
|
|
22
|
+
"Environment :: Console",
|
|
23
|
+
"Intended Audience :: Developers",
|
|
24
|
+
"Programming Language :: Python :: 3",
|
|
25
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
26
|
+
"Programming Language :: Python :: 3.9",
|
|
27
|
+
"Programming Language :: Python :: 3.10",
|
|
28
|
+
"Programming Language :: Python :: 3.11",
|
|
29
|
+
"Programming Language :: Python :: 3.12",
|
|
30
|
+
"Programming Language :: Python :: 3.13",
|
|
31
|
+
"Topic :: Software Development :: Interpreters",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://github.com/tsroten/pitybas"
|
|
36
|
+
Repository = "https://github.com/tsroten/pitybas"
|
|
37
|
+
Issues = "https://github.com/tsroten/pitybas/issues"
|
|
38
|
+
|
|
39
|
+
[project.scripts]
|
|
40
|
+
pb = "pitybas.cli:main"
|
|
41
|
+
|
|
42
|
+
[project.optional-dependencies]
|
|
43
|
+
test = ["pytest"]
|
|
44
|
+
|
|
45
|
+
[tool.setuptools.dynamic]
|
|
46
|
+
version = { attr = "pitybas.__version__" }
|
|
47
|
+
|
|
48
|
+
[tool.setuptools.packages.find]
|
|
49
|
+
where = ["src"]
|
|
50
|
+
|
|
51
|
+
[tool.pytest.ini_options]
|
|
52
|
+
testpaths = ["tests"]
|
pitybas-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import traceback
|
|
3
|
+
from optparse import OptionParser
|
|
4
|
+
|
|
5
|
+
from .interpret import Interpreter, Repl
|
|
6
|
+
from .common import Error
|
|
7
|
+
from pitybas.io.vt100 import IO as vt100
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def main(argv=None):
|
|
11
|
+
parser = OptionParser(usage='Usage: pb [options] filename')
|
|
12
|
+
parser.add_option('-a', '--ast', dest="ast", action="store_true", help="parse, print ast, and quit")
|
|
13
|
+
parser.add_option('-d', '--dump', dest="vardump", action="store_true", help="dump variables in stacktrace")
|
|
14
|
+
parser.add_option('-s', '--stacktrace', dest="stacktrace", action="store_true", help="always stacktrace")
|
|
15
|
+
parser.add_option('-v', '--verbose', dest="verbose", action="store_true", help="verbose output")
|
|
16
|
+
parser.add_option('-i', '--io', dest="io", help="select an IO system: simple (default), vt100")
|
|
17
|
+
|
|
18
|
+
(options, args) = parser.parse_args(argv)
|
|
19
|
+
|
|
20
|
+
if len(args) > 1:
|
|
21
|
+
parser.print_help()
|
|
22
|
+
sys.exit(1)
|
|
23
|
+
|
|
24
|
+
io = None
|
|
25
|
+
if options.io == 'vt100':
|
|
26
|
+
io = vt100
|
|
27
|
+
|
|
28
|
+
if args:
|
|
29
|
+
vm = Interpreter.from_file(args[0], history=20, io=io)
|
|
30
|
+
else:
|
|
31
|
+
print('Welcome to pitybas. Press Ctrl-D to exit.')
|
|
32
|
+
print()
|
|
33
|
+
vm = Repl(history=20, io=io)
|
|
34
|
+
|
|
35
|
+
if options.verbose:
|
|
36
|
+
vm.print_tokens()
|
|
37
|
+
print()
|
|
38
|
+
if args:
|
|
39
|
+
print('-===[ Running %s ]===-' % args[0])
|
|
40
|
+
|
|
41
|
+
if options.ast:
|
|
42
|
+
vm.print_ast()
|
|
43
|
+
sys.exit(0)
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
vm.execute()
|
|
47
|
+
if options.stacktrace:
|
|
48
|
+
vm.print_stacktrace(options.vardump)
|
|
49
|
+
except KeyboardInterrupt:
|
|
50
|
+
print()
|
|
51
|
+
vm.print_stacktrace(options.vardump)
|
|
52
|
+
except Exception as e:
|
|
53
|
+
print()
|
|
54
|
+
print()
|
|
55
|
+
vm.print_stacktrace(options.vardump)
|
|
56
|
+
|
|
57
|
+
print('%s on line %i:' % (e.__class__.__name__, vm.line), end=' ')
|
|
58
|
+
|
|
59
|
+
if isinstance(e, Error):
|
|
60
|
+
print(e.msg)
|
|
61
|
+
else:
|
|
62
|
+
print()
|
|
63
|
+
print('-===[ Python traceback ]===-')
|
|
64
|
+
print(traceback.format_exc())
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
if __name__ == '__main__':
|
|
68
|
+
main()
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
class Error(Exception):
|
|
2
|
+
def __init__(self, msg):
|
|
3
|
+
self.msg = msg
|
|
4
|
+
|
|
5
|
+
def __str__(self):
|
|
6
|
+
return self.msg
|
|
7
|
+
|
|
8
|
+
class StopError(Exception): pass
|
|
9
|
+
class ReturnError(Exception): pass
|
|
10
|
+
|
|
11
|
+
class ParseError(Error): pass
|
|
12
|
+
class ExecutionError(Error): pass
|
|
13
|
+
class ExpressionError(Error): pass
|
|
14
|
+
|
|
15
|
+
class Pri:
|
|
16
|
+
# evaluation happens in the following order:
|
|
17
|
+
# skip: expressions, functions, variables
|
|
18
|
+
# 1. exponents, factorials
|
|
19
|
+
# 2. multiplication, division
|
|
20
|
+
# 3. addition, subtraction
|
|
21
|
+
# 4. logic operators
|
|
22
|
+
# 5. boolean operators
|
|
23
|
+
# 6. variable setting
|
|
24
|
+
|
|
25
|
+
# these won't be parsed into expressions at all
|
|
26
|
+
INVALID = -2
|
|
27
|
+
# NONE means store but don't execute directly
|
|
28
|
+
# used for variables, lazy loading functions and expressions
|
|
29
|
+
NONE = -1
|
|
30
|
+
|
|
31
|
+
PROB = 0
|
|
32
|
+
EXPONENT = 1
|
|
33
|
+
MULTDIV = 2
|
|
34
|
+
ADDSUB = 3
|
|
35
|
+
|
|
36
|
+
LOGIC = 4
|
|
37
|
+
BOOL = 5
|
|
38
|
+
SET = 6
|
|
39
|
+
|
|
40
|
+
def is_number(num):
|
|
41
|
+
return str(num).lstrip('-').replace('.', '', 1).isdigit()
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
from . import tokens
|
|
2
|
+
from .common import ExpressionError, Pri, is_number
|
|
3
|
+
|
|
4
|
+
class Base:
|
|
5
|
+
priority = Pri.NONE
|
|
6
|
+
|
|
7
|
+
can_run = False
|
|
8
|
+
can_set = False
|
|
9
|
+
can_get = True
|
|
10
|
+
can_fill_left = False
|
|
11
|
+
can_fill_right = False
|
|
12
|
+
absorbs = ()
|
|
13
|
+
|
|
14
|
+
end = None
|
|
15
|
+
|
|
16
|
+
def __init__(self, *elements):
|
|
17
|
+
self.contents = []
|
|
18
|
+
self.raw = []
|
|
19
|
+
self.finished = False
|
|
20
|
+
|
|
21
|
+
for e in elements:
|
|
22
|
+
self.append(e)
|
|
23
|
+
|
|
24
|
+
def append(self, token):
|
|
25
|
+
if self.contents:
|
|
26
|
+
prev = self.contents[-1]
|
|
27
|
+
|
|
28
|
+
# the minus sign implies a * -1 when used by itself
|
|
29
|
+
if isinstance(prev, tokens.Minus):
|
|
30
|
+
# TODO: fix this the rest of the way
|
|
31
|
+
if len(self.contents) == 1:
|
|
32
|
+
self.contents.pop()
|
|
33
|
+
self.contents += [tokens.Value(-1), tokens.Mult()]
|
|
34
|
+
|
|
35
|
+
# absorb: tokens can absorb the next token from the expression if it matches a list of types
|
|
36
|
+
elif isinstance(token, prev.absorbs):
|
|
37
|
+
if isinstance(token, Base):
|
|
38
|
+
token = token.flatten()
|
|
39
|
+
|
|
40
|
+
prev.absorb(token)
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
# implied multiplication
|
|
44
|
+
elif prev.priority == token.priority == tokens.Pri.NONE:
|
|
45
|
+
|
|
46
|
+
# negative numbers actually have implied addition
|
|
47
|
+
if isinstance(token, tokens.Value)\
|
|
48
|
+
and is_number(token.value) and int(token.value) < 0:
|
|
49
|
+
self.contents.append(tokens.Plus())
|
|
50
|
+
else:
|
|
51
|
+
self.contents.append(tokens.Mult())
|
|
52
|
+
|
|
53
|
+
self.raw.append(token)
|
|
54
|
+
self.contents.append(token)
|
|
55
|
+
|
|
56
|
+
def extend(self, array):
|
|
57
|
+
for x in array:
|
|
58
|
+
self.append(x)
|
|
59
|
+
|
|
60
|
+
def flatten(self):
|
|
61
|
+
if len(self.contents) == 1:
|
|
62
|
+
first = self.contents[0]
|
|
63
|
+
if isinstance(first, Base):
|
|
64
|
+
return first.flatten()
|
|
65
|
+
elif first.can_get:
|
|
66
|
+
return first
|
|
67
|
+
|
|
68
|
+
return self
|
|
69
|
+
|
|
70
|
+
def fill(self):
|
|
71
|
+
# TODO: instead of this system, perhaps tokens should be able to specify whether they need/want left/right params
|
|
72
|
+
if not self.contents: return
|
|
73
|
+
|
|
74
|
+
# if we don't have a proper variable:token:variable pairing in the token list,
|
|
75
|
+
# this method will allow tokens to fill in an implied variable to their left or right
|
|
76
|
+
new = []
|
|
77
|
+
for i in range(len(self.contents)):
|
|
78
|
+
t = self.contents[i]
|
|
79
|
+
if (i % 2 == 0 and not t.can_get):
|
|
80
|
+
left = None
|
|
81
|
+
right = None
|
|
82
|
+
|
|
83
|
+
if i > 0:
|
|
84
|
+
left = self.contents[i-1]
|
|
85
|
+
if not left.can_fill_right:
|
|
86
|
+
left = None
|
|
87
|
+
|
|
88
|
+
right = self.contents[i]
|
|
89
|
+
if not right.can_fill_left:
|
|
90
|
+
right = None
|
|
91
|
+
|
|
92
|
+
if left is not None and right is not None:
|
|
93
|
+
if left < right:
|
|
94
|
+
left = None
|
|
95
|
+
else:
|
|
96
|
+
right = None
|
|
97
|
+
|
|
98
|
+
if left is not None:
|
|
99
|
+
new.append(left.fill_right())
|
|
100
|
+
elif right is not None:
|
|
101
|
+
new.append(right.fill_left())
|
|
102
|
+
|
|
103
|
+
new.append(t)
|
|
104
|
+
|
|
105
|
+
last = new[-1]
|
|
106
|
+
if not last.can_get:
|
|
107
|
+
if last.can_fill_right:
|
|
108
|
+
new.append(last.fill_right())
|
|
109
|
+
|
|
110
|
+
self.contents = new
|
|
111
|
+
|
|
112
|
+
def validate(self):
|
|
113
|
+
if not self.contents: return
|
|
114
|
+
|
|
115
|
+
# figure out how to handle in-place tokens like the symbol for ^3
|
|
116
|
+
# perhaps replace it with a ^3 so we can enforce (value, token, value)
|
|
117
|
+
# or we can pad "in-place" tokens with a null to be passed as right
|
|
118
|
+
|
|
119
|
+
# make sure expression is ordered (value, token, value, token, value)
|
|
120
|
+
for i in range(len(self.contents)):
|
|
121
|
+
t = self.contents[i]
|
|
122
|
+
|
|
123
|
+
if (i % 2 == 0 and not t.can_get) or ( i % 2 == 1 and not t.can_run):
|
|
124
|
+
raise ExpressionError('bad token order: %s' % self)
|
|
125
|
+
|
|
126
|
+
# determine whether we have any tokens after a ->
|
|
127
|
+
found_stor = False
|
|
128
|
+
for i in range(len(self.contents)):
|
|
129
|
+
t = self.contents[i]
|
|
130
|
+
odd = i % 2
|
|
131
|
+
|
|
132
|
+
if isinstance(t, tokens.Store):
|
|
133
|
+
found_stor = True
|
|
134
|
+
stor_odd = odd
|
|
135
|
+
elif found_stor and (odd == stor_odd):
|
|
136
|
+
raise ExpressionError('Store cannot be followed by non-Store tokens in expression: %s' % self)
|
|
137
|
+
|
|
138
|
+
def order(self):
|
|
139
|
+
# this step returns a list of ordered indicies
|
|
140
|
+
# to help reduce tokens to a single value
|
|
141
|
+
# see common.Pri for an ordering explanation
|
|
142
|
+
|
|
143
|
+
order = {}
|
|
144
|
+
|
|
145
|
+
for i in range(len(self.contents)):
|
|
146
|
+
token = self.contents[i]
|
|
147
|
+
p = token.priority
|
|
148
|
+
if p >= 0:
|
|
149
|
+
# anything below zero is to be ignored
|
|
150
|
+
if p in order:
|
|
151
|
+
order[p].append(i)
|
|
152
|
+
else:
|
|
153
|
+
order[p] = [i]
|
|
154
|
+
|
|
155
|
+
ret = []
|
|
156
|
+
for p in sorted(order):
|
|
157
|
+
ret += order[p]
|
|
158
|
+
|
|
159
|
+
return ret
|
|
160
|
+
|
|
161
|
+
def get(self, vm):
|
|
162
|
+
self.fill()
|
|
163
|
+
self.validate()
|
|
164
|
+
|
|
165
|
+
sub = []
|
|
166
|
+
expr = self.contents[:]
|
|
167
|
+
for i in self.order():
|
|
168
|
+
n = 0
|
|
169
|
+
for s in sub:
|
|
170
|
+
if s < i:
|
|
171
|
+
n += 1
|
|
172
|
+
|
|
173
|
+
sub += [i, i+1]
|
|
174
|
+
i -= n
|
|
175
|
+
|
|
176
|
+
right = expr.pop(i+1)
|
|
177
|
+
left = expr.pop(i-1)
|
|
178
|
+
|
|
179
|
+
token = expr[i-1]
|
|
180
|
+
expr[i-1] = tokens.Value(token.run(vm, left, right))
|
|
181
|
+
|
|
182
|
+
return vm.get(expr[0])
|
|
183
|
+
|
|
184
|
+
def finish(self):
|
|
185
|
+
self.finished = True
|
|
186
|
+
|
|
187
|
+
def close(self, char):
|
|
188
|
+
for stack in reversed(self.contents):
|
|
189
|
+
if isinstance(stack, Base):
|
|
190
|
+
if stack.close(char):
|
|
191
|
+
return False
|
|
192
|
+
|
|
193
|
+
if char == self.end and not self.finished:
|
|
194
|
+
self.finish()
|
|
195
|
+
return True
|
|
196
|
+
|
|
197
|
+
def __str__(self):
|
|
198
|
+
return ''.join([a.token for a in self.raw])
|
|
199
|
+
|
|
200
|
+
def __len__(self):
|
|
201
|
+
return len(self.contents)
|
|
202
|
+
|
|
203
|
+
def __repr__(self):
|
|
204
|
+
return 'E(%s)' % (' '.join(repr(token) for token in self.contents))
|
|
205
|
+
|
|
206
|
+
bracket_map = {'(':')', '{':'}', '[':']'}
|
|
207
|
+
|
|
208
|
+
class Expression(Base):
|
|
209
|
+
def set(self, vm, value):
|
|
210
|
+
if len(self.contents) == 1:
|
|
211
|
+
self.contents[0].set(vm, value)
|
|
212
|
+
|
|
213
|
+
class Bracketed(Base):
|
|
214
|
+
def __init__(self, end):
|
|
215
|
+
self.end = bracket_map[end]
|
|
216
|
+
Base.__init__(self)
|
|
217
|
+
|
|
218
|
+
def __repr__(self):
|
|
219
|
+
return 'B(%s)' % (' '.join(repr(token) for token in self.contents))
|
|
220
|
+
|
|
221
|
+
class ParenExpr(Bracketed):
|
|
222
|
+
end = ')'
|
|
223
|
+
|
|
224
|
+
class Tuple(Base):
|
|
225
|
+
priority = Pri.INVALID
|
|
226
|
+
|
|
227
|
+
def __init__(self):
|
|
228
|
+
Base.__init__(self)
|
|
229
|
+
|
|
230
|
+
def append(self, expr):
|
|
231
|
+
if isinstance(expr, Base):
|
|
232
|
+
expr = expr.flatten()
|
|
233
|
+
|
|
234
|
+
if self.contents:
|
|
235
|
+
last = self.contents[-1]
|
|
236
|
+
if isinstance(last, Base) and not last.finished:
|
|
237
|
+
last.append(expr)
|
|
238
|
+
return
|
|
239
|
+
|
|
240
|
+
expr = Expression(expr)
|
|
241
|
+
self.contents.append(expr)
|
|
242
|
+
|
|
243
|
+
def sep(self):
|
|
244
|
+
if self.contents:
|
|
245
|
+
self.contents[-1].finish()
|
|
246
|
+
|
|
247
|
+
def get(self, vm):
|
|
248
|
+
return [vm.get(arg) for arg in self.contents]
|
|
249
|
+
|
|
250
|
+
def __len__(self):
|
|
251
|
+
return len(self.contents)
|
|
252
|
+
|
|
253
|
+
def __repr__(self):
|
|
254
|
+
def expr_repr(e):
|
|
255
|
+
if not isinstance(e, Expression):
|
|
256
|
+
return '({})'.format(e)
|
|
257
|
+
else:
|
|
258
|
+
return repr(e)
|
|
259
|
+
|
|
260
|
+
return 'T(%s)' % (', '.join(expr_repr(expr) for expr in self.contents))
|
|
261
|
+
|
|
262
|
+
class Arguments(Tuple, Bracketed):
|
|
263
|
+
def __init__(self, end):
|
|
264
|
+
Bracketed.__init__(self, end)
|
|
265
|
+
|
|
266
|
+
def __repr__(self):
|
|
267
|
+
return 'A(%s)' % (', '.join(repr(expr) for expr in self.contents))
|
|
268
|
+
|
|
269
|
+
class FunctionArgs(Arguments):
|
|
270
|
+
end = ')'
|
|
271
|
+
|
|
272
|
+
class ListExpr(Arguments):
|
|
273
|
+
priority = Pri.NONE
|
|
274
|
+
end = '}'
|
|
275
|
+
|
|
276
|
+
def __repr__(self):
|
|
277
|
+
return 'L{%s}' % (', '.join(repr(expr) for expr in self.contents))
|
|
278
|
+
|
|
279
|
+
class MatrixExpr(Arguments):
|
|
280
|
+
priority = Pri.NONE
|
|
281
|
+
end = ']'
|
|
282
|
+
|
|
283
|
+
def __repr__(self):
|
|
284
|
+
return 'M[%s]' % (', '.join(repr(expr) for expr in self.contents))
|
|
285
|
+
|