prefscript 1.1__tar.gz → 1.2__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 → prefscript-1.2}/PKG-INFO +6 -6
- {prefscript-1.1 → prefscript-1.2}/README.md +3 -4
- {prefscript-1.1 → prefscript-1.2}/pyproject.toml +1 -1
- {prefscript-1.1 → prefscript-1.2}/src/ascii7io.py +9 -0
- prefscript-1.2/src/fundata.py +44 -0
- {prefscript-1.1 → prefscript-1.2}/src/prefscript.egg-info/PKG-INFO +6 -6
- {prefscript-1.1 → prefscript-1.2}/src/prefscript.egg-info/SOURCES.txt +3 -3
- {prefscript-1.1 → prefscript-1.2}/src/prefscript.egg-info/top_level.txt +2 -0
- {prefscript-1.1 → prefscript-1.2}/src/prefscript.py +117 -131
- prefscript-1.2/src/v1parser.py +76 -0
- prefscript-1.1/tests/test.py +0 -17
- prefscript-1.1/tests/test_fact.py +0 -20
- {prefscript-1.1 → prefscript-1.2}/LICENSE +0 -0
- {prefscript-1.1 → prefscript-1.2}/setup.cfg +0 -0
- {prefscript-1.1 → prefscript-1.2}/src/cantorpairs/__init__.py +0 -0
- {prefscript-1.1 → prefscript-1.2}/src/cantorpairs/src/cantorpairs.py +0 -0
- {prefscript-1.1 → prefscript-1.2}/src/cantorpairs/src/cp_ex.py +0 -0
- {prefscript-1.1 → prefscript-1.2}/src/prefscript.egg-info/dependency_links.txt +0 -0
- {prefscript-1.1 → prefscript-1.2}/src/prefscript.egg-info/entry_points.txt +0 -0
- {prefscript-1.1 → prefscript-1.2}/src/prefscript.egg-info/requires.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: prefscript
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2
|
|
4
4
|
Summary: Partial Recursive Functions for Scripting
|
|
5
5
|
Author-email: José Luis Balcázar <jose.luis.balcazar@upc.edu>
|
|
6
6
|
Project-URL: Homepage, https://github.com/balqui/prefscript
|
|
@@ -12,15 +12,16 @@ Requires-Python: >=3.10
|
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
14
|
Requires-Dist: pytokr>=1.0
|
|
15
|
+
Dynamic: license-file
|
|
15
16
|
|
|
16
17
|
# PReFScript:
|
|
17
18
|
## Partial Recursive Functions for Scripting
|
|
18
19
|
|
|
19
|
-
Author: Jose
|
|
20
|
+
Author: Jose Luis Balcazar, ORCID 0000-0003-4248-4528
|
|
20
21
|
|
|
21
22
|
Project started: mid Germinal 2003.
|
|
22
23
|
|
|
23
|
-
Current version: 1.
|
|
24
|
+
Current version: 1.2, mid Messidor 2026.
|
|
24
25
|
|
|
25
26
|
Copyleft: MIT License (https://en.wikipedia.org/wiki/MIT_License)
|
|
26
27
|
|
|
@@ -54,7 +55,6 @@ Thus, you have available two main ways of programming in PReFScript.
|
|
|
54
55
|
|
|
55
56
|
### Installation and ways to use PReFScript functions
|
|
56
57
|
|
|
57
|
-
See [doc.md](https://github.com/balqui/prefscript/blob/main/docs/doc.md)
|
|
58
|
-
for all the details (somewhat incomplete as of today).
|
|
58
|
+
See [doc.md](https://github.com/balqui/prefscript/blob/main/docs/doc.md).
|
|
59
59
|
|
|
60
60
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# PReFScript:
|
|
2
2
|
## Partial Recursive Functions for Scripting
|
|
3
3
|
|
|
4
|
-
Author: Jose
|
|
4
|
+
Author: Jose Luis Balcazar, ORCID 0000-0003-4248-4528
|
|
5
5
|
|
|
6
6
|
Project started: mid Germinal 2003.
|
|
7
7
|
|
|
8
|
-
Current version: 1.
|
|
8
|
+
Current version: 1.2, mid Messidor 2026.
|
|
9
9
|
|
|
10
10
|
Copyleft: MIT License (https://en.wikipedia.org/wiki/MIT_License)
|
|
11
11
|
|
|
@@ -39,7 +39,6 @@ Thus, you have available two main ways of programming in PReFScript.
|
|
|
39
39
|
|
|
40
40
|
### Installation and ways to use PReFScript functions
|
|
41
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).
|
|
42
|
+
See [doc.md](https://github.com/balqui/prefscript/blob/main/docs/doc.md).
|
|
44
43
|
|
|
45
44
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "prefscript"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.2"
|
|
8
8
|
authors = [ { name = "José Luis Balcázar", email = "jose.luis.balcazar@upc.edu" }, ]
|
|
9
9
|
description = "Partial Recursive Functions for Scripting"
|
|
10
10
|
readme = "README.md"
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
'''
|
|
2
|
+
Project started mid Germinal 2023:
|
|
3
|
+
PReFScript: A Partial Recursive Functions Lab
|
|
4
|
+
|
|
5
|
+
Ancillary module, early Thermidor 2024:
|
|
2
6
|
Single-integer encoding and decoding of (printable) 7-bit ASCII strings
|
|
7
|
+
Currently employed for versions up to 1.2, this docstring expanded
|
|
8
|
+
by mid Messidor 2026 with no change to code.
|
|
9
|
+
|
|
10
|
+
Author: Jose L Balcazar, ORCID 0000-0003-4248-4528
|
|
11
|
+
Copyleft: MIT License (https://en.wikipedia.org/wiki/MIT_License)
|
|
3
12
|
'''
|
|
4
13
|
|
|
5
14
|
TWO_pow_7 = 1 << 7
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Project started mid Germinal 2003:
|
|
3
|
+
PReFScript: A Partial Recursive Functions Lab
|
|
4
|
+
|
|
5
|
+
Module version mid Messidor 2026:
|
|
6
|
+
fundata: class FunData storing all necessary information about
|
|
7
|
+
one function, includes as well ancillary algorithms mu and prim_rec
|
|
8
|
+
(file distribution slight refactoring on the monolithic design of Thermidor 2004)
|
|
9
|
+
|
|
10
|
+
Author: Jose L Balcazar, ORCID 0000-0003-4248-4528, april 2023 onwards
|
|
11
|
+
Copyleft: MIT License (https://en.wikipedia.org/wiki/MIT_License)
|
|
12
|
+
|
|
13
|
+
Each function in a script has: associated Gödel number, nickname,
|
|
14
|
+
comments, and code (string); also, the last operation used to
|
|
15
|
+
construct it. Then, in a separate dict at script level (and used
|
|
16
|
+
as namespace for eval calls), a runnable version of the code.
|
|
17
|
+
|
|
18
|
+
Nicknames are alphanum strings not starting with a number (no surprise).
|
|
19
|
+
'''
|
|
20
|
+
|
|
21
|
+
import cantorpairs as cp
|
|
22
|
+
|
|
23
|
+
__version__ = "1.2"
|
|
24
|
+
|
|
25
|
+
class FunData(dict):
|
|
26
|
+
'Simple class for PReFScript functions data'
|
|
27
|
+
|
|
28
|
+
def __init__(self, nick = None, comment = None, how_def = None, def_on = None):
|
|
29
|
+
dict.__init__(self)
|
|
30
|
+
self["nick"] = nick # function name
|
|
31
|
+
self["comment"] = comment
|
|
32
|
+
self["how_def"] = how_def
|
|
33
|
+
self["def_on"] = def_on
|
|
34
|
+
|
|
35
|
+
def __str__(self):
|
|
36
|
+
return self["nick"] + "\n " + self["comment"]
|
|
37
|
+
|
|
38
|
+
def how_def(self):
|
|
39
|
+
if self["how_def"] == "basic":
|
|
40
|
+
return "basic"
|
|
41
|
+
return self["how_def"] + ": " + ' '.join(on_what for on_what in self["def_on"])
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# ~ Should add unit testing here
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: prefscript
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2
|
|
4
4
|
Summary: Partial Recursive Functions for Scripting
|
|
5
5
|
Author-email: José Luis Balcázar <jose.luis.balcazar@upc.edu>
|
|
6
6
|
Project-URL: Homepage, https://github.com/balqui/prefscript
|
|
@@ -12,15 +12,16 @@ Requires-Python: >=3.10
|
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
14
|
Requires-Dist: pytokr>=1.0
|
|
15
|
+
Dynamic: license-file
|
|
15
16
|
|
|
16
17
|
# PReFScript:
|
|
17
18
|
## Partial Recursive Functions for Scripting
|
|
18
19
|
|
|
19
|
-
Author: Jose
|
|
20
|
+
Author: Jose Luis Balcazar, ORCID 0000-0003-4248-4528
|
|
20
21
|
|
|
21
22
|
Project started: mid Germinal 2003.
|
|
22
23
|
|
|
23
|
-
Current version: 1.
|
|
24
|
+
Current version: 1.2, mid Messidor 2026.
|
|
24
25
|
|
|
25
26
|
Copyleft: MIT License (https://en.wikipedia.org/wiki/MIT_License)
|
|
26
27
|
|
|
@@ -54,7 +55,6 @@ Thus, you have available two main ways of programming in PReFScript.
|
|
|
54
55
|
|
|
55
56
|
### Installation and ways to use PReFScript functions
|
|
56
57
|
|
|
57
|
-
See [doc.md](https://github.com/balqui/prefscript/blob/main/docs/doc.md)
|
|
58
|
-
for all the details (somewhat incomplete as of today).
|
|
58
|
+
See [doc.md](https://github.com/balqui/prefscript/blob/main/docs/doc.md).
|
|
59
59
|
|
|
60
60
|
|
|
@@ -2,7 +2,9 @@ LICENSE
|
|
|
2
2
|
README.md
|
|
3
3
|
pyproject.toml
|
|
4
4
|
src/ascii7io.py
|
|
5
|
+
src/fundata.py
|
|
5
6
|
src/prefscript.py
|
|
7
|
+
src/v1parser.py
|
|
6
8
|
src/cantorpairs/__init__.py
|
|
7
9
|
src/cantorpairs/src/cantorpairs.py
|
|
8
10
|
src/cantorpairs/src/cp_ex.py
|
|
@@ -11,6 +13,4 @@ src/prefscript.egg-info/SOURCES.txt
|
|
|
11
13
|
src/prefscript.egg-info/dependency_links.txt
|
|
12
14
|
src/prefscript.egg-info/entry_points.txt
|
|
13
15
|
src/prefscript.egg-info/requires.txt
|
|
14
|
-
src/prefscript.egg-info/top_level.txt
|
|
15
|
-
tests/test.py
|
|
16
|
-
tests/test_fact.py
|
|
16
|
+
src/prefscript.egg-info/top_level.txt
|
|
@@ -1,57 +1,39 @@
|
|
|
1
1
|
'''
|
|
2
|
+
Project started mid Germinal 2023:
|
|
2
3
|
PReFScript: A Partial Recursive Functions Lab
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
Module version mid Messidor 2026:
|
|
6
|
+
prefscript with main class PReFScript and run entrypoint only
|
|
7
|
+
(file distribution slight refactoring on the monolithic design of Thermidor 2024)
|
|
5
8
|
|
|
6
|
-
Author: Jose L Balcazar, ORCID 0000-0003-4248-4528
|
|
9
|
+
Author: Jose L Balcazar, ORCID 0000-0003-4248-4528
|
|
7
10
|
Copyleft: MIT License (https://en.wikipedia.org/wiki/MIT_License)
|
|
8
11
|
|
|
9
|
-
Project started: mid Germinal 2003.
|
|
10
|
-
Current version: 0.5, mid Thermidor 2024.
|
|
11
|
-
|
|
12
12
|
A Python-based environment to explore and experiment with partial
|
|
13
13
|
recursive functions; naturally doubles as a (purely functional)
|
|
14
14
|
programming language, but it is not intended to be used as such.
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
construct it. Then, in a separate dict (used as namespace for
|
|
16
|
+
Functions are stored in FunData instances.
|
|
17
|
+
Then, in a separate dict (used as namespace for
|
|
19
18
|
eval calls), a runnable version of the code.
|
|
20
|
-
|
|
21
|
-
Nicknames are alphanum strings not starting with a number (no surprise).
|
|
22
19
|
'''
|
|
23
20
|
|
|
24
21
|
from collections import defaultdict as ddict
|
|
25
|
-
from
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
from ascii7io import int2str, str2int
|
|
23
|
+
import cantorpairs as cp
|
|
24
|
+
|
|
25
|
+
from v1parser import Parser, SyntErr
|
|
26
|
+
from fundata import FunData
|
|
29
27
|
|
|
30
|
-
__version__ = "1.
|
|
28
|
+
__version__ = "1.2"
|
|
31
29
|
|
|
32
30
|
# ~ In order to omit Gödel numbers too high, around 300 decimal digits,
|
|
33
31
|
# ~ LIMIT_GNUM set to 2**1000 but computed much faster via bit shift
|
|
34
32
|
|
|
35
33
|
LIMIT_GNUM = 2 << 999
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
35
|
+
# ~ Ancillary functions to implement mu-linear search and parameterized
|
|
36
|
+
# ~ and unparameterized primitive recursion.
|
|
55
37
|
|
|
56
38
|
def mu(x, test):
|
|
57
39
|
"ancillary linear search function for implementing mu-minimization"
|
|
@@ -75,62 +57,19 @@ def prim_rec(is_base, base, recurse):
|
|
|
75
57
|
return lambda x: cp.pr_L(c_of_v(x))
|
|
76
58
|
|
|
77
59
|
|
|
78
|
-
|
|
79
|
-
|
|
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"
|
|
60
|
+
def par_prim_rec(is_base, base, recurse):
|
|
61
|
+
"primitive recursion with parameters - careful, names swapped from the course notes in Spanish"
|
|
123
62
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
return nonfatal
|
|
63
|
+
def c_of_v(z):
|
|
64
|
+
"create the adequate course of values, not full anymore"
|
|
65
|
+
x = cp.pr_R(z)
|
|
66
|
+
sq = 0 # empty sequence
|
|
67
|
+
for y in range(x + 1):
|
|
68
|
+
new = base(z) if is_base(y) else recurse(cp.dp(z, sq))
|
|
69
|
+
sq = cp.dp(new, sq)
|
|
70
|
+
return sq
|
|
133
71
|
|
|
72
|
+
return lambda x: cp.pr_L(c_of_v(x))
|
|
134
73
|
|
|
135
74
|
|
|
136
75
|
class PReFScript:
|
|
@@ -138,17 +77,17 @@ class PReFScript:
|
|
|
138
77
|
def __init__(self, store_goedel_numbers = ""):
|
|
139
78
|
'''
|
|
140
79
|
Dicts for storing the functions:
|
|
141
|
-
|
|
80
|
+
nicks to retrieve FunData from nick,
|
|
142
81
|
gnums for Gödel numbers,
|
|
143
82
|
pycode for Python runnable code,
|
|
144
83
|
key is always nick for all of them;
|
|
145
84
|
include here the basic functions;
|
|
146
85
|
their implementation assumes 'import cantorpairs as cp'
|
|
147
86
|
'''
|
|
148
|
-
self.valid = True
|
|
149
|
-
self.
|
|
87
|
+
self.valid = True # program is correct until proven wrong
|
|
88
|
+
self.nicks = dict() # maps nick to FunData which includes nick
|
|
150
89
|
self.strcode = dict()
|
|
151
|
-
self.pycode = dict()
|
|
90
|
+
self.pycode = dict() # doubles as 'checked' set in former check_names
|
|
152
91
|
self.gnums = dict()
|
|
153
92
|
self.abouts = list()
|
|
154
93
|
self.pragmas = ddict(str)
|
|
@@ -177,7 +116,7 @@ class PReFScript:
|
|
|
177
116
|
data["def_on"] = tuple()
|
|
178
117
|
if self.store_gnums:
|
|
179
118
|
self.gnums[nick] = cp.dp(0, num)
|
|
180
|
-
self.
|
|
119
|
+
self.nicks[nick] = data
|
|
181
120
|
self.strcode[nick] = code
|
|
182
121
|
self.pycode[nick] = eval(code, globals() | self.pycode)
|
|
183
122
|
|
|
@@ -190,10 +129,10 @@ class PReFScript:
|
|
|
190
129
|
Gödel number printed depending on self.store_gnums and how big it is
|
|
191
130
|
'''
|
|
192
131
|
def list_one(nick, w_code):
|
|
193
|
-
print("\n" + str(self.
|
|
132
|
+
print("\n" + str(self.nicks[nick]))
|
|
194
133
|
if w_code:
|
|
195
134
|
'print how it is defined'
|
|
196
|
-
print(" " + self.
|
|
135
|
+
print(" " + self.nicks[nick].how_def())
|
|
197
136
|
if w_code == 2:
|
|
198
137
|
'print also the Python code in this case only'
|
|
199
138
|
print(" " + self.strcode[nick])
|
|
@@ -208,21 +147,21 @@ class PReFScript:
|
|
|
208
147
|
if what is not None:
|
|
209
148
|
list_one(what, w_code)
|
|
210
149
|
else:
|
|
211
|
-
for nick in self.
|
|
150
|
+
for nick in self.nicks:
|
|
212
151
|
list_one(nick, w_code)
|
|
213
152
|
|
|
214
153
|
|
|
215
154
|
def define(self, new_funct):
|
|
216
155
|
'here comes a new function to add to the collection'
|
|
217
156
|
|
|
218
|
-
if (nick := new_funct['nick']) in self.
|
|
157
|
+
if (nick := new_funct['nick']) in self.nicks:
|
|
219
158
|
'repeated nick, check for consistency'
|
|
220
|
-
if (self.
|
|
221
|
-
self.
|
|
159
|
+
if (self.nicks[nick]["how_def"] != new_funct['how_def'] or
|
|
160
|
+
self.nicks[nick]["def_on"] != new_funct['def_on']):
|
|
222
161
|
self.valid &= self.synt_err_handler.report(nonfatal = False,
|
|
223
162
|
info = f"Repeated, inconsistent definitions for function '{nick}' found.")
|
|
224
163
|
else:
|
|
225
|
-
self.
|
|
164
|
+
self.nicks[nick] = new_funct
|
|
226
165
|
on_what = new_funct['def_on']
|
|
227
166
|
|
|
228
167
|
if new_funct['how_def'] == "comp":
|
|
@@ -286,6 +225,21 @@ class PReFScript:
|
|
|
286
225
|
self.valid &= self.synt_err_handler.report(nonfatal = False,
|
|
287
226
|
info = f"Gödel number for '{nick}' too large, omitted.")
|
|
288
227
|
|
|
228
|
+
elif new_funct['how_def'] == "parprimrec":
|
|
229
|
+
if not self.pragmas['extended']:
|
|
230
|
+
self.valid &= self.synt_err_handler.report(nonfatal = True,
|
|
231
|
+
info = "Use of parprimrec requires '.pragma extended: True', changed.")
|
|
232
|
+
self.pragmas['extended'] = 'True'
|
|
233
|
+
self.strcode[nick] = "par_prim_rec(" + on_what[0] + ", " + on_what[1] + ", " + on_what[2] + ")"
|
|
234
|
+
if (self.store_gnums and on_what[1] in self.gnums and on_what[2] in self.gnums):
|
|
235
|
+
gnum = cp.dp(5, cp.dp(int(on_what[0]),
|
|
236
|
+
cp.dp(self.gnums[on_what[1]], self.gnums[on_what[2]])))
|
|
237
|
+
if gnum < LIMIT_GNUM:
|
|
238
|
+
self.gnums[nick] = gnum
|
|
239
|
+
else:
|
|
240
|
+
self.valid &= self.synt_err_handler.report(nonfatal = False,
|
|
241
|
+
info = f"Gödel number for '{nick}' too large, omitted.")
|
|
242
|
+
|
|
289
243
|
else:
|
|
290
244
|
"ascii_const, as no other 'how' captured by parser - kept out of the Goedel numbering for the time being"
|
|
291
245
|
if not self.pragmas['extended']:
|
|
@@ -293,17 +247,18 @@ class PReFScript:
|
|
|
293
247
|
info = "Use of ascii constants requires '.pragma extended: True', changed.")
|
|
294
248
|
self.pragmas['extended'] = 'True'
|
|
295
249
|
self.strcode[nick] = "lambda x: str2int( '" + on_what[0] + "' )"
|
|
250
|
+
# ~ self.pycode[nick] = eval(self.strcode[nick])
|
|
296
251
|
|
|
297
|
-
|
|
252
|
+
# ~ if new_funct['how_def'] != "ascii_const":
|
|
253
|
+
# ~ self.pycode[nick] = eval(self.strcode[nick], globals() | self.pycode)
|
|
298
254
|
|
|
299
255
|
|
|
300
256
|
def to_python(self, what):
|
|
301
257
|
'returns the Python-runnable version of the function'
|
|
302
258
|
if what not in self.pycode:
|
|
303
|
-
self.
|
|
304
|
-
|
|
305
|
-
return
|
|
306
|
-
return self.pycode[what]
|
|
259
|
+
self.gen_py(what)
|
|
260
|
+
if self.valid:
|
|
261
|
+
return self.pycode[what]
|
|
307
262
|
|
|
308
263
|
|
|
309
264
|
def find_script_in_file(self, filename, main):
|
|
@@ -319,12 +274,14 @@ class PReFScript:
|
|
|
319
274
|
info = f"Imported script {filename} not found.")
|
|
320
275
|
|
|
321
276
|
|
|
322
|
-
def load(self, filename, main =
|
|
277
|
+
def load(self, filename, main = False):
|
|
323
278
|
'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:
|
|
279
|
+
# ~ if filename.endswith('.prfs'):
|
|
280
|
+
# ~ self.valid &= self.synt_err_handler.report(nonfatal = True,
|
|
281
|
+
# ~ info = f"File name {filename} NOT expected to include the '.prfs' extension.")
|
|
282
|
+
# ~ else:
|
|
283
|
+
# ~ filename += '.prfs'
|
|
284
|
+
if not filename.endswith('.prfs'):
|
|
328
285
|
filename += '.prfs'
|
|
329
286
|
script = self.find_script_in_file(filename, main)
|
|
330
287
|
if not script:
|
|
@@ -337,10 +294,17 @@ class PReFScript:
|
|
|
337
294
|
"pragmas in main file are stored"
|
|
338
295
|
self.pragmas[what[0]] = what[1]
|
|
339
296
|
elif what[0] == 'extended' and what[1] == 'True':
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
297
|
+
'''
|
|
298
|
+
pragmas in imported files are ignored except extended when set to True -
|
|
299
|
+
v1.2: now this is done with no warning, while v1.1 would warn about it,
|
|
300
|
+
but it was ugly to get the warning when calling PReFScript.load from
|
|
301
|
+
inside the interpreter, also the message is not that useful and the
|
|
302
|
+
current plan is that for 2.0 onwards "extended" will be the default
|
|
303
|
+
and expressed in a different way
|
|
304
|
+
'''
|
|
305
|
+
# ~ if self.pragmas['extended'] != 'True':
|
|
306
|
+
# ~ self.valid &= self.synt_err_handler.report(nonfatal = True,
|
|
307
|
+
# ~ info = f"Warning: the .pragma extended declaration found in {filename} affects globally.")
|
|
344
308
|
self.pragmas[what[0]] = what[1]
|
|
345
309
|
if label == 'about':
|
|
346
310
|
self.abouts.append('about ' + filename + ': ' + what)
|
|
@@ -371,23 +335,45 @@ class PReFScript:
|
|
|
371
335
|
self.define(FunData(nick.strip(), comment.strip(), how.strip(), tuple(on_what.split())))
|
|
372
336
|
|
|
373
337
|
|
|
374
|
-
def
|
|
375
|
-
|
|
338
|
+
def gen_py(self, name, need = 'pragma main'):
|
|
339
|
+
if name not in self.pycode:
|
|
340
|
+
"make sure never to loop on it"
|
|
341
|
+
self.pycode[name] = "None"
|
|
342
|
+
if name in self.nicks:
|
|
343
|
+
if self.nicks[name]['how_def'] != "ascii_const":
|
|
344
|
+
"the def_on part of an ascii_const is a mere string already handled"
|
|
345
|
+
for nname in self.nicks[name]['def_on']:
|
|
346
|
+
"we need first the recursive calls"
|
|
347
|
+
self.gen_py(nname, name)
|
|
348
|
+
self.pycode[name] = eval(self.strcode[name], globals() | self.pycode)
|
|
349
|
+
else:
|
|
350
|
+
"newly found undefined name"
|
|
351
|
+
self.valid &= self.synt_err_handler.report(nonfatal = False,
|
|
352
|
+
info = f"Function '{name}' not found but needed by {need}.")
|
|
353
|
+
elif self.pycode[name] == "None":
|
|
354
|
+
"already attempted, make sure not to fall in a definition loop"
|
|
355
|
+
self.valid &= self.synt_err_handler.report(nonfatal = False,
|
|
356
|
+
info = f"Function '{name}' belongs to a disallowed definition loop.")
|
|
376
357
|
|
|
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
358
|
|
|
389
|
-
|
|
390
|
-
|
|
359
|
+
# ~ Process moved to gen_py
|
|
360
|
+
# ~ def check_names(self):
|
|
361
|
+
# ~ "checks that all function names needed to run main have been defined"
|
|
362
|
+
|
|
363
|
+
# ~ def check_name(self, name, need = 'pragma main'):
|
|
364
|
+
# ~ if name not in checked:
|
|
365
|
+
# ~ checked.add(name)
|
|
366
|
+
# ~ if name in self.nicks:
|
|
367
|
+
# ~ if self.nicks[name]['how_def'] != "ascii_const":
|
|
368
|
+
# ~ for nname in self.nicks[name]['def_on']:
|
|
369
|
+
# ~ check_name(self, nname, f"'{name}'")
|
|
370
|
+
# ~ else:
|
|
371
|
+
# ~ "newly found undefined name"
|
|
372
|
+
# ~ self.valid &= self.synt_err_handler.report(nonfatal = False,
|
|
373
|
+
# ~ info = f"Function '{name}' not found but needed by {need}.")
|
|
374
|
+
|
|
375
|
+
# ~ checked = set()
|
|
376
|
+
# ~ check_name(self, self.pragmas['main'])
|
|
391
377
|
|
|
392
378
|
|
|
393
379
|
def run():
|
|
@@ -405,9 +391,9 @@ def run():
|
|
|
405
391
|
action = "version", version = f'{__version__}')
|
|
406
392
|
args = aparser.parse_args()
|
|
407
393
|
f = PReFScript()
|
|
408
|
-
f.load(args.filename)
|
|
394
|
+
f.load(args.filename, main = True)
|
|
409
395
|
if f.valid:
|
|
410
|
-
f.
|
|
396
|
+
f.gen_py(f.pragmas['main'])
|
|
411
397
|
if f.valid:
|
|
412
398
|
'run it on data from stdin according to input/output/main pragmas'
|
|
413
399
|
if args.about:
|
|
@@ -425,7 +411,7 @@ def run():
|
|
|
425
411
|
if f.pragmas["input"] in ('', "int"):
|
|
426
412
|
arg = int(input())
|
|
427
413
|
elif f.pragmas["input"] == "none":
|
|
428
|
-
arg =
|
|
414
|
+
arg = 42 # for one
|
|
429
415
|
elif f.pragmas["input"] == "intseq":
|
|
430
416
|
arg = cp.tup_i(map(int, loop()))
|
|
431
417
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Project started mid Germinal 2003:
|
|
3
|
+
PReFScript: A Partial Recursive Functions Lab
|
|
4
|
+
|
|
5
|
+
Module version mid Messidor 2026:
|
|
6
|
+
v1parser: re-based parser for versions up to 1.2
|
|
7
|
+
(file distribution slight refactoring on the monolithic design of Thermidor 2004)
|
|
8
|
+
|
|
9
|
+
Author: Jose L Balcazar, ORCID 0000-0003-4248-4528, april 2023 onwards
|
|
10
|
+
Copyleft: MIT License (https://en.wikipedia.org/wiki/MIT_License)
|
|
11
|
+
'''
|
|
12
|
+
|
|
13
|
+
from re import compile as re_compile, finditer as re_finditer
|
|
14
|
+
|
|
15
|
+
from fundata import FunData
|
|
16
|
+
|
|
17
|
+
__version__ = "1.2"
|
|
18
|
+
|
|
19
|
+
class Parser:
|
|
20
|
+
'''Prepare an re-based parser to be used upon reading scripts
|
|
21
|
+
The single Parser with single parse generator is likely to mess up
|
|
22
|
+
the recursive imports, I bet it does not work yet.
|
|
23
|
+
'''
|
|
24
|
+
|
|
25
|
+
def __init__(self):
|
|
26
|
+
"group names require Python >= 3.11"
|
|
27
|
+
from re import compile as re_compile, finditer as re_finditer
|
|
28
|
+
about = r"\s*\.about(?P<about>.*)\n" # arbitrary documentation
|
|
29
|
+
pragma = r"\s*\.pragma\s+(?P<which>\w+):?\s+(?P<what>\w+)\s*" # compilation directives
|
|
30
|
+
importing = r"\s*\.import\s+(?P<to_import>[\w._-]+)\s*" # additional external script
|
|
31
|
+
a7str = r'''(?P<quote>['"])(?P<a7str>.*)(?P=quote)'''
|
|
32
|
+
startdef = r"\d+\s+define\:\s*"
|
|
33
|
+
group1_nick = r"(?P<nick>\w+)\s+"
|
|
34
|
+
group2_comment = r"\[\s*(?P<comment>(\w|\s|[.,:;<>=)(?!/+*-])+)\]\s+"
|
|
35
|
+
group4_how = r"(?P<how>pair|comp|mu|compair|primrec|ascii_const|parprimrec)\s+"
|
|
36
|
+
group5_on_what = r"((?P<on_what>([a-zA-Z_]\w*\s+)+)|" + a7str + ")" # nick args required not to start with a number
|
|
37
|
+
define = (startdef +
|
|
38
|
+
group1_nick +
|
|
39
|
+
group2_comment +
|
|
40
|
+
group4_how +
|
|
41
|
+
group5_on_what)
|
|
42
|
+
self.the_parser = re_compile(define + '|' + about + '|' + pragma + '|' + importing)
|
|
43
|
+
|
|
44
|
+
def parse(self, source):
|
|
45
|
+
for thing in re_finditer(self.the_parser, source):
|
|
46
|
+
"can one find out non-matched portions to message the user about?"
|
|
47
|
+
things = thing.groupdict(default = '')
|
|
48
|
+
if about := things['about']:
|
|
49
|
+
yield 'about', about
|
|
50
|
+
if which := things['which']:
|
|
51
|
+
yield 'pragma', (which, things['what'])
|
|
52
|
+
if to_import := things['to_import']:
|
|
53
|
+
yield 'import', to_import
|
|
54
|
+
if nick := things['nick']:
|
|
55
|
+
if things['how'] == "ascii_const":
|
|
56
|
+
on_what = [things['a7str']]
|
|
57
|
+
else:
|
|
58
|
+
on_what = things['on_what'].split()
|
|
59
|
+
yield "define", FunData(nick, things['comment'], things['how'], on_what)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class SyntErr:
|
|
63
|
+
"handle syntactic errors in the script - VERY PRIMITIVE for the time being"
|
|
64
|
+
|
|
65
|
+
def __init__(self):
|
|
66
|
+
from sys import stderr
|
|
67
|
+
self.e = stderr
|
|
68
|
+
|
|
69
|
+
def report(self, nonfatal = False, info = ''):
|
|
70
|
+
"return value to be given to the valid field / alt: fatal here and nonvalid at script"
|
|
71
|
+
p = 'Nonf' if nonfatal else 'F'
|
|
72
|
+
print(p + 'atal error in PReFScript:', info, sep = '\n ', file = self.e)
|
|
73
|
+
return nonfatal
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# ~ Should add unit testing here
|
prefscript-1.1/tests/test.py
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
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)
|
|
@@ -1,20 +0,0 @@
|
|
|
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
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|