-
Notifications
You must be signed in to change notification settings - Fork 1
/
selectcodegen.py
2150 lines (2006 loc) · 68.4 KB
/
selectcodegen.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# -*- coding: utf-8 -*-
#SELECT. Code Generator v0.6 by Quintopia
# All code-generating functions in this file are right-growing, meaning they make the following guarantees:
# * They do not read or write cells to the left of the current cell at call time.
# * The result that the algorithm computes will be contained in the rightmost cell clobbered by the algorithm.
# * The current cell upon completion will be the one containing the answer.
# * They do not clobber the arguments passed to them (or rather, if they do, they restore them before returning)
# At call time, you must be able to guarantee the current cell is the leftmost argument and that there are
# sufficiently many unclobbered cells to the right of the current cell.
# See comments inside individual generating functions for info on which cells need to be untouched, which are clobbered,
# and what values they contain upon algorithm completion.
# To use a left-growing algorithm instead, use startleftgr() and endleftgr().
# E.g.: startleftgr();add();endleftgr() writes out a left-growing addition algorithm with the sum leftmost
#TODO: update docs for: switch, drawStringLiteral, drawLetterXY, drawdigitXY, repeat, endRepeat, opposite, reciprocal, intdiv, mod
from re import sub,DOTALL,finditer
from itertools import dropwhile,islice
import sys
version = 0.6
#the list of strings that becomes the program
l=[]
#the height of the display
h=0
#the width of the display
w=0
#the position (in the complex plane) of the top row on the display
toploc=0
#the position (in the complex plane) of the left column on the display
leftloc=0
#the number of spaces to put at the beginning of a line
indentlevel=0
#a table of named tape locations
vardict = {}
#a list of variables that MUST be saved by a loop (e.g. internal variables created by a loop)
mustsave = []
#the "absolute" location of the tape head (maybe)
offset = 0
#set to True to prevent any code output. set by turnon() and turnoff()
dike = False
#set to True to allow LEFT. and RIGHT. to be output. only becomes true once a SELECT., CONJ., or GET. are output, as these are the only commands which can change the tape. prevents extraneous tapehead
#motion at the beginning of a program.
domove = False
#painstakingly drawn pixelmaps of printable ASCII characters. based on 7pt Deja Vu Sans Mono (with the anti-aliasing removed)
letterforms = {' ':[],
'!':[(0,-4),(0,-3),(0,-2),(0,-1),(0,0),(0,2)],
'"':[(1,-4),(1,-3),(-1,-4),(-1,-3)],
'#':[(0,-4),(2,-4),(-1,-3),(1,-3),(-2,-2),(-1,-2),(0,-2),(1,-2),(2,-2),(3,-2),(-1,-1),(1,-1),(2,0),(1,0),(0,0),(-1,0),(-2,0),(-3,0),(1,1),(-1,1),(0,2),(-2,2),(0,3),(-2,3)],
'$':[(0,-4),(0,-3),(1,-3),(2,-3),(2,-3),(-1,-3),(-2,-2),(-2,-1),(-1,-1),(0,-2),(0,-1),(0,0),(1,0),(2,0),(2,1),(1,2),(0,1),(0,2),(0,3),(-1,2),(-2,2)],
'%':[(-2,-4),(-3,-3),(-2,-2),(-1,-3),(-2,0),(-1,0),(0,-1),(1,-1),(1,1),(0,2),(1,3),(2,2),(2,-2),(-3,1),],
"'":[(-1,-4),(-1,-3)],
'(':[(0,-5),(0,3),(-1,2),(-1,1),(-1,0),(-1,-1),(-1,-2),(-1,-2),(-1,-3),(-1,-4),],
')':[(-1,-5),(-1,3),(0,2),(0,2),(0,1),(0,0),(0,-1),(0,-2),(0,-2),(0,-3),(0,-4),],
'*':[(-3,-4),(-2,-3),(-2,-2),(-3,-1),(-1,-4),(-1,-3),(-1,-2),(-1,-1),(0,-3),(0,-2),(1,-4),(1,-1),],
'+':[(-1,3),(-1,2),(-3,1),(-2,1),(-1,1),(0,1),(1,1),(-1,0),(-1,-1),],
',':[(-1,5),(-1,4),],
'-':[(-1,1),(0,1),],
'.':[(-1,2)],
'/':[(0,-4),(-1,-3),(-1,-2),(-2,-1),(-2,0),(-2,1),(-3,2),],
'0':[(-1,-4),(0,-4),(-2,-3),(1,-3),(-2,-2),(1,-2),(-2,-1),(0,-1),(1,-1),(-2,0),(1,0),(-2,1),(1,1),(-1,2),(0,2),],
'1':[(-1,-4),(0,-4),(0,-3),(0,-2),(0,-1),(0,0),(0,1),(0,2),(-1,2),(1,2)],
'2':[(-2,-3),(-1,-4),(0,-4),(1,-3),(1,-2),(0,-1),(-1,0),(-2,1),(-2,2),(-1,2),(0,2),(1,2),],
'3':[(-2,-3),(-1,-4),(0,-4),(1,-3),(1,-2),(0,-1),(-1,-1),(1,0),(1,1),(0,2),(-1,2),(-2,1),],
'4':[(0,-4),(0,-3),(0,-2),(0,-1),(0,0),(0,1),(0,2),(1,1),(-1,1),(-2,1),(-2,0),(-1,-1),(-1,-2),],
'5':[(-2,-4),(-1,-4),(0,-4),(1,-4),(-2,-3),(-2,-2),(-2,-1),(-1,-1),(0,-1),(1,0),(1,1),(0,2),(-1,2),(-2,2),],
'6':[(0,-4),(1,-4),(-1,-3),(-2,-2),(-2,-1),(-2,0),(-2,1),(-1,2),(0,2),(1,1),(1,0),(0,-1),(-1,-1),],
'7':[(-2,-4),(-2,-4),(-1,-4),(0,-4),(1,-4),(1,-3),(0,-2),(0,-1),(0,0),(-1,1),(-1,2),],
'8':[(-1,-4),(0,-4),(-2,-3),(1,-3),(-2,-2),(1,-2),(-1,-1),(0,-1),(-2,0),(1,0),(-2,1),(1,1),(-1,2),(0,2),],
'9':[(-1,-4),(0,-4),(-2,-3),(1,-3),(-2,-2),(1,-2),(-1,-1),(0,-1),(1,-1),(1,0),(1,1),(0,2),(-1,2),(-2,2),],
':':[(-1,-2),(-1,2)],
';':[(-1,-2),(-1,2),(-1,3),(-1,4)],
'<':[(-2,-1),(-2,0),(-1,-1),(-1,0),(0,-1),(0,0),(1,-2),(1,1),(2,-2),(2,1),],
'=':[(-3,-1),(-2,-1),(-1,-1),(0,-1),(1,-1),(-3,1),(-2,1),(-1,1),(0,1),(1,1),],
'>':[(-2,-2),(-2,1),(-1,-2),(-1,1),(0,-1),(0,0),(1,-1),(1,0),(2,-1),(2,0),],
'?':[(-2,-4),(-1,-4),(0,-4),(1,-3),(0,-2),(-1,-1),(-1,0),(-1,0),(-1,2),],
'@':[(-1,-3),(0,-3),(-2,-2),(1,-2),(-2,-1),(1,-1),(0,-1),(-2,0),(0,0),(1,0),(0,1),(1,1),(-2,1),(-2,2),(-1,2),(-1,3),(0,3),],
'A':[(-1,-4),(0,-4),(-1,-3),(0,-3),(-2,-2),(1,-2),(-2,-1),(1,-1),(-2,0),(1,0),(-2,1),(-1,1),(0,1),(1,1),(1,2),(-2,2),],
'B':[(-2,-4),(-1,-4),(0,-4),(1,-3),(-2,-3),(-2,-2),(1,-2),(-2,-1),(-1,-1),(0,-1),(1,0),(1,1),(-2,0),(-2,1),(-2,2),(-1,2),(0,2),],
'C':[(-1,-4),(0,-4),(0,-4),(1,-4),(-2,-3),(-2,-2),(-2,-1),(-2,0),(-2,1),(-1,2),(0,2),(1,2),],
'D':[(-2,-4),(-1,-4),(0,-4),(1,-3),(1,-2),(1,-1),(1,0),(1,1),(0,2),(-1,2),(-2,2),(-2,1),(-2,0),(-2,-1),(-2,-2),(-2,-3),],
'E':[(-2,-4),(-1,-4),(0,-4),(1,-4),(-2,-3),(-2,-2),(-2,-1),(-1,-1),(0,-1),(1,-1),(-2,0),(-2,1),(-2,2),(-1,2),(0,2),(1,2),],
'F':[(-2,-4),(-1,-4),(0,-4),(1,-4),(-2,-3),(-2,-2),(-2,-1),(-1,-1),(0,-1),(1,-1),(-2,0),(-2,1),(-2,2),],
'G':[(-1,-4),(0,-4),(1,-4),(-2,-3),(-2,-3),(-2,-2),(-2,-1),(-2,0),(-2,1),(-1,2),(0,2),(1,2),(1,1),(1,0),(1,-1),(0,-1)],
'H':[(-2,-4),(-2,-3),(-2,-2),(-2,-1),(-2,0),(-2,1),(-2,2),(-1,-1),(0,-1),(1,-1),(1,0),(1,1),(1,2),(1,-2),(1,-3),(1,-4),],
'I':[(-2,-4),(-1,-4),(0,-4),(-1,-3),(-1,-2),(-1,-1),(-1,0),(-1,1),(-2,2),(-1,2),(0,2),],
'J':[(-1,-4),(0,-4),(1,-4),(1,-3),(1,-2),(1,-1),(1,0),(1,1),(0,2),(-1,2),(-2,2),],
'K':[(-2,-4),(-2,-3),(-2,-2),(-2,-1),(-2,0),(-2,1),(-2,2),(1,-4),(0,-3),(-1,-2),(-1,-1),(0,0),(0,1),(1,2),],
'L':[(-2,-4),(-2,-3),(-2,-2),(-2,-1),(-2,0),(-2,1),(-2,2),(-1,2),(0,2),(1,2),],
'M':[(-2,-4),(-2,-3),(-2,-2),(-2,-1),(-2,0),(-2,1),(-2,2),(-1,-3),(0,-3),(1,-4),(1,-3),(1,-2),(1,-1),(1,0),(1,1),(1,2),(-1,0),(0,0),(0,-1),(-1,-1),(-1,-2),(0,-2),],
'N':[(-2,-4),(-2,-3),(-2,-2),(-2,-1),(-2,0),(-2,1),(-2,2),(-1,-3),(-1,-2),(-1,-1),(0,-1),(1,-4),(1,-3),(1,-2),(1,-1),(1,0),(1,1),(1,2),(0,0),(0,1),],
'O':[(-1,-4),(0,-4),(-2,-3),(-2,-2),(-2,-1),(-2,0),(-2,1),(-1,2),(0,2),(1,-3),(1,-2),(1,-1),(1,0),(1,1),],
'P':[(-2,-4),(-2,-3),(-2,-2),(-2,-1),(-2,0),(-2,1),(-2,2),(-1,-4),(-1,-4),(-1,-1),(0,-4),(0,-1),(1,-3),(1,-2),],
'Q':[(-1,-4),(0,-4),(-2,-3),(-2,-2),(-2,-1),(-2,0),(-2,1),(-1,2),(0,2),(1,-3),(1,-2),(1,-1),(1,0),(1,1),(0,3),(1,3),],
'R':[(-2,-4),(-2,-3),(-2,-2),(-2,-1),(-2,0),(-2,1),(-2,2),(-1,-4),(0,-4),(1,-3),(1,-2),(0,-1),(-1,-1),(0,0),(1,1),(1,2),],
'S':[(-1,-4),(0,-4),(1,-3),(-2,-3),(-2,-2),(-1,-1),(0,-1),(1,0),(1,1),(0,2),(-1,2),(-2,1),],
'T':[(-3,-4),(-2,-4),(-1,-4),(0,-4),(1,-4),(-1,-3),(-1,-2),(-1,-1),(-1,0),(-1,1),(-1,2),],
'U':[(-2,-4),(-2,-3),(-2,-2),(-2,-1),(-2,0),(-2,1),(-1,2),(0,2),(1,1),(1,0),(1,-1),(1,-2),(1,-3),(1,-4),],
'V':[(-2,-4),(-2,-3),(-2,-2),(-1,-1),(-1,-1),(-1,0),(-1,1),(-1,2),(0,2),(0,1),(0,0),(0,-1),(1,-2),(1,-3),(1,-4),],
'W':[(-3,-4),(-3,-3),(-3,-2),(-2,-2),(-2,-1),(-2,0),(-2,1),(-2,2),(-1,-2),(-1,-1),(0,-2),(0,-1),(0,0),(0,1),(0,2),(1,-4),(1,-3),(1,-2),],
'X':[(-2,-4),(-2,-3),(-2,1),(-2,2),(-1,-2),(-1,-1),(-1,0),(0,-2),(0,-1),(0,0),(1,1),(1,2),(1,-4),(1,-3),],
'Y':[(-3,-4),(-2,-3),(-2,-2),(-1,-2),(0,-2),(0,-3),(1,-4),(-1,-1),(-1,0),(-1,1),(-1,2),],
'Z':[(-2,-4),(-1,-4),(0,-4),(1,-4),(1,-3),(0,-2),(0,-1),(-1,0),(-2,1),(-2,2),(-1,2),(0,2),(1,2),(-1,-1),],
'[':[(-1,-5),(0,-5),(-1,-4),(-1,-3),(-1,-2),(-1,-1),(-1,0),(-1,1),(-1,2),(0,2),],
'\\':[(-3,-4),(-2,-3),(-2,-2),(-1,-1),(-1,0),(0,1),(0,2),],
']':[(-2,-5),(-1,-5),(-1,-4),(-1,-3),(-1,-2),(-1,-1),(-1,0),(-1,1),(-1,2),(-2,2),],
'^':[(-2,-3),(-1,-4),(0,-5),(1,-4),(1,-4),(2,-3),],
'_':[(-3,4),(-2,4),(-1,4),(0,4),(1,4),],
'`':[(-2,-5),(-1,-4),],
'a':[(-2,-2),(-1,-2),(0,-2),(1,-1),(1,0),(1,1),(1,2),(0,2),(-1,2),(-2,1),(-1,0),(0,0),],
'b':[(-2,-5),(-2,-4),(-2,-3),(-2,-2),(-2,-1),(-2,0),(-2,1),(-2,2),(-1,-2),(-1,2),(0,-2),(0,2),(1,-1),(1,0),(1,1),],
'c':[(-1,-2),(0,-2),(1,-2),(-2,-1),(-2,0),(-2,1),(-1,2),(0,2),(1,2),],
'd':[(1,-5),(1,-4),(1,-3),(1,-2),(1,-1),(1,0),(1,1),(1,2),(0,-2),(0,2),(-1,-2),(-1,2),(-2,-1),(-2,0),(-2,1),],
'e':[(-1,-2),(0,-2),(-2,-1),(1,-1),(-2,0),(-1,0),(0,0),(1,0),(-2,1),(-1,2),(0,2),(1,2),],
'f':[(0,-5),(1,-5),(-1,-4),(-1,-3),(-1,-2),(-2,-2),(0,-2),(1,-2),(-1,-1),(-1,0),(-1,1),(-1,2),],
'g':[(-1,-2),(0,-2),(1,-2),(-2,-1),(1,-1),(-2,0),(1,0),(-2,1),(1,1),(-1,2),(0,2),(1,2),(1,3),(0,4),(-1,4),],
'h':[(-2,-5),(-2,-4),(-2,-3),(-2,-2),(-2,-1),(-2,0),(-2,1),(-2,2),(-1,-2),(0,-2),(1,-1),(1,0),(1,1),(1,2),],
'i':[(-1,-5),(-1,-2),(-2,-2),(-1,-1),(-1,0),(-1,1),(-1,2),(-2,2),(-3,2),(0,2),(1,2),],
'j':[(0,-5),(-1,-2),(-1,-2),(0,-2),(0,-1),(0,0),(0,1),(0,2),(0,3),(-1,4),(-1,4),(-2,4),],
'k':[(-2,-5),(-2,-4),(-2,-3),(-2,-2),(-2,-1),(-2,0),(-2,1),(-2,2),(-1,0),(0,-1),(1,-2),(0,1),(1,2),],
'l':[(-3,-5),(-2,-5),(-1,-5),(-1,-4),(-1,-3),(-1,-2),(-1,-1),(-1,0),(-1,1),(0,2),(1,2),],
'm':[(-2,-2),(-1,-2),(0,-2),(1,-2),(-2,-1),(-2,-1),(-2,0),(-2,1),(-2,2),(0,-1),(0,0),(0,1),(0,2),(2,-1),(2,0),(2,1),(2,2),],
'n':[(-2,-2),(-2,-1),(-2,0),(-2,1),(-2,2),(-1,-2),(0,-2),(1,-1),(1,0),(1,1),(1,2),],
'o':[(-1,-2),(0,-2),(-2,-1),(-2,0),(-2,1),(-1,2),(0,2),(1,-1),(1,0),(1,1),],
'p':[(-2,-2),(-2,-1),(-2,0),(-2,1),(-2,2),(-2,3),(-2,4),(-1,-2),(0,-2),(1,-1),(1,0),(1,1),(0,2),(-1,2),],
'q':[(-1,-2),(0,-2),(1,-2),(-2,-1),(-2,0),(-2,1),(-1,2),(0,2),(1,-1),(1,0),(1,1),(1,2),(1,3),(1,4),],
'r':[(-1,-2),(0,-2),(1,-2),(-1,-1),(-1,0),(-1,1),(-1,2),],
's':[(-1,-2),(0,-2),(1,-2),(-2,-1),(-1,0),(0,0),(1,1),(0,2),(-1,2),(-2,2),],
't':[(-1,-3),(-2,-2),(-1,-2),(0,-2),(1,-2),(-1,-1),(-1,0),(-1,1),(0,2),(1,2),],
'u':[(-2,-2),(-2,-1),(-2,0),(-2,1),(1,-2),(1,-1),(1,0),(1,1),(-1,2),(0,2),(1,2),],
'v':[(-2,-2),(-2,-1),(-2,0),(-1,1),(-1,2),(0,2),(0,1),(1,0),(1,-1),(1,-2),],
'w':[(-3,-2),(-3,-1),(-3,0),(-2,1),(-2,2),(-1,0),(-1,-1),(0,1),(0,2),(1,0),(1,-1),(1,-2),],
'x':[(-2,-2),(-2,2),(-1,-1),(-1,0),(-1,1),(1,-2),(1,2),(0,-1),(0,0),(0,1),],
'y':[(-2,-2),(-2,-1),(1,-2),(1,-1),(-1,0),(0,0),(0,1),(-1,1),(-1,2),(-1,3),(-2,4),],
'z':[(-2,-2),(-1,-2),(0,-2),(1,-2),(0,-1),(-1,0),(-1,1),(-2,2),(-1,2),(0,2),(1,2),],
'{':[(-1,-5),(0,-5),(-1,-4),(-1,-3),(-1,-2),(-1,-2),(-2,-1),(-1,0),(-1,1),(-1,2),(0,2),],
'|':[(-1,-5),(-1,-4),(-1,-3),(-1,-2),(-1,-1),(-1,0),(-1,1),(-1,2),(-1,3),],
'}':[(-2,-5),(-1,-5),(-1,-4),(-1,-3),(-1,-2),(0,-1),(-1,0),(-1,1),(-1,2),(-2,2),],
'~':[(-2,-1),(-1,-2),(0,-2),(1,-1),(2,-2),],
}
#############UTILITY FUNCTIONS#####################
def _rindex(lst, item):
try:
return dropwhile(lambda x: lst[x] != item, reversed(xrange(len(lst)))).next()
except StopIteration:
raise ValueError, "_rindex(lst, item): item not in list"
#optimize the program string and write it to the named file
def writetofile(filename):
global l,code
s=''.join(l)
s = sub('\n\s*?\n\s*?\n','\n\n',s)
filename = sub('.sel','',filename)
oldstring=s
while True:
oldstring=s
s=sub('(?s)LEFT\.(?P<comment>(?:(?=(?P<tmp>\s*\*%.*?%\*\s*))(?P=tmp))*)\s*RIGHT\. ','\g<comment>',s)
s=sub('(?s)RIGHT\.(?P<comment>(?:(?=(?P<tmp>\s*\*%.*?%\*\s*))(?P=tmp))*)\s*LEFT\. ','\g<comment>',s)
s=sub('(?s)CONJ\.(?P<comment>(?:(?=(?P<tmp>\s*\*%.*?%\*\s*))(?P=tmp))*)\s*CONJ\.','\g<comment>',s)
if oldstring==s:
break
#find last occurrence of (END. |PRINT. )
match=None
for match in finditer(r"(END. |PRINT. )", s):
pass
#throw out all but HALT. (because it might be there for emphasis) and LOOP. (whose presence may indicate a bug that should be brought to the user's attention)
#all the rest of these commands are orphaned and will have no user-visible effect
if match is not None:
s=s[:match.start()+1]+sub('(COLOR\. |CONJ\. |EXP\. |LOG\. |SELECT\. |GET\. |LEFT\. |RIGHT\. )','',s[match.start()+1:])
s = sub('\n\s*?\n\s*?\n','\n \n',s)
s = sub('\*%(?P<comment>.*)%\*','\g<comment>',s)
s = sub('\n\n','\n \n',s) #so we can post these to the wiki easily
s+='\n######GENERATION CODE######'+code
try:
openfile = file(filename+'.sel',"w")
except IOError:
print("File not found.")
sys.exit()
theFile = openfile.write(s)
openfile.close()
l=[]
def startleftgr():
if dike:
return
l.append('STARTLEFTGROWING')
def endleftgr():
global l
doreplace = False
for i,s in enumerate(l):
if s=='STARTLEFTGROWING':
doreplace=True
del l[i]
continue
if doreplace:
s=s.replace('RIGHT. ','repl ')
s=s.replace('LEFT. ','RIGHT. ')
l[i]=s.replace('repl ','LEFT. ')
def turnoff():
global dike
dike=True
def turnon():
global dike
dike=False
#######################FILE FORMATTING AND DECORATION#########################
def comment(note):
global l,indentlevel,dike
if dike:
return
note=note.replace('\n','\\n')
l.append('\n*%'+(' '*indentlevel)+note+'%*\n'+(' '*indentlevel))
def init(x,y,z,desc=None):
global l,h,w,toploc,leftloc
h=y
w=x
toploc=-(h-1)/2
leftloc=-(w-1)/2
l.append("("+str(x)+","+str(y)+","+str(z)+")\n")
if desc is not None:
l.append(desc+'\n')
l.append("Generated by Quintopia's Select. Code Generator version "+str(version)+'\n')
def upindent():
global l,indentlevel
indentlevel+=3
l.append('\n'+(' '*indentlevel))
def downindent():
global l,indentlevel
indentlevel-=3
l.append('\n'+(' '*indentlevel))
#################################IO CODE GENERATORS#############################
#TODO: drawing letterforms
#TODO: R,G,B to complex number conversion
def output():
global l,dike
if dike:
return
l.append('PRINT. ')
def drawpixel(x,y):
global dike,h,w
if dike:
return
upindent()
comment("DRAW PIXEL AT ("+str(x)+","+str(y)+")")
makenum(x-(w-1)/2,y-(h-1)/2)
output()
comment("END DRAW PIXEL")
downindent()
def input():
global l,dike,domove
if dike:
return
domove=True
l.append('GET. ')
def outputleft(n):
global dike
if dike:
return
output()
for i in range(n-1):
left(1)
output()
right(n-1)
def outputright(n):
global dike
if dike:
return
output()
for i in range(n-1):
right(1)
output()
left(n-1)
def clear():
global l,dike,domove
if dike:
return
l.append('CLEAR. ')
def color():
global l,dike,domove
if dike:
return
l.append('COLOR. ')
def digitprintXY(x,y):
if dike:
return
#input: (x) k*86
#output: x 1/2 x* |x| x {...} 1|k (k)
def digits(n):
if n<10 and n>=0:
drawletterXY(chr(n+48),x,y)
switch("digits",10,digits)
def drawstringliteral(string,x,y,top=0,bottom=None,left=0,right=None):
global dike,h,w
if dike:
return
#input: (k) k*infinity (actually probably less than 25*len(string))
#output: {...} (k)
upindent()
comment('DRAW STRING LITERAL "'+string+'"')
if bottom is None:
bottom=h
if right is None:
right=w
string = string+' '
string = string.replace('\n',' \n') #insert a space before every newline to simplify algorithm
string = string.replace('\t',' \t') #same for tabs
for i,c in enumerate(string):
if c=='\t':
x=x+15 #counting the space before the tab, a tab is four spaces
continue
remainder = min(string.find(' ',i+1)-i,string.find('-',i+1)-i+1)
if remainder<0:
if string.find(' ',i+1)-i>0:
remainder=string.find(' ',i+1)-i
elif string.find('-',i+1)-i>0:
remainder=string.find('-',i+1)-i+1
else:
remainder=len(string)-i
nextword = min(string.find(' ',i+remainder+1)-i-remainder-1,string.find('-',i+remainder+1)-i-remainder)
if nextword<0:
if string.find('-',i+remainder+1)-i-remainder-1>0:
nextword=string.find('-',i+remainder+1)-i-remainder
elif string.find(' ',i+remainder+1)-i-remainder-1>0:
nextword=string.find(' ',i+remainder+1)-i-remainder-1
else:
nextword=0
if x>right-5 or c=='\n' or (x+5*remainder>right-5 and remainder*5<right-left and nextword*5<right-left): #reset to left side if we're off the right side (from tabbing) or at newline or will not see whitespace before we hit the right side unless the next word is longer than a line
if i+1<len(string) and string[i+1]=='\n' and c!='\n':
continue
y=y+12
x=left
if y>bottom-12: #if we would go off the bottom of the screen, block for input instead, then clear once we have it
go(-1)
input()
go(1)
clear()
y=top
if c=='\n' or c==' ': #go on to next character if it was a newline or space, otherwise try to print current char
continue
if c!=' ':
drawletterXY(c,x,y)
x=x+5
comment('END DRAW STRING LITERAL "'+string+"'")
downindent()
def drawletterXY(c,x,y,simulate=False):
global letterforms,dike,top,left
#input: (k) {k k? k? k? k?}*(at most 25)
#output: differentnumbers*(at most 125) (k)
if dike:
return
upindent()
comment('DRAW LETTER "'+c+'" AT ('+str(x)+','+str(y)+')')
form = letterforms[c]
if form is None:
return
for n in form:
makenum(x+n[0]+3+leftloc,y+n[1]+6+toploc)
if not simulate:
output()
go(1)
comment('END DRAW LETTER "'+c+'"')
downindent()
def drawletter(c):
global letterforms,dike
if dike:
return
#input: (x+yi) {k k k k k k? k? k? k?}*(at most 25)
#output: x+yi {? ?? ?? ?? ?? x+yi k^(?) k^(x+yi) (x+?)+(y+?)i} (k)
upindent()
comment('DRAW LETTER "'+c+'"')
var('drawLetterPosition')
form = letterforms[c]
if form is None:
return
for n in form:
go(1)
makenum(n[0]+3,n[1]+6)
go(1)
fetch('drawLetterPosition')
var('drawLetterPosition')
add(-1)
output()
go(1)
makenum(5)
go(1)
fetch('drawLetterPosition')
add(-1)
comment('END DRAW LETTER "'+c+'"')
downindent()
#######################COMMON ACTIONS IN SELECT############################
def left(n):
global l,offset,dike
if dike or not domove:
return
for i in range(n):
l.append('LEFT. ')
offset-=1
def right(n):
global l,offset,dike
if dike or not domove:
return
for i in range(n):
l.append('RIGHT. ')
offset+=1
def go(n):
global dike
if dike:
return
if n<0:
left(-n)
else:
right(n)
def exptarget(n):
global l,indentlevel,dike,domove
if dike:
return
l.append('EXP. ')
if n<0:
left(-n)
elif n>0:
right(n)
l.append('SELECT.\n'+(' '*indentlevel))
domove=True
def logtarget(n):
global l,indentlevel,dike,domove
if dike:
return
l.append('LOG. ')
if n<0:
left(-n)
elif n>0:
right(n)
l.append('SELECT.\n'+(' '*indentlevel))
domove=True
def copyfrom(n):
global dike
if dike:
return
exptarget(n)
go(-n)
logtarget(1)
left(1)
def var(name,savelist=None):
global dike
if dike:
return
global vardict,offset,s,indentlevel
vardict[name] = offset
if savelist is not None:
savelist.append(name)
#add the comment here manually so that it doesn't get the left/right stripped from around it, so that we actually are on the cell the comment claims to be marking.
#yes, it doesn't change the behavior of the code to strip them, but the code we output should be correctly documented
l.append('\n'+(' '*indentlevel)+'MARK AS '+name+'\n'+(' '*indentlevel))
def fetch(name):
global vardict,offset,dike
if dike:
return
#input: (k) k
#output: ($name) k
comment("FETCH "+name)
copyfrom(getoffset(name))
#vardict[name] = offset
comment(name+" FETCHED")
#return how far we have to go() to get to the named position, unless no position is named, in which case return absolute position
def getoffset(name=None):
global vardict,offset
if name is None:
return offset
if name not in vardict:
print("No location called '"+name+"' exists.")
sys.exit(1)
return vardict[name]-offset
def halt():
global s,dike
if dike:
return
l.append('HALT. \n!!!!PROGRAM EXIT POINT!!!!\n')
################################################LOOPING############################################
def loop(name,start=0,end=None,step=1,computei=False,savelist=[]):
global l,dike,mustsave
if dike:
return
#input: [(k) | (x) k] k k k k k k k k k k k k k k k k k k k {k k k k {k k} {k k k k {k k k}}} (20,24/26,28/30,31/33)k's
#output: 1/sqrt(2) -1 1/2 i -1 k^i k^-1 -1+i (-1+i)/sqrt(2) -1 1/n 1/2 i^(1/n) k k*i^(1/n) (sentinel) {k}...
#... 1/2 sentinel* sentinel ... computei=True and...
#... (i) k ...step=1 or
#... {loopcount step} ... ...step>1 and...
#... (i) k ...start=0
#... loopcount*step start k^(loopcount*step) k^start (i) k ...start>0
#... loopcount*step -1 -start start loopcount*step k^start k^loopcount*step (i) k ...start<0
#also creates the variables <name>sentinel, <name>step, used internally.
upindent()
if end is None:
comment("LOOP '"+name+"' FROM 0 TO VALUE ON TAPE BY INCREMENTS OF 1")
go(1)
else:
comment("LOOP '"+name+"' FROM "+str(start)+" TO "+str(end)+" BY INCREMENTS OF "+str(step))
makenum(2)
right(1)
makeneg1()
right(1)
makei()
#2 -1 1/2 (i)
add(-2)
#2 -1 1/2 i k^i k^-1 (-1+i) k
go(-6)
exptarget(2)
go(-2)
exptarget(1)
#1/sqrt(2) (-1) 1/2 i k^i k^-1 -1+i k
go(5)
multiply(-6)
#1/sqrt(2) -1 1/2 i k^i k^-1 -1+i ((-1+i)/sqrt(2)) k
var(name+'sentinel')
mustsave.append(name+'sentinel')
go(1)
makeneg1()
go(1)
if end is None:
copyfrom(-10)
else:
makenum((end-start)/step)
#...-1 (n) k
exptarget(-1)
go(2)
makei()
#...-1 1/n 1/2 (i) k
exptarget(-2)
#...-1 (1/n) 1/2 i^(1/n) k
go(2)
multiply()
var(name+'step')
mustsave.append(name+'step')
#...-1 1/n 1/2 i^(1/n) k (k*i^(1/n)) k
if len(mustsave)>2:
go(1)
for varname in mustsave:
if varname!=name+'sentinel' and varname!=name+'step':
fetch(varname)
var(varname)
go(1)
if savelist:
go(-1)
else:
go(1)
fetch(name+'step')
var(name+'step')
if savelist:
go(1)
for varname in savelist:
fetch(varname)
var(varname)
go(1)
go(1) #leave a gap for where the old sentinel will be fetched in endloop
fetch(name+'step')
var(name+'step')
#invalidate all variables which are not being saved (as looping will make their offsets wrong anyway)
for key in vardict.keys():
if key not in savelist and key not in mustsave:
del vardict[key]
go(1)
fetch(name+'sentinel')
var(name+'sentinel')
l.append('LOOP. ')
upindent()
if computei:
absval()
#...sentinel 1/2 sentinel* (|sentinel|) k
logtarget(1)
#...loopcount (k)
if step>1:
makenum(step)
#...loopcount (step) k
go(-1)
multiply()
else:
go(-1)
#...loopcount {step (loopcount*step)} k
#output()
if start>0:
go(1)
makenum(start)
add(-1)
#...loopcount {step loopcount*step} start k^(loopcount*step) k^start (i) k
elif start<0:
go(1)
makenum(start)
#...loopcount {step loopcount*step} -1 (start)
add(-2)
#...loopcount {step loopcount*step} -1 start k^(loopcount*step) k^start (i) k
#do not down indent. that is in endloop()
def endloop(name,computei=False,start=0,savelist=[]):
global l,dike,mustsave
if dike:
return
#input: (k) k k k {k k k k k {k k k k {k k k k}}} (4,9,13,17)k's
#output: oldsentinel step (newsentinel) {k}...
#...1/2 sentinel* loopcount step... computei=True and...
#...(i) k ...start=0
#...loopcount*step start k^(loopcount*step) k^start (i) k ...start>0
#...loopcount*step 2 -1 -start start loopcount*step k^start k^loopcount*step (i) k ...start<0
del mustsave[mustsave.index(name+"sentinel")]
del mustsave[mustsave.index(name+"step")]
go(1)
if mustsave:
for varname in mustsave:
fetch(varname)
var(varname)
go(1)
if savelist:
for varname in savelist:
fetch(varname)
var(varname)
go(1)
fetch(name+'sentinel')
go(1)
fetch(name+'step')
go(-1)
multiply()
#sentinel step (sentinel*step) k
downindent()
l.append('END. ')
if computei:
absval()
#...sentinel 1/2 sentinel* (|sentinel|) k
logtarget(1)
#...loopcount (k)
makenum(step)
#...loopcount (step) k
go(-1)
multiply()
#...loopcount step (loopcount*step) k
if start>0:
go(1)
makenum(start)
go(-1)
add()
#...loopcount step loopcount*step start k^(loopcount*step) k^start (i) k
elif start<0:
go(1)
makenum(-start)
go(1)
copyfrom(-5)
go(-1)
add()
#...loopcount step loopcount*step 2 -1 -start start loopcount*step k^start k^loopcount*step (i) k
#do not down indent. that is in endloop()
comment('END OF LOOP '+name)
downindent()
def repeat(name,savelist=[]):
global l,dike,mustsave
if dike:
return
upindent()
comment("WHILE")
var(name+"sentinel")
mustsave.append(name+"sentinel")
if len(mustsave)>1:
go(1)
for varname in mustsave:
if varname!=name+"sentinel":
fetch(varname)
var(varname)
go(1)
if savelist:
go(-1)
else:
fetch(name+"sentinel")
if savelist:
go(1)
for varname in savelist:
fetch(varname)
var(varname)
go(1)
fetch(name+"sentinel")
l.append("LOOP. ")
def endrepeat(name,savelist=[]):
global l,dike
if dike:
return
del mustsave[mustsave.index(name+"sentinel")]
var(name+"sentinel")
if mustsave:
go(1)
for varname in mustsave:
fetch(varname)
var(varname)
go(1)
if savelist:
go(-1)
else:
fetch(name+"sentinel")
if savelist:
go(1)
for varname in savelist:
fetch(varname)
var(varname)
go(1)
fetch(name+"sentinel")
l.append("END. ")
comment("END WHILE")
downindent()
##############################################CONDITIONALS##################################################
def ifnonpositive(name):
global l,dike,mustsave
if dike:
return
#x must be a real number!
#input: (x) ?
#output: (x) ?
upindent()
comment('IF ('+name+'):')
l.append('LOOP. ')
var('ifstart'+name)
mustsave.append('ifstart'+name)
upindent()
#no downindent
def ifzero(name):
global dike
#input (x) k k k k k
#output: x 1/2 x* |x| (x) k
if dike:
return
comment('IF ZERO ('+name+'):')
absval()
ifnonpositive(name)
go(1)
copyfrom(-4)
def els(name,copy=True):
global l,dike,offset
if dike:
return
#input: (x|?) k k k k
#output: x k 0 (x) k
if 'PADPOINT1'+name not in l:
padpoint(1,name)
go(1)
var('ifone'+name)
mustsave.append('ifone'+name)
go(1)
makezero()
go(-1)
downindent()
l.append('END. ')
#THE OFFSET HERE WOULD BE THE SAME AS THE OFFSET AT THE BEGINNING OF THE FIRST IF BLOCK (since only one of the two will be executed)
offset+=getoffset('ifstart'+name)
go(1)
lnot()
comment('ELSE ('+name+'):')
l.append('LOOP. ')
var('ifelse'+name)
mustsave.append('ifelse'+name)
upindent()
if copy:
go(1)
copyfrom(-3)
def endif(name):
global l,dike,vardict,offset
if dike:
return
#input: (x) k
#output: (x) k
#if an else branch wasn't needed for program logic but the tapehead moved, insert one anyway just to prevent tape usage differing depending on conditional result
if 'ifelse'+name not in vardict and getoffset('ifstart'+name)!=0:
els(name,True)
#no padding necessary if there is no else block and the tape head did not move
if 'ifelse'+name in vardict:
#calculate displacement of first branch
b1length = getoffset('ifone'+name)-getoffset('ifstart'+name)
if 'PADPOINT2'+name not in l:
padpoint(2,name)
#go(1)
#calculate displacement of second branch
b2length = -getoffset('ifelse'+name)
#output rights
if b2length>b1length:
#this branch is further right. insert rights at padpoint1
padlength=b2length-b1length
pp=_rindex(l,'PADPOINT1'+name)
l[pp]='({'+'RIGHT. '*padlength+'})'
pp=_rindex(l,'PADPOINT2'+name)
del l[pp]
elif b1length>b2length:
#insert rights at padpoint2
padlength=b1length-b2length
offset+=padlength
pp=_rindex(l,'PADPOINT2'+name)
l[pp]='({'+'RIGHT. '*padlength+'})'
pp=_rindex(l,'PADPOINT1'+name)
del l[pp]
else:
pp=_rindex(l,'PADPOINT2'+name)
del l[pp]
pp=_rindex(l,'PADPOINT1'+name)
del l[pp]
downindent()
l.append('END. ')
del mustsave[mustsave.index('ifstart'+name)]
del mustsave[mustsave.index('ifone'+name)]
del mustsave[mustsave.index('ifelse'+name)]
comment('END IF ('+name+')')
downindent()
def padpoint(n,name):
global l,dike
if dike:
return
l.append('PADPOINT'+str(n)+name)
#combined if/else/end overhead: firstbranch: x {...} k 1 (k)
# secondbranch: x 0 x {...} (k)
#for ifzero: firstbranch: x 1/2 x* |x| {...} k 1 (k)
# secondbranch: x 1/2 x* |x| 0 x {...} (k)
#because of automatic padding, the overhead is the same no matter which branch is chosen ...this is what makes looping over conditionals possible!
#evaluate f(x) by generating code for f(k) for k from 1 to n and running the code for f(k) only if x=k (x must be a positive integer!!!!)
#so really it's just an unrolled loop, with a conditional for each iteration, and in between it decrements k, entering a conditional when k hits zero (which will correspond to the original value of k)
#or a giant if/else chain, where the conditions are sequential guesses at the value of x
#n is the number of cases to try (the integers 1..n), and f is a function that takes an integer argument (or None) and generates code (None is the "default" case--for when all cases fail)
#fetchx specifies whether the code generated by f needs x under the tapehead when it executes, and so if True, will copy it there
def switch(name,n,f,fetchx=False):
global l,dike,offset
offsets = []
if dike:
return
#input: (x) k*(10+f's max tape usage)
#let fin=i^(2^((n-1)/x-2))
#output: zero: x 1/2 x* |x| x {...} k 1 (k)
# regular: x 1/2 x* |x| x 1/x -1 2^(1/x) i^(1/4) {...} 1 (k)
# default: x 1/2 x* |x| x 1/x -1 2^(1/x) fin^2 {...} k (k)
upindent()
comment('SWITCH ('+name+') WITH '+str(n)+' CASES')
#we'll start with the test case for zero. it won't change much except the tape use profile. it needs to be padded too. but wait...it gets auto-padded already.
#create the test sentinel and step just like the one for loop()
ifzero("switchbigcond"+name)
comment("CASE ("+name+") #0:")
if not fetchx:
go(1)
f(0)
comment("END CASE ("+name+") #0")
els("switchbigcond"+name)
var(name+'X')
go(1)
copyfrom(-1)
go(1)
makeneg1()
go(-1)
exptarget(1)
#x 1/x (-1) k
go(1)
makei()
#x 1/x -1 1/2 (i) k
exptarget(-1)
go(1)
#x 1/x -1 1/2 (i^(1/2)) k
exptarget(-1)
exptarget(-1)
#x 1/x (-1) 2 i^(1/4) k
go(1)
exptarget(-2)
#x (1/x) -1 2^(1/x) i^(1/4) k
go(3)
exptarget(-1)
go(1)
startpoint=getoffset()
for k in range(1,n):
l.append('LOOP. ')
upindent()
comment('CASE ('+name+') #'+str(k)+':')
go(1)
if fetchx:
fetch(name+'X')
f(k)
if 'PADPOINT'+str(k-1)+name not in l:
padpoint(k-1,name)
go(1)
#make sure that none of the cases after this one will run.
makeone()
offsets.append(getoffset()-startpoint)
comment('END CASE ('+name+') #'+str(k))
downindent()
l.append('END. ')
offset=startpoint
exptarget(-1)
go(1)
#if something has run: x 1/x -1 2^(1/x) i^(1/4) {...} (1) k
#if something has not run: x 1/x -1 2^(1/x) (i^(2^((n-1)/x-2))=fin) k
#now the default case. If one of the above cases has run, then sentinel=1 (the current tape cell), so we run this one for all other values.
square() #squaring will move any valid sentinel into the "true" range but fix 1
l.append('LOOP. ')
upindent()
comment('DEFAULT CASE:')
go(1)
if fetchx:
fetch('switch'+name+'X')
f(n)
if 'PADPOINT'+str(n-1)+name not in l:
padpoint(n-1,name)
offsets.append(getoffset()-startpoint) #?
go(1)
comment('END DEFAULT CASE')
downindent()
l.append('END. ')
#compute the max tape usage in any branch (we'll pad up to it in other branches)
maxpad=max(offsets)
#iterate through and pad too-small branches
for k in range(len(offsets)):
padlength=maxpad-offsets[k]
pp=_rindex(l,'PADPOINT'+str(k)+name)
if padlength>0:
l[pp]='({'+'RIGHT. '*padlength+'})'
else:
del l[pp]
offset=startpoint+maxpad+1
#if something has run: x 1/x -1 2^(1/x) i^(1/4) {...} (1) k
#if something has not run: x 1/x -1 2^(1/x) fin^2 {...} (k) k
endif("switchbigcond"+name)
comment('END SWITCH ('+name+')')
downindent()
############################LOGIC###########################
def lnot(target=0):
global dike
#turns positive real numbers into zero, and zero into 1.
#only works for positive real numbers!!! call abs() first!!!