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 ) )