Skip to content

Commit 6462f4f

Browse files
committed
Add S\" .\" words.
1 parent 6382afe commit 6462f4f

File tree

6 files changed

+130
-11
lines changed

6 files changed

+130
-11
lines changed

pkg/forth/builtin/02_core.f

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@
265265
: COUNT ( c-addr -- c-addr+1 n ) DUP CHAR+ SWAP C@ ;
266266
: STRING" '"' WORD ; ( -- c-addr ) \ read a string and put it on the stack as a counted string
267267
: LSTRING" '"' LWORD ; ( -- addr u ) \ read a string and put it on the stack as a string and length
268+
: LSTRING\" '"' LWORDESCAPED ;
268269
: C" \ this is an extended C" to allow it to run while interpreting
269270
STRING" \ always parse the counted string
270271
STATE @ IF \ but if we're compiling,
@@ -279,6 +280,13 @@
279280
THEN
280281
; IMMEDIATE
281282

283+
: S\" \ this is the extended mechanics of S\" to allow it to run while interpreting
284+
LSTRING\"
285+
STATE @ IF
286+
SWAP POSTPONE LITERAL POSTPONE LITERAL
287+
THEN
288+
; IMMEDIATE
289+
282290
: [CHAR]
283291
BL WORD \ get the next word
284292
COUNT DROP \ get the address of the first letter

pkg/forth/builtin/10_print.f

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,7 @@
6565
DROP \ remove c-addr
6666
;
6767

68-
: .DELIM ( c "ccc<paren>" -- )
69-
LWORD
68+
: .DELIM ( caddr u -- )
7069
STATE @ IF
7170
SWAP POSTPONE LITERAL POSTPONE LITERAL
7271
POSTPONE TYPE
@@ -77,12 +76,17 @@
7776

7877
\ Can be used to print a string while interpreting or compiling.
7978
: ."
80-
'"' .DELIM
79+
'"' LWORD .DELIM
8180
; IMMEDIATE
8281

8382
\ Can be used to print a string while interpreting or compiling.
8483
: .(
85-
')' .DELIM
84+
')' LWORD .DELIM
85+
; IMMEDIATE
86+
87+
\ Can be used to print a string while interpreting or compiling.
88+
: .\"
89+
'"' LWORDESCAPED .DELIM
8690
; IMMEDIATE
8791

8892
\ set EMIT to the system printchar by default

pkg/forth/parse.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func (p *ParseArea) Restore() error {
4848
return nil
4949
}
5050

51-
func (p *ParseArea) Word(delimiter byte) ([]byte, error) {
51+
func (p *ParseArea) Word(delimiter byte, escape bool) ([]byte, error) {
5252
// trim starting whitespace
5353
startIndex := p.index
5454
for ; startIndex < len(p.area); startIndex++ {
@@ -63,6 +63,7 @@ func (p *ParseArea) Word(delimiter byte) ([]byte, error) {
6363
p.index = endIndex
6464
return nil, nil
6565
}
66+
escapeNext := false
6667
L:
6768
for ; endIndex < len(p.area); endIndex++ {
6869
c := p.area[endIndex]
@@ -73,7 +74,18 @@ L:
7374
}
7475
default:
7576
if c == delimiter {
76-
break L
77+
if escape {
78+
if !escapeNext {
79+
break L
80+
}
81+
} else {
82+
break L
83+
}
84+
}
85+
if !escapeNext && c == '\\' {
86+
escapeNext = true
87+
} else {
88+
escapeNext = false
7789
}
7890
}
7991
}

pkg/forth/primitive.go

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ func PrimitiveSetup(vm *VirtualMachine) error {
9393
if err != nil {
9494
return JoinEntryError(err, entry, "could not pop delimiter")
9595
}
96-
str, err := vm.ParseArea.Word(byte(n))
96+
str, err := vm.ParseArea.Word(byte(n), false)
9797
if err != nil {
9898
return JoinEntryError(err, entry, "could not parse string")
9999
}
@@ -127,7 +127,7 @@ func PrimitiveSetup(vm *VirtualMachine) error {
127127
if err != nil {
128128
return JoinEntryError(err, entry, "could not pop delimiter")
129129
}
130-
str, err := vm.ParseArea.Word(byte(n))
130+
str, err := vm.ParseArea.Word(byte(n), false)
131131
if err != nil {
132132
return JoinEntryError(err, entry, "could not parse string")
133133
}
@@ -158,6 +158,83 @@ func PrimitiveSetup(vm *VirtualMachine) error {
158158
return nil
159159
},
160160
},
161+
{
162+
name: "LWORDESCAPED", // returns a string and length
163+
goFunc: func(vm *VirtualMachine, entry *DictionaryEntry) error {
164+
delim16, err := vm.Stack.PopNumber()
165+
if err != nil {
166+
return JoinEntryError(err, entry, "could not pop delimiter")
167+
}
168+
delim := byte(delim16)
169+
bytes, err := vm.ParseArea.Word(delim, true)
170+
if err != nil {
171+
return JoinEntryError(err, entry, "could not parse string")
172+
}
173+
str := make([]byte, 0)
174+
translation := map[byte][]byte{
175+
'a': {7},
176+
'b': {8},
177+
'e': {27},
178+
'f': {12},
179+
'l': {10},
180+
'm': {13, 10},
181+
'n': {10}, // just LF
182+
'q': {34},
183+
'r': {13},
184+
't': {9},
185+
'v': {11},
186+
'z': {0},
187+
'"': {34},
188+
'\\': {92},
189+
// nonstandard: allow parsing a different delimiter
190+
delim: {delim},
191+
}
192+
var i int
193+
for i = 0; i < len(bytes)-1; i++ {
194+
b := bytes[i]
195+
if b == '\\' {
196+
i += 1
197+
next := bytes[i]
198+
replace, ok := translation[next]
199+
if ok {
200+
str = append(str, replace...)
201+
} else {
202+
return EntryError(entry, "unknown escape character %c %d", next, next)
203+
}
204+
} else {
205+
str = append(str, b)
206+
}
207+
}
208+
if i == len(bytes)-1 {
209+
str = append(str, bytes[i]) // append whatever the final byte is
210+
}
211+
var de DictionaryEntry
212+
counted := false
213+
cells, err := bytesToCells(str, counted)
214+
if err != nil {
215+
return JoinEntryError(err, entry, "could not convert bytes to cells")
216+
}
217+
w := WordForth{cells, &de}
218+
de = DictionaryEntry{
219+
Word: &w,
220+
Flag: Flag{Data: true},
221+
}
222+
c := CellAddress{
223+
Entry: &de,
224+
Offset: 0,
225+
UpperByte: false,
226+
}
227+
err = vm.Stack.Push(c)
228+
if err != nil {
229+
return JoinEntryError(err, entry, "could not push address")
230+
}
231+
err = vm.Stack.Push(CellNumber{uint16(len(str))})
232+
if err != nil {
233+
return JoinEntryError(err, entry, "could not push length")
234+
}
235+
return nil
236+
},
237+
},
161238
{
162239
name: "--CREATE-FORTH",
163240
goFunc: func(vm *VirtualMachine, entry *DictionaryEntry) error {

pkg/forth/suite_test.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1517,10 +1517,28 @@ func TestCoreExtensionSuite(t *testing.T) {
15171517
T{ 4 5 6 0 ROLL -> 4 5 6 }T
15181518
`,
15191519
},
1520-
// S\" not implemented
1520+
{
1521+
name: "S\\\"", // S\"
1522+
setup: `
1523+
T{ : GC4 S\" XY" ; -> }T
1524+
T{ : GC5 S\" A String"2DROP ; -> }T \ There is no space between the " and 2DROP
1525+
\ This test is from an extended mechanic, S\" can be interpreted
1526+
T{ S\" A String"2DROP -> }T \ There is no space between the " and 2DROP
1527+
T{ S\" \q" swap c@ -> 1 22 }T
1528+
1529+
`,
1530+
code: `
1531+
T{ GC4 SWAP DROP -> 2 }T
1532+
T{ GC4 DROP DUP C@ SWAP CHAR+ C@ -> 58 59 }T
1533+
T{ GC5 -> }T
1534+
T{ S\" \\ " type -> }T
1535+
T{ S\" \\\"" type -> }T
1536+
`,
1537+
expect: `\ \"`,
1538+
},
15211539
// SAVE-INPUT not implemented
15221540
// SOURCE-ID not implemented
1523-
// TO not implemented
1541+
// TO see VALUE 2VALUE
15241542
{
15251543
name: "TRUE",
15261544
code: `

pkg/forth/vm.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ func (vm *VirtualMachine) executeLine(bytes []byte) error {
221221
return err
222222
}
223223
for {
224-
word, err := vm.ParseArea.Word(' ')
224+
word, err := vm.ParseArea.Word(' ', false)
225225
if err != nil {
226226
return err
227227
}

0 commit comments

Comments
 (0)