opmtch.red 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. module opmtch; % Functions that apply basic pattern matching rules.
  2. % Author: Anthony C. Hearn.
  3. % Copyright (c) 1987 The RAND Corporation. All rights reserved.
  4. fluid '(frlis!* subfg!*);
  5. % Operator // for extended quotient match to be used only in the
  6. % lhs of a rule.
  7. newtok '((!/ !/) slash);
  8. mkop 'slash;
  9. infix slash;
  10. precedence slash, quotient;
  11. % put('slash,'simpfn, function(lambda(u); typerr("//",'operator)));
  12. symbolic procedure emtch u;
  13. if atom u then u else (lambda x; if x then x else u) opmtch u;
  14. symbolic procedure opmtch u;
  15. begin scalar q,x,y,z;
  16. if null(x := get(car u,'opmtch)) then return nil
  17. else if null subfg!* then return nil % null(!*sub2 := t).
  18. else if q := assoc(u,cdr alglist!*) then return cdr q;
  19. z := for each j in cdr u collect emtch j;
  20. a: if null x then go to c;
  21. y := mcharg(z,caar x,car u);
  22. b: if null y then <<x := cdr x; go to a>>
  23. else if lispeval subla(car y,cdadar x)
  24. then <<q := subla(car y,caddar x); go to c>>;
  25. y := cdr y;
  26. go to b;
  27. c: rplacd(alglist!*,(u . q) . cdr alglist!*);
  28. return q
  29. end;
  30. symbolic procedure mcharg(u,v,w);
  31. <<if atsoc('minus,v) then mcharg1(reform!-minus(u,v),v,w) else
  32. if v and eqcar(car v,'slash) then
  33. for each f in reform!-minus2(u,v) join mcharg1(car f,cdr f,w)
  34. else mcharg1(u,v,w)>>;
  35. symbolic procedure mcharg1(u,v,w);
  36. % Procedure to determine if an argument list matches given template.
  37. % U is argument list of operator W, V is argument list template being
  38. % matched against. If there is no match, value is NIL,
  39. % otherwise a list of lists of free variable pairings.
  40. if null u and null v then list nil
  41. else begin integer m,n;
  42. m := length u;
  43. n := length v;
  44. if flagp(w,'nary) and m>2
  45. then if m<6 and flagp(w,'symmetric)
  46. then return mchcomb(u,v,w)
  47. else if n=2 then <<u := cdr mkbin(w,u); m := 2>>
  48. else return nil; % We cannot handle this case.
  49. return if m neq n then nil
  50. else if flagp(w,'symmetric) then mchsarg(u,v,w)
  51. else if mtp v then list pair(v,u)
  52. else mcharg2(u,v,list nil,w)
  53. end;
  54. symbolic procedure reform!-minus(u,v);
  55. % Convert forms (quotient (minus a) b) to (minus (quotient a b))
  56. % if the corresponding pattern in v has a top level minus.
  57. if null v or null u then u else
  58. ((if eqcar(car v,'minus) and eqcar(c,'quotient)
  59. and eqcar(cadr c,'minus)
  60. then {'minus,{'quotient,cadr cadr c,caddr c}} else c)
  61. . reform!-minus(cdr u,cdr v))
  62. where c=car u;
  63. symbolic procedure reform!-minus2(u,v);
  64. % Prepare an extended quotient match; v is a pattern with leading "//".
  65. % Create for a form (quotient a b) a second form
  66. % (quotient (minus a) (minus b)) if b contains a minus sign.
  67. if null u or not eqcar(car u,'quotient) then nil else
  68. <<v := ('quotient . cdar v) . cdr v;
  69. if not smemq('minus,caddar u) then {u.v}
  70. else
  71. {u . v,
  72. ({'quotient,reval {'minus,cadar u},reval {'minus,caddar u}} . cdr u)
  73. . v}>>;
  74. symbolic procedure mchcomb(u,v,op);
  75. begin integer n;
  76. n := length u - length v +1;
  77. if n<1 then return nil
  78. else if n=1 then return mchsarg(u,v,op)
  79. else if not smemqlp(frlis!*,v) then return nil;
  80. return for each x in comb(u,n) join
  81. mchsarg((op . x) . setdiff(u,x),v,op)
  82. end;
  83. symbolic procedure comb(u,n);
  84. % Value is list of all combinations of N elements from the list U.
  85. begin scalar v; integer m;
  86. if n=0 then return list nil
  87. else if (m:=length u-n)<0 then return nil
  88. else for i := 1:m do
  89. <<v := nconc!*(v,mapcons(comb(cdr u,n-1),car u));
  90. u := cdr u>>;
  91. return u . v
  92. end;
  93. symbolic procedure mcharg2(u,v,w,x);
  94. % Matches compatible list U of operator X against template V.
  95. begin scalar y;
  96. if null u then return w;
  97. y := mchk(car u,car v);
  98. u := cdr u;
  99. v := cdr v;
  100. return for each j in y
  101. join mcharg2(u,updtemplate(j,v,x),msappend(w,j),x)
  102. end;
  103. symbolic procedure msappend(u,v);
  104. % Mappend u and v with substitution.
  105. for each j in u collect append(v,sublis(v,j));
  106. symbolic procedure updtemplate(u,v,w);
  107. begin scalar x,y;
  108. return for each j in v collect
  109. if (x := subla(u,j)) = j then j
  110. else if (y := reval!-without(x,w)) neq x then y
  111. else x
  112. end;
  113. symbolic procedure reval!-without(u,v);
  114. % Evaluate U without rules for operator V. This avoids infinite
  115. % recursion with statements like
  116. % for all a,b let kp(dx a,kp(dx a,dx b)) = 0; kp(dx 1,dx 2).
  117. begin scalar x;
  118. x := get(v,'opmtch);
  119. remprop(v,'opmtch);
  120. u := errorset!*(list('reval,mkquote u),t);
  121. put(v,'opmtch,x);
  122. if errorp u then error1() else return car u
  123. end;
  124. symbolic procedure mchk(u,v);
  125. % Extension to optional arguments for binary forms suggested by
  126. % Herbert Melenk.
  127. if u=v then list nil
  128. else if eqcar(u,'!*sq) then mchk(prepsqxx cadr u,v)
  129. else if eqcar(v,'!*sq) then mchk(u,prepsqxx cadr v)
  130. else if atom v
  131. then if v memq frlis!* then list list (v . u) else nil
  132. else if atom u % Special check for negative number match.
  133. then if numberp u and u<0 and eqcar(v,'minus)
  134. then mchk(list('minus,-u),v) else mchkopt(u,v)
  135. % "difference" may occur in a pattern like (a - b)^~n.
  136. else if car v = 'difference then
  137. mchk(u,{'plus,cadr v,{'minus,caddr v}})
  138. else if get(car u,'dname) or get(car v,'dname) then nil
  139. else if car u eq car v then mcharg(cdr u,cdr v,car u)
  140. else if car v memq frlis!* % Free operator.
  141. then for each j in mcharg(subst(car u,car v,cdr u),
  142. subst(car u,car v,cdr v),
  143. car u)
  144. collect (car v . car u) . j
  145. else if car u eq 'minus then mchkminus(cadr u,v)
  146. else mchkopt(u,v);
  147. symbolic procedure mchkopt(u,v);
  148. % Check whether the pattern v is a binary form with an optional
  149. % argument.
  150. (if o then mchkopt1(u,v,o)) where o=get(car v,'optional);
  151. symbolic procedure mchkopt1(u,v,o);
  152. begin scalar v1,v2,w;
  153. if null (w:=cdr v) then return nil; v1:=car w;
  154. if null (w:=cdr w) then return nil; v2:=car w;
  155. if cdr w then return nil;
  156. return
  157. if flagp(v1,'optional) then
  158. for each r in mchk(u,v2) collect (v1.car o) . r
  159. else if flagp(v2,'optional) then
  160. for each r in mchk(u,v1) collect (v2.cadr o) . r
  161. else nil;
  162. end;
  163. put('plus,'optional,'(0 0));
  164. put('times,'optional,'(1 1));
  165. put('quotient,'optional,
  166. '((rule_error "fraction with optional numerator") 1));
  167. put('expt,'optional,
  168. '((rule_error "exponential with optional base") 1));
  169. symbolic procedure rule_error u;
  170. rederr{"error in rule:",u,"illegal"};
  171. symbolic operator rule_error;
  172. % The following function pushes a minus sign into a term.
  173. % E.g. a + ~~y*~z matches
  174. % y z
  175. % (a + b) 1 b
  176. % (a - b) -1 b
  177. % (a -3b) -3 b
  178. % b -3
  179. % (a - b*c) -b c
  180. % c -b
  181. %
  182. % For products, the minus is assigned to a numeric coefficient or
  183. % an artificial factor (-1) is created. For quotients the minus is
  184. % always put in the numerator.
  185. symbolic procedure mchkminus(u,v);
  186. if not(car v memq '(times quotient)) then nil else
  187. if atom u or not(car u eq car v) then
  188. if car v eq 'times then mchkopt1(u,v,'((minus 1)(minus 1)))
  189. else mchkopt({'minus,u},v)
  190. else if numberp cadr u or pairp cadr u and get(caadr u,'dname)
  191. or car v eq 'quotient
  192. then mcharg({'minus,cadr u}.cddr u,cdr v,car v)
  193. else mcharg('(minus 1).cdr u,cdr v,'times);
  194. symbolic procedure mkbin(u,v);
  195. if null cddr v then u . v else list(u,car v,mkbin(u,cdr v));
  196. symbolic procedure mtp v;
  197. null v or (car v memq frlis!* and not(car v member cdr v)
  198. and mtp cdr v);
  199. symbolic procedure mchsarg(u,v,w);
  200. % From ACH: I don't understand why I put in the following reversip,
  201. % since it causes the least direct match to come back first.
  202. reversip!* if mtp v and (W NEQ 'TIMES OR noncomfree u)
  203. then for each j in noncomperm v collect pair(j,u)
  204. else for each j in noncomperm u join mcharg2(j,v,list nil,w);
  205. symbolic procedure noncomfree u;
  206. if idp u then not flagp(u,'noncom)
  207. else atom u or noncomfree car u and noncomfree cdr u;
  208. symbolic procedure noncomperm u;
  209. % Find possible permutations when non-commutativity is taken into
  210. % account.
  211. if null u then list u
  212. else for each j in u join
  213. (if x eq 'failed then nil else mapcons(noncomperm x,j))
  214. where x=noncomdel(j,u);
  215. symbolic procedure noncomdel(u,v);
  216. if null NONCOMP!* u then delete(u,v) else noncomdel1(u,v);
  217. symbolic procedure noncomdel1(u,v);
  218. begin scalar z;
  219. a: if null v then return reversip!* z
  220. else if u eq car v then return nconc(reversip!* z,cdr v)
  221. else if NONCOMP!* car v then return 'failed;
  222. z := car v . z;
  223. v := cdr v;
  224. go to a
  225. end;
  226. symbolic procedure NONCOMP!* u;
  227. noncomp u or eqcar(u,'expt) and noncomp cadr u;
  228. flagop antisymmetric,symmetric;
  229. flag ('(plus times),'symmetric);
  230. endmodule;
  231. end;