pdecformal.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  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. { Read the formal declaration in procedure/function/method "headers"
  23. and in the main program }
  24. procedure read_formal_decs;
  25. { Read the formal declaration in class declarations }
  26. procedure read_formal_decs_in_class;
  27. implementation
  28. uses
  29. { common }
  30. cutils,cclasses,
  31. { global }
  32. globtype,globals,tokens,verbose,
  33. systems,
  34. { symtable }
  35. symconst,symbase,symtype,symtable,defutil,defcmp,
  36. fmodule,
  37. { pass 1 }
  38. node,pass_1,
  39. nmat,nadd,ncal,nset,ncnv,ninl,ncon,nld,nflw,nmem,
  40. { codegen }
  41. ncgutil,
  42. { parser }
  43. scanner,
  44. pbase,pexpr,ptype,ptconst,pdecsub,
  45. { link }
  46. import,
  47. { exceptions }
  48. sysutils,
  49. { procinfo }
  50. procinfo
  51. ;
  52. procedure read_definition; forward;
  53. procedure read_specvar; forward;
  54. procedure read_precondition; forward;
  55. procedure read_postcondition; forward;
  56. procedure read_retcondition; forward;
  57. type
  58. EInvalidAnnotation = class(Exception);
  59. procedure read_formal_decs;
  60. { Reads the formal declarations of specification variables ('specvar's),
  61. propositions, pre- and postconditions into a symtablestack. }
  62. begin
  63. if (token=_CLOSE_FORMAL) then
  64. begin
  65. consume(_CLOSE_FORMAL);
  66. exit;
  67. end;
  68. try
  69. repeat
  70. { expect "def", "specvar", "pre", "post" or "ret" }
  71. if (token<>_ID) then
  72. consume(_ID);
  73. if upcase(pattern)='DEF' then
  74. begin
  75. consume(_ID);
  76. read_definition;
  77. end
  78. else if upcase(pattern)='SPECVAR' then
  79. begin
  80. consume(_ID);
  81. read_specvar;
  82. end
  83. else if upcase(pattern)='PRE' then
  84. begin
  85. consume(_ID);
  86. read_precondition;
  87. end
  88. else if upcase(pattern)='POST' then
  89. begin
  90. consume(_ID);
  91. read_postcondition;
  92. end
  93. else if upcase(pattern)='RET' then
  94. begin
  95. consume(_ID);
  96. read_retcondition;
  97. end
  98. else
  99. begin
  100. raise EInvalidAnnotation.Create('specvar, pre, post or def expected');
  101. end;
  102. until not try_to_consume(_SEMICOLON);
  103. consume(_CLOSE_FORMAL);
  104. except
  105. on e: EInvalidAnnotation do
  106. begin
  107. { Consume the whole annotation }
  108. while token<>_CLOSE_FORMAL do
  109. consume(token);
  110. consume(_CLOSE_FORMAL);
  111. Message1(parser_e_invalid_formal_annotation, e.message);
  112. end;
  113. end; { try..except }
  114. end; { procedure read_formal_decs }
  115. procedure read_definition;
  116. var
  117. def_name : string;
  118. def_type : ttype;
  119. def_expr : tnode;
  120. def : tabstractvarsym;
  121. type_given : boolean;
  122. prev_ignore_equal : boolean;
  123. begin
  124. { parse A[,A]*, where A::= P [: TYPE ] = EXPRESSION }
  125. repeat
  126. def_name:=pattern;
  127. consume(_ID);
  128. if try_to_consume(_COLON) then
  129. begin
  130. prev_ignore_equal:=ignore_equal;
  131. ignore_equal:=true;{ Signals to read_type to ignore the = token }
  132. read_type(def_type, '', false);
  133. ignore_equal:=prev_ignore_equal;
  134. type_given:=true;
  135. end
  136. else
  137. type_given:=false;
  138. consume(_EQUAL);
  139. def_expr:=comp_expr_in_formal_context(true);
  140. do_resulttypepass(def_expr);
  141. if type_given then
  142. { Match the given type and the type of the expression }
  143. inserttypeconv(def_expr, def_type)
  144. else
  145. { Use the computed type of the expression }
  146. def_type:=def_expr.resulttype;
  147. if not(symtablestack.symtabletype in [localsymtable,globalsymtable,staticsymtable]) then
  148. begin
  149. { TODO : more descriptive error message }
  150. raise EInvalidAnnotation.Create('Proposition definition outside local '+
  151. 'or global declaration context');
  152. end;
  153. def:=tdefinitionsym.create(def_name, def_type, def_expr);
  154. symtablestack.insert(def);
  155. until not try_to_consume(_COMMA);
  156. end;
  157. procedure read_specvar;
  158. var
  159. sv : tspecvarsym;
  160. sv_name : string;
  161. sv_type : ttype;
  162. sv_expr : tnode;
  163. type_given : boolean;
  164. prev_ignore_equal : boolean;
  165. begin
  166. { parse A[,A]* where A::= ID[:TYPE] = EXPRESSION }
  167. repeat
  168. sv_name:=pattern;
  169. consume(_ID);
  170. if try_to_consume(_COLON) then
  171. begin
  172. type_given:=true;
  173. prev_ignore_equal:=ignore_equal;
  174. ignore_equal:=true; { Signals to read_type that the = token
  175. should be ignored }
  176. read_type(sv_type, '', false);
  177. ignore_equal:=prev_ignore_equal;
  178. end
  179. else
  180. type_given:=false;
  181. consume(_EQUAL);
  182. sv_expr:=comp_expr_in_formal_context(true);
  183. do_resulttypepass(sv_expr);
  184. if type_given then
  185. { Match the given type and the type of the expression }
  186. inserttypeconv(sv_expr, sv_type)
  187. else
  188. { Use the computed type of the expression }
  189. sv_type:=sv_expr.resulttype;
  190. if not(symtablestack.symtabletype in [localsymtable,globalsymtable,staticsymtable]) then
  191. raise EInvalidAnnotation.Create('Specvars can only be defined in local or global contexts');
  192. sv:=tspecvarsym.create(sv_name, sv_type, sv_expr);
  193. symtablestack.insert(sv);
  194. if (not assigned(current_procinfo)) or (not assigned(current_procinfo.procdef)) then
  195. raise EInvalidAnnotation.Create('Specvars cannot be defined here');
  196. current_procinfo.procdef.specvars.Add(sv);
  197. until not try_to_consume(_COMMA);
  198. end;
  199. procedure read_precondition;
  200. var
  201. expr : tnode;
  202. begin
  203. consume(_EQUAL);
  204. expr:=comp_expr_in_formal_context(true);
  205. { Check here the result type of the expression.
  206. This will be checked later on as well (after conversion to "assert"),
  207. but then an error message would contain the wrong position }
  208. do_resulttypepass(expr);
  209. if not is_boolean(expr.resulttype.def) then
  210. begin
  211. Message1(type_e_boolean_expr_expected, expr.resulttype.def.typename);
  212. exit;
  213. end;
  214. if assigned(current_procinfo.procdef.precondition) then
  215. { There was already a precondition specified,
  216. use the conjunction of expr and the previous precondition }
  217. expr:=caddnode.create(andn, current_procinfo.procdef.precondition, expr);
  218. current_procinfo.procdef.precondition:=expr;
  219. end;
  220. procedure read_postcondition;
  221. var
  222. expr : tnode;
  223. begin
  224. consume(_EQUAL);
  225. expr:=comp_expr_in_formal_context(true);
  226. { Check here the result type of the expression.
  227. This will be checked later on as well (after conversion to "assert"),
  228. but then an error message would contain the wrong position }
  229. do_resulttypepass(expr);
  230. if not is_boolean(expr.resulttype.def) then
  231. begin
  232. Message1(type_e_boolean_expr_expected, expr.resulttype.def.typename);
  233. exit;
  234. end;
  235. if assigned(current_procinfo.procdef.postcondition) then
  236. { There was already a postcondition specified,
  237. use the conjunction of expr and the previous postcondition }
  238. expr:=caddnode.create(andn, current_procinfo.procdef.postcondition, expr);
  239. current_procinfo.procdef.postcondition:=expr;
  240. end;
  241. procedure read_retcondition;
  242. var
  243. expr : tnode;
  244. evald : tnode;
  245. rst : tnode;
  246. begin
  247. consume(_EQUAL);
  248. expr:=comp_expr_in_formal_context(true);
  249. { Convert this to "Result = expr" }
  250. if not assigned(current_procinfo.procdef.funcretsym) then
  251. raise EInvalidAnnotation.Create('{@ ret : expr } in a non-returning something'); { TODO }
  252. rst:=cloadnode.create(current_procinfo.procdef.funcretsym,current_procinfo.procdef.localst);
  253. evald:=caddnode.create(equaln, rst, expr);
  254. if assigned(current_procinfo.procdef.postcondition) then
  255. { there was already a postcondition specified,
  256. use the conjunction of the previous postcondition and result = expr }
  257. evald:=caddnode.create(andn, current_procinfo.procdef.postcondition, evald);
  258. current_procinfo.procdef.postcondition:=evald;
  259. end;
  260. procedure read_formal_decs_in_class;
  261. var
  262. inv_name : string;
  263. inv_expr : tnode;
  264. begin
  265. if not (try_to_consume(_CLOSE_FORMAL)) then
  266. begin
  267. try
  268. if (upcase(pattern)<>'INV') then
  269. raise EInvalidAnnotation.Create('inv expected');
  270. consume(_ID);
  271. inv_name:=pattern;
  272. consume(_ID);
  273. consume(_EQUAL);
  274. inv_expr:=comp_expr_in_formal_context_class_header(true);
  275. do_resulttypepass(inv_expr);
  276. if not is_boolean(inv_expr.resulttype.def) then
  277. raise EInvalidAnnotation.Create('boolean expression expected');
  278. if assigned(aktobjectdef.invariant) then
  279. aktobjectdef.invariant:=caddnode.create(andn,
  280. aktobjectdef.invariant,
  281. inv_expr)
  282. else
  283. aktobjectdef.invariant:=inv_expr;
  284. consume(_CLOSE_FORMAL);
  285. except
  286. on e: EInvalidAnnotation do
  287. begin
  288. { Consume the whole annotation }
  289. while token<>_CLOSE_FORMAL do
  290. consume(token);
  291. consume(_CLOSE_FORMAL);
  292. Message1(parser_e_invalid_formal_annotation, e.message);
  293. end; { on EInvalidAnnotation }
  294. end; { try..except }
  295. end; { if not try_to_consume(_CLOSE_FORMAL) }
  296. end;
  297. end.