2019-04-20 01:33:31 +01:00
|
|
|
REM > types library for mal in BBC BASIC
|
2019-03-31 22:44:13 +01:00
|
|
|
|
|
|
|
|
REM This library should be the only thing that understands the
|
|
|
|
|
REM implementation of mal data types in BBC BASIC. All other
|
|
|
|
|
REM code should use routines in this library to access them.
|
|
|
|
|
|
|
|
|
|
REM As far as other code is concerned, a mal object is just an
|
|
|
|
|
REM opaque 32-bit integer, which might be a pointer, or might not.
|
|
|
|
|
|
2019-05-26 13:28:16 +01:00
|
|
|
REM All mal objects live in an array, Z%(), with string values held
|
|
|
|
|
REM in a parallel array, Z$(). There's one row in Z%(), and one
|
|
|
|
|
REM entry in Z$(), for each mal object.
|
2019-04-13 00:10:36 +01:00
|
|
|
|
2019-05-12 10:45:49 +01:00
|
|
|
REM Z%(x,0) holds the type of an object and other small amounts of
|
2019-05-26 13:28:16 +01:00
|
|
|
REM information. The bottom bit indicates the semantics of Z%(x,1):
|
2019-05-12 10:45:49 +01:00
|
|
|
|
|
|
|
|
REM &01 : Z%(x,1) is a pointer into Z%()
|
2019-05-13 23:27:07 +01:00
|
|
|
|
|
|
|
|
REM Z%(x,2) and Z%(x,3) are always pointers into Z%(), to 'nil' if nothing
|
|
|
|
|
REM else.
|
2019-04-17 23:50:26 +01:00
|
|
|
|
2019-05-15 20:26:36 +01:00
|
|
|
REM The &40 bit is used to distinguish empty lists, vectors and hash-maps.
|
|
|
|
|
REM The &80 bit distinguishes vectors from lists and macros from functions.
|
|
|
|
|
|
2019-04-17 23:50:26 +01:00
|
|
|
REM sS%() is a shadow stack, used to keep track of which mal values might
|
|
|
|
|
REM be referenced from local variables at a given depth of the BASIC call
|
|
|
|
|
REM stack. It grows upwards. sSP% points to the first unused word. sFP%
|
|
|
|
|
REM points to the start of the current shadow stack frame. The first word
|
|
|
|
|
REM of each shadow stack frame is the saved value of sFP%. The rest are
|
|
|
|
|
REM mal values.
|
2019-04-12 23:27:36 +01:00
|
|
|
|
|
|
|
|
REM Types are:
|
2019-05-12 10:45:49 +01:00
|
|
|
REM &00 nil
|
2019-05-13 23:27:07 +01:00
|
|
|
REM &04 boolean
|
|
|
|
|
REM &08 integer
|
|
|
|
|
REM &0C core function
|
2019-05-12 10:45:49 +01:00
|
|
|
REM &01 atom
|
2019-05-13 23:27:07 +01:00
|
|
|
REM &05 free block
|
|
|
|
|
REM &09 list/vector (each object is a cons cell)
|
|
|
|
|
REM &0D environment
|
|
|
|
|
REM &11 hash-map internal node
|
|
|
|
|
REM &15 mal function (first part)
|
|
|
|
|
REM &19 mal function (second part)
|
|
|
|
|
REM &02 string/keyword
|
|
|
|
|
REM &06 symbol
|
2019-05-12 11:19:03 +01:00
|
|
|
REM &0A hash-map leaf node
|
2019-04-12 23:27:36 +01:00
|
|
|
|
|
|
|
|
REM Formats of individual objects are defined below.
|
2019-03-31 22:44:13 +01:00
|
|
|
|
|
|
|
|
DEF PROCtypes_init
|
2019-05-28 20:47:13 +01:00
|
|
|
REM Mal's heap has to be statically dimensioned, but we also
|
|
|
|
|
REM need to leave enough space for BASIC's stack and heap.
|
|
|
|
|
REM The BASIC heap is where all strings live.
|
|
|
|
|
REM
|
|
|
|
|
REM Each row of Z%() consumes 16 bytes. The size of each entry
|
|
|
|
|
REM in Z$() varies by platform: 5 bytes in ARM BBC BASIC V,
|
|
|
|
|
REM 8 bytes in Brandy on a 32-bit system, 16 bytes in Brandy on
|
|
|
|
|
REM a 64-bit system.
|
|
|
|
|
|
2019-05-30 22:31:12 +01:00
|
|
|
DIM Z%((HIMEM-LOMEM)/110,3), Z$((HIMEM-LOMEM)/110)
|
2019-04-17 23:50:26 +01:00
|
|
|
DIM sS%((HIMEM-LOMEM)/64)
|
2019-05-28 20:47:13 +01:00
|
|
|
|
2019-05-13 23:27:07 +01:00
|
|
|
Z%(1,0) = &04 : REM false
|
|
|
|
|
Z%(2,0) = &04 : Z%(2,1) = TRUE : REM true
|
2019-05-15 20:26:36 +01:00
|
|
|
Z%(3,0) = &49 : Z%(3,1) = 3 : REM empty list
|
|
|
|
|
Z%(4,0) = &C9 : Z%(4,1) = 4 : REM empty vector
|
|
|
|
|
Z%(5,0) = &51 : REM empty hashmap
|
2019-04-10 22:25:18 +01:00
|
|
|
next_Z% = 6
|
2019-04-17 23:50:26 +01:00
|
|
|
sSP% = 1
|
|
|
|
|
sFP% = 0
|
2019-04-18 22:40:33 +01:00
|
|
|
F% = 0
|
2019-03-31 22:44:13 +01:00
|
|
|
ENDPROC
|
|
|
|
|
|
|
|
|
|
DEF FNtype_of(val%)
|
2019-05-15 20:26:36 +01:00
|
|
|
=Z%(val%,0) AND &1F
|
2019-03-31 22:44:13 +01:00
|
|
|
|
2019-04-17 23:50:26 +01:00
|
|
|
DEF PROCgc_enter
|
2019-04-19 15:44:18 +01:00
|
|
|
REM PRINT ;sFP%;
|
2019-04-17 23:50:26 +01:00
|
|
|
sS%(sSP%) = sFP%
|
|
|
|
|
sFP% = sSP%
|
|
|
|
|
sSP% += 1
|
2019-04-19 15:44:18 +01:00
|
|
|
REM PRINT " >>> ";sFP%
|
2019-04-17 23:50:26 +01:00
|
|
|
ENDPROC
|
2019-04-14 16:52:59 +01:00
|
|
|
|
2019-05-03 22:57:56 +01:00
|
|
|
REM FNgc_save is equivalent to PROCgc_enter except that it returns a
|
|
|
|
|
REM value that can be passed to PROCgc_restore to pop all the stack
|
|
|
|
|
REM frames back to (and including) the one pushed by FNgc_save.
|
2019-04-18 22:40:33 +01:00
|
|
|
DEF FNgc_save
|
2019-05-03 22:57:56 +01:00
|
|
|
PROCgc_enter
|
2019-04-18 22:40:33 +01:00
|
|
|
=sFP%
|
|
|
|
|
|
2019-04-17 23:50:26 +01:00
|
|
|
DEF PROCgc_exit
|
2019-04-19 15:44:18 +01:00
|
|
|
REM PRINT ;sS%(sFP%);" <<< ";sFP%
|
2019-04-17 23:50:26 +01:00
|
|
|
sSP% = sFP%
|
|
|
|
|
sFP% = sS%(sFP%)
|
2019-04-14 16:52:59 +01:00
|
|
|
ENDPROC
|
|
|
|
|
|
2019-04-18 22:40:33 +01:00
|
|
|
DEF PROCgc_restore(oldFP%)
|
|
|
|
|
sFP% = oldFP%
|
2019-05-03 22:57:56 +01:00
|
|
|
REM PRINT "!!! FP reset"
|
|
|
|
|
PROCgc_exit
|
|
|
|
|
ENDPROC
|
2019-04-18 22:40:33 +01:00
|
|
|
|
2019-04-17 23:50:26 +01:00
|
|
|
DEF FNref_local(val%)
|
|
|
|
|
sS%(sSP%) = val%
|
|
|
|
|
sSP% += 1
|
|
|
|
|
=val%
|
|
|
|
|
|
|
|
|
|
DEF FNgc_exit(val%)
|
|
|
|
|
PROCgc_exit
|
2019-05-03 22:57:56 +01:00
|
|
|
=FNref_local(val%)
|
|
|
|
|
|
|
|
|
|
DEF FNgc_restore(oldFP%, val%)
|
|
|
|
|
PROCgc_restore(oldFP%)
|
|
|
|
|
=FNref_local(val%)
|
2019-04-18 22:40:33 +01:00
|
|
|
|
2019-04-19 15:42:39 +01:00
|
|
|
DEF PROCgc_keep_only2(val1%, val2%)
|
|
|
|
|
PROCgc_exit
|
|
|
|
|
PROCgc_enter
|
|
|
|
|
val1% = FNref_local(val1%)
|
|
|
|
|
val2% = FNref_local(val2%)
|
|
|
|
|
ENDPROC
|
|
|
|
|
|
2019-04-18 22:40:33 +01:00
|
|
|
DEF FNmalloc(type%)
|
2019-04-06 00:54:30 +01:00
|
|
|
LOCAL val%
|
2019-04-19 15:41:17 +01:00
|
|
|
REM If the heap is full, collect garbage first.
|
2019-05-17 00:58:15 +01:00
|
|
|
IF F% = 0 AND next_Z% > DIM(Z%(),1) THEN
|
|
|
|
|
PROCgc
|
|
|
|
|
IF F% = 0 ERROR &40E80950, "Out of mal heap memory"
|
|
|
|
|
ENDIF
|
2019-04-18 22:40:33 +01:00
|
|
|
IF F% <> 0 THEN
|
|
|
|
|
val% = F%
|
|
|
|
|
F% = Z%(val%,1)
|
|
|
|
|
ELSE
|
|
|
|
|
val% = next_Z%
|
|
|
|
|
next_Z% += 1
|
|
|
|
|
ENDIF
|
2019-05-06 18:45:20 +01:00
|
|
|
Z%(val%,0) = type%
|
2019-04-17 23:50:26 +01:00
|
|
|
=FNref_local(val%)
|
2019-04-06 00:54:30 +01:00
|
|
|
|
2019-04-18 22:40:33 +01:00
|
|
|
DEF PROCfree(val%)
|
2019-05-13 23:27:07 +01:00
|
|
|
Z%(val%,0) = &05
|
2019-04-18 22:40:33 +01:00
|
|
|
Z%(val%,1) = F%
|
|
|
|
|
Z%(val%,2) = 0
|
|
|
|
|
Z%(val%,3) = 0
|
2019-05-26 13:28:16 +01:00
|
|
|
Z$(val%) = ""
|
2019-04-18 22:40:33 +01:00
|
|
|
F% = val%
|
|
|
|
|
ENDPROC
|
|
|
|
|
|
|
|
|
|
DEF PROCgc
|
2019-04-19 15:44:18 +01:00
|
|
|
REM PRINT "** START GC **"
|
2019-04-18 22:40:33 +01:00
|
|
|
PROCgc_markall
|
|
|
|
|
PROCgc_sweep
|
2019-04-19 15:44:18 +01:00
|
|
|
REM PRINT "** FINISH GC **"
|
2019-04-18 22:40:33 +01:00
|
|
|
ENDPROC
|
|
|
|
|
|
|
|
|
|
DEF PROCgc_markall
|
|
|
|
|
LOCAL sp%, fp%
|
|
|
|
|
fp% = sFP%
|
2019-04-19 15:44:18 +01:00
|
|
|
REM PRINT ">>marking...";
|
2019-04-18 22:40:33 +01:00
|
|
|
FOR sp% = sSP% - 1 TO 0 STEP -1
|
|
|
|
|
IF sp% = fp% THEN
|
|
|
|
|
fp% = sS%(sp%)
|
2019-04-19 15:44:18 +01:00
|
|
|
REM PRINT " / ";
|
2019-04-18 22:40:33 +01:00
|
|
|
ELSE PROCgc_mark(sS%(sp%))
|
|
|
|
|
ENDIF
|
|
|
|
|
NEXT sp%
|
2019-04-19 15:44:18 +01:00
|
|
|
REM PRINT
|
2019-04-18 22:40:33 +01:00
|
|
|
ENDPROC
|
|
|
|
|
|
|
|
|
|
DEF PROCgc_mark(val%)
|
2019-05-06 18:45:20 +01:00
|
|
|
IF (Z%(val%,0) AND &100) = 0 THEN
|
2019-04-19 15:44:18 +01:00
|
|
|
REM PRINT " ";val%;
|
2019-05-06 18:45:20 +01:00
|
|
|
Z%(val%,0) += &100
|
2019-05-12 10:45:49 +01:00
|
|
|
IF (Z%(val%,0) AND &01) THEN PROCgc_mark(Z%(val%,1))
|
2019-05-13 23:27:07 +01:00
|
|
|
PROCgc_mark(Z%(val%,2))
|
2019-05-12 23:49:51 +01:00
|
|
|
PROCgc_mark(Z%(val%,3))
|
2019-04-18 22:40:33 +01:00
|
|
|
ENDIF
|
|
|
|
|
ENDPROC
|
|
|
|
|
|
|
|
|
|
DEF PROCgc_sweep
|
|
|
|
|
LOCAL val%
|
2019-04-19 15:44:18 +01:00
|
|
|
REM PRINT ">>sweeping ...";
|
2019-04-18 22:40:33 +01:00
|
|
|
FOR val% = 6 TO next_Z% - 1
|
2019-05-13 23:27:07 +01:00
|
|
|
IF FNtype_of(val%) <> &05 AND (Z%(val%,0) AND &100) = 0 THEN
|
2019-04-19 15:44:18 +01:00
|
|
|
REM PRINT " ";val%;
|
2019-04-18 22:40:33 +01:00
|
|
|
PROCfree(val%)
|
2019-05-06 18:45:20 +01:00
|
|
|
ELSE
|
|
|
|
|
Z%(val%,0) -= &100
|
2019-04-18 22:40:33 +01:00
|
|
|
ENDIF
|
|
|
|
|
NEXT val%
|
2019-04-19 15:44:18 +01:00
|
|
|
REM PRINT
|
2019-04-18 22:40:33 +01:00
|
|
|
ENDPROC
|
|
|
|
|
|
2019-05-12 23:49:51 +01:00
|
|
|
DEF FNmeta(val%)
|
|
|
|
|
=Z%(val%,3)
|
|
|
|
|
|
|
|
|
|
DEF FNwith_meta(val%, meta%)
|
|
|
|
|
LOCAL newval%
|
|
|
|
|
newval% = FNmalloc(Z%(val%,0))
|
|
|
|
|
Z%(newval%,1) = Z%(val%,1)
|
|
|
|
|
Z%(newval%,2) = Z%(val%,2)
|
|
|
|
|
Z%(newval%,3) = meta%
|
2019-05-26 13:36:39 +01:00
|
|
|
Z$(newval%) = Z$(val%)
|
2019-05-12 23:49:51 +01:00
|
|
|
=newval%
|
|
|
|
|
|
2019-03-31 22:44:13 +01:00
|
|
|
REM ** Nil **
|
|
|
|
|
|
2019-04-07 10:30:56 +01:00
|
|
|
DEF FNis_nil(val%)
|
|
|
|
|
=FNtype_of(val%) = 0
|
|
|
|
|
|
2019-03-31 22:44:13 +01:00
|
|
|
DEF FNnil
|
|
|
|
|
=0
|
|
|
|
|
|
2019-04-07 11:01:11 +01:00
|
|
|
REM ** Boolean **
|
|
|
|
|
|
2019-04-12 23:38:30 +01:00
|
|
|
REM Z%(x,1) = TRUE or FALSE
|
2019-04-12 23:27:36 +01:00
|
|
|
|
2019-04-07 11:01:11 +01:00
|
|
|
DEF FNis_boolean(val%)
|
2019-05-13 23:27:07 +01:00
|
|
|
=FNtype_of(val%) = &04
|
2019-04-07 11:01:11 +01:00
|
|
|
|
|
|
|
|
DEF FNalloc_boolean(bval%)
|
2019-04-10 22:25:18 +01:00
|
|
|
IF bval% THEN =2
|
|
|
|
|
=1
|
2019-04-07 11:01:11 +01:00
|
|
|
|
|
|
|
|
DEF FNunbox_boolean(val%)
|
2019-04-07 11:02:20 +01:00
|
|
|
IF NOT FNis_boolean(val%) THEN ERROR &40E80911, "Not a boolean"
|
2019-04-12 23:38:30 +01:00
|
|
|
=Z%(val%,1)
|
2019-04-07 11:01:11 +01:00
|
|
|
|
2019-04-07 22:27:28 +01:00
|
|
|
DEF FNis_truish(val%)
|
|
|
|
|
IF FNis_nil(val%) THEN =FALSE
|
2019-04-12 23:38:30 +01:00
|
|
|
IF FNis_boolean(val%) THEN =FNunbox_boolean(val%)
|
2019-04-07 22:27:28 +01:00
|
|
|
=TRUE
|
|
|
|
|
|
2019-03-31 22:44:13 +01:00
|
|
|
REM ** Integers **
|
|
|
|
|
|
2019-04-12 23:27:36 +01:00
|
|
|
REM Z%(x,1) = integer value
|
|
|
|
|
|
2019-03-31 22:44:13 +01:00
|
|
|
DEF FNis_int(val%)
|
2019-05-13 23:27:07 +01:00
|
|
|
=FNtype_of(val%) = &08
|
2019-03-31 22:44:13 +01:00
|
|
|
|
|
|
|
|
DEF FNalloc_int(ival%)
|
|
|
|
|
LOCAL val%
|
2019-05-13 23:27:07 +01:00
|
|
|
val% = FNmalloc(&08)
|
2019-04-10 22:25:18 +01:00
|
|
|
Z%(val%,1) = ival%
|
2019-03-31 22:44:13 +01:00
|
|
|
=val%
|
|
|
|
|
|
|
|
|
|
DEF FNunbox_int(val%)
|
2019-04-07 11:02:20 +01:00
|
|
|
IF NOT FNis_int(val%) THEN ERROR &40E80912, "Not an integer"
|
2019-04-10 22:25:18 +01:00
|
|
|
=Z%(val%,1)
|
2019-03-31 22:44:13 +01:00
|
|
|
|
2019-05-04 21:02:09 +01:00
|
|
|
REM ** Strings and keywords **
|
|
|
|
|
|
2019-05-26 13:28:16 +01:00
|
|
|
REM Z$(x) is the string value
|
|
|
|
|
REM Z%(x,2) points to the next part of the string
|
2019-05-04 21:02:09 +01:00
|
|
|
REM A keyword is a string with first character CHR$(127).
|
2019-04-20 13:44:12 +01:00
|
|
|
|
|
|
|
|
DEF FNis_string(val%)
|
2019-05-13 23:27:07 +01:00
|
|
|
=FNtype_of(val%) = &02
|
2019-04-20 13:44:12 +01:00
|
|
|
|
|
|
|
|
DEF FNalloc_string(sval$)
|
|
|
|
|
LOCAL val%
|
2019-05-13 23:27:07 +01:00
|
|
|
val% = FNmalloc(&02)
|
2019-05-26 13:28:16 +01:00
|
|
|
Z$(val%) = sval$
|
2019-04-20 13:44:12 +01:00
|
|
|
=val%
|
|
|
|
|
|
|
|
|
|
DEF FNunbox_string(val%)
|
|
|
|
|
IF NOT FNis_string(val%) THEN ERROR &40E80914, "Not a string"
|
2019-05-26 13:28:16 +01:00
|
|
|
IF NOT FNis_nil(Z%(val%,2)) ERROR &40E80914, "Cannot unbox a long string"
|
|
|
|
|
=Z$(val%)
|
2019-04-20 13:44:12 +01:00
|
|
|
|
2019-05-27 12:51:20 +01:00
|
|
|
DEF FNstring_append(val%, add$)
|
|
|
|
|
LOCAL newval%
|
2019-05-18 22:28:13 +01:00
|
|
|
IF NOT FNis_string(val%) THEN ERROR &40E80914, "Not a string"
|
2019-05-27 12:51:20 +01:00
|
|
|
newval% = FNalloc_string(Z$(val%))
|
|
|
|
|
IF FNis_nil(Z%(val%,2)) THEN
|
|
|
|
|
IF LEN(Z$(newval%)) + LEN(add$) <= 255 THEN
|
|
|
|
|
Z$(newval%) += add$
|
|
|
|
|
ELSE
|
|
|
|
|
Z%(newval%,2) = FNalloc_string(add$)
|
|
|
|
|
ENDIF
|
2019-05-18 22:28:13 +01:00
|
|
|
ELSE
|
2019-05-27 12:51:20 +01:00
|
|
|
Z%(newval%,2) = FNstring_append(Z%(val%,2), add$)
|
2019-05-18 22:28:13 +01:00
|
|
|
ENDIF
|
2019-05-27 12:51:20 +01:00
|
|
|
=newval%
|
2019-05-18 22:28:13 +01:00
|
|
|
|
2019-05-27 12:51:20 +01:00
|
|
|
DEF FNstring_concat(val%, add%)
|
2019-05-27 22:51:09 +01:00
|
|
|
LOCAL newval%
|
2019-05-18 22:28:13 +01:00
|
|
|
IF NOT FNis_string(val%) THEN ERROR &40E80914, "Not a string"
|
|
|
|
|
IF NOT FNis_string(add%) THEN ERROR &40E80914, "Not a string"
|
2019-05-27 22:51:09 +01:00
|
|
|
newval% = FNalloc_string(Z$(val%))
|
|
|
|
|
IF FNis_nil(Z%(val%,2)) THEN
|
|
|
|
|
IF LEN(Z$(newval%)) + LEN(Z$(add%)) <= 255 THEN
|
|
|
|
|
Z$(newval%) += Z$(add%)
|
|
|
|
|
Z%(newval%,2) = Z%(add%,2)
|
|
|
|
|
ELSE
|
|
|
|
|
Z%(newval%,2) = add%
|
|
|
|
|
ENDIF
|
|
|
|
|
ELSE
|
|
|
|
|
Z%(newval%,2) = FNstring_concat(Z%(val%,2), add%)
|
|
|
|
|
ENDIF
|
|
|
|
|
=newval%
|
2019-05-18 22:28:13 +01:00
|
|
|
|
|
|
|
|
DEF FNstring_len(val%)
|
2019-05-26 13:28:16 +01:00
|
|
|
LOCAL len%
|
|
|
|
|
WHILE NOT FNis_nil(val%)
|
|
|
|
|
len% += LEN(Z$(val%))
|
|
|
|
|
val% = Z%(val%,2)
|
2019-05-18 22:28:13 +01:00
|
|
|
ENDWHILE
|
|
|
|
|
=len%
|
|
|
|
|
|
|
|
|
|
DEF FNstring_chr(val%, pos%)
|
2019-05-26 13:28:16 +01:00
|
|
|
WHILE pos% > LEN(Z$(val%))
|
|
|
|
|
pos% -= LEN(Z$(val%))
|
|
|
|
|
val% = Z%(val%,2)
|
|
|
|
|
IF FNis_nil(val%) THEN =""
|
2019-05-18 22:28:13 +01:00
|
|
|
ENDWHILE
|
2019-05-26 13:28:16 +01:00
|
|
|
=MID$(Z$(val%), pos%, 1)
|
2019-05-18 22:28:13 +01:00
|
|
|
|
2019-03-31 22:44:13 +01:00
|
|
|
REM ** Symbols **
|
|
|
|
|
|
2019-05-26 13:28:16 +01:00
|
|
|
REM Z$(x) = value of the symbol
|
2019-04-12 23:27:36 +01:00
|
|
|
|
2019-03-31 22:44:13 +01:00
|
|
|
DEF FNis_symbol(val%)
|
2019-05-13 23:27:07 +01:00
|
|
|
=FNtype_of(val%) = &06
|
2019-03-31 22:44:13 +01:00
|
|
|
|
|
|
|
|
DEF FNalloc_symbol(sval$)
|
2019-04-11 00:08:56 +01:00
|
|
|
LOCAL val%
|
2019-05-13 23:27:07 +01:00
|
|
|
val% = FNmalloc(&06)
|
2019-05-26 13:28:16 +01:00
|
|
|
Z$(val%) = sval$
|
2019-03-31 22:44:13 +01:00
|
|
|
=val%
|
|
|
|
|
|
|
|
|
|
DEF FNunbox_symbol(val%)
|
2019-04-07 11:02:20 +01:00
|
|
|
IF NOT FNis_symbol(val%) THEN ERROR &40E80915, "Not a symbol"
|
2019-05-26 13:28:16 +01:00
|
|
|
=Z$(val%)
|
2019-03-31 22:44:13 +01:00
|
|
|
|
2019-05-05 13:36:21 +01:00
|
|
|
REM ** Lists and vectors **
|
2019-03-31 22:44:13 +01:00
|
|
|
|
2019-05-05 13:36:21 +01:00
|
|
|
REM Lists and vectors are both represented as linked lists: the only
|
|
|
|
|
REM difference is in the state of the is_vector flag in the head cell
|
|
|
|
|
REM of the list. Note that this means that the tail of a list may be
|
|
|
|
|
REM a vector, and vice versa. FNas_list and FNas_vector can be used
|
|
|
|
|
REM to convert a sequence to a particular type as necessary.
|
|
|
|
|
|
|
|
|
|
REM Z%(x,0) AND &80 = is_vector flag
|
2019-04-12 23:27:36 +01:00
|
|
|
REM Z%(x,1) = index in Z%() of next pair
|
|
|
|
|
REM Z%(x,2) = index in Z%() of first element
|
|
|
|
|
|
2019-05-13 23:06:12 +01:00
|
|
|
REM The empty list is a distinguished value, with elements that match
|
|
|
|
|
REM the spec of 'first' and 'rest'.
|
2019-04-12 23:27:36 +01:00
|
|
|
|
2019-03-31 22:44:13 +01:00
|
|
|
DEF FNempty
|
2019-04-10 22:25:18 +01:00
|
|
|
=3
|
2019-03-31 22:44:13 +01:00
|
|
|
|
2019-05-05 13:36:21 +01:00
|
|
|
DEF FNempty_vector
|
|
|
|
|
=4
|
|
|
|
|
|
2019-03-31 22:44:13 +01:00
|
|
|
DEF FNalloc_pair(car%, cdr%)
|
|
|
|
|
LOCAL val%
|
2019-05-13 23:27:07 +01:00
|
|
|
val% = FNmalloc(&09)
|
2019-04-17 23:50:26 +01:00
|
|
|
Z%(val%,2) = car%
|
|
|
|
|
Z%(val%,1) = cdr%
|
|
|
|
|
=val%
|
2019-04-14 16:52:59 +01:00
|
|
|
|
2019-05-05 13:36:21 +01:00
|
|
|
DEF FNalloc_vector_pair(car%, cdr%)
|
|
|
|
|
LOCAL val%
|
|
|
|
|
val% = FNalloc_pair(car%, cdr%)
|
2019-05-05 23:52:31 +01:00
|
|
|
Z%(val%,0) = Z%(val%,0) OR &80
|
2019-05-05 13:36:21 +01:00
|
|
|
=val%
|
|
|
|
|
|
2019-03-31 22:44:13 +01:00
|
|
|
DEF FNis_empty(val%)
|
2019-05-15 20:26:36 +01:00
|
|
|
=(Z%(val%,0) AND &40) = &40
|
2019-03-31 22:44:13 +01:00
|
|
|
|
2019-05-05 23:52:31 +01:00
|
|
|
DEF FNis_seq(val%)
|
2019-05-13 23:27:07 +01:00
|
|
|
=FNtype_of(val%) = &09
|
2019-03-31 22:44:13 +01:00
|
|
|
|
2019-05-05 23:52:31 +01:00
|
|
|
DEF FNis_list(val%)
|
2019-05-13 23:27:07 +01:00
|
|
|
=FNtype_of(val%) = &09 AND (Z%(val%, 0) AND &80) = &00
|
2019-05-05 23:52:31 +01:00
|
|
|
|
2019-05-05 13:36:21 +01:00
|
|
|
DEF FNis_vector(val%)
|
2019-05-13 23:27:07 +01:00
|
|
|
=FNtype_of(val%) = &09 AND (Z%(val%, 0) AND &80) = &80
|
2019-05-05 13:36:21 +01:00
|
|
|
|
|
|
|
|
DEF FNas_list(val%)
|
2019-05-05 23:52:31 +01:00
|
|
|
IF FNis_list(val%) THEN =val%
|
2019-05-05 13:36:21 +01:00
|
|
|
IF FNis_empty(val%) THEN =FNempty
|
2019-05-06 12:00:25 +01:00
|
|
|
=FNalloc_pair(FNfirst(val%), FNrest(val%))
|
2019-05-05 13:36:21 +01:00
|
|
|
|
|
|
|
|
DEF FNas_vector(val%)
|
2019-05-12 10:45:49 +01:00
|
|
|
IF FNis_vector(val%) THEN =val%
|
2019-05-05 13:36:21 +01:00
|
|
|
IF FNis_empty(val%) THEN =FNempty_vector
|
2019-05-06 12:00:25 +01:00
|
|
|
=FNalloc_vector_pair(FNfirst(val%), FNrest(val%))
|
2019-05-05 13:36:21 +01:00
|
|
|
|
2019-05-06 12:00:25 +01:00
|
|
|
DEF FNfirst(val%)
|
2019-05-05 23:52:31 +01:00
|
|
|
IF NOT FNis_seq(val%) THEN ERROR &40E80916, "Can't get car of non-sequence"
|
2019-04-17 23:50:26 +01:00
|
|
|
=FNref_local(Z%(val%,2))
|
2019-03-31 22:44:13 +01:00
|
|
|
|
2019-05-06 12:00:25 +01:00
|
|
|
DEF FNrest(val%)
|
2019-05-05 23:52:31 +01:00
|
|
|
IF NOT FNis_seq(val%) THEN ERROR &40E80916, "Can't get cdr of non-sequence"
|
2019-04-17 23:50:26 +01:00
|
|
|
=FNref_local(Z%(val%,1))
|
2019-04-06 11:25:40 +01:00
|
|
|
|
2019-04-28 10:41:10 +01:00
|
|
|
DEF FNalloc_list2(val0%, val1%)
|
|
|
|
|
=FNalloc_pair(val0%, FNalloc_pair(val1%, FNempty))
|
|
|
|
|
|
|
|
|
|
DEF FNalloc_list3(val0%, val1%, val2%)
|
|
|
|
|
=FNalloc_pair(val0%, FNalloc_pair(val1%, FNalloc_pair(val2%, FNempty)))
|
|
|
|
|
|
2019-05-06 12:00:25 +01:00
|
|
|
DEF FNcount(val%)
|
2019-04-06 12:43:16 +01:00
|
|
|
LOCAL i%
|
|
|
|
|
WHILE NOT FNis_empty(val%)
|
2019-05-06 12:00:25 +01:00
|
|
|
val% = FNrest(val%)
|
2019-04-06 12:43:16 +01:00
|
|
|
i% += 1
|
|
|
|
|
ENDWHILE
|
|
|
|
|
= i%
|
|
|
|
|
|
2019-05-06 12:00:25 +01:00
|
|
|
DEF FNnth(val%, n%)
|
2019-04-22 19:15:08 +01:00
|
|
|
WHILE n% > 0
|
2019-04-07 18:37:36 +01:00
|
|
|
IF FNis_empty(val%) THEN ERROR &40E80923, "Subscript out of range"
|
2019-05-06 12:00:25 +01:00
|
|
|
val% = FNrest(val%)
|
2019-04-07 18:37:36 +01:00
|
|
|
n% -= 1
|
|
|
|
|
ENDWHILE
|
2019-05-01 12:30:36 +01:00
|
|
|
IF FNis_empty(val%) THEN ERROR &40E80923, "Subscript out of range"
|
2019-05-06 12:00:25 +01:00
|
|
|
=FNfirst(val%)
|
2019-04-07 18:37:36 +01:00
|
|
|
|
2019-04-06 12:43:16 +01:00
|
|
|
REM ** Core functions **
|
|
|
|
|
|
2019-04-12 23:27:36 +01:00
|
|
|
REM Z%(x,1) = index of function in FNcore_call
|
|
|
|
|
|
2019-04-06 12:43:16 +01:00
|
|
|
DEF FNis_corefn(val%)
|
2019-05-13 23:27:07 +01:00
|
|
|
=FNtype_of(val%) = &0C
|
2019-04-06 12:43:16 +01:00
|
|
|
|
|
|
|
|
DEF FNalloc_corefn(fn%)
|
|
|
|
|
LOCAL val%
|
2019-05-13 23:27:07 +01:00
|
|
|
val% = FNmalloc(&0C)
|
2019-04-10 22:25:18 +01:00
|
|
|
Z%(val%,1) = fn%
|
2019-04-06 12:43:16 +01:00
|
|
|
=val%
|
|
|
|
|
|
|
|
|
|
DEF FNunbox_corefn(val%)
|
2019-04-07 11:02:20 +01:00
|
|
|
IF NOT FNis_corefn(val%) THEN ERROR &40E80919, "Not a core function"
|
2019-04-10 22:25:18 +01:00
|
|
|
=Z%(val%,1)
|
2019-04-06 12:43:16 +01:00
|
|
|
|
2019-04-12 23:27:36 +01:00
|
|
|
REM ** Hash-maps **
|
|
|
|
|
|
2019-05-15 20:45:50 +01:00
|
|
|
REM Hash-maps are represented as a crit-bit tree.
|
|
|
|
|
|
2019-05-12 11:19:03 +01:00
|
|
|
REM An internal node has:
|
|
|
|
|
REM Z%(x,0) >> 16 = next bit of key to check
|
|
|
|
|
REM Z%(x,1) = index in Z%() of left child (if next bit of key is 0)
|
|
|
|
|
REM Z%(x,2) = index in Z%() of right child (if next bit of key is 1)
|
|
|
|
|
|
|
|
|
|
REM A leaf node has
|
2019-05-26 13:28:16 +01:00
|
|
|
REM Z$(x) = key
|
2019-05-12 10:45:49 +01:00
|
|
|
REM Z%(x,2) = index in Z%() of value
|
2019-04-06 11:25:40 +01:00
|
|
|
|
2019-05-12 11:19:03 +01:00
|
|
|
REM The empty hash-map is a special value containing no data.
|
2019-04-06 11:25:40 +01:00
|
|
|
|
|
|
|
|
DEF FNempty_hashmap
|
2019-04-10 22:25:18 +01:00
|
|
|
=5
|
2019-04-06 11:25:40 +01:00
|
|
|
|
2019-05-12 11:19:03 +01:00
|
|
|
DEF FNhashmap_alloc_leaf(key$, val%)
|
2019-04-06 11:25:40 +01:00
|
|
|
LOCAL entry%
|
2019-05-12 11:19:03 +01:00
|
|
|
entry% = FNmalloc(&0A)
|
2019-05-26 13:28:16 +01:00
|
|
|
Z$(entry%) = key$
|
2019-05-12 10:45:49 +01:00
|
|
|
Z%(entry%,2) = val%
|
2019-05-12 11:19:03 +01:00
|
|
|
=entry%
|
2019-04-06 11:25:40 +01:00
|
|
|
|
2019-05-12 13:34:26 +01:00
|
|
|
DEF FNhashmap_alloc_node(bit%, left%, right%)
|
|
|
|
|
LOCAL entry%
|
2019-05-13 23:27:07 +01:00
|
|
|
entry% = FNmalloc(&11)
|
2019-05-12 13:34:26 +01:00
|
|
|
Z%(entry%,0) += (bit% << 16)
|
|
|
|
|
Z%(entry%,1) = left%
|
|
|
|
|
Z%(entry%,2) = right%
|
|
|
|
|
=entry%
|
|
|
|
|
|
2019-04-06 11:25:40 +01:00
|
|
|
DEF FNis_hashmap(val%)
|
2019-05-12 11:19:03 +01:00
|
|
|
LOCAL t%
|
|
|
|
|
t% = FNtype_of(val%)
|
2019-05-15 20:26:36 +01:00
|
|
|
=t% = &11 OR t% = &0A
|
2019-05-12 11:19:03 +01:00
|
|
|
|
2019-05-12 13:34:26 +01:00
|
|
|
DEF FNkey_bit(key$, bit%)
|
|
|
|
|
LOCAL cnum%
|
|
|
|
|
cnum% = bit% >> 3
|
|
|
|
|
IF cnum% >= LEN(key$) THEN =FALSE
|
2019-05-18 14:38:32 +01:00
|
|
|
=ASC(MID$(key$, cnum% + 1, 1)) AND (&80 >> (bit% AND 7))
|
2019-05-12 13:34:26 +01:00
|
|
|
|
|
|
|
|
DEF FNkey_bitdiff(key1$, key2$)
|
|
|
|
|
LOCAL bit%
|
|
|
|
|
WHILE FNkey_bit(key1$, bit%) = FNkey_bit(key2$, bit%)
|
|
|
|
|
bit% += 1
|
|
|
|
|
ENDWHILE
|
|
|
|
|
=bit%
|
|
|
|
|
|
2019-05-12 11:19:03 +01:00
|
|
|
DEF FNhashmap_set(map%, key$, val%)
|
2019-05-18 14:38:32 +01:00
|
|
|
LOCAL bit%, nearest%
|
2019-05-15 20:26:36 +01:00
|
|
|
IF FNis_empty(map%) THEN =FNhashmap_alloc_leaf(key$, val%)
|
2019-05-18 14:38:32 +01:00
|
|
|
nearest% = FNhashmap_find(map%, key$)
|
2019-05-26 13:28:16 +01:00
|
|
|
IF Z$(nearest%) = key$ THEN =FNhashmap_replace(map%, key$, val%)
|
|
|
|
|
bit% = FNkey_bitdiff(key$, Z$(nearest%))
|
2019-05-18 14:38:32 +01:00
|
|
|
=FNhashmap_insert(map%, bit%, key$, val%)
|
|
|
|
|
|
|
|
|
|
DEF FNhashmap_insert(map%, bit%, key$, val%)
|
|
|
|
|
LOCAL left%, right%
|
|
|
|
|
IF FNtype_of(map%) = &11 AND (Z%(map%,0) >> 16) < bit% THEN
|
|
|
|
|
IF FNkey_bit(key$, Z%(map%,0) >> 16) THEN
|
|
|
|
|
left% = Z%(map%,1)
|
|
|
|
|
right% = FNhashmap_insert(Z%(map%,2), bit%, key$, val%)
|
2019-05-12 13:34:26 +01:00
|
|
|
ELSE
|
2019-05-18 14:38:32 +01:00
|
|
|
left% = FNhashmap_insert(Z%(map%,1), bit%, key$, val%)
|
|
|
|
|
right% = Z%(map%,2)
|
2019-05-12 13:34:26 +01:00
|
|
|
ENDIF
|
2019-05-18 14:38:32 +01:00
|
|
|
=FNhashmap_alloc_node(Z%(map%,0)>>16, left%, right%)
|
|
|
|
|
ENDIF
|
|
|
|
|
IF FNkey_bit(key$, bit%) THEN
|
|
|
|
|
left% = map%
|
|
|
|
|
right% = FNhashmap_alloc_leaf(key$, val%)
|
|
|
|
|
ELSE
|
|
|
|
|
left% = FNhashmap_alloc_leaf(key$, val%)
|
|
|
|
|
right% = map%
|
2019-05-12 13:34:26 +01:00
|
|
|
ENDIF
|
2019-05-18 14:38:32 +01:00
|
|
|
=FNhashmap_alloc_node(bit%, left%, right%)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
REM Replace a known-present key in a non-empty hashmap.
|
|
|
|
|
DEF FNhashmap_replace(map%, key$, val%)
|
|
|
|
|
LOCAL left%, right%
|
|
|
|
|
IF FNtype_of(map%) = &0A THEN =FNhashmap_alloc_leaf(key$, val%)
|
2019-05-12 13:34:26 +01:00
|
|
|
IF FNkey_bit(key$, Z%(map%,0) >> 16) THEN
|
2019-05-18 14:38:32 +01:00
|
|
|
left% = Z%(map%,1)
|
|
|
|
|
right% = FNhashmap_replace(Z%(map%,2), key$, val%)
|
2019-05-12 13:34:26 +01:00
|
|
|
ELSE
|
2019-05-18 14:38:32 +01:00
|
|
|
left% = FNhashmap_replace(Z%(map%,1), key$, val%)
|
|
|
|
|
right% = Z%(map%,2)
|
2019-05-12 13:34:26 +01:00
|
|
|
ENDIF
|
2019-05-18 14:38:32 +01:00
|
|
|
=FNhashmap_alloc_node(Z%(map%,0)>>16, left%, right%)
|
2019-04-19 15:23:17 +01:00
|
|
|
|
2019-05-12 21:52:15 +01:00
|
|
|
DEF FNhashmap_remove(map%, key$)
|
|
|
|
|
LOCAL child%
|
2019-05-15 20:26:36 +01:00
|
|
|
IF FNis_empty(map%) THEN =map%
|
2019-05-12 21:52:15 +01:00
|
|
|
IF FNtype_of(map%) = &0A THEN
|
2019-05-26 13:28:16 +01:00
|
|
|
IF Z$(map%) = key$ THEN =FNempty_hashmap
|
2019-05-12 21:52:15 +01:00
|
|
|
ENDIF
|
|
|
|
|
IF FNkey_bit(key$, Z%(map%,0) >> 16) THEN
|
|
|
|
|
child% = FNhashmap_remove(Z%(map%,2), key$)
|
2019-05-15 20:26:36 +01:00
|
|
|
IF FNis_empty(child%) THEN =Z%(map%,1)
|
2019-05-12 21:52:15 +01:00
|
|
|
=FNhashmap_alloc_node(Z%(map%,0)>>16, Z%(map%,1), child%)
|
|
|
|
|
ELSE
|
|
|
|
|
child% = FNhashmap_remove(Z%(map%,1), key$)
|
2019-05-15 20:26:36 +01:00
|
|
|
IF FNis_empty(child%) THEN =Z%(map%,2)
|
2019-05-12 21:52:15 +01:00
|
|
|
=FNhashmap_alloc_node(Z%(map%,0)>>16, child%, Z%(map%,2))
|
|
|
|
|
ENDIF
|
|
|
|
|
|
2019-05-18 13:58:14 +01:00
|
|
|
REM FNhashmap_find finds the nearest entry in a non-empty hash-map to
|
|
|
|
|
REM the key requested, and returns the entire entry.
|
|
|
|
|
DEF FNhashmap_find(map%, key$)
|
|
|
|
|
WHILE FNtype_of(map%) = &11
|
|
|
|
|
IF FNkey_bit(key$, Z%(map%,0) >> 16) THEN map% = Z%(map%,2) ELSE map% = Z%(map%,1)
|
|
|
|
|
ENDWHILE
|
|
|
|
|
=map%
|
2019-05-12 21:52:15 +01:00
|
|
|
|
2019-04-06 11:25:40 +01:00
|
|
|
DEF FNhashmap_get(map%, key$)
|
2019-04-07 11:02:20 +01:00
|
|
|
IF NOT FNis_hashmap(map%) THEN ERROR &40E80918, "Can't get item from a non-hashmap"
|
2019-05-15 20:26:36 +01:00
|
|
|
IF FNis_empty(map%) THEN =FNnil
|
2019-05-18 13:58:14 +01:00
|
|
|
map% = FNhashmap_find(map%, key$)
|
2019-05-26 13:28:16 +01:00
|
|
|
IF Z$(map%) = key$ THEN =FNref_local(Z%(map%,2)) ELSE =FNnil
|
2019-04-07 16:01:55 +01:00
|
|
|
|
2019-04-20 14:29:28 +01:00
|
|
|
DEF FNhashmap_contains(map%, key$)
|
|
|
|
|
IF NOT FNis_hashmap(map%) THEN ERROR &40E80918, "Can't get item from a non-hashmap"
|
2019-05-15 20:26:36 +01:00
|
|
|
IF FNis_empty(map%) THEN =FALSE
|
2019-05-18 13:58:14 +01:00
|
|
|
map% = FNhashmap_find(map%, key$)
|
2019-05-26 13:28:16 +01:00
|
|
|
=Z$(map%) = key$
|
2019-04-20 14:29:28 +01:00
|
|
|
|
2019-05-12 11:19:03 +01:00
|
|
|
DEF FNhashmap_keys(map%)
|
2019-05-12 13:34:26 +01:00
|
|
|
=FNhashmap_keys1(map%, FNempty)
|
|
|
|
|
|
|
|
|
|
DEF FNhashmap_keys1(map%, acc%)
|
2019-05-15 20:26:36 +01:00
|
|
|
IF FNis_empty(map%) THEN =acc%
|
2019-05-12 13:34:26 +01:00
|
|
|
IF FNtype_of(map%) = &0A THEN
|
2019-05-26 13:28:16 +01:00
|
|
|
=FNalloc_pair(FNalloc_string(Z$(map%)), acc%)
|
2019-05-12 13:34:26 +01:00
|
|
|
ENDIF
|
2019-05-18 14:38:32 +01:00
|
|
|
=FNhashmap_keys1(Z%(map%,1), FNhashmap_keys1(Z%(map%,2), acc%))
|
2019-05-12 13:34:26 +01:00
|
|
|
|
2019-05-12 21:52:15 +01:00
|
|
|
DEF FNhashmap_vals(map%)
|
|
|
|
|
=FNhashmap_vals1(map%, FNempty)
|
|
|
|
|
|
|
|
|
|
DEF FNhashmap_vals1(map%, acc%)
|
2019-05-15 20:26:36 +01:00
|
|
|
IF FNis_empty(map%) THEN =acc%
|
2019-05-12 21:52:15 +01:00
|
|
|
IF FNtype_of(map%) = &0A THEN
|
|
|
|
|
=FNalloc_pair(Z%(map%,2), acc%)
|
|
|
|
|
ENDIF
|
2019-05-18 14:38:32 +01:00
|
|
|
=FNhashmap_vals1(Z%(map%,1), FNhashmap_vals1(Z%(map%,2), acc%))
|
2019-05-12 21:52:15 +01:00
|
|
|
|
2019-05-12 13:34:26 +01:00
|
|
|
DEF PROChashmap_dump(map%)
|
2019-05-15 20:26:36 +01:00
|
|
|
IF FNis_empty(map%) THEN
|
2019-05-12 13:34:26 +01:00
|
|
|
PRINT "[empty]"
|
|
|
|
|
ELSE
|
|
|
|
|
PRINT "[-----]"
|
|
|
|
|
PROChashmap_dump_internal(map%, "")
|
|
|
|
|
ENDIF
|
|
|
|
|
ENDPROC
|
|
|
|
|
|
|
|
|
|
DEF PROChashmap_dump_internal(map%, prefix$)
|
2019-05-26 13:28:16 +01:00
|
|
|
IF FNtype_of(map%) = &0A PRINT prefix$;Z$(map%)
|
2019-05-13 23:27:07 +01:00
|
|
|
IF FNtype_of(map%) = &11 THEN
|
2019-05-12 13:34:26 +01:00
|
|
|
PRINT prefix$;"<";Z%(map%,0) >> 16;">"
|
|
|
|
|
PROChashmap_dump_internal(Z%(map%,1), prefix$ + "L ")
|
|
|
|
|
PROChashmap_dump_internal(Z%(map%,2), prefix$ + "R ")
|
|
|
|
|
ENDIF
|
|
|
|
|
ENDPROC
|
2019-05-11 16:47:12 +01:00
|
|
|
|
2019-04-07 23:45:29 +01:00
|
|
|
REM ** Functions **
|
|
|
|
|
|
2019-05-12 23:43:48 +01:00
|
|
|
REM A function is represented by two cells:
|
2019-04-30 21:24:40 +01:00
|
|
|
REM Z%(x,0) AND &80 = is_macro flag
|
2019-04-12 23:27:36 +01:00
|
|
|
REM Z%(x,1) = index in Z%() of ast
|
2019-05-12 23:43:48 +01:00
|
|
|
REM Z%(x,2) = y
|
|
|
|
|
|
|
|
|
|
REM Z%(y,1) = index in Z%() of params
|
|
|
|
|
REM Z%(y,2) = index in Z%() of env
|
2019-04-12 23:27:36 +01:00
|
|
|
|
2019-04-07 23:45:29 +01:00
|
|
|
DEF FNis_fn(val%)
|
2019-05-13 23:27:07 +01:00
|
|
|
=FNtype_of(val%) = &15
|
2019-04-07 23:45:29 +01:00
|
|
|
|
2019-04-30 21:24:40 +01:00
|
|
|
DEF FNis_nonmacro_fn(val%)
|
2019-05-13 23:27:07 +01:00
|
|
|
=FNtype_of(val%) = &15 AND (Z%(val%, 0) AND &80) = &00
|
2019-04-30 21:24:40 +01:00
|
|
|
|
|
|
|
|
DEF FNis_macro(val%)
|
2019-05-13 23:27:07 +01:00
|
|
|
=FNtype_of(val%) = &15 AND (Z%(val%, 0) AND &80) = &80
|
2019-04-30 21:24:40 +01:00
|
|
|
|
2019-04-10 23:59:17 +01:00
|
|
|
DEF FNalloc_fn(ast%, params%, env%)
|
2019-05-12 23:43:48 +01:00
|
|
|
LOCAL val1%, val2%
|
2019-05-13 23:27:07 +01:00
|
|
|
val1% = FNmalloc(&15)
|
2019-05-12 23:43:48 +01:00
|
|
|
Z%(val1%,1) = ast%
|
2019-05-13 23:27:07 +01:00
|
|
|
val2% = FNmalloc(&19)
|
2019-05-12 23:43:48 +01:00
|
|
|
Z%(val1%,2) = val2%
|
|
|
|
|
Z%(val2%,1) = params%
|
|
|
|
|
Z%(val2%,2) = env%
|
|
|
|
|
=val1%
|
2019-04-07 23:45:29 +01:00
|
|
|
|
2019-05-15 20:34:06 +01:00
|
|
|
DEF FNas_macro(val%)
|
2019-04-30 21:24:40 +01:00
|
|
|
IF NOT FNis_fn(val%) THEN ERROR &40E8091A, "Not a function"
|
2019-05-15 20:34:06 +01:00
|
|
|
LOCAL newval%
|
|
|
|
|
newval% = FNmalloc(Z%(val%,0) OR &80)
|
|
|
|
|
Z%(newval%,1) = Z%(val%,1)
|
|
|
|
|
Z%(newval%,2) = Z%(val%,2)
|
|
|
|
|
Z%(newval%,3) = Z%(val%,3)
|
|
|
|
|
=newval%
|
2019-04-30 21:24:40 +01:00
|
|
|
|
2019-04-10 23:59:17 +01:00
|
|
|
DEF FNfn_ast(val%)
|
2019-04-07 23:45:29 +01:00
|
|
|
IF NOT FNis_fn(val%) THEN ERROR &40E8091A, "Not a function"
|
2019-04-17 23:50:26 +01:00
|
|
|
=FNref_local(Z%(val%,1))
|
2019-04-07 23:45:29 +01:00
|
|
|
|
2019-04-10 23:59:17 +01:00
|
|
|
DEF FNfn_params(val%)
|
2019-04-07 23:45:29 +01:00
|
|
|
IF NOT FNis_fn(val%) THEN ERROR &40E8091A, "Not a function"
|
2019-05-12 23:43:48 +01:00
|
|
|
=FNref_local(Z%(Z%(val%,2),1))
|
2019-04-07 23:45:29 +01:00
|
|
|
|
|
|
|
|
DEF FNfn_env(val%)
|
|
|
|
|
IF NOT FNis_fn(val%) THEN ERROR &40E8091A, "Not a function"
|
2019-05-12 23:43:48 +01:00
|
|
|
=FNref_local(Z%(Z%(val%,2),2))
|
2019-04-07 23:45:29 +01:00
|
|
|
|
2019-04-20 18:25:09 +01:00
|
|
|
REM ** Atoms **
|
|
|
|
|
|
|
|
|
|
REM Z%(x,1) = index in Z% of current referent
|
|
|
|
|
|
|
|
|
|
DEF FNis_atom(val%)
|
2019-05-12 10:45:49 +01:00
|
|
|
=FNtype_of(val%) = &01
|
2019-04-20 18:25:09 +01:00
|
|
|
|
|
|
|
|
DEF FNalloc_atom(contents%)
|
|
|
|
|
LOCAL val%
|
2019-05-12 10:45:49 +01:00
|
|
|
val% = FNmalloc(&01)
|
2019-04-20 18:25:09 +01:00
|
|
|
Z%(val%,1) = contents%
|
|
|
|
|
=val%
|
|
|
|
|
|
|
|
|
|
DEF FNatom_deref(val%)
|
|
|
|
|
=FNref_local(Z%(val%,1))
|
|
|
|
|
|
|
|
|
|
DEF PROCatom_reset(val%, contents%)
|
|
|
|
|
Z%(val%,1) = contents%
|
|
|
|
|
ENDPROC
|
|
|
|
|
|
2019-04-07 16:01:55 +01:00
|
|
|
REM ** Environments **
|
|
|
|
|
|
2019-04-12 23:27:36 +01:00
|
|
|
REM Z%(x,1) = index in Z% of hash-map
|
|
|
|
|
REM Z%(x,2) = index in Z% of outer environment
|
|
|
|
|
|
2019-04-07 16:01:55 +01:00
|
|
|
DEF FNis_environment(val%)
|
2019-05-13 23:27:07 +01:00
|
|
|
=FNtype_of(val%) = &0D
|
2019-04-07 16:01:55 +01:00
|
|
|
|
|
|
|
|
DEF FNalloc_environment(outer%)
|
|
|
|
|
LOCAL val%
|
2019-05-13 23:27:07 +01:00
|
|
|
val% = FNmalloc(&0D)
|
2019-04-10 22:25:18 +01:00
|
|
|
Z%(val%,1) = FNempty_hashmap
|
|
|
|
|
Z%(val%,2) = outer%
|
2019-04-07 16:01:55 +01:00
|
|
|
=val%
|
|
|
|
|
|
|
|
|
|
DEF FNenvironment_data(val%)
|
|
|
|
|
IF NOT FNis_environment(val%) THEN ERROR &40E8091D, "Not an environment"
|
2019-04-17 23:50:26 +01:00
|
|
|
=FNref_local(Z%(val%,1))
|
2019-04-07 16:01:55 +01:00
|
|
|
|
|
|
|
|
DEF PROCenvironment_set_data(val%, data%)
|
|
|
|
|
IF NOT FNis_environment(val%) THEN ERROR &40E8091D, "Not an environment"
|
2019-04-10 22:25:18 +01:00
|
|
|
Z%(val%,1) = data%
|
2019-04-07 16:01:55 +01:00
|
|
|
ENDPROC
|
|
|
|
|
|
|
|
|
|
DEF FNenvironment_outer(val%)
|
|
|
|
|
IF NOT FNis_environment(val%) THEN ERROR &40E8091D, "Not an environment"
|
2019-04-17 23:50:26 +01:00
|
|
|
=FNref_local(Z%(val%,2))
|
2019-04-19 17:44:19 +01:00
|
|
|
|
|
|
|
|
REM Local Variables:
|
|
|
|
|
REM indent-tabs-mode: nil
|
|
|
|
|
REM End:
|