!--------------------------------------------------------------------------! ! sTABLE: Routine to Convert Boolean Equations to TRUTH Table ! ! ! ! This routine is called by two ILAP utilities : TABLE as ILAP routine, ! ! TABLE as a program. The program version gives more feedback to the user, ! ! while the external routine only gives feedback on a fatal error. ! ! ! ! George A. McCaskill 20th August 1982 ! ! ! ! Fix added by IMN for %continue ambiguity in APM IMP... 19/11/85 ! ! Unstiffened 27/10/86 by DJR ! !--------------------------------------------------------------------------! %include "nmos.inc" %include "plautils.inc" %include "inc:util.imp" %const %string (1) SNL = " " {#########################################################################} {# #} {# This program is part of the ILAP library, and was written in #} {# The Department of Computer Science at the University of Edinburgh #} {# (James Clerk Maxwell Building, Kings Buildings, Edinburgh) #} {# #} {# This software is available free to other educational establisments #} {# but the University of Edinburgh, retains all commercial rights. #} {# It is a condition of having this software is that the sources are #} {# not passed on to any other site, and that Edinburgh University is #} {# given credit in any re-implementations of any of the algorithms #} {# used, or articles published which refer to the software. #} {# #} {# There is no formal support for this software, but any bugs should #} {# be reported to Gordon Hughes or David Rees at the above address, #} {# and these are likely to be fixed in a future release. #} {# #} {#########################################################################} %external %routine sTABLE %alias "ILAP_STABLE" ( %string(255) filespec, %integer diags ) %record %format %spec prodlf ; %record %format %spec productf %record %format %spec termlf ; %record %format %spec termf %record %format %spec cell !-----------------------------------------! ! Grammar :- ! ! ! ! A -> id = S nl | I | O ! ! S -> P PL ! ! PL -> + P PL | e ! ! P -> T TL ! ! TL -> * T TL | e ! ! T -> \ id | \ ( S ) | id | ( S ) ! ! I -> in id IL ! ! O -> out id IL ! ! IL -> , id IL | e ! ! ! !-----------------------------------------! %record %format sumf ( %record(productf)%name p , %record(prodlf)%name pl ) %record %format prodlf ( %record(productf)%name p , %record(prodlf)%name next ) %record %format productf ( %record(termf)%name t , %record(termlf)%name tl ) %record %format termlf ( %record(termf)%name t , %record(termlf)%name next ) %record %format termf ( %byte type, inv, ( %string(6) id %or %record(sumf)%name s )) %record %format idlist ( %string(6) id , %record(idlist)%name next ) %record %format assf ( %c ( %string(6) id, %record(sumf)%name s, %byte flattened, sorted %c %or %record(idlist)%name il ), %byte mode, type ) !--------------------------------------------------------! ! Heap ! %record %format cell (( %record(sumf) s %or %c %record(prodlf) pl %or %c %record(productf) p %or %c %record(termlf) tl %or %c %record(termf) t %or %c %record(idlist) i %or %c %record(assf) a ), %record(cell)%name next, %byte type ) %own %record (cell) %name FREE !--------------------------------------------------------! %record %format termlfa (%record (termlfa) %name N) %constant %integer max equations = 500 %record(assf)%name %array equations (1:max equations) %record(assf)%name ca %integer ep %record(idlist)%name ins,outs,il %constant %integer idt = 1 { type of record element } %constant %integer sumt = 2 %constant %integer equationt = 3 %constant %integer int = 4 %constant %integer outt = 5 %constant %integer sum = 1 { type of record } %constant %integer prodl = 2 %constant %integer product = 3 %constant %integer terml = 4 %constant %integer term = 5 %constant %integer ass = 6 %constant %integer idl = 7 %constant %integer in = 1 { type of identifier } %constant %integer out = 2 %constant %integer feed = in!out %integer echo { whether we need to echo incorrect input } %integer input { current input character } %integer len { length of most recent identifier } %integer error { error flag } %integer products { number of product terms } %string(6) name { most recent identifier } %string(127) fin, fout { file names } %string(255) line { text buffer } %integer tp { text pointer } %integer i { counter } %integer inn { stream number } %integer outn { "" } %integer oldin { old stream number } %integer oldout { "" } %integer minterm error { \a and a appear in term } !---------------------------------------------------------! %routine serror ( %string(80) m ) %integer i %if echo = false %start space %for i = 1,1,tp+6 %finish %else %start print string(line);newline %if tp > 1 %start space %for i = 1,1,tp-1 %finish %finish print symbol('^');newline print string("TABLE - syntax error : ".m) %if input # nl %start read symbol ( input ) %until input = nl %finish error = true prompt("] ") newline %stop %if echo = true %or diags = false %end %routine pdebug (%String(255) line) %return %if diags # true printstring(line);newline %End %integer %function id ( %integer i ) %result = true %if 'A' <= i&95 <= 'Z' %result = false %end %integer %function alpha numeric ( %integer i ) %result = true %if id(i) = true %or '0' <= i <= '9' %result = false %end %integer %function disj ( %integer s ) %result = true %if s = '+' %or s = '!' %result = false %end %integer %function conj ( %integer s ) %result = true %if s = '*' %or s = '.' %or s = '&' %result = false %end %routine read name ( %string(*)%name s, %integer %name ch ) ! ! chop off identifier after 6 characters ! s = "" %cycle ch = ch&95 - 'A' + 'a' %if id(ch) = true s = s.tostring(ch) %if length(s)<7 %exit %if alpha numeric( next symbol ) = false read symbol(ch) line = line.to string(ch) %if echo = true %repeat len = length(s) %end %routine advance %integer sym prompt("_ ") ! ! Skip spaces, comments ! Skip newlines after disjunction and conjunction symbols, and commas ! ! Current input line is buffered on 'line' ! %while next symbol = nl %or next symbol = ' ' %or next symbol = '{' %cycle %if next symbol = ' ' %start tp = tp + 1 line = line." " %if echo = true skip symbol %finish %else %if next symbol = '{' %start read symbol (sym) %until sym='}' %finish %else %if conj(input)=true %or disj(input)=true %or input=',' %start tp = 0 line = line.snl %if echo = true skip symbol %finish %else %start %exit %finish %repeat read symbol(input) tp = tp + 1 line = line.to string(input) %if echo = true read name ( name , input ) %and tp = tp + len-1 %if id ( input ) = true %end %record(productf)%map %spec P %record(prodlf)%map %spec PL %record(sumf)%map S %record(sumf)%name sx %record(prodlf)%name link sx == nil sx == new(sx) sx_p == P sx_pl == PL link == nil link == new ( link ) { Link all products together } link_p == sx_p link_next == sx_pl sx_pl == link %result == sx %end %record(termf)%map %spec T %record(termlf)%map %spec TL %record(productf)%map P %record(productf)%name px %record(termlf)%name link px == nil px == new(px) px_t == T px_tl == TL link == nil link == new (link) { link all Terms together } link_t == px_t link_next == px_tl px_tl == link %result == px %end %record(prodlf)%map PL %record(prodlf)%name plx %if disj( input ) = true %start advance plx == nil plx == new(plx) plx_p == P plx_next == PL %result == plx %finish %else %start %result == nil %finish %end %record(termlf)%map TL %record(termlf)%name tlx %if conj( input ) = true %start advance tlx == nil tlx == new(tlx) tlx_t == T tlx_next == TL %result == tlx %finish %else %start %result == nil %finish %end %record(termf)%map T %record(termf)%name tx tx == nil tx == new (tx) %if input = '\' %or input = '~' %start advance tx_inv = true %finish %else %start tx_inv = false %finish %if alpha numeric( input ) = true %start tx_id = name tx_type = idt advance %if input = '''' %start tx_inv = true advance %finish %result == tx %finish %else %if input = '(' %start advance tx_type = sumt tx_s == S %if input = ')' %start advance %if input = '''' %start tx_inv = true advance %finish %result == tx %finish %else %start serror("missing ')'") %result == nil %finish %finish %else %start serror("unexpected symbol : looking for identifier or '(' ") %result == nil %finish %end %record(idlist)%map get idlist %record(idlist)%name il,start %unless alpha numeric( input) = true %start serror("unexpected symbol : looking for identifier list") %result == nil %finish %else %start il == nil il == new(il) il_id = name %finish start == il advance %while input = ',' %cycle advance %unless alpha numeric( input ) = true %start serror("unexpected symbol : looking for identifier") %result == nil %finish il_next == nil il_next == new(il_next) il == il_next il_id = name advance %repeat %if input # nl %start serror("unexpected symbol : looking for newline or ','") %result == nil %finish il_next == nil %result == start %end %record(idlist)%map endof ( %record(idlist)%name r ) %if r == nil %start %result == nil %finish %else %if r_next == nil %start %result == r %finish %else %start %result == endof(r_next) %finish %end %record(assf)%map A ! IMN's fix put in here... %record(assf)%name ax %on 9 %start %result == nil %finish ax == nil ax == new(ax) prompt("] ") %cycle tp = 0; line = "" error = false skip symbol %while next symbol = nl advance error = true %and %continue %if input = nl %if alpha numeric( input ) = true %start %if name = "in" %or name = "out" %start %if name = "in" %then ax_type = int %c %else ax_type = outt advance ax_il == get idlist ! Spot the frig.. %exit %if error = false %continue %finish %else %start ax_type = equationt ax_id = name advance %finish %finish %else %start serror("unexpected symbol : looking for assignment or I/O statement") %continue %finish %if input = '=' %and error = false %start advance %finish %else %if error = false %start serror("unexpected symbol : looking for '='") %continue %finish ax_s == S ax_flattened = false ax_sorted = false ax_mode = 0 %if input # nl %and error = false %start serror("unexpected symbol : looking for newline") %finish %repeat %until error = false %result == ax %end %record(sumf)%map lookup ( %string(6) id , %byte input ) %integer i %for i = 1,1,ep %cycle %result == equations(i)_s %if ( input = true %or equations(i)_mode&in # in ) %and equations(i)_id = id %repeat %result == nil %end %routine print id ( %record(termf)%name t ) print symbol('\') %if t_inv = true print string( t_id ) %end %routine %spec collapse tree ( %record(sumf)%name s ) %routine collapse term ( %record(termf)%name t ) collapse tree(t_s) %if t_type = sumt dispose(t) %end %routine collapse product ( %record(productf)%name p ) %record(termlf)%name tl, dt tl == p_tl %while tl ## nil %cycle dt == tl tl == tl_next collapse term(dt_t) dispose(dt) %repeat %end %routine collapse tree ( %record(sumf)%name s ) %record(prodlf)%name pl, dp pl == s_pl %while pl ## nil %cycle dp == pl pl == pl_next collapse product(dp_p) dispose(dp) %repeat %end %routine print sum ( %record(sumf)%name s ) %record(prodlf)%name pl %record(termlf)%name tl pl == s_pl %while pl ## nil %cycle tl == pl_p_tl spaces (8) %if diags=true %while tl ## nil %cycle print id(tl_t) %if diags=true tl == tl_next print symbol('.') %if diags=true %and tl ## nil %repeat pl == pl_next space %and print symbol('+') %and newline %if diags=true %and pl ## nil products = products + 1 %repeat newline %if diags=true %end %integer %function same id ( %record(termlf)%name a,b ) %result = true %if a_t_inv = b_t_inv %and a_t_id = b_t_id %result = false %end %integer %function same ( %record(termlf)%name a,b ) %if a_next ## nil %and b_next ## nil %start %if same id( a , b ) = true %then %result = same( a_next, b_next) %c %else %result = false %finish %else %if a_next == nil %and b_next == nil %start %result = same id( a, b) %finish %else %start %result = false %finish %end %integer %function product in sum ( %record(termlf)%name tl, %record(sumf)%name s ) %record(prodlf)%name pl %result = false %if s == nil pl == s_pl %while pl ## nil %cycle %result = true %if same(pl_p_tl, tl ) = true pl == pl_next %repeat %result = false %end %integer %function includes ( %record(termlf)%name tl1, tl2 ) %result = true %if tl2 == nil %result = false %if tl1 == nil %if same id( tl1, tl2 ) = true %start %result = includes( tl1_next, tl2_next ) %finish %else %start %result = includes( tl1_next, tl2 ) %finish %end %integer %function term includes term ( %record(termlf)%name tl, %record(sumf)%name s ) %record(prodlf)%name pl,tp %result = false %if s == nil pl == s_pl %while pl ## nil %cycle %if includes( pl_p_tl, tl ) = true %start %if s_pl == pl %start s_pl == pl_next %finish %else %start tp == s_pl tp == tp_next %while tp_next ## pl tp_next == pl_next %finish pl == pl_next %continue %finish %result = true %if includes( tl, pl_p_tl) = true pl == pl_next %repeat %result = false %end %routine sort ( %record(termlf)%name %array %name a , %integer n ) %integer %function less than ( %string(6) a, b ) { order of inputs } %record(idlist)%name il il == ins %while il ## nil %cycle %if ( il_id = a %and il_id = b ) %or il_id = b %start %result = false %finish %else %if il_id = a %start %result = true %finish il == il_next %repeat disaster ("TABLE: input not declared, or equation undefined in pair < ".%c a.", ".b." >") %result=0 %end %routine sort ( %integer l, r) %integer i,j %record(termlf) x,w i = l j = r x = a( (l+r)//2 ) %cycle i = i + 1 %while less than ( a(i)_t_id, x_t_id ) = true { quicksort } j = j - 1 %while less than ( x_t_id, a(j)_t_id ) = true %if i <= j %start w = a(i) ; a(i) = a(j) ; a(j) = w i = i + 1 ; j = j - 1 %finish %repeat %until i > j sort ( l,j ) %if l < j sort ( i,r ) %if i < r %end sort(1,n) %end %routine sort product ( %record(productf)%name p ) %record(termlf)%name tl %integer i tl == p_tl i = 0 i = i + 1 %and tl == tl_next %while tl ## nil %return %if i < 2 %begin %integer j %record(termlf)%name %array terms (1:i) tl == p_tl terms(j) == tl %and tl == tl_next %for j = 1,1,i sort ( terms , i ) terms(j)_next == terms(j+1) %for j = 1,1,i-1 terms(i)_next == nil p_tl == terms(1) %end %end %routine order terms ( %record(assf)%name a ) %record(prodlf)%name pl %if a_sorted = false %start pl == a_s_pl %while pl ## nil %cycle sort product( pl_p ) pl == pl_next %repeat a_sorted = true %finish %end %routine throw out useless terms ( %record(sumf)%name s ) %record(prodlf)%name pl0, pl1 %record(termlf)%name tl1, tl2 %integer reject term pl0 == nil pl1 == s_pl %while pl1 ## nil %cycle tl1 == pl1_p_tl tl2 == pl1_p_tl_next reject term = false %while tl2 ## nil %cycle %if tl1_t_id = tl2_t_id %start %if tl1_t_inv = tl2_t_inv %start { both the same, miss one out } tl1_next == tl2_next %finish %else %start reject term = true { i.e. 1&0 = 0, hence not needed } %exit %finish %finish %else %start tl1 == tl2 %finish tl2 == tl2_next %repeat %if reject term = true %start { miss out term completely } %if pl0 == nil %start s_pl == pl1_next %finish %else %start pl0_next == pl1_next %finish %finish %else %start pl0 == pl1 %finish pl1 == pl1_next %repeat %end %record(sumf)%map flatten ( %record(sumf)%name s , %integer inv ) %record(sumf)%name x, y %record(prodlf)%name pl %record(sumf)%map new sum of ( %record(termlf)%name tl, %integer inv ) %record(sumf)%name s %record(prodlf)%name pl %record(productf)%name p %record(termlf)%name tlx %record(termf)%name t s == nil s == new (s) pl == nil pl == new ( pl) p == nil p == new ( p) tlx == nil tlx == new (tlx) t == nil t == new (t) s_pl == pl s_pl_next == nil s_pl_p == p s_pl_p_tl == tlx t = tl_t s_pl_p_tl_t == t s_pl_p_tl_next == nil s_pl_p_tl_t_inv = inv %result == s %end %integer %function not ( %integer bool ) %result = true %if bool = false %result = false %end %record(sumf)%map sum of ( %record(termlf)%name tl, %integer inv ) %record(sumf)%name s %integer i i = tl_t_inv i = not(i) %if inv = true %if tl_t_type = sumt %start s == flatten( tl_t_s, i ) %result == s %finish %else %start s == lookup( tl_t_id, false ) %if s ## nil %then %result == flatten(s,i) %c %else %result == new sum of ( tl, i ) %finish %end %record(sumf)%map make sum ( %record(sumf)%name s1,s2 ) %record(prodlf)%name p %result == s2 %if s1 == nil %or s1_pl == nil p == s1_pl p == p_next %while p_next ## nil p_next == s2_pl %result == s1 %end %record(productf)%map join ( %record(productf)%name p1, p2 ) %record(termlf)%name tl, x %record(productf)%name p tl == p1_tl x == nil x == new (x) p == nil p == new (p) p_tl == x %while tl ## nil %cycle x_next == nil x_next == new(x_next) x == x_next x_t == tl_t tl == tl_next %repeat tl == p2_tl %while tl ## nil %cycle x_next == nil x_next == new(x_next) x == x_next x_t == tl_t tl == tl_next %repeat x_next == nil p_tl == p_tl_next sort product(p) %result == p %end %record(sumf)%map do product ( %record(sumf)%name s1 , s2 ) %record(prodlf)%name p1, p2, p3, tpl %record(sumf)%name s,sn %result == s2 %if s1 == nil s == nil s == new(s) s_pl == nil p1 == s1_pl %while p1 ## nil %cycle p2 == s2_pl %while p2 ## nil %cycle p3 == nil p3 == new(p3) p3_p == join( p1_p, p2_p ) p3_next == nil sn == nil sn == new(sn) sn_pl == p3 throw out useless terms(sn) %if sn_pl ## nil %and term includes term( sn_pl_p_tl, s ) = false %start %if s_pl == nil %start s_pl == sn_pl %finish %else %start tpl == s_pl tpl == tpl_next %while tpl_next ## nil tpl_next == sn_pl %finish %finish p2 == p2_next %repeat p1 == p1_next %repeat %result == s %end %record(sumf)%map sop ( %record(productf)%name p , %integer inv ) %record(sumf)%name x,y %record(termlf)%name tl y == nil tl == p_tl %if inv = false %start %while tl ## nil %cycle x == sum of ( tl, false ) y == do product( y , x ) tl == tl_next %repeat %finish %else %start %while tl ## nil %cycle x == sum of ( tl, true ) y == make sum( y, x ) tl == tl_next %repeat %finish %result == y %end %result == nil %if s == nil y == nil pl == s_pl %if inv = false %start { sum of products } %while pl ## nil %cycle x == sop( pl_p, false ) y == make sum( y , x ) pl == pl_next %repeat %finish %else %start { product of sums } %while pl ## nil %cycle x == sop( pl_p, true ) y == do product( y, x ) pl == pl_next %repeat %finish %result == y %end %routine smooth ( %record(assf)%name a ) %record(sumf)%name s %if a_flattened = false %start s == flatten( a_s, false ) collapse tree ( a_s ) a_s == s a_flattened = true %finish order terms ( a ) throw out useless terms ( a_s ) print string(a_id." = ") %and newline %if diags = true print sum ( a_s ) %end %integer %function appears ( %string(6) id, %record(assf)%name a ) %record(prodlf)%name pl %record(termlf)%name tl pl == a_s_pl %while pl ## nil %cycle tl == pl_p_tl %while tl ## nil %cycle %result = true %if tl_t_id = id tl == tl_next %repeat pl == pl_next %repeat %result = false %end %routine mark mode %record(assf)%name a %record(idlist)%name il %record(assf)%map find ( %string(6) id ) %integer i %for i = 1,1,ep %cycle %result == equations(i) %if equations(i)_id = id %repeat %result == nil %end pdebug ("Mark mode") il == ins %while il ## nil %cycle a == find( il_id ) a_mode = in %if a ## nil il == il_next %repeat il == outs %while il ## nil %cycle a == find( il_id ) %if a == nil %start disaster ("TABLE: undefined output < ".il_id." >");newline %finish a_mode = a_mode!out il == il_next %repeat %end %routine find transitive closure %integer n { number of non inputs } %integer i pdebug ("Find transitive closure") n = 0 %for i = 1,1,ep %cycle n = n + 1 %if equations(i)_mode&in = 0 { count non inputs } %repeat %begin %integer i,j,k %byte %array B ( 1:n, 1:n ) { find transitive closure } { Warshall's Algorithm } %integer %array who ( 1:n ) { pointer to equation } j = 1 %for i = 1,1,n %cycle j = j + 1 %while equations(j)_mode&in # 0 who(i) = j j = j + 1 %repeat %for i = 1,1,n %cycle %for j = 1,1,n %cycle %if appears( equations(who(i))_id,equations(who(j))) = true %then %c b(j,i) = true %else b(j,i) = false %repeat %repeat %for i = 1,1,n %cycle { n*n*n algorithm } %for j = 1,1,n %cycle %for k = 1,1,n %cycle %if b(j,i) = true %start b(j,k) = b(j,k)!b(i,k) %finish %repeat %repeat %repeat %for i = 1,1,n %cycle %if b(i,i) = true %start disaster ("TABLE: infinite loop detected on equation ".equations(who(i))_id) %finish %repeat %end %end %routine make matrix %record(termlf)%name %array p (1:products ) %integer pp, i, l %integer in already %record(idlist)%name il %routine print outs(%record(termlf)%name tl ) %record(idlist)%name il %integer i il == outs space %while il ## nil %cycle space %for i = 1,1,length(il_id) %if product in sum( tl, lookup( il_id, true )) = true %start print symbol('1') %finish %else %start print symbol('0') %finish il == il_next %repeat newline %end %routine add on ( %record(assf)%name a ) %record(prodlf)%name pl %record(termlf)%name tl %record(idlist)%name ilx %integer i %if a_mode&out = out %start pl == a_s_pl %while pl ## nil %cycle in already = false %for i = 1,1,pp %cycle in already = true %if same( pl_p_tl , p(i) ) = true %repeat %if in already = false %start pp = pp + 1 p(pp) == pl_p_tl tl == pl_p_tl ilx == ins spaces(3) %while ilx ## nil %cycle %if tl == nil %or tl_t_id # ilx_id %start space %for i = 1,1,length(ilx_id) print symbol('x') ilx == ilx_next %finish %else %start space %for i = 1,1,length(ilx_id) %if tl_t_inv = true %start print symbol('0') %finish %else %start print symbol('1') %finish tl == tl_next ilx == ilx_next %finish %repeat print outs(p(pp)) %finish pl == pl_next %repeat %finish %end pdebug ("Make matrix") pp = 0 l = 1 print string("IN ") il == ins %while il ## nil %cycle printstring(il_id) print symbol(',') %if il_next ## nil l = l + length(il_id) + 1 il == il_next %repeat newline add on ( equations(i) ) %for i = 1,1,ep il == outs print string("OUT") space %for i = 1,1,l+1 %while il ## nil %cycle print string(il_id) print symbol(',') %if il_next ## nil il == il_next %repeat newline %end !---------------------------------------------------------------! ! Main Program ! !---------------------------------------------------------------! echo = true error = false fin = "" fout = "" min term error = false free == nil oldin = in stream oldout = out stream parse filespec ( filespec, "eqn", "tbl", fin, fout, inn, outn ) %if diags = true %start %if fin # "" %start print string("reading from < ".fin." >");newline %finish %if fout # "" %start print string("writing to < ".fout." >");newline %finish %finish echo = false %if fin = "" ins == nil ; outs == nil ep = 0 products = 0 select input(inn) select output(0) %cycle ! print string ("Starting....".snl) ca == A %exit %if ca == nil %if ca_type = equationt %start ep = ep + 1 disaster ("TABLE: too many equations < ".itos(ep,0)." >") %c %if ep > max equations equations(ep) == ca %finish %else %if ca_type = int %start il == endof(ins) %if il == nil %start ins == ca_il %finish %else %start il_next == ca_il %finish %finish %else %start il == endof(outs) %if il == nil %start outs == ca_il %finish %else %start il_next == ca_il %finish %finish %repeat mark mode %for i = 1,1,ep !find transitive closure smooth( equations(i) ) %for i = 1,1,ep %if ins == nil %and outs == nil %start disaster ("TABLE: inputs and outputs are not specified") %finish %else %if ins == nil %start disaster ("TABLE: inputs are not specified") %finish %else %if outs == nil %start disaster ("TABLE: outputs are not specified") %finish select output(outn) make matrix close input close output select output( oldout ) select input ( oldin ) %end %external %routine table %alias "ILAP_TABLE" ( %string(255) filespec ) sTABLE (filespec, false) %end %external %routine tablewithdiags %alias "ILAP_TABLE_WITH_DIAGS" (%string (255) filespec) sTABLE (filespec, true) %end %end %of %file