Ultima attività 1748061902

Revisione 1aa985ad7a573ac88ed6735c1911aaea9ffb66a8

novaweb-compiler.nv Raw Playground
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 Raw Playground
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"
45def 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"
77def 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"
132def 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"
189def 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"
199def facts(ruleset):
200 for left, right in ruleset:
201 if not left:
202 for element in right:
203 yield element
204
205def rules(ruleset):
206 for left, right in ruleset:
207 if left:
208 yield (left, right)
209
210def 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
240def 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
249def 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
267def reset(pattern, knowledge):
268 label, _ = pattern
269 if label in knowledge:
270 knowledge[label][1] = 0
271
272def 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
282def 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
303def 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
335def 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
350def 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"
362def 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"
400def 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"
460def 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"
696def 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"
765def _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"
790def 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"
876def _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"
964def 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"
1024try:
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
1032except:
1033 pygame_supported = False
1034 pass
1035"
1036~code~
1037
1038~name~
1039"Set up NFC"
1040~name~
1041~code~
1042"
1043try:
1044 import nfc, ndef, zlib, base64
1045 from nfc.clf import RemoteTarget
1046 nfc_supported = True
1047except:
1048 nfc_supported = False
1049 pass
1050"
1051~code~
1052
1053
1054~name~
1055"Finally, the main program"
1056~name~
1057~code~
1058"
1059def 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
1101if __name__ == "__main__":
1102 main(sys.argv[0], sys.argv[1:])
1103"
1104~code~