1
0
mirror of https://github.com/dschmenk/PLASMA.git synced 2025-01-07 00:29:34 +00:00

Improved tail recursion and loop example

This commit is contained in:
David Schmenk 2024-07-11 15:17:05 -07:00
parent de72884699
commit c29ab9bd82
7 changed files with 57 additions and 35 deletions

View File

@ -6,7 +6,7 @@ LISP interpreted on a bytecode VM running on a 1 MHz 6502 is going to be sssllll
- The PROG feature isn't present. Programming is limited to interpreting lambda S-expressions
- Number values are limited to 32 bit integers, no floating point
- Deep recursion. The 6502 architecture limits recursion, so don't expect too much here
- General recursion. The 6502 architecture limits recursion (but see tail recursion below), so don't expect too much here
- Arrays not implemented
However, the code is partitioned to allow for easy extension so some of these missing features could be implemented.
@ -14,7 +14,7 @@ However, the code is partitioned to allow for easy extension so some of these mi
## Features of DRAWL
- 32 bit integers and basic math operators. Hey, better than you probably expected
- Recursion handles about nine levels deep. Better than nothing
- Tail recursion handles handles deep recursion. Check out [loop.lisp](https://github.com/dschmenk/PLASMA/blob/master/src/lisp/loop.lisp)
- Fully garbage collected behind the scenes
- Optionally read LISP source file at startup
- SET and SETQ implemented for setting variables

View File

@ -6,6 +6,6 @@ s-expr.pla is the guts of the system. Some features missing from the LISP 1.5 ma
drawl.pla because this is a sslloowwww implementation of LISP, so named DRAWL in keeping with the speech theme. The file reading and REPL functions are contained here. DRAWL is meant to be extensible so adding PROG and floating point is easily possible.
The sample LISP code comes from the LISP 1.5 manual. These are the only LISP programs that have been run on DRAWL, so other LISP programs may uncover bugs or limitations of DRAWL.
The sample LISP code comes from the LISP 1.5 manual. These are the only LISP programs that have been run on DRAWL, so other LISP programs may uncover bugs or limitations of DRAWL. HOwever, tail recursion is supported - check out loop.lisp
More information and links can be found here: https://github.com/dschmenk/PLASMA/blob/master/doc/DRAWL.md

View File

@ -52,9 +52,6 @@ end
// REPL interface to S-expression evaluator
//
const GC_RESET = 2
byte gc_trigger = GC_RESET
const FILEBUF_SIZE = 128
var readfn // read input routine
var fileref, filebuf // file read vars
@ -165,7 +162,7 @@ new_sym("BYE")=>natv = @natv_bye
new_sym("MEM")=>natv = @natv_memavail
while not quit
putln; print_expr(eval_expr(readfn()))
gc_trigger--; if gc_trigger == 0; gc; gc_trigger = GC_RESET; fin
gc
loop
putln
done

11
src/lisp/loop.lisp Normal file
View File

@ -0,0 +1,11 @@
(LABEL LOOP (LAMBDA (I M FN)
(COND ((AND (< I M) (FN I)),(LOOP (+ 1 I) M FN))
(T,(EQ I M)))
)
)
(LABEL LPRINT (LAMBDA (N)
(ATOM (PRINT N))
)
)
(LOOP 1 100 LPRINT)

View File

@ -476,44 +476,56 @@ end
// Evaluate expression
//
def enter_lambda(expr, params)
def enter_lambda(curl, expr, params)#2 // curl, expr
var args, arglist, pairlist, pair
if !expr or expr=>car <> sym_lambda
puts("Invalid LAMBDA expression: ")
print_expr(expr)
return NULL
return NULL, NULL
fin
//
// Build arg list before prepending to assoc_list
//
args = expr=>cdr=>car
arglist = NULL
while args
args = expr=>cdr=>car
if curl == expr
//
// Update current associations during tail recursion
//
while args
assoc(args=>car)=>cdr = eval_expr(params=>car)
args = args=>cdr
params = params=>cdr
loop
else
//
// Build arg list before prepending to assoc_list
//
arglist = NULL
while args
if arglist
pairlist=>cdr = new_cons
pairlist = pairlist=>cdr
else
arglist = new_cons
pairlist = arglist
fin
pair = new_cons
pair=>car = args=>car
pair=>cdr = eval_expr(params=>car)
pairlist=>car = pair
args = args=>cdr
params = params=>cdr
loop
if arglist
pairlist=>cdr = new_cons
pairlist = pairlist=>cdr
else
arglist = new_cons
pairlist = arglist
pairlist=>cdr = assoc_list
assoc_list = arglist
fin
pair = new_cons
pair=>car = args=>car
pair=>cdr = eval_expr(params=>car)
pairlist=>car = pair
args = args=>cdr
params = params=>cdr
loop
if arglist
pairlist=>cdr = assoc_list
assoc_list = arglist
fin
return expr=>cdr=>cdr=>car
return expr, expr=>cdr=>cdr=>car
end
export def eval_expr(expr)#1
var alist_enter, expr_car
var alist_enter, curl, expr_car
curl = NULL // Current lambda
alist_enter = assoc_list
while expr
if expr->type == CONS_TYPE
@ -526,7 +538,7 @@ export def eval_expr(expr)#1
expr = expr_car=>natv(expr=>cdr) // Native function
break
elsif expr_car=>lambda // DEFINEd lambda S-expression
expr = enter_lambda(expr_car=>lambda, expr=>cdr)
curl, expr = enter_lambda(curl, expr_car=>lambda, expr=>cdr)
elsif expr_car == sym_cond // Inline cond() evaluation
expr = expr=>cdr
while expr
@ -537,10 +549,10 @@ export def eval_expr(expr)#1
expr = expr=>cdr
loop
else // Symbol associated with lambda
expr = enter_lambda(assoc(expr_car)=>cdr, expr=>cdr)
curl, expr = enter_lambda(curl, assoc(expr_car)=>cdr, expr=>cdr)
fin
elsif expr_car->type == CONS_TYPE and expr_car=>car == sym_lambda
expr = enter_lambda(expr_car, expr=>cdr) // Inline lambda
curl, expr = enter_lambda(NULL, expr_car, expr=>cdr) // Inline lambda
fin
else
//

View File

@ -23,3 +23,4 @@ cat lisp/list.lisp | ./ac.jar -ptx DRAWL.po lisp/LIST.LISP TXT
cat lisp/maplist.lisp | ./ac.jar -ptx DRAWL.po lisp/MAPLIST.LISP TXT
cat lisp/gcd.lisp | ./ac.jar -ptx DRAWL.po lisp/GCD.LISP TXT
cat lisp/fact.lisp | ./ac.jar -ptx DRAWL.po lisp/FACT.LISP TXT
cat lisp/loop.lisp | ./ac.jar -ptx DRAWL.po lisp/LOOP.LISP TXT

View File

@ -183,6 +183,7 @@ cp lisp/list.lisp prodos/bld/lisp/LIST.LISP.TXT
cp lisp/maplist.lisp prodos/bld/lisp/MAPLIST.LISP.TXT
cp lisp/gcd.lisp prodos/bld/lisp/GCD.LISP.TXT
cp lisp/fact.lisp prodos/bld/lisp/FACT.LISP.TXT
cp lisp/loop.lisp prodos/bld/lisp/LOOP.LISP.TXT
#mkdir prodos/bld/examples
#cp samplesrc/examples/ex.1.pla prodos/bld/examples/EX.1.PLA.TXT