1
0
mirror of https://github.com/dschmenk/PLASMA.git synced 2025-08-08 09:25:19 +00:00
Files
PLASMA/src/lisp/drawl.pla
2024-07-07 17:15:56 -07:00

856 lines
16 KiB
Plaintext

include "inc/cmdsys.plh"
include "inc/int32.plh"
include "inc/args.plh"
include "inc/fileio.plh"
const TYPE_MASK = $70
const NIL = $00
const BOOL_FALSE = $00
const BOOL_TRUE = $01
const CONS_TYPE = $10
const SYM_TYPE = $20
const SYM_LEN = $0F
const NUM_TYPE = $30
const NUM_INT = $31
const MARK_BIT = $80
const MARK_MASK = $7F
struc t_elem
word link
byte type
end
struc t_cons
res[t_elem]
word car
word cdr
end
struc t_sym
res[t_elem]
var natv
var lambda
char[0] name
end
struc t_numint
res[t_elem]
var intval[2]
end
predef eval_expr(expr)
var sym_quote, sym_lambda
res[t_elem] pred_true = 0, 0, BOOL_TRUE
res[t_elem] pred_false = 0, 0, BOOL_FALSE
var cons_list = NULL
var cons_free = NULL
var int_list = NULL
var int_free = NULL
var sym_list = NULL
var assoc_list = NULL // SYM->value association list
const FILEBUF_SIZE = 128
var readfn // read input routine
var fileref, filebuf // file read vars
byte quit = FALSE // quit interpreter flag
//
// Garbage collector
//
const GC_RESET = 2
byte gc_trigger = GC_RESET
def mark_list(listptr)#0
while listptr
listptr->type = listptr->type | MARK_BIT
listptr = listptr=>link
loop
end
def mark_elems#0
mark_list(cons_list)
mark_list(int_list)
end
def sweep_expr(expr)#0
while expr
expr->type = expr->type & MARK_MASK
if expr->type == CONS_TYPE
sweep_expr(expr=>car)
expr = expr=>cdr
else
expr = NULL
fin
loop
end
def sweep_used#0
var symptr
sweep_expr(assoc_list)
symptr = sym_list
while symptr
if symptr=>lambda
sweep_expr(symptr=>lambda)
fin
symptr = symptr=>link
loop
end
def collect_list(listhead, freehead)#2
var listptr, prevptr
prevptr = NULL
listptr = listhead
while listptr
if listptr->type & MARK_BIT
if prevptr
prevptr=>link = listptr=>link
listptr=>link = freehead
freehead = listptr
listptr = prevptr=>link
else
listhead = listptr=>link
listptr=>link = freehead
freehead = listptr
listptr = listhead
fin
else
prevptr = listptr
listptr = listptr=>link
fin
loop
return listhead, freehead
end
def collect_unused#0
cons_list, cons_free = collect_list(cons_list, cons_free)
int_list, int_free = collect_list(int_list, int_free)
end
def gc#0
mark_elems
sweep_used
collect_unused
gc_trigger = GC_RESET
end
//
// Build ATOMS
//
def new_cons
var consptr
if cons_free
consptr = cons_free
cons_free = cons_free=>link
//puts("Recycle cons\n")
else
consptr = heapalloc(t_cons)
//puts("Alloc cons\n")
fin
consptr=>link = cons_list
cons_list = consptr
consptr->type = CONS_TYPE
consptr=>car = NULL
consptr=>cdr = NULL
return consptr
end
def match_int(intlo, inthi)
var intptr
intptr = int_list
while intptr
if intptr=>intval[0] == intlo and intptr=>intval[1] == inthi
//puts("Match int: ")
//puti(int); putln
return intptr
fin
intptr = intptr=>link
loop
return NULL
end
def new_int(intlo, inthi)
var intptr
intptr = match_int(intlo, inthi)
if intptr; return intptr; fin
if int_free
intptr = int_free
int_free = int_free=>link
//puts("Recycle int\n")
else
intptr = heapalloc(t_numint)
//puts("Alloc int\n")
fin
intptr=>link = int_list
int_list = intptr
intptr->type = NUM_INT
intptr=>intval[0] = intlo
intptr=>intval[1] = inthi
//puts("New int: "); puti(int); putln
return intptr
end
def match_sym(symstr)
var symptr
byte len, typelen, i
len = ^symstr
typelen = SYM_TYPE | len
len--; symstr++
symptr = sym_list
while symptr
if symptr->type == typelen
for i = 0 to len
if symptr->name[i] <> symstr->[i]; break; fin
next
if i > len
//puts("Match symbol: ")
//puts(symstr - 1); putln
return symptr
fin
fin
symptr = symptr=>link
loop
return NULL
end
def new_sym(symstr)
var symptr
symptr = match_sym(symstr)
if symptr; return symptr; fin // Return already existing symbol
symptr = heapalloc(t_sym + ^symstr)
symptr=>link = sym_list
sym_list = symptr
symptr->type = ^symstr | SYM_TYPE
symptr=>natv = NULL
symptr=>lambda = NULL
memcpy(symptr + name, symstr + 1, ^symstr)
//puts("New symbol: "); puts(symstr); putln
return symptr
end
//
// Build/set association between symbols and values
//
def assoc(symptr)
var pair
if symptr->type & TYPE_MASK == SYM_TYPE
//
// Search association list for symbol
//
pair = assoc_list
while pair
if (pair=>car=>car == symptr)
return pair=>car
fin
pair = pair=>cdr
loop
fin
return NULL // SYM not associated
end
def new_assoc(symptr, valptr)#0
var pair, newlist
if symptr and (symptr->type & TYPE_MASK <> SYM_TYPE)
puts("Not a SYM in new_assoc\n")
return
fin
pair = new_cons
pair=>car = symptr
pair=>cdr = valptr
newlist = new_cons
newlist=>car = pair
newlist=>cdr = assoc_list
assoc_list = newlist
end
def set_assoc(symptr, valptr)#0
var pair
//
// Search association list for symbol
//
pair = assoc(symptr)
if pair
pair=>cdr = valptr // update association
else
new_assoc(symptr, valptr) // add association if unknown
fin
end
//
// Print textual representation of S-expression
//
def print_atom(atom)#0
char prstr[32]
if not atom
puts("NIL")
else
when atom->type & TYPE_MASK
is NIL
putc(atom->type ?? 'T' :: 'F')
break
is NUM_TYPE
when atom->type
is NUM_INT
puti32(atom + intval)
break
wend
break
is SYM_TYPE
prstr = atom->type & SYM_LEN
memcpy(@prstr + 1, atom + name, prstr)
puts(@prstr)
break;
otherwise
puts("Unkown atom type\n")
wend
fin
end
def print_expr(expr)#0
if not expr
puts("NIL")
else
if expr->type == CONS_TYPE
putc('(')
while expr and expr->type == CONS_TYPE
print_expr(expr=>car)
expr = expr=>cdr
if expr
if expr->type <> CONS_TYPE
putc('.')
print_atom(expr)
expr = NULL
else
putc(' ')
fin
fin
loop
putc(')')
else
print_atom(expr)
fin
fin
end
//
// Parse textual representation of S-expression
//
def is_int(c); return c >= '0' and c <= '9'; end
def is_alphasym(c)
c=toupper(c)
return c >= '*' and c <= 'Z' and c <> '.'
end
def parse_int(evalptr)#2 // return evalptr, intptr
var int[2]
byte sign
zero32
sign = FALSE
if ^evalptr == '-'
sign = TRUE
evalptr++
fin
while ^evalptr >= '0' and ^evalptr <= '9'
muli16(10); addi16(^evalptr - '0')
evalptr++
loop
if sign; neg32; fin
store32(@int)
return evalptr, new_int(int[0], int[1])
end
def parse_sym(evalptr)#2 // return evalptr, symptr
var symstr
symstr = evalptr - 1
while is_alphasym(^evalptr)
^evalptr = toupper(^evalptr)
evalptr++
loop
^symstr = evalptr - symstr - 1
if ^symstr > 31; ^symstr = 31; fin
return evalptr, new_sym(symstr)
end
def parse_expr(evalptr, level, refill)#2 // return evalptr, exprptr
var exprptr, consptr, elemptr, quotecons
exprptr = NULL
consptr = NULL
while TRUE
//
// Parse textual S-expression
//
elemptr = NULL
when ^evalptr
is 0
if level
// Refill input buffer
evalptr = refill()
else
return evalptr, exprptr
fin
break
is ' '
is ','
evalptr++
break
is ')'
if not exprptr
exprptr = new_cons // NIL
fin
return evalptr + 1, exprptr
is '('
evalptr++
if level == 0
level++
else
evalptr, elemptr = parse_expr(evalptr, 1, refill)
fin
break
is '\''
evalptr++
evalptr, elemptr = parse_expr(evalptr, 0, refill)
quotecons = new_cons
quotecons=>car = sym_quote
quotecons=>cdr = new_cons
quotecons=>cdr=>car = elemptr
elemptr = quotecons
if level == 0
return evalptr, elemptr
fin
break
is '.'
evalptr++
evalptr, elemptr = parse_expr(evalptr, 0, refill)
//
// Add expression to CDR
//
if not (consptr and consptr=>car)
puts("Invalid . operator\n")
return evalptr, NULL
fin
consptr=>cdr = elemptr
return evalptr, exprptr
otherwise
if (^evalptr == '-' and is_int(^(evalptr+1))) or is_int(^evalptr)
evalptr, elemptr = parse_int(evalptr)
elsif is_alphasym(^evalptr)
evalptr, elemptr = parse_sym(evalptr)
else
putc('\\')
putc(^evalptr)
evalptr++
fin
if level == 0
return evalptr, elemptr
fin
wend
if elemptr
//
// Add element to S-expression
//
if not consptr
consptr = new_cons
exprptr = consptr
else
consptr=>cdr = new_cons
consptr = consptr=>cdr
fin
//
// Add element to CAR
//
consptr=>car = elemptr
fin
loop
return evalptr, exprptr
end
//
// Evaluate expression
//
def eval_atom(atom)
var pair
if atom->type & TYPE_MASK == SYM_TYPE
atom = assoc(atom)=>cdr
fin
return atom
end
def eval_lambda(expr, params)
var args, assoc_org, result
if !expr or expr=>car <> sym_lambda
puts("Invalid LAMBDA expression: ")
print_expr(expr)
return NULL
fin
assoc_org = assoc_list
args = expr=>cdr=>car
while args
new_assoc(args=>car, eval_expr(params=>car))
args = args=>cdr
params = params=>cdr
loop
result = eval_expr(expr=>cdr=>cdr=>car)
assoc_list = assoc_org
return result
end
def eval_expr(expr)
if expr
if expr->type == CONS_TYPE
if expr=>car->type & TYPE_MASK == SYM_TYPE
if expr=>car=>natv
return expr=>car=>natv(expr=>cdr)
elsif expr=>car=>lambda
return eval_lambda(expr=>car=>lambda, expr=>cdr)
else
return eval_lambda(assoc(expr=>car)=>cdr, expr=>cdr)
fin
elsif expr=>car->type == CONS_TYPE and expr=>car=>car == sym_lambda
return eval_lambda(expr=>car, expr=>cdr)
fin
else
return eval_atom(expr)
fin
fin
return NULL
end
//
// Base native functions
//
def eval_pred(bool)
return bool ?? @pred_true :: @pred_false
end
def natv_atom(atom)
return eval_pred(!atom or atom->type <> CONS_TYPE)
end
def natv_eq(expr)
return eval_pred(eval_expr(expr=>car) == eval_expr(expr=>cdr=>car))
end
def natv_cons(expr)
var consptr
consptr = new_cons
consptr=>car = eval_expr(expr=>car)
consptr=>cdr = eval_expr(expr=>cdr=>car)
return consptr
end
def natv_car(expr)
return eval_expr(expr=>car)=>car
end
def natv_cdr(expr)
return eval_expr(expr=>car)=>cdr
end
def natv_quote(expr)
return expr=>car
end
def natv_cond(expr)
while expr
if eval_expr(expr=>car=>car) == @pred_true
return eval_expr(expr=>car=>cdr=>car)
fin
expr = expr=>cdr
loop
return NULL
end
def natv_null(expr)
var result
result = eval_expr(expr=>car)
return eval_pred(!result or !result->type)
end
def natv_label(expr)
var valptr
valptr = expr=>cdr=>car
set_assoc(expr=>car, valptr)
return valptr
end
def natv_define(expr)
var symptr, funclist, funcptr
funclist = NULL
if expr
funclist = new_cons
funcptr = funclist
fin
while expr
symptr = expr=>car=>car
symptr=>lambda = expr=>car=>cdr=>car
funcptr=>car = symptr
expr = expr=>cdr
if expr
funcptr=>cdr = new_cons
funcptr = funcptr=>cdr
fin
loop
return funclist
end
def natv_set(expr)
var valptr
valptr = eval_expr(expr=>cdr=>car)
set_assoc(eval_expr(expr=>car), valptr)
return valptr
end
def natv_setq(expr)
var valptr
valptr = eval_expr(expr=>cdr=>car)
set_assoc(expr=>car, valptr)
return valptr
end
def eval_num(expr)#2
var result
result = eval_expr(expr=>car)
if result->type == NUM_INT
return result=>intval[0], result=>intval[1]
fin
puts("Not an number\n")
return 0, 0
end
def natv_add(expr)
var num[2]
zero32
while expr
num[0], num[1] = eval_num(expr)
add32(@num)
expr = expr=>cdr
loop
store32(@num)
return new_int(num[0], num[1])
end
def natv_sub(expr)
var num[2]
num[0], num[1] = eval_num(expr)
load32(@num)
num[0], num[1] = eval_num(expr=>cdr)
sub32(@num)
store32(@num)
return new_int(num[0], num[1])
end
def natv_mul(expr)
var num[2]
num[0], num[1] = eval_num(expr)
load32(@num)
num[0], num[1] = eval_num(expr=>cdr)
mul32(@num)
store32(@num)
return new_int(num[0], num[1])
end
def natv_div(expr)
var num[2]
num[0], num[1] = eval_num(expr)
load32(@num)
num[0], num[1] = eval_num(expr=>cdr)
div32(@num)
store32(@num)
return new_int(num[0], num[1])
end
def natv_rem(expr)
var num[2]
num[0], num[1] = eval_num(expr)
load32(@num)
num[0], num[1] = eval_num(expr=>cdr)
num[1], num[0] = div32(@num)
return new_int(num[0], num[1])
end
def natv_gt(expr)
var num[2]
num[0], num[1] = eval_num(expr)
load32(@num)
num[0], num[1] = eval_num(expr=>cdr)
return eval_pred(isgt32(@num))
end
def natv_lt(expr)
var num[2]
num[0], num[1] = eval_num(expr)
load32(@num)
num[0], num[1] = eval_num(expr=>cdr)
return eval_pred(islt32(@num))
end
def natv_print(expr)
print_expr(eval_expr(expr=>car))
putln
return NULL
end
def natv_bye(expr)
quit = TRUE
return NULL // Quick exit from REPL
end
//
// Install default functions
//
def install_defaults#0
new_assoc(new_sym("NIL"), NULL)
new_assoc(new_sym("T"), @pred_true)
new_assoc(new_sym("F"), @pred_false)
sym_lambda = new_sym("LAMBDA")
sym_quote = new_sym("QUOTE")
sym_quote=>natv = @natv_quote)
new_sym("CAR")=>natv = @natv_car)
new_sym("CDR")=>natv = @natv_cdr)
new_sym("CONS")=>natv = @natv_cons)
new_sym("ATOM")=>natv = @natv_atom)
new_sym("EQ")=>natv = @natv_eq)
new_sym("COND")=>natv = @natv_cond)
new_sym("SET")=>natv = @natv_set)
new_sym("SETQ")=>natv = @natv_setq)
new_sym("NULL")=>natv = @natv_null)
new_sym("LABEL")=>natv = @natv_label)
new_sym("DEFINE")=>natv = @natv_define)
new_sym("+")=>natv = @natv_add)
new_sym("-")=>natv = @natv_sub)
new_sym("*")=>natv = @natv_mul)
new_sym("/")=>natv = @natv_div)
new_sym("REM")=>natv = @natv_rem)
new_sym(">")=>natv = @natv_gt)
new_sym("<")=>natv = @natv_lt)
new_sym("PRINT")=>natv = @natv_print)
new_sym("BYE")=>natv = @natv_bye)
end
//
// REPL interface to S-expression evaluator
//
def refill_keybd
var readline
repeat
readline = gets('>'|$80)
^(readline + ^readline + 1) = 0
readline++
until ^readline
return readline
end
def read_keybd
var readline, expr
repeat
readline = gets('?'|$80)
^(readline + ^readline + 1) = 0
readline++
until ^readline
drop, expr = parse_expr(readline, 0, @refill_keybd)
//print_expr(expr); putln // DEBUG - print parsed expression
return expr
end
def read_fileline
var len
repeat
len = fileio:read(fileref, filebuf, FILEBUF_SIZE-1)
if len
if ^(filebuf + len - 1) == $0D
len-- // Remove trailing carriage return
fin
^(filebuf + len) = 0 // NULL terminate
else
fileio:close(fileref)
readfn = @read_keybd
return FALSE
fin
until len
return TRUE
end
def refill_file
if not read_fileline
puts("File input prematurely ended\n")
return refill_keybd
fin
return filebuf
end
def read_file
var expr
if not read_fileline
return read_keybd
fin
drop, expr = parse_expr(filebuf, 0, @refill_file)
return expr
end
def parse_cmdline#0
var filename
puts("DRAWL (LISP 1.5) symbolic processing")
readfn = @read_keybd
filename = argNext(argFirst)
if ^filename
fileref = fileio:open(filename)
if fileref
fileio:newline(fileref, $7F, $0D)
readfn = @read_file
filebuf = heapalloc(FILEBUF_SIZE)
fin
fin
end
parse_cmdline
install_defaults
while not quit
putln; print_expr(eval_expr(readfn()))
gc_trigger--; if gc_trigger == 0; gc; gc_trigger = GC_RESET; fin
loop
done