Skip to content

Commit a21db55

Browse files
committed
Initial commit
0 parents  commit a21db55

File tree

5 files changed

+447
-0
lines changed

5 files changed

+447
-0
lines changed

goboscript_gdsl/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .parse import GDSLDecoder as Decoder

goboscript_gdsl/cls.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from dataclasses import dataclass, field
2+
3+
4+
@dataclass
5+
class UnOp:
6+
opcode: str
7+
input: str
8+
fields: dict[str, str]
9+
10+
11+
@dataclass
12+
class BinOp:
13+
opcode: str
14+
lhs: str
15+
rhs: str
16+
17+
18+
@dataclass
19+
class Menu:
20+
input: str
21+
field: str
22+
opcode: str
23+
default: str
24+
25+
26+
@dataclass
27+
class Block:
28+
name: str
29+
opcode: str
30+
args: list[str]
31+
fields: dict[str, str]
32+
menu: Menu | None
33+
34+
35+
@dataclass
36+
class GDSLData:
37+
un_ops: dict[str, UnOp | None] = field(default_factory=lambda: {})
38+
bin_ops: dict[str, BinOp | None] = field(default_factory=lambda: {})
39+
blocks: dict[str, Block | list[Block]] = field(default_factory=lambda: {})
40+
reporters: dict[str, Block | list[Block]] = field(default_factory=lambda: {})

goboscript_gdsl/parse.py

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
from typing import Literal
2+
3+
from .cls import *
4+
5+
6+
def snake_to_pascal(s: str):
7+
return "".join(x.title() for x in s.split("_"))
8+
9+
10+
def table_split(table: str, n: int):
11+
xs = table.split()
12+
xs.extend([""] * (n - len(xs)))
13+
return xs
14+
15+
16+
class GDSLDecoder:
17+
def __init__(self, debug: bool = True):
18+
self.data = None
19+
self.debug = debug
20+
21+
def load(self, data: str):
22+
self.data = data
23+
24+
def parse(self) -> GDSLData:
25+
# Maybe split this into a bunch of methods to make it less messy/long
26+
ret = GDSLData()
27+
28+
old_opcode = ""
29+
old_input = ""
30+
31+
old_lhs = ""
32+
old_rhs = ""
33+
34+
old_fields: list[str] = []
35+
old_menu = ""
36+
opcode_prefix = ""
37+
old_args = ""
38+
39+
section: Literal["UNARY", "BINARY", "BLOCKS", "REPORTERS"] | None = None
40+
lines = iter(open("gdsl.txt"))
41+
42+
for line in lines:
43+
line = line[:-1].strip()
44+
45+
if line.startswith("#") or not line:
46+
continue
47+
48+
if self.debug:
49+
print(line)
50+
51+
if line in ["UNARY OPERATORS", "BINARY OPERATORS", "BLOCKS", "REPORTERS"]:
52+
section = line.split()[0] # type: ignore
53+
next(lines, None)
54+
next(lines, None)
55+
next(lines, None)
56+
continue
57+
58+
if section == "UNARY":
59+
if line.endswith("~"):
60+
ret.un_ops[line.removesuffix("~")] = None
61+
continue
62+
63+
table, fields = line.split("|")
64+
fields = fields.strip()
65+
66+
fields = (
67+
dict(
68+
(old_fields[i] if key == "..." else key, value)
69+
for i, (key, value) in enumerate(
70+
x.split("=") for x in fields.strip().split(",")
71+
)
72+
)
73+
if fields
74+
else {}
75+
)
76+
77+
old_fields = list(fields.keys())
78+
variant, opcode, inp = table.split()
79+
80+
if opcode == "...":
81+
opcode = old_opcode
82+
else:
83+
opcode = "operator_" + opcode
84+
old_opcode = opcode
85+
86+
if inp == "...":
87+
inp = old_input
88+
else:
89+
old_input = inp
90+
91+
ret.un_ops[variant] = UnOp(opcode, inp, fields)
92+
93+
elif section == "BINARY":
94+
if line.endswith("~"):
95+
ret.bin_ops[line.removesuffix("~")] = None
96+
continue
97+
98+
variant, opcode, lhs, rhs = line.split()
99+
100+
if opcode == "...":
101+
opcode = old_opcode
102+
else:
103+
opcode = "operator_" + opcode
104+
old_opcode = opcode
105+
106+
if lhs == "...":
107+
lhs = old_lhs
108+
else:
109+
old_lhs = lhs
110+
111+
if rhs == "...":
112+
rhs = old_rhs
113+
else:
114+
old_rhs = rhs
115+
116+
ret.bin_ops[variant] = BinOp(opcode, lhs, rhs)
117+
118+
else:
119+
if line.startswith("["):
120+
opcode_prefix = line.split("]")[0].removeprefix("[")
121+
continue
122+
123+
table, fields, menu = line.split("|")
124+
menu = menu.strip()
125+
126+
if menu:
127+
input_opcode, default = menu.split("=")
128+
if input_opcode == "...":
129+
input_opcode = old_menu
130+
131+
old_menu = input_opcode
132+
inp, opcode = input_opcode.split(":")
133+
134+
if "@" in inp:
135+
inp, fld = inp.split("@")
136+
else:
137+
fld = inp
138+
139+
menu = Menu(
140+
input=inp,
141+
field=fld,
142+
opcode=opcode,
143+
default=default,
144+
)
145+
146+
else:
147+
menu = None
148+
149+
_fields = fields.strip()
150+
151+
if _fields:
152+
fields = {}
153+
for i, (key, value) in enumerate(
154+
x.split("=") for x in _fields.strip().split(",")
155+
):
156+
if key == "...":
157+
key = old_fields[i]
158+
fields[key] = value
159+
160+
else:
161+
fields = {}
162+
163+
old_fields = list(fields.keys())
164+
name, opcode, args = table_split(table, 3)
165+
variant = snake_to_pascal(name)
166+
167+
if opcode == "...":
168+
opcode = old_opcode
169+
else:
170+
old_opcode = opcode
171+
172+
opcode = f"{opcode_prefix}_{opcode}"
173+
if args == "...":
174+
args = old_args
175+
else:
176+
old_args = args
177+
178+
args = args.split(",") if args else []
179+
if section == "BLOCKS":
180+
container = ret.blocks
181+
else:
182+
container = ret.reporters
183+
184+
if variant in container:
185+
block = container[variant]
186+
187+
if not isinstance(block, list):
188+
block = [block]
189+
190+
block.append(Block(name, opcode, args, fields, menu))
191+
container[variant] = block
192+
193+
else:
194+
container[variant] = Block(name, opcode, args, fields, menu)
195+
196+
return ret

0 commit comments

Comments
 (0)