%begin; !KEYGEN: generate Keyword dict
%include "I:UTIL.INC"
!Input streams:-
%constinteger old=1
!Output streams:-
%constinteger report=0, new=1, list=2
!
%constinteger atommax=99
%constinteger itembound=500
%shortintegerarray item,next(0:itembound)
%constinteger dictbound=500
%ownshortintegerarray dicthead('a':'z') = 0 (26)
%shortintegerarray symbol,cont,alt(0:dictbound)
%string(15)%array classname(0:atommax)

%owninteger dictmax=0
%owninteger sym=nl,last=0

%on %event 9 %start
  %stop
%finish

%routine croak(%string(63) s)
  select output(report)
  printstring("**".s."  Run abandoned  ")
  %cycle
    print symbol(sym)
    %exit %if sym = nl
    read symbol(sym)
  %repeat
  %stop
%end

%routine read sym
  read symbol(sym)
  print symbol(sym)
%end

%routine read(%integername v)
!Read numeric item, including terminating symbol
  read sym %until sym # ' '
  croak("Not digit: ".tostring(sym)) %unless '0' <= sym <= '9'
  v = 0
  %cycle
    v = v*10+sym-'0'
    read sym
  %repeat %until %not '0' <= sym <= '9'
%end

%routine read keyword definitions
%integer l,class
%string(255) s
%on %event 9 %start
  croak("Premature end-of-file")
%finish

%routine read atoms(%integer class)
%shortintegername p
%integer sub
  %cycle
    read sym %until sym > ' '
    croak("*Not lower-case letter: ".tostring(sym)) %unless 'a' <= sym <= 'z'
    p == dicthead(sym)
    %cycle
      read sym
      %exit %unless 'a' <= sym <= 'z'
      p == alt(p) %while 128 > symbol(p) # sym
      %if symbol(p) >= 128 %start
        dictmax = dictmax+1
        symbol(dictmax) = sym
        cont(dictmax) = 0;  alt(dictmax) = p
        p = dictmax
      %finish
      p == cont(p)
    %repeat
    sub = 0
    read(sub) %if sym = '_'
    p == alt(p) %while symbol(p) < 128
    %if symbol(p) = 128 %start
      dictmax = dictmax+1
      symbol(dictmax) = class+128
      cont(dictmax) = 0;  alt(dictmax) = sub
      p = dictmax
    %finish %else %start
      select output(report)
      print string("Dict clash: ")
      print string(classname(class))
      newline
      select output(new)
    %finish
  %repeat %until sym # ','
  read sym %if sym = '}'
  read sym %while sym = ' '
%end

  classname(l) = "" %for l = 0,1,atommax
  %cycle
    read sym %until sym > ' '
    l = 0
    %cycle
      l = l+1;  length(s) = l
      charno(s,l) = sym
      read sym
    %repeat %until sym < 'A'
    read sym %while sym # '='
    read(class)
    croak("Class out of range") %if class > atommax
    croak("Duplicate: ".s) %if classname(class) # ""
    classname(class) = s
    read sym %while sym = ' '
    read atoms(class) %if sym = '{'
  %repeat %until sym # ','
%end;  !read keyword defs

%integerfn dicth(%integer i)
%integer j
!  i = i!32 %if 'A' <= i <= 'Z'
!  %if 'a' <= i <= 'z' %start
    j = dicthead(i)
    j = 1 %if j = 0
    %result = j
!    %result = -j
!  %finish 
!  %result = -999 %if '0' <= i <= '9'
!  %result = 0 %if i <= ' ' %or i = '{' %or i = '%'
!  %result = '%' %if i = '|'
!  %result = '\' %if i = '^'
!  %result = '?' %if i > atommax
!  %result = i
%end
%integerfn symb(%integer i)
  %result = item(i)
%end
%integerfn dnext(%integer i)
  %result = next(i)
%end

%routine put(%integer case,from,to,places,perline)
%integer n
  print symbol('(');  write(from,-1)
  print symbol(':');  write(to,-1)
  printstring(") =  %C")
  n = perline
  %cycle
     n = n+1;  newline %and n = 1 %if n > perline
     %if case = 1 %then write(dicth(from),places) %c
     %else %if case = 2 %then write(symb(from),places) %c
     %else write(dnext(from),places)
     from = from+1
     %exit %if from > to
     print symbol(',')
  %repeat
  newlines(2)
%end

%routine put tables
%integer i,j,k,dlim

%routine linearise(%shortintegername qq)
%integer p,q
  q = qq
  %return %if q = 0
  qq = dlim
  %cycle
    item(dlim) = symbol(q);  next(dlim) = alt(q)
    dlim = dlim+1;  q = cont(q)
  %repeat %until q = 0
  p = qq;  q = dlim-1;  !last cell is class_subclass
  %while p < q %cycle
    %if next(p) # 0 %start
      linearise(next(p))
      next(p) = next(p)-p
    %finish
    p = p+1
  %repeat
%end

%routine show(%integer i,pos)
%integer j
  newline %and %return %if i = 0
  %cycle
    j = item(i)
    %exit %if j >= 128
    space;  print symbol(j)
    show(i+1,pos+2)
    j = next(i)
    %return %if j = 0
    i = i+j
    spaces(pos)
  %repeat
  print symbol('.') %and pos = pos+1 %until pos >= 20
  write(j-128,1)
  j = next(i)
  %if j # 0 %start
    print symbol('[')
    write(j,-1)
    print symbol(']')
  %finish
  newline
%end

  item(1) = 128;  next(1) = 0
  dlim = 2
  linearise(dicthead(i)) %for i = 'a',1,'z'

  select output(list)
  newlines(2)
  %for i = 'a',1,'z' %cycle
    space;  print symbol(i)
    show(dicthead(i),2)
  %repeat
  newlines(2)
  select output(new)

!    printstring("%CONSTSTRING(9)%ARRAY CLASSNAME(")
!    write(0,-1);  print symbol(':');  write(atommax,-1)
!    printstring(") = %C")
!    j = 0
!    %for i = 0,1,atommax %cycle
!      print symbol(',') %unless i = 0
!      newline %and j = 5 %if j = 0
!      space;  print symbol('"')
!      %if i <= ' ' %or (i <= atommax %and classname(i) # "") %start
!        print string(classname(i))
!      %finish %else %start
!        k = dicth(i)
!        %if k > ' ' %then print symbol(k) %else write(k,-1)
!        print symbol(k) %if k = '"'
!      %finish
!      print symbol('"')
!      j = j-1
!    %repeat
!    newlines(2)

  print string("%CONSTSHORTINTEGERARRAY SYMINIT")
  put(1,'a','z',-3,16)
  print string("%CONSTBYTEINTEGERARRAY SYMBOL")
  put(2,1,dlim-1,-3,16)
  print string("%CONSTBYTEINTEGERARRAY ALTDISP")
  put(3,1,dlim-1,-3,16)
  newline

%end;  !put tables

%ownstring(127) oldfile="",newfile="",listfile=""
  define param("Old compiler",oldfile,pamnodefault+paminfile)
  define param("New compiler",newfile,pamnewgroup)
  define param("Listing",listfile,0)
  process parameters(cliparam)
  newfile = oldfile %if newfile = ""
  open output(new,newfile)
  open output(list,listfile) %if listfile # ""

  symbol(0) = 128;  cont(0) = 0;  alt(0) = 0

  select input(old);  select output(new)
!Find keyword declarations
  last = sym %and read sym %until last=nl %and sym='!' %and nextsymbol='~'
  read sym %until sym = nl
  read sym %until sym = '%';  read sym %until sym = ' '
  read keyword definitions
!Find tables
  last = sym %and read sym %until last=nl %and sym='!' %and nextsymbol='~'
  read sym %until sym = nl
!Skip existing tables
  read symbol(sym) %until sym = nl %and next symbol = '!'
  put tables
  %cycle
    read symbol(sym)
    print symbol(sym)
  %repeat
%endofprogram
