-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjsforth.js
More file actions
5679 lines (3745 loc) · 144 KB
/
jsforth.js
File metadata and controls
5679 lines (3745 loc) · 144 KB
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
// Disclaimer: JS-Forth is delivered as-is. No warranties,
// implicit or explicit, towards its function, usability, fitness
// for any purpose are given. It is distributed for educative
// purposes, you may study it to your hearts delight. Should
// you plan to execute JS-Forth on any computer, you declare
// to not hold the programmer liable in any way for any damage
// JS-Forth may cause, be it to that computer, peripherals, or any
// other object in the range of several thousand kilometers, or
// more. The person initiating execution of JS-Forth is the one
// carrying sole responsibility for all and any damage resulting
// from this action. Also, you do not hold the programmer liable
// for any damage resulting from the study of JS-Forth. Please
// do not stick your tongue into the power supply of the computer
// which is running JS-Forth.
// By opening JS-Forth for reading or execution, you make a full
// statement that you have read and understood all of the above
// disclaimer, and proceed willingly, volunteerily, and of your
// own choice on your own risk and responsability.
//
// Having said that, i can assure you that JS-Forth has not been
// written to perform any malicious action on your computer or
// anyone elses. I run Js-Forth frequently, and no damage has
// occured from doing so, though no extensive testing has been
// done on it as a whole yet. Those parts which work do so in a
// pretty stable manner. A major version jump to v0.01 may be imminent.
var version = "0" ;
var subversion = "5200804171342" ;
var title = "## JS-Forth " + version + "." + subversion + " ##" ;
// if (document.captureEvents) document.captureEvents(Event.KEYPRESS) ;
// if (window.captureEvents) window.captureEvents(Event.CLICK) ;
// commented out on 2017feb20: meant to reload most recent version from forthfreak wiki. But as wiki is defunct, so is reloading.
// if (document.domain.indexOf("forthfreak.net") == -1) { version = -1 ; location.replace("http://forthfreak.net/jsforth80x25.html") ; }
// document.getElementById('status').contentDocument.designMode = "on";
// --------------------------------------------- vars you may wish to customize ---------------------------------------------------
var memend = 0x100000 ; // memory allocated to jsforth (1 megacells is more than plenty)
var maxcookies = 4 // number of disk sectors. >4 may be unsafe.
var cookiebasename = "jsfblk" ; // cookie name for saved blocks (blk number gets appended)
var cookieexpirationdate = "Fri, 31 Dec 2015 23:59:59 GMT" ; // the date your hard disk will get erased.
var infolines = 1000 ; // backscroll buffer size of info screen
var paddistance = 512 ; // space between here and pad.
var padsize = 512 ; // remaining space above pad until dictionary overflow error
var maxbufs = 2 // number of buffers. works with any between 1 ... maxmem
// default=2, more may be useful if working with many remote blocks
var blocktimeout = 5000 // file i/o error if request not completed with this time (ms)
// --------------------------------------------------------------------------------------------------------------------------------
// --- character codes
var backspace = 8 ;
var tab = 9 ;
var carriagereturn = 13 ;
var esc = 27 ;
var bl = 32 ;
var suspended = -1 ; // reason for suspending interpreter (event type)
// -1: never started (helps source locator in see)
var dp = 0 ; // dictionary pointer
var catchframe = 0 ; // for catch/throw
var wc = 0 ; // header count
var inbuf = new Array() ; // accumulated input characters
var linelen = 80 ; // main screen
var lines = 30 ;
var linelen2 = 55 ; // info screen
var lines2 = lines ;
var screensize = lines * linelen ;
var tibsize = linelen + 1 ;
var dictionaryfull = memend - (paddistance + padsize) ;
// heap memory tracking
var usedchunk = new Array() ;
var freechunk = new Array() ;
var heapend = memend ;
// word header bit masks
var immediate = 1 ;
var smudgebit = 2 ;
var precedencebit = immediate ;
var dp_cold ; // allows "cold" to restore dp and wc to
var wc_cold ; // initial settings
var heap_cold ;
var s = new Array(); // data stack
var r = new Array(); // return stack
var m = new Array(); // main memory
var h = new Array(); // headers
var hf = new Array(); // header flags (precendence bit, hide/reveal)
var x = new Array(); // execution tokens
var t = new Array(); // word type - accelerated execution because no
var sourceid = new Array(); // nested loads/evaluate stack previous source id here
// conditional branching to the corresponding
// code for next per wordtype. this array contains
// the proper next to use. this should also
// simplify does>
h[0] = "" ; // in case header 0 gets accidently requested
var f = new Array(); // float stack
var ip; // forth vm instruction pointer
var w; // forth vm word register
var sp = 0; // forth vm parameter stack pointer
var rp = 0; // forth vm return stack pointer
var tos; // stack cache
var ftos; // float stack cache
// constants for standard compliance bit masks
// a combination of those are written to the description of each word
// through variable COMPLIANCE, subsets of provided words are masked
var standard = new Array() ;
// standard selection and vocabularies share the same mechanism. therefore,
// using both concepts is a bit of a mix here now.
// standards and vocabularies use bit masks
var nextvocabulary = 1 ; var uncertain = nextvocabulary ; standard[uncertain] = " possibly other, need to look that up first" ;
nextvocabulary <<= 1 ; var fig = nextvocabulary ; standard[fig] = "fig" ;
nextvocabulary <<= 1 ; var f79 = nextvocabulary ; standard[f79] = "f79" ;
nextvocabulary <<= 1 ; var f83 = nextvocabulary ; standard[f83] = "f83" ;
nextvocabulary <<= 1 ; var ans = nextvocabulary ; standard[ans] = "dpans94" ;
nextvocabulary <<= 1 ; var foerthchen= nextvocabulary ; standard[foerthchen]= "FOeRTHchen" ;
nextvocabulary <<= 1 ; var jsf = nextvocabulary ; standard[jsf] = "JS-Forth" ;
var higheststandard = nextvocabulary ;
var any = ans | f83 | f79 | fig | jsf ; // but not foerthchen
// --- no more standards, vocabularies follow ---
nextvocabulary <<= 1 ; var only = nextvocabulary ;
nextvocabulary <<= 1 ; var forth = nextvocabulary ;
nextvocabulary <<= 1 ; var hidden = nextvocabulary ;
nextvocabulary <<= 1 ; var teststuff = nextvocabulary ;
var lastsystemvocabulary = nextvocabulary ;
var vocstack = new Array() ; // top element is in m[context]
var vocname = new Array() ; // contains xt of all vocs
function printvocname(n) {
var temp = 0 ;
for (var i=only ; i != 0x40000000 ; i <<=1 ) {
if (i == n) {
type(h[vocname[temp]] + " ") ;
break ;
}
temp++ ;
}
}
function jscomma(n) { m[dp++] = n ; return (dp-1) ;}
function jshiallot0(n) { // heap (buffers, allocate)
for (var i=n ; i ; i--) m[heapend++] = 0 ;
return (heapend-n) ;
}
// variables, shared between javascript and forth:
// addressed by m[varname] from javascript, define a constant
// with value of address for access from forth.
var casesensitive = jscomma(0) ; // switch case sensitive/insensitive dictionary search
var debugging = jscomma(0) ; // get spilled with output on info display
var popups = jscomma(0) ; // errors to terminal or popup alert
var warnings = jscomma(-1) ; // meant to disable javascript warnings, but hides only error messages
var compliance = jscomma(jsf) ; // cause find, words to scope only those words complying with the selected standard
var fittype = jscomma(0) ; // ALLOCATE chunk matching: 0: first, other: best fit
var outfile = jscomma(-1) ; // switch between output routines:
// -1 : fast terminal
// -2 : slow terminal
// >=0 : output to consecutive blocks ( not implemented )
var blk = jscomma(-1) ; // currently accessed block, as set by load
var scr = jscomma(0) ; // last block accessed through list or user tools
var context = jscomma(forth) ; // first searched vocabulary on vocabulary stack
var current = jscomma(forth) ; // the vocabulary compiled to
var lastxt = jscomma(0) ; // contains execution token of most recently compiled word
var base = jscomma(10) ; // radix for i/o number conversion
var state = jscomma(0) ; // switch interpret/compile
var innerloop = jscomma(0) ; // compile time helper variable for loops
var innercase = jscomma(0) ; // of counter for inner case
var span = jscomma(0) ; // obsolete -- expect stores string len in here
var toin = jscomma(0) ; // input buffer handling
var hashtib = jscomma(0) ; // obsolescent - contains # chars in tib
var tib = jshiallot0(tibsize) // input buffer
var parsebuf ; // usually address of tib, but can be block address
var parsebuflen ; // number of chars in parse buffer
// ----------------------------------------------- mass memory buffers ---------------------------------------------
var nextbuf = 0 ; // index of next buffer to use
var buf = new Array() ; // buffer addresses
var bufdirty = new Array() ; // buffer dirty flag
var bufblk = new Array() ; // block in this buf, or -1
var blockstat = new Array() // -1: indexed by blk, gives -1 for unbuffered, or, if buffered, buffer id
// ----- data storage for descriptions, stack effects -----
// also trying to use these for vocabularies
// "standard" is printed along with help. As this information is supplied
// anyway, the compiler could use it, by making sure only words which belong
// to a user specified standard are used, or printing warning otherwise.
var ds = new Array(); // bitmask for compliancy and vocs
var dse = new Array(); // stack effect, text
var lineofspaces = "" ;
for ( var i=0 ; i<linelen ; i++ ) lineofspaces += " " ;
var linesonscreen = 1 ;
var terminal = new Array() ; terminal[0] = "" ;
var charsperline = new Array() ; charsperline[0] = 0 ;
function describe(string1,bitmask) {
dse[wc] = string1 ;
ds[wc] = 0 | m[current] ; if (bitmask) ds[wc] = bitmask | m[current];
}
var infoline = new Array() ;
var allinfos = 0 ;
// =================================================================================================
// dumb terminal emulator
// =================================================================================================
// ---- infos screen ----
function printinfos() {
for (var i=infoline.length ; i>infolines ; i--) infoline.shift() ; // limit #lines in buffer
if (allinfos) {
temp = infoline.join("\n") ; // want to see all lines
} else {
var temp="" ;
for (i=Math.max(0,infoline.length-lines2); i<infoline.length ; i++) {
temp += (infoline[i] + "\n") ; // want to see only last x lines
}
}
document.terminal.status.value = temp ; // flush
}
function clsinfo() { for (i=infoline.length ; i ; i--) infoline.shift() ; }
function info(string) { infoline.push((string+"").substr(0,linelen2)) ; }
function debug(string) { if (m[debugging]) info(string) ; }
function moreinfo(string) { // appends to last info line
var temp = infoline.length - 1 ;
var temp2 = infoline[temp] + string ;
infoline[temp] = temp2.substr(0,linelen2) ;
}
function seperator() {
info("----------------------------------------------------------------") ;
}
// ---- interactive terminal screen ----
function flushscreen() {
document.terminal.dialog.value = terminal.join("\n") ;
printinfos() ;
}
function show() { flushscreen() } ;
function type(string) {
terminal[--linesonscreen] += string ; // add output text to cursorline
charsperline[linesonscreen++] += string.length ; // update chars/line accordingly
if (charsperline[linesonscreen-1] <= linelen) return ;
for ( var lastline = --linesonscreen ; charsperline[lastline] > linelen ; ) { // line longer than terminal wide ?
if (lastline >= lines) { // need to scroll ?
terminal.shift() ; // remove top line
charsperline.shift() ;
lastline-- ; // unregister top line
}
terminal.push(terminal[lastline].substr(linelen)) ; // break line, adding a new one
terminal[lastline] = terminal[lastline].substr(0,linelen) ; // move part beyond linelen to next line
charsperline.push(charsperline[lastline]-linelen) ; // calculate count chars on new line
charsperline[lastline++] = linelen ; // set line len for cut line
if (m[outfile]==-2) flushscreen() ; // update screen
}
linesonscreen = ++lastline ;
}
function write(string) {
type(string) ;
flushscreen() ;
}
function cr() {
terminal.push("") ; // add new empty line
charsperline.push(0) ; // add new chars per line
linesonscreen++ ;
if (linesonscreen<lines) { // no scrolling, need to update only last line
document.terminal.dialog.value += terminal[linesonscreen-1] ;
} else {
for ( ; linesonscreen > lines ; linesonscreen-- ) { // scrolling necessary ?
terminal.shift() ; // remove top line
charsperline.shift() ; // remove top line length
}
if (m[outfile]==-2) flushscreen() ; // update screen
}
}
function cls() {
for ( ; linesonscreen > 1 ; linesonscreen-- ) { // remove all but one lines
terminal.pop() ;
charsperline.pop() ;
}
terminal[0]="" ; // empty the one remaining line
charsperline[0] = 0 ;
flushscreen() ;
}
function backspaces(n) {
if (n>0) {
var lastline = linesonscreen - 1 ;
var charstoremain = charsperline[lastline] - n ;
if (charstoremain < 0) {
charstoremain = 0 ;
}
terminal[lastline] = terminal[lastline].substr(0,charstoremain) ;
charsperline[lastline] = charstoremain ;
}
}
function emit(asc) { type(String.fromCharCode(asc)); }
function querytype(string) { // breaks line if string won't fit
if ((charsperline[linesonscreen-1] + string.length) > linelen) cr() ;
type(string) ;
}
// =================================================================================================
// primitives constructor
// =================================================================================================
var src = new Array() ;
// -2: script file
// -1: interactive console
// 0... blk*1024+charpos
function from() {
if (suspended) return -2 ;
if (m[blk] >= 0) return m[blk]*1024+m[toin] ;
return -1 ;
}
function newheader(name,flags) { // wc = word count
h[++wc] = name ; // header name
src[wc] = from() ;
hf[wc] = flags ; // immediate/reveal
x[wc] = dp ; // pointer to word body (was: xt)
m[lastxt] = wc ; // last
ds[wc] = any | foerthchen | m[current] ; // new words standard compliance
dse[wc] = "" ; // new word stack effect
debug("compiling: " + name);
}
function nextprimitive() { x[w]() ; }
function nexthilevel() { w = x[w] ; x[m[w]]() ; }
function nextconstant() { s[++sp]=tos ; tos=m[x[w]+1] ; }
function nextvariable() { s[++sp]=tos ; tos=x[w]+1 ; }
// function nexthilevel() { r[++rp] = ip ; ip = x[w]+1 ; } // slower ...
// there's a dovocabulary further below, and a dodoes
function primitive(name,code,flags) {
newheader(name,flags|smudgebit) ;
x[wc] = code ;
t[wc] = nextprimitive ;
return wc ;
}
function headerless() { h[wc] = "" ; }
// =================================================================================================
// misc helper words
// =================================================================================================
primitive("cls2",clsinfo) ; // clear info window
describe("--",jsf) ;
definitions(hidden) ;
function pack(a,n) {
w = "" ;
for ( var i=n ; i; i--) w += String.fromCharCode(m[a++]) ;
return w;
}
function forthpack() { tos=pack(s[sp--],tos) ; }
var x_pack=primitive("pack",forthpack) ;
describe("a n -- x",jsf) ;
function unpackstring(string,address) { // returns len
var stringlen = string.length ;
var destaddr = address + stringlen ;
for (var i=stringlen; i; m[--destaddr]=string.charCodeAt(--i) ) {} ;
return stringlen ;
}
// unpack packed string x to address, return number of characters
// can reuse the function above
function unpack() { // ( x a -- n )
var string = s[sp--] ; // string
w = string.length ; // string len
tos += w ; // last dest address + 1
for (var i=w; i; m[--tos]=string.charCodeAt(--i) ) {} ;
tos = w ;
}
var x_unpack = primitive("unpack",unpack) ;
describe("x a -- n",jsf) ;
function definitions(vocabulary) { m[current] = vocabulary ; }
definitions(forth) ;
function forthstackeffect() { // ( xt -- a n )
s[++sp] = dp ;
if (dse[tos]) {
tos = unpackstring("( " + dse[tos] + " )",dp) ;
} else {
tos = 0 ;
}
}
primitive("stackeffect",forthstackeffect) ;
describe("xt -- a n",jsf) ;
definitions(hidden)
function forthstorestackeffect() { // ( a n -- )
forthpack() ;
dse[m[lastxt]] = tos ;
tos = s[sp--]
}
var x_storestackeffect = primitive("stackeffect!",forthstorestackeffect) ;
describe("a n --",jsf) ;
definitions(forth)
function forthinfo() { // ( a n -- ) info
info(pack(s[sp--],tos)) ;
tos = m[sp--] ;
}
primitive("info",forthinfo) ;
describe("a n --",jsf) ;
function forthmoreinfo() { // ( a n -- ) ...info
moreinfo(pack(s[sp--],tos)) ;
tos = m[sp--] ;
}
primitive("...info",forthmoreinfo) ;
describe("a n --",jsf) ;
var clock ;
function forthstartclock() {
clock = new Date().getTime();
}
primitive("startclock",forthstartclock) ;
describe("--",jsf) ;
function forthelapsed() {
w = new Date().getTime();
s[++sp] = tos ;
tos = w - clock ;
}
primitive("elapsed",forthelapsed) ;
describe("-- u",jsf) ;
// ----- multi standard selection -----
definitions(forth) ;
function forthfig() { m[compliance] = fig ; }
primitive("fig",forthfig) ;
describe("--",jsf) ;
function forthfoerthchen() { m[compliance] = foerthchen ; }
primitive("foerthchen",forthfoerthchen) ;
describe("--",jsf) ;
function forthf79() { m[compliance] = f79 ; }
primitive("f79",forthf79) ;
describe("--",jsf) ;
function forthf83() { m[compliance] = f83 ; }
primitive("f83",forthf83) ;
describe("--",f83|jsf) ;
function forthans() { m[compliance] = ans ; }
primitive("ans",forthans) ;
describe("--",jsf) ;
function forthjsforth() { m[compliance] = jsf ; }
primitive("jsf",forthjsforth) ;
describe("--",any|foerthchen) ;
// =================================================================================================
// virtual machine
// =================================================================================================
definitions(hidden) ;
function forthnest() { r[++rp] = ip ; ip = ++w ; }
var x_nest=primitive("(nest)",forthnest) ;
describe("--",jsf) ;
definitions(forth) ;
function forthunnest() { ip = r[rp--] ; }
var x_unnest=primitive("exit",forthunnest) ;
describe("--",any) ;
// =================================================================================================
// catch, throw
// =================================================================================================
var customerror = new Array() ;
var systemerror = new Array() ;
systemerror[1] = "aborted" ;
systemerror[2] = "aborted" ;
systemerror[3] = "stack overflow" ;
systemerror[4] = "stack underflow" ;
systemerror[5] = "return stack overflow" ;
systemerror[6] = "return stack underflow" ;
systemerror[7] = "do loops nested too deeply" ;
systemerror[8] = "dictionary overflow" ;
systemerror[9] = "invalid memory address" ;
systemerror[10] = "division by zero" ;
systemerror[11] = "result out of range" ;
systemerror[12] = "argument type mismatch" ;
systemerror[13] = "word not found" ;
systemerror[14] = "use only during compilation" ;
systemerror[15] = "invalid forget" ;
systemerror[16] = "attempt to use zero-length string as name" ;
systemerror[17] = "pictured numeric ouput string overflow" ;
systemerror[18] = "pictured numeric ouput string overflow" ;
systemerror[19] = "word name too long" ;
systemerror[20] = "write to a read-only location" ;
systemerror[21] = "unsupported operation" ;
systemerror[22] = "unstructured" ;
systemerror[23] = "address alignment exception" ;
systemerror[24] = "invalid numeric argument" ;
systemerror[25] = "return stack imbalance" ;
systemerror[26] = "loop parameters unavailable" ;
systemerror[27] = "invalid recursion" ;
systemerror[28] = "user interrupt" ;
systemerror[29] = "compiler nesting" ;
systemerror[30] = "obsolescent feature" ;
systemerror[31] = ">BODY used on non-CREATEd definition" ;
systemerror[32] = "invalid name argument" ;
systemerror[33] = "Block read exception" ;
systemerror[34] = "Block write exception" ;
systemerror[35] = "Invalid block number" ;
systemerror[36] = "Invalid file position" ;
systemerror[37] = "File I/O exception" ;
systemerror[38] = "File not found" ;
// additional jsforth error messages:
systemerror[64] = "use only while interpreting" ;
systemerror[65] = "executed BODY> on a non-body address" ;
systemerror[66] = "unstructured" ; // message gets overwritten for more detail
systemerror[67] = "TO must be used on a VALUE" ;
systemerror[68] = "JavaScript boo, mostly the result of uninitialized memory access" ;
systemerror[69] = "Too many vocabularies" ;
systemerror[70] = "No cookie by that name found" ;
systemerror[71] = "Can't write to read-only block" ;
systemerror[72] = "Invalid memory region specifier, or heap corrupted" ;
function errordialog(x) {
if (x < 0) {
if (systemerror[-x]) return ("error(" + x + "): " + systemerror[-x]) ;
return ("error #" + x) ;
}
if (customerror[x]) return ("error: " + customerror[x]) ;
return ("error #" + x) ;
}
// throw without catch frame - top level error handler
function exception(x) {
cr() ;
if (m[blk]>=0) {
var temp = m[toin] % 64 ;
write(pack(parsebuf + m[toin] - temp,temp)) ;
} else {
write(pack(parsebuf,m[toin])) ;
}
cr() ;
if (m[popups]) {
alert(errordialog(x)) ;
} else {
type("=== " + errordialog(x) + " ===") ; cr() ;
}
// just calling the virtual machine won't do, as that would require more and more javascript return stack.
// stopping the interpreter, and have it restart with a one-time event at the warm start point solves this.
debug("issuing timed event 'warmstart vm in 1 ms'") ;
suspended = warm ;
setTimeout(virtualmachine,1,warm) ;
tos = s[sp--] ;
}
definitions(hidden) ;
function forththrow0() {
catchframe = r[rp] ;
sp = r[--rp] ;
ip = r[--rp] ;
rp-- ;
tos = 0 ;
}
var brthrow0 = dp
m[dp++] = primitive("throw0",forththrow0) ;
definitions(forth) ;
function forthcatch() {
r[++rp] = ip ;
r[++rp] = sp ;
r[++rp] = catchframe ;
catchframe = rp ;
r[++rp] = brthrow0 ;
forthexecute() ;
}
var x_catch = primitive("catch",forthcatch) ;
describe("xn ... x0 a -- xn ... x0 n",ans|jsf) ;
function throwerror(x) {
if (catchframe) {
tos = x
rp = catchframe ;
catchframe = r[rp--] ;
sp = r[rp--] ;
ip = r[rp--] ;
} else {
exception(x) ;
}
}
function forththrow() {
if (tos != 0) {
throwerror(tos) ;
} else {
tos = s[sp--] ;
}
}
var x_throw = primitive("throw",forththrow) ;
describe("n --",ans|jsf|f83) ;
function forthnewerror() {
if (tos<0) {
systemerror[-tos] = pack(s[sp-1],s[sp]) ;
} else {
customerror[tos] = pack(s[sp-1],s[sp]) ;
}
sp -= 2 ;
tos = s[sp--] ;
}
primitive("newerror",forthnewerror) ;
describe("a n1 n2 --",jsf) ;
// =================================================================================================
// run time words
// =================================================================================================
definitions(hidden) ;
function forthdolit() { s[++sp]=tos ; tos=m[ip++] ; }
var x_lit=primitive("(lit)",forthdolit) ;
function forthbrsquote() { s[++sp]=tos ; tos=m[ip++] ; s[++sp]=ip ; ip+=tos ; }
var x_brsquote=primitive('(s")',forthbrsquote) ;
function forthbrcquote() { s[++sp]=tos ; tos=ip++ ; ip += m[tos] }
var x_brcquote=primitive('(c")',forthbrcquote) ;
function forthbrdotquote() {
forthbrsquote() ;
forthpack() ;
type(tos) ;
tos=s[sp--] ;
}
var x_brdotquote=primitive('(.")',forthbrdotquote) ;
function forthdovar() { s[++sp] = tos ; tos = ++w ; }
var x_dovar=primitive("(var)",forthdovar) ;
function forthdoconst() { s[++sp]=tos ; tos=m[++w] ; }
var x_doconst = primitive("(const)",forthdoconst) ;
var x_dovalue = primitive("(value)",forthdoconst) ;
function forthdofconst() { f.push(ftos) ; ; ftos = m[++w] ; }
var x_dofconst = primitive("(fconst)",forthdofconst) ;
// function forthdodefer() { ip=++w ; }
// branch 1 works as nop, branch -1 is infinite loop
function forthbranch() { ip+=m[ip] ; }
var x_branch=primitive("(branch)",forthbranch) ;
function forth0branch() {
if (tos) {
ip++ ;
} else {
ip+=m[ip] ;
}
tos=s[sp--] ;
}
var x_0branch=primitive("(0branch)",forth0branch) ;
function forthwarminit() { // not for interactive use
tos = r[rp] ; rp = 0 ; r[rp+1] = 0 ; r[rp] = tos ;
tos = 0 ; sp = 0 ; s[sp+1] = 0 ; s[sp] = tos ;
ftos = 0 ;
for ( ; f.length ; f.pop() ) ;
catchframe = 0 ;
m[state] = 0 ;
m[innerloop] = 0 ;
m[innercase] = 0 ;
m[blk] = -1 ;
m[outfile] = -1 ;
for ( ; inbuf.length ; inbuf.pop() ) ;
}
var x_warminit = primitive("warminit",forthwarminit) ;
function forthcoldinit() { // not for interactive use
window.onerror = ErrorEvent ;
window.onwarning = ErrorEvent ;
window.offscreenBuffering = false ;
for ( ; sourceid.length ; sourceid.pop() ) ;
for ( ; nextvocabulary>lastsystemvocabulary ; nextvocabulary>>=1 ) vocname.pop() ;
dp = dp_cold ;
wc = wc_cold ;
heapend = heap_cold ;
for ( ; usedchunk.length; usedchunk.pop()) ;
nusedchunks = 0 ;
for ( ; i=freechunk.length ; freechunk.pop() ) ;
nfreechunks = 0 ;
for (i=0 ; i<maxbufs ; i++ ) { // for each buffer:
buf[i] = jshiallot0(1024) ; // allocate and initialize buffer
info("buffer " + i + " allocated at addr " + buf[i]) ;
bufdirty[i] = 0 ; // set flushed
bufblk[i] = -1 ; // contains no block
}
for (i=0 ; i<capacity() ; blockstat[i++] = -1 ) ; // mark all blocks unbuffered
coldstartinfo() ;
m[base] = 10 ;
forthonly() ;
forthforth() ;
forthalso() ;
m[current] = forth ;
forthwarminit() ;
}
var x_coldinit = primitive("coldinit",forthcoldinit) ;
function forthbrabortquote() {
if (tos) {
forthbrsquote() ;
forthpack() ;
systemerror[2] = tos ;
throwerror(-2) ;
} else {
tos = s[sp--] ;
ip += m[ip]+1 ;
}
}
var x_brabortquote = primitive('(abort")',forthbrabortquote) ;
function forthbrto() {
m[m[ip++]] = tos ;
tos = s[sp--] ;
}
var x_brto = primitive("(to)",forthbrto) ;
// --- not portable: output packed string literal ---
function jsdotquote() { type(m[ip++])} ;
var dotquote = primitive("",jsdotquote) ;
// =================================================================================================
// stack operators
// =================================================================================================
definitions(forth) ;
function forthdup() { s[++sp] = tos ; } // dup
var x_dup=primitive("dup",forthdup) ;
describe("x -- x x",any|foerthchen) ;
function forthqdup() { if (tos) s[++sp]=tos ; } // ?dup
var x_qdup=primitive("?dup",forthqdup) ;
describe("x -- 0 | x x",any) ;
function forthdrop() { tos = s[sp--] ; } // drop
var x_drop=primitive("drop",forthdrop) ;
describe("x --",any|foerthchen) ;
function forthswap() { w = s[sp] ; s[sp] = tos ; tos = w ; } // swap
var x_swap=primitive("swap",forthswap) ;
describe("x1 x2 -- x2 x1",any|foerthchen) ;
function forthover() { s[++sp]= tos ; tos=s[sp-1] ; } // over
var x_over=primitive("over",forthover) ;