Skip to content

Commit 561982e

Browse files
committed
Inline deferred words that cannot be altered in cross compiled output.
1 parent c35e261 commit 561982e

File tree

6 files changed

+80
-0
lines changed

6 files changed

+80
-0
lines changed

pkg/forth/builtin/02_core.f

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
POSTPONE @ \ compile @
3131
POSTPONE EXECUTE \ compile EXECUTE
3232
POSTPONE ; \ end the definition
33+
LAST SET-DEFERRED \ mark the new word as deferred
3334
;
3435

3536
: DEFER@ ( xt1 -- xt2 ) >BODY @ ;

pkg/forth/cell.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ func (c CellAddress) AddToList(u *Ulp) error {
9696
}
9797

9898
func (c CellAddress) BuildExecution(u *Ulp) (string, error) {
99+
c.Entry.Flag.inToken = true
99100
name, err := c.OutputReference(u)
100101
if err != nil {
101102
return "", err
@@ -168,6 +169,10 @@ func (c CellLiteral) AddToList(u *Ulp) error {
168169
}
169170
litref := c.reference(ref)
170171
u.literals[litref] = ref
172+
cellAddress, ok := c.cell.(CellAddress)
173+
if ok {
174+
cellAddress.Entry.Flag.inToken = true
175+
}
171176
return nil
172177
}
173178

pkg/forth/flag.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@ type Flag struct {
1717
recursive bool // This Forth word is recursive.
1818
visited bool // This Forth word has already been visited in this optimization pass.
1919
isExit bool // This assembly word is the EXIT word.
20+
isDeferred bool // This forth word is deferred.
21+
inToken bool // This word is compiled into a token somewhere (literal or data).
2022
}

pkg/forth/optimize.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ func (o *Optimizer) Optimize() error {
1515
if err != nil {
1616
return errors.Join(fmt.Errorf("could not tag recursion during optimization"), err)
1717
}
18+
err = o.removeDeferred()
19+
if err != nil {
20+
return errors.Join(fmt.Errorf("could not remove deferred word"), err)
21+
}
1822
// change calls at end of words to tail calls
1923
err = o.putTailCalls()
2024
if err != nil {
@@ -75,6 +79,36 @@ func (o *Optimizer) putTailCalls() error {
7579
return nil
7680
}
7781

82+
func (o *Optimizer) removeDeferred() error {
83+
for _, w := range o.u.forthWords {
84+
f := w.Entry.Flag
85+
// if this word was made with DEFER and it cannot be altered
86+
if f.isDeferred && !f.inToken {
87+
literal, ok := w.Cells[0].(CellLiteral)
88+
if !ok {
89+
return fmt.Errorf("could not read literal %s in %s", w.Cells[0], w)
90+
}
91+
address, ok := literal.cell.(CellAddress)
92+
if !ok {
93+
return fmt.Errorf("could not read address in %s", w)
94+
}
95+
data, ok := address.Entry.Word.(*WordForth)
96+
if !ok {
97+
return fmt.Errorf("error reading a deferred word, please file a bug report.")
98+
}
99+
if len(data.Cells) < 1 {
100+
return fmt.Errorf("deferred word not allocated, please file a bug report.")
101+
}
102+
embedded := data.Cells[0]
103+
exit := w.Cells[len(w.Cells)-1]
104+
w.Cells = w.Cells[0:2]
105+
w.Cells[0] = embedded
106+
w.Cells[1] = exit
107+
}
108+
}
109+
return nil
110+
}
111+
78112
func (o *Optimizer) clearVisited() {
79113
for _, w := range o.u.forthWords {
80114
w.Entry.ClearVisited()

pkg/forth/primitive.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,22 @@ func PrimitiveSetup(vm *VirtualMachine) error {
10561056
return nil
10571057
},
10581058
},
1059+
{
1060+
name: "SET-DEFERRED",
1061+
goFunc: func(vm *VirtualMachine, entry *DictionaryEntry) error {
1062+
cell0, err := vm.Stack.Pop()
1063+
if err != nil {
1064+
return PopError(err, entry)
1065+
}
1066+
cellAddr, ok := cell0.(CellAddress)
1067+
if !ok {
1068+
return EntryError(entry, "requires an address cell, found %s type %T", cell0, cell0)
1069+
}
1070+
flag := true
1071+
cellAddr.Entry.Flag.isDeferred = flag
1072+
return nil
1073+
},
1074+
},
10591075
{
10601076
name: "EXIT",
10611077
flag: Flag{

pkg/forth/ulpBuild.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,15 @@ func (u *Ulp) buildAssemblyHelper(vm *VirtualMachine, header string) (string, er
162162
if err != nil {
163163
return "", err
164164
}
165+
// rebuild the various lists because optimizations may have changed things
166+
err = u.clearLists()
167+
if err != nil {
168+
return "", err
169+
}
170+
err = u.buildLists(vmInitEntry)
171+
if err != nil {
172+
return "", err
173+
}
165174
// create the different assemblies
166175
asm, err := u.buildAssemblyWords()
167176
if err != nil {
@@ -270,6 +279,19 @@ func (u *Ulp) buildLists(entry *DictionaryEntry) error {
270279
return entry.AddToList(u)
271280
}
272281

282+
func (u *Ulp) clearLists() error {
283+
for _, w := range u.forthWords {
284+
w.Entry.Flag.addedToList = false
285+
}
286+
for _, w := range u.assemblyWords {
287+
w.Entry.Flag.addedToList = false
288+
}
289+
for _, w := range u.dataWords {
290+
w.Entry.Flag.addedToList = false
291+
}
292+
return nil
293+
}
294+
273295
func (u *Ulp) name(middle string, word string, addSuffix bool) string {
274296
// Forth words can have any character, replace them all
275297
fixed := u.replaceOtherChars(word)

0 commit comments

Comments
 (0)