prefscript 1.1__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.
- prefscript-1.1/LICENSE +21 -0
- prefscript-1.1/PKG-INFO +60 -0
- prefscript-1.1/README.md +45 -0
- prefscript-1.1/pyproject.toml +24 -0
- prefscript-1.1/setup.cfg +4 -0
- prefscript-1.1/src/ascii7io.py +43 -0
- prefscript-1.1/src/cantorpairs/__init__.py +1 -0
- prefscript-1.1/src/cantorpairs/src/cantorpairs.py +117 -0
- prefscript-1.1/src/cantorpairs/src/cp_ex.py +38 -0
- prefscript-1.1/src/prefscript.egg-info/PKG-INFO +60 -0
- prefscript-1.1/src/prefscript.egg-info/SOURCES.txt +16 -0
- prefscript-1.1/src/prefscript.egg-info/dependency_links.txt +1 -0
- prefscript-1.1/src/prefscript.egg-info/entry_points.txt +2 -0
- prefscript-1.1/src/prefscript.egg-info/requires.txt +1 -0
- prefscript-1.1/src/prefscript.egg-info/top_level.txt +3 -0
- prefscript-1.1/src/prefscript.py +437 -0
- prefscript-1.1/tests/test.py +17 -0
- prefscript-1.1/tests/test_fact.py +20 -0
prefscript-1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Balqui
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
prefscript-1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: prefscript
|
|
3
|
+
Version: 1.1
|
|
4
|
+
Summary: Partial Recursive Functions for Scripting
|
|
5
|
+
Author-email: José Luis Balcázar <jose.luis.balcazar@upc.edu>
|
|
6
|
+
Project-URL: Homepage, https://github.com/balqui/prefscript
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/balqui/prefscript/issues
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: pytokr>=1.0
|
|
15
|
+
|
|
16
|
+
# PReFScript:
|
|
17
|
+
## Partial Recursive Functions for Scripting
|
|
18
|
+
|
|
19
|
+
Author: Jose L Balcazar, ORCID 0000-0003-4248-4528
|
|
20
|
+
|
|
21
|
+
Project started: mid Germinal 2003.
|
|
22
|
+
|
|
23
|
+
Current version: 1.1, late Thermidor 2024.
|
|
24
|
+
|
|
25
|
+
Copyleft: MIT License (https://en.wikipedia.org/wiki/MIT_License)
|
|
26
|
+
|
|
27
|
+
A Python-based environment to explore and experiment with partial
|
|
28
|
+
recursive functions; naturally doubles as a (purely functional)
|
|
29
|
+
programming language, but it is not intended to be used much as such.
|
|
30
|
+
|
|
31
|
+
### Scripts
|
|
32
|
+
|
|
33
|
+
In PReFScript, a script is a sequence of functions defined in terms of each
|
|
34
|
+
other and of a few basic functions via the partial recursion rules
|
|
35
|
+
of composition and minimization. All functions are from the
|
|
36
|
+
natural numbers into the natural numbers and may be undefined
|
|
37
|
+
for some inputs. In order to handle tuples or sequences of natural
|
|
38
|
+
numbers, a Cantor-like encoding is used.
|
|
39
|
+
The always available basic functions include:
|
|
40
|
+
`k_1`, the constant 1 function;
|
|
41
|
+
`id`, the identity function;
|
|
42
|
+
addition and multiplication, `add` and `mul` respectively,
|
|
43
|
+
that interpret the single number received as the Cantor encoding
|
|
44
|
+
of a pair `<x.y>` and compute the corresponding operation on `x` and
|
|
45
|
+
`y`; modified difference `diff` that receives likewise a Cantor-encoded
|
|
46
|
+
pair `<x.y>` and computes `max(0, x - y)` so that we always stay
|
|
47
|
+
within the natural numbers; and two functions related to projections
|
|
48
|
+
of Cantor-encoded sequences.
|
|
49
|
+
|
|
50
|
+
Scripts are maintained in objects of the class PReFScript,
|
|
51
|
+
that can be imported into your own Python program.
|
|
52
|
+
Alternatively, a stand-alone interpreter is also provided.
|
|
53
|
+
Thus, you have available two main ways of programming in PReFScript.
|
|
54
|
+
|
|
55
|
+
### Installation and ways to use PReFScript functions
|
|
56
|
+
|
|
57
|
+
See [doc.md](https://github.com/balqui/prefscript/blob/main/docs/doc.md)
|
|
58
|
+
for all the details (somewhat incomplete as of today).
|
|
59
|
+
|
|
60
|
+
|
prefscript-1.1/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# PReFScript:
|
|
2
|
+
## Partial Recursive Functions for Scripting
|
|
3
|
+
|
|
4
|
+
Author: Jose L Balcazar, ORCID 0000-0003-4248-4528
|
|
5
|
+
|
|
6
|
+
Project started: mid Germinal 2003.
|
|
7
|
+
|
|
8
|
+
Current version: 1.1, late Thermidor 2024.
|
|
9
|
+
|
|
10
|
+
Copyleft: MIT License (https://en.wikipedia.org/wiki/MIT_License)
|
|
11
|
+
|
|
12
|
+
A Python-based environment to explore and experiment with partial
|
|
13
|
+
recursive functions; naturally doubles as a (purely functional)
|
|
14
|
+
programming language, but it is not intended to be used much as such.
|
|
15
|
+
|
|
16
|
+
### Scripts
|
|
17
|
+
|
|
18
|
+
In PReFScript, a script is a sequence of functions defined in terms of each
|
|
19
|
+
other and of a few basic functions via the partial recursion rules
|
|
20
|
+
of composition and minimization. All functions are from the
|
|
21
|
+
natural numbers into the natural numbers and may be undefined
|
|
22
|
+
for some inputs. In order to handle tuples or sequences of natural
|
|
23
|
+
numbers, a Cantor-like encoding is used.
|
|
24
|
+
The always available basic functions include:
|
|
25
|
+
`k_1`, the constant 1 function;
|
|
26
|
+
`id`, the identity function;
|
|
27
|
+
addition and multiplication, `add` and `mul` respectively,
|
|
28
|
+
that interpret the single number received as the Cantor encoding
|
|
29
|
+
of a pair `<x.y>` and compute the corresponding operation on `x` and
|
|
30
|
+
`y`; modified difference `diff` that receives likewise a Cantor-encoded
|
|
31
|
+
pair `<x.y>` and computes `max(0, x - y)` so that we always stay
|
|
32
|
+
within the natural numbers; and two functions related to projections
|
|
33
|
+
of Cantor-encoded sequences.
|
|
34
|
+
|
|
35
|
+
Scripts are maintained in objects of the class PReFScript,
|
|
36
|
+
that can be imported into your own Python program.
|
|
37
|
+
Alternatively, a stand-alone interpreter is also provided.
|
|
38
|
+
Thus, you have available two main ways of programming in PReFScript.
|
|
39
|
+
|
|
40
|
+
### Installation and ways to use PReFScript functions
|
|
41
|
+
|
|
42
|
+
See [doc.md](https://github.com/balqui/prefscript/blob/main/docs/doc.md)
|
|
43
|
+
for all the details (somewhat incomplete as of today).
|
|
44
|
+
|
|
45
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools >= 61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "prefscript"
|
|
7
|
+
version = "1.1"
|
|
8
|
+
authors = [ { name = "José Luis Balcázar", email = "jose.luis.balcazar@upc.edu" }, ]
|
|
9
|
+
description = "Partial Recursive Functions for Scripting"
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"License :: OSI Approved :: MIT License",
|
|
15
|
+
"Operating System :: OS Independent",
|
|
16
|
+
]
|
|
17
|
+
dependencies = [ "pytokr>=1.0" ]
|
|
18
|
+
|
|
19
|
+
[project.urls]
|
|
20
|
+
"Homepage" = "https://github.com/balqui/prefscript"
|
|
21
|
+
"Bug Tracker" = "https://github.com/balqui/prefscript/issues"
|
|
22
|
+
|
|
23
|
+
[project.scripts]
|
|
24
|
+
prefscript = "prefscript:run"
|
prefscript-1.1/setup.cfg
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Single-integer encoding and decoding of (printable) 7-bit ASCII strings
|
|
3
|
+
'''
|
|
4
|
+
|
|
5
|
+
TWO_pow_7 = 1 << 7
|
|
6
|
+
|
|
7
|
+
def str2int(s):
|
|
8
|
+
'integer from any string, even with nonprintable chars'
|
|
9
|
+
r = 0
|
|
10
|
+
for c in s:
|
|
11
|
+
r = r * TWO_pow_7 + ord(c)
|
|
12
|
+
return r
|
|
13
|
+
|
|
14
|
+
def int2raw_str(n):
|
|
15
|
+
'string from any integer, even with nonprintable chars'
|
|
16
|
+
r = list()
|
|
17
|
+
while n > 0:
|
|
18
|
+
r.append(chr(n % TWO_pow_7))
|
|
19
|
+
n //= TWO_pow_7
|
|
20
|
+
return ''.join(reversed(r))
|
|
21
|
+
|
|
22
|
+
def int2str(n):
|
|
23
|
+
'string from any integer, nonprintable chars replaced by underscores'
|
|
24
|
+
r = list()
|
|
25
|
+
while n > 0:
|
|
26
|
+
c = n % TWO_pow_7
|
|
27
|
+
if 31 < c:
|
|
28
|
+
r.append(chr(c))
|
|
29
|
+
else:
|
|
30
|
+
r.append('_')
|
|
31
|
+
n //= TWO_pow_7
|
|
32
|
+
return ''.join(reversed(r))
|
|
33
|
+
|
|
34
|
+
if __name__ == "__main__":
|
|
35
|
+
print(TWO_pow_7)
|
|
36
|
+
for s in ['', 'a', 'aa', 'abcde']:
|
|
37
|
+
n = str2int(s)
|
|
38
|
+
print(s, n, int2str(n))
|
|
39
|
+
print(hw := str2int("Hello, World!"))
|
|
40
|
+
print(int2raw_str(hw))
|
|
41
|
+
print(int2str(hw))
|
|
42
|
+
for i in range(10000, 100000): print(int2str(i*103), end = ' ')
|
|
43
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .src.cantorpairs import __version__, dp, pr_L, pr_R, tup_e, tup_i, s_tup, pr
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Author: Jose L Balcazar, ORCID 0000-0003-4248-4528, april 2023 onwards
|
|
3
|
+
Copyleft: MIT License (https://en.wikipedia.org/wiki/MIT_License)
|
|
4
|
+
|
|
5
|
+
Ancillary functions for PReFScript, the Partial Recursive Functions lab.
|
|
6
|
+
|
|
7
|
+
Implements a slight variant of Cantor's pair / unpair functions;
|
|
8
|
+
adapted from `https://en.wikipedia.org/wiki/Pairing_function`
|
|
9
|
+
The dotted pair is a bijection between `NxN` and `N-{0}`:
|
|
10
|
+
the natural number zero is used as "nil".
|
|
11
|
+
|
|
12
|
+
Project started: mid Germinal 2003.
|
|
13
|
+
Current version: 0.2, early Thermidor 2024.
|
|
14
|
+
Not pip-installable as of today. See README at `https://github.com/balqui/cantorpairs`
|
|
15
|
+
|
|
16
|
+
After each push, the following extra incantation is most likely
|
|
17
|
+
necessary in the local copy of the git repo for `prefscript`:
|
|
18
|
+
`git submodule update --remote`
|
|
19
|
+
'''
|
|
20
|
+
|
|
21
|
+
from functools import cache
|
|
22
|
+
from math import isqrt
|
|
23
|
+
|
|
24
|
+
__version__ = "0.3"
|
|
25
|
+
|
|
26
|
+
@cache
|
|
27
|
+
def _isqrt(n):
|
|
28
|
+
return isqrt(n)
|
|
29
|
+
|
|
30
|
+
# ~ def _isqrt(n):
|
|
31
|
+
# ~ '''
|
|
32
|
+
# ~ int square root via binary search, own program
|
|
33
|
+
# ~ because math.sqrt fails with big numbers, e.g. the
|
|
34
|
+
# ~ decoding of dp(10, 10^17) comes out wrong.
|
|
35
|
+
# ~ '''
|
|
36
|
+
# ~ def rr(n, k):
|
|
37
|
+
# ~ """
|
|
38
|
+
# ~ Pre: k <= sqrt(n)
|
|
39
|
+
# ~ Post: a <= sqrt(n) < b and b - a = k
|
|
40
|
+
# ~ """
|
|
41
|
+
# ~ if 4*k*k > n:
|
|
42
|
+
# ~ return k, 2*k
|
|
43
|
+
# ~ a, b = rr(n, 2*k)
|
|
44
|
+
# ~ m = (a + b)//2 # m = a + k
|
|
45
|
+
# ~ if m*m <= n:
|
|
46
|
+
# ~ return m, b
|
|
47
|
+
# ~ else:
|
|
48
|
+
# ~ return a, m
|
|
49
|
+
# ~ assert n >= 0
|
|
50
|
+
# ~ if n == 0:
|
|
51
|
+
# ~ return 0
|
|
52
|
+
# ~ return rr(n, 1)[0]
|
|
53
|
+
|
|
54
|
+
@cache
|
|
55
|
+
def _unpair(z):
|
|
56
|
+
"local sq root instead of math.sqrt"
|
|
57
|
+
assert z > 0
|
|
58
|
+
w = (_isqrt(8*(z - 1) + 1) - 1)//2
|
|
59
|
+
t = (w*w + w)//2
|
|
60
|
+
x = z - 1 - t
|
|
61
|
+
return x, w - x
|
|
62
|
+
|
|
63
|
+
@cache
|
|
64
|
+
def dp(x, y):
|
|
65
|
+
return ((x + y)*(x + y + 1))//2 + x + 1
|
|
66
|
+
|
|
67
|
+
def pr_L(z):
|
|
68
|
+
if z == 0:
|
|
69
|
+
return 0
|
|
70
|
+
return _unpair(z)[0]
|
|
71
|
+
|
|
72
|
+
def pr_R(z):
|
|
73
|
+
if z == 0:
|
|
74
|
+
return 0
|
|
75
|
+
return _unpair(z)[1]
|
|
76
|
+
|
|
77
|
+
def tup_e(*nums):
|
|
78
|
+
'''
|
|
79
|
+
nums: arbitrary quantity of numbers to encode the sequence;
|
|
80
|
+
fall back into the iterable version, the tuple cast will do nothing
|
|
81
|
+
'''
|
|
82
|
+
return tup_i(nums)
|
|
83
|
+
|
|
84
|
+
def tup_i(nums):
|
|
85
|
+
'''
|
|
86
|
+
nums expected to be an iterable here;
|
|
87
|
+
the reversed nature of the encoding needs to
|
|
88
|
+
expand it into a sequence;
|
|
89
|
+
end of sequence (= empty sequence) encoded
|
|
90
|
+
by 0 in its role of # nil, out of dotted pair range
|
|
91
|
+
'''
|
|
92
|
+
t = 0
|
|
93
|
+
for i in reversed(tuple(nums)):
|
|
94
|
+
t = dp(i, t)
|
|
95
|
+
return t
|
|
96
|
+
|
|
97
|
+
@cache
|
|
98
|
+
def s_tup(t, k):
|
|
99
|
+
'''
|
|
100
|
+
suffix tuple: t assumed a tuple of at least k components,
|
|
101
|
+
return the suffix tuple from k-th on;
|
|
102
|
+
t itself for k == 0, empty tuple 0 if k larger than len of t
|
|
103
|
+
'''
|
|
104
|
+
if t == 0:
|
|
105
|
+
"empty tuple or original k too large"
|
|
106
|
+
return 0
|
|
107
|
+
if k == 0:
|
|
108
|
+
"full suffix"
|
|
109
|
+
return t
|
|
110
|
+
return s_tup(pr_R(t), k-1)
|
|
111
|
+
|
|
112
|
+
def pr(t, k):
|
|
113
|
+
'''
|
|
114
|
+
projection function: get the k-th component;
|
|
115
|
+
returns zero (meaning end of tuple) if k is too large
|
|
116
|
+
'''
|
|
117
|
+
return pr_L(s_tup(t, k))
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Author: Jose L Balcazar, ORCID 0000-0003-4248-4528, april 2023 onwards
|
|
3
|
+
Copyleft: MIT License (https://en.wikipedia.org/wiki/MIT_License)
|
|
4
|
+
|
|
5
|
+
Ancillary functions for the Partial Recursive Functions lab.
|
|
6
|
+
|
|
7
|
+
Examples of usage of pairing and tupling.
|
|
8
|
+
'''
|
|
9
|
+
|
|
10
|
+
import cantorpairs as cp
|
|
11
|
+
|
|
12
|
+
for i in range(4):
|
|
13
|
+
for j in range(4):
|
|
14
|
+
print(i, j, cp.dp(i, j))
|
|
15
|
+
|
|
16
|
+
for i in range(90, 100):
|
|
17
|
+
print(cp.pr_L(i), cp.pr_R(i), cp.dp(cp.pr_L(i), cp.pr_R(i)), i)
|
|
18
|
+
|
|
19
|
+
t = cp.tup_e(4, 7, 56, 101)
|
|
20
|
+
for i in range(5):
|
|
21
|
+
'last call is actually out of range'
|
|
22
|
+
print(cp.pr(t, i))
|
|
23
|
+
|
|
24
|
+
st = cp.s_tup(t, 2)
|
|
25
|
+
print(st)
|
|
26
|
+
for i in range(3):
|
|
27
|
+
print(cp.pr(st, i))
|
|
28
|
+
|
|
29
|
+
while t:
|
|
30
|
+
print(t, cp.pr_L(t))
|
|
31
|
+
t = cp.pr_R(t)
|
|
32
|
+
|
|
33
|
+
t = cp.tup_i(range(8, 16))
|
|
34
|
+
while t:
|
|
35
|
+
print(t, cp.pr_L(t))
|
|
36
|
+
t = cp.pr_R(t)
|
|
37
|
+
|
|
38
|
+
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: prefscript
|
|
3
|
+
Version: 1.1
|
|
4
|
+
Summary: Partial Recursive Functions for Scripting
|
|
5
|
+
Author-email: José Luis Balcázar <jose.luis.balcazar@upc.edu>
|
|
6
|
+
Project-URL: Homepage, https://github.com/balqui/prefscript
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/balqui/prefscript/issues
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: pytokr>=1.0
|
|
15
|
+
|
|
16
|
+
# PReFScript:
|
|
17
|
+
## Partial Recursive Functions for Scripting
|
|
18
|
+
|
|
19
|
+
Author: Jose L Balcazar, ORCID 0000-0003-4248-4528
|
|
20
|
+
|
|
21
|
+
Project started: mid Germinal 2003.
|
|
22
|
+
|
|
23
|
+
Current version: 1.1, late Thermidor 2024.
|
|
24
|
+
|
|
25
|
+
Copyleft: MIT License (https://en.wikipedia.org/wiki/MIT_License)
|
|
26
|
+
|
|
27
|
+
A Python-based environment to explore and experiment with partial
|
|
28
|
+
recursive functions; naturally doubles as a (purely functional)
|
|
29
|
+
programming language, but it is not intended to be used much as such.
|
|
30
|
+
|
|
31
|
+
### Scripts
|
|
32
|
+
|
|
33
|
+
In PReFScript, a script is a sequence of functions defined in terms of each
|
|
34
|
+
other and of a few basic functions via the partial recursion rules
|
|
35
|
+
of composition and minimization. All functions are from the
|
|
36
|
+
natural numbers into the natural numbers and may be undefined
|
|
37
|
+
for some inputs. In order to handle tuples or sequences of natural
|
|
38
|
+
numbers, a Cantor-like encoding is used.
|
|
39
|
+
The always available basic functions include:
|
|
40
|
+
`k_1`, the constant 1 function;
|
|
41
|
+
`id`, the identity function;
|
|
42
|
+
addition and multiplication, `add` and `mul` respectively,
|
|
43
|
+
that interpret the single number received as the Cantor encoding
|
|
44
|
+
of a pair `<x.y>` and compute the corresponding operation on `x` and
|
|
45
|
+
`y`; modified difference `diff` that receives likewise a Cantor-encoded
|
|
46
|
+
pair `<x.y>` and computes `max(0, x - y)` so that we always stay
|
|
47
|
+
within the natural numbers; and two functions related to projections
|
|
48
|
+
of Cantor-encoded sequences.
|
|
49
|
+
|
|
50
|
+
Scripts are maintained in objects of the class PReFScript,
|
|
51
|
+
that can be imported into your own Python program.
|
|
52
|
+
Alternatively, a stand-alone interpreter is also provided.
|
|
53
|
+
Thus, you have available two main ways of programming in PReFScript.
|
|
54
|
+
|
|
55
|
+
### Installation and ways to use PReFScript functions
|
|
56
|
+
|
|
57
|
+
See [doc.md](https://github.com/balqui/prefscript/blob/main/docs/doc.md)
|
|
58
|
+
for all the details (somewhat incomplete as of today).
|
|
59
|
+
|
|
60
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/ascii7io.py
|
|
5
|
+
src/prefscript.py
|
|
6
|
+
src/cantorpairs/__init__.py
|
|
7
|
+
src/cantorpairs/src/cantorpairs.py
|
|
8
|
+
src/cantorpairs/src/cp_ex.py
|
|
9
|
+
src/prefscript.egg-info/PKG-INFO
|
|
10
|
+
src/prefscript.egg-info/SOURCES.txt
|
|
11
|
+
src/prefscript.egg-info/dependency_links.txt
|
|
12
|
+
src/prefscript.egg-info/entry_points.txt
|
|
13
|
+
src/prefscript.egg-info/requires.txt
|
|
14
|
+
src/prefscript.egg-info/top_level.txt
|
|
15
|
+
tests/test.py
|
|
16
|
+
tests/test_fact.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pytokr>=1.0
|
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
'''
|
|
2
|
+
PReFScript: A Partial Recursive Functions Lab
|
|
3
|
+
|
|
4
|
+
Rather: Towards a Partial Recursive Functions lab.
|
|
5
|
+
|
|
6
|
+
Author: Jose L Balcazar, ORCID 0000-0003-4248-4528, april 2023 onwards
|
|
7
|
+
Copyleft: MIT License (https://en.wikipedia.org/wiki/MIT_License)
|
|
8
|
+
|
|
9
|
+
Project started: mid Germinal 2003.
|
|
10
|
+
Current version: 0.5, mid Thermidor 2024.
|
|
11
|
+
|
|
12
|
+
A Python-based environment to explore and experiment with partial
|
|
13
|
+
recursive functions; naturally doubles as a (purely functional)
|
|
14
|
+
programming language, but it is not intended to be used as such.
|
|
15
|
+
|
|
16
|
+
Each function in a script has: associated Gödel number, nickname,
|
|
17
|
+
comments, and code (string); also, the last operation used to
|
|
18
|
+
construct it. Then, in a separate dict (used as namespace for
|
|
19
|
+
eval calls), a runnable version of the code.
|
|
20
|
+
|
|
21
|
+
Nicknames are alphanum strings not starting with a number (no surprise).
|
|
22
|
+
'''
|
|
23
|
+
|
|
24
|
+
from collections import defaultdict as ddict
|
|
25
|
+
from re import compile as re_compile, finditer as re_finditer
|
|
26
|
+
from ascii7io import int2str, str2int, int2raw_str # users are not expected to need int2raw_str
|
|
27
|
+
import cantorpairs
|
|
28
|
+
cp = cantorpairs
|
|
29
|
+
|
|
30
|
+
__version__ = "1.1"
|
|
31
|
+
|
|
32
|
+
# ~ In order to omit Gödel numbers too high, around 300 decimal digits,
|
|
33
|
+
# ~ LIMIT_GNUM set to 2**1000 but computed much faster via bit shift
|
|
34
|
+
|
|
35
|
+
LIMIT_GNUM = 2 << 999
|
|
36
|
+
|
|
37
|
+
class FunData(dict):
|
|
38
|
+
'Simple class for PReFScript functions data'
|
|
39
|
+
|
|
40
|
+
def __init__(self, nick = None, comment = None, how_def = None, def_on = None):
|
|
41
|
+
dict.__init__(self)
|
|
42
|
+
self["nick"] = nick # function name
|
|
43
|
+
self["comment"] = comment
|
|
44
|
+
self["how_def"] = how_def
|
|
45
|
+
self["def_on"] = def_on
|
|
46
|
+
|
|
47
|
+
def __str__(self):
|
|
48
|
+
return self["nick"] + "\n " + self["comment"]
|
|
49
|
+
|
|
50
|
+
def how_def(self):
|
|
51
|
+
if self["how_def"] == "basic":
|
|
52
|
+
return "basic"
|
|
53
|
+
return self["how_def"] + ": " + ' '.join(on_what for on_what in self["def_on"])
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def mu(x, test):
|
|
57
|
+
"ancillary linear search function for implementing mu-minimization"
|
|
58
|
+
z = 0
|
|
59
|
+
while not test(cp.dp(x, z)):
|
|
60
|
+
z += 1
|
|
61
|
+
return z
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def prim_rec(is_base, base, recurse):
|
|
65
|
+
"ancillary course-of-values primitive recursion more efficient than by minimization"
|
|
66
|
+
|
|
67
|
+
def c_of_v(x):
|
|
68
|
+
"create the full course of values"
|
|
69
|
+
sq = 0
|
|
70
|
+
for y in range(x + 1):
|
|
71
|
+
new = base(y) if is_base(y) else recurse(cp.dp(y, sq))
|
|
72
|
+
sq = cp.dp(new, sq)
|
|
73
|
+
return sq
|
|
74
|
+
|
|
75
|
+
return lambda x: cp.pr_L(c_of_v(x))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class Parser:
|
|
79
|
+
'''Prepare an re-based parser to be used upon reading scripts
|
|
80
|
+
The single Parser with single parse generator is likely to mess up
|
|
81
|
+
the recursive imports, I bet it does not work yet.
|
|
82
|
+
'''
|
|
83
|
+
|
|
84
|
+
def __init__(self):
|
|
85
|
+
"group names require Python >= 3.11"
|
|
86
|
+
from re import compile as re_compile, finditer as re_finditer
|
|
87
|
+
about = r"\s*\.about(?P<about>.*)\n" # arbitrary documentation
|
|
88
|
+
pragma = r"\s*\.pragma\s+(?P<which>\w+):?\s+(?P<what>\w+)\s*" # compilation directives
|
|
89
|
+
importing = r"\s*\.import\s+(?P<to_import>[\w._-]+)\s*" # additional external script
|
|
90
|
+
a7str = r'''(?P<quote>['"])(?P<a7str>.*)(?P=quote)'''
|
|
91
|
+
startdef = r"\d+\s+define\:\s*"
|
|
92
|
+
group1_nick = r"(?P<nick>\w+)\s+"
|
|
93
|
+
group2_comment = r"\[\s*(?P<comment>(\w|\s|[.,:;<>=)(?!/+*-])+)\]\s+"
|
|
94
|
+
group4_how = r"(?P<how>pair|comp|mu|compair|primrec|ascii_const)\s+"
|
|
95
|
+
group5_on_what = r"((?P<on_what>([a-zA-Z_]\w*\s+)+)|" + a7str + ")" # nick args required not to start with a number
|
|
96
|
+
define = (startdef +
|
|
97
|
+
group1_nick +
|
|
98
|
+
group2_comment +
|
|
99
|
+
group4_how +
|
|
100
|
+
group5_on_what)
|
|
101
|
+
self.the_parser = re_compile(define + '|' + about + '|' + pragma + '|' + importing)
|
|
102
|
+
|
|
103
|
+
def parse(self, source):
|
|
104
|
+
for thing in re_finditer(self.the_parser, source):
|
|
105
|
+
"can one find out non-matched portions to message the user about?"
|
|
106
|
+
things = thing.groupdict(default = '')
|
|
107
|
+
if about := things['about']:
|
|
108
|
+
yield 'about', about
|
|
109
|
+
if which := things['which']:
|
|
110
|
+
yield 'pragma', (which, things['what'])
|
|
111
|
+
if to_import := things['to_import']:
|
|
112
|
+
yield 'import', to_import
|
|
113
|
+
if nick := things['nick']:
|
|
114
|
+
if things['how'] == "ascii_const":
|
|
115
|
+
on_what = [things['a7str']]
|
|
116
|
+
else:
|
|
117
|
+
on_what = things['on_what'].split()
|
|
118
|
+
yield "define", FunData(nick, things['comment'], things['how'], on_what)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class SyntErr:
|
|
122
|
+
"handle syntactic errors in the script - VERY PRIMITIVE for the time being"
|
|
123
|
+
|
|
124
|
+
def __init__(self):
|
|
125
|
+
from sys import stderr
|
|
126
|
+
self.e = stderr
|
|
127
|
+
|
|
128
|
+
def report(self, nonfatal = False, info = ''):
|
|
129
|
+
"return value to be given to the valid field / alt: fatal here and nonvalid at script"
|
|
130
|
+
p = 'Nonf' if nonfatal else 'F'
|
|
131
|
+
print(p + 'atal error in PReFScript:', info, sep = '\n ', file = self.e)
|
|
132
|
+
return nonfatal
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class PReFScript:
|
|
137
|
+
|
|
138
|
+
def __init__(self, store_goedel_numbers = ""):
|
|
139
|
+
'''
|
|
140
|
+
Dicts for storing the functions:
|
|
141
|
+
main for the function data,
|
|
142
|
+
gnums for Gödel numbers,
|
|
143
|
+
pycode for Python runnable code,
|
|
144
|
+
key is always nick for all of them;
|
|
145
|
+
include here the basic functions;
|
|
146
|
+
their implementation assumes 'import cantorpairs as cp'
|
|
147
|
+
'''
|
|
148
|
+
self.valid = True # program is correct until proven wrong
|
|
149
|
+
self.main = dict() # RENAME some day, as I am using also 'main' for the main function to be called
|
|
150
|
+
self.strcode = dict()
|
|
151
|
+
self.pycode = dict()
|
|
152
|
+
self.gnums = dict()
|
|
153
|
+
self.abouts = list()
|
|
154
|
+
self.pragmas = ddict(str)
|
|
155
|
+
self.store_gnums = store_goedel_numbers
|
|
156
|
+
self.add_basic("k_1", "The constant 1 function", "lambda x: 1", 0)
|
|
157
|
+
self.add_basic("id", "The identity function", "lambda x: x", 1)
|
|
158
|
+
self.add_basic("s_tup", "Single-argument version of suffix tuple",
|
|
159
|
+
"lambda x: cp.s_tup(cp.pr_L(x), cp.pr_R(x))", 2)
|
|
160
|
+
self.add_basic("proj", "Single-argument version of projection",
|
|
161
|
+
"lambda x: cp.pr(cp.pr_L(x), cp.pr_R(x))", 3)
|
|
162
|
+
self.add_basic("add", "Addition x+y of the two components of input <x.y>",
|
|
163
|
+
"lambda x: cp.pr_L(x) + cp.pr_R(x)", 4)
|
|
164
|
+
self.add_basic("mul", "Multiplication x*y of the two components of input <x.y>",
|
|
165
|
+
"lambda x: cp.pr_L(x) * cp.pr_R(x)", 5)
|
|
166
|
+
self.add_basic("diff", "Modified difference max(0, x-y) of the two components of input <x.y>",
|
|
167
|
+
"lambda x: max(0, cp.pr_L(x) - cp.pr_R(x))", 6)
|
|
168
|
+
self.parser = Parser()
|
|
169
|
+
self.synt_err_handler = SyntErr()
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def add_basic(self, nick, comment, code, num):
|
|
173
|
+
data = FunData()
|
|
174
|
+
data["nick"] = nick
|
|
175
|
+
data["comment"] = comment
|
|
176
|
+
data["how_def"] = "basic"
|
|
177
|
+
data["def_on"] = tuple()
|
|
178
|
+
if self.store_gnums:
|
|
179
|
+
self.gnums[nick] = cp.dp(0, num)
|
|
180
|
+
self.main[nick] = data
|
|
181
|
+
self.strcode[nick] = code
|
|
182
|
+
self.pycode[nick] = eval(code, globals() | self.pycode)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def list(self, what = None, w_code = 0):
|
|
186
|
+
'''
|
|
187
|
+
if what is None: list everything
|
|
188
|
+
else: search for that what on the dicts
|
|
189
|
+
w_code 0: no code, 1: how and on what, 2: strcode also
|
|
190
|
+
Gödel number printed depending on self.store_gnums and how big it is
|
|
191
|
+
'''
|
|
192
|
+
def list_one(nick, w_code):
|
|
193
|
+
print("\n" + str(self.main[nick]))
|
|
194
|
+
if w_code:
|
|
195
|
+
'print how it is defined'
|
|
196
|
+
print(" " + self.main[nick].how_def())
|
|
197
|
+
if w_code == 2:
|
|
198
|
+
'print also the Python code in this case only'
|
|
199
|
+
print(" " + self.strcode[nick])
|
|
200
|
+
if self.store_gnums:
|
|
201
|
+
if nick in self.gnums:
|
|
202
|
+
gnum = self.gnums[nick]
|
|
203
|
+
print(" Gödel number:", gnum,
|
|
204
|
+
"= <" + str(cp.pr_L(gnum)) + "." + str(cp.pr_R(gnum)) + ">")
|
|
205
|
+
else:
|
|
206
|
+
self.valid &= self.synt_err_handler(fatal = False, info = "Gödel number too large, omitted.")
|
|
207
|
+
|
|
208
|
+
if what is not None:
|
|
209
|
+
list_one(what, w_code)
|
|
210
|
+
else:
|
|
211
|
+
for nick in self.main:
|
|
212
|
+
list_one(nick, w_code)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def define(self, new_funct):
|
|
216
|
+
'here comes a new function to add to the collection'
|
|
217
|
+
|
|
218
|
+
if (nick := new_funct['nick']) in self.main:
|
|
219
|
+
'repeated nick, check for consistency'
|
|
220
|
+
if (self.main[nick]["how_def"] != new_funct['how_def'] or
|
|
221
|
+
self.main[nick]["def_on"] != new_funct['def_on']):
|
|
222
|
+
self.valid &= self.synt_err_handler.report(nonfatal = False,
|
|
223
|
+
info = f"Repeated, inconsistent definitions for function '{nick}' found.")
|
|
224
|
+
else:
|
|
225
|
+
self.main[nick] = new_funct
|
|
226
|
+
on_what = new_funct['def_on']
|
|
227
|
+
|
|
228
|
+
if new_funct['how_def'] == "comp":
|
|
229
|
+
self.strcode[nick] = "lambda x: " + on_what[0] + "(" + on_what[1] + "(x))"
|
|
230
|
+
if self.store_gnums and on_what[0] in self.gnums and on_what[1] in self.gnums:
|
|
231
|
+
gnum = cp.dp(1, cp.dp(self.gnums[on_what[0]], self.gnums[on_what[1]]))
|
|
232
|
+
if gnum < LIMIT_GNUM:
|
|
233
|
+
self.gnums[nick] = gnum
|
|
234
|
+
else:
|
|
235
|
+
self.valid &= self.synt_err_handler.report(nonfatal = False,
|
|
236
|
+
info = f"Gödel number for '{nick}' too large, omitted.")
|
|
237
|
+
|
|
238
|
+
elif new_funct['how_def'] == "pair":
|
|
239
|
+
self.strcode[nick] = "lambda x: cp.dp(" + on_what[0] + "(x), " + on_what[1] + "(x))"
|
|
240
|
+
if self.store_gnums and on_what[0] in self.gnums and on_what[1] in self.gnums:
|
|
241
|
+
gnum = cp.dp(2, cp.dp(self.gnums[on_what[0]], self.gnums[on_what[1]]))
|
|
242
|
+
if gnum < LIMIT_GNUM:
|
|
243
|
+
self.gnums[nick] = gnum
|
|
244
|
+
else:
|
|
245
|
+
self.valid &= self.synt_err_handler.report(nonfatal = False,
|
|
246
|
+
info = f"Gödel number for '{nick}' too large, omitted.")
|
|
247
|
+
|
|
248
|
+
elif new_funct['how_def'] == "mu":
|
|
249
|
+
self.strcode[nick] = "lambda x: mu(x, " + on_what[0] + ")"
|
|
250
|
+
if self.store_gnums and on_what[0] in self.gnums:
|
|
251
|
+
gnum = cp.dp(3, self.gnums[on_what[0]])
|
|
252
|
+
if gnum < LIMIT_GNUM:
|
|
253
|
+
self.gnums[nick] = gnum
|
|
254
|
+
else:
|
|
255
|
+
self.valid &= self.synt_err_handler.report(nonfatal = False,
|
|
256
|
+
info = f"Gödel number for '{nick}' too large, omitted.")
|
|
257
|
+
|
|
258
|
+
elif new_funct['how_def'] == "compair":
|
|
259
|
+
if not self.pragmas['extended']:
|
|
260
|
+
self.valid &= self.synt_err_handler.report(nonfatal = True,
|
|
261
|
+
info = "Use of compair requires '.pragma extended: True', changed.")
|
|
262
|
+
self.pragmas['extended'] = 'True'
|
|
263
|
+
self.strcode[nick] = "lambda x: " + on_what[0] + "( cp.dp(" + on_what[1] + "(x), " + on_what[2] + "(x)))"
|
|
264
|
+
if (self.store_gnums and on_what[0] in self.gnums and
|
|
265
|
+
on_what[1] in self.gnums and on_what[2] in self.gnums):
|
|
266
|
+
gnum = cp.dp(1, cp.dp(self.gnums[on_what[0]],
|
|
267
|
+
cp.dp(2, cp.dp(self.gnums[on_what[1]], self.gnums[on_what[2]]))))
|
|
268
|
+
if gnum < LIMIT_GNUM:
|
|
269
|
+
self.gnums[nick] = gnum
|
|
270
|
+
else:
|
|
271
|
+
self.valid &= self.synt_err_handler.report(nonfatal = False,
|
|
272
|
+
info = f"Gödel number for '{nick}' too large, omitted.")
|
|
273
|
+
|
|
274
|
+
elif new_funct['how_def'] == "primrec":
|
|
275
|
+
if not self.pragmas['extended']:
|
|
276
|
+
self.valid &= self.synt_err_handler.report(nonfatal = True,
|
|
277
|
+
info = "Use of primrec requires '.pragma extended: True', changed.")
|
|
278
|
+
self.pragmas['extended'] = 'True'
|
|
279
|
+
self.strcode[nick] = "prim_rec(" + on_what[0] + ", " + on_what[1] + ", " + on_what[2] + ")"
|
|
280
|
+
if (self.store_gnums and on_what[1] in self.gnums and on_what[2] in self.gnums):
|
|
281
|
+
gnum = cp.dp(4, cp.dp(int(on_what[0]),
|
|
282
|
+
cp.dp(self.gnums[on_what[1]], self.gnums[on_what[2]])))
|
|
283
|
+
if gnum < LIMIT_GNUM:
|
|
284
|
+
self.gnums[nick] = gnum
|
|
285
|
+
else:
|
|
286
|
+
self.valid &= self.synt_err_handler.report(nonfatal = False,
|
|
287
|
+
info = f"Gödel number for '{nick}' too large, omitted.")
|
|
288
|
+
|
|
289
|
+
else:
|
|
290
|
+
"ascii_const, as no other 'how' captured by parser - kept out of the Goedel numbering for the time being"
|
|
291
|
+
if not self.pragmas['extended']:
|
|
292
|
+
self.valid &= self.synt_err_handler.report(nonfatal = True,
|
|
293
|
+
info = "Use of ascii constants requires '.pragma extended: True', changed.")
|
|
294
|
+
self.pragmas['extended'] = 'True'
|
|
295
|
+
self.strcode[nick] = "lambda x: str2int( '" + on_what[0] + "' )"
|
|
296
|
+
|
|
297
|
+
self.pycode[nick] = eval(self.strcode[nick], globals() | self.pycode)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def to_python(self, what):
|
|
301
|
+
'returns the Python-runnable version of the function'
|
|
302
|
+
if what not in self.pycode:
|
|
303
|
+
self.valid &= self.synt_err_handler.report(nonfatal = False,
|
|
304
|
+
info = f"No Python code for function '{what}' found.")
|
|
305
|
+
return None
|
|
306
|
+
return self.pycode[what]
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def find_script_in_file(self, filename, main):
|
|
310
|
+
try:
|
|
311
|
+
with open(filename) as infile:
|
|
312
|
+
return infile.read()
|
|
313
|
+
except IOError:
|
|
314
|
+
if main:
|
|
315
|
+
self.valid &= self.synt_err_handler.report(nonfatal = False,
|
|
316
|
+
info = f"Script {filename} not found.")
|
|
317
|
+
else:
|
|
318
|
+
self.valid &= self.synt_err_handler.report(nonfatal = True,
|
|
319
|
+
info = f"Imported script {filename} not found.")
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def load(self, filename, main = True):
|
|
323
|
+
'load in definitions from .prfs file(s) - use .import to recurse into further loading'
|
|
324
|
+
if filename.endswith('.prfs'):
|
|
325
|
+
self.valid &= self.synt_err_handler.report(nonfatal = True,
|
|
326
|
+
info = f"File name {filename} NOT expected to include the '.prfs' extension.")
|
|
327
|
+
else:
|
|
328
|
+
filename += '.prfs'
|
|
329
|
+
script = self.find_script_in_file(filename, main)
|
|
330
|
+
if not script:
|
|
331
|
+
return None
|
|
332
|
+
lastread = None
|
|
333
|
+
for label, what in self.parser.parse(script):
|
|
334
|
+
'make the FunData or store the about or the pragma or the import'
|
|
335
|
+
if label == 'pragma':
|
|
336
|
+
if main:
|
|
337
|
+
"pragmas in main file are stored"
|
|
338
|
+
self.pragmas[what[0]] = what[1]
|
|
339
|
+
elif what[0] == 'extended' and what[1] == 'True':
|
|
340
|
+
"pragmas in imported files are ignored except extended when set to True"
|
|
341
|
+
if self.pragmas['extended'] != 'True':
|
|
342
|
+
self.valid &= self.synt_err_handler.report(nonfatal = True,
|
|
343
|
+
info = f"Warning: the .pragma extended declaration found in {filename} affects globally.")
|
|
344
|
+
self.pragmas[what[0]] = what[1]
|
|
345
|
+
if label == 'about':
|
|
346
|
+
self.abouts.append('about ' + filename + ': ' + what)
|
|
347
|
+
if label == 'define':
|
|
348
|
+
self.define(what)
|
|
349
|
+
lastread = what['nick'] # nickname of the last function defined, used for default main
|
|
350
|
+
if label == "import":
|
|
351
|
+
"non-main recursive call"
|
|
352
|
+
self.load(what, main = False)
|
|
353
|
+
if main and not self.pragmas['main']:
|
|
354
|
+
self.valid &= self.synt_err_handler.report(nonfatal = True,
|
|
355
|
+
info = f"No main .pragma found in '{filename}'.")
|
|
356
|
+
if self.valid and lastread:
|
|
357
|
+
"warn that main assumed is lastread"
|
|
358
|
+
self.valid &= self.synt_err_handler.report(nonfatal = True,
|
|
359
|
+
info = f"Function '{lastread}' guessed to be the main function, cross fingers.")
|
|
360
|
+
self.pragmas['main'] = lastread
|
|
361
|
+
elif self.valid:
|
|
362
|
+
self.valid &= self.synt_err_handler.report(nonfatal = False,
|
|
363
|
+
info = f"No guess available for the main function.")
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def dialog(self):
|
|
367
|
+
nick = input("Function name? ")
|
|
368
|
+
comment = input("What is it? ")
|
|
369
|
+
how = input("How is it made? [pair or comp or mu] ")
|
|
370
|
+
on_what = input("Applied to what? [1 or 2 space-sep names] ")
|
|
371
|
+
self.define(FunData(nick.strip(), comment.strip(), how.strip(), tuple(on_what.split())))
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def check_names(self):
|
|
375
|
+
"checks that all function names needed to run main have been defined"
|
|
376
|
+
|
|
377
|
+
def check_name(self, name, need = 'pragma main'):
|
|
378
|
+
if name not in checked:
|
|
379
|
+
checked.add(name)
|
|
380
|
+
if name in self.main:
|
|
381
|
+
if self.main[name]['how_def'] != "ascii_const":
|
|
382
|
+
for nname in self.main[name]['def_on']:
|
|
383
|
+
check_name(self, nname, f"'{name}'")
|
|
384
|
+
else:
|
|
385
|
+
"newly found undefined name"
|
|
386
|
+
self.valid &= self.synt_err_handler.report(nonfatal = False,
|
|
387
|
+
info = f"Function '{name}' not found but needed by {need}.")
|
|
388
|
+
|
|
389
|
+
checked = set()
|
|
390
|
+
check_name(self, self.pragmas['main'])
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def run():
|
|
394
|
+
'Stand-alone CLI command to be handled as entry point - no Goedel numbers stored; handle the filename as argument'
|
|
395
|
+
from argparse import ArgumentParser
|
|
396
|
+
from pytokr import pytokr
|
|
397
|
+
read, loop = pytokr(iter = True)
|
|
398
|
+
aparser = ArgumentParser(prog = 'prefscript',
|
|
399
|
+
description = 'Partial Recursive Functions Scripting interpreter')
|
|
400
|
+
aparser.add_argument('filename', help = 'script filename to be run, extension .prfs assumed')
|
|
401
|
+
aparser.add_argument('-a', '--about',
|
|
402
|
+
help = "show contents of .about directives in file and .import'd files before running",
|
|
403
|
+
action = "store_true")
|
|
404
|
+
aparser.add_argument('-v', '--version',
|
|
405
|
+
action = "version", version = f'{__version__}')
|
|
406
|
+
args = aparser.parse_args()
|
|
407
|
+
f = PReFScript()
|
|
408
|
+
f.load(args.filename)
|
|
409
|
+
if f.valid:
|
|
410
|
+
f.check_names()
|
|
411
|
+
if f.valid:
|
|
412
|
+
'run it on data from stdin according to input/output/main pragmas'
|
|
413
|
+
if args.about:
|
|
414
|
+
print('\n'.join(f.abouts))
|
|
415
|
+
|
|
416
|
+
r = f.to_python(f.pragmas["main"])
|
|
417
|
+
|
|
418
|
+
if f.pragmas["output"] in ('', "int"):
|
|
419
|
+
post = lambda x: x
|
|
420
|
+
elif f.pragmas["output"] == "ascii":
|
|
421
|
+
post = int2str
|
|
422
|
+
elif f.pragmas["output"] == "bool":
|
|
423
|
+
post = bool
|
|
424
|
+
|
|
425
|
+
if f.pragmas["input"] in ('', "int"):
|
|
426
|
+
arg = int(input())
|
|
427
|
+
elif f.pragmas["input"] == "none":
|
|
428
|
+
arg = 666 # for one
|
|
429
|
+
elif f.pragmas["input"] == "intseq":
|
|
430
|
+
arg = cp.tup_i(map(int, loop()))
|
|
431
|
+
|
|
432
|
+
if f.valid:
|
|
433
|
+
print(post(r(arg)))
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
if __name__ == "__main__":
|
|
437
|
+
run()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# ~ import scaff.cantorpairs as cp
|
|
2
|
+
# ~ from prefscript import PReFScript
|
|
3
|
+
from prefscript import PReFScript, cp
|
|
4
|
+
my_fs = PReFScript()
|
|
5
|
+
# ~ my_fs = PReFScript("Store Gödel numbers")
|
|
6
|
+
my_fs.load("script_sign.prfs")
|
|
7
|
+
my_fs.list(w_code = 1)
|
|
8
|
+
my_fs.load("script_proj.prfs")
|
|
9
|
+
my_fs.load("script_bool.prfs")
|
|
10
|
+
my_fs.load("script_compar.prfs")
|
|
11
|
+
my_fs.load("script_division.prfs")
|
|
12
|
+
my_fs.list()
|
|
13
|
+
d = my_fs.to_python("div")
|
|
14
|
+
print(d(cp.dp(14, 5)))
|
|
15
|
+
print(d(cp.dp(15, 5)))
|
|
16
|
+
my_fs.list("swap", w_code = 1)
|
|
17
|
+
my_fs.list("sign", w_code = 2)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from time import process_time
|
|
2
|
+
from prefscript import PReFScript
|
|
3
|
+
import scaff.cantorpairs as cp
|
|
4
|
+
|
|
5
|
+
ff = PReFScript()
|
|
6
|
+
ff.load("fact_00.prfs")
|
|
7
|
+
f = ff.to_python("fact")
|
|
8
|
+
|
|
9
|
+
def t(n):
|
|
10
|
+
try:
|
|
11
|
+
start = process_time()
|
|
12
|
+
r = f(n)
|
|
13
|
+
tm = process_time() - start
|
|
14
|
+
except:
|
|
15
|
+
print(f"Stopped; time: {1000*(process_time() - start):3.6f} ms")
|
|
16
|
+
print(f"Outcome: {r}; time: {1000*(tm):3.6f} ms")
|
|
17
|
+
|
|
18
|
+
t(int(input("n: ")))
|
|
19
|
+
|
|
20
|
+
|