-
Notifications
You must be signed in to change notification settings - Fork 2
/
purefalse.sml
1600 lines (982 loc) · 50.4 KB
/
purefalse.sml
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
(* the Pure False stack machine *)
fun say s = TextIO.output(TextIO.stdOut,"\n\n"^s^"\n\n");
fun versiondate() = say "August 30, 2005";
(* Differences from standard False
@ used to be different but is now restored
? used to be different but is now restored
pick is not there (but can be written)
) is flush
no inline assembly (` means something novel)
the language is (dynamically) typed, so the peek/poke trick does not work.
only functions can be stored in character variables (but given
the additional tools for handling lists/functions, this is more
an apparent than a real problem).
multi-digit integers cannot be read in the usual format:
_ stands for (x,y...)->(10y+x...) and is used to write
multi-digit decimals in a single-character opcode format.
There is considerable additional functionality:
one can carry out standard list operations on "functions" (in two flavors,
since some components of lists have execution behavior and one does not
always wish to encourage this!) Since all contents of character-labelled
variables are functions, ; includes the effect of application.
there is file I/O
*)
(* August 30:
I wrote the notext function which should convert a function to
a unique form almost without program text. The only place where
I actually use it is in the = for functions. I banished Opcode "0"
and its kindred.
fixed another bug in the first function. Something needs to
be done about the dual representation of functions as
lists and text.
Restored original False meaning of @ (just for compatibility,
since this is also restored for ?). The old meaning is new \@
The preservation of order of arguments 1 and 2 is useful.
The control structures now differ slightly in behavior and
fundamentally in implementation (the profound change is in D
and P, which now really support continuations). The bracket
around the test is no longer wanted for ? (returning to the way
False itself behaves). There are no longer subsidiary calculations
going on: the "program stack" P honestly contains complete information
about what is to be run. # behaves in the same way as before (except
under trace, where it looks rather different) but the pending
executions are actually recorded in the P parameter. I also had
to fix i for the same reasons, and discovered a bug in incremental
execution of program text.
I seriously should consider converting program text to list
structures? The mixture of parsing and stack operations is a little
weird, though it seems to be transparent except to =.
removed the prompt for character input from the program: this is
easily supplied by the user (and can now be controlled by the user).
env2 is another attempt at an environment, in some ways more satisfactory.
input reacts badly with the current implementation of trace.
*)
(*
August 29 status report
Reason for doing this: Strictly False will be a good language for
the Palm when I implement it there (easy to implement and easy to use
in the sense of requiring few keystrokes).
The purity of the single character approach is enhanced by the new
integer display :-)
File commands appear to work.
Defects:
Clean up problems caused by the use of text in Program components in
functions. These are very handy but can cause trouble: either make
the simulation perfect or eliminate. If a function with program text
is tested for equality the program text should perhaps be eliminated?
Features to add:
Casting between characters and integers is a legitimate feature. A
function which casts from characters to opcodes is also a good idea.DONE
Retrieving the stack as a data item, and checking whether it is empty
are legitimate functions. How about replacing the stack?
How about two-way access to the program being executed?DONE
I implemented a test for empty stack, grabbing the data stack and
putting it on the stack, replacing the data stack with its top
element, and grabbing the program stack and putting it on the stack,
and discarding the program stack and executing the function on top of
the data stack (this enables continuation handling). The data stack
always means "the data stack after any opcodes or program text
on the data stack is executed" and any D command executed from the
data stack will actually be executed from a correct program stack.
D can only prevent pending conditional execution by arranging to
return a false.
File I/O; open a file and bind it to a character. Get a character,
put a character, execute a file and put a function in a file (this
last command opens the file, saves the data in it, and closes
it). Close a file and flush a file (ML only allows for flushing
outstreams). Open file is DONE; read and write characters are DONE;
output buffer is always flushed when doing input or closing file, so
no need for flush command. The file read command R always leads
its output with a boolean telling whether there is a character read
or not. Errors as to existence of files stop the program.
I implemented loading and saving of functions to files. This also meant
that I need to enforce the condition that the execution of the display
of a stack will be the same as the execution of the stack: to do
this I introduce opcodes for true and false and a slightly demented
but readable way of displaying multi-digit integers. This fixes
a bug, actually: functions containing multi-digit numbers or
truth values would not have executed correctly in previous versions?
Ability to define new opcodes: take a defined function and
"permanently" bind it to a character (from the standpoint of any fixed
program). This command can only be issued in the programming
environment and the defined character remains dynamically allocable.
Ability to save data of all types to numerical addresses. This
restores a cheat capability of False. This will be static storage
(though functions/lists can be stored). Allocation of this memory
might be an issue in a non-ML version.
There are real advantages to my formulation of ?; it will stay the
same. My @ is better (I reversed myself on both of these).
i really is trace: my notes below about this were wrong. Think of
the function as living in P, not on the stack.
I could install an integer input mode, I suppose, and make it so that
integers in multi-digit format can be read by the system.NOT NEEDED:
there is now a standard way to write integers using single digit
opcodes.
Clean up the format of the user environment and install the stupid line
editor.
Write real documentation and set up a web page. Write to Wouter about it.
The main philosophical issues are the typing and the ability to
manipulate saved stacks (including executable programs) as lists (and
the requisite need to assign (virtual) internal structure to
programs). The invisible OpCode type is needed to preserve the idea
that executing a function is simply dropping it onto the data stack.
This is an essentially more powerful language than False (and probably
still admits a small implementation: write in C and compile?) A
mathematical issue is demonstrating that this language does
combinators: do this by exhibiting the combinators and testing them.
*)
(* the language now works. It is somewhat different from the original
False and these differences should be set out. *)
(* in the help command, the language Pure False is now specified *)
(* mod debugging I believe this implements a dialect of False which
adds the ability to push items into "lambdas" (this makes it combinatorially
complete). This dialect is fairly strongly dynamically typed: no casting.
Only
function definitions are supported by : and ;, but every character can
be defined as a function. The " " and [ ] forms are not supported
in the underlying opcodes: these programs are constructed by a string
preprocessing step. Numerical input is restricted to 0-9
as in Befunge. *)
(* I need to install the ability to run programs from the console or a file.
The internal environment in which this is done should also allow control
of the trace features. I need to install comments in the language.
A help command with information about each opcode would be useful:
the help command is now available; it takes a string argument,
and single character strings can be used to get info about opcodes.
I need to fix the (deliberately permitted) crashes: ML crashes will
break the projected native Pure False programming environment, which is not
a good thing (though I can use error handling to prevent this).
Starting on this: the definition facility will no longer crash, and
the conditional tests escape gracefully when they do not detect truth values.
Division by zero is also corrected; I _think_ these are all the ML
errors possible...
There is still the need to guard against arithmetic overflow. This could
be done neatly by putting all arithmetic in a suitable modulus?DONE
Comments are installed.
August 25th updates were bug fixes to list functions; writing
a list reverse procedure was an eye-opener. Notice that treating
i as trace is not correct at all, since the operators would get
to act on later operators normally. There were difficulties with
handling of the underflow situation and a very subtle confusion
between Stack(nil) and Stack[Program nil]: probably best handled
by eliminating embedded text. But embedded text does neatly solve
the problem it was designed to handle, and for the moment seems to
be correctly simulated.
I now have the desktop environment mostly developed. Its look and
feel need to be improved. The ability to turn off trace during
a trace is useful. It would be nice if trusted functions could work
as single steps under trace. I would like to have a line editor for
Strictly False in the repertoire. The crashes are mostly repaired.
A main aim for this is to implement it in another language (iziBasic,
probably, but this might be via C as well). That's what the memory
model stuff is for.
*)
(*
It would be straightforward to allow static installation of
new opcodes (basically similar to ; in implementation. This
would of course be a programming environment command.
Explicit integer/character type casting should be supported,
since the implicit kind has been disabled. A "cast" opcode
which takes an appropriate integer to a character and a character
to an integer is reasonable.
I have now added the ability to execute a function step by step
in the language itself (i); it might be nice to add the ability
to display the top opcode of a function and to display the
stack as opcodes in the language; one could then debug programs
inside the interpreter! No, one can't: opcodes will execute, but
not on the right arguments!
A real trace might copy all arguments to the stack, carry out one
step, and encase the top item in a function. Such a command could
be provided.
Adding equality of functions violates type safety completely;
what about adding equality of opcodes? Or does thinking of it
in terms of equality of opcodes reveal that it is not a type safety
violation to have equality of functions? Maybe it is simply that
functions are a union type into which many types can be cast?
*)
(* Why is Pure False pure?
with the addition of the p and o commands, it is combinatorially
complete (the S and K combinators can be constructed; I should
demonstrate this). there is a very small set of commands which is a
complete pure calculus of stacks; that way lies madness, though.
it is completely reliant on single character opcodes. 'X and `X
are apparent exceptions, but really can be thought of as providing
extensions to the alphabet. The same can be thought of X: and X; --
these can be thought of as allowing definition of and access to
a further alphabet of defined opcodes. Even the "..."
and "[...]" forms can be rephrased in terms of single character
opcodes (I tried using this in the very first version, but
the string preprocessor I wrote didn't work correctly on
nested brackets; in any event one would have exponential
blowup with bracket nesting).
Note that variables are addressable by the language,
since their names are characters which can be produced
by program operations.
it is typed, unlike earlier versions. there is no casting
of objects from one type to another, and no messing with
assembly code; programming in Pure False must be pure
(though it is not completely safe :-)
I'm now adding i j and x commands which give more list
functionality. i pops off a function, executes its first command,
and puts its tail back. j pops off a function, puts the one-element
stack of the first element on, then puts the tail back. x checks
whether the function on top is empty. In effect this gives
hd, tl.
There is a subtle problem with i: in its simplest version, it
just grabs items of the form Program <text>. It shouldn't do this.
This is now fixed (the interpreter parses out the first
component).
After consideration, I added the function equality test;
it behaves differently because it does not consume its arguments
(they being potentially large). This makes x redundant, but it
is still convenient (and makes more sense philosophically).
I have made the arithmetic safe from overflow: for multiplication,
this is rather tricky :-). Comments have been implemented.
What would be needed for static type checking? Is it possible at all?
Perhaps the language should be called Strictly False :-)
*)
(*
Considerations for an implementation at an even lower level:
cells in the Strictly False virtual machine contain a switch telling
what kind of cell it is (boolean, integer, character, or program),
the address of the next cell and the address of the last cell in the
list (program) to which it belongs. The last cell address is useful
because it makes it easy to take a program at the top of the master
stack and put it at the top. How hard would it be to also have unique
cells? This would require that cells have additional data that organizes
them into a sorted tree. Last cell address idea doesn't work;
one still needs to scan through to update last cell addresses in
the list, so one gains nothing by recording it.
For execution purposes, there is a master address that points into the
heap: the address is changed on execution (and new cells may be created
or destroyed). Do we avoid circular structures? Yes. So reference
counting will work. Put in fields in addition: reference count,
cells to the left in the sorted tree, cells to the right in the sorted
tree.
The order on cells is lexicographic on the switch, the data and the
address of the next cell (the address of the last cell is of
course strictly speaking redundant).
Unused memory is organized into another linear list; more memory can
presumably be requested and organized as needed.
Unique reference is important because it makes the function equality
test simple.
In ML one can simulate this using a binary tree with cells numbered by
position: in a flat tree this is harmless (the cells in use will be
roughly a solidly filled initial segment of the naturals) This is
advantageous since I don't have arrays (or at least I don't know how
they work.) This makes it reasonably easy to get to an address.
This observation means that one doesn't actually need a tree structure:
flattening the tree means that we can just regard 2n and 2n+1 as the
nodes to the left and right of n. So in an array, ref count, nodes in
use to left and nodes in use to right are enough.
*)
(* the datatype of items allowed on the machine stack *)
datatype StackItem = OpCode of char |
Bool of bool |
Int of int |
Char of char |
Program of char list |
Stack of StackItem list;
(* prevent integer overflow -- there should be no ML crashes. *)
fun intfix0 n = n mod 200000000;
fun intfix n = let val A = n mod 200000000 in
if A<=100000000 then A else A-200000000 end;
(* more care is required for multiplication to avoid overflow *)
(* x*y = 2x*(y/2) = 2x*(y div 2 + y mod 2/2) = 2x*(y div 2) + x*y mod 2 *)
fun multiply x y =
if y=0 then 0 else if y<0 then intfix(multiply (~x) (~y))
else if y=1 then intfix x else
intfix(multiply(intfix(2*x))(y div 2)+x*(y mod 2));
fun makestring n = Int.toString n;
fun implode s = String.concat s
fun explode s = List.map str (String.explode s);
fun display (OpCode x) = str x |
display (Bool x) = if x then "t" else "f" |
display (Char x) = "'"^(str x) |
(* execution of an integer display will now put that integer on the stack *)
display (Int x) = if x<0 then ("0"^(display (Int(~x)))^"-")
else if x>9 then ((display(Int(x div 10)))^(display(Int(x mod 10)))^"_")
else makestring x |
display (Program L) = (implode (map str L)) |
display (Stack L) = "["^(listdisplay L)^"]"
and listdisplay nil = "" |
listdisplay [x] = display x |
listdisplay (x::L) = (display x)^" "^(listdisplay L);
val DEFINITIONS = ref [(#"x",[Stack nil])]; (* defined functions *)
val _ = DEFINITIONS:=nil;
(* management of the function database *)
fun drop x nil = nil |
drop x ((y,z)::L) = if x = y then drop x L else ((y,z)::drop x L);
fun add x y L = (x,y)::(drop x L);
(* this is now safe. *)
fun find x nil = (say "Definition not found";nil) |
find x ((y,z)::L) = if x=y then z else find x L;
fun foundin x nil = false |
foundin x ((y,z)::L) = if x = y then true else foundin x L;
(* the standard input buffer *)
val INSTREAM = ref [#"x"];
val _ =INSTREAM:=nil;
fun Tl nil = nil |
Tl [x] = [x] |
Tl x = tl x;
(* the file database *)
val FILES = ref
[(#"a",[(TextIO.openOut("boojum.txt"),TextIO.openIn("boojum.txt"))])];
fun p1(x,y) = x; fun p2(x,y) = y;
val _ = (TextIO.closeOut(p1(hd(find #"a" (!FILES))));
TextIO.closeIn((p2(hd(find #"a"
(!FILES)))));FILES:=nil);
fun getstringto c nil = nil |
getstringto c (x::L) = if x=c then nil else (x::(getstringto c L));
fun crest c nil = nil |
crest c (x::L) = if x=c then L else crest c L;
val getquotestring = getstringto #"\"";
val quoterest = crest #"\"";
fun getprogram nil = (say "Close bracket missing";nil) |
getprogram ((#"]")::L) = nil |
getprogram ((#"[")::L) =
[#"["]@(getprogram L)@[#"]"]@(getprogram (programrest L)) |
getprogram ((#"\"")::L) =
[#"\""]@(getquotestring L)@[#"\""]@(getprogram (quoterest L)) |
getprogram (x::L) = (x::(getprogram L))
and
programrest nil = nil |
programrest((#"]")::L) = L |
programrest ((#"[")::L) = programrest (programrest L) |
programrest ((#"\"")::L) = programrest (quoterest L) |
programrest (x::L) = programrest L;
fun first nil = nil |
first L =
if hd L = #"\"" then ((#"\"")::((getquotestring L)@[#"\""]))
else if hd L = #"[" then ((#"[")::((getprogram (tl L))@[#"]"]))
else if hd L = #"'" then if tl L = nil then
(say "Character not identified";nil) else
[hd L,hd(tl L)]
else if hd L = #"`" then if tl L = nil then
(say "Static opcode not identified";nil) else
[hd L,hd(tl L)]
else [hd L];
fun rest nil = nil |
rest L = if hd L = #"\"" then quoterest (tl L)
else if hd L = #"[" then programrest (tl L)
else if hd L = #"'" orelse hd L = #"`"
then if tl L = nil then nil else (tl(tl L))
else tl L;
fun boolhead ((Bool x)::L) = true |
boolhead x = false;
val FILENAME = ref "";
fun openfile x ((Char c)::L) = (FILENAME:=(str c)^(!FILENAME);openfile x L) |
openfile x L = if (!FILENAME) = "" then say "No file name read!"
else (if not(foundin x (!FILES)) then
FILES:=add x [(TextIO.openOut((!FILENAME)),TextIO.openIn((!FILENAME)))](!FILES)
else say "This character is already bound to a file";
FILENAME:="");
fun restfile ((Char c)::L) = restfile L |
restfile L = L;
fun help c =
if c= "O" orelse c = "R" orelse c = "W" orelse c = "F" then say
("The basic file I/O commands are\n"^
"O open a file: use first character on stack as file ID, then \n"^
" use the rest of the characters as the name. R read a character\n"^
" from a file (ID read from top of stack. Puts a boolean on top of the\n"^
" stack: if the character is not read, just \"false\"; if a character is\n"^
" read it puts the character on then \"true\" on top.\n"^
"W write a character (second item) to a file ID (top).\n"^
"F closes a file (File ID on top of stack).\n"^
"R,W,F all crash if the file ID isn't found in the database.")
else if c = "t" orelse c = "f" then say
"t loads true onto the stack and f loads false onto the stack"
else if c = "M" orelse c = "m" then say
("M executes the file whose ID is on top of the stack.\n"^
"m writes the function on top of the stack to the file ID which\n"^
"is next on the stack.")
else if c = "s" then say
"Tests whether the data stack is empty"
else if c = "d" then say
"Replaces the data stack with its top element (a stack)"
else if c = "P" then say
"Puts the program stack (continuation) on the top of the stack"
else if c = "D" then say
"Discards the current program stack and executes the function on top of the stack"
else if c = "c" then say
("c is a type casting command: toggles between integers and characters,\n"^
"applying modular arithmetic to convert large or negative integers.")
else if c = "C" then say
("C is a type casting command: it toggles between characters and\n"^
"inert opcodes")
else if c = "t" orelse c = "T" orelse c = "U" orelse c = "V" then say
("The opcodes T,U,V implement environment functions useful for\n"^
"tracing. T toggles the trace function on and off. U gives the stack\n"^
"display. V gives the current continuation (pending commands).\n\n"^
"In the shell, T turns trace off and t turns it on."
)
else if c = "b" orelse c = "X" then say
("In the shell b signals the beginning of a program: it appears\n"^
"on the line before the program begins; X ends the program\n"^
"(appearing at the beginning of a line")
else if c = "L" orelse c = "S" then say
("L/S is a shell command: load/save from/to the following filename (which\n"^
"should be separated by a single space from the L or S -- the extension\n"^
".sf will be supplied; S is also an opcode (copy the data stack to the top of itself)")
else if c = "l" then say
"This lists the current program in the shell"
else if c = " " orelse c = "\n" then say ("Whitespace is ignored, and plays\n"^
"no essential role in Pure False. The r opcode will print a carriage\n"^
"return.\n\n"^
"Space appears as the opcode when opcodes or whole programs are\n"^
"being executed from the stack.")
else if c = "?" then say
("The ? opcode is a conditional construct. Its first argument\n"^
"(second in stack) is boolean and if it is true the second argument\n"^
"(a function on top of the stack) is executed and otherwise discarded")
else if c = "#" then say
("The # opcode is a loop construct. The top function is the body and\n"^
"the second function on the stack is the test. The test is run first;\n"^
"as long as the test leaves a (consumed) truth value of true on\n"^
"top of the stack the body will be repeated. A truth value of false\n"^
"causes the body to be discarded. A non-truth value crashes.")
else if c = "!" then say
("The function at the top of the stack is unpacked and placed\n"^
"on top of the stack. This is intended to serve as function\n"^
"application, but it is more general.")
else if c = "`" then say
("This is a pseudo-opcode. For any character X, `X is effectively\n"^
"another character with the meaning [X] (a function with X in it).\n"^
"This provides an inert form in which opcodes can be inserted into\n"^
"functions by the o command. Totally different from False. When\n"^
"executed this item is put on the stack.")
else if c = "'" then say
("This is a pseudo-opcode. 'X is the character X. (push this\n"^
"on the stack)")
else if c = "n" then say
("This is the empty stack considered as a program: push it\n"^
"onto the stack.")
else if c = "p" then say
("This is a new capability in Pure False: the top item on the\n"^
"stack is pushed into the second item on the stack (a function)\n"^
"(as first item). This allows a theoretically interesting bit\n"^
"of function editing (along with the o command)\n"^
"Both arguments are removed and replaced with the result.")
else if c = "o" then say
("The first and second elements of the stack, functions,\n"^
"are composed (the first one to be done first), removed from\n"^
"the stack and replaced with the composition. In combination\n"^
"with the p command, a theoretically interesting degree of\n"^
"function editing is obtained.")
else if c = "%" then say
"Drop the top element from the stack"
else if c = "\\" then say
"Swap the top two elements of the stack"
else if c = "@" then say
"Rotates third element of the stack to the top: xyz->zxy"
else if c = "$" then say
"Duplicate the top element of the stack"
else if c = "0" orelse c = "1" orelse c = "2" orelse c = "3"
orelse c = "4" orelse c = "5" orelse c = "6" orelse c = "7"
orelse c = "8" orelse c = "9" orelse c = "_"
then say
("Each decimal digit is an opcode, loading that integer on the stack\n"^
"As in Befunge, Pure False does not support multi-digit decimal\n"^
"integers directly in programming: the operation _ (add the top of the stack\n"^
"to ten times the second element) enables this. Also as in False there is \n"^
"no integer input command.")
else if c = "^"
then say
("This is character input: a single character from standard input\n"^
"is placed on the stack. This is the only primitive input command.\n\n"^
"Input is buffered: you can type as many characters as you like\n"^
"at the input prompt >> and the program will accept these as needed.\n"^
"The ) command clears this buffer of any unused characters.")
else if c = "," orelse c = "." orelse c = "r" orelse c = "q"
then say
("There are four output commands. , prints (and consumes) the\n"^
"character on the stack. . prints (and consumes) the integer on\n"^
"the stack. Integer output can be multi-digit (impure but\n"^
"convenient) and is preceded by a space.\n"^
"Type errors cause crashes. q prints a quote and r\n"^
"effects a carriage return.\n\n"^
"in the shell, q means quit")
else if c = "+" orelse c = "-" orelse c = "*" orelse c = "/"
then say
("These are the usual integer arithmetic operations. Two items\n"^
"are popped off the stack and operated on and replaced with the\n"^
"result (the second item on the stack is first argument)\n\n"^
"Support for additional operations (unary minus and mod) is likely\n"^
"to be added. Integers are expected: type errors cause crashes.")
else if c = "=" orelse c = "<" orelse c = ">"
then say
("Comparison operators: overloaded to compare integers to each other\n"^
"or characters to each other. Type errors stop the show. Two arguments\n"^
"are popped off the stack, compared and replaced with the appropriate\n"^
"truth value.\n\n"^
"Functions can also be compared (dangerous): in this case\n"^
"the arguments are not consumed!")
else if c = "~" orelse c = "&" orelse c = "|" orelse c = "~"
then say
("Logical operations. ~ is logical negation (unary) and the others\n"^
"are binary. The appropriate number of arguments, which must be\n"^
"truth values, is popped off the stack and replaced with the result.")
else if c = ":" orelse c = ";"
then say
("Each character can be used as a variable. Only functions can\n"^
"be assigned as variable values (though it is easy to hack around this\n"^
"with the help of p and o). : takes two arguments: the top of the\n"^
"stack is a character (one must use ' to address a specific character\n"^
"literally, not as in False) and the second item on the stack is a function\n"^
"which is bound to that variable. Both are discarded. ; takes one\n"^
"argument, a character (consumed): the associated function is unpacked onto the\n"^
"stack and executed (it does not need to be applied with !)")
else if c = ")"
then say
("This command flushes the ML output buffer and the interpreter's\n"^
"input buffer.")
else if c = "\""
then say
("Characters enclosed in quotes are printed to standard output\n"^
"and discarded. The Hello, world function is \"Hello, world\"!\n\n"^
"The r command allows printing of a quote; returns in strings\n"^
"are allowed by Pure False but difficult to produce in ML.\n"^
"The q command allows printing of a double quote")
else if c = "["
then say
("Characters enclosed in brackets are a function (a stack placed\n"^
"on the stack). Brackets can be nested. The ! command executes\n"^
"a bracketed function (removes it from the stack, unpacks it\n"^
"and places all its contents on the stack). Some other commands\n"^
"can do this as well (? # ;). The p and o commands allow manipulation\n"^
"of functions, which was not supported in False.")
else if c = "i" orelse c = "j" orelse c = "x"
then say
("i allows execution of one step in the function on the top of the\n"^
"stack: remove the function from the stack, supply the top element\n"^
"to the stack and execute, then replace the tail of the function;\n"^
"of course this also allows functions with data in them to serve\n"^
"as list data structures. j is a static version: put the head of\n"^
"the function on in a 1-element list, then put the tail of the function on.\n"^
"x tests the function on top of the stack to see if it is empty\n"^
"(note that underflow is a show-stopper!); x does not remove the function,\n"^
"just adds the truth value concerning its emptiness or lack thereof.")
else if c = "{"
then say
"Comments begin with { and end with }. They may be nested and multi-line."
else say
("No help available on "^c);
(* this should produce a standard expanded form of a term
without embedded program text (with one exception) *)
fun notextitem [#"'",x] = Char x |
notextitem [#"`",x] = if ord(x) >= ord (#"0") andalso ord(x) <= ord(#"9")
then Stack[Int (ord(x)-ord(#"0"))] else Stack[OpCode (x)] |
notextitem [x] = if ord(x) >= ord (#"0") andalso ord(x) <= ord(#"9")
then Int (ord(x)-ord(#"0")) else OpCode x |
notextitem (#"["::L) = Stack(notextlist(rev(tl(rev L)))) |
notextitem (#"\""::L) = Stack([Program(#"\""::L)]) | (* no other representation for this! *)
notextitem x = Char (chr 0)
and notextlist nil = nil |
notextlist (#" "::L) = notextlist L |
notextlist (#"\n"::L) = notextlist L |
notextlist L = (notextitem(first L))::(notextlist(rest L))
and notextlist2 ((Program L)::M) = (notextlist L)@(notextlist2 M) |
notextlist2 ((Stack L)::M) = (notext(Stack L))::(notextlist2 M) |
notextlist2 (x::M) = x::(notextlist2 M) |
notextlist2 nil = nil
and notext (Stack L) = Stack(notextlist2 L) |
notext T = T;
val TRACE = ref true;
val PAUSE = ref true;
fun settrace() = TRACE:=true;
fun setnotrace() = TRACE := false;
fun setpause() = PAUSE:=true;
fun setnopause() = PAUSE:=false;
fun show code L = if (!TRACE) then (TextIO.output(TextIO.stdOut,
"\n opcode: "^(str code)^"\n\n stack: "^(listdisplay L)^"\n\n");TextIO.flushOut(TextIO.stdOut);
if (!PAUSE) then
INSTREAM:=((String.explode(TextIO.inputN(TextIO.stdIn,1)))@(!INSTREAM))
else ();
L)
else L;
fun (* ignore all whitespace *)
Execute (#" "::P) L = Execute P L |
Execute (#"\n"::P) L = Execute P L |
(* two kinds of executables on the stack: program text
objects and opcodes. *)
(* program text objects could be constructed out of opcodes
but in practice are not. The distinction is usually transparent,
except for equality *)
(* reorganization of text program structures *)
Execute P ((Stack((Program nil)::L))::M) = Execute P ((Stack L)::M) |
(* Execute P ((Stack((Program [x])::L))::M) = Execute P
((Stack((OpCode(x))::L))::M) | *)
(* Execute P ((Stack((Program L)::(Program M)::N))::S) =
Execute P ((Stack(Program (L@M)::N))::S) | *)
(* opcodes and program text on stack -- introduced by application *)
Execute P ((OpCode x)::L) = Execute (x::P) (show (#" ")L) |
Execute P ((Program Q)::L) = Execute (Q@P) (show (#" ")L) |
(* finished with program *)
Execute nil L = show (#" ")L |
(* control structures *)
(* this is the original False conditional;
just take out the brackets around tests and current
programs ought to work *)
(* it's much simpler... *)
Execute (#"?"::P) ((Stack y)::(Bool true)::L) =
Execute (#"!"::P) (show (#"?")((Stack y)::L)) |
Execute (#"?"::P) ((Stack y)::(Bool false)::L) =
Execute P (show (#"?") L) |
(* the loop construction -- notice that the pending
future executions are now on the program stack *)
Execute ((#"#")::P) ((Stack y)::(Stack x)::M) =
Execute
(#"!"::
(String.explode(
display(
Stack(y@
[Stack x]@
[Stack y]@
[Program [#"#"]])))@
[#"?"]@
P))
(show (#"#") ((Stack x)::M)) |
(* application *)
Execute (#"!"::P) ((Stack M)::L) = (Execute P (show(#"!")(M@(L)))) |
(* inert forms of opcodes *)
(* something needs to be done with iterated ` and `'s followed by ' *)
(* answer: ` should not be iterated: it is strictly used to
put active opcodes in inert form: `x is synoymous with [x]
only for single characters (and this is only meaningful if
x is an opcode) *)
(* deferred crashes can be caused by misuse of `, since the system
does not check for meaningfulness of such opcodes *)
Execute (#"`"::x::P) L = if ord(x)>=ord(#"0") andalso ord(x)>=ord(#"9")
then (Execute P (show(#"`")(Stack [Int (ord(x)-ord(#"0"))]::(L))))
else (Execute P (show(#"`")(Stack [OpCode x]::(L)))) |
(* character constants *)
Execute (#"'"::x::P) L = (Execute P (show(#"'")(Char x::(L)))) |
(* the empty stack *)
Execute (#"n"::P) L = (Execute P (show(#"n")(Stack nil::(L)))) |
(* add to stack at front *)