pdecformal.pas 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. {
  2. Copyright (c) 2006 by Eindhoven University of Technology
  3. Parses formal annotation in declarations.
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. ****************************************************************************
  16. }
  17. unit pdecformal;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. symsym, symdef;
  22. procedure read_formal_decs;
  23. implementation
  24. uses
  25. { common }
  26. cutils,cclasses,
  27. { global }
  28. globtype,globals,tokens,verbose,
  29. systems,
  30. { symtable }
  31. symconst,symbase,symtype,symtable,defutil,defcmp,
  32. fmodule,
  33. { pass 1 }
  34. node,pass_1,
  35. nmat,nadd,ncal,nset,ncnv,ninl,ncon,nld,nflw,nmem,
  36. { codegen }
  37. ncgutil,
  38. { parser }
  39. scanner,
  40. pbase,pexpr,ptype,ptconst,pdecsub,
  41. { link }
  42. import,
  43. { exceptions }
  44. sysutils,
  45. { procinfo }
  46. procinfo
  47. ;
  48. procedure read_proposition; forward;
  49. procedure read_specvar; forward;
  50. procedure read_precondition; forward;
  51. procedure read_postcondition; forward;
  52. procedure read_retcondition; forward;
  53. type
  54. EInvalidAnnotation = class(Exception);
  55. procedure read_formal_decs;
  56. { Reads the formal declarations of specification variables ('specvar's),
  57. propositions, pre- and postconditions into a symtablestack. }
  58. begin
  59. if (token=_CLOSE_FORMAL) then
  60. begin
  61. consume(_CLOSE_FORMAL);
  62. exit;
  63. end;
  64. try
  65. repeat
  66. { expect "def", "specvar", "pre" or "post" }
  67. if (token<>_ID) then
  68. consume(_ID);
  69. if upcase(pattern)='DEF' then
  70. begin
  71. consume(_ID);
  72. read_proposition;
  73. end
  74. else if upcase(pattern)='SPECVAR' then
  75. begin
  76. consume(_ID);
  77. read_specvar;
  78. end
  79. else if upcase(pattern)='PRE' then
  80. begin
  81. consume(_ID);
  82. read_precondition;
  83. end
  84. else if upcase(pattern)='POST' then
  85. begin
  86. consume(_ID);
  87. read_postcondition;
  88. end
  89. else if upcase(pattern)='RET' then
  90. begin
  91. consume(_ID);
  92. read_retcondition;
  93. end
  94. else
  95. begin
  96. raise EInvalidAnnotation.Create('specvar, pre, post or def expected');
  97. end;
  98. until not try_to_consume(_SEMICOLON);
  99. consume(_CLOSE_FORMAL);
  100. except
  101. on e: EInvalidAnnotation do
  102. begin
  103. { Consume the whole annotation }
  104. while token<>_CLOSE_FORMAL do
  105. consume(token);
  106. consume(_CLOSE_FORMAL);
  107. Message1(parser_e_invalid_formal_annotation, e.message);
  108. end;
  109. end; { try..except }
  110. end; { procedure read_formal_decs }
  111. procedure read_proposition;
  112. var
  113. prop_name : string;
  114. expr : tnode;
  115. vs : tabstractvarsym;
  116. begin
  117. { parse P : expression }
  118. repeat
  119. prop_name:=pattern;
  120. consume(_ID);
  121. consume(_COLON);
  122. expr:=comp_expr_in_formal_context(true);
  123. do_resulttypepass(expr);
  124. if not is_boolean(expr.resulttype.def) then
  125. begin
  126. Message1(type_e_boolean_expr_expected, expr.resulttype.def.typename);
  127. exit;
  128. end;
  129. if not(symtablestack.symtabletype in [localsymtable,globalsymtable]) then
  130. begin
  131. { TODO : more descriptive error message }
  132. raise EInvalidAnnotation.Create('Proposition definition outside local '+
  133. 'or global declaration context');
  134. end;
  135. vs:=tspecvarsym.create(prop_name, expr.resulttype { Boolean }, expr);
  136. symtablestack.insert(vs);
  137. until not try_to_consume(_COMMA);
  138. end;
  139. procedure read_specvar;
  140. var
  141. sv_name : string;
  142. sv_type : ttype;
  143. sv_expr : tnode;
  144. sv : tabstractvarsym;
  145. type_given : boolean;
  146. prev_ignore_equal : boolean;
  147. begin
  148. repeat
  149. { parse P [: type] = expression }
  150. sv_name:=pattern;
  151. consume(_ID);
  152. { Is a type given ? }
  153. if try_to_consume(_COLON) then
  154. begin
  155. prev_ignore_equal:=ignore_equal;
  156. ignore_equal:=true; { Signals to read_type to ignore the = token }
  157. read_type(sv_type, '', false);
  158. ignore_equal:=prev_ignore_equal;
  159. type_given:=true;
  160. end
  161. else
  162. type_given:=false;
  163. consume(_EQUAL);
  164. { Parse the expression }
  165. sv_expr:=comp_expr(true);
  166. do_resulttypepass(sv_expr);
  167. if type_given then
  168. { Match the given type and the type of the expression }
  169. inserttypeconv(sv_expr, sv_type)
  170. else
  171. { Set the type to the type of the expression }
  172. sv_type:=sv_expr.resulttype;
  173. sv:=tspecvarsym.create(sv_name, sv_type, sv_expr);
  174. symtablestack.insert(sv);
  175. until not try_to_consume(_COMMA);
  176. end;
  177. procedure read_precondition;
  178. var
  179. expr : tnode;
  180. begin
  181. consume(_COLON);
  182. expr:=comp_expr_in_formal_context(true);
  183. { Check here the result type of the expression.
  184. This will be checked later on as well (after conversion to "assert"),
  185. but then an error message would contain the wrong position }
  186. do_resulttypepass(expr);
  187. if not is_boolean(expr.resulttype.def) then
  188. begin
  189. Message1(type_e_boolean_expr_expected, expr.resulttype.def.typename);
  190. exit;
  191. end;
  192. if assigned(current_procinfo.procdef.precondition) then
  193. { There was already a precondition specified,
  194. use the conjunction of expr and the previous precondition }
  195. expr:=caddnode.create(andn, current_procinfo.procdef.precondition, expr);
  196. current_procinfo.procdef.precondition:=expr;
  197. end;
  198. procedure read_postcondition;
  199. var
  200. expr : tnode;
  201. begin
  202. consume(_COLON);
  203. expr:=comp_expr_in_formal_context(true);
  204. { Check here the result type of the expression.
  205. This will be checked later on as well (after conversion to "assert"),
  206. but then an error message would contain the wrong position }
  207. do_resulttypepass(expr);
  208. if not is_boolean(expr.resulttype.def) then
  209. begin
  210. Message1(type_e_boolean_expr_expected, expr.resulttype.def.typename);
  211. exit;
  212. end;
  213. if assigned(current_procinfo.procdef.postcondition) then
  214. { There was already a postcondition specified,
  215. use the conjunction of expr and the previous postcondition }
  216. expr:=caddnode.create(andn, current_procinfo.procdef.postcondition, expr);
  217. current_procinfo.procdef.postcondition:=expr;
  218. end;
  219. procedure read_retcondition;
  220. var
  221. expr : tnode;
  222. evald : tnode;
  223. rst : tnode;
  224. begin
  225. consume(_COLON);
  226. { Proposition variables are not allowed here }
  227. expr:=comp_expr(true);
  228. { Convert this to "Result = expr" }
  229. if not assigned(current_procinfo.procdef.funcretsym) then
  230. raise EInvalidAnnotation.Create('{@ ret : expr } in a non-returning something'); { TODO }
  231. rst:=cloadnode.create(current_procinfo.procdef.funcretsym,current_procinfo.procdef.localst);
  232. evald:=caddnode.create(equaln, rst, expr);
  233. if assigned(current_procinfo.procdef.postcondition) then
  234. { there was already a postcondition specified,
  235. use the conjunction of the previous postcondition and result = expr }
  236. evald:=caddnode.create(andn, current_procinfo.procdef.postcondition, evald);
  237. current_procinfo.procdef.postcondition:=evald;
  238. end;
  239. end.