summaryrefslogtreecommitdiff
path: root/examples/cuneiform_python.py
blob: 9d4e74d502e297d6c37946d364a50a62ea06044b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#
# cuneiform_python.py
#
# Example showing how to create a custom Unicode set for parsing
#
# Copyright Paul McGuire, 2021
#
from typing import List, Tuple
import pyparsing as pp


class Cuneiform(pp.unicode_set):
    """Unicode set for Cuneiform Character Range"""

    _ranges: List[Tuple[int, ...]] = [
        (0x10380, 0x103d5),
        (0x12000, 0x123FF),
        (0x12400, 0x1247F),
    ]


# list out all valid identifier characters
# print(Cuneiform.identchars)


"""
Simple Cuneiform Python language transformer

Define Cuneiform "words"
    print: π’„‘π’‰Ώπ’…”π’‹«
    hello: π’€„π’‚–π’†·π’Ž
    world: π’Ÿπ’Žπ’‰Ώπ’†·π’€³
    def: π’΄π’ˆ«
"""

# uncomment to show parse-time debugging
# pp.enable_diag(pp.Diagnostics.enable_debug_on_named_expressions)

# define a MINIMAL Python parser
LPAR, RPAR, COLON, EQ = map(pp.Suppress, "():=")
def_ = pp.Keyword("π’΄π’ˆ«", ident_chars=Cuneiform.identbodychars).set_name("def")
any_keyword = def_
ident = (~any_keyword) + pp.Word(
    Cuneiform.identchars, Cuneiform.identbodychars, asKeyword=True
)
str_expr = pp.infix_notation(
    pp.QuotedString('"') | pp.common.integer,
    [
        ("*", 2, pp.OpAssoc.LEFT),
        ("+", 2, pp.OpAssoc.LEFT),
    ],
)

rvalue = pp.Forward()
fn_call = (ident + pp.Group(LPAR + pp.Optional(rvalue) + RPAR)).set_name("fn_call")

rvalue <<= fn_call | ident | str_expr | pp.common.number
assignment_stmt = ident + EQ + rvalue

stmt = pp.Group(fn_call | assignment_stmt).set_name("stmt")

fn_def = pp.Group(
    def_ + ident + pp.Group(LPAR + pp.Optional(rvalue) + RPAR) + COLON
).set_name("fn_def")
fn_body = pp.IndentedBlock(stmt).set_name("fn_body")
fn_expr = pp.Group(fn_def + pp.Group(fn_body))

script = fn_expr[...] + stmt[...]


# parse some Python written in Cuneiform
cuneiform_hello_world = r"""
π’΄π’ˆ« π’€„π’‚–π’†·π’Ž():
    𒀁 = "π’€„π’‚–π’†·π’Ž, π’Ÿπ’Žπ’‰Ώπ’†·π’€³!\n" * 3
    π’„‘π’‰Ώπ’…”π’‹«(𒀁)

π’€„π’‚–π’†·π’Ž()"""
script.parseString(cuneiform_hello_world).pprint(width=40)


# use transform_string to convert keywords and builtins to runnable Python
names_map = {
    "π’„‘π’‰Ώπ’…”π’‹«": "print",
}
ident.add_parse_action(lambda t: names_map.get(t[0], t[0]))
def_.add_parse_action(lambda: "def")

print("\nconvert Cuneiform Python to executable Python")
transformed = (
    # always put ident last
    (def_ | ident)
    .ignore(pp.quoted_string)
    .transform_string(cuneiform_hello_world)
    .strip()
)
print(
    "=================\n"
    + cuneiform_hello_world.strip()
    + "\n=================\n"
    + transformed
    + "\n=================\n"
)
print("# run transformed Python")
exec(transformed)