Lisp Interpreter
Posted: Mon Mar 07, 2016 8:16 am
Here is a small lisp interpreter with a garbage collector and the most basic commands. Integer support only (shouldn't be to hard to change it to floating point or both), it can do strings as well.
Compile it as a console program, works on all platforms in unicode mode.
Supported commands:
print - prints string or numbers
newline - prints a newline
= > < >= <= != compares two numbers and returns T or Nil
eval - evalutes a list
+ - / - % - arithmetic on numbers
car - get first element of list
cdr - rest of list
cons - combine list
load - loads and runs a file
defun - defines a command
progn - evaluates lists continously
setq - binds symbol to variable
while - while true evaluate list
em - return elapsed milliseconds since program started
cond - multiple condition
if - if condition
you can register your own commands in InitSubr() using Defsubr() and Deffsubr()
See the functions that start with F_ to see how they are defined, or I can help make a built in function if you really need to use it.
my github for this project: https://github.com/creamcast/lispinterpreter
Example Programs:
save to a text file and load with (load "filename.txt") or type in manually into the REPL
sum of squares
silly loop - simple program that returns milliseconds it took to process a loop 50000000 times
Compile it as a console program, works on all platforms in unicode mode.
Supported commands:
print - prints string or numbers
newline - prints a newline
= > < >= <= != compares two numbers and returns T or Nil
eval - evalutes a list
+ - / - % - arithmetic on numbers
car - get first element of list
cdr - rest of list
cons - combine list
load - loads and runs a file
defun - defines a command
progn - evaluates lists continously
setq - binds symbol to variable
while - while true evaluate list
em - return elapsed milliseconds since program started
cond - multiple condition
if - if condition
you can register your own commands in InitSubr() using Defsubr() and Deffsubr()
See the functions that start with F_ to see how they are defined, or I can help make a built in function if you really need to use it.
my github for this project: https://github.com/creamcast/lispinterpreter
Code: Select all
EnableExplicit
#HEAPSIZE = 65536
#FREESIZE = 16384
#STACKSIZE = 32768
#SYMSIZE = 256
#BUFSIZE = 256
#nil = 0
#T = 4
#READ_ERROR = -32
#EVAL_ERROR = -64
#APPLY_ERROR = -65
#SUBR_ERROR = -65
#EVLIS_ERROR = -66
#CHECK_ERROR = -67
;error code
Enumeration
#NONE_ERR
#CANT_FIND_ERR
#ARG_SYM_ERR
#ARG_NUM_ERR
#ARG_STR_ERR
#ARG_LIS_ERR
#ARG_LEN0_ERR
#ARG_LEN1_ERR
#ARG_LEN2_ERR
#ARG_LEN3_ERR
#MALFORM_ERR
#CANT_READ_ERR
#ILLEGAL_OBJ_ERR
#MISSING_QUOTE_ERR
EndEnumeration
;arg check codex
Enumeration
#TEST_NONE
#TEST_NUMLIST
#TEST_SYMBOL
#TEST_STRING
#TEST_NUMBER
#TEST_LIST
#TEST_LEN0
#TEST_LEN1
#TEST_LEN2
#TEST_LEN3
#TEST_LENS1
#TEST_LENS2
#TEST_COND
EndEnumeration
Enumeration
#TAG_EMP
#TAG_NUM
#TAG_SYM
#TAG_LIS
#TAG_SUBR
#TAG_FSUBR
#TAG_FUNC
#TAG_STR
EndEnumeration
Enumeration
#FLAG_FRE
#FLAG_USE
EndEnumeration
Enumeration
#TOKTYPE_LPAREN
#TOKTYPE_RPAREN
#TOKTYPE_QUOTE
#TOKTYPE_DOT
#TOKTYPE_NUMBER
#TOKTYPE_SYMBOL
#TOKTYPE_STRING
#TOKTYPE_STRINGERR
#TOKTYPE_OTHER
EndEnumeration
Enumeration
#BACKTRACK_GO
#BACKTRACK_BACK
EndEnumeration
Macro GET_CAR(addr) : heap(addr)\car : EndMacro
Macro GET_CDR(addr) : heap(addr)\cdr : EndMacro
Macro GET_TAG(addr) : heap(addr)\tag : EndMacro
Macro GET_NAME(addr) : heap(addr)\name : EndMacro
Macro GET_NUMBER(addr) : heap(addr)\num : EndMacro
Macro GET_BIND(addr) : heap(addr)\bind : EndMacro
Macro GET_SUBR(addr) : heap(addr)\subroutine : EndMacro
Macro SET_CAR(addr,x) : heap(addr)\car = x : EndMacro
Macro SET_CDR(addr,x) : heap(addr)\cdr = x : EndMacro
Macro SET_TAG(addr,x) : heap(addr)\tag = x : EndMacro
Macro SET_NUMBER(addr,x) : heap(addr)\num = x : EndMacro
Macro SET_NAME(addr, x) : heap(addr)\name = x : EndMacro
Macro SET_SUBR(addr, x) : heap(addr)\subroutine = x : EndMacro
Macro SET_BIND(addr, x) : heap(addr)\bind = x : EndMacro
Macro IS_SYMBOL(addr) : heap(addr)\tag = #TAG_SYM : EndMacro
Macro IS_NUMBER(addr) : heap(addr)\tag = #TAG_NUM : EndMacro
Macro IS_STRING(addr) : heap(addr)\tag = #TAG_STR : EndMacro
Macro IS_NIL(addr) : addr = 0 Or addr = 1 : EndMacro
Macro IS_NOT_NIL(addr) : addr <> 0 And addr <> 1 : EndMacro
Macro IS_LIST(addr) : heap(addr)\tag = #TAG_LIS : EndMacro
Macro MARK_CELL(addr) : heap(addr)\flag = #FLAG_USE : EndMacro
Macro NOMARK_CELL(addr) : heap(addr)\flag = #FLAG_FRE : EndMacro
Macro ArgPop() : ap-1 : EndMacro
Macro ArgPush(addr) : argstk(ap) = addr : ap + 1 : EndMacro
; Procedure ArgPop()
; ap-1
; EndProcedure
;
; Procedure ArgPush(addr.i)
; argstk(ap) = addr
; ap+1
; EndProcedure
Macro CFLOAT : "([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?" : EndMacro
Macro SYMBOL : "[a-zA-Z\_\+|\-|\*|\/|\>\=|\>|\<\=|\<|\=|\!\=|\%|\^]+[a-zA-Z0-9\-\_\+\-\*\/\#\%\$]*" : EndMacro
Macro STRING_ : #DOUBLEQUOTE$ + "(?:[^" + #DOUBLEQUOTE$ + "\\]|\\.)*" + #DOUBLEQUOTE$ : EndMacro
Structure Cell
tag.b
flag.b
name.i
StructureUnion
num.i
bind.i
subroutine.i
EndStructureUnion
car.i
cdr.i
EndStructure
Structure NameCode
name.s
code.i
EndStructure
Structure Token
ch.c
backtrack_flag.b
toktype_type.b
buf.s ;token string
EndStructure
Global NewList NameTable.NameCode()
#QUOTE_CODE = 1
Global global_namecounter = 2
Global current_line.s
Global current_line_index.i = 1
Global string_memory_total.i = 0
Global ep.i; //environment pointer
Global hp.i; //heap pointer
Global sp.i; //stack pointer
Global fc.i; //free counter
Global ap.i; //arglist pointer
Global Dim heap.Cell(#HEAPSIZE)
Global Dim stack.i(#STACKSIZE)
Global Dim argstk.i(#STACKSIZE)
Global stok.Token
stok\backtrack_flag = #BACKTRACK_GO
stok\toktype_type = #TOKTYPE_OTHER
Declare PrintS(addr.i)
Declare ReadList()
Declare Eval(addr.i)
Declare Nullp(addr)
Declare eqp(addr1.i,addr2.i)
Declare Is_Empty(addr.i)
Declare Is_Func(addr.I)
Declare Car(addr)
Declare Cdr(addr)
Declare Used_Cell(addr)
Declare Free_Cell(addr)
Declare SymbolP(addr.i)
Declare NumberP(addr.i)
Declare ListP(addr.i)
Declare StringP(addr.i)
Declare ResetInputBuffer()
Procedure GetNameCode(name.s)
Debug global_namecounter
ForEach NameTable()
If name = NameTable()\name
ProcedureReturn NameTable()\code
EndIf
Next
AddElement(NameTable())
If name.s = "quote"
NameTable()\code = #QUOTE_CODE
Else
NameTable()\code = global_namecounter
EndIf
NameTable()\name = name.s
global_namecounter + 1
ProcedureReturn NameTable()\code
EndProcedure
Procedure.s GetNameStrFromCode(code.i)
ForEach NameTable()
If NameTable()\code = code
ProcedureReturn NameTable()\name
EndIf
Next
EndProcedure
Procedure Print_(msg.s, newline.b = 1)
If newline
PrintN(msg)
Else
Print(msg)
EndIf
ProcedureReturn #True
EndProcedure
Procedure Error(errnum.i, fun.s, arg.i)
Select errnum
Case #CANT_FIND_ERR : Print_(fun + " can't find definition of ",0) : PrintS(arg)
Case #CANT_READ_ERR : Print_(fun + " can't read ",0) : PrintS(arg)
Case #MISSING_QUOTE_ERR : Print_(fun + " is missing a quote ", 0) : PrintS(arg)
Case #ILLEGAL_OBJ_ERR : Print_(fun + " received an illegal object ",0) : PrintS(arg)
Case #ARG_SYM_ERR : Print_(fun + " requires a symbol but received ", 0) : PrintS(arg)
Case #ARG_NUM_ERR : Print_(fun + " requires a number but received ", 0) : PrintS(arg)
Case #ARG_STR_ERR : Print_(fun + " requres a string but recieved ", 0) : PrintS(arg)
Case #ARG_LIS_ERR : Print_(fun + " requires a list but received ", 0) : PrintS(arg)
Case #ARG_LEN0_ERR : Print_(fun + " requires 0 arg but received ", 0) : PrintS(arg)
Case #ARG_LEN1_ERR : Print_(fun + " requires 1 arg but received ", 0) : PrintS(arg)
Case #ARG_LEN2_ERR : Print_(fun + " requires 2 arg but received ", 0) : PrintS(arg)
Case #ARG_LEN3_ERR : Print_(fun + " requires 3 arg but received ", 0) : PrintS(arg)
Case #MALFORM_ERR : Print_(fun + " received malformed args ", 0) : PrintS(arg)
EndSelect
Print_("") ;newline
ProcedureReturn 0
EndProcedure
Procedure Length(addr.i)
Define len.i = 0
While Nullp(addr) = #False
len + 1
addr = cdr(addr)
Wend
ProcedureReturn len
EndProcedure
Procedure IsNumLis(arg.i)
While IS_NOT_NIL(arg)
If NumberP(car(arg))
arg = cdr(arg)
Else
ProcedureReturn 0
EndIf
Wend
ProcedureReturn 1
EndProcedure
Procedure CheckArgs(test.b, fun.s, arg.i)
;ProcedureReturn #True
Select test
Case #TEST_NUMLIST : If IsNumLis(arg) : ProcedureReturn #True : Else : Error(#ARG_NUM_ERR,fun,arg) : ProcedureReturn #CHECK_ERROR : EndIf
Case #TEST_SYMBOL : If SymbolP(arg) : ProcedureReturn #True : Else : Error(#ARG_SYM_ERR,fun,arg) : ProcedureReturn #CHECK_ERROR : EndIf
Case #TEST_NUMBER : If NumberP(arg) : ProcedureReturn #True : Else : Error(#ARG_NUM_ERR,fun,arg) : ProcedureReturn #CHECK_ERROR : EndIf
Case #TEST_STRING : If StringP(arg) : ProcedureReturn #True : Else : Error(#ARG_STR_ERR,fun,arg) : ProcedureReturn #CHECK_ERROR : EndIf
Case #TEST_LIST : If ListP(arg) : ProcedureReturn #True : Else : Error(#ARG_LIS_ERR,fun,arg) : ProcedureReturn #CHECK_ERROR : EndIf
Case #TEST_LEN0 : If Length(arg) = 0 : ProcedureReturn #True : Else : Error(#ARG_LEN0_ERR,fun,arg): ProcedureReturn #CHECK_ERROR : EndIf
Case #TEST_LEN1 : If Length(arg) = 1 : ProcedureReturn #True : Else : Error(#ARG_LEN1_ERR,fun,arg): ProcedureReturn #CHECK_ERROR : EndIf
Case #TEST_LEN2 : If Length(arg) = 2 : ProcedureReturn #True : Else : Error(#ARG_LEN2_ERR,fun,arg): ProcedureReturn #CHECK_ERROR : EndIf
Case #TEST_LEN3 : If Length(arg) = 3 : ProcedureReturn #True : Else : Error(#ARG_LEN3_ERR,fun,arg): ProcedureReturn #CHECK_ERROR : EndIf
EndSelect
ProcedureReturn #SUBR_ERROR
EndProcedure
Procedure MarkObList()
Define addr.i
addr = ep
While NullP(addr) = #False
MARK_CELL(addr)
addr = cdr(addr)
Wend
MARK_CELL(0)
EndProcedure
Procedure MarkCell(addr.i)
If Used_Cell(addr) : ProcedureReturn 0 : EndIf
MARK_CELL(addr)
If car(addr) <> 0
MarkCell(car(addr))
EndIf
If cdr(addr) <> 0
MarkCell(cdr(addr))
EndIf
If GET_BIND(addr) <> 0 And Is_Func(addr)
MarkCell(GET_BIND(addr))
EndIf
EndProcedure
Procedure GbcMark()
Define addr.i, i.i
MarkObList()
addr = ep
While Nullp(addr) = #False
MarkCell(car(addr))
addr = cdr(addr)
Wend
i = 0
While i < ap
MarkCell(argstk(i))
i + 1
Wend
EndProcedure
Procedure ClrCell(addr.i)
If GET_TAG(addr) = #TAG_STR : FreeMemory(GET_NUMBER(addr)) : EndIf
SET_TAG(addr, #TAG_EMP)
heap(addr)\name = 0
SET_CAR(addr,0)
SET_CDR(addr,0)
SET_BIND(addr,0)
EndProcedure
Procedure GbcSweep()
Define addr.i
addr = 0
While addr <= #HEAPSIZE
If Used_Cell(addr)
NOMARK_CELL(addr)
Else
ClrCell(addr)
SET_CDR(addr, hp)
hp = addr
EndIf
addr + 1
Wend
EndProcedure
Procedure GBC()
Define addr.i
;PrintN("enter GBC free= " + Str(fc))
GbcMark()
GbcSweep()
fc = 0
addr = 0
While addr <= #HEAPSIZE
If Is_Empty(addr) : fc + 1 : EndIf
addr + 1
Wend
;PrintN("exit GBC free= " + Str(fc))
EndProcedure
Procedure Push(pt.i)
stack(sp) = pt ;stack[sp++] = pt
sp + 1
EndProcedure
Procedure Pop()
sp - 1 ;stack[--sp]
ProcedureReturn stack(sp)
EndProcedure
Procedure Used_Cell(addr)
If heap(addr)\flag = #FLAG_USE
ProcedureReturn 1
EndIf
ProcedureReturn 0
EndProcedure
Procedure Free_Cell(addr)
If heap(addr)\flag = #FLAG_FRE
ProcedureReturn 1
EndIf
ProcedureReturn 0
EndProcedure
Procedure Is_Subr(addr.i)
If heap(addr)\tag = #TAG_SUBR : ProcedureReturn 1 : EndIf
ProcedureReturn 0
EndProcedure
Procedure Is_FSubr(addr.i)
If heap(addr)\tag = #TAG_FSUBR : ProcedureReturn 1 : EndIf
ProcedureReturn 0
EndProcedure
Procedure Is_Func(addr.i)
If heap(addr)\tag = #TAG_FUNC : ProcedureReturn 1 : EndIf
ProcedureReturn 0
EndProcedure
Procedure Is_Empty(addr.i)
If heap(addr)\tag = #TAG_EMP : ProcedureReturn 1 : EndIf
ProcedureReturn 0
EndProcedure
Procedure Has_Name(addr.i, x.i)
If heap(addr)\name = x
ProcedureReturn #True
Else
ProcedureReturn #False
EndIf
EndProcedure
Procedure.c GetChar()
Define r.c = Asc(Mid(current_line, current_line_index, 1))
If current_line_index >= Len(current_line)
current_line_index = 0
current_line = ""
EndIf
current_line_index + 1
ProcedureReturn r
EndProcedure
Procedure StringToken(buff.s)
Define regex_symbol = CreateRegularExpression(#PB_Any, STRING_)
If MatchRegularExpression(regex_symbol, buff)
FreeRegularExpression(regex_symbol)
ProcedureReturn #True
EndIf
FreeRegularExpression(regex_symbol)
ProcedureReturn #False
EndProcedure
Procedure NumberToken(buff.s)
Define regex_cfloat = CreateRegularExpression(#PB_Any, "^"+ CFLOAT + "$")
If MatchRegularExpression(regex_cfloat, buff)
FreeRegularExpression(regex_cfloat)
ProcedureReturn #True
EndIf
FreeRegularExpression(regex_cfloat)
ProcedureReturn #False
EndProcedure
Procedure SymbolToken(buff.s)
Define regex_symbol = CreateRegularExpression(#PB_Any, SYMBOL)
If MatchRegularExpression(regex_symbol, buff)
FreeRegularExpression(regex_symbol)
ProcedureReturn #True
EndIf
FreeRegularExpression(regex_symbol)
ProcedureReturn #False
EndProcedure
Procedure FreshCell()
Define res.i
res = hp
hp = heap(hp)\cdr ;set heap pointer to next free cell
SET_CDR(res,0)
fc - 1
ProcedureReturn res
EndProcedure
Procedure Cons(car.i, cdr.i)
Define addr.i
addr = FreshCell()
SET_TAG(addr, #TAG_LIS)
SET_CAR(addr, car)
SET_CDR(addr, cdr)
ProcedureReturn addr
EndProcedure
Procedure Car(addr) : ProcedureReturn GET_CAR(addr) : EndProcedure
Procedure Cdr(addr) : ProcedureReturn GET_CDR(addr) : EndProcedure
Procedure Caar(addr) : ProcedureReturn car(car(addr)) : EndProcedure
Procedure Cdar(addr) : ProcedureReturn cdr(car(addr)) : EndProcedure
Procedure Cadr(addr) : ProcedureReturn car(cdr(addr)) : EndProcedure
Procedure Caddr(addr) : ProcedureReturn car(cdr(cdr(addr))) : EndProcedure
Procedure MakeNum(num.i)
Define addr.i
addr = FreshCell()
SET_TAG(addr,#TAG_NUM)
SET_NUMBER(addr,num)
ProcedureReturn addr
EndProcedure
Procedure MakeStr(str.s)
Define addr = FreshCell()
SET_TAG(addr, #TAG_STR)
CompilerIf #PB_Compiler_Unicode
Define *mem = AllocateMemory( (Len(str) + 1) * 2 )
CompilerElse
Define *mem = AllocateMemory(Len(str) + 1)
CompilerEndIf
PokeS(*mem, str)
SET_NUMBER(addr, *mem)
ProcedureReturn addr
EndProcedure
Procedure MakeSym(namecode.i)
Define addr.i
addr = FreshCell()
SET_TAG(addr, #TAG_SYM)
SET_NAME(addr, namecode)
ProcedureReturn addr
EndProcedure
Procedure AssocSym(sym.i, val.i)
ep = cons(cons(sym, val), ep)
EndProcedure
Procedure Assoc(sym.i, lis.i)
If Nullp(lis)
ProcedureReturn 0
ElseIf eqp(sym, caar(lis))
ProcedureReturn car(lis)
Else
ProcedureReturn assoc(sym, cdr(lis))
EndIf
EndProcedure
Procedure Atomp(addr.i)
If IS_NUMBER(addr) Or IS_SYMBOL(addr) Or IS_STRING(addr)
ProcedureReturn #True
Else
ProcedureReturn #False
EndIf
EndProcedure
Procedure NumberP(addr.i)
If IS_NUMBER(addr)
ProcedureReturn #True
Else
ProcedureReturn #False
EndIf
EndProcedure
Procedure SymbolP(addr.i)
If IS_SYMBOL(addr)
ProcedureReturn #True
Else
ProcedureReturn #False
EndIf
EndProcedure
Procedure StringP(addr.i)
If IS_STRING(addr)
ProcedureReturn #True
Else
ProcedureReturn #False
EndIf
EndProcedure
Procedure Listp(addr)
If IS_LIST(addr)
ProcedureReturn #True
Else
ProcedureReturn #False
EndIf
EndProcedure
Procedure Nullp(addr)
If IS_NIL(addr)
ProcedureReturn #True
Else
ProcedureReturn #False
EndIf
EndProcedure
Procedure GetToken()
Define c.c
If stok\backtrack_flag = #BACKTRACK_BACK
stok\backtrack_flag = #BACKTRACK_GO
ProcedureReturn #True
EndIf
If stok\ch = ')'
stok\toktype_type = #TOKTYPE_RPAREN
stok\ch = #Null
ProcedureReturn #True
EndIf
If stok\ch = '('
stok\toktype_type = #TOKTYPE_LPAREN
stok\ch = #Null
ProcedureReturn #True
EndIf
c = GetChar()
While c = ' ' Or c = #TAB;tab
c=GetChar() ;skip whitespace
Wend
Select c
Case '(' : stok\toktype_type = #TOKTYPE_LPAREN
Case ')' : stok\toktype_type = #TOKTYPE_RPAREN
Case 39 : stok\toktype_type = #TOKTYPE_QUOTE
Case '.' : stok\toktype_type = #TOKTYPE_DOT
Case '"' :
Define max_line_len = Len(current_line)
stok\buf = ""
stok\buf = stok\buf + Chr(c)
c = GetChar()
Define inc = 0
While c <> '"'
stok\buf = stok\buf + Chr(c)
c = GetChar()
;prevent unquoted string
inc + 1
If inc > max_line_len
stok\toktype_type = #TOKTYPE_STRINGERR
stok\buf = ""
ProcedureReturn #False
EndIf
Wend
stok\buf = stok\buf + Chr(c) ;add end quote
stok\ch = c
If StringToken(stok\buf)
stok\toktype_type = #TOKTYPE_STRING
Else
stok\toktype_type = #TOKTYPE_OTHER
EndIf
Default :
stok\buf = ""
stok\buf = stok\buf + Chr(c)
c=GetChar()
While c <> 0 And c <> ' ' And c <> '(' And c <> ')'
stok\buf = stok\buf + Chr(c)
c=GetChar()
Wend
stok\ch = c
If NumberToken(stok\buf)
stok\toktype_type = #TOKTYPE_NUMBER
ElseIf SymbolToken(stok\buf)
stok\toktype_type = #TOKTYPE_SYMBOL
Else
stok\toktype_type = #TOKTYPE_OTHER
EndIf
EndSelect
EndProcedure
Procedure Read_()
GetToken()
Select stok\toktype_type
Case #TOKTYPE_NUMBER : ProcedureReturn MakeNum(Val(stok\buf))
Case #TOKTYPE_STRING : ProcedureReturn MakeStr(stok\buf)
Case #TOKTYPE_SYMBOL : ProcedureReturn MakeSym(GetNameCode(stok\buf))
Case #TOKTYPE_QUOTE : ProcedureReturn Cons(MakeSym(GetNameCode("quote")), cons(Read_(), #nil))
Case #TOKTYPE_LPAREN : ProcedureReturn ReadList()
Case #TOKTYPE_STRINGERR : Error(#MISSING_QUOTE_ERR, "read", #nil) : ProcedureReturn #READ_ERROR
EndSelect
Error(#CANT_READ_ERR, "read", #nil)
ProcedureReturn #READ_ERROR
EndProcedure
Procedure ReadList()
Define car.i, cdr.i
GetToken()
If stok\toktype_type = #TOKTYPE_RPAREN : ProcedureReturn #nil : EndIf
If stok\toktype_type = #TOKTYPE_DOT
cdr = Read_()
If cdr = #READ_ERROR : ProcedureReturn #READ_ERROR : EndIf
If Atomp(cdr) : GetToken() : EndIf
ProcedureReturn cdr
Else
stok\backtrack_flag = #BACKTRACK_BACK
car = Read_()
If car = #READ_ERROR : ProcedureReturn #READ_ERROR : EndIf
cdr = ReadList()
If cdr = #READ_ERROR : ProcedureReturn #READ_ERROR : EndIf
ProcedureReturn cons(car, cdr)
EndIf
EndProcedure
Procedure PrintList(addr.i)
If IS_NIL(addr)
Print_(")",0)
ProcedureReturn #True
EndIf
If listp(Cdr(addr)) = #False And Nullp(cdr(addr)) = #False
PrintS(Car(addr))
Print_(" . ", 0)
PrintS(Cdr(addr))
Print_(")", 0)
Else
PrintS(GET_CAR(addr))
Define gcdr = GET_CDR(addr)
If IS_NIL(gcdr)
;do nothing
Else
print_(" ", 0)
EndIf
PrintList(gcdr)
EndIf
EndProcedure
Procedure PrintS(addr.i)
Select GET_TAG(addr)
Case #TAG_NUM : Print_(Str(GET_NUMBER(addr)), 0)
Case #TAG_SYM : Print_(GetNameStrFromCode(GET_NAME(addr)), 0)
Case #TAG_STR : Print_(PeekS(GET_NUMBER(addr)), 0)
Case #TAG_SUBR : Print_("subr", 0)
Case #TAG_FSUBR: Print_("<fsubr>", 0)
Case #TAG_FUNC : Print_("<function>", 0)
Case #TAG_LIS : Print_("(", 0) : PrintList(addr)
Default : Print_("<undef>", 0)
EndSelect
EndProcedure
Procedure Same_Name(addr1, addr2)
If heap(addr1)\name = heap(addr2)\name
ProcedureReturn #True
Else
ProcedureReturn #False
EndIf
EndProcedure
Procedure eqp(addr1.i, addr2.i)
If heap(addr1)\tag = #TAG_NUM And heap(addr2)\tag = #TAG_NUM
If GET_NUMBER(addr1) = GET_NUMBER(addr2)
ProcedureReturn 1
EndIf
EndIf
If heap(addr1)\tag = #TAG_SYM And heap(addr2)\tag = #TAG_SYM
If heap(addr1)\name = heap(addr2)\name
ProcedureReturn 1
EndIf
EndIf
If heap(addr1)\tag = #TAG_STR And heap(addr2)\tag = #TAG_STR
If GET_NUMBER(addr1) = GET_NUMBER(addr2)
ProcedureReturn 1
EndIf
EndIf
ProcedureReturn 0
EndProcedure
;
; Procedure eqp(addr1.i, addr2.i)
; Define gnum1 = GET_NUMBER(addr1)
; Define gnum2 = GET_NUMBER(addr2)
;
; If NumberP(addr1) And NumberP(addr2) And gnum1 = gnum2
; ProcedureReturn 1
; ElseIf SymbolP(addr1) And Symbolp(addr2) And Same_Name(addr1,addr2)
; ProcedureReturn 1
; Else
; ProcedureReturn 0
; EndIf
; EndProcedure
Procedure FindSym(sym.i)
Define addr.i
addr = Assoc(sym,ep)
If addr = 0
ProcedureReturn -1
Else
ProcedureReturn cdr(addr)
EndIf
EndProcedure
Procedure FSubRp(addr.i)
Define val.i
val = FindSym(addr)
If val <> -1
ProcedureReturn IS_FSUBR(val)
Else
ProcedureReturn 0
EndIf
EndProcedure
Procedure SubRp(addr.i)
Define val.i
val = FindSym(addr)
If val <> -1
ProcedureReturn Is_Subr(val)
Else
ProcedureReturn 0
EndIf
EndProcedure
Procedure BindSym(sym.i, val.i)
Define addr.i
addr = Assoc(sym, ep)
If addr = 0
AssocSym(sym,val)
Else
SET_CDR(addr,val)
EndIf
EndProcedure
Procedure BindFunc(name.i, tag.b, func.i)
Define sym.i, val.i
sym = MakeSym(name)
val = FreshCell()
SET_TAG(val, tag)
SET_SUBR(val, func)
SET_CDR(val, 0)
BindSym(sym,val)
EndProcedure
Procedure BindFunc1(name.i, addr.i)
Define sym.i, val.i
sym = MakeSym(name)
val = FreshCell()
SET_TAG(val, #TAG_FUNC)
SET_BIND(val, addr)
SET_CDR(val, 0)
BindSym(sym, val)
EndProcedure
Procedure DefSubr(symcode.i, func.i)
BindFunc(symcode, #TAG_SUBR, func)
EndProcedure
Procedure DefFSubr(symcode.i, func.i)
BindFunc(symcode, #TAG_FSUBR, func)
EndProcedure
Procedure FunctionP(addr.i)
Define val.i
val = FindSym(addr)
If val <> -1
ProcedureReturn IS_FUNC(val)
Else
ProcedureReturn 0
EndIf
EndProcedure
Procedure BindArg(varlist.i, arglist.i)
Define arg1.i, arg2.i
push(ep)
While IS_NOT_NIL(varlist)
arg1 = car(varlist)
arg2 = car(arglist)
AssocSym(arg1, arg2)
varlist = cdr(varlist)
arglist = cdr(arglist)
Wend
EndProcedure
Procedure UnBind()
ep = pop()
EndProcedure
Prototype proto(arglist.i)
Procedure RunSubroutine(fn.proto, arglist.i)
ProcedureReturn fn(arglist)
EndProcedure
Procedure Apply(func.i, args.i)
Define symaddr.i, varlist.i, body.i, res.i
symaddr = FindSym(func)
If symaddr = -1
Error(#CANT_FIND_ERR, "apply", func)
ProcedureReturn #APPLY_ERROR
Else
Select GET_TAG(symaddr)
Case #TAG_SUBR : ProcedureReturn RunSubroutine(GET_SUBR(symaddr), args) ;get subroutine and run it with argument list
Case #TAG_FSUBR : ProcedureReturn RunSubroutine(GET_SUBR(symaddr), args)
Case #TAG_FUNC
varlist = car(GET_BIND(symaddr))
body = cdr(GET_BIND(symaddr))
BindArg(varlist, args)
While IS_NOT_NIL(body)
res = Eval(car(body))
body = cdr(body)
Wend
UnBind()
ProcedureReturn res
Default
Error(#ILLEGAL_OBJ_ERR, "eval", symaddr)
ProcedureReturn #APPLY_ERROR
EndSelect
EndIf
EndProcedure
Procedure CheckGBC()
If fc < #FREESIZE
gbc()
EndIf
EndProcedure
Procedure Evlis(addr.i)
Define car_addr.i, cdr_addr.i
argpush(addr)
CheckGBC()
If IS_NIL(addr)
argpop()
ProcedureReturn addr
Else
car_addr = eval(car(addr))
If car_addr = #EVAL_ERROR
argpop()
ProcedureReturn #EVLIS_ERROR
EndIf
argpush(car_addr)
cdr_addr = evlis(cdr(addr))
argpop()
argpop()
If cdr_addr = #EVLIS_ERROR
ProcedureReturn #EVLIS_ERROR
EndIf
ProcedureReturn cons(car_addr, cdr_addr)
EndIf
EndProcedure
Procedure Eval(addr.i)
Define res.i
If atomp(addr) ;if it is an atom
If NumberP(addr) : ProcedureReturn addr : EndIf ;return number if number
If StringP(addr) : ProcedureReturn addr : EndIf
If SymbolP(addr) ;lookup number if symbol
Define res = FindSym(addr)
If res = -1
Error(#CANT_FIND_ERR, "eval", addr)
ProcedureReturn #EVAL_ERROR
Else
ProcedureReturn res
EndIf
EndIf
EndIf
If listp(addr)
If SymbolP(car(addr)) And Has_Name(car(addr), #QUOTE_CODE) ;if first element is quote return quoted part
ProcedureReturn cadr(addr)
EndIf
If NumberP(car(addr)) ;if list start is number error (list must be a symbol)
Error(#ARG_SYM_ERR, "eval", addr)
ProcedureReturn #EVAL_ERROR
EndIf
If SubRp(car(addr))
Define arg1 = car(addr)
ArgPush(arg1)
Define arg2 = evlis(cdr(addr))
If arg2 = #EVLIS_ERROR
ArgPop()
ProcedureReturn #EVAL_ERROR
EndIf
ArgPush(arg2)
Define r = Apply(arg1, arg2)
ArgPop()
ArgPop()
If r = #APPLY_ERROR : ProcedureReturn #EVAL_ERROR : EndIf
ProcedureReturn r
EndIf
If FSubRp(car(addr))
Define arg1 = car(addr)
ArgPush(arg1)
Define arg2 = cdr(addr)
ArgPush(arg2)
Define r = Apply(arg1, arg2)
ArgPop()
ArgPop()
If r = #APPLY_ERROR : ProcedureReturn #EVAL_ERROR : EndIf
ProcedureReturn r
EndIf
If FunctionP(car(addr))
Define arg1 = car(addr)
ArgPush(arg1)
Define arg2 = evlis(cdr(addr))
If arg2 = #EVLIS_ERROR
ArgPop()
ProcedureReturn #EVAL_ERROR
EndIf
ArgPush(arg2)
Define r = Apply(arg1, arg2)
ArgPop()
ArgPop()
If r = #APPLY_ERROR : ProcedureReturn #EVAL_ERROR : EndIf
ProcedureReturn r
EndIf
EndIf
Error(#CANT_FIND_ERR, "eval", addr)
ProcedureReturn #EVAL_ERROR
EndProcedure
Procedure InitCell()
Define addr.i, addr1.i
addr = 0
While addr < #HEAPSIZE
heap(addr)\flag = #FLAG_FRE
heap(addr)\cdr = addr + 1
addr + 1
Wend
hp = 0
fc = #HEAPSIZE
ep = MakeSym(GetNameCode("nil"))
AssocSym(MakeSym(GetNameCode("nil")), #nil)
AssocSym(MakeSym(GetNameCode("t")), MakeSym(GetNameCode("t")))
sp = 0
ap = 0
EndProcedure
Macro F_NumCmp(op, f_name, f_name_str)
Procedure f_name(arglist)
Define num1, num2
If CheckArgs(#TEST_LEN2, f_name_str, arglist) = #CHECK_ERROR : ProcedureReturn #SUBR_ERROR : EndIf
If CheckArgs(#TEST_NUMLIST, f_name_str, arglist) = #CHECK_ERROR : ProcedureReturn #SUBR_ERROR : EndIf
num1 = GET_NUMBER(car(arglist))
num2 = GET_NUMBER(cadr(arglist))
If num1 op num2
ProcedureReturn #T
Else
ProcedureReturn #nil
EndIf
EndProcedure
EndMacro
F_NumCmp(=, F_Num_EQ, "=" )
F_NumCmp(>, F_Num_GT, ">" )
F_NumCmp(<, F_Num_LT, "<" )
F_NumCmp(>=,F_Num_GE, ">=" )
F_NumCmp(<=,F_Num_LE, "<=" )
F_NumCmp(<>,F_Num_NE, "<>" )
Macro F_Arith(op, f_name, f_name_str)
Procedure f_name(arglist.i)
Define arg.i, res.i
If CheckArgs(#TEST_NUMLIST, f_name_str, arglist) = #CHECK_ERROR
ProcedureReturn #SUBR_ERROR
EndIf
res = GET_NUMBER(car(arglist))
arglist = cdr(arglist)
While IS_NOT_NIL(arglist)
arg = GET_NUMBER(car(arglist))
arglist = cdr(arglist)
res = res op arg
Wend
ProcedureReturn MakeNum(res)
EndProcedure
EndMacro
F_Arith(+, F_Plus, "+")
F_Arith(-, F_Minus, "-")
F_Arith(/, F_Div, "/")
F_Arith(*, F_Mul, "*")
Procedure F_Mod(arglist.i)
Define arg.i, res.i
If CheckArgs(#TEST_NUMLIST, "%", arglist) = #CHECK_ERROR
ProcedureReturn #SUBR_ERROR
EndIf
res = GET_NUMBER(car(arglist))
arglist = cdr(arglist)
While IS_NOT_NIL(arglist)
arg = GET_NUMBER(car(arglist))
arglist = cdr(arglist)
res = Mod(res, arg)
Wend
ProcedureReturn MakeNum(res)
EndProcedure
Procedure F_Defun(arglist.i)
Define arg1, arg2
Define test1 = CheckArgs(#TEST_LEN3, "defun", arglist)
Define test2 = CheckArgs(#TEST_SYMBOL, "defun", car(arglist))
Define test3 = CheckArgs(#TEST_LIST, "defun", cadr(arglist))
Define test4 = Checkargs(#TEST_LIST, "defun", caddr(arglist))
If test1 = #CHECK_ERROR Or test2 = #CHECK_ERROR Or test3= #CHECK_ERROR Or test4 = #CHECK_ERROR
ProcedureReturn #SUBR_ERROR
EndIf
arg1 = car(arglist)
arg2 = cdr(arglist)
Bindfunc1(GET_NAME(arg1), arg2)
ProcedureReturn #T
EndProcedure
Procedure F_Eval(arglist.i)
If CheckArgs(#TEST_LEN1, "eval", arglist) = #CHECK_ERROR : ProcedureReturn #SUBR_ERROR : EndIf
ProcedureReturn eval(car(arglist))
EndProcedure
Procedure F_Apply(arglist.i)
If CheckArgs(#TEST_LEN2, "apply", arglist) = #CHECK_ERROR : ProcedureReturn #SUBR_ERROR : EndIf
If CheckArgs(#TEST_SYMBOL, "apply", car(arglist)) = #CHECK_ERROR : ProcedureReturn #SUBR_ERROR : EndIf
If CheckArgs(#TEST_LIST, "apply", cadr(arglist)) = #CHECK_ERROR : ProcedureReturn #SUBR_ERROR : EndIf
Define arg1.i, arg2.i
arg1 = car(arglist)
arg2 = cadr(arglist)
ProcedureReturn Apply(arg1, arg2)
EndProcedure
Procedure F_NewLine(arglist.i)
Print_("", 1)
ProcedureReturn #T
EndProcedure
Procedure F_Print(arglist.i)
If CheckArgs(#TEST_LEN1, "print", arglist) = #CHECK_ERROR : ProcedureReturn #SUBR_ERROR : EndIf
Define addr = car(arglist)
If GET_TAG(car(arglist)) = #TAG_STR
Print_(Trim(PeekS(GET_NUMBER(addr)), Chr(34)), 0)
Else
PrintS(addr)
EndIf
ProcedureReturn #T
EndProcedure
Procedure F_If(arglist.i)
Define arg1.i, arg2.i, arg3.i
If CheckArgs(#TEST_LEN3, "if", arglist) = #CHECK_ERROR : ProcedureReturn #SUBR_ERROR : EndIf
arg1 = car(arglist)
arg2 = cadr(arglist)
arg3 = car(cdr(cdr(arglist)))
If NullP(eval(arg1)) = #False
ProcedureReturn eval(arg2)
Else
ProcedureReturn eval(arg3)
EndIf
EndProcedure
Declare F_Progn(arglist.i)
Declare F_Cond(arglist.i)
Procedure F_Cond(arglist.i)
Define arg1.i, arg2.i, arg3.i
If NullP(arglist)
ProcedureReturn #nil
EndIf
arg1 = car(arglist)
If CheckArgs(#TEST_LIST, "cond", arg1) = #CHECK_ERROR : ProcedureReturn #SUBR_ERROR : EndIf
arg2 = car(arg1)
arg3 = cdr(arg1)
If NullP(eval(arg2)) = #False
ProcedureReturn F_Progn(arg3)
Else
ProcedureReturn F_Cond(cdr(arglist))
EndIf
EndProcedure
;( while () )
Procedure F_While(arglist.i)
If CheckArgs(#TEST_LEN1, "while", arglist) = #CHECK_ERROR : ProcedureReturn #SUBR_ERROR : EndIf
Define res.i
Repeat
res = Eval(car(arglist))
If res = #EVAL_ERROR : ProcedureReturn #SUBR_ERROR : EndIf
If IS_NIL(res) : Break : EndIf
ForEver
ProcedureReturn #T
EndProcedure
;(em)
Procedure F_ElapsedMilliseconds(arglist.i)
ProcedureReturn MakeNum(ElapsedMilliseconds())
EndProcedure
Procedure F_Progn(arglist.i)
Define res.i
While IS_NOT_NIL(arglist)
res = eval(car(arglist))
If res = #EVAL_ERROR : ProcedureReturn #SUBR_ERROR : EndIf
arglist = cdr(arglist)
Wend
ProcedureReturn res
EndProcedure
Procedure F_SetQ(arglist.i)
Define arg1.i, arg2.i
If CheckArgs(#TEST_LEN2, "setq", arglist) = #CHECK_ERROR Or CheckArgs(#TEST_SYMBOL, "setq", car(arglist)) = #CHECK_ERROR
ProcedureReturn #SUBR_ERROR
EndIf
arg1 = car(arglist)
arg2 = eval(cadr(arglist))
If arg2 = #EVAL_ERROR
ProcedureReturn #SUBR_ERROR
EndIf
BindSym(arg1, arg2)
ProcedureReturn arg2
EndProcedure
Procedure F_Car(arglist.i)
If CheckArgs(#TEST_LEN1, "car", arglist) = #CHECK_ERROR : ProcedureReturn #SUBR_ERROR : EndIf
Define arg1.i = car(arglist)
ProcedureReturn car(arg1)
EndProcedure
Procedure F_Cdr(arglist.i)
If CheckArgs(#TEST_LEN1, "cdr", arglist) = #CHECK_ERROR : ProcedureReturn #SUBR_ERROR : EndIf
Define arg1.i = car(arglist)
ProcedureReturn cdr(arg1)
EndProcedure
Procedure F_Cons(arglist.i)
If CheckArgs(#TEST_LEN2, "cons", arglist) = #CHECK_ERROR : ProcedureReturn #SUBR_ERROR : EndIf
Define arg1 = car(arglist)
Define arg2 = cadr(arglist)
ProcedureReturn cons(arg1, arg2)
EndProcedure
Procedure F_RunFile(arglist.i)
If CheckArgs(#TEST_LEN1, "load", arglist) = #CHECK_ERROR : ProcedureReturn #SUBR_ERROR : EndIf
If CheckArgs(#TEST_STRING, "load", car(arglist)) = #CHECK_ERROR : ProcedureReturn #SUBR_ERROR : EndIf
Define arg = car(arglist)
Define filename.s = PeekS(GET_NUMBER(arg))
Define str.s = ""
If ReadFile(0, Trim(filename, Chr(34)))
While Eof(0) = #False
Define tmpstr.s = ReadString(0)
tmpstr = ReplaceString(tmpstr, #TAB$, "") ;remove tabs
str = str + tmpstr
Wend
CloseFile(0)
current_line = str : current_line_index = 1
Define r = Read_()
If r = #READ_ERROR : Goto ErrorExit : EndIf
r= Eval(r)
If r = #EVAL_ERROR : Goto ErrorExit : EndIf
If r = #READ_ERROR : Goto ErrorExit : EndIf
ProcedureReturn r
Else
ProcedureReturn #nil
EndIf
ErrorExit:
ResetInputBuffer()
ProcedureReturn #nil
EndProcedure
Procedure InitSubr()
;define built in subroutines
;defsubr - evaulate arguments
DefSubr(GetNameCode("print"), @F_Print())
DefSubr(GetNameCode("="), @F_Num_EQ())
DefSubr(GetNameCode(">"), @F_Num_GT())
DefSubr(GetNameCode("<"), @F_Num_LT())
DefSubr(GetNameCode(">="), @F_Num_GE())
DefSubr(GetNameCode("<="), @F_Num_LE())
DefSubr(GetNameCode("!="), @F_Num_NE())
DefSubr(GetNameCode("apply"), @F_Apply())
DefSubr(GetNameCode("eval"), @F_Eval())
DefSubr(GetNameCode("+"), @F_Plus())
DefSubr(GetNameCode("*"), @F_Mul())
DefSubr(GetNameCode("/"), @F_Div())
DefSubr(GetNameCode("-"), @F_Minus())
DefSubr(GetNameCode("%"), @F_Mod())
DefSubr(GetNameCode("car"), @F_Car())
DefSubr(GetNameCode("cdr"), @F_Cdr())
DefSubr(GetNameCode("cons"), @F_Cons())
DefSubr(GetNameCode("load"), @F_RunFile())
;deffsubr - do not evaluate arguments
DefFSubr(GetNameCode("defun"), @F_Defun())
DefFSubr(GetNameCode("progn"), @F_Progn())
DefFSubr(GetNameCode("setq"), @F_SetQ())
DefFSubr(GetNameCode("while"), @F_While())
DefFSubr(GetNameCode("em"), @F_ElapsedMilliseconds())
DefFSubr(GetNameCode("cond"), @F_Cond())
DefFSubr(GetNameCode("if"), @F_If())
DefFSubr(GetNameCode("newline"), @F_NewLine())
EndProcedure
Procedure ResetInputBuffer()
current_line_index = 1
current_line = ""
EndProcedure
Procedure Main()
OpenConsole()
InitCell()
InitSubr()
Define ret = 0
repl:
If ret = 0
Repeat
Print_("> ",0)
current_line = Input() : current_line_index = 1
Define r = Read_()
If r = #READ_ERROR : ret = 1 : Goto repl : EndIf
r = Eval(r)
If r = #EVAL_ERROR : ret = 1 : Goto repl : EndIf
If r = #READ_ERROR : ret = 1 : Goto repl : EndIf
PrintS(r)
Print_("")
ForEver
Else
If ret = 1
ResetInputBuffer()
ret = 0
Goto repl
Else
ProcedureReturn 0
EndIf
EndIf
EndProcedure
Main()
save to a text file and load with (load "filename.txt") or type in manually into the REPL
sum of squares
Code: Select all
(progn
(defun square (x) ( * x x))
(defun sum-of-squares (x y) ( + (square x) ( square y)))
(sum-of-squares 10 11)
)
Code: Select all
(progn (setq top 50000000) (setq n 0) (setq t1 (em)) (while (> top (setq n (+ n 1 ))) ) (- (em) t1 ) )