!***********************************************************************
!*
!*                            LISP for EMAS
!*
!*                 Edinburgh Regional Computing Centre
!*
!*        Updated by R.D. Eager   University of Kent   MCMLXXXII
!*
!***********************************************************************
!
!
!***********************************************************************
!*
!*          Constants
!*
!***********************************************************************
!
%constantinteger no = 0, yes = 1
%constantinteger setup = 1,instream = 2,outstream = 3
%constantinteger ssdatafiletype = 4;    ! Subsystem file type
%constantinteger marker = m'LISP';      ! Marker at the front of LISP machine files
%constantinteger default line length = 80
                                        ! For formatting output
%constantinteger maxlevel = 15
%constantinteger long base = 256
%constantinteger long tail = 511
%constantinteger name base = 512
%constantinteger name tail = 2047
%constantinteger stack base = 1024
%constantinteger stack tail = 2047
%constantinteger short base = 2048
%constantinteger short tail = 4095
%constantinteger list base = 4096
%constantinteger list tail = x'7fff'
%constantinteger atom base = 256
%constantinteger char base = 1919
%constantinteger zero base = 3072
%constantinteger pname max = 8191
%constantinteger t = 2003
%constantinteger percent = 1956
%constantinteger nil = 512
%constantinteger quote = 513
%constantinteger label = 514
%constantinteger lambda = 515
%constantinteger apval = 516
%constantinteger subr = 517
%constantinteger fsubr = 518
%constantinteger expr = 519
%constantinteger fexpr = 520
%constantinteger exit = 521
%constantinteger evln = x'8000'!522
%constantinteger stars = 523
%constantinteger error = 0
%constantinteger error1 = 1
%constantinteger error2 = 2
%constantinteger error3 = 3
%constantinteger escape = x'88',eof = x'89'
%constantbyteintegerarray mask(apval:fexpr) =  3, 4!2, 2, 4!1, 1
%constantbyteintegerarray code(0:127) = %c
x'80', x'80', x'80', x'80', x'80', x'80', x'80', x'80',
x'80', x'80', x'80', x'80', x'80', x'80', x'80', x'80',
x'80', x'80', x'80', x'80', x'80', x'80', x'80', x'80',
x'80',   eof, x'80', x'80', x'80', x'80', x'80', x'80',
x'80', x'21', x'22', x'23',escape, x'25', x'26', x'84',
x'81', x'83', x'2a', x'2b', x'2c', x'2d', x'82',escape,
x'30', x'31', x'32', x'33', x'34', x'35', x'36', x'37',
x'38', x'39', x'3a', x'3b', x'3c', x'3d', x'3e', x'3f',
x'84', x'41', x'42', x'43', x'44', x'45', x'46', x'47',
x'48', x'49', x'4a', x'4b', x'4c', x'4d', x'4e', x'4f',
x'50', x'51', x'52', x'53', x'54', x'55', x'56', x'57',
x'58', x'59', x'5a', x'85', x'5c', x'87', x'5e', x'5f',
x'60', x'61', x'62', x'63', x'64', x'65', x'66', x'67',
x'68', x'69', x'6a', x'6b', x'6c', x'6d', x'6e', x'6f',
x'70', x'71', x'72', x'73', x'74', x'75', x'76', x'77',
x'78', x'79', x'7a', x'7b', x'7c', x'7d', x'7e', x'7f'
%constantstring(1) snl = "
"
%constantstring(1)%array charx(0:7) = %c
 " ", "(", ".", ")", "'", "[", " ", "]"
!
!
!***********************************************************************
!*
!*          Record and array formats
!*
!***********************************************************************
!
%recordformat atom cell(%halfinteger bind,prop,func,%byteinteger form,
                        %stringname pname)
%recordformat lisp cell(%halfinteger car,cdr)
%recordformat lispinfo(%integer dataend,datastart,filesize,filetype,
                       sum,datetime,format,records,marker,const,
                       long head,pname space,pname base,pname head,name,
                       name head,stack,global,list,list head,list count,
                       line length)
%recordformat rf(%integer conad,filetype,datastart,dataend)
%recordformat stack frame(%halfinteger back,bind,link)
!
%ownintegerarrayformat constf(long base:long tail)
%ownrecord(stack frame)%arrayformat stackf(stack base:stack tail)
%ownrecord(lisp cell)%arrayformat listf(0:list tail)
%ownrecord(atom cell)%arrayformat namef(name base:name tail)
%ownbyteintegerarrayformat pnamef(0:pname max)
!
!
!***********************************************************************
!*
!*          Subsystem references
!*
!***********************************************************************
!
%systemroutinespec connect(%string(31) file,%integer mode,hole,
                           prot,%record(rf)%name r,%integername flag)
%systemroutinespec define(%integer chan,%string(31) iden,
                          %integername afd,flag)
%systemstringfunctionspec failuremessage(%integer mess)
%systemstringfunctionspec itos(%integer n)
%systemroutinespec outfile(%string(31) file,%integer size,hole,
                           prot,%integername conad,flag)
%externalroutinespec prompt(%string(255) s)
%systemroutinespec setfname(%string(63) s)
%externalroutinespec set return code(%integer i)
!
!
!***********************************************************************
!*
!*          Own variables
!*
!***********************************************************************
!
%owninteger auxp;                       ! Auxiliary stack pointer
%owninteger errval,infile,outf
%owninteger char,reset,front,pname tail,progflag,local,nillist
%ownintegername list count,list head,long head,pname head,global,name head
%ownintegername line length
%ownstring(255) pmpt,plabel,line,clause,pspaces
%ownintegerarrayname const
%ownrecord(lispinfo)%name lispfile
%ownrecord(atom cell)%arrayname name
%ownrecord(lisp cell)%arrayname list
%ownrecord(stack frame)%arrayname stack
%ownintegerarray auxs(0:1023);          ! Auxiliary stack
%ownbyteintegerarray blanks(0:255) = ' '(*)
%ownstring(5) errors = "Error"
!
!
!***********************************************************************
!*
!*          Forward references
!*
!***********************************************************************
!
%integerfunctionspec eval(%integer form)
%integerfunctionspec func(%record(atom cell)%name atom,%integer args)
%routinespec loop(%string(255) pmpt,%integer term)
!
!
!***********************************************************************
!*
!*          Miscellaneous routines
!*
!***********************************************************************
!
%integerfunction push(%integer index)
! Adds  the item 'index' to the auxiliary stack, yielding 'index' as the
! result.
auxs(auxp) = index
auxp = auxp + 1
%result = index
%end;   ! of push
!
!-----------------------------------------------------------------------
!
%integerfunction pop(%integer index)
! Removes an item from the auxiliary stack, yielding that  item  as  its
! result.
auxp = auxp - 1
%result = index
%end;   ! of pop
!
!-----------------------------------------------------------------------
!
%stringfunction pname(%integer index)
! Yields the printable form of the atom described by 'index'.
%if index >= long base %then %start
   %if index >= name base %then %start
      %result = itos(index-zero base) %if index >= short base
      %result = tostring(index-char base) %if index >= char base
      %result = name(index)_p name
   %finish %else %result = itos(const(index))
%finish %else %result = errors
%end;   ! of pname
!
!-----------------------------------------------------------------------
!
%string(255)%function pack(%integer index)
%integer car
%string(255) packed
!
packed = ""
%while index >= list base %cycle;       ! cdr down the list
   car = list(index)_car
   index = list(index)_cdr
   %if car >= list base %then packed <- packed.pack(car) %else %c
      packed <- packed.pname(car)
%repeat
packed <- packed.pname(index) %unless index = nil
%result = packed
%end;   ! of pack
!
!-----------------------------------------------------------------------
!
%integerfunction numberp(%integername value)
! Performs  the  LISP  function  'numberp',  on  the  item  described by
! 'value'.
value = const(value) %and %result = t %if long base <= value <= long tail
value = value - zero base %and %result = t %if short base <= value <= short tail
%result = nil
%end;   ! of numberp
!
!-----------------------------------------------------------------------
!
%integerfunction equal(%integer arg1,arg2)
! Performs the LISP function 'equal'  on  the  two  items  described  by
! 'arg1' and 'arg2'.
%if arg1 = arg2 %or (numberp(arg1) = t = numberp(arg2) %and %c
    arg1 = arg2) %or(arg1 >= list base %and arg2 >= list base %and %c
    equal(list(arg1)_car,list(arg2)_car) = t %and %c
    equal(list(arg1)_cdr,list(arg2)_cdr) = t) %then %result = t
%result = nil
%end;   ! of equal
!
!-----------------------------------------------------------------------
!
%integerfunction mnumb(%integer value)
! Allocates storage for a number (if necessary), and yields its value as
! the result.
%integer index
!
%result = value + zero base %if -1024 <= value <= 1023
%unless long base <= long head <= long tail %then %start
   printstring(snl."Atom error:   No more room for long constants".snl)
   %result = error
%finish
index = long base
%while index # long head %cycle
   %result = index %if const(index) = value
   index = index + 1
%repeat
long head = long head + 1
const(index) = value
%result = index
%end;   ! of mnumb
!
!-----------------------------------------------------------------------
!
%integerfunction matom(%string(255) pname)
! Allocates  storage  for  the name 'pname', and yields its index as the
! result.  Uses an existing copy if it is already in the name table.
%integer index
%record(atom cell)%name atom
!
%if length(pname) = 1 %then %start
   %result = char base + (charno(pname,1) & x'7f')
%finish
%for index = name base,1,name head - 1 %cycle
   %result = index %if pname = name(index)_pname
%repeat
%unless name head < char base %and %c
   pname head + length(pname) + 1 < pname tail %then %start
   printstring(snl."Atom error:   No more space for names".snl)
   %result = error
%finish
atom == name(name head)
atom_pname == string(pname head)
pname head = pname head + length(pname) + 1
atom_pname = pname
index = name head
name head = name head + 1
%result = index
%end;   ! of matom
!
!-----------------------------------------------------------------------
!
%integerfunction ratom
! Reads and allocates space for an atom, after classifying  it.   Yields
! the index of the item read.
%integer type,sign,value,tsign
%string(255) pname
!
type = 0
value = 0
sign = +1
tsign = +1
pname = ""
%cycle
   %if char & x'80' # 0 %then %start;   ! Separator
      %if pname # "" %then %start;      ! Terminator
         %result = matom(pname) %if type < 2
                                        ! Symbolic atom
         %result = mnumb(value);        ! Numeric atom
      %finish
      value = char & x'7f'
      char = x'80' %and %result = value %if char # x'80'
                                        ! Break character
   %finish %else %start;                ! Normal character
      %if 0 <= type <= 2 %then %start;  ! Possibly numeric
         %if '0' <= char <= '9' %then %start
                                        ! Yes for now
            type = 2
            %if sign = -1 %then %start
               value = -value
               sign = +1
            %finish
            value = value*10 + (char-'0')*tsign
         %finish %else %start;          ! Possibly signed
            %if type = 0 %and (char = '+' %or char = '-') %then %start
               type = 1
               %if char = '-' %then %start
                  sign = -1
                  tsign = -1
               %finish
            %finish %else type = -1;    ! Not numeric
         %finish
      %finish
      pname <- pname.tostring(char);    ! Always symbolic
   %finish
   %cycle
      readch(char);                     ! Next symbol
      char = code(char & x'7f')
      selectinput(0) %if char = eof
   %repeat %until char # eof
   readch(char) %and type = -1 %if char = escape
%repeat
%end;   ! of ratom
!
!-----------------------------------------------------------------------
!
%routine printchars(%string(255) phrase)
%integer adjustment
!
phrase <- plabel.phrase %and plabel = "" %if plabel # ""
%if phrase = "" %then %start
   %if length(line) + length(clause) < line length %then %start
      printstring(line.clause.snl)
   %finish %else printstring(line.snl.pspaces.clause.snl)
   line = ""
   clause = ""
%finish
%if length(pspaces)+length(clause)+length(phrase) >= line length %then %start
   adjustment = length(pspaces) - length(phrase)
   %if adjustment < 0 %then %start
      printstring(line.snl) %if line # ""
      printstring(pspaces.clause.snl)
      line = ""
      clause = ""
   %finish %else length(pspaces) = adjustment
%finish
clause = clause.phrase;                 ! Append phrase
%end;   ! of printchars
!
!-----------------------------------------------------------------------
!
%routine print(%integer index)
%integer level,clevel,adjustment,i
%byteinteger r flag,c count,r count,line 1
%integerarray line pos(0:maxlevel)
!
!
%string(255)%function padding
%integer index,count
!
%if level < maxlevel + 1 %then index = level %else index = maxlevel
count = line pos(index) - length(plabel)
count = line length %if count > line length
count = 0 %if count < 0
blanks(0) = count
%result = string(addr(blanks(0)))
%end;   ! of padding
!
!
%routine lparen
%integer i
!
%if level <= maxlevel %and line pos(level) = 0 %then %start
   line pos(level) = length(line) + length(clause)
%finish
c count = 10 %if 50 < length(line) > line pos(level) %or %c
   length(clause) > 30 %or (length(clause) > 20 %and length(line) > 20)
%if r flag > 0 %or c count >= 10 %then %start
                                        ! Start of new phrase
   %if length(line) + length(clause) > line length %or %c
      length(line) - length(pspaces) > 25 %or c count >= 2 %then %start
      !
      ! Adjust the position of the left parenthesis at this level
      !
      %if level > clevel %then %start
         adjustment = length(line) - length(pspaces)
         %for i = clevel + 1,1,level %cycle
            line pos(i) = line pos(i) - adjustment
         %repeat
      %finish
      printstring(line.snl) %if line # ""
      line = pspaces.clause
      line 1 = 0
   %finish %else line = line.clause
   c count = r count
   clause = ""
   clevel = level
   pspaces = padding
%finish
level = level + 1 %if line 1 = 0
r flag = 0
r count = 0
print chars("(")
%end;   ! of lparen
!
!
%routine rparen
print chars(")")
line pos(level) = 0 %if 0 < level <= maxlevel
level = level - 1 %if level > 0
r flag = 1
rcount = rcount + 1
%end;   ! of rparen
!
!-----------------------------------------------------------------------
!
%routine print sexp(%integer index)
! Prints  out the S-expression described by 'index'.  This may be a list
! cell (and therefore something fairly complex) or it  may  just  be  an
! atom.
%integer car,cdr
%record(lisp cell)%name cell
!
%if index >= list base %then %start;    ! List cell
   cell == list(index)
   car = cell_car
   cdr = cell_cdr;                      ! Map onto cell
   lparen
   print sexp(car);                     ! Start of list
   %if cdr >= list base %then %start
      %cycle;                           ! Print tail
         index = cdr
         cell == list(index)
         car = cell_car
         cdr = cell_cdr
         print chars(" ") %if plabel = ""
                                        ! Print space
         %exit %if cdr < list base;     ! End of list
         %if car = nil %then %start;    ! Print empty list
            lparen
            print chars(" ")
            rparen
         %finish %else print sexp(car); ! Print car
      %repeat
      print sexp(car)
   %finish
   printchars(" . ") %and printchars(pname(cdr)) %and rflag = 0 %if cdr # nil
   r paren;                             ! Close list
%finish %else %start;                   ! Atom
   %if r flag = 1 %then %start
      r flag = 2
      plabel <- pname(index)." " 
   %finish %else %start
      print chars(pname(index))
      r flag = 0
   %finish
%finish
%end;   ! of print sexp
!
!
line 1 = 1
r flag = 0
c count = 0
r count = 0
pspaces = ""
level = 0
clevel = 0
line pos(0) = 4;                        ! Initial indentation
line pos(i) = 0 %for i = 1,1,maxlevel
print sexp(index)
%end;   ! of print
!
!-----------------------------------------------------------------------
!
%routine mark(%integer index)
! Marks  the  chain  of  cells headed by 'index', by adding bit x'8000'.
! Used by the garbage collector.
%halfintegername car
!
%while index >= list base %and list(index)_car >= 0 %cycle
   car == list(index)_car
   index = list(index)_cdr
   car <- car!x'8000'
   mark(car & x'7fff') %if car & x'7fff' >= list base
%repeat
%end;   ! of mark
!
!-----------------------------------------------------------------------
!
%routine garbage collect
%record(lisp cell)%name cell
%integer i
!
mark(name(i)_prop) %for i = name base,1,name head - 1
mark(name(i)_prop) %for i = char base,1,name tail
mark(stack(i)_bind) %for i = stack base,1,front
mark(stack(i)_bind) %for i = global,1,stack tail
%if auxp > 0 %then %start
   mark(auxs(i)) %for i = 0,1,auxp - 1
%finish
list count = 0
list head = 0
%for i = list base,1,list tail %cycle
   cell == list(i)
   %if cell_car < 0 %then cell_car <- cell_car & x'7fff' %else %start
      list count = list count + 1
      cell_car = list head
      list head = i
   %finish
%repeat
%end;   ! of garbage collect
!
!-----------------------------------------------------------------------
!
%integerfunction cons(%integer car,cdr)
! Performs the LISP function 'cons' on the items described by 'car'  and
! 'cdr'.  Yields the index of the cell constructed.
%integer index,dummy
%record(lisp cell)%name cell
!
%if list count <= 100 %or list head < list base %then %start
   dummy = push(car);                   ! Ensure these lists are not garbage collected
   dummy = push(cdr)
   auxp = auxp + 2
   garbage collect
   auxp = auxp - 2
   %if list count <= 1000 %then %start
      printstring(snl."Lisp  note:   Less than 1000 free cells remaining - free something".snl)
      loop("Free:",percent)
   %finish
%finish
%if list head < list base %then %start
   printstring(snl."Lisp error:   No more free space left".snl)
   %result = error
%finish
list count = list count - 1
index = list head
cell == list(index)
list head = cell_car
cell_car = car
cell_cdr = cdr
%result = index
%end;   ! of cons
!
!-----------------------------------------------------------------------
!
%integerfunction reverse(%integer curr)
%integer last
%record(lisp cell)%name cell
!
last = nil
%while curr >= list base %cycle
   cell == list(curr)
   last = cons(cell_car,last)
   curr = cell_cdr
%repeat
%result = last
%end;   ! of reverse
!
!-----------------------------------------------------------------------
!
%integerfunction read sexp(%string(255) pmpt)
%integerfunctionspec cell(%integer car)
%integerfunctionspec head
%integerfunctionspec tail
%integer colapse
!
!
%integerfunction cell(%integer car)
%integer cdr
!
%if car >= atom base %then %start;      ! Head not in error
   auxs(auxp) = car
   auxp = auxp + 1
   cdr = tail
   auxp = auxp - 1
   %result = cons(car,cdr) %if cdr >= atom base
                                        ! Tail not in error
%finish
%result = error
%end;   ! of cell
!
!
%integerfunction head
%integer temp,res
%switch sw(0:3)
!
temp = ratom
%result = temp %if temp >= atom base;   ! Atom
-> sw(temp & 3);                        ! Handle by case
!
sw(0):                                  ! ""
%result = cons(quote,cons(head,nil))
!
sw(1):                                  ! '(' or '['
res = tail
colapse = no %if temp >= 4;             ! '['
%result = res
!
sw(2):
sw(3):                                  ! '.' or ')'
printstring(snl."Read error:   S-expression begins with a ".charx(temp).snl)
%result = error
%end;   ! of head
!
!
%integerfunction tail
%integer temp,res
%switch sw(0:3)
!
%result = nil %if colapse = yes;        ! Collapse back to '['
temp = ratom;                           ! Separator
%result = cell(temp) %if temp >= atom base
                                        ! Atom
-> sw(temp & 3);                        ! Handle by case
!
sw(0):
%result = cell(cons(quote,cons(head,nil)))
!
sw(1):                                  ! '(' or '['
res = tail
colapse = no %if temp >= 4;             ! '['
%result = cell(res)
!
sw(2):                                  ! '.'
temp = head
%result = temp %if tail = nil
printstring(snl."Read error:   Dotted pair not enclosed in brackets".snl)
%result = error
!
sw(3):                                  ! ')' or ']'
colapse = yes %if temp >= 4
%result = nil
%end;   ! of tail
!
!
colapse = no
prompt(pmpt)
%result = head
%end;   ! of read sexp
!
!-----------------------------------------------------------------------
!
%routine loop(%string(255) pmpt,%integer term)
%integer value
!
%cycle
   reset = 0
   value = eval(read sexp(pmpt))
   %exit %if value = term
   print(value) %and print chars("") %unless reset # 0
%repeat
%end;   ! of loop
!
!-----------------------------------------------------------------------
!
%integerfunction pcons(%integer car,cdr)
auxp = auxp - 1
%result = cons(car,cdr)
%end;   ! of pcons
!
!-----------------------------------------------------------------------
!
%routine xprint(%string(255) mess,%integer form)
%string(255) save
!
save = line
line = mess
print(form)
print chars("")
line = save
%end;   ! of xprint
!
!-----------------------------------------------------------------------
!
%routine bind(%integer symb,entry,bind)
%record(atom cell)%name atom
%record(stack frame)%name frame
!
%unless name base <= symb < list base %then %start
   printstring(snl."Bind error:   Element of name list not an atom, element = ")
   xprint("",symb)
   %return
%finish
%if name(symb)_form = 3 %then %start
   xprint(snl."Bind error:   Name list entry has constant binding, name=",symb)
   %return
%finish
%unless global > front %then %start
   printstring(snl."Bind error:  Stack overflow".snl)
   %return
%finish
frame == stack(entry)
atom == name(symb)
%unless bind >= atom base %then %start
   printstring(snl."Bind error:   Unassigned argument ")
   xprint("",symb)
   bind = error
%finish
frame_bind = bind
frame_back = symb
frame_link = atom_bind
atom_bind = entry
%end;   ! of bind
!
!-----------------------------------------------------------------------
!
%routine bindlist(%integername names,args)
%record(lisp cell)%name cell,argc
!
stack(front)_link = local
stack(front)_back = 0
local = front
front = front + 1
%while names >= list base %cycle
   cell == list(names)
   argc == list(args)
   bind(cell_car,front,argc_car)
   front = front + 1
   names = cell_cdr
   args = argc_cdr
%repeat
%end;   ! of bindlist
!
!-----------------------------------------------------------------------
!
%integerfunction unbind(%integer result)
%record(stack frame)%name frame
!
%while front > local %cycle
   front = front - 1
   frame == stack(front)
   name(frame_back)_bind = frame_link %if frame_back > 0
%repeat
front = local
local = stack(front)_link
%result = result
%end;   ! of unbind
!
!-----------------------------------------------------------------------
!
%integerfunction prog(%integer names,body)
%integer proglist,result
%record(lisp cell)%name cell
!
bindlist(names,nillist)
progflag = progflag + 4;                ! In prog
proglist = body
%while body >= list base %cycle;        ! Evaluate body
   cell == list(body)
   %if cell_car >= list base %then %start
                                        ! Not a plabel
      result = eval(cell_car);          ! So evaluate
      %if progflag & 3 # 0 %then %start;! Return or go
         %if progflag & 1 # 0 %then %start
                                        ! Return
            progflag = progflag & (\3) - 4
            %result = unbind(result)
         %finish
         cell == list(proglist)
         progflag = progflag & (\3)
         %while cell_car # result %cycle; ! Scan for label
            %if cell_cdr < list base %then %start
               progflag = progflag - 4
               %result = unbind(error); ! Not found
            %finish
            cell == list(cell_cdr)
         %repeat
      %finish
   %finish
   body = cell_cdr
%repeat
progflag = progflag - 4
%result = unbind(result);               ! Fell through
%end;   ! of prog
!
!-----------------------------------------------------------------------
!
%integerfunction evlist(%integer args)
%record(lisp cell)%name cell
!
%result = args %unless args >= list base
cell == list(args)
%result = pcons(push(eval(cell_car)),evlist(cell_cdr))
%end;   ! of evlist
!
!-----------------------------------------------------------------------
!
%integerfunction apply(%integer function,args)
%integer car,cadr,caddr
%record(lisp cell)%name cell
!
%if function >= list base %then %start
   cell == list(function)
   car = cell_car
   cell == list(cell_cdr)
   cadr = cell_car
   cell == list(cell_cdr)
   caddr = cell_car
   %if car = label %then %start
      bind(cadr,front,caddr)
      front = front + 1
      %result = apply(caddr,args)
   %finish
   %if car = lambda %then %start
      bindlist(cadr,args)
      bind(cadr,front,args) %and front = front + 1 %if cadr # nil
      %result = unbind(eval(caddr))
   %finish
   %result = apply(eval(function),args)
%finish
%if name base <= function <= name tail %then %start
   %result = func(name(function),args)
%finish
%result = error
%end;   ! of apply
!
!-----------------------------------------------------------------------
!
%integerfunction put(%integer atom,bind,prop)
%integer id
%halfintegername hole
%record(lisp cell)%name prop cell,bind cell
!
%unless name base <= atom <= name tail %and name base <= prop <= name tail %then %start
   %result = error3
%finish
hole == name(atom)_prop
%cycle;                                 ! Search property list
   hole = cons(prop,cons(bind,nil)) %and %exit %if hole < list base
                                        ! Not on list
   prop cell == list(hole)
   bind cell == list(prop cell_cdr)
   bind cell_car = bind %and %exit %if prop cell_car = prop
                                        ! Property found
   hole == bind cell_cdr;               ! Try next entry
%repeat
%if apval <= prop <= fexpr %then %start;! Function definition
   name(atom)_form = mask(prop)
   %if subr <= prop <= fsubr %then %start
      id = bind
      %result = error3 %unless numberp(id) = t
      name(atom)_func = id
   %finish %else name(atom)_func = bind
%finish
%result = bind
%end;   ! of put
!
!-----------------------------------------------------------------------
!
%integerfunction func(%record(atom cell)%name atom,%integer args)
! Interprets  a  function  call.   EXPRs  and  FEXPRs are interpreted by
! calling 'apply'.  SUBRs and FSUBRs are  interpreted  in  the  'func()'
! switch below.
%integer arg1,arg2,arg3,symb,afd,flag
%string(80) line
%halfintegername hole
%record(lisp cell)%name cell
%record(stack frame)%name frame
%switch type(0:3)
%switch func(0:86)
!
-> type(atom_form & 3)
!
type(3):                                ! Apval
type(0):                                ! No function definition on property list
%result = error2 %unless atom_bind < global
                                        ! Nor on alist
front = front + 1
args = evlist(args)
front = front - 1
%result = apply(stack(atom_bind)_bind,args)
!
type(1):                                ! Expr or Fexpr
%result = apply(atom_func,args)
!
type(2):                                ! Subr or Fsubr
cell == list(args);     arg1 = cell_car
cell == list(cell_cdr); arg2 = cell_car
cell == list(cell_cdr); arg3 = cell_car
!
-> func(atom_func)
!
func(0):                                ! Quote
%result = arg1
!
func(1):                                ! Car
%result = list(arg1)_car
!
func(2):                                ! Cdr
%result = list(arg1)_cdr
!
func(3):                                ! Caar
%result = list(list(arg1)_car)_car
!
func(4):                                ! Cadr
%result = list(list(arg1)_cdr)_car
!
func(5):                                ! Cdar
%result = list(list(arg1)_car)_cdr
!
func(6):                                ! Cddr
%result = list(list(arg1)_cdr)_cdr
!
func(7):                                ! Cons
%result = cons(arg1,arg2)
!
func(8):                                ! List
%result = args
!
func(9):                                ! Cond
%while args >= list base %cycle
   cell == list(list(args)_car)
   arg1 = eval(cell_car)
   %if arg1 # nil %then %start
      %while cell_cdr >= list base %cycle
         cell == list(cell_cdr)
         arg1 = eval(cell_car)
      %repeat
      %result = arg1
   %finish
   args = list(args)_cdr
%repeat
%result = nil
!
func(10):                               ! And
%while args >= list base %cycle
   cell == list(args)
   %result = nil %unless eval(cell_car) # nil
   args = cell_cdr
%repeat
%result = t
!
func(11):                               ! Or
%while args >= list base %cycle
   cell == list(args)
   %result = t %if eval(cell_car) # nil
   args = cell_cdr
%repeat
%result = nil
!
func(12):                               ! Null
%if arg1 = nil %then %result = t %else %result = nil
!
func(13):                               ! Atom
%if atom base <= arg1 < list base %then %result = t %else %result = nil
!
func(14):                               ! Numberp
%result = numberp(arg1)
!
func(56):                               ! Evenp
%if numberp(arg1) = t %and (arg1 & 1) = 0 %then %result = t 
%result = nil
!
func(55):                               ! Onep
arg1 = arg1 - 1
!
func(15):                               ! Zerop
%if arg1 = zero base %then %result = t %else %result = nil
!
func(16):                               ! Eq
%if arg1 = arg2 %then %result = t %else %result = nil
!
func(17):                               ! Equal
%result = equal(arg1,arg2)
!
func(18):                               ! Lessp
%if numberp(arg1) = t = numberp(arg2) %and arg1 < arg2 %then %result = t %else %c
   %result = nil
!
func(19):                               ! Greaterp
%if numberp(arg1) = t = numberp(arg2) %and arg1 > arg2 %then %result = t %else %c
   %result = nil
!
func(20):                               ! Memb/Memq
%while arg2 >= list base %cycle
   cell == list(arg2)
   %result = t %if arg1 = cell_car
   arg2 = cell_cdr
%repeat
%result = nil
!
func(21):                               ! Member
%while arg2 >= list base %cycle
   cell == list(arg2)
   %result = t %if equal(arg1,cell_car) = t
   arg2 = cell_cdr
%repeat
%result = nil
!
func(22):                               ! Assoc
%while arg2 >= list base %cycle
   cell == list(arg2)
   %result = cell_car %if equal(arg1,list(cell_car)_car) = t
   arg2 = cell_cdr
%repeat
%result = nil
!
func(23):                               ! Plus
arg1 = 0
%while args >= list base %cycle
   cell == list(args)
   arg2 = cell_car
   %if numberp(arg2) = t %then arg1 = arg1 + arg2 %else %result = error3
   args = cell_cdr
%repeat
%result = mnumb(arg1)
!
func(24):                               ! Difference
%unless numberp(arg1) = t %then %result = error3
%while args >= list base %cycle
   cell == list(args)
   arg2 = cell_car
   %if numberp(arg2) = t %then arg1 = arg1 - arg2 %else %result = error3
   args = cell_cdr
%repeat
%result = mnumb(arg1)
!
func(25):                               ! Times
arg1 = 1
%while args >= list base %cycle
   cell == list(args)
   arg2 = cell_car
   %if numberp(arg2) = t %then arg1 = arg1 * arg2 %else %result = error3
   args = cell_cdr
%repeat
%result = mnumb(arg1)
!
func(26):                               ! Quotient
%unless numberp(arg1) = t %then %result = error3
%while args >= list base %cycle
   cell == list(args)
   arg2 = cell_cdr
   %if numberp(arg2) = t %then arg1 = arg1//arg2 %else %result = error3
   args = cell_cdr
%repeat
%result = mnumb(arg1)
!
func(27):                               ! Add1
%if numberp(arg1) = t %then %result = mnumb(arg1 + 1)
%result = error3
!
func(28):                               ! Sub1
%if numberp(arg1) = t %then %result = mnumb(arg1 - 1)
%result = error3
!
func(29):                               ! Abs
%if numberp(arg1) = t %then %result = mnumb(imod(arg1))
%result = error3
!
func(30):                               ! Selectq
arg1 = eval(arg1)
args = list(args)_cdr
%cycle
   arg3 = args
   args = list(arg3)_cdr
   %exit %if args < list base
   cell == list(list(arg3)_car)
   arg2 = cell_car
   arg3 = cell_cdr
   %while arg2 >= list base %cycle
      cell == list(arg2)
      -> exit %if cell_car = arg1
      arg2 = cell_cdr
   %repeat
   %exit %if arg2 = arg1
%repeat
!
exit:
%while arg3 >= list base %cycle
   cell == list(arg3)
   arg1 = eval(cell_car)
   arg3 = cell_cdr
%repeat
%result = arg1
!
func(31):                               ! Put
%result = put(arg1,arg3,arg2)
!
func(32):                               ! Prop
%result = error3 %unless name base <= arg1 <= name tail
%result = name(arg1)_prop
!
func(33):                               ! Rem/Remprop
%result = error3 %unless %c
   name base <= arg1 <= name tail %and name base <= arg2 <= name tail
atom == name(arg1)
hole == atom_prop
%while hole >= list base %cycle
   cell == list(hole)
   %if cell_car = arg2 %then %start
      cell == list(cell_cdr)
      atom_form = 0 %if cell_car = atom_func
      hole = cell_cdr
      %result = t
   %finish
   hole == list(cell_cdr)_cdr
%repeat
%result = nil
!
func(34):                               ! Get
%result = error3 %unless %c
   name base <= arg1 <= name tail %and name base <= arg2 <= name tail
args = name(arg1)_prop
%while args >= list base %cycle
   cell == list(args)
   %result = list(cell_cdr)_car %if cell_car = arg2
   args = list(cell_cdr)_cdr
%repeat
%result = nil
!
func(35):                               ! Put/Putprop/Defprop
%result = put(arg1,arg2,arg3)
!
func(36):                               ! Eval
%result = eval(arg1)
!
func(37):                               ! Evlis
%result = evlist(args)
!
func(38):                               ! Apply
%result = apply(arg1,arg2)
!
func(39):                               ! Errset
arg1 = cons(eval(arg1),nil)
arg1 = errval %and reset = 0 %if reset = 2
%result = arg1
!
func(40):                               ! Rplaca
%result = error3 %if arg1 < list base
list(arg1)_car = arg2
%result = arg2
!
func(41):                               ! Rplacd
%result = error3 %if arg1 < list base
list(arg1)_cdr = arg2
%result = arg2
!
func(42):                               ! Nconc
%result = arg2 %if arg1 = nil
%result = error3 %unless arg1 >= list base
args = arg1;                            ! Remember 'a'
arg1 = list(arg1)_cdr %while list(arg1)_cdr >= list base
                                        ! Cdr down 'a'
list(arg1)_cdr = arg2
%result = args
!
func(43):                               ! Minusp
%if numberp(arg1) = t %and arg1 < 0 %then %result = t
%result = nil
!
func(44):                               ! Setq
arg2 = eval(arg2)
!
func(45):                               ! Set
%result = error3 %unless name base <= arg1 <= name tail
arg3 = name(arg1)_bind
%if arg3 < stack tail %then %start
   stack(arg3)_bind = arg2
%finish %else %start
   global = global - 1
   bind(arg1,global,arg2)
%finish
%result = arg2
!
func(46):                               ! Explode
%result = error3 %unless atom base <= arg1 < list base
line = pname(arg1)
arg2 = nil
%for arg1 = addr(line) + length(line),-1,addr(line) + 1 %cycle
   symb = byteinteger(arg1)
   %if '0' <= symb <= '9' %then symb = zero base + symb - '0' %else %c
      symb = char base + symb
   arg2 = cons(symb,arg2)
%repeat
%result = arg2
!
func(47):                               ! Implode
%result = matom(pack(arg1))
!
func(48):                               ! Prog2
%result = arg2
!
func(49):                               ! Progn
%while args >= list base %cycle
   cell == list(args)
   arg1 = eval(cell_car)
   args = cell_cdr
%repeat
%result = arg1
!
func(50):                               ! Prog
%result = prog(arg1,list(args)_cdr)
!
func(51):                               ! Minus
%if numberp(arg1) = t %then %result = mnumb(-arg1)
%result = error3
!
func(52):                               ! Return
progflag = progflag!1
%result = arg1
!
func(53):                               ! Go
progflag = progflag!2
%result = arg1
!
func(54):                               ! Reverse
%result = reverse(arg1)
!
func(60):                               ! Prompt
%result = error3 %unless name base <= arg1 <= name tail
pmpt = pname(arg1)
%result = arg1
!
func(61):                               ! Readch
%if atom base <= arg1 < list base %then prompt(pname(arg1)) %else prompt(pmpt)
readch(symb)
%if '0' <= symb <= '9' %then %result = mnumb(symb - '0') %else %c
   %result = matom(tostring(symb))
!
func(62):                               ! Read
%result = read sexp(pname(arg1)) %if atom base <= arg1 < list base
%result = read sexp(pmpt)
!
func(63):                               ! Princ
print(arg1)
%result = arg1
!
func(64):                               ! Print
print(arg1)
print chars("")
%result = arg1
!
func(65):                               ! Terpri
print chars("")
arg1 = nil %unless arg1 >= atom base
%result = arg1
!
func(66):                               ! Inunit
selectinput(arg1) %and %result = mnumb(arg1) %if numberp(arg1) = t
%result = error3
!
func(67):                               ! Outunit
selectoutput(arg1) %and %result = mnumb(arg1) %if numberp(arg1) = t
%result = error3
!
func(68):                               ! Input
%result = error3 %unless name base <= arg1 <= name tail
selectinput(0)
closestream(instream)
arg2 = infile
infile = arg1
define(instream,name(infile)_pname,afd,flag)
selectinput(instream)
%result = arg2
!
func(69):                               ! Output
%result = error3 %unless name base <= arg1 <= name tail
selectoutput(0)
closestream(outstream)
arg2 = outf
outf = arg1
define(outstream,name(outf)_pname,afd,flag)
selectoutput(outstream)
%result = arg2
!
func(70):                               ! Trace
%result = error3 %unless name base <= arg1 <= name tail
atom == name(arg1)
atom_form = atom_form!8
%result = arg1
!
func(71):                               ! Untrace
%result = error3 %unless name base <= arg1 <= name tail
atom == name(arg1)
atom_form = atom_form & (\8)
%result = arg1
!
func(72):                               ! Break
%result = error3 %unless name base <= arg1 <= name tail
name(arg1)_form = name(arg1)_form!16
%result = arg1
!
func(73):                               ! Unbreak
%result = error3 %unless name base <= arg1 <= name tail
name(arg1)_form = name(arg1)_form & (\16)
%result = arg1
!
func(74):                               ! $Delete
%result = error3 %unless name base <= arg1 <= name tail
atom == name(arg1)
atom_bind = stack tail
atom_prop = nil
atom_func = 0
atom_form = 0
%result = arg1
!
func(75):                               ! Peek
%if numberp(arg1) = t %and front - arg1 > stack base %then %c
   arg1 = front - arg1 %else arg1 = stack base
%if front # arg1 %then %start
   %for arg1 = front - 1,-1,arg1 %cycle
      frame == stack(arg1)
      line <- pname(frame_back & x'fff')."         "
      length(line) = 9
      %if frame_back & x'8000' # 0 %then line = line."* " %else line = line."= "
      xprint(line,frame_bind)
   %repeat
%finish
%result = stars
!
func(76):                               ! Linelength
%result = error3 %unless zerobase + 40 <= arg1 <= zerobase + 255
line length = arg1 - zerobase
%result = arg1
!
func(77):                               ! Garb
garbage collect
%result = mnumb(list count)
!
func(78):                               ! Reset
pmpt = "Read:"
reset = 1
errval = nil
%result = percent
!
func(79):                               ! Err
errval = arg1
reset = 2
%result = percent
!
func(80):                               ! Oblist
arg2 = nil
%for arg1 = name head - 1,-1,name base %cycle
   arg2 = cons(arg1,arg2)
%repeat
%result = arg2
!
func(81):                               ! Alist
arg2 = nil
arg3 = nil
%for arg1 = stack base,1,front - 1 %cycle
   frame == stack(arg1)
   arg2 = cons(cons(frame_back,frame_bind),arg2) %if %c
      name base <= frame_back <= name tail
%repeat
%for arg1 = stack tail - 1,-1,global %cycle
   frame == stack(arg1)
   arg3 = cons(cons(frame_back,frame_bind),arg3)
%repeat
%result = cons(arg2,arg3)
!
func(82):                               ! Ascii
%result = error3 %unless numberp(arg1) = t %and 0 <= arg1 <= 127
%result = matom(tostring(arg1))
!
func(83):                               ! Max
%unless numberp(arg1) = t %then %result = error3
%while args >= list base %cycle
   cell == list(args)
   arg2 = cell_car
   %unless numberp(arg2) = t %then %result = error3
   arg1 = arg2 %if arg1 < arg2
   args = cell_cdr
%repeat
%result = mnumb(arg1)
!
func(84):                               ! Min
%unless numberp(arg1) = t %then %result = error3
%while args >= list base %cycle
   cell == list(args)
   arg2 = cell_car
   %unless numberp(arg2) = t %then %result = error3
   arg1 = arg2 %if arg2 < arg1
   args = cell_cdr
%repeat
%result = mnumb(arg1)
!
func(85):                               ! Sqrt
%unless numberp(arg1) = t %then %result = error3
%result = int(sqrt(arg1))
!
func(86):                               ! Expt
%unless numberp(arg1) = t = numberp(arg2) %then %result = error3
%result = mnumb(arg1****arg2)
%end;   ! of func
!
!-----------------------------------------------------------------------
!
%integerfunction trace(%string(255) mess,%integer form)
xprint(mess,form)
%result = form
%end;   ! of trace
!
!-----------------------------------------------------------------------
!
%integerfunction eval(%integer form)
%integer car,cdr
%record(lisp cell)%name cell
%record(atom cell)%name atom
%record(stack frame)%name frame
!
!
%integerfunction break(%integer result)
%integer sexp
%switch error(0:3)
!
%result = result %if result >= atom base %or reset # 0
selectinput(0)
selectoutput(0)
xprint("Eval error:   ",form)
!
-> error(result)
!
error(1):
printstring("    Atom is not bound to a value".snl)
-> error(0)
!
error(2):
xprint("    Function not defined: ",car)
-> error(0)
!
error(3):
xprint("    Argument not of the correct form in ",cdr)
!
error(0):
loop("   %:",percent)
%result = percent %if reset # 0
sexp = read sexp("Eval:")
sexp = form %if sexp = percent
%result = eval(sexp)
%end;   ! of break
!
! ** Body of eval **
!
%result = percent %if reset # 0
frame == stack(front)
frame_back <- evln
frame_bind = form
%if form >= list base %then %start;     ! Form is a list
   cell == list(form)
   car = cell_car
   cdr = cell_cdr
   %if name base <= car <= name tail %then %start
      atom == name(car)
      %if atom_form & 4 # 0 %then %start
                                        ! Evaluate tail if EXPR or SUBR
         front = front + 1
         cdr = evlist(cdr)
         front = front - 1
      %finish
      form = push(form)
      frame_back <- car!x'8000'
      frame_bind = cdr
      %if atom_form & 16 # 0 %then %start
         selectinput(0)
         selectoutput(0)
         xprint("Lisp Break:   ",form)
         front = front + 1
         loop("   %:",percent)
         front = front - 1
      %finish
      %if atom_form & 8 # 0 %then %start
                                        ! Name being traced
         %result = pop(break(trace("<--- ".pname(car)." ",func(atom,
            trace("---> ".pname(car)." ",cdr)))))
      %finish
      %result = pop(break(func(atom,cdr)))
                                        ! Form of Apply
   %finish
   front = front + 1
   cdr = evlist(cdr)
   front = front - 1
   %result = break(apply(car,cdr));     ! Function is a list
%finish
%if name base <= form <= name tail %then %start
   atom == name(form)
   %result = atom_func %if atom_form & x'7' = 3
                                        ! Apval
   %result = break(stack(atom_bind)_bind)
                                        ! Return binding
%finish
%result = form;                         ! Constant
%end;   ! of eval
!
!-----------------------------------------------------------------------
!
%routine initlisp
%integer i,sexp
%record(atom cell)%name atom
%record(lisp cell)%name cell
%record(stack frame)%name frame
!
%for i = name base,1,name tail %cycle
   atom == name(i)
   atom_bind = stack tail
   atom_prop = nil
   atom_func = 0
   atom_form = 0
   atom_pname == errors
%repeat
selectinput(setup)
reset = 0
sexp = ratom %for i = nil,1,stars;      ! Read in known atoms
%for i = 0,1,list base - 1 %cycle
   cell == list(i)
   cell_car = error3
   cell_cdr = error3
%repeat
list head = list base
list count = list tail - list head
list(i)_car = i + 1 %for i = list base,1,list tail - 1
list(list tail) = 0
sexp = put(ratom,ratom,ratom) %until sexp = nil
                                        ! Initialise from INITLISP
stack(front)_bind = error
frame == stack(global)
frame_link = global
frame_bind = error1
auxs(auxp) = error
sexp = eval(read sexp("")) %until sexp = nil
selectinput(0)
%end;   ! of initlisp
!
!
!***********************************************************************
!*
!*          L I S P
!*
!***********************************************************************
!
%externalroutine lisp(%string(255) parms)
%integer flag,conad,initmode,i,fixup
%string(255) work
%record(rf) rr
%byteintegerarrayname pname space
%record(atom cell)%name atom
!
local = stack base
front = stack base
auxp = 0
pmpt = "Read:"
char = x'80';                           ! Arbitrary separator to start
!
%if parms -> work.("/").parms %and work = "" %then %start
   %if parms = "" %then parms = "T#LSPMACH"
   outfile(parms,196608,0,0,conad,flag)
   -> err %if flag # 0
   initmode = yes
%finish %else %start
   initmode = no
   connect(parms,3,0,0,rr,flag)
   -> err %if flag # 0
   conad = rr_conad
%finish
! <define channels 2 and 3???>
!
lispfile == record(conad);              ! LISP machine store
!
%if initmode = yes %then %start
   lispfile_dataend = lispfile_filesize
   lispfile_filetype = ssdatafiletype
   lispfile_format = 3;                 ! Un-structured
   lispfile_marker = marker
   lispfile_const = x'1000'
   lispfile_long head = long base
   lispfile_pname space = lispfile_const + 4*(long tail-long base+1)
   lispfile_pname base = lispfile_pname space
   lispfile_pname head = lispfile_pname base
   lispfile_name = x'4000'
   lispfile_name head = name base
   lispfile_stack = x'A000'
   lispfile_global = stack tail
   lispfile_list = x'10000'
   lispfile_line length = default line length
%finish %else %start
   %if lispfile_marker # marker %then %start
                                        ! Not a LISP machine file
      flag = 311;                       ! Corrupt file
      setfname(parms)
      -> err
   %finish
%finish
!
! Map variables onto machine file
!
const == array(conad+lispfile_const,constf)
long head == lispfile_long head
pname space == array(conad+lispfile_pname space,pnamef)
pname head == lispfile_pname head
pname tail = addr(pname space(pname max))
name == array(conad+lispfile_name,namef)
name head == lispfile_name head
global == lispfile_global
line length == lispfile_line length
!
! Relocate 'pname' addresses.
!
fixup = addr(pname space(0)) - lispfile_pname base
lispfile_pname base = lispfile_pname base + fixup
lispfile_pname head = lispfile_pname head + fixup
%for i = name base,1,name head - 1 %cycle
   atom == name(i)
   atom_pname == string(addr(atom_pname)+fixup)
   atom_bind = stack tail %unless global <= atom_bind <= stack tail
%repeat
!
%for i = char base,1,name tail %cycle
   atom == name(i)
   atom_bind = stack tail %unless global <= atom_bind <= stack tail
%repeat
!
list == array(conad+lispfile_list,listf)
list head == lispfile_list head
list count == lispfile_list count
stack == array(conad + lispfile_stack,stackf)
!
pspaces = ""
plabel = ""
line = ""
clause = ""
progflag = 0
flag = 0
%if initmode = yes %then initlisp
nillist = cons(nil,nil)
list(nillist)_cdr = push(nillist)
infile = matom(".IN")
outf = matom(".OUT")
!
loop("Lisp:",exit)
set return code(0)
%stop
!
err:
selectoutput(0)
printstring(snl."LISP fails -".failuremessage(flag))
set return code(flag)
%stop
%end;   ! of lisp
%endoffile
