konilo/konilo.pali

5152 lines
76 KiB
Text
Raw Normal View History

2024-05-23 20:02:11 +02:00
w================================================================
2024-05-23 20:00:14 +02:00
,dPYb, ,dPYb,
IP'`Yb IP'`Yb
I8 8I gg I8 8I
I8 8bgg, "" I8 8'
I8 dP" "8 ,ggggg, ,ggg,,ggg, gg I8 dP ,ggggg,
I8d8bggP" dP" "Y8 ,8" "8P" "8, 88 I8dP dP" "Y8
I8P' "Yb, i8' ,8I d8 8I 8I 88 I8P i8' ,8I
,d8 `Yb,,d8, ,d8P8P 8I Yb,_,88,_,d8b,_ ,d8, ,d8'
88P Y8P"Y8888P" 8I `Y88P""Y88P'"Y88P"Y8888P"
================================================================
Copyright (c) charles childers
================================================================
This is a small, pragmatic forth running on the ilo virtual
computer. The source is written in unu (see unu.retroforth.org),
a format for literate programming.
Commentary is mixed throughout the code. I'll try to keep the
comments brief, describing the provided functions and adding in
notes where I think they are relevant.
Let's take a brief moment to look at the source format. The code
is placed between fences (the ~~~ / ~~~) pairs. Anything outside
of these fences is commentary. In addition, I have comment lines
(starting with "c ") inside the code blocks.
The code here is assembly, for the `pali` assembler.
Pali assembly takes the form of:
<directive> <data>
Directives are a single character. The length of the data part
varies.
Directives are:
+-----------+--------------------------------------------------+
| Directive | Action |
+===========+==================================================+
| : | data is a label |
| i | data is an instruction bundle |
| r | data is a reference to a label |
| - | data is a reference to a label |
| d | data is an integer (decimal) value |
| s | data is a string |
| c | data is a comment; ignored by pali |
| * | data is an integer, the number is the amount |
| | of space to allocate |
+-----------+--------------------------------------------------+
Instruction bundles are groups of four ilo instructions. The
instruction names are identified by two letter codes. These are
shown in the following tables:
Opode Instruction Names Data Stack Effects
===== ================= ====================================
00-05 .. li du dr sw pu - -n n-nn n- nm-mn n-
06-11 po ju ca cc cj re -n a- a- af- af- -
12-17 eq ne lt gt fe st nn-f nn-f nn-f nn-f a-n na-
18-23 ad su mu di an or nn-n nn-n nn-nn nn-n nn-n nn-n
24-29 xo sl sr cp cy io nn-n nn-n nn-n nnn- nnn- n-
===== ================= ====================================
An important note: instructions that modify the instruction
pointer must only be followed by a non-op. These instructions
are: ju ca cc cj re
================================================================
On startup ilo will load a memory image (the "rom") into ram and
jump to address 0. I call `setup:sigls` to setup the environment
and then jump into `forth` to begin actually handling the user
environment.
ilo provides 65,536 memory words. For Konilo, these are
organized as:
+---------------+----------------------------------------------+
| Range | Used For |
+===============+==============================================+
| 00000 - 59999 | forth & user code / data |
| 60000 - 61025 | block buffer |
| 61026 - 61091 | string evaluation buffers |
| 61092 - 61104 | loop indices |
| 61105 - 61139 | number conversion |
| 61140 - 61141 | lexical scope |
| 61142 - 61206 | needs buffer |
| 61207 - 61335 | sigils table |
2024-05-23 20:02:11 +02:00
| 61336 - 63376 | reserved for future system use |
| 63377 - 64377 | misc. system variables |
2024-05-23 20:00:14 +02:00
| 64378 - 65406 | string buffers (8 x 127 chars) |
| 65407 - 65535 | text input buffer (127 chars) |
+---------------+----------------------------------------------+
I map names to these before proceeding. This will help keep
later code using these more readable.
~~~
c set to 1 after the actual start to leave room for a length
c cell
o 60001
: sys:buffers/block
2024-05-23 20:02:11 +02:00
o 61026
2024-05-23 20:00:14 +02:00
: sys:buffers/string-eval
o 61092
: sys:buffers/loops
o 61105
: sys:buffers/numeric-conversion
o 61140
: sys:buffers/scope
o 61142
: sys:buffers/needs
o 61207
: Sigils
2024-05-23 20:02:11 +02:00
c ! for fetching from variables
o 61240
r sigil:!
c # for numbers
o 61242
r sigil:#
c $ for characters
o 61243
r sigil:$
c & for pointers
o 61245
r sigil:&
c ' for strings
o 61246
r sigil:'
c ( for comments
o 61247
r drop
c : for colon definitions
o 61265
r sigil::
c @ for fetching from variables
o 61271
r sigil:@
c \ for creating names
o 61299
r sigil:\
2024-05-23 20:00:14 +02:00
o 61336
: sys:buffers/reserved
2024-05-23 20:02:11 +02:00
o 63377
: sys:buffers/variables
2024-05-23 20:00:14 +02:00
o 64378
: sys:buffers/strings+arrays
o 65407
: sys:buffers/input
o 65408
: sys:buffers/input/text
o 65409
: sys:buffers/input/text+1
~~~
2024-05-23 20:02:11 +02:00
~~~
c A number of parts of the system make use of variables. I store
c these in the system buffers
o 63377
c Compiler tracks the compiler state (-1 for on, 0 for off)
: Compiler
d 0
: BaseBlock
c BaseBlock is used by the block i/o words
d 0
: Blocks
c number of blocks available
d 16
: Block
c number of the current block
d 0
c These are used to construct the choice table used by `choose`
: choice:true
d 0
: choice:false
d 0
c ACount stores the number of matches found by `a:indices`
: ACount
d 0
c Current is used by `indexed-times` to track the current loop
c depth
: Current
d 0
c Source, End, and At are used by the string evaluation code.
: Source
d 0
: End
d 0
: At
d 0
c s:Length & s:Current are used by `s:to-n`
: s:Length
d 0
: s:Current
d 0
c Next-String holds the number for the next string in the temp.
c pool
: Next-String
d 0
c Used by `needs`
: needs.Len
d 0
~~~
2024-05-23 20:00:14 +02:00
~~~
2024-05-23 20:02:11 +02:00
c After creating the labels for the system buffers, return to
c address 0 to start the actual code.
c On startup this calls a function to populate the default
c sigils, then jumps into forth.
2024-05-23 20:00:14 +02:00
o 0
i liju....
r forth
~~~
================================================================
With the configuration out of the way, I move on to implementing
the various words.
Being a Forth, functions or subroutines are called `words`.
Information about these is stored in a `dictionary`.
The first set of words I'm defining map directly to the
instructions in the ilo computer.
~~~
2024-05-23 20:02:11 +02:00
c `dup` duplicates the top item on the data stack.
c
c +---+ +---+---+
c | 1 | ==> | 1 | 1 |
c +---+ +---+---+
c
2024-05-23 20:00:14 +02:00
: dup
c (n-nn)
i dure....
2024-05-23 20:02:11 +02:00
c `drop` discards the top item on the data stack.
c
c +---+---+ +---+
c | 1 | 2 | ==> | 1 |
c +---+---+ +---+
c
2024-05-23 20:00:14 +02:00
: drop
c (n-)
i drre....
2024-05-23 20:02:11 +02:00
c `swap` exchanges the positions of the top two values on the
c data stack.
c
c +---+---+ +---+---+
c | 1 | 2 | ==> | 2 | 1 |
c +---+---+ +---+---+
c
2024-05-23 20:00:14 +02:00
: swap
c (nm-mn)
i swre....
2024-05-23 20:02:11 +02:00
c `eq?` compares two items on the stack for equality. If they
c are equal, it will push a true (-1) flag. If they are not
c equal, it pushes false (0) instead.
c
c +---+---+ +----+
c | 1 | 1 | ==> | -1 |
c +---+---+ +----+
c
c +---+---+ +----+
c | 2 | 1 | ==> | 0 |
c +---+---+ +----+
c
2024-05-23 20:00:14 +02:00
: eq?
c (nn-f)
i eqre....
2024-05-23 20:02:11 +02:00
c `-eq?` compares two items on the stack for inequality. If they
c are not equal, it will push a true (-1) flag. If they are
c equal, it pushes false (0) instead.
c
c +---+---+ +----+
c | 1 | 1 | ==> | 0 |
c +---+---+ +----+
c
c +---+---+ +----+
c | 2 | 1 | ==> | -1 |
c +---+---+ +----+
c
2024-05-23 20:00:14 +02:00
: neq?
c (nn-f)
i nere....
2024-05-23 20:02:11 +02:00
c +---+---+ +----+
c | 1 | 1 | ==> | 0 |
c +---+---+ +----+
c
c +---+---+ +----+
c | 2 | 1 | ==> | 0 |
c +---+---+ +----+
c
c +---+---+ +----+
c | 1 | 2 | ==> | -1 |
c +---+---+ +----+
c
2024-05-23 20:00:14 +02:00
: lt?
c (nn-f)
i ltre....
2024-05-23 20:02:11 +02:00
c +---+---+ +----+
c | 1 | 1 | ==> | 0 |
c +---+---+ +----+
c
c +---+---+ +----+
c | 2 | 1 | ==> | -1 |
c +---+---+ +----+
c
c +---+---+ +----+
c | 1 | 2 | ==> | 0 |
c +---+---+ +----+
c
2024-05-23 20:00:14 +02:00
: gt?
c (nn-f)
i gtre....
: fetch
c (p-n)
i fere....
: store
c (np-)
i stre....
2024-05-23 20:02:11 +02:00
c `n:add` adds two values, returning the result.
c
c +---+---+ +---+
c | 1 | 2 | ==> | 3 |
c +---+---+ +---+
c
2024-05-23 20:00:14 +02:00
: n:add
c (nn-n)
i adre....
2024-05-23 20:02:11 +02:00
c `n:sub` subtracts two values, returning the result.
c
c +---+---+ +---+
c | 3 | 1 | ==> | 2 |
c +---+---+ +---+
c
2024-05-23 20:00:14 +02:00
: n:sub
c (nn-n)
i sure....
2024-05-23 20:02:11 +02:00
c `n:mul` multiplies two values, returning the result.
c
c +---+---+ +---+
c | 2 | 3 | ==> | 6 |
c +---+---+ +---+
c
2024-05-23 20:00:14 +02:00
: n:mul
c (nn-n)
i mure....
2024-05-23 20:02:11 +02:00
c `n:divmod` divides two values, returning the result and
c remainer.
c
2024-05-23 20:00:14 +02:00
: n:divmod
c (nn-nn)
i dire....
: and
c (nn-n)
i anre....
: or
c (nn-n)
i orre....
: xor
c (nn-n)
i xore....
: shift-left
c (nn-n)
i slre....
: shift-right
c (nn-n)
i srre....
2024-05-23 20:02:11 +02:00
c `io` triggers an i/o operation. The basic ilo system provides
c just a few i/o devices.
c
c Using this is a matter of pushing any needed values to the
c stack, pushing the i/o device number, then calling `io`. For
c a list of devices:
c
c +--------+--------------------------------------+----------+
c | Device | Action | Required |
c +========+======================================+==========+
c | 0 | Display a character. Consumes a | Yes |
c | | character from the stack. | |
c | 1 | Read a character from the input | Yes |
c | | device. The character is pushed to | |
c | | the stack. | |
c | 2 | Read a block into memory. | Yes |
c | 3 | Write memory to a block. | Yes |
c | 4 | Write all memory to an image/rom. | No |
c | 5 | Reload the image/rom, and jump to | Yes |
c | | address 0. Also empty all stacks. | |
c | 6 | End execution. On a hosted system, | No |
c | | exit ilo. If native, suspend | |
c | | execution. | |
c | 7 | Obtain stack depths. Pushes the data | Yes |
c | | depth then the address depth. | |
c +--------+-------------------------------------------------+
c
2024-05-23 20:00:14 +02:00
: io
c (...n-?)
i iore....
2024-05-23 20:02:11 +02:00
c `copy` copies "n" cells of memory starting at p1 to p2. For
c example:
c
c +---+---+---+---+---+---+---+---+---+
c | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
c +---+---+---+---+---+---+---+---+---+
c | A | B | C | D | D | E | A | B | C |
c +---+---+---+---+---+---+---+---+---+
c
c With a stack of:
c
c #2 #6 #3
c
c This would change memory to:
c
c +---+---+---+---+---+---+---+---+---+
c | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
c +---+---+---+---+---+---+---+---+---+
c | A | B | C | D | D | B | C | D | C |
c +---+---+---+---+---+---+---+---+---+
c
c The ilo specification does not require the system to support
c overlapping copies. If the memory areas would overlap, test
c and write a specific word to handle the overlap if needed.
c
2024-05-23 20:00:14 +02:00
: copy
c (ppn-)
i cyre....
2024-05-23 20:02:11 +02:00
c `compare` will compare "n" cells of memory starting at p1 and
c p2.
c
c As an example:
c
c +---+---+---+---+---+---+---+---+---+
c | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
c +---+---+---+---+---+---+---+---+---+
c | A | B | C | D | D | E | A | B | C |
c +---+---+---+---+---+---+---+---+---+
c
c With a stack of:
c
c #1 #7 #3
c
c This would return true (-1) as the ABC sequences at 1 & 7
c match.
c
c But a stack of:
c
c #1 #5 #3
c
c Would return false (0) since the sequences (ABC & DEA) do not
c match.
c
2024-05-23 20:00:14 +02:00
: compare
c (ppn-)
i cpre....
~~~
================================================================
~~~
2024-05-23 20:02:11 +02:00
c The next set makes a copy of the second item on the stack,
c puts a copy of the top item under the second item, discards
c the second item, discards the top two items, and makes copies
c of the top two items.
c
c +---+---+ +---+---+---+
c | 1 | 2 | ==> | 1 | 2 | 1 |
c +---+---+ +---+---+---+
c
2024-05-23 20:00:14 +02:00
: over
c (xy-xyx)
i puduposw
i re......
2024-05-23 20:02:11 +02:00
c +---+---+ +---+---+---+
c | 1 | 2 | ==> | 2 | 1 | 2 |
c +---+---+ +---+---+---+
c
2024-05-23 20:00:14 +02:00
: tuck
c (xy-yxy)
i dupuswpo
i re......
2024-05-23 20:02:11 +02:00
c +---+---+ +---+
c | 1 | 2 | ==> | 2 |
c +---+---+ +---+
c
2024-05-23 20:00:14 +02:00
: nip
c (xy-y)
i swdrre..
2024-05-23 20:02:11 +02:00
c `drop-pair` discards the top two stack items. (In some Forths
c this is called "2drop")
c
c +---+---+
c | 1 | 2 | ==>
c +---+---+
c
2024-05-23 20:00:14 +02:00
: drop-pair
c (xy-)
i drdrre..
2024-05-23 20:02:11 +02:00
c `dup-pair` makes a copy of the top two stack items. (In some
c Forths this is called "2dup")
c
c +---+---+ +---+---+---+---+
c | 1 | 2 | ==> | 1 | 2 | 1 | 2 |
c +---+---+ +---+---+---+---+
c
2024-05-23 20:00:14 +02:00
: dup-pair
c (xy-xyxy)
i puduposw
i puduposw
i re......
~~~
================================================================
~~~
2024-05-23 20:02:11 +02:00
c There is a second stack, which holds return addresses. It's
c used for subroutine calls, but you can temporarily store data
c there as well. `push` and `pop` provide access to this stack.
c
c Note that this is more complicated than simply pushing values.
c Thess definitions take the direct threading implementation
c into account to ensure that code does not crash when using
c them, as long as the `push` and `pop` operations are paired
c correctly.
c
2024-05-23 20:00:14 +02:00
: push
c (n-) (A:-n)
i poswposw
i pupupure
: pop
c (-n) (A:n-)
i popoposw
i puswpure
2024-05-23 20:02:11 +02:00
c A better way to work with data items on the address stack is
c to use `dip` and `sip`. Using these ensures that the address
c stack remains balanced, and is more idomatic.
c
c In this case, `dip` removes the value before calling the
c function, but `sip` does not. In both, the value is restored
c to the stack after the function return.
c
2024-05-23 20:00:14 +02:00
: dip
c (np-n)
i swpuca..
i pore....
: sip
c (np-n)
i puduposw
i puca....
i pore....
2024-05-23 20:02:11 +02:00
c For those familiar with traditional Forth systems, the `push`,
c `pop`, `dip`, and `sip` are similar to:
c
c +--------------+---------------+
c | Konilo | Traditional |
c +==============+===============+
c | push ... pop | >R ... R> |
c | [ ... ] dip | >R ... R> |
c | [ ... ] sip | dup >R ... R> |
c +--------------+---------------+
c
~~~
2024-05-23 20:00:14 +02:00
================================================================
~~~
2024-05-23 20:02:11 +02:00
c Konilo provides a number of words for controlling the flow
c of execution.
c
c The initial building blocks are calls, jumps, and returns. A
c call takes an address and jumps to it, saving the previous
c execution address on the return (or address) stack. Return
c will take the address from the return stack and jump back to
c it. A jump is like call, but does not save a return address
c and thus can not be used with return.
c
2024-05-23 20:00:14 +02:00
: jump
c (p-)
i popopodr
i drdrju..
: call
c (p-)
i ju......
~~~
================================================================
~~~
2024-05-23 20:02:11 +02:00
c `?jump` branches to a function if a passed flag is true
c (non-zero).
2024-05-23 20:00:14 +02:00
: ?jump
i swline..
d 0
i licj....
r jump
i drre....
~~~
================================================================
# Loops
~~~
2024-05-23 20:02:11 +02:00
c The `times` combinator takes a count and an address. It then
c runs the function the specified number of times.
c
c An example:
c
c #0 #10 [ dup n:put n:inc ] times drop
2024-05-23 20:00:14 +02:00
: times
c (np-)
i sw......
: times.loop
i dulieq..
d 0
i licj....
r times.done
i lisupudu
d 1
i puca....
i popoliju
r times.loop
: times.done
i drdrre..
2024-05-23 20:02:11 +02:00
c The `until` combinator takes a function address. The function
c must leave a flag on the stack. `until` runs the function
c repeatedly until the flag is true (non-zero).
2024-05-23 20:00:14 +02:00
: until
c (p-)
c pointer must return a value to be used as a flag
i dupuca..
i poswlieq
d 0
i licj....
r until
i drre....
2024-05-23 20:02:11 +02:00
c The `while` combinator takes a function address. The function
c must leave a flag on the stack. `while` runs the function
c repeatedly until the flag is false (zero).
2024-05-23 20:00:14 +02:00
: while
c (p-)
c pointer must return a value to be used as a flag
i dupuca..
i poswline
d 0
i licj....
r while
i drre....
2024-05-23 20:02:11 +02:00
c `forever` runs a function repeatedly in an unending loop.
2024-05-23 20:00:14 +02:00
: forever
c (p-)
i dupuca..
i poliju..
r forever
~~~
================================================================
~~~
2024-05-23 20:02:11 +02:00
c `restart` triggers the reset functionality in ilo. This will
c reload the image, reset the stack pointers to empty, and then
c jump to address 0.
2024-05-23 20:00:14 +02:00
: restart
c (...-)
i liio....
d 5
2024-05-23 20:02:11 +02:00
c `bye` triggers the shutdown functionality it ilo. On a hosted
c system, this will normally return to the system shell.
2024-05-23 20:00:14 +02:00
: bye
c (-)
i liio....
d 6
~~~
================================================================
# Block I/O
~~~
: block:load
c (np-)
i pulifead
r BaseBlock
i poliiore
d 2
: block:save
c (np-)
i pulifead
r BaseBlock
i poliiore
d 3
~~~
# Maths
~~~
2024-05-23 20:02:11 +02:00
c `n:zero?` and `n:-zero?` compare a value to zero.
2024-05-23 20:00:14 +02:00
: n:zero?
c (n-f)
i lieqre..
d 0
: n:-zero?
c (n-f)
i linere..
d 0
2024-05-23 20:02:11 +02:00
c `n:divmod` wraps the instruction that divides and returns the
c quotient and remainder. These separate out the functionality.
2024-05-23 20:00:14 +02:00
: n:mod
c (n-n)
i didrre..
: n:div
c (n-n)
i diswdrre
2024-05-23 20:02:11 +02:00
c `n:min` returns the lesser of two values; `n:max` returns the
c greater.
2024-05-23 20:00:14 +02:00
: n:min
c (nn-n)
2024-05-23 20:02:11 +02:00
c inline dup-pair
i puduposw
i puduposw
2024-05-23 20:00:14 +02:00
i ltlilili
r drop
r nip
r choose
i ju......
: n:max
c (nn-n)
2024-05-23 20:02:11 +02:00
c inline dup-pair
i puduposw
i puduposw
2024-05-23 20:00:14 +02:00
i gtlilili
r drop
r nip
r choose
i ju......
2024-05-23 20:02:11 +02:00
c I use `n:min` and `n:max` to implement `n:limit`, which takes
c a value, and an upper and lower limit. It will return the
c value if it is in range, or the closest value in range if
c outside it.
2024-05-23 20:00:14 +02:00
: n:limit
i swlilica
r n:min
r dip
i liju....
r n:max
: not
c (n-n)
i lixore..
d -1
~~~
================================================================
# Memory
~~~
2024-05-23 20:02:11 +02:00
c `fetch-next` takes an address. It fetches the value stored at
c the address, and returns both this and the next address.
2024-05-23 20:00:14 +02:00
: fetch-next
c (p-pn)
i duliadsw
d 1
i fere....
2024-05-23 20:02:11 +02:00
c `store-next` takes a value and an address. It stores the value
c in the address, then returns the next address.
2024-05-23 20:00:14 +02:00
: store-next
c (np-p)
i duliadpu
d 1
i stpore..
2024-05-23 20:02:11 +02:00
c `fill` is used to fill a region of memory with a specified
c value.
c
c Takes a value, and address, and a length.
2024-05-23 20:00:14 +02:00
: fill
c (npn-)
2024-05-23 20:02:11 +02:00
i pu......
c inline dup-pair
i puduposw
i puduposw
2024-05-23 20:00:14 +02:00
i stliadpo
d 1
i lisuduli
d 1
d 0
i eqlicj..
r fill.done
i liju....
r fill
: fill.done
i drdrdrre
2024-05-23 20:02:11 +02:00
c `gc` runs a function, saving and restoring the value of `Free`
c This replaces the pattern of `@Free [ ... ] dip !Free` with
c `[ ... ] gc`, a shorter and more readable approach.
2024-05-23 20:00:14 +02:00
: gc
c (p-)
i lifepuca
r Free
i polistre
r Free
~~~
# Conditionals
~~~
2024-05-23 20:02:11 +02:00
c `choose` takes a flag and two function addresses. If the flag
c is true (non-zero), it runs the first function. If false
c (zero), it will run the second.
c
c This works by storing the pointers in a table. A pointer to
c the second entry in the table (the false operation) is then
c placed on the stack, and the flag is added to the pointer. If
c true (-1) it will then point to the true function. I can then
c just fetch and branch.
c
c A small bit of logic is inserted to ensure the flags are
c either -1 or 0.
2024-05-23 20:00:14 +02:00
: choose
c (fpp-)
c store possible branch targets
i listlist
r choice:false
r choice:true
c normalize the flag to 0 or -1, then add this to the "false"
c target address
i lineliad
d 0
r choice:false
c branch to the selected target
i feju....
2024-05-23 20:02:11 +02:00
c `if` and `-if` each take a flag and a function pointer. `if`
c will run the function if the flag is true. `-if` will run it
c if the flag is false.
2024-05-23 20:00:14 +02:00
: -if
c (fp-)
i pulieqpo
d 0
: if
c (fp-)
i cj......
i re......
~~~
`lteq?` checks to see if a value is less than or equal to
another value. Given:
| 1 | 2 |
This would return true (-1) since 1 is less than or equal to 2.
~~~
: lteq?
c (nn-f)
2024-05-23 20:02:11 +02:00
c inline dup-pair
i puduposw
i puduposw
2024-05-23 20:00:14 +02:00
i eqpultpo
i orre....
~~~
`gteq?` is like `lteq?`, but checks for greater than or equal
to.
~~~
: gteq?
c (nn-f)
i swliju..
r lteq?
~~~
# Dictionary
The dictionary maps names to functions. It is set up as a simple
linked list, with the following fields:
+---+-----------+---------------------------+
| # | Accessor | Description |
+===+===========+===========================+
| 0 | d:link | link to previous entry |
| 1 | d:hash | hash of word name |
| 2 | d:address | pointer to word start |
+---+-----------+---------------------------+
Note that the name of the word is not actually saved, just a
hash of it. This saves considerable space and makes lookups
fast, but comes at the cost of some flexibility.
Prior implementations of Konilo have used *word classes* to
handle different word behaviors. This implementation does not.
You have normal words or immediate ones, with all other cases
being handled by use of sigils.
Immediate words are kept to a minimum:
; [ ]
`d:lookup` takes a string with the word name and returns the
address of the dictionary header, or zero if the word is not
found in the dictionary.
~~~
: d:lookup
c (s-d)
c get the hash of the nam we are trying to find
i lica....
r a:hash
c push a pointer to the most recent entry to the stack,
c then run a loop looking for the end. When done, clean
c up the stack and return a pointer to the dictionary
c header (or 0 if not found)
i lifelica
r Latest
r d:lookup.next
i swdrre..
: d:lookup.next
c the lookup loop begins
c duplicate the top two entries on the stack
i puduposw
i puduposw
c get the contents of the `d:hash` field; compare
c to the desired hash. If they match, branch to
c "d:lookup.done".
i liadfene
d 1
i dulieqli
d 0
r d:lookup.done
i cj......
c if not a match, we fetch the next header. If it
c is zero, no other entries remain. Otherwise we
c then repeat the loop.
i drfedudu
i lieqlicj
d 0
r d:lookup.done
i drliju..
r d:lookup.next
c finish up and return to `d:lookup`
: d:lookup.done
i drre....
~~~
# Dictionary Fields
Given a dictionary pointer, these return the address of each
field.
~~~
: d:link
c (d-p)
i re......
: d:hash
c (d-p)
i liadre..
d 1
: d:address
c (d-p)
i liadre..
d 2
~~~
`d:exists?` checks for the presence of a word. It uses
`d:lookup` to seach for a word, then returns either true (-1)
or false (0).
~~~
: d:exists?
c (s-f)
i lica....
r d:lookup
i linere..
d 0
~~~
# Arrays & Strings
Arrays are linear sequences of values. They start with a count
and are immediately followed by the values.
Strings are arrays where the values are the ASCII character
codes.
E.g.,
'hello
In memory is:
5 104 101 108 108 111
The array words are placed in the `a:` namespace. They are
mirrored under the `s:` namespace for strings.
`a:length` takes a pointer to an array and returns the length.
~~~
: a:length
c (a-n)
i fere....
~~~
`a:copy` makes a copy of an array. Provide a pointer to the
array and a pointer to the destination.
:a:copy over a:length n:inc copy ;
~~~
: a:copy
c (ap-)
i puduposw
2024-05-23 20:02:11 +02:00
i feliadcy
2024-05-23 20:00:14 +02:00
d 1
2024-05-23 20:02:11 +02:00
i re......
2024-05-23 20:00:14 +02:00
~~~
`a:dup` takes a pointer to an array. It makes a copy of this
and then returns a pointer to the new one.
:a:dup here [ dup a:length comma &comma a:for-each ] dip ;
The copy is made at `here`, so using this can generate garbage.
~~~
: a:dup
c (a-a)
c get a pointer to `here`, move it out of the way
i lifepu..
r Free
c get the length, comma it to the new array
i dulica..
r a:length
i lica....
r comma
c run `comma` against each value, copying them to the
c new array
i lilica..
r comma
r a:for-each
c restore the pointer to the new array, and we're done
i pore....
~~~
`a:th` takes a pointer to an array and an index. It then returns
the address that the index corresponds to.
~~~
: a:th
c (an-p)
i liadadre
d 1
~~~
`a:fetch` takes a pointer to an array and an index. It then
returns the value stored in the array at the specified index.
~~~
: a:fetch
c (an-n)
i liadadfe
d 1
i re......
~~~
`a:store` takes a value, a pointer to an array, and an index.
It stores the value into the array at the specified index.
~~~
: a:store
c (nan-)
i liadadst
d 1
i re......
~~~
`a:hash` returns a hash (using a modified djb2 model) for the
values in an array.
This is a very important function as the dictionary is stored
as hashes of the word names. If you alter this, please be aware
that you will need to update the dictionary hashes, and likely
some code in blocks that use hashes as well.
The hashes are constrained due to the use of 32-bit signed
integers. If using a host system with larger cells, you will
either need to update the dictionary and other places the hashes
are used, or ensure that your system handles overflow and wrap-
around as expected by ilo.
~~~
: a:hash
c (a-n)
i liswlili
d 5381
r a:hash.values
r a:for-each
i ju......
: a:hash.values
i swlimuad
d 33
i re......
~~~
`a:for-each` takes an array and a pointer to a function. It
then pushes each value from the array to the stack and runs
the code indicated by the pointer for each value.
Assuming an array of and a pointer to `[ n:put nl ]`:
here #3 comma $98 comma $99 comma $100 comma
[ n:put nl ] a:for-each
Is the same as doing:
$98 n:put nl
$99 n:put nl
$100 n:put nl
~~~
: a:for-each
c (ap-)
c get the length and a pointer to the first data element
c in the array
i swlica..
r fetch-next
c reorder the stack. We are now:
c pointer-to-data pointer-to-function length
i puswpo..
c run the actual loop over the data, then clean up
i lica....
r a:for-each.value
i drdrdrre
: a:for-each.value
c (ppn-ppn)
c is there remaining data?
i duline..
d 0
i dulieq..
d 0
c if not, branch to the exit point
i licj....
r a:for-each.value.done
c if so, move the length and function pointers out of the
c way and get a pointer to the next data item and the current
c item
i dr......
i pupulica
r fetch-next
c reorder the stack to: value ptr-function
c then call the function
i swpodupu
i swpuca..
c restore the various pointers (data, function) and length,
c then decrease the length by 1.
i popopoli
d 1
i suliju..
c and repeat until completed
r a:for-each.value
: a:for-each.value.done
i drre....
~~~
Use `a:prepend` to merge two arrays into a new one. Takes two
pointers, with the items from the second one being placed
before the items in the first.
:a:prepend
here
&swap dip swap
&swap dip swap
dup-pair a:length swap a:length n:add comma
&comma a:for-each &comma a:for-each ;
~~~
: a:prepend
c (aa-a)
2024-05-23 20:02:11 +02:00
i lifepusw
r Free
i poswpusw
i poswlica
r dup-pair
2024-05-23 20:00:14 +02:00
i lica....
2024-05-23 20:02:11 +02:00
r a:length
i swlica..
r a:length
i adlica..
2024-05-23 20:00:14 +02:00
r comma
2024-05-23 20:02:11 +02:00
i lilica..
2024-05-23 20:00:14 +02:00
r comma
2024-05-23 20:02:11 +02:00
r a:for-each
i liliju..
r comma
r a:for-each
~~~
2024-05-23 20:00:14 +02:00
Use `a:append` to merge two arrays into a new one. Takes two
pointers, with the items from the first one being placed before
the items in the second.
~~~
: a:append
c (aa-a)
i swliju..
r a:prepend
~~~
To reverse the order of items in an array, use `a:reverse`. It
takes a pointer to an array and returns a new array.
A quick Forth take on this:
:a:reverse (a-a)
here [
dup fetch dup comma allot (length+space)
here n:dec swap [ over store n:dec ] a:for-each drop
] dip ;
~~~
: a:reverse
c (a-a)
i lifepu..
r Free
i dufedu..
i lica....
r comma
i lica....
r allot
i lifelisu
r Free
d 1
i swlilica
r a:reverse.values
r a:for-each
i drpo....
i dulica..
r s:temp
i swlistre
r Free
: a:reverse.values
i puduposw
i stlisure
d 1
~~~
2024-05-23 20:02:11 +02:00
Arrays are important, being the data structure used for strings
and a variety of other things. This is a very simple way to
create them.
~~~
: a:make
c (...n-a)
i lifepu..
r Free
i dulica..
r comma
i lilica..
r comma
r times
i pore....
: a:make/temp
c (...n-a)
i liliju..
r a:make/temp.internal
r gc
: a:make/temp.internal
i lica....
r a:make
i liju....
r s:temp
~~~
`a:reduce` will iterate over an array and value, applying a
function to reduce the array down to a single value. For instance,
to sum an array:
&array #0 &n:add a:reduce
~~~
: a:reduce
c (anq-n)
i puswpoli
r a:for-each
i ju......
~~~
To map a function to each value in an array, I provide `a:map`.
This modifies the original array, not a copy of it.
~~~
: a:map
c (aq-a)
i swdupu..
i lica....
r fetch-next
i lilica..
r a:map.inner
r times
i drdrpore
: a:map.inner
i dupufepu
c "over"
i duposwca
i polilica
r store
r sip
i liadre..
d 1
~~~
The next couple of words allow for extracting portions of an
array into a new array. The subsets will be stored in the
temporary buffers.
~~~
: a:middle
c (afl-a)
c here push dup comma push n:inc n:add pop here fetch swap copy
c pop dup !Free s:temp
i lifepu..
r Free
i dulica..
r comma
i puliadad
d 1
i polifesw
r Free
i cypoduli
r Free
i stliju..
r s:temp
: a:left
c (an-a)
c #0 swap a:middle
i liswliju
d 0
r a:middle
: a:right
c (an-a)
c over fetch over n:sub swap a:middle
i puduposw
i fe......
i puduposw
i suswliju
r a:middle
~~~
2024-05-23 20:00:14 +02:00
`allot` reserves memory. Provide it a number and it'll reserve
that amount of memory. You can reclaim memory by passing in a
negative value.
Note that memory is allocated at `here`, in a linear fashion.
Expect issues if you reclaim memory after something else also
allocates memory.
:allot @Free n:add !Free ;
~~~
: allot
c (n-)
i lifead..
r Free
i listre..
r Free
~~~
2024-05-23 20:02:11 +02:00
Input tokens are whitespace delimited. This presents an issue
for strings: how to add spaces?
2024-05-23 20:00:14 +02:00
2024-05-23 20:02:11 +02:00
The solution used here is to replace (via `s:map`) underscores
with spaces. I've been doing this now for many years, and it's
not proven to be a problem. The `sigil:'` will use the latest
`s:rewrite` it can find, so you can redefine this to make other
changes if you need to.
2024-05-23 20:00:14 +02:00
2024-05-23 20:02:11 +02:00
~~~
: s:rewrite
c (s-)
i liliju..
r s:rewrite.spaces
r a:map
: s:rewrite.spaces
i dulieqli
d 95
r s:rewrite.replace
i liju..
r if
2024-05-23 20:00:14 +02:00
2024-05-23 20:02:11 +02:00
: s:rewrite.replace
i drlire..
d 32
2024-05-23 20:00:14 +02:00
~~~
2024-05-23 20:02:11 +02:00
`a:indices` returns an array with the locations of a value that
matches the passed value. `a:index` returns the first location
that matches.
~~~
: a:indices
c (av-a)
c reset the count
i lilist..
2024-05-23 20:00:14 +02:00
d 0
2024-05-23 20:02:11 +02:00
r ACount
i lifepu..
r Free
i swlilica
d 0
r comma
i lilica..
r a:indices.iterate
r a:for-each
i drpolife
r Free
i puduposw
i sulisu..
d 1
i puduposw
i stdulica
r s:temp
i swlistre..
r Free
2024-05-23 20:00:14 +02:00
2024-05-23 20:02:11 +02:00
: a:indices.iterate
i puduposw
i eqlilica
r a:indices.record
r if
i liliju..
r ACount
r v:inc
2024-05-23 20:00:14 +02:00
2024-05-23 20:02:11 +02:00
: a:indices.record
i lifeliju
r ACount
r comma
: a:index
c (av-n)
i lifepu..
r Free
2024-05-23 20:00:14 +02:00
i lica....
2024-05-23 20:02:11 +02:00
r a:indices
i lilica..
d 0
r a:fetch
i polistre
r Free
2024-05-23 20:00:14 +02:00
~~~
~~~
2024-05-23 20:02:11 +02:00
: a:contains?
c (an-f)
i swliswli
d 0
r a:contains?.inner
i lica....
r a:for-each
i swdrre..
2024-05-23 20:00:14 +02:00
2024-05-23 20:02:11 +02:00
: a:contains?.inner
i swpu....
i puduposw
i eqpoorre
2024-05-23 20:00:14 +02:00
~~~
2024-05-23 20:02:11 +02:00
`a:filter` runs a quote against each value in an array. The quote
needs to consume the value and return a single flag. If true, the
value is added to a new array.
2024-05-23 20:00:14 +02:00
~~~
2024-05-23 20:02:11 +02:00
: a:filter
i lifepuli
r Free
r a:filter.action
i lica....
r curry
i lifepu..
r Free
i puduposw
i felica..
r comma
i lica....
r a:for-each
i polifepu
r Free
i duposwsu
i lisupudu
d 1
i poswst..
i lica....
r s:temp
i polistre
r Free
2024-05-23 20:00:14 +02:00
2024-05-23 20:02:11 +02:00
: a:filter.action
i puduposw
i puca....
i poswlili
r comma
r drop
i liju....
r choose
~~~
2024-05-23 20:00:14 +02:00
2024-05-23 20:02:11 +02:00
~~~
: c:lowercase?
c (c-f) $a $z n:between? ;
i lililiju
d 97
d 122
r n:between?
2024-05-23 20:00:14 +02:00
2024-05-23 20:02:11 +02:00
: c:uppercase?
c (c-f) $A $Z n:between? ;
i lililiju
d 65
d 90
r n:between?
2024-05-23 20:00:14 +02:00
2024-05-23 20:02:11 +02:00
: c:to-upper
c (c-c) dup c:lowercase? [ #32 n:sub ] if ;
i dulica..
r c:lowercase?
i liliju..
r c:to-upper.change
r if
2024-05-23 20:00:14 +02:00
2024-05-23 20:02:11 +02:00
: c:to-upper.change
i lisure..
d 32
2024-05-23 20:00:14 +02:00
2024-05-23 20:02:11 +02:00
: c:to-lower
c (c-c) dup c:uppercase? [ #32 n:add ] if ;
i dulica..
r c:uppercase?
i liliju..
r c:to-lower.change
r if
2024-05-23 20:00:14 +02:00
2024-05-23 20:02:11 +02:00
: c:to-lower.change
i liadre..
d 32
2024-05-23 20:00:14 +02:00
2024-05-23 20:02:11 +02:00
: c:to-s
c (c-s) '_ s:temp tuck #0 s:store ;
i lilica..
r c:to-s.temp
r s:temp
i luca....
r tuck
i liliju..
2024-05-23 20:00:14 +02:00
d 0
2024-05-23 20:02:11 +02:00
r a:store
2024-05-23 20:00:14 +02:00
2024-05-23 20:02:11 +02:00
: c:to-s.temp
s _
2024-05-23 20:00:14 +02:00
2024-05-23 20:02:11 +02:00
: s:to-upper
c (s-s) [ s:dup &c:to-upper s:map s:temp ] gc
i lifepu..
r Free
2024-05-23 20:00:14 +02:00
i lica....
2024-05-23 20:02:11 +02:00
r a:dup
i lilica..
r c:to-upper
r a:map
i lica....
r s:temp
i polistre
r Free
: s:to-lower
c (s-s) [ s:dup &c:to-lower s:map s:temp ] gc ;
i lifepu..
r Free
i lica....
r a:dup
i lilica..
r c:to-lower
r a:map
i lica....
r s:temp
i polistre
r Free
2024-05-23 20:00:14 +02:00
~~~
~~~
2024-05-23 20:02:11 +02:00
: bi
c (xqq-) &sip dip call ;
i pulica..
r sip
i poju....
: bi@
c (xyq-) dup bi* ;
c using fall-through here
i du......
: bi*
c (xyqq-) &dip dip call ;
i pulica..
r dip
i poju....
: tri
c (xqqq-) [ &sip dip sip ] dip call ;
i pulilica
r sip
r dip
i lica....
r sip
i poju....
: tri@
c (xyzq-) dup dup tri* ;
c using fall-through here
i dudu....
: tri*
c (xyzqqq-) [ [ swap &dip dip ] dip dip ] dip call ;
i pupusw..
i lilica..
r dip
r dip
i polica..
r dip
i poju....
: a:eq?
c (aa-f) &a:hash bi@ eq? ;
i lilica..
r a:hash
r bi@
i eqre....
: a:-eq?
c (aa-a) &a:hash bi@ -eq? ;
i lilica..
r a:hash
r bi@
i nere....
: a:chop
c (a-a) a:temp &v:dec sip ;
i lica....
r s:temp
i liliju..
r v:dec
r sip
: a:behead
c (a-a)
c a:chop [ [ n:inc dup n:inc swap ]
c &a:length bi copy ] sip ;
i lica....
r a:chop
i dupulili
r a:behead.inner
r a:length
i lica....
r bi
i cypore..
: a:behead.inner
i liadduli
d 1
d 1
i adswre..
: a:first
c (a-n) #0 a:fetch ;
i liliju..
d 0
r a:fetch
: a:last
c (a-n) dup a:length n:dec a:fetch ;
i dulica..
r a:length
i lisuliju
d 1
r a:fetch
~~~
~~~
: get-index
i lilifead
r sys:buffers/loops
r Current
i re......
: prepare
c increment Current
i lilica..
r Current
r v:inc
c zero out the loop counter
i lilica..
d 0
r get-index
i stre....
: cleanup
c decrement Current
i liliju..
r Current
r v:dec
: indexed-times.inner
i swpudupu
i ca......
i polica..
r get-index
i lica....
r v:inc
i polisu..
d 1
i lica....
r tuck
i linelicj
d 0
r indexed-times.inner
i drdrre..
: I
c (-n)
i lica....
r get-index
i fere....
: J
c (-n)
i lica....
r get-index
i lisufere
d 1
: K
c (-n)
i lica....
r get-index
i lisufere
d 2
: indexed-times
c (nq-)
i puduposw
i lieq....
d 0
i lililiju
r drop-pair
r indexed-times.run
r choose
: indexed-times.run
i lica....
r prepare
i lica....
r indexed-times.inner
i liju....
r cleanup
~~~
~~~
: n:get
c (-n)
i lica....
r s:get/token
i lica....
r s:temp
i liju....
r s:to-n
~~~
================================================================
# The main entry point
On startup this tries to find a word named `startup`. If found,
it then runs it. After this is complete, it drops into listen,
which runs the interactive listener (REPL).
~~~
: forth
c look for a word named `startup`
i lilica..
r $startup
r d:lookup
c if it was found, run it
i dulieq..
d 0
i lililica
r drop
r run-startup
r choose
c after checking for (and running if found) the latest `startup`
c word, we enter the listener loop.
: listen
c (-)
i lica....
r s:get/token
i lica....
r interpret
i liju....
r listen
~~~
~~~
: run-startup
i liadfeju
d 2
: $startup
s startup
~~~
get length
start address, dest address, length
copy
get length
decrement
store new length
~~~
: remove-sigil
c sigils receive a string with the input token sans the sigil.
c This removes the sigil by copying the rest of the string over
c the original data in the TIB and decrementing the count.
i lililicy
r sys:buffers/input/text+1
r sys:buffers/input/text
d 127
i liliju..
r sys:buffers/input
r v:dec
~~~
The `interpret` word takes a token and processes it.
If the token starts with a defined sigil, the sigil is removed
and the remainder of the token is passed to the sigil function
to deal with.
If it does not, the dictionary is searched for the word name.
Assuming it's found, the word type (normal or immediate) and
address are obtained, and the word is either called or compiled
into a definition as needed.
If the token was not found in the dictionary, a warning is
reported.
~~~
: interpret
c (s-)
c copy token to tib
i lilicyli
r sys:buffers/input
d 129
r sys:buffers/input
c check the first character in a string to see if a sigil has
c been defined for it. Returns a flag normalized to true (-1)
c or false (0)
i duliadfe
d 1
i lica....
r sigil:get
i line....
d 0
c then dispatch to appropriate handler
i lililica
r with-sigil
r without-sigil
r choose
i re......
~~~
~~~
: with-sigil
i lifelica
r sys:buffers/input/text
r remove-sigil
i lica....
r sigil:get
i ju......
~~~
~~~
: without-sigil
c see if the word is in the dictionary
i lica....
r d:lookup
i dulieq..
2024-05-23 20:00:14 +02:00
d 0
c if not found, branch to not-found
i licj....
r not-found
2024-05-23 20:02:11 +02:00
c if found, get the address
i liadfe..
2024-05-23 20:00:14 +02:00
d 2
2024-05-23 20:02:11 +02:00
c if address is negative, it's immediate
i dulilt..
d 0
2024-05-23 20:00:14 +02:00
c then, based on the flags, either run it (immediate) or pass
c the address to handle-word for further processing
i lililiju
r handle-immediate
r handle-word
r choose
: not-found
i drlilica
r sys:buffers/input
r a:length
i lieqlicj
d 0
r empty-token
i lilica..
r $not-found
r s:put
i lilica..
r sys:buffers/input
r s:put
i lica....
r nl
: empty-token
i re......
: $not-found
s word not found:
c note: the above string ends with a space character. This may
c be hidden by your editor.
~~~
~~~
: handle-word
c (p-)
c if compiling (`Compiler` set to -1), comma the address into
2024-05-23 20:02:11 +02:00
c the current definition. If not, call the word.
2024-05-23 20:00:14 +02:00
i lifelicj
r Compiler
r comma
2024-05-23 20:02:11 +02:00
i ju......
2024-05-23 20:00:14 +02:00
: handle-immediate
c immediate words are always called.
2024-05-23 20:02:11 +02:00
i limuju..
d -1
2024-05-23 20:00:14 +02:00
~~~
================================================================
String evaluation. I am doing this by remapping `c:get` to read
input from a string instead of the keyboard. This is done to
ensure that evaluation and direct entry are treated the same.
If at the end of the input buffer, reset `c:get` to the default,
then return ASCII 32 (space).
~~~
: get/last
c (-c)
i lilist..
d 0
r c:get
i lililiad
d 0
r c:get
d 1
i stlire..
d 32
~~~
When not at the end, return the current character, and advance
the At pointer.
~~~
: get/next
c (-c)
i lifelife
r Source
r At
i lica....
r a:fetch
i liliju..
r At
r v:inc
~~~
A top level "get" decides which of the prior functions
(get/last and get/next) to use.
~~~
: get
c (-c)
i lifelife
r At
r End
i eqlili..
r get/last
r get/next
i liju....
r choose
~~~
The "process" function takes in a token and runs `interpret` on
it if the token is not empty.
~~~
: process
c (s-)
c check for non-empty token
c (s-sf)
i dulica..
r a:length
i line....
d 0
c process token if not empty
i lililiju
r interpret
r drop
r choose
~~~
~~~
: s:evaluate
c (s-)
c patch `c:get` to jump to "get" instead of reading keyboard
i lilist..
d 1793
r c:get
i lililiad
r get
r c:get
d 1
i st......
c obtain length of string to evaluate; set Source, End, and
c At
i dulica..
r a:length
i listlist
r End
r Source
i lilist..
d 0
r At
c begin the actual evaluation loop. Read in a token, then
c "process" it.
: s:evaluate/loop
i lica....
r s:get/token
i lica....
r process
c Check to see if we have reached the End of the string. If
c so, branch to the exit point. Otherwise, repeat the loop.
i lifelife
r At
r End
i eqlicj..
r s:evaluate/done
i liju....
r s:evaluate/loop
c This is the exit point.
: s:evaluate/done
i re......
~~~
================================================================
Convert a string to a number with `s:to-n`.
The process used here is to fetch each character, determine the
value it represents, adding it to a placeholder on the stack.
The placeholder is then multiplied by ten before the next cycle.
Some care is taken to not do the multiplication if it would
cause the value to overflow. A sign value is factored in as
well. The `-` character changes the sign.
In higher level Forth:
'Length var
'Currently var
:scale &Length fetch &Currently fetch -eq?
[ #10 n:mul &Currently v:inc ] if ;
:s:to-n (s-n)
dup a:length &Length store #1 &Currently store
#1 swap #0 swap
[ dup #45 eq? [ drop swap n:negate swap ]
[ #48 n:sub n:add scale ] choose ] a:for-each
n:mul ;
'2147483647 s:to-n bye
~~~
: s:to-n.scale
c this will adjust the current sum to get it ready for adding
c the next value. Basically, just multiplying by the base (10).
c The scaling is only done if we aren't at the end of the input.
c This is done to avoid an overflow issue with large numbers.
i lifelife
r s:Length
r s:Current
i gtliliju
r s:to-n.adjust
r if
: s:to-n.adjust
i limulili
d 10
r s:Current
r v:inc
i ju......
: s:to-n
c (s-n)
i dufelist
r s:Length
i lilist..
d 1
r s:Current
i liswlisw
d 1
d 0
i lilica..
r s:to-n.digit
r a:for-each
i mure....
: s:to-n.digit
c check to see if the character is a "-" (indicating a negative
c value). If so, branch to the negative handler. Otherwise,
c branch to the general digit handler.
i dulieqli
d 45
r s:to-n.handle-negative
i liliju..
r s:to-n.process-digit
r choose
: s:to-n.handle-negative
c for negative values, we multiply the value by -1 and then
c return to process the next character.
i drswlimu
d -1
i swliliju
r s:Current
r v:inc
: s:to-n.process-digit
c processing digits is modestly involved. I start by checking
c for a few characters I want to ignore: . and ,
c ignore "."
i dulieq..
d 46
i licj....
r s:to-n.ignore
c ignore ","
i dulieq..
d 44
i licj....
r s:to-n.ignore
c if not an ignored character, process as digit. Convert the
c ASCII value to a simple decimal, add it to the running sum,
c and then setup for the next character.
i lisuad..
d 48
i liju....
r s:to-n.scale
: s:to-n.ignore
c handles ignored characters
i drliliju
r s:Current
r v:inc
~~~
The inverse is `n:to-s`, which converts a number into a string.
~~~
: n:to-s
c (n-s)
c get the current `here` and move it out of the way.
i lifepu..
r Free
c assign `Free` to the numeric conversion buffer.
i lilist..
r sys:buffers/numeric-conversion
r Free
c store an initial count (0)
i lilica..
d 0
r comma
c make a copy of the number, but ensure the copy is positive
i dulica..
r n:abs
c with these done, we are ready for the main loop. This will
c iterate over the number, dividing by the base, converting to
c ASCII, and then adding it to the string representation. We
c are at the end when the remainder is zero.
: n:to-s.internal
i lica....
r convert
i lica....
r record
i duline..
d 0
i licj....
r n:to-s.internal
c at this point there are no remaining values. Clean up the
c stack and check to see if the original value was negative.
c if so, call "add-negative-sign"
i drlilt..
d 0
i licc....
r add-negative-sign
c we now have a complete string, but it's in reversed order.
c This is corrected using `a:reverse`.
i lilica..
r sys:buffers/numeric-conversion
r a:reverse
c the last step is to reset `Free` to the saved value from the
c start.
i polistre
r Free
: add-negative-sign
i liliju..
d 45
r record
: convert
c divide the value by the base (hard coded to 10) to get a
c positional value
i lidisw..
d 10
c convert the positional value to an ASCII character
i liswliju
r $valid-digits
r a:fetch
: record
c add the ASCII character for the digit to the string and
c increment the length
i lica....
r comma
i liliju..
r sys:buffers/numeric-conversion
r v:inc
: $valid-digits
s 0123456789
~~~
================================================================
`v:inc` increments the value stored in a variable.
~~~
: v:inc
c (p-)
i dufeliad
d 1
i swstre..
~~~
`v:dec` decrements the value stored in a variable.
~~~
: v:dec
c (p-)
i dufelisu
d 1
i swstre..
~~~
`n:abs` returns the absolute value of a number.
~~~
: n:abs
c (n-n)
i duliltli
d 0
r n:negate
i liju....
r if
~~~
Use `n:negate` to invert the sign of a number.
~~~
: n:negate
c (n-n)
i limure..
d -1
~~~
`nl` displays a newline.
~~~
: nl
i liliju..
d 10
r c:put
~~~
`sp` displays a space.
~~~
: sp
i liliju..
d 32
r c:put
~~~
`tab` displays a tab.
~~~
: tab
i liliju..
d 9
r c:put
~~~
# Input
~~~
: c:get
c (-c)
c Leave two cells filled with nop's. This allows the definition
c to be temporarily replaced later. (Used by `s:evaluate`)
i ........
i ........
i liiore..
d 1
~~~
`s:get/token` reads a whitespace delimited token into the text
input buffer (tib).
If you use this and need to keep the token around, pass it to
`s:temp`.
~~~
: s:get/token
c (-s)
c clear tib (by seting length to zero)
i lilist..
d 0
r sys:buffers/input
c run an input loop
i lica....
r s:get.loop
c clean up the stack and push the TIB address before returning
i drlire..
r sys:buffers/input
: s:get.loop
c the actual input handling occurs here
i lica....
r c:get
c check to see if the character is an end of line/token (ASCII
c 10 [line feed] or 32 [space])
i dudulieq
d 10
i swlieqor
d 32
c if we are at end of input, branch to the end
i lixoduli
d -1
d 0
i eqlicj..
r s:get.loop.done
c if not, check to see if the user entered a backspace (ASCII 8
c or 127). If so, branch to a backspace handler.
i drdulieq
d 8
i licj....
r s:get.backspace
i dulieq..
d 127
i licj....
r s:get.backspace
c if not end of token or backspace, we add it to the TIB using
c a helper function, then repeat the loop.
i lica....
r add-to-tib
i liju....
r s:get.loop
c this is the exit point of the entry loop
: s:get.loop.done
i drre....
: s:get.backspace
c this handles backspacing. Note that this does not prevent
c writing to memory before the start of the TIB.
i dr......
2024-05-23 20:02:11 +02:00
c check to see if the buffer is empty. If so, abort this.
i lifelieq
r sys:buffers/input
d 0
i licj....
r s:get.loop
2024-05-23 20:00:14 +02:00
i lifelisu
r sys:buffers/input
d 1
i listliju
r sys:buffers/input
r s:get.loop
: add-to-tib
c this adds a character to the TIB and increases the length
c counter.
i lidufead
r sys:buffers/input
i liadstli
d 1
r sys:buffers/input
i liju....
r v:inc
~~~
# Output
~~~
: c:put
c (c-)
c Leave two cells filled with nop's. This allows the definition
c to be temporarily replaced later.
i ........
i ........
i liiore..
d 0
~~~
Display a string.
~~~
: s:put
c (s-)
i liliju..
r c:put
r a:for-each
~~~
Display a number
~~~
: n:put
i lica....
r n:to-s
i liju....
r s:put
~~~
# Setup the System
# Sigils
~~~
: sigil:set
c (ac-)
i liadstre
r Sigils
: sigil:get
c (c-p)
i liadfere
r Sigils
~~~
~~~
2024-05-23 20:02:11 +02:00
: )
2024-05-23 20:00:14 +02:00
c (-)
i re......
~~~
~~~
: process-data
i lica....
r compiling?
i liliju..
r process-data.compile
r if
: process-data.compile
i lilica..
r lit
r comma
i liju....
r comma
~~~
2024-05-23 20:02:11 +02:00
~~~
c The $ sigil returns the first character in a string.
: sigil:$
c (s-c)
i lilica..
d 0
r a:fetch
i liju....
r process-data
~~~
2024-05-23 20:00:14 +02:00
New definitions start with a `:` sigil. This sigil creates a new
dictionary header (via `d:create`), then runs `compiler:dtc` to
setup a call to the `dtc` word, and finally turns the `Compiler`
on.
~~~
: sigil::
c (s-)
i lica....
2024-05-23 20:02:11 +02:00
r d:create
i lica....
r compiler:dtc
i liju....
r compiler:on
2024-05-23 20:00:14 +02:00
~~~
Numbers are provided via the `#` sigil. This is done by
converting the token to a number, then calling `process-data`
to compile it into a definition if necessary.
~~~
: sigil:#
c (s-n)
i lica....
r s:to-n
i liju....
r process-data
~~~
Pointers are provided by the `&` sigil. This will lookup an
entry in the dictionary. It fetches the `d:address` field, then
uses `process-data` to compile it into a definition if
necessary.
~~~
: sigil:&
c (s-p)
i lica....
r d:lookup
i liadfe..
d 2
i liju....
r process-data
~~~
The `'` sigil processes strings. At compile time, it passes the
string to `s:keep`. At interpret time it passes it to `s:temp`
instead. Prior to this, it passes the string to `s:rewrite`
for preliminary processing (if defined).
~~~
: sigil:'
c (s-s)
c check to see if `s:rewrite` is in the dictionary
i lilica..
r $s:rewrite
r d:lookup
c if it is, call it to rewrite the string
i duline..
d 0
i lililica
r run.s:rewrite
r drop
r choose
c check to see if we are compiling or at the listener. Call
c either `s:keep` or `s:temp` based on the compiler status.
i lica....
r compiling?
i lililiju
r s:keep
r s:temp
r choose
c a string with the `s:rewrite` name.
: $s:rewrite
s s:rewrite
: run.s:rewrite
c this is a helper used to run `s:rewrite` if present.
i liadfeju
d 2
~~~
2024-05-23 20:02:11 +02:00
~~~
: sigil:@
c (s-a)
i lica....
r d:lookup
i lica....
r d:address
i felife..
r Compiler
i lililiju
r sigil:@.compile
r fetch
r choose
: sigil:@.compile
c (a-)
i lilica..
r lit
r comma
i lica....
r comma
i liliju..
r fetch
r comma
~~~
~~~
: sigil:!
c (ns-)
i lica....
r d:lookup
i lica....
r d:address
i felife..
r Compiler
i lililiju
r sigil:!.compile
r store
r choose
: sigil:!.compile
c (a-)
i lilica..
r lit
r comma
i lica....
r comma
i liliju..
r store
r comma
~~~
~~~
: sigil:\
c (as-)
i lica....
r d:create
i lifelica
r Latest
r d:address
i stre....
~~~
~~~
: depths
c (-nn) (data, address)
i liiore..
d 7
: depth/data
i liiodrre
d 7
: depth/address
i liioswdr
d 7
i re......
~~~
~~~
: {{
i lifeduli
r Latest
r sys:buffers/scope
i lica....
r store-next
i stre....
: ---reveal---
i lilica..
r scope.hidden
r d:create
i lifelili
r Latest
r sys:buffers/scope
d 1
i adstre..
: scope.hidden
s _
: }}
i lilica..
r sys:buffers/scope
r fetch-next
i swfelica
r d:link
i stre....
~~~
~~~
: var
c (s-)
i lisw....
d 0
c fall through into var-n
: var-n
c (ns-)
i lica....
r d:create
i liju....
r comma
: rot
c (abc-bca)
i puswposw
i re......
: n:between?
c (nlu-f)
i puswposw
i dupu....
i puswposw
i puswposw
i lica....
r n:limit
i poeqre..
~~~
2024-05-23 20:00:14 +02:00
# Compiler
Compilation is mostly easy. To compile code, we needs to create
a new dictionary header with `d:create`, set a variable that
indicates we wish to compile to true (`Compiler`), lay down a
call to `dtc`, then just let `interpret` and the sigils handle
the actual details of laying down the addresses.
I define helper functions to enable and disable the `Compiler`.
~~~
: compiler:on
c (-)
i lilistre
d -1
r Compiler
~~~
~~~
: compiler:off
c (-)
i lilistre
d 0
r Compiler
~~~
And a `compiling?` to return the value of `Compiler`. This one
is just a readability aid.
~~~
: compiling?
c (-f)
i lifere..
r Compiler
~~~
The compiler needs to be able to store data in memory. This is
done with `comma` (called "," in most Forths). To implement
this I need a variable to track the next free memory address.
This variable is named `Free`. I also define a word (`here`) to
return the value of `Free`.
~~~
: Free
r FREE-SPACE
: here
c (-p)
i lifere..
r Free
~~~
With these, `comma` is easy. Fetch `Free`, use `store-next` to
write the value, then update `Free` with the next address.
~~~
: comma
c (n-)
i lifelica
r Free
r store-next
i listre..
r Free
~~~
Ending a word is done with `;`. This is one of a small number of
immediate words.
A DTC sequence ends with a NULL (0) value, so this uses `comma`
to write this value, then `compiler:off` to end compilation.
~~~
: ;
c (-)
i lilica..
d 0
r comma
i liju....
r compiler:off
~~~
`compiler:dtc` lays down a call to the `dtc` word at the start
of a compiled word.
~~~
: compiler:dtc
c (-)
c compile a "lica...." bundle
i lilica..
d 2049
r comma
c then a reference to `dtc`
i liliju..
r dtc
r comma
~~~
:d:create
here swap @Latest comma
a:hash comma
2024-05-23 20:02:11 +02:00
dup #3 n:add comma
2024-05-23 20:00:14 +02:00
!Latest ;
`d:create` makes a new dictionary header.
~~~
: d:create
c (s-)
c get a pointer to `here`, and a pointer to the previous
c entry. Store the pointer to the previous entry.
i lifeswli
r Free
r Latest
i felica..
r comma
c calculate the hash of the word name, store it
i lica....
r a:hash
i lica....
r comma
c calculate the starting address of the word and store it
i duliad..
2024-05-23 20:02:11 +02:00
d 3
2024-05-23 20:00:14 +02:00
i lica....
r comma
c finally, patch Latest (`Dictionary`) to point to the start
c of this entry.
i listre..
r Latest
~~~
# Quotations
Quotations are anonymous code blocks. They are basically words
with no names attached. Konilo uses them for a lot: loops,
conditionals, some stack flow, etc.
~~~
: [
c (-a)
c get the current Compiler state
2024-05-23 20:02:11 +02:00
i lica....
r compiling?
2024-05-23 20:00:14 +02:00
c begin compiling
2024-05-23 20:02:11 +02:00
i lica....
r compiler:on
2024-05-23 20:00:14 +02:00
c lay down a call to `internal:quote`
2024-05-23 20:02:11 +02:00
i lilica..
2024-05-23 20:00:14 +02:00
r quote
2024-05-23 20:02:11 +02:00
r comma
2024-05-23 20:00:14 +02:00
c get a pointer to `here` for later use
2024-05-23 20:02:11 +02:00
i lifelica
r Free
r tuck
2024-05-23 20:00:14 +02:00
c store a dummy value for the length. This will be patched
c by `]`.
2024-05-23 20:02:11 +02:00
i lilica..
2024-05-23 20:00:14 +02:00
d 0
2024-05-23 20:02:11 +02:00
r comma
2024-05-23 20:00:14 +02:00
c use `compiler:dtc` to lay down code tostart a DTC code
c sequence
2024-05-23 20:02:11 +02:00
i liju....
r compiler:dtc
2024-05-23 20:00:14 +02:00
: ]
c (a-a)
c lay down a 0 to end the quote definition
2024-05-23 20:02:11 +02:00
i lilica..
2024-05-23 20:00:14 +02:00
d 0
2024-05-23 20:02:11 +02:00
r comma
2024-05-23 20:00:14 +02:00
c determine the length of the quotation
2024-05-23 20:02:11 +02:00
i life....
r Free
i puduposw
i sulisu..
d 1
2024-05-23 20:00:14 +02:00
c update the dummy length with the actual length
2024-05-23 20:02:11 +02:00
i swst....
2024-05-23 20:00:14 +02:00
c reset the compiler state to whatever it was prior
c to invoking `[`
2024-05-23 20:02:11 +02:00
i list....
2024-05-23 20:00:14 +02:00
r Compiler
c if the compiler is still on, we are nested in a
c defintion. In this case, we can just drop the value
c on the stack.
c
c if at the interpreter, we can just increment the
c value on the stack to get a pointer to the actual
c code. This gets left on the stack.
2024-05-23 20:02:11 +02:00
i lifelili
r Compiler
2024-05-23 20:00:14 +02:00
r drop
r interpreting-quote
2024-05-23 20:02:11 +02:00
i liju....
r choose
2024-05-23 20:00:14 +02:00
: interpreting-quote
i liju....
r n:inc
~~~
# Direct Threading
Most words in Konilo are direct threaded. This means that
they consist of a series of addresses, ending in a NULL (0).
A single word, `dtc`, walks through these lists, calling each
address in order.
The in memory representation of a word like:
:foo #1 #2 n:add ;
Would look like:
0000 i lica....
0001 r dtc
0002 - internal:lit
0003 d 1
0002 - internal:lit
0003 d 2
0004 - n:add
0005 d 0
The `internal:lit` pushes the value in the following cell to the
data stack and advances the instruction pointer.
DTC is slower than subroutine threading as it has an additional
layer of processing before each call. But it can be more dense,
and is easily disassembled/decompiled if you have name data.
~~~
: dtc
c (-)
c get the starting address of the DTC sequence. This will be
c the cell following the call to `dtc`.
i poliad..
d 1
c then walk through the list of addresses
i lica....
r dtc:walk
c when done, clean up and return to the parent of the caller
i drre....
: dtc:walk
c get the function address (and a pointer to the next address)
2024-05-23 20:02:11 +02:00
i duliadsw
d 1
i fe......
2024-05-23 20:00:14 +02:00
c check to see if we are at the end (function pointer set to
c zero). If so, exit the loop.
i dulieqli
d 0
r dtc:done
i cj......
c if not, move the pointer to the next function address out of
c the way and call the function
i swpuca..
c then restore the address of the next function and repeat the
c loop
i poliju..
r dtc:walk
: dtc:done
i drre....
~~~
This is exposed to the Forth dictionary as `internal:lit`. (We
had issues with mistyping `lit` and `list`, so moved both this
and the following "quote" under an `internal:` namespace)
~~~
: lit
c (-n)
2024-05-23 20:02:11 +02:00
i popoduli
d 1
i adswfesw
i puswpure
2024-05-23 20:00:14 +02:00
~~~
This is exposed to the Forth dictionary as `internal:quote`.
~~~
: quote
c (-p)
2024-05-23 20:02:11 +02:00
i popoduli
d 1
i adswfesw
i dupuadpo
i swpuswpu
i re......
2024-05-23 20:00:14 +02:00
~~~
# Numbers
`n:inc` and `n:dec` increment and decrement numbers.
~~~
: n:inc
c (n-n)
i liadre..
d 1
: n:dec
c (n-n)
i lisure..
d 1
~~~
2024-05-23 20:02:11 +02:00
The words in `compile:` are intended to help in writing compiler
extensions for generating ilo code words. I use them to implement
`curry`, which binds a value and function together into a new
function.
E.g.,
#678 &n:put curry \display:678
~~~
: compile:lit
c (n-)
i lilica..
d 1
r comma
i liju....
r comma
: compile:call
c (a-)
i lilica..
2024-05-23 20:02:11 +02:00
i lica....
r comma
i liju....
2024-05-23 20:02:11 +02:00
r comma
: compile:jump
c (a-)
i lilica..
i liju....
r comma
i liju....
2024-05-23 20:02:11 +02:00
r comma
: curry
c (vq-q)
i lifepu..
r Free
i swlica..
r compile:lit
i lica....
r compile:jump
i pore....
~~~
2024-05-23 20:00:14 +02:00
# Strings
In Konilo, strings are arrays containing character data.
I provide a rotating buffer of space for temporary strings (and
general arrays). In a default configuation this has space for 8
items.
I use this along with the buffer address (the rather long
`sys:buffers/strings+arrays`) to calculate an address for
a temporary string.
Note here that strings/arrays are (somewhat) limited to 127
values. Longer ones can be made and used with `s:keep` or
moved to another buffer. A longer string/array will have some
degree of data corruption with later entries.
~~~
: s:temp-pointer
c (-p)
i lifelimu
r Next-String
d 128
i liadre..
r sys:buffers/strings+arrays
~~~
The next word updates the "Next-String" variable, constraining
it to a max of 8 entries.
~~~
: s:next
c (-)
c increment Next-String
2024-05-23 20:02:11 +02:00
i lilica..
2024-05-23 20:00:14 +02:00
r Next-String
2024-05-23 20:02:11 +02:00
r v:inc
2024-05-23 20:00:14 +02:00
c compare Next-String to 8
2024-05-23 20:02:11 +02:00
i lilifeeq
2024-05-23 20:00:14 +02:00
d 8
r Next-String
c if 8, use "s:adjust" to reset the counter
2024-05-23 20:02:11 +02:00
i liliju..
2024-05-23 20:00:14 +02:00
r s:adjust
2024-05-23 20:02:11 +02:00
r if
2024-05-23 20:00:14 +02:00
~~~
`s:adjust` resets the "Next-String" variable to zero. It is
called when by "s:next" when it reaches the end of the string
space.
~~~
: s:adjust
c (-)
i lilistre
d 0
r Next-String
~~~
`s:temp` moves a string into the temporary buffer pool.
~~~
: s:temp
c (s-s)
i lica....
2024-05-23 20:02:11 +02:00
r s:temp-pointer
i lica....
r tuck
i lica....
r a:copy
i liju....
r s:next
2024-05-23 20:00:14 +02:00
~~~
:data:string pop pop dup fetch-next n:add push swap push ;
~~~
: data:string
c (-s)
i popodu..
i lica....
r fetch-next
i lica....
r n:add
i puswpure
~~~
`s:keep` inlines a string to memory at `here`. It'll also add
code to skip over the string (in case of compilation being
active).
~~~
: s:keep
c (s-s)
c compile a call to `data:string`
2024-05-23 20:02:11 +02:00
i lilica..
r data:string
r comma
2024-05-23 20:00:14 +02:00
c get a reference to here, tuck it out of the way
2024-05-23 20:02:11 +02:00
i lifesw..
r Free
2024-05-23 20:00:14 +02:00
c get the string length, store it `here`
2024-05-23 20:02:11 +02:00
i dulica..
r a:length
i lica....
r comma
2024-05-23 20:00:14 +02:00
c then use `a:for-each` to copy the characters to `here`
2024-05-23 20:02:11 +02:00
i lilica..
2024-05-23 20:00:14 +02:00
r comma
2024-05-23 20:02:11 +02:00
r a:for-each
2024-05-23 20:00:14 +02:00
c finally, if compiling, drop the pointer from the stack. (If
c not compiling, we just leave it on the stack)
2024-05-23 20:02:11 +02:00
i lifelili
r Compiler
2024-05-23 20:00:14 +02:00
r drop
2024-05-23 20:02:11 +02:00
r if
i ju......
2024-05-23 20:00:14 +02:00
~~~
`s:append` is similar to `a:append`, but specifically uses the
temporary buffer memory.
~~~
: s:append
c (ss-s)
2024-05-23 20:02:11 +02:00
i lifepu..
r Free
2024-05-23 20:00:14 +02:00
i lica....
2024-05-23 20:02:11 +02:00
r s:temp-pointer
i listlica
2024-05-23 20:00:14 +02:00
r Free
2024-05-23 20:02:11 +02:00
r s:next
i lica....
r a:append
i polistre
2024-05-23 20:00:14 +02:00
r Free
: s:prepend
c (ss-)
i swliju..
r s:append
~~~
2024-05-23 20:02:11 +02:00
~~~
: block:buffer
c (-a)
i lire....
r sys:buffers/block
~~~
~~~
: e:to-line
c (n-a) #64 n:mul block:buffer n:add ;
i limuliad
d 64
r sys:buffers/block
i re....
: e:line
c (n-) e:to-line #64 [ fetch-next c:put ] times drop nl ;
i lica....
r e:to-line
i lililica
d 64
r e:line.inner
r times
i drliju..
r nl
: e:line.inner
i lica....
r fetch-next
i liju....
r c:put
: sep
i lica....
r sp
i lica....
r sp
i lica....
r sp
i lililica
d 6
r ed.sep.mid
r times
i lilica..
r ed.sep.end$
r s:put
i liju....
r nl
: ed.sep.mid
i liliju..
r ed.sep.mid$
r s:put
: ed.sep.mid$
s +----5----
: ed.sep.end$
s +---
: l/n
i lica....
r I
i duliltli
d 10
r sp
i lica..
r if
i lica....
r n:put
i liju....
r sp
: line
i lica....
r l/n
i lica....
r I
i liju....
r e:line
: list#
: lines
i lililiju
d 16
r line
r indexed-times
: info
i lilica..
r info$
r d:lookup
i duline..
d 0
i lililiju
r info.action
r drop
r choose
: info.action
i lica....
r d:address
i feju....
: info$
s sys:info
: list*
c (-)
i lililiju
d 16
r list*.inner
r indexed-times
: list*.inner
i lica....
r I
i liju....
r e:line
: list
c (-)
i lica....
r sep
i lica....
r lines
i lica....
r sep
i liju....
r info
: e:Display
r list
~~~
~~~
: ed.reset
c #1024 block:buffer n:dec store ;
i lililisu
d 1024
r sys:buffers/block
d 1
i stre....
: ed.constrain
c @Block #0 @Blocks n:dec n:limit !Block ;
i lifeli..
r Block
d 0
i lifelisu
r Blocks
d 1
i lica....
r n:limit
i listre..
r Block
: set
c (n-) !Block constrain ;
i listliju
r Block
r ed.constrain
: save
c (-) @Block block:buffer block:save ;
i lifeli..
r Block
r sys:buffers/block
i liju....
r block:save
: load
c (-) @Block block:buffer block:load reset ;
i lifeli..
r Block
r sys:buffers/block
i lica....
r block:load
i liju....
r ed.reset
: next
c (-) &Block v:inc constrain load ;
i lilica..
r Block
r v:inc
i lica....
r ed.constrain
i liju....
r load
: prev
c (-) &Block v:dec constrain load ;
i lilica..
r Block
r v:dec
i lica....
r ed.constrain
i liju....
r load
: new
c (-) #32 block:buffer #1024 fill reset ;
i lilili..
d 32
r sys:buffers/block
d 1024
i lica....
r fill
i liju....
r ed.reset
: edit
c (n-) set load @e:Display call ;
i lica....
r set
i lica....
r load
i lifeju..
r e:Display
: run
c (-) reset block:buffer n:dec s:evaluate ;
i lica....
r ed.reset
i lilisu..
r sys:buffers/block
d 1
i liju....
r s:evaluate
: use
c (block) set load run ;
i lica....
r set
i lica....
r load
i liju....
r run
: using
c (first,last) over n:sub swap use [ next run ] times ;
i puduposw
i suswlica
r use
i liliju..
r using.times
r times
: using.times
i lica....
r next
i liju....
r run
~~~
~~~
: s:get/line.handle
i dulieqpu
d 8
i duposwli
d 127
i eqorlili
r s:get/line.handle:b/s
r comma
i liju....
r choose
: s:get/line.handle:b/s
i dr......
i lifelife
r Free
r s:get/line.Start
i eqlicj..
r s:get/line.process
i liliju..
d -1
r allot
: s:get/line.process
i dulieqli
d 10
r s:get/line.process:done
i liliju..
r s:get/line.process.handle
r choose
: s:get/line.process:done
i drlire..
d -1
: s:get/line.process.handle
i lica....
r s:get/line.handle
i lire....
d 0
: s:get/line.Start
d 0
: s:get/line
c (-s)
c here n:inc &s:get/line.Start store
c here [ #0 comma [ c:get process ] until ] sip
c here over n:sub n:dec over !Free swap store drop
c here s:temp ;
i lifeliad
r Free
d 1
i list....
r s:get/line.Start
i lifedupu
r Free
i lilica..
d 0
r comma
i lilica..
r s:get/line.read
r until
i polifepu
r Free
i duposwsu
i lisupudu
d 1
i poswlist
r Free
i swstdrli
r Free
i feliju..
r s:temp
: s:get/line.read
i lica....
r c:get
i liju....
r s:get/line.process
~~~
~~~
: e:erase/line
c (n-)
c e:to-line #32 swap #64 fill ;
i lica...
r e:to-line
i liswli..
d 32
d 64
i liju....
r fill
: e:replace
c (sn-)
c n:inc swap e:to-line over n:dec s:length copy ;
i liadsw..
d 1
i lica....
r e:to-line
i puduposw
i lisulica
d 1
r a:length
i cyre....
: e:replace-at
c (snn-)
c [ &e:to-line dip n:add ] dip
c [ over store n:inc ] s:for-each drop ;
i pulilica
r e:to-line
r dip
i adpolili
r e:replace-at:internal
r a:for-each
i ca......
i drre....
: e:replace-at:internal
i puduposw
i stliadre
d 1
: e:insert
c (n"-)
c dup e:erase/line s:get/line e:replace ;
i dulica..
r e:erase/line
i lica....
r s:get/line
i liju....
r e:replace
: e:insert-at
c (nn"-)
c s:get/line e:replace-at ;
i lica....
r s:get/line
i liju....
r e:replace-at
: 0
c ("-)
i liliju..
d 0
r e:insert
: 1
c ("-)
i liliju..
d 1
r e:insert
: 2
c ("-)
i liliju..
d 2
r e:insert
: 3
c ("-)
i liliju..
d 3
r e:insert
: 4
c ("-)
i liliju..
d 4
r e:insert
: 5
c ("-)
i liliju..
d 5
r e:insert
: 6
c ("-)
i liliju..
d 6
r e:insert
: 7
c ("-)
i liliju..
d 7
r e:insert
: 8
c ("-)
i liliju..
d 8
r e:insert
: 9
c ("-)
i liliju..
d 9
r e:insert
: 10
c ("-)
i liliju..
d 10
r e:insert
: 11
c ("-)
i liliju..
d 11
r e:insert
: 12
c ("-)
i liliju..
d 12
r e:insert
: 13
c ("-)
i liliju..
d 13
r e:insert
: 14
c ("-)
i liliju..
d 14
r e:insert
: 15
c ("-)
i liliju..
d 15
r e:insert
~~~
~~~
: .
c ("-)
c #62 [ c:get drop ] times ;
i lililiju
d 62
r .:internal
r times
: .:internal
i lica....
r c:get
i drre....
~~~
The basic `use` and `using` words aren't very convienient as you
need to keep track of the exact blocks to load. `needs` allows
you to use the first part of a block title line (typically a
comment for code blocks) instead. It'll scan through the block
set, running any blocks that match the provided text. E.g., to
load a block set named "(pali)":
'(pali) needs
Blocks are loaded in order from 0 to N, where N is the value in
`Blocks`. This can be very slow if you have a large block set or
are loading multiple sets of blocks. You may want to consider
keeping blocks towards the low end of the block set and limiting
`Blocks` before running this.
~~~
: needs.check
c (-f)
c block:buffer &sys:buffers/needs @Len compare ;
i lililife
r sys:buffers/block
r sys:buffers/needs
r needs.Len
i cpre....
: needs.setup
c (s-)
c dup s:length !Len n:inc &sys:buffers/needs @Len copy ;
i dulica..
r a:length
i listliad
r needs.Len
d 1
i lilifecy
r sys:buffers/needs
r needs.Len
i re......
: needs
c (s-)
c setup @Block [
c @Blocks [ I set load check &run if ] indexed-times
c ] dip !Block load ;
i lica....
r needs.setup
i lifepu..
r Block
i lifelili
r Blocks
r needs:internal
r indexed-times
i ca......
i polist..
r Block
i liju....
r load
: needs:internal
i lica....
r I
i lica....
r set
i lica....
r load
i lica....
r needs.check
i liliju..
r run
r if
~~~
For generating an index of the blocks, `titles` is provided.
This shows the block number and index line for any block with
a title line.
~~~
: titles
c (-)
c @Block @Blocks
c [ I set load block:buffer fetch #32 -eq?
c [ I n:put sp #64 block:buffer n:dec &store &s:put bi nl ] if
c ] indexed-times !Block load ;
i lifelife
r Block
r Blocks
i lilica..
r titles:inner
r indexed-times
i listliju
r Block
r load
: titles:inner
i lica....
r I
i lica....
r set
i lica....
r load
i lifeline
r sys:buffers/block
d 32
i liliju..
r titles:display
r if
: titles:display
i lica....
r I
i lica....
r n:put
i lica....
r sp
i lililisu
d 64
r sys:buffers/block
d 1
i lililica
r store
r s:put
r bi
i liju....
r nl
~~~
`sys:info` is called by the editor. It displays a status line below
the editor output. You can write a new `sys:info`, and the the code
will use the most recent one.
~~~
: sys:info
c (-)
c '___B: s:put @Block n:put $/ c:put @Blocks n:dec n:put
i lilica..
r sys:info:B$
r s:put
i lifelica
r Block
r n:put
i lilica..
d 47
r c:put
i lifelisu
r Blocks
d 1
i lica....
r n:put
c '___S: s:put depth/data n:put $/ c:put #32 n:put
i lilica..
r sys:info:S$
r s:put
i lica....
r depth/data
i lica....
r n:put
i lilica..
d 47
r c:put
i lilica..
d 32
r n:put
c '___M: s:put here n:put $/ c:put #59999 n:put nl ;
i lilica..
r sys:info:M$
r s:put
i lifelica
r Free
r n:put
i lilica..
d 47
r c:put
i lilica..
d 59999
r n:put
i liju....
r nl
: sys:info:B$
s B:
: sys:info:S$
s S:
: sys:info:M$
s M:
~~~
================================================================
The last couple of things are just to save the image and set a
startup word. The default startup word runs the code in blocks
1 and 2, then starts the editor on block 0.
~~~
: ~startup:process
c (n-f)
i lica....
r set
i lica....
r load
i lifelieq
r sys:buffers/block
d 40
i liliju..
r run
r if
: prelude
i lilica..
d 1
r ~startup:process
i liliju..
d 2
r ~startup:process
: startup
i liju....
r prelude
: rom:save
c (-)
i liiore..
d 4
~~~
================================================================
EOF
2024-05-23 20:00:14 +02:00
~~~
: ENTRY.0
d 0
2024-05-23 20:02:11 +02:00
d 177614
r )
2024-05-23 20:00:14 +02:00
: ENTRY.1
r ENTRY.0
2024-05-23 20:02:11 +02:00
d -1644352334
r ---reveal---
2024-05-23 20:00:14 +02:00
: ENTRY.2
r ENTRY.1
2024-05-23 20:02:11 +02:00
d 2088204551
r neq?
2024-05-23 20:00:14 +02:00
: ENTRY.3
r ENTRY.2
2024-05-23 20:02:11 +02:00
d 193429569
r -if
2024-05-23 20:00:14 +02:00
: ENTRY.4
r ENTRY.3
2024-05-23 20:02:11 +02:00
d 177619
r .
2024-05-23 20:00:14 +02:00
: ENTRY.5
r ENTRY.4
2024-05-23 20:02:11 +02:00
d 177621
r 0
2024-05-23 20:00:14 +02:00
: ENTRY.6
r ENTRY.5
2024-05-23 20:02:11 +02:00
d 177622
r 1
2024-05-23 20:00:14 +02:00
: ENTRY.7
r ENTRY.6
2024-05-23 20:02:11 +02:00
d 5861574
r 10
2024-05-23 20:00:14 +02:00
: ENTRY.8
r ENTRY.7
2024-05-23 20:02:11 +02:00
d 5861575
r 11
2024-05-23 20:00:14 +02:00
: ENTRY.9
r ENTRY.8
2024-05-23 20:02:11 +02:00
d 5861576
r 12
2024-05-23 20:00:14 +02:00
: ENTRY.10
r ENTRY.9
2024-05-23 20:02:11 +02:00
d 5861577
r 13
2024-05-23 20:00:14 +02:00
: ENTRY.11
r ENTRY.10
2024-05-23 20:02:11 +02:00
d 5861578
r 14
2024-05-23 20:00:14 +02:00
: ENTRY.12
r ENTRY.11
2024-05-23 20:02:11 +02:00
d 5861579
r 15
2024-05-23 20:00:14 +02:00
: ENTRY.13
r ENTRY.12
2024-05-23 20:02:11 +02:00
d 177623
r 2
2024-05-23 20:00:14 +02:00
: ENTRY.14
r ENTRY.13
2024-05-23 20:02:11 +02:00
d 177624
r 3
2024-05-23 20:00:14 +02:00
: ENTRY.15
r ENTRY.14
2024-05-23 20:02:11 +02:00
d 177625
r 4
2024-05-23 20:00:14 +02:00
: ENTRY.16
r ENTRY.15
2024-05-23 20:02:11 +02:00
d 177626
r 5
2024-05-23 20:00:14 +02:00
: ENTRY.17
r ENTRY.16
2024-05-23 20:02:11 +02:00
d 177627
r 6
2024-05-23 20:00:14 +02:00
: ENTRY.18
r ENTRY.17
2024-05-23 20:02:11 +02:00
d 177628
r 7
2024-05-23 20:00:14 +02:00
: ENTRY.19
r ENTRY.18
2024-05-23 20:02:11 +02:00
d 177629
r 8
2024-05-23 20:00:14 +02:00
: ENTRY.20
r ENTRY.19
2024-05-23 20:02:11 +02:00
d 177630
r 9
2024-05-23 20:00:14 +02:00
: ENTRY.21
r ENTRY.20
2024-05-23 20:02:11 +02:00
d 177632
R ;
2024-05-23 20:00:14 +02:00
: ENTRY.22
r ENTRY.21
2024-05-23 20:02:11 +02:00
d 212805696
r ?jump
2024-05-23 20:00:14 +02:00
: ENTRY.23
r ENTRY.22
2024-05-23 20:02:11 +02:00
d 67966955
r BaseBlock
2024-05-23 20:00:14 +02:00
: ENTRY.24
r ENTRY.23
2024-05-23 20:02:11 +02:00
d 216428464
r Block
2024-05-23 20:00:14 +02:00
: ENTRY.25
r ENTRY.24
2024-05-23 20:02:11 +02:00
d -1447795165
r Blocks
: ENTRY.26
r ENTRY.25
d -1210660288
r Compiler
: ENTRY.27
r ENTRY.26
d 1264838491
r Latest
: ENTRY.28
r ENTRY.27
d 2089116775
r Free
: ENTRY.29
r ENTRY.28
d 177646
r I
: ENTRY.30
r ENTRY.29
d 177647
r J
: ENTRY.31
r ENTRY.30
d 177648
r K
: ENTRY.32
r ENTRY.31
d -786332176
r Sigils
: ENTRY.33
r ENTRY.32
d 177664
R [
: ENTRY.34
r ENTRY.33
d 177666
R ]
: ENTRY.35
r ENTRY.34
d -296263550
r a:-eq?
: ENTRY.36
r ENTRY.35
d 1539635992
r a:append
: ENTRY.37
r ENTRY.36
d 1565438329
r a:behead
: ENTRY.38
r ENTRY.37
d -294319702
r a:chop
: ENTRY.39
r ENTRY.38
d 63806334
r a:contains?
: ENTRY.40
r ENTRY.39
d -294312037
r a:copy
: ENTRY.41
r ENTRY.40
d 251383785
r a:dup
: ENTRY.42
r ENTRY.41
d 251384693
r a:eq?
: ENTRY.43
r ENTRY.42
d -1119160502
r a:fetch
: ENTRY.44
r ENTRY.43
d 1726883814
r a:filter
: ENTRY.45
r ENTRY.44
d -1119018392
r a:first
: ENTRY.46
r ENTRY.45
d -1309732155
r a:for-each
: ENTRY.47
r ENTRY.46
d -294147516
r a:hash
: ENTRY.48
r ENTRY.47
d -1115296648
r a:index
: ENTRY.49
r ENTRY.48
d 917819423
r a:indices
: ENTRY.50
r ENTRY.49
d -294003756
r a:last
: ENTRY.51
r ENTRY.50
d -293999829
r a:left
: ENTRY.52
r ENTRY.51
d 1957010690
r a:length
: ENTRY.53
r ENTRY.52
d -293968098
r a:make
: ENTRY.54
r ENTRY.53
d -1751031389
r a:make/temp
: ENTRY.55
r ENTRY.54
d 251392926
r a:map
: ENTRY.56
r ENTRY.55
d 2000526863
r a:middle
: ENTRY.57
r ENTRY.56
d 1526142126
r a:prepend
: ENTRY.58
r ENTRY.57
d -2103488936
r a:reduce
: ENTRY.59
r ENTRY.58
d -674869668
r a:reverse
: ENTRY.60
r ENTRY.59
d -1104799682
r a:right
: ENTRY.61
r ENTRY.60
d -1103209427
r a:store
: ENTRY.62
r ENTRY.61
d -293712106
r s:temp
: ENTRY.63
r ENTRY.62
d 2090026588
r a:th
: ENTRY.64
r ENTRY.63
d 253189153
r allot
: ENTRY.65
r ENTRY.64
d 193486360
r and
: ENTRY.66
r ENTRY.65
d 5863248
r bi
: ENTRY.67
r ENTRY.66
d 193487226
r bi*
: ENTRY.68
r ENTRY.67
d 193487248
r bi@
: ENTRY.69
r ENTRY.68
d 1122748452
r block:buffer
: ENTRY.70
r ENTRY.69
2024-05-23 20:00:14 +02:00
d 427330826
r block:load
2024-05-23 20:02:11 +02:00
: ENTRY.71
r ENTRY.70
2024-05-23 20:00:14 +02:00
d 427567833
r block:save
2024-05-23 20:02:11 +02:00
: ENTRY.72
r ENTRY.71
2024-05-23 20:00:14 +02:00
d 193487813
r bye
2024-05-23 20:02:11 +02:00
: ENTRY.73
r ENTRY.72
2024-05-23 20:00:14 +02:00
d 253758370
r c:get
2024-05-23 20:02:11 +02:00
: ENTRY.74
r ENTRY.73
d -157167450
r c:lowercase?
: ENTRY.75
r ENTRY.74
2024-05-23 20:00:14 +02:00
d 253768699
r c:put
2024-05-23 20:02:11 +02:00
: ENTRY.76
r ENTRY.75
d 153339739
r c:to-lower
: ENTRY.77
r ENTRY.76
d -215432539
r c:to-s
: ENTRY.78
r ENTRY.77
d 164041342
r c:to-upper
: ENTRY.79
r ENTRY.78
d 430999977
r c:uppercase?
: ENTRY.80
r ENTRY.79
2024-05-23 20:00:14 +02:00
d 2090140673
r call
2024-05-23 20:02:11 +02:00
: ENTRY.81
r ENTRY.80
2024-05-23 20:00:14 +02:00
d -161057562
r choose
2024-05-23 20:02:11 +02:00
: ENTRY.82
r ENTRY.81
2024-05-23 20:00:14 +02:00
d 255669810
r comma
2024-05-23 20:02:11 +02:00
: ENTRY.83
r ENTRY.82
2024-05-23 20:00:14 +02:00
d -748339476
r compare
2024-05-23 20:02:11 +02:00
: ENTRY.84
r ENTRY.83
d 425733796
r compile:call
: ENTRY.85
r ENTRY.84
d 426007172
r compile:jump
: ENTRY.86
r ENTRY.85
d -898142575
r compile:lit
: ENTRY.87
r ENTRY.86
2024-05-23 20:00:14 +02:00
d -1979274138
r compiling?
2024-05-23 20:02:11 +02:00
: ENTRY.88
r ENTRY.87
2024-05-23 20:00:14 +02:00
d 2090156064
r copy
2024-05-23 20:02:11 +02:00
: ENTRY.89
r ENTRY.88
d 255891066
r curry
: ENTRY.90
r ENTRY.89
2024-05-23 20:00:14 +02:00
d 352952457
r d:address
2024-05-23 20:02:11 +02:00
: ENTRY.91
r ENTRY.90
2024-05-23 20:00:14 +02:00
d 626189207
r d:create
2024-05-23 20:02:11 +02:00
: ENTRY.92
r ENTRY.91
2024-05-23 20:00:14 +02:00
d 2012546722
r d:exists?
2024-05-23 20:02:11 +02:00
: ENTRY.93
r ENTRY.92
2024-05-23 20:00:14 +02:00
d -176741337
r d:hash
2024-05-23 20:02:11 +02:00
: ENTRY.94
r ENTRY.93
2024-05-23 20:00:14 +02:00
d -176589039
r d:link
2024-05-23 20:02:11 +02:00
: ENTRY.95
r ENTRY.94
2024-05-23 20:00:14 +02:00
d 975220285
r d:lookup
2024-05-23 20:02:11 +02:00
: ENTRY.96
r ENTRY.95
d -1182620049
r depth/address
: ENTRY.97
r ENTRY.96
d -214216093
r depth/data
: ENTRY.98
r ENTRY.97
d -125438899
r depths
: ENTRY.99
r ENTRY.98
2024-05-23 20:00:14 +02:00
d 193489474
r dip
2024-05-23 20:02:11 +02:00
: ENTRY.100
r ENTRY.99
2024-05-23 20:00:14 +02:00
d 2090195226
r drop
2024-05-23 20:02:11 +02:00
: ENTRY.101
r ENTRY.100
2024-05-23 20:00:14 +02:00
d 288947475
r drop-pair
2024-05-23 20:02:11 +02:00
: ENTRY.102
r ENTRY.101
2024-05-23 20:00:14 +02:00
d 193489824
r dtc
2024-05-23 20:02:11 +02:00
: ENTRY.103
r ENTRY.102
2024-05-23 20:00:14 +02:00
d 193489870
r dup
2024-05-23 20:02:11 +02:00
: ENTRY.104
r ENTRY.103
2024-05-23 20:00:14 +02:00
d -59285433
r dup-pair
2024-05-23 20:02:11 +02:00
: ENTRY.105
r ENTRY.104
d -572166886
r e:Display
: ENTRY.106
r ENTRY.105
d 1606468171
r e:erase/line
: ENTRY.107
r ENTRY.106
d 525535321
r e:insert
: ENTRY.108
r ENTRY.107
d 1191682587
r e:insert-at
: ENTRY.109
r ENTRY.108
d -137453652
r e:line
: ENTRY.110
r ENTRY.109
d -1454437472
r e:replace
: ENTRY.111
r ENTRY.110
d 1632613378
r e:replace-at
: ENTRY.112
r ENTRY.111
d 1440404764
r e:to-line
: ENTRY.113
r ENTRY.112
d 2090215723
r edit
: ENTRY.114
r ENTRY.113
2024-05-23 20:00:14 +02:00
d 193490778
r eq?
2024-05-23 20:02:11 +02:00
: ENTRY.115
r ENTRY.114
2024-05-23 20:00:14 +02:00
d 258875503
r fetch
2024-05-23 20:02:11 +02:00
: ENTRY.116
r ENTRY.115
2024-05-23 20:00:14 +02:00
d -1885660229
r fetch-next
2024-05-23 20:02:11 +02:00
: ENTRY.117
r ENTRY.116
2024-05-23 20:00:14 +02:00
d 2090257196
r fill
2024-05-23 20:02:11 +02:00
: ENTRY.118
r ENTRY.117
2024-05-23 20:00:14 +02:00
d -1163346114
r forever
2024-05-23 20:02:11 +02:00
: ENTRY.119
r ENTRY.118
2024-05-23 20:00:14 +02:00
d 5863407
r gc
2024-05-23 20:02:11 +02:00
: ENTRY.120
r ENTRY.119
2024-05-23 20:00:14 +02:00
d 193493055
r gt?
2024-05-23 20:02:11 +02:00
: ENTRY.121
r ENTRY.120
2024-05-23 20:00:14 +02:00
d 260584565
r gteq?
2024-05-23 20:02:11 +02:00
: ENTRY.122
r ENTRY.121
2024-05-23 20:00:14 +02:00
d 2090324905
r here
2024-05-23 20:02:11 +02:00
: ENTRY.123
r ENTRY.122
2024-05-23 20:00:14 +02:00
d 5863476
r if
2024-05-23 20:02:11 +02:00
: ENTRY.124
r ENTRY.123
d 123652725
r indexed-times
: ENTRY.125
r ENTRY.124
d -1223977595
r lit
: ENTRY.126
r ENTRY.125
d -1465379862
r quote
: ENTRY.127
r ENTRY.126
2024-05-23 20:00:14 +02:00
d 314257922
r interpret
2024-05-23 20:02:11 +02:00
: ENTRY.128
r ENTRY.127
2024-05-23 20:00:14 +02:00
d 5863485
r io
2024-05-23 20:02:11 +02:00
: ENTRY.129
r ENTRY.128
2024-05-23 20:00:14 +02:00
d 2090414049
r jump
2024-05-23 20:02:11 +02:00
: ENTRY.130
r ENTRY.129
d 2090473057
r list
: ENTRY.131
r ENTRY.130
d 266134180
r list#
: ENTRY.132
r ENTRY.131
d 266134187
r list*
: ENTRY.133
r ENTRY.132
d 2090478981
r load
: ENTRY.134
r ENTRY.133
2024-05-23 20:00:14 +02:00
d 193498500
r lt?
2024-05-23 20:02:11 +02:00
: ENTRY.135
r ENTRY.134
2024-05-23 20:00:14 +02:00
d 266514170
r lteq?
2024-05-23 20:02:11 +02:00
: ENTRY.136
r ENTRY.135
d -494948871
r n:-zero?
: ENTRY.137
r ENTRY.136
2024-05-23 20:00:14 +02:00
d 266796867
r n:abs
2024-05-23 20:02:11 +02:00
: ENTRY.138
r ENTRY.137
2024-05-23 20:00:14 +02:00
d 266796918
r n:add
2024-05-23 20:02:11 +02:00
: ENTRY.139
r ENTRY.138
d 1032861494
r n:between?
: ENTRY.140
r ENTRY.139
2024-05-23 20:00:14 +02:00
d 266800217
r n:dec
2024-05-23 20:02:11 +02:00
: ENTRY.141
r ENTRY.140
2024-05-23 20:00:14 +02:00
d 266800368
r n:div
2024-05-23 20:02:11 +02:00
: ENTRY.142
r ENTRY.141
2024-05-23 20:00:14 +02:00
d 1637942608
r n:divmod
2024-05-23 20:02:11 +02:00
: ENTRY.143
r ENTRY.142
d 266803501
r n:get
: ENTRY.144
r ENTRY.143
2024-05-23 20:00:14 +02:00
d 266805959
r n:inc
2024-05-23 20:02:11 +02:00
: ENTRY.145
r ENTRY.144
2024-05-23 20:00:14 +02:00
d -1502694228
r n:limit
2024-05-23 20:02:11 +02:00
: ENTRY.146
r ENTRY.145
2024-05-23 20:00:14 +02:00
d 266809907
r n:max
2024-05-23 20:02:11 +02:00
: ENTRY.147
r ENTRY.146
2024-05-23 20:00:14 +02:00
d 266810161
r n:min
2024-05-23 20:02:11 +02:00
: ENTRY.148
r ENTRY.147
2024-05-23 20:00:14 +02:00
d 266810349
r n:mod
2024-05-23 20:02:11 +02:00
: ENTRY.149
r ENTRY.148
2024-05-23 20:00:14 +02:00
d 266810555
r n:mul
2024-05-23 20:02:11 +02:00
: ENTRY.150
r ENTRY.149
2024-05-23 20:00:14 +02:00
d 2024000897
r n:negate
2024-05-23 20:02:11 +02:00
: ENTRY.151
r ENTRY.150
2024-05-23 20:00:14 +02:00
d 266813830
r n:put
2024-05-23 20:02:11 +02:00
: ENTRY.152
r ENTRY.151
2024-05-23 20:00:14 +02:00
d 266817079
r n:sub
2024-05-23 20:02:11 +02:00
: ENTRY.153
r ENTRY.152
2024-05-23 20:00:14 +02:00
d 215056784
r n:to-s
2024-05-23 20:02:11 +02:00
: ENTRY.154
r ENTRY.153
2024-05-23 20:00:14 +02:00
d -1486229492
r n:zero?
2024-05-23 20:02:11 +02:00
: ENTRY.155
r ENTRY.154
d 268346580
r needs
: ENTRY.156
r ENTRY.155
d 193500239
r new
: ENTRY.157
r ENTRY.156
d 2090540740
r next
: ENTRY.158
r ENTRY.157
2024-05-23 20:00:14 +02:00
d 193500364
r nip
2024-05-23 20:02:11 +02:00
: ENTRY.159
r ENTRY.158
2024-05-23 20:00:14 +02:00
d 5863647
r nl
2024-05-23 20:02:11 +02:00
: ENTRY.160
r ENTRY.159
2024-05-23 20:00:14 +02:00
d 193500566
r not
2024-05-23 20:02:11 +02:00
: ENTRY.161
r ENTRY.160
2024-05-23 20:00:14 +02:00
d 5863686
r or
2024-05-23 20:02:11 +02:00
: ENTRY.162
r ENTRY.161
2024-05-23 20:00:14 +02:00
d 2090594561
r over
2024-05-23 20:02:11 +02:00
: ENTRY.163
r ENTRY.162
2024-05-23 20:00:14 +02:00
d 193502740
r pop
2024-05-23 20:02:11 +02:00
: ENTRY.164
r ENTRY.163
d -1031328682
r prelude
: ENTRY.165
r ENTRY.164
d 2090626146
r prev
: ENTRY.166
r ENTRY.165
2024-05-23 20:00:14 +02:00
d -853324053
r process-data
2024-05-23 20:02:11 +02:00
: ENTRY.167
r ENTRY.166
2024-05-23 20:00:14 +02:00
d 2090629861
r push
2024-05-23 20:02:11 +02:00
: ENTRY.168
r ENTRY.167
2024-05-23 20:00:14 +02:00
d 1059716234
r restart
2024-05-23 20:02:11 +02:00
: ENTRY.169
r ENTRY.168
d 337707900
r rom:save
: ENTRY.170
r ENTRY.169
d 193504922
r rot
: ENTRY.171
r ENTRY.170
d 193505114
r run
: ENTRY.172
r ENTRY.171
d 408173524
r a:-eq?
: ENTRY.173
r ENTRY.172
2024-05-23 20:00:14 +02:00
d -127536406
r s:append
2024-05-23 20:02:11 +02:00
: ENTRY.174
r ENTRY.173
d -101734069
r a:behead
: ENTRY.175
r ENTRY.174
d 410117372
r a:chop
: ENTRY.176
r ENTRY.175
d 1683118608
r a:contains?
: ENTRY.177
r ENTRY.176
2024-05-23 20:00:14 +02:00
d 410125037
r a:copy
2024-05-23 20:02:11 +02:00
: ENTRY.178
r ENTRY.177
2024-05-23 20:00:14 +02:00
d 272730363
r a:dup
2024-05-23 20:02:11 +02:00
: ENTRY.179
r ENTRY.178
d 272731271
r a:eq?
: ENTRY.180
r ENTRY.179
2024-05-23 20:00:14 +02:00
d 102250697
r s:evaluate
2024-05-23 20:02:11 +02:00
: ENTRY.181
r ENTRY.180
2024-05-23 20:00:14 +02:00
d 652426460
r a:fetch
2024-05-23 20:02:11 +02:00
: ENTRY.182
r ENTRY.181
d 59711416
r a:filter
: ENTRY.183
r ENTRY.182
d 652568570
r a:first
: ENTRY.184
r ENTRY.183
2024-05-23 20:00:14 +02:00
d -89307369
r a:for-each
2024-05-23 20:02:11 +02:00
: ENTRY.185
r ENTRY.184
d -369411895
r s:get/line
: ENTRY.186
r ENTRY.185
2024-05-23 20:00:14 +02:00
d 704009186
r s:get/token
2024-05-23 20:02:11 +02:00
: ENTRY.187
r ENTRY.186
2024-05-23 20:00:14 +02:00
d 410289558
r a:hash
2024-05-23 20:02:11 +02:00
: ENTRY.188
r ENTRY.187
d 1735582460
r a:index
: ENTRY.189
r ENTRY.188
d 1735705137
r a:indices
: ENTRY.190
r ENTRY.189
2024-05-23 20:00:14 +02:00
d 410401271
r s:keep
2024-05-23 20:02:11 +02:00
: ENTRY.191
r ENTRY.190
d 410433318
r a:last
: ENTRY.192
r ENTRY.191
d 410437245
r a:left
: ENTRY.193
r ENTRY.192
2024-05-23 20:00:14 +02:00
d 289838292
r a:length
2024-05-23 20:02:11 +02:00
: ENTRY.194
r ENTRY.193
d 272739504
r a:map
: ENTRY.195
r ENTRY.194
d 333354465
r a:middle
: ENTRY.196
r ENTRY.195
2024-05-23 20:00:14 +02:00
d -1950939456
r s:prepend
2024-05-23 20:02:11 +02:00
: ENTRY.197
r ENTRY.196
2024-05-23 20:00:14 +02:00
d 272743435
r s:put
2024-05-23 20:02:11 +02:00
: ENTRY.198
r ENTRY.197
d 524305962
r a:reduce
: ENTRY.199
r ENTRY.198
2024-05-23 20:00:14 +02:00
d 143016046
r a:reverse
2024-05-23 20:02:11 +02:00
: ENTRY.200
r ENTRY.199
d 144659380
r s:rewrite
: ENTRY.201
r ENTRY.200
d 666787280
r a:right
: ENTRY.202
r ENTRY.201
2024-05-23 20:00:14 +02:00
d 668377535
r a:store
2024-05-23 20:02:11 +02:00
: ENTRY.203
r ENTRY.202
2024-05-23 20:00:14 +02:00
d 410724968
r s:temp
2024-05-23 20:02:11 +02:00
: ENTRY.204
r ENTRY.203
2024-05-23 20:00:14 +02:00
d 2090673454
r a:th
2024-05-23 20:02:11 +02:00
: ENTRY.205
r ENTRY.204
d 1238161771
r s:to-lower
: ENTRY.206
r ENTRY.205
2024-05-23 20:00:14 +02:00
d 410733744
r s:to-n
2024-05-23 20:02:11 +02:00
: ENTRY.207
r ENTRY.206
d 1248863374
r s:to-upper
: ENTRY.208
r ENTRY.207
d 2090715988
r save
: ENTRY.209
r ENTRY.208
d 193505681
r set
: ENTRY.210
r ENTRY.209
2024-05-23 20:00:14 +02:00
d -38720901
r shift-left
2024-05-23 20:02:11 +02:00
: ENTRY.211
r ENTRY.210
2024-05-23 20:00:14 +02:00
d -1270529650
r shift-right
2024-05-23 20:02:11 +02:00
: ENTRY.212
r ENTRY.211
d -1801857832
r sigil:!
: ENTRY.213
r ENTRY.212
2024-05-23 20:00:14 +02:00
d -1801857830
r sigil:#
2024-05-23 20:02:11 +02:00
: ENTRY.214
r ENTRY.213
d -1801857829
r sigil:$
: ENTRY.215
r ENTRY.214
2024-05-23 20:00:14 +02:00
d -1801857827
r sigil:&
2024-05-23 20:02:11 +02:00
: ENTRY.216
r ENTRY.215
2024-05-23 20:00:14 +02:00
d -1801857826
r sigil:'
2024-05-23 20:02:11 +02:00
: ENTRY.217
r ENTRY.216
d -1801857825
r drop
: ENTRY.218
r ENTRY.217
2024-05-23 20:00:14 +02:00
d -1801857807
r sigil::
2024-05-23 20:02:11 +02:00
: ENTRY.219
r ENTRY.218
d -1801857801
r sigil:@
: ENTRY.220
r ENTRY.219
d -1801857773
r sigil:\
: ENTRY.221
r ENTRY.220
2024-05-23 20:00:14 +02:00
d 576954903
r sigil:get
2024-05-23 20:02:11 +02:00
: ENTRY.222
r ENTRY.221
2024-05-23 20:00:14 +02:00
d 576967971
r sigil:set
2024-05-23 20:02:11 +02:00
: ENTRY.223
r ENTRY.222
2024-05-23 20:00:14 +02:00
d 193505809
r sip
2024-05-23 20:02:11 +02:00
: ENTRY.224
r ENTRY.223
2024-05-23 20:00:14 +02:00
d 5863816
r sp
2024-05-23 20:02:11 +02:00
: ENTRY.225
r ENTRY.224
d -1378149864
r startup
: ENTRY.226
r ENTRY.225
2024-05-23 20:00:14 +02:00
d 274826578
r store
2024-05-23 20:02:11 +02:00
: ENTRY.227
r ENTRY.226
2024-05-23 20:00:14 +02:00
d 1976567422
r store-next
2024-05-23 20:02:11 +02:00
: ENTRY.228
r ENTRY.227
2024-05-23 20:00:14 +02:00
d 2090739264
r swap
2024-05-23 20:02:11 +02:00
: ENTRY.229
r ENTRY.228
2024-05-23 20:00:14 +02:00
d -1371592987
r sys:buffers/block
2024-05-23 20:02:11 +02:00
: ENTRY.230
r ENTRY.229
d -1363217974
r sys:buffers/input
: ENTRY.231
r ENTRY.230
2024-05-23 20:00:14 +02:00
d -1359625529
r sys:buffers/loops
2024-05-23 20:02:11 +02:00
: ENTRY.232
r ENTRY.231
2024-05-23 20:00:14 +02:00
d -1357624343
r sys:buffers/needs
2024-05-23 20:02:11 +02:00
: ENTRY.233
r ENTRY.232
d 942827360
r sys:buffers/numeric-conversion
: ENTRY.234
r ENTRY.233
2024-05-23 20:00:14 +02:00
d 1106725658
r sys:buffers/reserved
2024-05-23 20:02:11 +02:00
: ENTRY.235
r ENTRY.234
d -1351755340
r sys:buffers/scope
: ENTRY.236
r ENTRY.235
2024-05-23 20:00:14 +02:00
d 1438625409
r sys:buffers/strings+arrays
2024-05-23 20:02:11 +02:00
: ENTRY.237
r ENTRY.236
d 1558645459
r sys:buffers/variables
: ENTRY.238
r ENTRY.237
d 270722346
r sys:info
: ENTRY.239
r ENTRY.238
2024-05-23 20:00:14 +02:00
d 193506620
r tab
2024-05-23 20:02:11 +02:00
: ENTRY.240
r ENTRY.239
2024-05-23 20:00:14 +02:00
d 275614599
r times
2024-05-23 20:02:11 +02:00
: ENTRY.241
r ENTRY.240
d 505606010
r titles
: ENTRY.242
r ENTRY.241
d 193507188
r tri
: ENTRY.243
r ENTRY.242
d 2090769950
r tri*
: ENTRY.244
r ENTRY.243
d 2090769972
r tri@
: ENTRY.245
r ENTRY.244
2024-05-23 20:00:14 +02:00
d 2090773084
r tuck
2024-05-23 20:02:11 +02:00
: ENTRY.246
r ENTRY.245
2024-05-23 20:00:14 +02:00
d 276987953
r until
2024-05-23 20:02:11 +02:00
: ENTRY.247
r ENTRY.246
d 193508306
r use
: ENTRY.248
r ENTRY.247
d 277155819
r using
: ENTRY.249
r ENTRY.248
2024-05-23 20:00:14 +02:00
d 276287585
r v:dec
2024-05-23 20:02:11 +02:00
: ENTRY.250
r ENTRY.249
2024-05-23 20:00:14 +02:00
d 276293327
r v:inc
2024-05-23 20:02:11 +02:00
: ENTRY.251
r ENTRY.250
d 193508814
r var
: ENTRY.252
r ENTRY.251
d 277702537
r var-n
: ENTRY.253
r ENTRY.252
2024-05-23 20:00:14 +02:00
d 279132286
r while
2024-05-23 20:02:11 +02:00
: ENTRY.254
r ENTRY.253
2024-05-23 20:00:14 +02:00
d 193511454
r xor
2024-05-23 20:02:11 +02:00
: ENTRY.255
r ENTRY.254
d 5864091
r {{
: ENTRY.256
r ENTRY.255
d 5864159
r }}
2024-05-23 20:00:14 +02:00
: Latest
2024-05-23 20:02:11 +02:00
r ENTRY.256
2024-05-23 20:00:14 +02:00
: FREE-SPACE
~~~