Skip to content

Commit 22513c2

Browse files
committed
Add >BODY CHAR CREATE DOES> and tests.
1 parent 96356f0 commit 22513c2

File tree

5 files changed

+128
-24
lines changed

5 files changed

+128
-24
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,7 @@ error if it cannot be cannot be cross compiled.
477477
* `<`
478478
* `=`
479479
* `>`
480+
* `>BODY`
480481
* `>R`
481482
* `?DUP`
482483
* `@`
@@ -496,15 +497,20 @@ error if it cannot be cannot be cross compiled.
496497
* `C@`
497498
* `CELL+`
498499
* `CELLS`
500+
* `CHAR`
499501
* `CHAR+`
500502
* `CHARS`
501503
* `CONSTANT`
502504
* Can only run on host.
503505
* `COUNT`
504506
* `CR`
507+
* `CREATE`
508+
* Can only run on host.
505509
* `DECIMAL`
506510
* `DEPTH`
507511
* `DO`
512+
* `DOES>`
513+
* Can only run on host.
508514
* `DROP`
509515
* `DUP`
510516
* `ELSE`

pkg/forth/builtin/01_compile.f

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,30 +28,30 @@
2828

2929
\ CREATE parses the next name and creates a Forth definition
3030
\ for that name.
31-
BL WORD CREATE ( -- ) --CREATE-FORTH ] BL WORD --CREATE-FORTH EXIT [
31+
BL WORD CREATEWORD ( -- ) --CREATE-FORTH ] BL WORD --CREATE-FORTH EXIT [
3232

3333
\ SEE parses the next name and prints the definition of that word.
3434
\ An error is thrown if there is not a word with that name in the dictionary.
35-
CREATE SEE ] BL WORD --SEE EXIT [ \ not core but really nice for debugging
35+
CREATEWORD SEE ] BL WORD --SEE EXIT [ \ not core but really nice for debugging
3636

3737
\ TRUE places the 'true' value onto the stack.
38-
CREATE TRUE ( -- true ) ] -1 EXIT [
38+
CREATEWORD TRUE ( -- true ) ] -1 EXIT [
3939

4040
\ FALSE places the 'false' value onto the stack.
41-
CREATE FALSE ( -- false ) ] 0 EXIT [
41+
CREATEWORD FALSE ( -- false ) ] 0 EXIT [
4242

4343
\ : parses the next name, creates a dictionary entry for it, hides that word,
4444
\ and puts the VM into compile state.
4545
\ Used to start compilation.
46-
CREATE : ( -- ) ]
47-
CREATE \ create the new dictionary entry
46+
CREATEWORD : ( -- ) ]
47+
CREATEWORD \ create the new dictionary entry
4848
TRUE LAST SET-HIDDEN \ hide it
4949
] \ and put in compile mode
5050
EXIT [
5151

5252
\ ; appends EXIT to the most recent definition, unhides it, and puts the VM into interpret state.
5353
\ Used to end compilation. Immediate.
54-
CREATE ; ( -- ) ]
54+
CREATEWORD ; ( -- ) ]
5555
POSTPONE EXIT \ compile EXIT into new word
5656
FALSE LAST SET-HIDDEN \ unhide the new word
5757
POSTPONE [ \ put back in interpret mode

pkg/forth/builtin/02_core.f

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@
9191
SWAP 1 AND \ isolate the lowest bit
9292
+ \ then put back the pit
9393
;
94+
: CHAR
95+
BL WORD
96+
CHAR+ C@
97+
;
9498

9599
: U> SWAP U< ; \ greaterthan is just lessthan with operands swapped
96100
: >
@@ -444,3 +448,25 @@
444448
LOOP
445449
2DROP
446450
;
451+
452+
: CREATE
453+
ALIGN \ align the data space pointer
454+
HERE \ get the data space pointer
455+
: \ parse the next input, create a word with that name
456+
POSTPONE LITERAL \ create a literal of the previous data space pointer
457+
POSTPONE EXIT \ put an extra exit, can be overwritten
458+
POSTPONE ; \ end the definition
459+
;
460+
461+
: DOES>
462+
POSTPONE EXIT \ put a dummy value here for now
463+
LAST \ get the definition that called DOES>
464+
LAST-ADDRESS \ get the last address of that definition
465+
POSTPONE LAST \ later get the most recent definition
466+
POSTPONE 1+ \ later go 1 past the end
467+
POSTPONE ! \ later store the value
468+
POSTPONE ; \ end the definition that called DOES>
469+
:NONAME \ start a new unnamed definition
470+
LITERALIZE \ literalize the noname address
471+
SWAP ! \ store the literal noname address inside the dummy value
472+
; IMMEDIATE

pkg/forth/primitive.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,49 @@ func PrimitiveSetup(vm *VirtualMachine) error {
743743
return nil
744744
},
745745
},
746+
{
747+
name: "LAST-ADDRESS",
748+
goFunc: func(vm *VirtualMachine, entry *DictionaryEntry) error {
749+
// get address
750+
cell, err := vm.Stack.Pop()
751+
if err != nil {
752+
return PopError(err, entry)
753+
}
754+
cellAddress, ok := cell.(CellAddress)
755+
if !ok {
756+
return EntryError(entry, "can only get length of an address, got %s type %T", cellAddress, cellAddress)
757+
}
758+
w, ok := cellAddress.Entry.Word.(*WordForth)
759+
if !ok {
760+
return EntryError(entry, "can only get length of forth data words found %s type %T", w, w)
761+
}
762+
// get length
763+
length := len(w.Cells)
764+
// update the address cell with the last value
765+
cellAddress.Offset = length - 1
766+
cellAddress.UpperByte = false
767+
err = vm.Stack.Push(cellAddress)
768+
if err != nil {
769+
return PushError(err, entry)
770+
}
771+
return nil
772+
},
773+
},
774+
{
775+
// convert a cell into a literal of that cell
776+
name: "LITERALIZE",
777+
goFunc: func(vm *VirtualMachine, entry *DictionaryEntry) error {
778+
cell, err := vm.Stack.Pop()
779+
if err != nil {
780+
return PopError(err, entry)
781+
}
782+
err = vm.Stack.Push(CellLiteral{cell})
783+
if err != nil {
784+
return PushError(err, entry)
785+
}
786+
return nil
787+
},
788+
},
746789
{
747790
name: "@",
748791
goFunc: func(vm *VirtualMachine, entry *DictionaryEntry) error {

pkg/forth/suite_test.go

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,12 @@ func TestSuite(t *testing.T) {
134134
T{ 127 CHARS BUFFER: TBUF2 -> }T \ Buffer is aligned
135135
T{ TBUF1 ALIGNED -> TBUF1 }T \ Buffers do not overlap
136136
\ T{ TBUF2 TBUF1 - ABS 127 CHARS < -> <FALSE> }T \ Buffer can be written to
137-
\ 1 CHARS CONSTANT /CHAR
138-
\ : TFULL? ( c-addr n char -- flag )
139-
\ TRUE 2SWAP CHARS OVER + SWAP ?DO
140-
\ OVER I C@ = AND
141-
\ /CHAR +LOOP NIP
142-
\ ;
137+
1 CHARS CONSTANT /CHAR
138+
: TFULL? ( c-addr n char -- flag )
139+
TRUE 2SWAP CHARS OVER + SWAP ?DO
140+
OVER I C@ = AND
141+
/CHAR +LOOP NIP
142+
;
143143
\ T{ TBUF1 127 CHAR * FILL -> }T
144144
\ T{ TBUF1 127 CHAR * TFULL? -> <TRUE> }T
145145
\ T{ TBUF1 127 0 FILL -> }T
@@ -267,7 +267,13 @@ func TestSuite(t *testing.T) {
267267
T{ c1 CHAR+ C@ -> 0x12 }T
268268
`,
269269
},
270-
// CHAR
270+
{
271+
name: "CHAR",
272+
setup: `
273+
T{ CHAR X -> 58 }T
274+
T{ CHAR HELLO -> 48 }T
275+
`,
276+
},
271277
// CHAR+
272278
// CHARS
273279
// COMPILE,
@@ -412,7 +418,26 @@ func TestSuite(t *testing.T) {
412418
`,
413419
},
414420
// DO does not have any tests
415-
// DOES>
421+
{
422+
name: "DOES>",
423+
setup: `
424+
T{ : DOES1 DOES> @ 1 + ; -> }T
425+
T{ : DOES2 DOES> @ 2 + ; -> }T
426+
T{ CREATE CR1 -> }T
427+
T{ CR1 -> HERE }T
428+
T{ 1 , -> }T
429+
T{ CR1 @ -> 1 }T
430+
T{ DOES1 -> }T
431+
T{ CR1 -> 2 }T
432+
T{ DOES2 -> }T
433+
T{ CR1 -> 3 }T
434+
T{ : WEIRD: CREATE DOES> 1 + DOES> 2 + ; -> }T
435+
T{ WEIRD: W1 -> }T
436+
T{ ' W1 >BODY -> HERE }T
437+
T{ W1 -> HERE 1 + }T
438+
T{ W1 -> HERE 2 + }T
439+
`,
440+
},
416441
{
417442
name: "DROP",
418443
code: `
@@ -531,14 +556,12 @@ func TestSuite(t *testing.T) {
531556
T{ VARIABLE iw3 IMMEDIATE 234 iw3 ! iw3 @ -> 234 }T
532557
T{ : iw4 iw3 [ @ ] LITERAL ; iw4 -> 234 }T
533558
T{ :NONAME [ 345 ] iw3 [ ! ] ; DROP iw3 @ -> 345 }T
534-
\ The rest of these tests fail because CREATE does not
535-
\ put the newly created definition on the data space.
536-
\ T{ CREATE iw5 456 , IMMEDIATE -> }T
537-
\ T{ :NONAME iw5 [ @ iw3 ! ] ; DROP iw3 @ -> 456 }T
538-
\ T{ : iw6 CREATE , IMMEDIATE DOES> @ 1+ ; -> }T
539-
\ T{ 111 iw6 iw7 iw7 -> 112 }T
540-
\ T{ : iw8 iw7 LITERAL 1+ ; iw8 -> 113 }T
541-
\ T{ : iw9 CREATE , DOES> @ 2 + IMMEDIATE ; -> }T
559+
T{ CREATE iw5 456 , IMMEDIATE -> }T
560+
T{ :NONAME iw5 [ @ iw3 ! ] ; DROP iw3 @ -> 456 }T
561+
T{ : iw6 CREATE , IMMEDIATE DOES> @ 1+ ; -> }T
562+
T{ 111 iw6 iw7 iw7 -> 112 }T
563+
T{ : iw8 iw7 LITERAL 1+ ; iw8 -> 113 }T
564+
T{ : iw9 CREATE , DOES> @ 2 + IMMEDIATE ; -> }T
542565
\ : find-iw BL WORD FIND NIP ;
543566
\ T{ 222 iw9 iw10 find-iw iw10 -> -1 }T \ iw10 is not immediate
544567
\ T{ iw10 find-iw iw10 -> 224 1 }T \ iw10 becomes immediate
@@ -1360,7 +1383,13 @@ func TestSuite(t *testing.T) {
13601383
T{ 1 ?DUP -> 1 1 }T
13611384
`,
13621385
},
1363-
// >BODY does not have a test case we can replicate
1386+
{
1387+
name: ">BODY",
1388+
setup: `
1389+
T{ CREATE CR0 -> }T
1390+
T{ ' CR0 >BODY -> HERE }T
1391+
`,
1392+
},
13641393
// >IN
13651394
// >NUMBER
13661395
{

0 commit comments

Comments
 (0)