novaweb-compiler.nv
· 3.3 KiB · Text
Неформатований
Playground
|| :: load entry point into source
:: process novaweb
|:: load entry point into source? :entry point: $char|
:processed: $char
|:: load entry point into source? :processed: $char|
:source: $char
|:: load entry point into source|
|:: process novaweb? :second pass needed:? :processed: $char|
:source: $char
|:: process novaweb? :second pass needed:|
|:: process novaweb? :source: "@@@"|
:: signal a second pass is needed
:: load link :strip left: :strip right:
:: search dictionary
|:: process novaweb? :source: $char|
:processed: $char
|::process novaweb|
:: finished compiling
|:: signal a second pass is needed :second pass needed:?|
|:: signal a second pass is needed|
:second pass needed:
|:: load link? :source: "@@@"? :incoming link: 9 :strip right:?|
|:: load link? :source: "@@@"? :incoming link: 10 :strip right:?|
|:: load link? :source: "@@@"? :incoming link: 13 :strip right:?|
|:: load link? :source: "@@@"? :incoming link: 32 :strip right:?|
|:: load link? :source: "@@@"? :strip right:|
|:: load link? :source: "@@@"? :incoming link: $char|
:link: $char
|:: load link :source: "@@@"|
|:: load link? :source: 9 :strip left:?|
|:: load link? :source: 10 :strip left:?|
|:: load link? :source: 13 :strip left:?|
|:: load link? :source: 32 :strip left:?|
|:: load link? :strip left:|
|:: load link? :source: $char|
:incoming link: $char
|:: search dictionary? :link: $char? :name: $char?|
:: match made
|:: search dictionary? :link: $char? :name:?|
:: search failed
|:: search dictionary? :link: $char? :name: $other-char?|
:: search failed
|:: search dictionary :name:?|
:: search succeeded
|:: search dictionary? :link: $char?|
:: search exhausted
|:: search succeeded|
:: restore name dictionary
:: flush link
:: push code to processed
:: restore code
|:: restore name dictionary? :consumed name:|
:name:
|:: restore name dictionary? :consumed name: $char|
:name: $char
|:: restore name dictionary|
|:: restore code? :consumed code:|
:code:
|:: restore code? :consumed code: $char|
:code: $char
|:: restore code|
|:: flush link? :consumed link: $char|
|:: flush link|
|:: push code to processed? :code: $char|
:processed: $char :consumed code: $char
|:: push code to processed :code:|
:consumed code:
|:: search failed|
:: next name in dictionary
:: next page of code
:: reset link
|:: next name in dictionary :name:|
:consumed name:
|:: next name in dictionary? :name: $char|
:consumed name: $char
|:: next page of code :code:|
:consumed code:
|:: next page of code? :code: $char|
:consumed code: $char
|:: reset link? :consumed link: $char|
:link: $char
|:: reset link|
|:: search exhausted|
:: show error :error message: "No reference found for"
:error message: (58 32)
:: exit
|:: show error? :error message: $char|
:@stdio: write $char
|:: show error? :link: $char|
:@stdio: write $char
|:: show error|
:@stdio: write 10
|:: match made|
:: advance link
:: advance name dictionary
|:: advance link :link: $char|
:consumed link: $char
|:: advance name dictionary :name: $char|
:consumed name: $char
|:: finished compiling? :processed: $char|
:source: $char
|:: finished compiling|
:: print source
|:: print source? :source: $char|
:@stdio: write $char
|:: print source|
1 | || :: load entry point into source |
2 | :: process novaweb |
3 | |
4 | |:: load entry point into source? :entry point: $char| |
5 | :processed: $char |
6 | |:: load entry point into source? :processed: $char| |
7 | :source: $char |
8 | |:: load entry point into source| |
9 | |
10 | |:: process novaweb? :second pass needed:? :processed: $char| |
11 | :source: $char |
12 | |:: process novaweb? :second pass needed:| |
13 | |:: process novaweb? :source: "@@@"| |
14 | :: signal a second pass is needed |
15 | :: load link :strip left: :strip right: |
16 | :: search dictionary |
17 | |:: process novaweb? :source: $char| |
18 | :processed: $char |
19 | |::process novaweb| |
20 | :: finished compiling |
21 | |
22 | |
23 | |:: signal a second pass is needed :second pass needed:?| |
24 | |:: signal a second pass is needed| |
25 | :second pass needed: |
26 | |
27 | |
28 | |:: load link? :source: "@@@"? :incoming link: 9 :strip right:?| |
29 | |:: load link? :source: "@@@"? :incoming link: 10 :strip right:?| |
30 | |:: load link? :source: "@@@"? :incoming link: 13 :strip right:?| |
31 | |:: load link? :source: "@@@"? :incoming link: 32 :strip right:?| |
32 | |:: load link? :source: "@@@"? :strip right:| |
33 | |:: load link? :source: "@@@"? :incoming link: $char| |
34 | :link: $char |
35 | |:: load link :source: "@@@"| |
36 | |
37 | |:: load link? :source: 9 :strip left:?| |
38 | |:: load link? :source: 10 :strip left:?| |
39 | |:: load link? :source: 13 :strip left:?| |
40 | |:: load link? :source: 32 :strip left:?| |
41 | |:: load link? :strip left:| |
42 | |:: load link? :source: $char| |
43 | :incoming link: $char |
44 | |
45 | |
46 | |:: search dictionary? :link: $char? :name: $char?| |
47 | :: match made |
48 | |:: search dictionary? :link: $char? :name:?| |
49 | :: search failed |
50 | |:: search dictionary? :link: $char? :name: $other-char?| |
51 | :: search failed |
52 | |:: search dictionary :name:?| |
53 | :: search succeeded |
54 | |:: search dictionary? :link: $char?| |
55 | :: search exhausted |
56 | |
57 | |:: search succeeded| |
58 | :: restore name dictionary |
59 | :: flush link |
60 | :: push code to processed |
61 | :: restore code |
62 | |
63 | |:: restore name dictionary? :consumed name:| |
64 | :name: |
65 | |:: restore name dictionary? :consumed name: $char| |
66 | :name: $char |
67 | |:: restore name dictionary| |
68 | |
69 | |
70 | |:: restore code? :consumed code:| |
71 | :code: |
72 | |:: restore code? :consumed code: $char| |
73 | :code: $char |
74 | |:: restore code| |
75 | |
76 | |
77 | |:: flush link? :consumed link: $char| |
78 | |:: flush link| |
79 | |
80 | |
81 | |:: push code to processed? :code: $char| |
82 | :processed: $char :consumed code: $char |
83 | |:: push code to processed :code:| |
84 | :consumed code: |
85 | |
86 | |
87 | |:: search failed| |
88 | :: next name in dictionary |
89 | :: next page of code |
90 | :: reset link |
91 | |
92 | |:: next name in dictionary :name:| |
93 | :consumed name: |
94 | |:: next name in dictionary? :name: $char| |
95 | :consumed name: $char |
96 | |
97 | |:: next page of code :code:| |
98 | :consumed code: |
99 | |:: next page of code? :code: $char| |
100 | :consumed code: $char |
101 | |
102 | |:: reset link? :consumed link: $char| |
103 | :link: $char |
104 | |:: reset link| |
105 | |
106 | |:: search exhausted| |
107 | :: show error :error message: "No reference found for" |
108 | :error message: (58 32) |
109 | :: exit |
110 | |
111 | |:: show error? :error message: $char| |
112 | :@stdio: write $char |
113 | |:: show error? :link: $char| |
114 | :@stdio: write $char |
115 | |:: show error| |
116 | :@stdio: write 10 |
117 | |
118 | |:: match made| |
119 | :: advance link |
120 | :: advance name dictionary |
121 | |
122 | |:: advance link :link: $char| |
123 | :consumed link: $char |
124 | |:: advance name dictionary :name: $char| |
125 | :consumed name: $char |
126 | |
127 | |:: finished compiling? :processed: $char| |
128 | :source: $char |
129 | |:: finished compiling| |
130 | :: print source |
131 | |
132 | |:: print source? :source: $char| |
133 | :@stdio: write $char |
134 | |:: print source| |
novaweb-nova.nv
· 36 KiB · Text
Неформатований
Playground
^^
~entry point~ "
@@@ Import Python core libraries @@@
@@@ Set up PyGame @@@
@@@ Set up NFC @@@
@@@ The implementation of a Nova parser @@@
@@@ The implementation of a Nova interpreter @@@
@@@ The built-in modules @@@
@@@ Let's include a REPL @@@
@@@ Finally, the main program @@@
"
~name~
"The implementation of a Nova parser"
~name~
~code~
"
@@@ Parse the container format @@@
@@@ Parse the label format @@@
@@@ Parse the pattern format @@@
@@@ Chain these steps together for the parser @@@
"
~code~
~name~
"The built-in modules"
~name~
~code~
"
@@@ The Math Module @@@
@@@ The Arrays Module @@@
@@@ The Graphics Module @@@
@@@ The Midi Module @@@
@@@ The Time Module @@@
@@@ The Stdio Module @@@
@@@ The NFC Module @@@
"
~code~
~name~
"Parse the container format"
~name~
~code~
"
def parse_container(string):
left = []
right = []
target = left
characters = (character for character in list(string) + [None])
delimiter = None
for character in characters:
if character in {' ', '\n', '\r', '\t'}:
continue
delimiter = character
break
if delimiter is None:
return None
for character in characters:
if character in {delimiter, None}:
if target is left:
target = right
else:
target = left
yield (left.copy(), right.copy())
left.clear()
right.clear()
else:
target.append(character)
"
~code~
~name~
"Parse the label format"
~name~
~code~
"
def parse_labels(rules):
left = []
right = []
pair = ([], [])
target = left
delimiter = None
for condition, result in rules:
for side in (condition, result):
characters = (character for character in side + [None])
state = "find delimiter"
for character in characters:
if character == None:
label = "".join(pair[0]).strip()
pattern = "".join(pair[1]).strip()
if not(len(label) == 0 and len(pattern) == 0):
target.append((label, pattern))
pair[0].clear()
pair[1].clear()
break
elif state == "find delimiter":
if character in {' ', '\n', '\r', '\t'}:
continue
delimiter = character
state = "parse label"
elif state == "parse label":
if character == delimiter:
state = "parse pattern"
continue
pair[0].append(character)
elif state == "parse pattern":
if character in {delimiter, None}:
state = "parse label"
label = "".join(pair[0]).strip()
pattern = "".join(pair[1]).strip()
if not(len(label) == 0 and len(pattern) == 0):
target.append((label, pattern))
pair[0].clear()
pair[1].clear()
continue
pair[1].append(character)
if target is left:
target = right
else:
target = left
yield (left.copy(), right.copy())
left.clear()
right.clear()
"
~code~
~name~
"Parse the pattern format"
~name~
~code~
"
def parse_patterns(rules):
for condition, result in rules:
left = []
right = []
preserves = []
for label, pattern in condition:
preserve = False
if pattern.endswith('?'):
pattern = pattern[:-1]
preserve = True
if (pattern.startswith('"') or pattern.startswith("'")) and (pattern.endswith('"') or pattern.endswith("'")):
for character in pattern[1:-1]:
left.append((label, [str(ord(character))]))
if preserve:
right.append((label, [str(ord(character))]))
elif pattern.startswith('(') and pattern.endswith(')'):
for token in pattern[1:-1].split():
left.append((label, [token]))
if preserve:
right.append((label, [token]))
elif pattern.startswith('.'):
for tuple in pattern.split('.'):
tuple = tuple.strip()
if len(tuple) != 0:
left.append((label, tuple.split()))
if preserve:
right.append((label, tuple.split()))
else:
left.append((label, pattern.split()))
if preserve:
preserves.append((label, pattern.split()))
for label, pattern in result:
if (pattern.startswith('"') or pattern.startswith("'")) and (pattern.endswith('"') or pattern.endswith("'")):
for character in pattern[1:-1]:
right.append((label, [str(ord(character))]))
elif pattern.startswith('(') and pattern.endswith(')'):
for token in pattern[1:-1].split():
right.append((label, [token]))
elif pattern.startswith('.'):
for tuple in pattern.split('.'):
tuple = tuple.strip()
if len(tuple) != 0:
right.append((label, tuple.split()))
else:
right.append((label, pattern.split()))
yield (left.copy(), right.copy() + preserves.copy())
left.clear()
right.clear()
preserves.clear()
"
~code~
~name~
"Chain these steps together for the parser"
~name~
~code~
"
def parse(string):
return parse_patterns(parse_labels(parse_container(string)))
"
~code~
~name~
"The implementation of a Nova interpreter"
~name~
~code~
"
def facts(ruleset):
for left, right in ruleset:
if not left:
for element in right:
yield element
def rules(ruleset):
for left, right in ruleset:
if left:
yield (left, right)
def match(pattern, knowledge, variables=None):
if variables is None:
variables = {}
label, elements = pattern
if label not in knowledge or not knowledge[label][0]:
return None
if knowledge[label][1] >= len(knowledge[label][0]):
knowledge[label][1] = 0
return None
top = knowledge[label][0][(-1 - knowledge[label][1])]
len_top = len(top)
if len_top != len(elements):
knowledge[label][1] = 0
return None
for index in range(len_top):
left = elements[index]
right = top[index]
if left[0] == '$':
variable = left[1:]
if variable not in variables:
variables[variable] = right
elif variables[variable] != right:
knowledge[label][1] = 0
return None
elif left != right:
knowledge[label][1] = 0
return None
knowledge[label][1] += 1
return variables
def consume(pattern, knowledge):
if type(pattern) == str:
knowledge[pattern][0].pop()
knowledge[pattern][1] = 0
else:
label, _ = pattern
knowledge[label][0].pop()
knowledge[label][1] = 0
def produce(pattern, knowledge, variables=None):
if variables is None:
variables = {}
label, elements = pattern
len_elements = len(elements)
fact = [None] * len_elements
for index in range(len_elements):
element = elements[index]
if element[0] == '$':
variable = element[1:]
if variable in variables:
element = variables[variable]
fact[index] = element
if label not in knowledge:
knowledge[label] = [[], 0]
knowledge[label][0].append(fact)
knowledge[label][1] = 0
def reset(pattern, knowledge):
label, _ = pattern
if label in knowledge:
knowledge[label][1] = 0
def prepare(ruleset, knowledge=None):
if knowledge == None:
knowledge = {}
for fact in reversed(list(facts(ruleset))):
label, elements = fact
if label not in knowledge:
knowledge[label] = [[], 0]
knowledge[label][0].append(elements)
return knowledge
def print_knowledge(knowledge, debug=0, maximum=10):
for label, facts in knowledge.items():
facts = facts[0]
if not facts:
continue
if not label:
label = ":"
print(label + ': ', end="")
if len(facts) >= maximum and debug % 2:
print("...", len(facts), "...")
continue
if facts:
if len(facts[-1]) == 0:
print("<blank>")
else:
print(" ".join(facts[-1]))
for fact in reversed(facts[:-1]):
if len(fact) == 0:
fact = ["<blank>"]
print(' ' * (len(label) + 1), " ".join(fact))
def step(ruleset, primitives=[], knowledge=None, snapshots=[], debug=0, trace=False):
variables = {}
steps = 0
if debug >= 3:
print_knowledge(knowledge, debug)
print("")
matched = False
for primitive in primitives:
steps += 1
if primitive(ruleset, knowledge, snapshots):
matched = True
break
if matched:
return (True, steps, knowledge)
for left, right in ruleset:
steps += 1
matched = True
variables.clear()
for pattern in left:
if match(pattern, knowledge, variables) is None:
matched = False
for pattern in left:
reset(pattern, knowledge)
break
if matched:
for pattern in left:
consume(pattern, knowledge)
for pattern in reversed(right):
produce(pattern, knowledge, variables)
break
return (matched, steps, knowledge)
def run(ruleset, primitives=[], knowledge=None, debug=0, trace=False):
knowledge = prepare(ruleset, knowledge)
ruleset = list(rules(ruleset))
matched = True
steps = 0
snapshots = []
while matched:
matched, completed, knowledge = step(ruleset, primitives, knowledge, snapshots, debug, trace)
steps += completed
if trace:
snapshots.append(copy.deepcopy(knowledge))
if debug:
print("Took", steps, "steps.")
return (knowledge, snapshots)
def usage(name):
print(name, ":: a nova interpreter")
print("usage:")
print('\t' + name, "<file> [-d] [-r]")
"
~code~
~name~
"The Math Module"
~name~
~code~
"
def math(ruleset, knowledge, snapshots=[], trace=False):
if (variables := match(("@math", ["$operation", "$x", "$y"]), knowledge)) is not None:
operation = variables["operation"]
x = int(variables["x"])
y = int(variables["y"])
if operation == "add":
variables["z"] = str(x + y)
elif operation == "subtract":
variables["z"] = str(x - y)
elif operation == "multiply":
variables["z"] = str(x * y)
elif operation == "divide":
variables["z"] = str(x // y)
elif operation == "modulo":
variables["z"] = str(x % y)
elif operation == "compare":
if x < y:
variables["z"] = "less"
elif x == y:
variables["z"] = "equal"
else:
variables["z"] = "greater"
elif operation == "random":
if y < x:
x, y = y, x
variables["z"] = str(random.randrange(x, y))
consume("@math", knowledge)
produce(("@math", ["$z"]), knowledge, variables)
return True
return False
"
~code~
~name~
"The Arrays Module"
~name~
~code~
"
def arrays():
arrays = {}
def create(ruleset, knowledge, snapshots=[], trace=False):
nonlocal arrays
if (variables := match(("@array", ["create", "$array", "$size"]), knowledge)) is not None:
array = variables["array"]
size = int(variables["size"])
consume("@array", knowledge)
if array in arrays:
produce(("@array", ["$array", "exists"]), knowledge, variables)
else:
arrays[array] = [None] * size
return True
return False
def set(ruleset, knowledge, snapshots=[], trace=False):
nonlocal arrays
if (variables := match(("@array", ["set", "$array", "$index", "$value"]), knowledge)) is not None:
array = variables["array"]
index = int(variables["index"])
value = variables["value"]
consume("@array", knowledge)
if array not in arrays:
produce(("@array", ["$array", "not", "found"]), knowledge, variables)
elif index >= len(arrays[array]):
produce(("@array", ["$index", "out", "of", "bounds", "for", "$array"]), knowledge, variables)
else:
arrays[array][index] = value
return True
return False
def get(ruleset, knowledge, snapshots=[], trace=False):
nonlocal arrays
if (variables := match(("@array", ["get", "$array", "$index"]), knowledge)) is not None:
array = variables["array"]
index = int(variables["index"])
consume("@array", knowledge)
if array not in arrays:
produce(("@array", ["$array", "not", "found"]), knowledge, variables)
elif index >= len(arrays[array]):
produce(("@array", ["$index", "out", "of", "bounds", "for", "$array"]), knowledge, variables)
else:
if arrays[array][index] == None:
produce(("@array", ["$array", "$index"]), knowledge, variables)
else:
variables["value"] = str(arrays[array][index])
produce(("@array", ["$array", "$index", "$value"]), knowledge, variables)
return True
return False
return [
create,
set,
get
]
"
~code~
~name~
"The Graphics Module"
~name~
~code~
"
def graphics():
keys = {}
mouse_buttons = {}
screen = None
pixels = None
clock = None
font = None
resolution = (0, 0)
def poll_input(ruleset, knowledge, snapshots=[]):
nonlocal keys
nonlocal mouse_buttons
if match(("@input", ["poll-input"]), knowledge) is not None:
consume("@input", knowledge)
for event in pygame.event.get():
if event.type == pygame.QUIT:
produce(("@signal", ["quit"]), knowledge)
keys = pygame.key.get_pressed()
mouse_buttons = pygame.mouse.get_pressed()
return True
return False
def check_key(ruleset, knowledge, snapshots=[]):
nonlocal keys
if (variables := match(("@input", ["check-key", "$key"]), knowledge)) is not None:
consume("@input", knowledge)
keycode = pygame.key.key_code(variables["key"])
if keys[keycode]:
produce(("@input", ["key-pressed", "$key"]), knowledge, variables)
else:
produce(("@input", ["key-released", "$key"]), knowledge, variables)
return True
return False
def check_mouse_button(ruleset, knowledge, snapshots=[]):
nonlocal mouse_buttons
if (variables := match(("@input", ["check-mouse-button", "$button"]), knowledge)) is not None:
consume("@input", knowledge)
button = int(variables["button"])
if mouse_buttons[button]:
produce(("@input", ["mouse-button-pressed", "$button"]), knowledge, variables)
else:
produce(("@input", ["mouse-button-released", "$button"]), knowledge, variables)
return True
return False
def get_mouse_position(ruleset, knowledge, snapshots=[]):
if (variables := match(("@input", ["get-mouse-position"]), knowledge)) is not None:
consume("@input", knowledge)
position = pygame.mouse.get_pos()
variables["x"], variables["y"] = (str(position[0]), str(position[1]))
produce(("@input", ["mouse-position", "$x", "$y"]), knowledge, variables)
return True
return False
def set_pixel(ruleset, knowledge, snapshots=[]):
nonlocal pixels
if (variables := match(("@graphics", ["set-pixel", "$x", "$y", "$r", "$g", "$b"]), knowledge)) is not None:
x = int(variables["x"])
y = int(variables["y"])
r = int(variables["r"])
g = int(variables["g"])
b = int(variables["b"])
pixels[x % resolution[0], y % resolution[1]] = (r % 256, g % 256, b % 256)
consume("@graphics", knowledge)
return True
return False
def draw_line(ruleset, knowledge, snapshots=[]):
nonlocal screen
if (variables := match(("@graphics", ["draw-line", "$x1", "$y1", "$x2", "$y2", "$r", "$g", "$b", "$w"]), knowledge)) is not None:
x1 = int(variables["x1"])
y1 = int(variables["y1"])
x2 = int(variables["x2"])
y2 = int(variables["y2"])
r = int(variables["r"])
g = int(variables["g"])
b = int(variables["b"])
w = int(variables["w"])
pygame.draw.line(screen, (r % 256, g % 256, b % 256), (x1, y1), (x2, y2), w)
consume("@graphics", knowledge)
return True
return False
def draw_rect(ruleset, knowledge, snapshots=[]):
nonlocal screen
if (variables := match(("@graphics", ["draw-rect", "$x1", "$y1", "$x2", "$y2", "$r", "$g", "$b", "$w"]), knowledge)) is not None:
x1 = int(variables["x1"])
y1 = int(variables["y1"])
x2 = int(variables["x2"])
y2 = int(variables["y2"])
r = int(variables["r"])
g = int(variables["g"])
b = int(variables["b"])
w = int(variables["w"])
pygame.draw.rect(screen, (r % 256, g % 256, b % 256), (x1, y1, x2, y2), w)
consume("@graphics", knowledge)
return True
return False
def draw_circle(ruleset, knowledge, snapshots=[]):
nonlocal screen
if (variables := match(("@graphics", ["draw-circle", "$x", "$y", "$d", "$r", "$g", "$b", "$w"]), knowledge)) is not None:
x = int(variables["x"])
y = int(variables["y"])
d = int(variables["d"])
r = int(variables["r"])
g = int(variables["g"])
b = int(variables["b"])
w = int(variables["w"])
pygame.draw.circle(screen, (r % 256, g % 256, b % 256), (x, y), d, w)
consume("@graphics", knowledge)
return True
return False
def clear_screen(ruleset, knowledge, snapshots=[]):
nonlocal screen
if (variables := match(("@graphics", ["clear-screen", "$r", "$g", "$b"]), knowledge)) is not None:
r = int(variables["r"])
g = int(variables["g"])
b = int(variables["b"])
screen.fill((r % 256, g % 256, b % 256))
consume("@graphics", knowledge)
return True
return False
def draw_fps(ruleset, knowledge, snapshots=[]):
nonlocal pixels
nonlocal screen
nonlocal clock
nonlocal font
if match(("@graphics", ["draw-fps"]), knowledge) is not None:
if clock is None:
clock = pygame.time.Clock()
clock.tick()
if font is None:
font = pygame.font.SysFont("m3x6", 16)
framerate = clock.get_fps()
if framerate <= 1_000_000:
fps = font.render(str(int(clock.get_fps())) , 1, pygame.Color("RED"))
pixels.close()
screen.blit(fps, (0, 0))
pixels = pygame.PixelArray(screen)
consume("@graphics", knowledge)
return True
return False
def display(ruleset, knowledge, snapshots=[]):
nonlocal pixels
if match(("@graphics", ["display"]), knowledge) is not None:
pixels.close()
pygame.display.flip()
pixels = pygame.PixelArray(screen)
consume("@graphics", knowledge)
return True
return False
def draw_text(ruleset, knowledge, snapshots=[]):
nonlocal font
nonlocal screen
nonlocal pixels
if (variables := match(("@graphics", ["draw-text", "$x", "$y"]), knowledge)) is not None:
x = int(variables["x"])
y = int(variables["y"])
if font is None:
font = pygame.font.SysFont("m3x6", 16)
text = ''.join(chr(int(n[0])) for n in reversed(knowledge["@text to draw"][0]))
knowledge["@text to draw"] = [[], 0]
drawn_text = font.render(text, False, pygame.Color("WHITE"))
pixels.close()
pygame.draw.rect(screen, (0, 0, 0), (0, 48, 64, 64))
screen.blit(drawn_text, (x, y))
pixels = pygame.PixelArray(screen)
consume("@graphics", knowledge)
return True
return False
def set_resolution(ruleset, knowledge, snapshots=[]):
nonlocal screen
nonlocal pixels
nonlocal resolution
if (variables := match(("@graphics", ["set-resolution", "$x", "$y"]), knowledge)) is not None:
if screen is None:
pygame.init()
x = int(variables["x"])
y = int(variables["y"])
screen = pygame.display.set_mode((x, y))
pygame.display.set_caption("Nova")
icon = pygame.Surface((32, 32))
icon.fill("black")
icon.set_colorkey((0, 0, 0))
pygame.display.set_icon(icon)
pixels = pygame.PixelArray(screen)
resolution = (x, y)
consume("@graphics", knowledge)
return True
elif (variables := match(("@graphics", ["set-resolution", "fullscreen", "$x", "$y"]), knowledge)) is not None:
if screen is None:
pygame.init()
x = int(variables["x"])
y = int(variables["y"])
screen = pygame.display.set_mode((x, y), flags=pygame.FULLSCREEN | pygame.SCALED)
pygame.display.set_caption("Nova")
icon = pygame.Surface((32, 32))
icon.fill("black")
icon.set_colorkey((0, 0, 0))
pygame.display.set_icon(icon)
pixels = pygame.PixelArray(screen)
resolution = (x, y)
consume("@graphics", knowledge)
return True
elif (variables := match(("@graphics", ["set-resolution", "fullscreen"]), knowledge)) is not None:
if screen is None:
pygame.init()
screen = pygame.display.set_mode((0, 0), flags=pygame.FULLSCREEN)
pygame.display.set_caption("Nova")
icon = pygame.Surface((32, 32))
icon.fill("black")
icon.set_colorkey((0, 0, 0))
pygame.display.set_icon(icon)
pixels = pygame.PixelArray(screen)
resolution = pygame.display.get_surface().get_size()
consume("@graphics", knowledge)
return True
return False
return [
set_pixel,
draw_line,
draw_rect,
draw_circle,
draw_text,
display,
poll_input,
draw_fps,
clear_screen,
check_key,
check_mouse_button,
get_mouse_position,
set_resolution
]
"
~code~
~name~
"The Midi Module"
~name~
~code~
"
def midi():
output = None
def set_output(ruleset, knowledge, snapshots=[]):
nonlocal output
if (variables := match(("@midi", ["set-output", "$x"]), knowledge)) is not None:
if output is None:
pygame.midi.init()
else:
del output
output = pygame.midi.Output(int(variables["x"]))
consume("@midi", knowledge)
return True
return False
def abort(ruleset, knowledge, snapshots=[]):
nonlocal output
if (variables := match(("@midi", ["abort"]), knowledge)) is not None:
output.abort()
consume("@midi", knowledge)
return True
return False
def set_instrument(ruleset, knowledge, snapshots=[]):
nonlocal output
if (variables := match(("@midi", ["set-instrument", "$x"]), knowledge)) is not None:
output.set_instrument(int(variables["x"]))
consume("@midi", knowledge)
return True
return False
def note(ruleset, knowledge, snapshots=[]):
nonlocal output
if (variables := match(("@midi", ["note", "$x", "$y", "$z", "$w"]), knowledge)) is not None:
state = variables["x"]
note = int(variables["y"])
velocity = int(variables["z"])
channel = int(variables["w"])
if state == "on":
output.note_on(note, velocity, channel)
elif state == "off":
output.note_off(note, velocity, channel)
else:
return False
consume("@midi", knowledge)
return True
elif (variables := match(("@midi", ["note", "$x", "$y", "$z"]), knowledge)) is not None:
state = variables["x"]
note = int(variables["y"])
velocity = int(variables["z"])
if state == "on":
output.note_on(note, velocity)
elif state == "off":
output.note_off(note, velocity)
else:
return False
consume("@midi", knowledge)
return True
return False
return [
set_output,
abort,
set_instrument,
note
]
"
~code~
~name~
"The Time Module"
~name~
~code~
"
def _time():
def sleep(ruleset, knowledge, snapshots=[]):
if (variables := match(("@time", ["sleep", "$x", "$y"]), knowledge)) is not None:
duration = int(variables["x"])
unit = variables["y"]
if unit == "seconds":
time.sleep(duration)
elif unit == "milliseconds":
time.sleep(0.001 * duration)
else:
return False
consume("@time", knowledge)
return True
return False
return [
sleep
]
"
~code~
~name~
"The Stdio Module"
~name~
~code~
"
def stdio():
def read(ruleset, knowledge, snapshots=[]):
if (variables := match(("@stdio", ["read", "$x"]), knowledge)) is not None:
try:
amount = int(variables["x"])
bytes = sys.stdin.read(amount)
consume("@stdio", knowledge)
for byte in reversed(bytes.encode()):
variables["x"] = str(byte)
produce(("@stdio", ["$x"]), knowledge, variables)
return True
except KeyboardInterrupt:
consume("@stdio", knowledge)
return True
except:
pass
elif (variables := match(("@stdio", ["read"]), knowledge)) is not None:
try:
variables["x"] = str(ord(sys.stdin.read(1)))
consume("@stdio", knowledge)
produce(("@stdio", ["$x"]), knowledge, variables)
return True
except KeyboardInterrupt:
consume("@stdio", knowledge)
return True
except:
pass
return False
def write(ruleset, knowledge, snapshots=[]):
if (variables := match(("@stdio", ["write", "$x", "$y"]), knowledge)) is not None:
try:
byte = int(variables["x"])
length = int(variables["y"])
if byte < 0:
sys.stdout.buffer.write(byte.to_bytes(length, signed=True))
else:
sys.stdout.buffer.write(byte.to_bytes(length))
sys.stdout.buffer.flush()
consume("@stdio", knowledge)
variables["x"] = str(length)
produce(("@stdio", ["wrote", "$x"]), knowledge, variables)
return True
except OverflowError:
length = (byte.bit_length() + 7) // 8
if length == 0:
length = 1
if byte < 0:
sys.stdout.buffer.write(byte.to_bytes(length, signed=True))
else:
sys.stdout.buffer.write(byte.to_bytes(length))
sys.stdout.buffer.flush()
consume("@stdio", knowledge)
variables["x"] = str(length)
produce(("@stdio", ["wrote", "$x"]), knowledge, variables)
return True
except:
pass
elif (variables := match(("@stdio", ["write", "$x"]), knowledge)) is not None:
try:
byte = int(variables["x"])
length = (byte.bit_length() + 7) // 8
if length == 0:
length = 1
if byte < 0:
sys.stdout.buffer.write(byte.to_bytes(length, signed=True))
else:
sys.stdout.buffer.write(byte.to_bytes(length))
sys.stdout.buffer.flush()
consume("@stdio", knowledge)
variables["x"] = str(length)
produce(("@stdio", ["wrote", "$x"]), knowledge, variables)
return True
except:
pass
return False
return [
read, write
]
"
~code~
~name~
"The NFC Module"
~name~
~code~
"
def _nfc():
clf = None
last_card = None
def nfc_read_card():
nonlocal clf
payload = []
identifier = None
card_types = [RemoteTarget('106A'), RemoteTarget('106B'), RemoteTarget('212F')]
target = clf.sense(*card_types)
if target is not None:
tag = nfc.tag.activate(clf, target)
if tag is not None and tag.ndef is not None:
for record in tag.ndef.records:
text = zlib.decompress(base64.b64decode(record.text)).decode()
payload += list(parse(text))
identifier = tag.identifier
return (payload, identifier)
def open(ruleset, knowledge, snapshots=[]):
nonlocal clf
if (variables := match(("@nfc", ["open"]), knowledge)) is not None:
try:
consume("@nfc", knowledge)
clf = nfc.ContactlessFrontend('usb')
return True
except:
produce(("@nfc", ["no-device"]), knowledge)
return True
return False
def close(ruleset, knowledge, snapshots=[]):
nonlocal clf
if (variables := match(("@nfc", ["close"]), knowledge)) is not None:
try:
consume("@nfc", knowledge)
clf.close()
return True
except:
produce(("@nfc", ["no-device"]), knowledge)
return True
return False
def read_card(ruleset, knowledge, snapshots=[]):
nonlocal last_card
if (variables := match(("@nfc", ["read-card"]), knowledge)) is not None:
try:
consume("@nfc", knowledge)
(payload, identifier) = nfc_read_card()
variables["x"] = "".join([hex(byte)[2:] for byte in identifier])
last_card = payload
produce(("@nfc", ["card", "$x"]), knowledge, variables)
return True
except:
produce(("@nfc", ["read-failed"]), knowledge)
return True
return False
def load_card_rules(ruleset, knowledge, snapshots=[]):
nonlocal last_card
if (variables := match(("@nfc", ["load-rules"]), knowledge)) is not None:
try:
for rule in rules(reversed(last_card)):
ruleset.insert(0, rule)
consume("@nfc", knowledge)
return True
except:
pass
return False
def load_card_facts(ruleset, knowledge, snapshots=[]):
if (variables := match(("@nfc", ["load-facts"]), knowledge)) is not None:
try:
prepare(last_card, knowledge)
consume("@nfc", knowledge)
return True
except:
pass
return False
return [
open,
close,
read_card,
load_card_rules,
load_card_facts
]
"
~code~
~name~
"Let's include a REPL"
~name~
~code~
"
def repl(rulesets, primitives, debug):
try:
knowledge, _ = run(rulesets)
print_knowledge(knowledge, debug)
current_stack = ""
command = input("::> ")
while command != "@quit":
if command in {"@run", '!'}:
knowledge, _ = run(rulesets, primitives, knowledge)
if debug:
print_knowledge(knowledge, debug)
if match(("@signal", ["quit"]), knowledge) != None:
break
elif command.startswith('@'):
current_stack = command[1:]
elif command.startswith('.'):
command = command[1:]
knowledge, _ = run(list(parse(command)) + list(rules(rulesets)), primitives, knowledge, debug)
if debug:
print_knowledge(knowledge, debug)
if match(("@signal", ["quit"]), knowledge) != None:
break
elif command.startswith(':'):
command = command[1:]
rulesets = list(parse(command)) + rulesets
knowledge, _ = run(rulesets, primitives, knowledge, debug)
if debug:
print_knowledge(knowledge, debug)
if match(("@signal", ["quit"]), knowledge) != None:
break
elif len(command) != 0:
command = "||:" + current_stack + ':' + command
knowledge, _ = run(list(parse(command)) + list(rules(rulesets)), primitives, knowledge, debug)
if debug:
print_knowledge(knowledge, debug)
if match(("@signal", ["quit"]), knowledge) != None:
break
else:
if debug:
print_knowledge(knowledge, debug)
command = input(":" + current_stack + ":> ")
except EOFError:
pass
except KeyboardInterrupt:
pass
"
~code~
~name~
"Import Python core libraries"
~name~
~code~
"import sys, time, random, copy"
~code~
~name~
"Set up PyGame"
~name~
~code~
"
try:
import warnings
warnings.filterwarnings("ignore")
from os import environ
environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1'
import pygame
import pygame.midi
pygame_supported = True
except:
pygame_supported = False
pass
"
~code~
~name~
"Set up NFC"
~name~
~code~
"
try:
import nfc, ndef, zlib, base64
from nfc.clf import RemoteTarget
nfc_supported = True
except:
nfc_supported = False
pass
"
~code~
~name~
"Finally, the main program"
~name~
~code~
"
def main(name, arguments):
primitives = _time() + [math] + arrays() + stdio()
if pygame_supported:
primitives += graphics() + midi()
if nfc_supported:
primitives += _nfc()
rulesets = []
debug = 0
prompt = False
trace = False
if not arguments:
usage(name)
return
for argument in arguments:
if argument == '-d':
debug += 1
elif argument == '-r':
prompt = True
elif argument == '-t':
trace = True
else:
with open(argument) as file:
rulesets += list(parse(file.read()))
if prompt:
repl(rulesets, primitives, debug)
else:
if len(rulesets) == 0:
usage(name)
return
if debug:
knowledge, snapshots = run(rulesets, primitives, None, debug, trace)
if trace:
for index in range(len(snapshots)):
print("Snapshot", str(index) + ':')
print("----------------")
print_knowledge(snapshots[index], debug)
print("")
else:
print_knowledge(knowledge, debug)
else:
run(rulesets, primitives, None, debug, trace)
if __name__ == "__main__":
main(sys.argv[0], sys.argv[1:])
"
~code~
1 | ^^ |
2 | ~entry point~ " |
3 | @@@ Import Python core libraries @@@ |
4 | @@@ Set up PyGame @@@ |
5 | @@@ Set up NFC @@@ |
6 | @@@ The implementation of a Nova parser @@@ |
7 | @@@ The implementation of a Nova interpreter @@@ |
8 | @@@ The built-in modules @@@ |
9 | @@@ Let's include a REPL @@@ |
10 | @@@ Finally, the main program @@@ |
11 | " |
12 | |
13 | ~name~ |
14 | "The implementation of a Nova parser" |
15 | ~name~ |
16 | ~code~ |
17 | " |
18 | @@@ Parse the container format @@@ |
19 | @@@ Parse the label format @@@ |
20 | @@@ Parse the pattern format @@@ |
21 | @@@ Chain these steps together for the parser @@@ |
22 | " |
23 | ~code~ |
24 | |
25 | ~name~ |
26 | "The built-in modules" |
27 | ~name~ |
28 | ~code~ |
29 | " |
30 | @@@ The Math Module @@@ |
31 | @@@ The Arrays Module @@@ |
32 | @@@ The Graphics Module @@@ |
33 | @@@ The Midi Module @@@ |
34 | @@@ The Time Module @@@ |
35 | @@@ The Stdio Module @@@ |
36 | @@@ The NFC Module @@@ |
37 | " |
38 | ~code~ |
39 | |
40 | ~name~ |
41 | "Parse the container format" |
42 | ~name~ |
43 | ~code~ |
44 | " |
45 | def parse_container(string): |
46 | left = [] |
47 | right = [] |
48 | target = left |
49 | characters = (character for character in list(string) + [None]) |
50 | delimiter = None |
51 | for character in characters: |
52 | if character in {' ', '\n', '\r', '\t'}: |
53 | continue |
54 | delimiter = character |
55 | break |
56 | if delimiter is None: |
57 | return None |
58 | for character in characters: |
59 | if character in {delimiter, None}: |
60 | if target is left: |
61 | target = right |
62 | else: |
63 | target = left |
64 | yield (left.copy(), right.copy()) |
65 | left.clear() |
66 | right.clear() |
67 | else: |
68 | target.append(character) |
69 | " |
70 | ~code~ |
71 | |
72 | ~name~ |
73 | "Parse the label format" |
74 | ~name~ |
75 | ~code~ |
76 | " |
77 | def parse_labels(rules): |
78 | left = [] |
79 | right = [] |
80 | pair = ([], []) |
81 | target = left |
82 | delimiter = None |
83 | for condition, result in rules: |
84 | for side in (condition, result): |
85 | characters = (character for character in side + [None]) |
86 | state = "find delimiter" |
87 | for character in characters: |
88 | if character == None: |
89 | label = "".join(pair[0]).strip() |
90 | pattern = "".join(pair[1]).strip() |
91 | if not(len(label) == 0 and len(pattern) == 0): |
92 | target.append((label, pattern)) |
93 | pair[0].clear() |
94 | pair[1].clear() |
95 | break |
96 | elif state == "find delimiter": |
97 | if character in {' ', '\n', '\r', '\t'}: |
98 | continue |
99 | delimiter = character |
100 | state = "parse label" |
101 | elif state == "parse label": |
102 | if character == delimiter: |
103 | state = "parse pattern" |
104 | continue |
105 | pair[0].append(character) |
106 | elif state == "parse pattern": |
107 | if character in {delimiter, None}: |
108 | state = "parse label" |
109 | label = "".join(pair[0]).strip() |
110 | pattern = "".join(pair[1]).strip() |
111 | if not(len(label) == 0 and len(pattern) == 0): |
112 | target.append((label, pattern)) |
113 | pair[0].clear() |
114 | pair[1].clear() |
115 | continue |
116 | pair[1].append(character) |
117 | if target is left: |
118 | target = right |
119 | else: |
120 | target = left |
121 | yield (left.copy(), right.copy()) |
122 | left.clear() |
123 | right.clear() |
124 | " |
125 | ~code~ |
126 | |
127 | ~name~ |
128 | "Parse the pattern format" |
129 | ~name~ |
130 | ~code~ |
131 | " |
132 | def parse_patterns(rules): |
133 | for condition, result in rules: |
134 | left = [] |
135 | right = [] |
136 | preserves = [] |
137 | for label, pattern in condition: |
138 | preserve = False |
139 | if pattern.endswith('?'): |
140 | pattern = pattern[:-1] |
141 | preserve = True |
142 | if (pattern.startswith('"') or pattern.startswith("'")) and (pattern.endswith('"') or pattern.endswith("'")): |
143 | for character in pattern[1:-1]: |
144 | left.append((label, [str(ord(character))])) |
145 | if preserve: |
146 | right.append((label, [str(ord(character))])) |
147 | elif pattern.startswith('(') and pattern.endswith(')'): |
148 | for token in pattern[1:-1].split(): |
149 | left.append((label, [token])) |
150 | if preserve: |
151 | right.append((label, [token])) |
152 | elif pattern.startswith('.'): |
153 | for tuple in pattern.split('.'): |
154 | tuple = tuple.strip() |
155 | if len(tuple) != 0: |
156 | left.append((label, tuple.split())) |
157 | if preserve: |
158 | right.append((label, tuple.split())) |
159 | else: |
160 | left.append((label, pattern.split())) |
161 | if preserve: |
162 | preserves.append((label, pattern.split())) |
163 | for label, pattern in result: |
164 | if (pattern.startswith('"') or pattern.startswith("'")) and (pattern.endswith('"') or pattern.endswith("'")): |
165 | for character in pattern[1:-1]: |
166 | right.append((label, [str(ord(character))])) |
167 | elif pattern.startswith('(') and pattern.endswith(')'): |
168 | for token in pattern[1:-1].split(): |
169 | right.append((label, [token])) |
170 | elif pattern.startswith('.'): |
171 | for tuple in pattern.split('.'): |
172 | tuple = tuple.strip() |
173 | if len(tuple) != 0: |
174 | right.append((label, tuple.split())) |
175 | else: |
176 | right.append((label, pattern.split())) |
177 | yield (left.copy(), right.copy() + preserves.copy()) |
178 | left.clear() |
179 | right.clear() |
180 | preserves.clear() |
181 | " |
182 | ~code~ |
183 | |
184 | ~name~ |
185 | "Chain these steps together for the parser" |
186 | ~name~ |
187 | ~code~ |
188 | " |
189 | def parse(string): |
190 | return parse_patterns(parse_labels(parse_container(string))) |
191 | " |
192 | ~code~ |
193 | |
194 | ~name~ |
195 | "The implementation of a Nova interpreter" |
196 | ~name~ |
197 | ~code~ |
198 | " |
199 | def facts(ruleset): |
200 | for left, right in ruleset: |
201 | if not left: |
202 | for element in right: |
203 | yield element |
204 | |
205 | def rules(ruleset): |
206 | for left, right in ruleset: |
207 | if left: |
208 | yield (left, right) |
209 | |
210 | def match(pattern, knowledge, variables=None): |
211 | if variables is None: |
212 | variables = {} |
213 | label, elements = pattern |
214 | if label not in knowledge or not knowledge[label][0]: |
215 | return None |
216 | if knowledge[label][1] >= len(knowledge[label][0]): |
217 | knowledge[label][1] = 0 |
218 | return None |
219 | top = knowledge[label][0][(-1 - knowledge[label][1])] |
220 | len_top = len(top) |
221 | if len_top != len(elements): |
222 | knowledge[label][1] = 0 |
223 | return None |
224 | for index in range(len_top): |
225 | left = elements[index] |
226 | right = top[index] |
227 | if left[0] == '$': |
228 | variable = left[1:] |
229 | if variable not in variables: |
230 | variables[variable] = right |
231 | elif variables[variable] != right: |
232 | knowledge[label][1] = 0 |
233 | return None |
234 | elif left != right: |
235 | knowledge[label][1] = 0 |
236 | return None |
237 | knowledge[label][1] += 1 |
238 | return variables |
239 | |
240 | def consume(pattern, knowledge): |
241 | if type(pattern) == str: |
242 | knowledge[pattern][0].pop() |
243 | knowledge[pattern][1] = 0 |
244 | else: |
245 | label, _ = pattern |
246 | knowledge[label][0].pop() |
247 | knowledge[label][1] = 0 |
248 | |
249 | def produce(pattern, knowledge, variables=None): |
250 | if variables is None: |
251 | variables = {} |
252 | label, elements = pattern |
253 | len_elements = len(elements) |
254 | fact = [None] * len_elements |
255 | for index in range(len_elements): |
256 | element = elements[index] |
257 | if element[0] == '$': |
258 | variable = element[1:] |
259 | if variable in variables: |
260 | element = variables[variable] |
261 | fact[index] = element |
262 | if label not in knowledge: |
263 | knowledge[label] = [[], 0] |
264 | knowledge[label][0].append(fact) |
265 | knowledge[label][1] = 0 |
266 | |
267 | def reset(pattern, knowledge): |
268 | label, _ = pattern |
269 | if label in knowledge: |
270 | knowledge[label][1] = 0 |
271 | |
272 | def prepare(ruleset, knowledge=None): |
273 | if knowledge == None: |
274 | knowledge = {} |
275 | for fact in reversed(list(facts(ruleset))): |
276 | label, elements = fact |
277 | if label not in knowledge: |
278 | knowledge[label] = [[], 0] |
279 | knowledge[label][0].append(elements) |
280 | return knowledge |
281 | |
282 | def print_knowledge(knowledge, debug=0, maximum=10): |
283 | for label, facts in knowledge.items(): |
284 | facts = facts[0] |
285 | if not facts: |
286 | continue |
287 | if not label: |
288 | label = ":" |
289 | print(label + ': ', end="") |
290 | if len(facts) >= maximum and debug % 2: |
291 | print("...", len(facts), "...") |
292 | continue |
293 | if facts: |
294 | if len(facts[-1]) == 0: |
295 | print("<blank>") |
296 | else: |
297 | print(" ".join(facts[-1])) |
298 | for fact in reversed(facts[:-1]): |
299 | if len(fact) == 0: |
300 | fact = ["<blank>"] |
301 | print(' ' * (len(label) + 1), " ".join(fact)) |
302 | |
303 | def step(ruleset, primitives=[], knowledge=None, snapshots=[], debug=0, trace=False): |
304 | variables = {} |
305 | steps = 0 |
306 | if debug >= 3: |
307 | print_knowledge(knowledge, debug) |
308 | print("") |
309 | matched = False |
310 | for primitive in primitives: |
311 | steps += 1 |
312 | if primitive(ruleset, knowledge, snapshots): |
313 | matched = True |
314 | break |
315 | if matched: |
316 | return (True, steps, knowledge) |
317 | for left, right in ruleset: |
318 | steps += 1 |
319 | matched = True |
320 | variables.clear() |
321 | for pattern in left: |
322 | if match(pattern, knowledge, variables) is None: |
323 | matched = False |
324 | for pattern in left: |
325 | reset(pattern, knowledge) |
326 | break |
327 | if matched: |
328 | for pattern in left: |
329 | consume(pattern, knowledge) |
330 | for pattern in reversed(right): |
331 | produce(pattern, knowledge, variables) |
332 | break |
333 | return (matched, steps, knowledge) |
334 | |
335 | def run(ruleset, primitives=[], knowledge=None, debug=0, trace=False): |
336 | knowledge = prepare(ruleset, knowledge) |
337 | ruleset = list(rules(ruleset)) |
338 | matched = True |
339 | steps = 0 |
340 | snapshots = [] |
341 | while matched: |
342 | matched, completed, knowledge = step(ruleset, primitives, knowledge, snapshots, debug, trace) |
343 | steps += completed |
344 | if trace: |
345 | snapshots.append(copy.deepcopy(knowledge)) |
346 | if debug: |
347 | print("Took", steps, "steps.") |
348 | return (knowledge, snapshots) |
349 | |
350 | def usage(name): |
351 | print(name, ":: a nova interpreter") |
352 | print("usage:") |
353 | print('\t' + name, "<file> [-d] [-r]") |
354 | " |
355 | ~code~ |
356 | |
357 | ~name~ |
358 | "The Math Module" |
359 | ~name~ |
360 | ~code~ |
361 | " |
362 | def math(ruleset, knowledge, snapshots=[], trace=False): |
363 | if (variables := match(("@math", ["$operation", "$x", "$y"]), knowledge)) is not None: |
364 | operation = variables["operation"] |
365 | x = int(variables["x"]) |
366 | y = int(variables["y"]) |
367 | if operation == "add": |
368 | variables["z"] = str(x + y) |
369 | elif operation == "subtract": |
370 | variables["z"] = str(x - y) |
371 | elif operation == "multiply": |
372 | variables["z"] = str(x * y) |
373 | elif operation == "divide": |
374 | variables["z"] = str(x // y) |
375 | elif operation == "modulo": |
376 | variables["z"] = str(x % y) |
377 | elif operation == "compare": |
378 | if x < y: |
379 | variables["z"] = "less" |
380 | elif x == y: |
381 | variables["z"] = "equal" |
382 | else: |
383 | variables["z"] = "greater" |
384 | elif operation == "random": |
385 | if y < x: |
386 | x, y = y, x |
387 | variables["z"] = str(random.randrange(x, y)) |
388 | consume("@math", knowledge) |
389 | produce(("@math", ["$z"]), knowledge, variables) |
390 | return True |
391 | return False |
392 | " |
393 | ~code~ |
394 | |
395 | ~name~ |
396 | "The Arrays Module" |
397 | ~name~ |
398 | ~code~ |
399 | " |
400 | def arrays(): |
401 | arrays = {} |
402 | def create(ruleset, knowledge, snapshots=[], trace=False): |
403 | nonlocal arrays |
404 | if (variables := match(("@array", ["create", "$array", "$size"]), knowledge)) is not None: |
405 | array = variables["array"] |
406 | size = int(variables["size"]) |
407 | consume("@array", knowledge) |
408 | if array in arrays: |
409 | produce(("@array", ["$array", "exists"]), knowledge, variables) |
410 | else: |
411 | arrays[array] = [None] * size |
412 | return True |
413 | return False |
414 | def set(ruleset, knowledge, snapshots=[], trace=False): |
415 | nonlocal arrays |
416 | if (variables := match(("@array", ["set", "$array", "$index", "$value"]), knowledge)) is not None: |
417 | array = variables["array"] |
418 | index = int(variables["index"]) |
419 | value = variables["value"] |
420 | consume("@array", knowledge) |
421 | if array not in arrays: |
422 | produce(("@array", ["$array", "not", "found"]), knowledge, variables) |
423 | elif index >= len(arrays[array]): |
424 | produce(("@array", ["$index", "out", "of", "bounds", "for", "$array"]), knowledge, variables) |
425 | else: |
426 | arrays[array][index] = value |
427 | return True |
428 | return False |
429 | def get(ruleset, knowledge, snapshots=[], trace=False): |
430 | nonlocal arrays |
431 | if (variables := match(("@array", ["get", "$array", "$index"]), knowledge)) is not None: |
432 | array = variables["array"] |
433 | index = int(variables["index"]) |
434 | consume("@array", knowledge) |
435 | if array not in arrays: |
436 | produce(("@array", ["$array", "not", "found"]), knowledge, variables) |
437 | elif index >= len(arrays[array]): |
438 | produce(("@array", ["$index", "out", "of", "bounds", "for", "$array"]), knowledge, variables) |
439 | else: |
440 | if arrays[array][index] == None: |
441 | produce(("@array", ["$array", "$index"]), knowledge, variables) |
442 | else: |
443 | variables["value"] = str(arrays[array][index]) |
444 | produce(("@array", ["$array", "$index", "$value"]), knowledge, variables) |
445 | return True |
446 | return False |
447 | return [ |
448 | create, |
449 | set, |
450 | get |
451 | ] |
452 | " |
453 | ~code~ |
454 | |
455 | ~name~ |
456 | "The Graphics Module" |
457 | ~name~ |
458 | ~code~ |
459 | " |
460 | def graphics(): |
461 | keys = {} |
462 | mouse_buttons = {} |
463 | screen = None |
464 | pixels = None |
465 | clock = None |
466 | font = None |
467 | resolution = (0, 0) |
468 | def poll_input(ruleset, knowledge, snapshots=[]): |
469 | nonlocal keys |
470 | nonlocal mouse_buttons |
471 | if match(("@input", ["poll-input"]), knowledge) is not None: |
472 | consume("@input", knowledge) |
473 | for event in pygame.event.get(): |
474 | if event.type == pygame.QUIT: |
475 | produce(("@signal", ["quit"]), knowledge) |
476 | keys = pygame.key.get_pressed() |
477 | mouse_buttons = pygame.mouse.get_pressed() |
478 | return True |
479 | return False |
480 | def check_key(ruleset, knowledge, snapshots=[]): |
481 | nonlocal keys |
482 | if (variables := match(("@input", ["check-key", "$key"]), knowledge)) is not None: |
483 | consume("@input", knowledge) |
484 | keycode = pygame.key.key_code(variables["key"]) |
485 | if keys[keycode]: |
486 | produce(("@input", ["key-pressed", "$key"]), knowledge, variables) |
487 | else: |
488 | produce(("@input", ["key-released", "$key"]), knowledge, variables) |
489 | return True |
490 | return False |
491 | def check_mouse_button(ruleset, knowledge, snapshots=[]): |
492 | nonlocal mouse_buttons |
493 | if (variables := match(("@input", ["check-mouse-button", "$button"]), knowledge)) is not None: |
494 | consume("@input", knowledge) |
495 | button = int(variables["button"]) |
496 | if mouse_buttons[button]: |
497 | produce(("@input", ["mouse-button-pressed", "$button"]), knowledge, variables) |
498 | else: |
499 | produce(("@input", ["mouse-button-released", "$button"]), knowledge, variables) |
500 | return True |
501 | return False |
502 | def get_mouse_position(ruleset, knowledge, snapshots=[]): |
503 | if (variables := match(("@input", ["get-mouse-position"]), knowledge)) is not None: |
504 | consume("@input", knowledge) |
505 | position = pygame.mouse.get_pos() |
506 | variables["x"], variables["y"] = (str(position[0]), str(position[1])) |
507 | produce(("@input", ["mouse-position", "$x", "$y"]), knowledge, variables) |
508 | return True |
509 | return False |
510 | def set_pixel(ruleset, knowledge, snapshots=[]): |
511 | nonlocal pixels |
512 | if (variables := match(("@graphics", ["set-pixel", "$x", "$y", "$r", "$g", "$b"]), knowledge)) is not None: |
513 | x = int(variables["x"]) |
514 | y = int(variables["y"]) |
515 | r = int(variables["r"]) |
516 | g = int(variables["g"]) |
517 | b = int(variables["b"]) |
518 | pixels[x % resolution[0], y % resolution[1]] = (r % 256, g % 256, b % 256) |
519 | consume("@graphics", knowledge) |
520 | return True |
521 | return False |
522 | def draw_line(ruleset, knowledge, snapshots=[]): |
523 | nonlocal screen |
524 | if (variables := match(("@graphics", ["draw-line", "$x1", "$y1", "$x2", "$y2", "$r", "$g", "$b", "$w"]), knowledge)) is not None: |
525 | x1 = int(variables["x1"]) |
526 | y1 = int(variables["y1"]) |
527 | x2 = int(variables["x2"]) |
528 | y2 = int(variables["y2"]) |
529 | r = int(variables["r"]) |
530 | g = int(variables["g"]) |
531 | b = int(variables["b"]) |
532 | w = int(variables["w"]) |
533 | pygame.draw.line(screen, (r % 256, g % 256, b % 256), (x1, y1), (x2, y2), w) |
534 | consume("@graphics", knowledge) |
535 | return True |
536 | return False |
537 | def draw_rect(ruleset, knowledge, snapshots=[]): |
538 | nonlocal screen |
539 | if (variables := match(("@graphics", ["draw-rect", "$x1", "$y1", "$x2", "$y2", "$r", "$g", "$b", "$w"]), knowledge)) is not None: |
540 | x1 = int(variables["x1"]) |
541 | y1 = int(variables["y1"]) |
542 | x2 = int(variables["x2"]) |
543 | y2 = int(variables["y2"]) |
544 | r = int(variables["r"]) |
545 | g = int(variables["g"]) |
546 | b = int(variables["b"]) |
547 | w = int(variables["w"]) |
548 | pygame.draw.rect(screen, (r % 256, g % 256, b % 256), (x1, y1, x2, y2), w) |
549 | consume("@graphics", knowledge) |
550 | return True |
551 | return False |
552 | def draw_circle(ruleset, knowledge, snapshots=[]): |
553 | nonlocal screen |
554 | if (variables := match(("@graphics", ["draw-circle", "$x", "$y", "$d", "$r", "$g", "$b", "$w"]), knowledge)) is not None: |
555 | x = int(variables["x"]) |
556 | y = int(variables["y"]) |
557 | d = int(variables["d"]) |
558 | r = int(variables["r"]) |
559 | g = int(variables["g"]) |
560 | b = int(variables["b"]) |
561 | w = int(variables["w"]) |
562 | pygame.draw.circle(screen, (r % 256, g % 256, b % 256), (x, y), d, w) |
563 | consume("@graphics", knowledge) |
564 | return True |
565 | return False |
566 | def clear_screen(ruleset, knowledge, snapshots=[]): |
567 | nonlocal screen |
568 | if (variables := match(("@graphics", ["clear-screen", "$r", "$g", "$b"]), knowledge)) is not None: |
569 | r = int(variables["r"]) |
570 | g = int(variables["g"]) |
571 | b = int(variables["b"]) |
572 | screen.fill((r % 256, g % 256, b % 256)) |
573 | consume("@graphics", knowledge) |
574 | return True |
575 | return False |
576 | def draw_fps(ruleset, knowledge, snapshots=[]): |
577 | nonlocal pixels |
578 | nonlocal screen |
579 | nonlocal clock |
580 | nonlocal font |
581 | if match(("@graphics", ["draw-fps"]), knowledge) is not None: |
582 | if clock is None: |
583 | clock = pygame.time.Clock() |
584 | clock.tick() |
585 | if font is None: |
586 | font = pygame.font.SysFont("m3x6", 16) |
587 | framerate = clock.get_fps() |
588 | if framerate <= 1_000_000: |
589 | fps = font.render(str(int(clock.get_fps())) , 1, pygame.Color("RED")) |
590 | pixels.close() |
591 | screen.blit(fps, (0, 0)) |
592 | pixels = pygame.PixelArray(screen) |
593 | consume("@graphics", knowledge) |
594 | return True |
595 | return False |
596 | def display(ruleset, knowledge, snapshots=[]): |
597 | nonlocal pixels |
598 | if match(("@graphics", ["display"]), knowledge) is not None: |
599 | pixels.close() |
600 | pygame.display.flip() |
601 | pixels = pygame.PixelArray(screen) |
602 | consume("@graphics", knowledge) |
603 | return True |
604 | return False |
605 | def draw_text(ruleset, knowledge, snapshots=[]): |
606 | nonlocal font |
607 | nonlocal screen |
608 | nonlocal pixels |
609 | if (variables := match(("@graphics", ["draw-text", "$x", "$y"]), knowledge)) is not None: |
610 | x = int(variables["x"]) |
611 | y = int(variables["y"]) |
612 | if font is None: |
613 | font = pygame.font.SysFont("m3x6", 16) |
614 | text = ''.join(chr(int(n[0])) for n in reversed(knowledge["@text to draw"][0])) |
615 | knowledge["@text to draw"] = [[], 0] |
616 | drawn_text = font.render(text, False, pygame.Color("WHITE")) |
617 | pixels.close() |
618 | pygame.draw.rect(screen, (0, 0, 0), (0, 48, 64, 64)) |
619 | screen.blit(drawn_text, (x, y)) |
620 | pixels = pygame.PixelArray(screen) |
621 | consume("@graphics", knowledge) |
622 | return True |
623 | return False |
624 | |
625 | |
626 | def set_resolution(ruleset, knowledge, snapshots=[]): |
627 | nonlocal screen |
628 | nonlocal pixels |
629 | nonlocal resolution |
630 | if (variables := match(("@graphics", ["set-resolution", "$x", "$y"]), knowledge)) is not None: |
631 | if screen is None: |
632 | pygame.init() |
633 | x = int(variables["x"]) |
634 | y = int(variables["y"]) |
635 | screen = pygame.display.set_mode((x, y)) |
636 | pygame.display.set_caption("Nova") |
637 | icon = pygame.Surface((32, 32)) |
638 | icon.fill("black") |
639 | icon.set_colorkey((0, 0, 0)) |
640 | pygame.display.set_icon(icon) |
641 | pixels = pygame.PixelArray(screen) |
642 | resolution = (x, y) |
643 | consume("@graphics", knowledge) |
644 | return True |
645 | elif (variables := match(("@graphics", ["set-resolution", "fullscreen", "$x", "$y"]), knowledge)) is not None: |
646 | if screen is None: |
647 | pygame.init() |
648 | x = int(variables["x"]) |
649 | y = int(variables["y"]) |
650 | screen = pygame.display.set_mode((x, y), flags=pygame.FULLSCREEN | pygame.SCALED) |
651 | pygame.display.set_caption("Nova") |
652 | icon = pygame.Surface((32, 32)) |
653 | icon.fill("black") |
654 | icon.set_colorkey((0, 0, 0)) |
655 | pygame.display.set_icon(icon) |
656 | pixels = pygame.PixelArray(screen) |
657 | resolution = (x, y) |
658 | consume("@graphics", knowledge) |
659 | return True |
660 | elif (variables := match(("@graphics", ["set-resolution", "fullscreen"]), knowledge)) is not None: |
661 | if screen is None: |
662 | pygame.init() |
663 | screen = pygame.display.set_mode((0, 0), flags=pygame.FULLSCREEN) |
664 | pygame.display.set_caption("Nova") |
665 | icon = pygame.Surface((32, 32)) |
666 | icon.fill("black") |
667 | icon.set_colorkey((0, 0, 0)) |
668 | pygame.display.set_icon(icon) |
669 | pixels = pygame.PixelArray(screen) |
670 | resolution = pygame.display.get_surface().get_size() |
671 | consume("@graphics", knowledge) |
672 | return True |
673 | return False |
674 | return [ |
675 | set_pixel, |
676 | draw_line, |
677 | draw_rect, |
678 | draw_circle, |
679 | draw_text, |
680 | display, |
681 | poll_input, |
682 | draw_fps, |
683 | clear_screen, |
684 | check_key, |
685 | check_mouse_button, |
686 | get_mouse_position, |
687 | set_resolution |
688 | ] |
689 | " |
690 | ~code~ |
691 | ~name~ |
692 | "The Midi Module" |
693 | ~name~ |
694 | ~code~ |
695 | " |
696 | def midi(): |
697 | output = None |
698 | def set_output(ruleset, knowledge, snapshots=[]): |
699 | nonlocal output |
700 | if (variables := match(("@midi", ["set-output", "$x"]), knowledge)) is not None: |
701 | if output is None: |
702 | pygame.midi.init() |
703 | else: |
704 | del output |
705 | output = pygame.midi.Output(int(variables["x"])) |
706 | consume("@midi", knowledge) |
707 | return True |
708 | return False |
709 | def abort(ruleset, knowledge, snapshots=[]): |
710 | nonlocal output |
711 | if (variables := match(("@midi", ["abort"]), knowledge)) is not None: |
712 | output.abort() |
713 | consume("@midi", knowledge) |
714 | return True |
715 | return False |
716 | def set_instrument(ruleset, knowledge, snapshots=[]): |
717 | nonlocal output |
718 | if (variables := match(("@midi", ["set-instrument", "$x"]), knowledge)) is not None: |
719 | output.set_instrument(int(variables["x"])) |
720 | consume("@midi", knowledge) |
721 | return True |
722 | return False |
723 | def note(ruleset, knowledge, snapshots=[]): |
724 | nonlocal output |
725 | if (variables := match(("@midi", ["note", "$x", "$y", "$z", "$w"]), knowledge)) is not None: |
726 | state = variables["x"] |
727 | note = int(variables["y"]) |
728 | velocity = int(variables["z"]) |
729 | channel = int(variables["w"]) |
730 | if state == "on": |
731 | output.note_on(note, velocity, channel) |
732 | elif state == "off": |
733 | output.note_off(note, velocity, channel) |
734 | else: |
735 | return False |
736 | consume("@midi", knowledge) |
737 | return True |
738 | elif (variables := match(("@midi", ["note", "$x", "$y", "$z"]), knowledge)) is not None: |
739 | state = variables["x"] |
740 | note = int(variables["y"]) |
741 | velocity = int(variables["z"]) |
742 | if state == "on": |
743 | output.note_on(note, velocity) |
744 | elif state == "off": |
745 | output.note_off(note, velocity) |
746 | else: |
747 | return False |
748 | consume("@midi", knowledge) |
749 | return True |
750 | return False |
751 | return [ |
752 | set_output, |
753 | abort, |
754 | set_instrument, |
755 | note |
756 | ] |
757 | " |
758 | ~code~ |
759 | |
760 | ~name~ |
761 | "The Time Module" |
762 | ~name~ |
763 | ~code~ |
764 | " |
765 | def _time(): |
766 | def sleep(ruleset, knowledge, snapshots=[]): |
767 | if (variables := match(("@time", ["sleep", "$x", "$y"]), knowledge)) is not None: |
768 | duration = int(variables["x"]) |
769 | unit = variables["y"] |
770 | if unit == "seconds": |
771 | time.sleep(duration) |
772 | elif unit == "milliseconds": |
773 | time.sleep(0.001 * duration) |
774 | else: |
775 | return False |
776 | consume("@time", knowledge) |
777 | return True |
778 | return False |
779 | return [ |
780 | sleep |
781 | ] |
782 | " |
783 | ~code~ |
784 | |
785 | ~name~ |
786 | "The Stdio Module" |
787 | ~name~ |
788 | ~code~ |
789 | " |
790 | def stdio(): |
791 | def read(ruleset, knowledge, snapshots=[]): |
792 | if (variables := match(("@stdio", ["read", "$x"]), knowledge)) is not None: |
793 | try: |
794 | amount = int(variables["x"]) |
795 | bytes = sys.stdin.read(amount) |
796 | consume("@stdio", knowledge) |
797 | for byte in reversed(bytes.encode()): |
798 | variables["x"] = str(byte) |
799 | produce(("@stdio", ["$x"]), knowledge, variables) |
800 | return True |
801 | except KeyboardInterrupt: |
802 | consume("@stdio", knowledge) |
803 | return True |
804 | except: |
805 | pass |
806 | elif (variables := match(("@stdio", ["read"]), knowledge)) is not None: |
807 | try: |
808 | variables["x"] = str(ord(sys.stdin.read(1))) |
809 | consume("@stdio", knowledge) |
810 | produce(("@stdio", ["$x"]), knowledge, variables) |
811 | return True |
812 | except KeyboardInterrupt: |
813 | consume("@stdio", knowledge) |
814 | return True |
815 | except: |
816 | pass |
817 | return False |
818 | def write(ruleset, knowledge, snapshots=[]): |
819 | if (variables := match(("@stdio", ["write", "$x", "$y"]), knowledge)) is not None: |
820 | try: |
821 | byte = int(variables["x"]) |
822 | length = int(variables["y"]) |
823 | if byte < 0: |
824 | sys.stdout.buffer.write(byte.to_bytes(length, signed=True)) |
825 | else: |
826 | sys.stdout.buffer.write(byte.to_bytes(length)) |
827 | sys.stdout.buffer.flush() |
828 | consume("@stdio", knowledge) |
829 | variables["x"] = str(length) |
830 | produce(("@stdio", ["wrote", "$x"]), knowledge, variables) |
831 | return True |
832 | except OverflowError: |
833 | length = (byte.bit_length() + 7) // 8 |
834 | if length == 0: |
835 | length = 1 |
836 | if byte < 0: |
837 | sys.stdout.buffer.write(byte.to_bytes(length, signed=True)) |
838 | else: |
839 | sys.stdout.buffer.write(byte.to_bytes(length)) |
840 | sys.stdout.buffer.flush() |
841 | consume("@stdio", knowledge) |
842 | variables["x"] = str(length) |
843 | produce(("@stdio", ["wrote", "$x"]), knowledge, variables) |
844 | return True |
845 | except: |
846 | pass |
847 | elif (variables := match(("@stdio", ["write", "$x"]), knowledge)) is not None: |
848 | try: |
849 | byte = int(variables["x"]) |
850 | length = (byte.bit_length() + 7) // 8 |
851 | if length == 0: |
852 | length = 1 |
853 | if byte < 0: |
854 | sys.stdout.buffer.write(byte.to_bytes(length, signed=True)) |
855 | else: |
856 | sys.stdout.buffer.write(byte.to_bytes(length)) |
857 | sys.stdout.buffer.flush() |
858 | consume("@stdio", knowledge) |
859 | variables["x"] = str(length) |
860 | produce(("@stdio", ["wrote", "$x"]), knowledge, variables) |
861 | return True |
862 | except: |
863 | pass |
864 | return False |
865 | return [ |
866 | read, write |
867 | ] |
868 | " |
869 | ~code~ |
870 | |
871 | ~name~ |
872 | "The NFC Module" |
873 | ~name~ |
874 | ~code~ |
875 | " |
876 | def _nfc(): |
877 | clf = None |
878 | last_card = None |
879 | def nfc_read_card(): |
880 | nonlocal clf |
881 | payload = [] |
882 | identifier = None |
883 | card_types = [RemoteTarget('106A'), RemoteTarget('106B'), RemoteTarget('212F')] |
884 | target = clf.sense(*card_types) |
885 | if target is not None: |
886 | tag = nfc.tag.activate(clf, target) |
887 | if tag is not None and tag.ndef is not None: |
888 | for record in tag.ndef.records: |
889 | text = zlib.decompress(base64.b64decode(record.text)).decode() |
890 | payload += list(parse(text)) |
891 | identifier = tag.identifier |
892 | return (payload, identifier) |
893 | def open(ruleset, knowledge, snapshots=[]): |
894 | nonlocal clf |
895 | if (variables := match(("@nfc", ["open"]), knowledge)) is not None: |
896 | try: |
897 | consume("@nfc", knowledge) |
898 | clf = nfc.ContactlessFrontend('usb') |
899 | return True |
900 | except: |
901 | produce(("@nfc", ["no-device"]), knowledge) |
902 | return True |
903 | return False |
904 | def close(ruleset, knowledge, snapshots=[]): |
905 | nonlocal clf |
906 | if (variables := match(("@nfc", ["close"]), knowledge)) is not None: |
907 | try: |
908 | consume("@nfc", knowledge) |
909 | clf.close() |
910 | return True |
911 | except: |
912 | produce(("@nfc", ["no-device"]), knowledge) |
913 | return True |
914 | return False |
915 | def read_card(ruleset, knowledge, snapshots=[]): |
916 | nonlocal last_card |
917 | if (variables := match(("@nfc", ["read-card"]), knowledge)) is not None: |
918 | try: |
919 | consume("@nfc", knowledge) |
920 | (payload, identifier) = nfc_read_card() |
921 | variables["x"] = "".join([hex(byte)[2:] for byte in identifier]) |
922 | last_card = payload |
923 | produce(("@nfc", ["card", "$x"]), knowledge, variables) |
924 | return True |
925 | except: |
926 | produce(("@nfc", ["read-failed"]), knowledge) |
927 | return True |
928 | return False |
929 | def load_card_rules(ruleset, knowledge, snapshots=[]): |
930 | nonlocal last_card |
931 | if (variables := match(("@nfc", ["load-rules"]), knowledge)) is not None: |
932 | try: |
933 | for rule in rules(reversed(last_card)): |
934 | ruleset.insert(0, rule) |
935 | consume("@nfc", knowledge) |
936 | return True |
937 | except: |
938 | pass |
939 | return False |
940 | def load_card_facts(ruleset, knowledge, snapshots=[]): |
941 | if (variables := match(("@nfc", ["load-facts"]), knowledge)) is not None: |
942 | try: |
943 | prepare(last_card, knowledge) |
944 | consume("@nfc", knowledge) |
945 | return True |
946 | except: |
947 | pass |
948 | return False |
949 | return [ |
950 | open, |
951 | close, |
952 | read_card, |
953 | load_card_rules, |
954 | load_card_facts |
955 | ] |
956 | " |
957 | ~code~ |
958 | |
959 | ~name~ |
960 | "Let's include a REPL" |
961 | ~name~ |
962 | ~code~ |
963 | " |
964 | def repl(rulesets, primitives, debug): |
965 | try: |
966 | knowledge, _ = run(rulesets) |
967 | print_knowledge(knowledge, debug) |
968 | current_stack = "" |
969 | command = input("::> ") |
970 | while command != "@quit": |
971 | if command in {"@run", '!'}: |
972 | knowledge, _ = run(rulesets, primitives, knowledge) |
973 | if debug: |
974 | print_knowledge(knowledge, debug) |
975 | if match(("@signal", ["quit"]), knowledge) != None: |
976 | break |
977 | elif command.startswith('@'): |
978 | current_stack = command[1:] |
979 | elif command.startswith('.'): |
980 | command = command[1:] |
981 | knowledge, _ = run(list(parse(command)) + list(rules(rulesets)), primitives, knowledge, debug) |
982 | if debug: |
983 | print_knowledge(knowledge, debug) |
984 | if match(("@signal", ["quit"]), knowledge) != None: |
985 | break |
986 | elif command.startswith(':'): |
987 | command = command[1:] |
988 | rulesets = list(parse(command)) + rulesets |
989 | knowledge, _ = run(rulesets, primitives, knowledge, debug) |
990 | if debug: |
991 | print_knowledge(knowledge, debug) |
992 | if match(("@signal", ["quit"]), knowledge) != None: |
993 | break |
994 | elif len(command) != 0: |
995 | command = "||:" + current_stack + ':' + command |
996 | knowledge, _ = run(list(parse(command)) + list(rules(rulesets)), primitives, knowledge, debug) |
997 | if debug: |
998 | print_knowledge(knowledge, debug) |
999 | if match(("@signal", ["quit"]), knowledge) != None: |
1000 | break |
1001 | else: |
1002 | if debug: |
1003 | print_knowledge(knowledge, debug) |
1004 | command = input(":" + current_stack + ":> ") |
1005 | except EOFError: |
1006 | pass |
1007 | except KeyboardInterrupt: |
1008 | pass |
1009 | " |
1010 | ~code~ |
1011 | |
1012 | ~name~ |
1013 | "Import Python core libraries" |
1014 | ~name~ |
1015 | ~code~ |
1016 | "import sys, time, random, copy" |
1017 | ~code~ |
1018 | |
1019 | ~name~ |
1020 | "Set up PyGame" |
1021 | ~name~ |
1022 | ~code~ |
1023 | " |
1024 | try: |
1025 | import warnings |
1026 | warnings.filterwarnings("ignore") |
1027 | from os import environ |
1028 | environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1' |
1029 | import pygame |
1030 | import pygame.midi |
1031 | pygame_supported = True |
1032 | except: |
1033 | pygame_supported = False |
1034 | pass |
1035 | " |
1036 | ~code~ |
1037 | |
1038 | ~name~ |
1039 | "Set up NFC" |
1040 | ~name~ |
1041 | ~code~ |
1042 | " |
1043 | try: |
1044 | import nfc, ndef, zlib, base64 |
1045 | from nfc.clf import RemoteTarget |
1046 | nfc_supported = True |
1047 | except: |
1048 | nfc_supported = False |
1049 | pass |
1050 | " |
1051 | ~code~ |
1052 | |
1053 | |
1054 | ~name~ |
1055 | "Finally, the main program" |
1056 | ~name~ |
1057 | ~code~ |
1058 | " |
1059 | def main(name, arguments): |
1060 | primitives = _time() + [math] + arrays() + stdio() |
1061 | if pygame_supported: |
1062 | primitives += graphics() + midi() |
1063 | if nfc_supported: |
1064 | primitives += _nfc() |
1065 | rulesets = [] |
1066 | debug = 0 |
1067 | prompt = False |
1068 | trace = False |
1069 | if not arguments: |
1070 | usage(name) |
1071 | return |
1072 | for argument in arguments: |
1073 | if argument == '-d': |
1074 | debug += 1 |
1075 | elif argument == '-r': |
1076 | prompt = True |
1077 | elif argument == '-t': |
1078 | trace = True |
1079 | else: |
1080 | with open(argument) as file: |
1081 | rulesets += list(parse(file.read())) |
1082 | if prompt: |
1083 | repl(rulesets, primitives, debug) |
1084 | else: |
1085 | if len(rulesets) == 0: |
1086 | usage(name) |
1087 | return |
1088 | if debug: |
1089 | knowledge, snapshots = run(rulesets, primitives, None, debug, trace) |
1090 | if trace: |
1091 | for index in range(len(snapshots)): |
1092 | print("Snapshot", str(index) + ':') |
1093 | print("----------------") |
1094 | print_knowledge(snapshots[index], debug) |
1095 | print("") |
1096 | else: |
1097 | print_knowledge(knowledge, debug) |
1098 | else: |
1099 | run(rulesets, primitives, None, debug, trace) |
1100 | |
1101 | if __name__ == "__main__": |
1102 | main(sys.argv[0], sys.argv[1:]) |
1103 | " |
1104 | ~code~ |