2
0

test_unit.js 77 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626
  1. "use strict";
  2. var assert = require("rtl.js").assert;
  3. var Class = require("rtl.js").Class;
  4. var CodeGenerator = require("js/CodeGenerator.js");
  5. var Grammar = require("grammar.js");
  6. var Test = require("test.js");
  7. var TestUnitCommon = require("test_unit_common.js");
  8. var TestUnitEberon = require("test_unit_eberon.js");
  9. var TestUnitOberon = require("test_unit_oberon.js");
  10. var eberon = require("eberon/eberon_grammar.js").language;
  11. var oberon = require("oberon/oberon_grammar.js").language;
  12. var context = TestUnitCommon.context;
  13. var pass = TestUnitCommon.pass;
  14. var fail = TestUnitCommon.fail;
  15. var setupParser = TestUnitCommon.setupParser;
  16. var testWithSetup = TestUnitCommon.testWithSetup;
  17. function makeSuiteForGrammar(language){
  18. var grammar = language.grammar;
  19. function testWithContext(context, pass, fail){
  20. return TestUnitCommon.testWithContext(context, grammar.declarationSequence, language, pass, fail);
  21. }
  22. function testWithModule(src, pass, fail){
  23. return TestUnitCommon.testWithModule(src, language, pass, fail);
  24. }
  25. function testWithGrammar(parser, pass, fail){
  26. return TestUnitCommon.testWithGrammar(parser, language, pass, fail);
  27. }
  28. return {
  29. "comment": testWithGrammar(
  30. grammar.expression,
  31. pass("(**)123",
  32. "(*abc*)123",
  33. "(*abc*)(*def*)123",
  34. "(*a(*b*)c*)123"),
  35. fail(["(*123", "comment was not closed"])
  36. ),
  37. "spaces are required to separate keywords and integers": testWithGrammar(
  38. grammar.typeDeclaration,
  39. pass(),
  40. fail(["T = ARRAY10OFARRAY5OFINTEGER", "not parsed"],
  41. ["T = ARRAY10 OF ARRAY 5 OF INTEGER", "not parsed"],
  42. ["T = ARRAY 10OF ARRAY 5 OF INTEGER", "not parsed"],
  43. ["T = ARRAY 10 OFARRAY 5 OF INTEGER", "not parsed"],
  44. ["T = ARRAY 10 OF ARRAY5 OF INTEGER", "undeclared identifier: 'ARRAY5'"],
  45. ["T = ARRAY 10 OF ARRAY 5OF INTEGER", "not parsed"],
  46. ["T = ARRAY 10 OF ARRAY 5 OFINTEGER", "not parsed"])
  47. ),
  48. "expression": testWithContext(
  49. context(grammar.expression,
  50. "TYPE ProcType = PROCEDURE(): INTEGER;"
  51. + "PROCEDURE p1(): INTEGER; RETURN 1 END p1;"
  52. + "PROCEDURE p2(): ProcType; RETURN p1 END p2;"
  53. + "PROCEDURE noResult(); END noResult;"),
  54. pass("123",
  55. "1+2",
  56. "1 + 2",
  57. "1 + 2 + 3",
  58. "-1",
  59. "+1",
  60. "p1()",
  61. "p1() + p1()",
  62. "p2()",
  63. "~FALSE",
  64. "~TRUE"
  65. ),
  66. fail(["", "not parsed"],
  67. ["12a", "not parsed"],
  68. ["noResult()", "procedure returning no result cannot be used in an expression"],
  69. ["1 + INTEGER", "type name 'INTEGER' cannot be used as an expression"],
  70. ["INTEGER + 1", "type name 'INTEGER' cannot be used as an expression"],
  71. ["1 * INTEGER", "type name 'INTEGER' cannot be used as an expression"],
  72. ["INTEGER * 1", "type name 'INTEGER' cannot be used as an expression"],
  73. ["-INTEGER", "type name 'INTEGER' cannot be used as an expression"],
  74. ["+INTEGER", "type name 'INTEGER' cannot be used as an expression"],
  75. ["~BOOLEAN", "type name 'BOOLEAN' cannot be used as an expression"],
  76. ["INTEGER", "type name 'INTEGER' cannot be used as an expression"],
  77. ["~~INTEGER", "type name 'INTEGER' cannot be used as an expression"],
  78. ["1 + + 1", "invalid operand"],
  79. ["1 * + 1", "invalid operand"]
  80. )
  81. ),
  82. "string expression": testWithContext(
  83. context(grammar.expression,
  84. "CONST cs = \"abc\";"
  85. + "PROCEDURE charByRef(VAR c: CHAR): CHAR; RETURN c END charByRef;"
  86. ),
  87. pass("\"\"",
  88. "\"a\"",
  89. "\"abc\"",
  90. "0FFX",
  91. "0AX",
  92. "22X",
  93. "0X"),
  94. fail(["\"", "unexpected end of string"],
  95. ["\"abc", "unexpected end of string"],
  96. ["FFX", "undeclared identifier: 'FFX'"],
  97. ["charByRef(cs[1])", "read-only array's element cannot be passed as VAR actual parameter"]
  98. )
  99. ),
  100. "parentheses": testWithGrammar(
  101. grammar.expression,
  102. pass("(1)",
  103. "(1 + 2)",
  104. "(1 + 2) * 3",
  105. "3 * (1 + 2)"),
  106. fail(["(1 + 2", "no matched ')'"])
  107. ),
  108. "identifier": testWithSetup(
  109. function(){
  110. var IdentDeclarationContext = Class.extend({
  111. init: function IdentDeclarationContext(){this.__ident = undefined;},
  112. handleIdent: function(id){this.__ident = id;},
  113. ident: function() {return this.__ident;},
  114. getResult: function() {return this.__ident;},
  115. });
  116. function makeContext() {return new IdentDeclarationContext();}
  117. return setupParser(grammar.ident, language, makeContext);},
  118. pass("i", "abc1"),
  119. fail(["", "not parsed"],
  120. [";", "not parsed"],
  121. ["1", "not parsed"]
  122. )
  123. ),
  124. "variable declaration": testWithGrammar(
  125. grammar.variableDeclaration,
  126. pass("i: INTEGER",
  127. "i, j: INTEGER"),
  128. fail(["i: T", "undeclared identifier: 'T'"],
  129. ["p: POINTER TO T", "type 'T' was not declared"])
  130. ),
  131. "record declaration": testWithGrammar(
  132. grammar.typeDeclaration,
  133. pass("T = RECORD END",
  134. "T = RECORD i: INTEGER END",
  135. "T = RECORD i, j: INTEGER END",
  136. "T = RECORD i, j: INTEGER; b: BOOLEAN END",
  137. "T = RECORD p: PROCEDURE(r: T) END",
  138. "T = POINTER TO RECORD p: PROCEDURE(): T END"
  139. ),
  140. fail(["T = RECORD i, j, i: INTEGER END", "duplicated field: 'i'"],
  141. ["T = RECORD r: T END", "recursive field definition: 'r'"],
  142. ["T = RECORD a: ARRAY 10 OF T END", "recursive field definition: 'a'"],
  143. ["T = RECORD a: ARRAY 3 OF ARRAY 5 OF T END", "recursive field definition: 'a'"],
  144. ["T = RECORD r: RECORD rr: T END END", "recursive field definition: 'r'"],
  145. ["T = RECORD (T) END", "recursive inheritance: 'T'"],
  146. ["T = RECORD r: RECORD (T) END END", "recursive field definition: 'r'"],
  147. ["T = RECORD p: PROCEDURE(): T END", "procedure cannot return T"]
  148. )
  149. ),
  150. "record cannot have forward type as a base": testWithGrammar(
  151. grammar.declarationSequence,
  152. pass(),
  153. fail(["TYPE PForward = POINTER TO Forward; T = RECORD (Forward) END;",
  154. "undeclared identifier: 'Forward'"])
  155. ),
  156. "record extension": testWithContext(
  157. context(grammar.typeDeclaration,
  158. "TYPE B = RECORD END;"),
  159. pass("T = RECORD(B) END"
  160. ),
  161. fail(["T = RECORD(INTEGER) END", "RECORD type is expected as a base type, got 'INTEGER'"],
  162. ["T = RECORD(INTEGER) m: INTEGER END", "RECORD type is expected as a base type, got 'INTEGER'"]
  163. )
  164. ),
  165. "array declaration": testWithContext(
  166. context(grammar.typeDeclaration,
  167. "CONST c1 = 5; VAR v1: INTEGER; p: POINTER TO RECORD END;"),
  168. pass("T = ARRAY 10 OF INTEGER",
  169. "T = ARRAY 10 OF BOOLEAN",
  170. "T = ARRAY 1 + 2 OF INTEGER",
  171. "T = ARRAY c1 OF INTEGER",
  172. "T = ARRAY ORD({0..5} <= {0..8}) OF INTEGER",
  173. "T = ARRAY 1, 2 OF ARRAY 3, 4 OF INTEGER"
  174. ),
  175. fail(["T = ARRAY 0 OF INTEGER",
  176. "array size must be greater than 0, got 0"],
  177. ["T = ARRAY TRUE OF INTEGER",
  178. "'INTEGER' constant expression expected, got 'BOOLEAN'"],
  179. ["T = ARRAY v1 OF INTEGER",
  180. "constant expression expected as ARRAY size"],
  181. ["T = ARRAY p OF INTEGER",
  182. "'INTEGER' constant expression expected, got 'POINTER TO anonymous RECORD'"],
  183. ["T = ARRAY c1 - 10 OF INTEGER",
  184. "array size must be greater than 0, got -5"],
  185. ["T = ARRAY ORD({0..5} >= {0..8}) OF INTEGER",
  186. "array size must be greater than 0, got 0"]
  187. )
  188. ),
  189. "multi-dimensional array declaration": testWithGrammar(
  190. grammar.typeDeclaration,
  191. pass("T = ARRAY 10 OF ARRAY 5 OF INTEGER",
  192. "T = ARRAY 10, 5 OF INTEGER")
  193. ),
  194. "PROCEDURE type declaration": testWithContext(
  195. context(grammar.typeDeclaration, "TYPE R = RECORD END; A = ARRAY 3 OF INTEGER;"),
  196. pass("T = PROCEDURE",
  197. "T = PROCEDURE()",
  198. "T = PROCEDURE(a: INTEGER)",
  199. "T = PROCEDURE(a: INTEGER; b: BOOLEAN)",
  200. "T = PROCEDURE(): T"),
  201. fail(["T = PROCEDURE(): A;", "procedure cannot return ARRAY 3 OF INTEGER"],
  202. ["T = PROCEDURE(): R;", "procedure cannot return R"],
  203. ["T = ARRAY 3 OF PROCEDURE(): T;", "procedure cannot return ARRAY 3 OF PROCEDURE"]
  204. )
  205. ),
  206. "POINTER declaration": testWithGrammar(
  207. grammar.typeDeclaration,
  208. pass("T = POINTER TO RECORD END",
  209. "T = RECORD p: POINTER TO T END",
  210. "T = POINTER TO RECORD p: T END"),
  211. fail(["T = POINTER TO INTEGER",
  212. "RECORD is expected as a POINTER base type, got 'INTEGER'"],
  213. ["T = POINTER TO POINTER TO RECORD END",
  214. "RECORD is expected as a POINTER base type, got 'POINTER TO anonymous RECORD'"],
  215. ["T = POINTER TO RECORD p: POINTER TO T END",
  216. "RECORD is expected as a POINTER base type, got 'T'"],
  217. ["T = POINTER TO T",
  218. "RECORD is expected as a POINTER base type"]
  219. )
  220. ),
  221. "POINTER dereference": testWithContext(
  222. context(grammar.statement,
  223. "TYPE T = RECORD END; PT = POINTER TO T;"
  224. + "VAR pt: PT; p: POINTER TO RECORD field: INTEGER END; i: INTEGER; r: RECORD END;"
  225. + "PROCEDURE pVar(VAR r: T);END pVar;"),
  226. pass("p^.field := 1",
  227. "p.field := 0",
  228. "pVar(pt^)"),
  229. fail(["i^", "POINTER TO type expected, got 'INTEGER'"],
  230. ["r^", "POINTER TO type expected, got 'anonymous RECORD'"],
  231. ["p.unknown := 0", "type 'anonymous RECORD' has no 'unknown' field"],
  232. ["pt.constructor := 0", "type 'T' has no 'constructor' field"], // "constructor" is JS predefined property
  233. ["pt.prototype := 0", "type 'T' has no 'prototype' field"], // "prototype" is JS predefined property
  234. ["pt.unknown := 0", "type 'T' has no 'unknown' field"])
  235. ),
  236. "POINTER argument dereference and passing as VAR": testWithContext(
  237. context(grammar.declarationSequence,
  238. "TYPE T = RECORD END; PT = POINTER TO T;"
  239. + "VAR pt: PT; p: POINTER TO RECORD field: INTEGER END; i: INTEGER; r: RECORD END;"
  240. + "PROCEDURE pVar(VAR r: T);END pVar;"),
  241. pass("PROCEDURE proc(p: PT); BEGIN pVar(p^); END proc;"),
  242. fail()
  243. ),
  244. "POINTER assignment": testWithContext(
  245. context(grammar.statement,
  246. "TYPE Base = RECORD END;"
  247. + "Derived = RECORD (Base) END;"
  248. + "PDerivedAnonymous = POINTER TO RECORD(Base) END;"
  249. + "VAR p1, p2: POINTER TO RECORD END;"
  250. + "pBase: POINTER TO Base; pDerived: POINTER TO Derived;"
  251. + "pDerivedAnonymous: PDerivedAnonymous;"
  252. + "pDerivedAnonymous2: POINTER TO RECORD(Base) END;"
  253. ),
  254. pass("p1 := NIL",
  255. "p1 := p2",
  256. "pBase := pDerived",
  257. "pBase := pDerivedAnonymous",
  258. "pBase := pDerivedAnonymous2"
  259. ),
  260. fail(["p1 := pBase",
  261. "type mismatch: 'POINTER TO anonymous RECORD' cannot be assigned to 'POINTER TO Base' expression"],
  262. ["pDerived := pBase",
  263. "type mismatch: 'POINTER TO Derived' cannot be assigned to 'POINTER TO Base' expression"],
  264. ["NIL := p1", "not parsed"])
  265. ),
  266. "typeguard": testWithContext(
  267. context(grammar.expression,
  268. "TYPE Base = RECORD END; PBase = POINTER TO Base; Derived = RECORD (Base) END; PDerived = POINTER TO Derived;"
  269. + "VAR p1, p2: POINTER TO RECORD END; pBase: POINTER TO Base; pDerived: POINTER TO Derived;"
  270. + "vb: Base; i: INTEGER;"),
  271. pass("pBase(PDerived)",
  272. "pBase^(Derived)"),
  273. fail(["pDerived(PDerived)",
  274. "invalid type cast: 'Derived' is not an extension of 'Derived'"],
  275. ["p1(PBase)",
  276. "invalid type cast: 'Base' is not an extension of 'anonymous RECORD'"],
  277. ["p1(INTEGER)",
  278. "invalid type cast: POINTER type expected as an argument of POINTER type cast, got 'INTEGER'"],
  279. ["i(Derived)",
  280. "invalid type cast: POINTER to type or RECORD expected, got 'INTEGER'"],
  281. ["vb(Derived)",
  282. "invalid type cast: a value variable cannot be used"],
  283. ["vb(PDerived)",
  284. "invalid type cast: a value variable cannot be used"])
  285. ),
  286. "NIL": testWithContext(
  287. context(grammar.expression,
  288. "VAR i: INTEGER;"),
  289. pass(),
  290. fail(["i = NIL", "type mismatch: expected 'INTEGER', got 'NIL'"])
  291. ),
  292. "POINTER relations": testWithContext(
  293. context(grammar.expression,
  294. "TYPE B = RECORD END; D = RECORD(B) END; PB = POINTER TO B;"
  295. + "VAR p1, p2: POINTER TO RECORD END; pb: POINTER TO B; pd: POINTER TO D;"),
  296. pass("p1 = p2",
  297. "p1 # p2",
  298. "pb = pd",
  299. "pd # pb"
  300. ),
  301. fail(["p1 < p2", "operator '<' type mismatch: numeric type or CHAR or character array expected, got 'POINTER TO anonymous RECORD'"],
  302. ["p1 <= p2", "operator '<=' type mismatch: numeric type or SET or CHAR or character array expected, got 'POINTER TO anonymous RECORD'"],
  303. ["p1 > p2", "operator '>' type mismatch: numeric type or CHAR or character array expected, got 'POINTER TO anonymous RECORD'"],
  304. ["p1 >= p2", "operator '>=' type mismatch: numeric type or SET or CHAR or character array expected, got 'POINTER TO anonymous RECORD'"],
  305. ["p1 = pb", "type mismatch: expected 'POINTER TO anonymous RECORD', got 'POINTER TO B'"],
  306. ["pb = PB", "type name 'PB' cannot be used as an expression"],
  307. ["PB = pb", "type name 'PB' cannot be used as an expression"]
  308. )
  309. ),
  310. "IS expression": testWithContext(
  311. context(grammar.expression,
  312. "TYPE Base = RECORD END; Derived = RECORD (Base) END; PDerived = POINTER TO Derived;"
  313. + "VAR p: POINTER TO RECORD END; pBase: POINTER TO Base; pDerived: POINTER TO Derived; vDerived: Derived; i: INTEGER;"),
  314. pass("pBase IS PDerived",
  315. "pBase^ IS Derived"
  316. ),
  317. fail(["pBase IS pDerived", "type name expected"],
  318. ["pBase IS TRUE", "type name expected"],
  319. ["pBase IS vDerived", "type name expected"],
  320. ["Derived IS Derived",
  321. "type name 'Derived' cannot be used as an expression"],
  322. ["i IS Derived",
  323. "invalid type test: POINTER to type or RECORD expected, got 'INTEGER'"],
  324. ["p^ IS Derived",
  325. "invalid type test: 'Derived' is not an extension of 'anonymous RECORD'"],
  326. ["p IS PDerived",
  327. "invalid type test: 'Derived' is not an extension of 'anonymous RECORD'"],
  328. ["pDerived^ IS Derived",
  329. "invalid type test: 'Derived' is not an extension of 'Derived'"],
  330. ["pDerived IS PDerived",
  331. "invalid type test: 'Derived' is not an extension of 'Derived'"],
  332. ["pDerived^ IS Base",
  333. "invalid type test: 'Base' is not an extension of 'Derived'"],
  334. ["pDerived IS INTEGER",
  335. "invalid type test: POINTER type expected as an argument of POINTER type test, got 'INTEGER'"],
  336. ["pBase IS Derived",
  337. "invalid type test: POINTER type expected as an argument of POINTER type test, got 'Derived'"],
  338. ["pBase^ IS PDerived",
  339. "invalid type test: RECORD type expected as an argument of RECORD type test, got 'PDerived'"]
  340. )
  341. ),
  342. "IS for VAR argument": testWithContext(
  343. context(grammar.procedureDeclaration,
  344. "TYPE Base = RECORD END; Derived = RECORD (Base) i: INTEGER END;"
  345. + "T = RECORD END; TD = RECORD(T) b: Base END;"),
  346. pass("PROCEDURE proc(VAR p: Base): BOOLEAN; RETURN p IS Derived END proc"),
  347. fail(["PROCEDURE proc(p: Base): BOOLEAN; RETURN p IS Derived END proc",
  348. "invalid type test: a value variable cannot be used"],
  349. ["PROCEDURE proc(p: TD): BOOLEAN; RETURN p.b IS Derived END proc",
  350. "invalid type test: a value variable cannot be used"],
  351. ["PROCEDURE proc(VAR p: T):BOOLEAN; RETURN p(TD).b IS Derived END proc",
  352. "invalid type test: a value variable cannot be used"])
  353. ),
  354. "BYTE": testWithContext(
  355. context(grammar.statement,
  356. "VAR b1, b2: BYTE; i: INTEGER; set: SET; a: ARRAY 3 OF BYTE; ai: ARRAY 3 OF INTEGER;"
  357. + "PROCEDURE varIntParam(VAR i: INTEGER); END varIntParam;"
  358. + "PROCEDURE varByteParam(VAR b: BYTE); END varByteParam;"
  359. + "PROCEDURE arrayParam(b: ARRAY OF BYTE); END arrayParam;"
  360. + "PROCEDURE arrayIntParam(i: ARRAY OF INTEGER); END arrayIntParam;"
  361. ),
  362. pass("b1 := b2",
  363. "i := b1",
  364. "b2 := i",
  365. "a[b1] := i",
  366. "ASSERT(i = b1)",
  367. "ASSERT(b1 = i)",
  368. "ASSERT(i < b1)",
  369. "ASSERT(b1 > i)",
  370. "ASSERT(b1 IN set)",
  371. "i := b1 DIV i",
  372. "i := i DIV b1",
  373. "b1 := b1 MOD i",
  374. "b1 := i MOD b1",
  375. "b1 := b1 + i",
  376. "b1 := i - b1",
  377. "i := b1 * i",
  378. "i := -b1",
  379. "i := +b1",
  380. "arrayParam(a)",
  381. "arrayIntParam(ai)"
  382. ),
  383. fail(["i := b1 / i", "operator DIV expected for integer division"],
  384. ["varIntParam(b1)", "type mismatch for argument 1: cannot pass 'BYTE' as VAR parameter of type 'INTEGER'"],
  385. ["varByteParam(i)", "type mismatch for argument 1: cannot pass 'INTEGER' as VAR parameter of type 'BYTE'"],
  386. ["arrayParam(ai)", "type mismatch for argument 1: 'ARRAY 3 OF INTEGER' cannot be converted to 'ARRAY OF BYTE'"],
  387. ["arrayIntParam(a)", "type mismatch for argument 1: 'ARRAY 3 OF BYTE' cannot be converted to 'ARRAY OF INTEGER'"]
  388. )
  389. ),
  390. "NEW": testWithContext(
  391. context(grammar.statement,
  392. "TYPE P = POINTER TO RECORD END; T = RECORD END;"
  393. + "VAR p: P; i: INTEGER; r: RECORD END;"
  394. + "PROCEDURE proc(): P; RETURN NIL END proc;"
  395. ),
  396. pass("NEW(p)"),
  397. fail(["NEW.NEW(p)", "selector '.NEW' cannot be applied to 'standard procedure NEW'"],
  398. ["NEW(i)", "POINTER variable expected, got 'INTEGER'"],
  399. ["NEW(r)", "POINTER variable expected, got 'anonymous RECORD'"],
  400. ["NEW()", "1 argument(s) expected, got 0"],
  401. ["NEW(p, p)", "1 argument(s) expected, got 2"],
  402. ["NEW(proc())", "expression cannot be used as VAR parameter"],
  403. ["NEW(P)", "cannot apply type cast to standard procedure NEW"],
  404. ["NEW(T)", "cannot apply type cast to standard procedure NEW"]
  405. )
  406. ),
  407. "ABS": testWithContext(
  408. context(grammar.statement,
  409. "VAR i: INTEGER; r: REAL; c: CHAR;"),
  410. pass("i := ABS(i)",
  411. "r := ABS(r)"),
  412. fail(["i := ABS(r)", "type mismatch: 'INTEGER' cannot be assigned to 'REAL' expression"],
  413. ["i := ABS(c)", "type mismatch: expected numeric type, got 'CHAR'"],
  414. ["i := ABS(i, i)", "1 argument(s) expected, got 2"]
  415. )
  416. ),
  417. "FLOOR": testWithContext(
  418. context(grammar.statement, "VAR i: INTEGER; r: REAL;"),
  419. pass("i := FLOOR(r)"),
  420. fail(["i := FLOOR(i)", "type mismatch for argument 1: 'INTEGER' cannot be converted to 'REAL'"],
  421. ["i := FLOOR(r, r)", "1 argument(s) expected, got 2"]
  422. )
  423. ),
  424. "FLT": testWithContext(
  425. context(grammar.statement, "VAR i: INTEGER; r: REAL;"),
  426. pass("r := FLT(i)"),
  427. fail(["r := FLT(r)", "type mismatch for argument 1: 'REAL' cannot be converted to 'INTEGER'"],
  428. ["i := FLT(i, i)", "1 argument(s) expected, got 2"]
  429. )
  430. ),
  431. "LSL": testWithContext(
  432. context(grammar.statement,
  433. "VAR i: INTEGER; r: REAL; c: CHAR;"),
  434. pass("i := LSL(i, i)"),
  435. fail(["i := LSL(i, r)", "type mismatch for argument 2: 'REAL' cannot be converted to 'INTEGER'"],
  436. ["i := LSL(r, i)", "type mismatch for argument 1: 'REAL' cannot be converted to 'INTEGER'"],
  437. ["r := LSL(i, i)", "type mismatch: 'REAL' cannot be assigned to 'INTEGER' expression"],
  438. ["i := LSL(i)", "2 argument(s) expected, got 1"]
  439. )
  440. ),
  441. "ASR": testWithContext(
  442. context(grammar.statement,
  443. "VAR i: INTEGER; r: REAL; c: CHAR;"),
  444. pass("i := ASR(i, i)"),
  445. fail(["i := ASR(i, r)", "type mismatch for argument 2: 'REAL' cannot be converted to 'INTEGER'"],
  446. ["i := ASR(r, i)", "type mismatch for argument 1: 'REAL' cannot be converted to 'INTEGER'"],
  447. ["r := ASR(i, i)", "type mismatch: 'REAL' cannot be assigned to 'INTEGER' expression"],
  448. ["i := ASR(i)", "2 argument(s) expected, got 1"]
  449. )
  450. ),
  451. "ROR": testWithContext(
  452. context(grammar.statement,
  453. "VAR i: INTEGER; r: REAL; c: CHAR;"),
  454. pass("i := ROR(i, i)"),
  455. fail(["i := ROR(i, r)", "type mismatch for argument 2: 'REAL' cannot be converted to 'INTEGER'"],
  456. ["i := ROR(r, i)", "type mismatch for argument 1: 'REAL' cannot be converted to 'INTEGER'"],
  457. ["r := ROR(i, i)", "type mismatch: 'REAL' cannot be assigned to 'INTEGER' expression"],
  458. ["i := ROR(i)", "2 argument(s) expected, got 1"]
  459. )
  460. ),
  461. "ODD": testWithContext(
  462. context(grammar.statement, "VAR b: BOOLEAN;"),
  463. pass("b := ODD(1)",
  464. "b := ODD(123)"
  465. ),
  466. fail(["b := ODD(1.2)", "type mismatch for argument 1: 'REAL' cannot be converted to 'INTEGER'"],
  467. ["b := ODD(TRUE)", "type mismatch for argument 1: 'BOOLEAN' cannot be converted to 'INTEGER'"]
  468. )
  469. ),
  470. "ODD const expression": testWithGrammar(
  471. grammar.typeDeclaration,
  472. pass("T = ARRAY ORD(ODD(1)) OF INTEGER",
  473. "T = ARRAY ORD(ODD(3)) OF INTEGER"
  474. ),
  475. fail(["T = ARRAY ORD(ODD(0)) OF INTEGER", "array size must be greater than 0, got 0"],
  476. ["T = ARRAY ORD(ODD(2)) OF INTEGER", "array size must be greater than 0, got 0"]
  477. )
  478. ),
  479. "ORD": testWithContext(
  480. context(grammar.statement, "VAR ch: CHAR; i: INTEGER; b: BOOLEAN;"),
  481. pass("i := ORD(ch)",
  482. "i := ORD(TRUE)",
  483. "i := ORD(b)",
  484. "i := ORD(b = FALSE)",
  485. "i := ORD({1})",
  486. "i := ORD(\"a\")",
  487. "b := ORD(22X) = 022H"),
  488. fail(["i := ORD(1.2)", "ORD function expects CHAR or BOOLEAN or SET as an argument, got 'REAL'"],
  489. ["i := ORD(\"abc\")", "ORD function expects CHAR or BOOLEAN or SET as an argument, got 'multi-character string'"]
  490. )
  491. ),
  492. "ORD const expression": testWithGrammar(
  493. grammar.typeDeclaration,
  494. pass("T = ARRAY ORD({0}) OF INTEGER",
  495. "T = ARRAY ORD({0}) + 1 OF INTEGER",
  496. "T = ARRAY ORD(TRUE) OF INTEGER",
  497. "T = ARRAY ORD(TRUE) + 1 OF INTEGER",
  498. "T = ARRAY ORD(\"A\") OF INTEGER",
  499. "T = ARRAY ORD(\"A\") + 1 OF INTEGER"
  500. ),
  501. fail(["T = ARRAY ORD({}) OF INTEGER", "array size must be greater than 0, got 0"],
  502. ["T = ARRAY ORD(FALSE) OF INTEGER", "array size must be greater than 0, got 0"],
  503. ["T = ARRAY ORD(0X) OF INTEGER", "array size must be greater than 0, got 0"]
  504. )
  505. ),
  506. "CHR": testWithContext(
  507. context(grammar.statement, "VAR i: INTEGER; ch: CHAR;"),
  508. pass("ch := CHR(i)"),
  509. fail(["ch := CHR(ch)", "type mismatch for argument 1: 'CHAR' cannot be converted to 'INTEGER'"])
  510. ),
  511. "INC": testWithContext(
  512. context(grammar.statement, "VAR i: INTEGER;"),
  513. pass("INC(i)",
  514. "INC(i, 3)",
  515. "INC(i, i)"),
  516. fail(["INC(i + i)", "expression cannot be used as VAR parameter"],
  517. ["INC()", "at least 1 argument expected, got 0"],
  518. ["INC(i, 1, 2)", "at most 2 arguments expected, got 3"]
  519. )
  520. ),
  521. "DEC": testWithContext(
  522. context(grammar.statement, "VAR i: INTEGER;"),
  523. pass("DEC(i)",
  524. "DEC(i, 3)",
  525. "DEC(i, i)"),
  526. fail(["DEC(i + i)", "expression cannot be used as VAR parameter"],
  527. ["DEC()", "at least 1 argument expected, got 0"],
  528. ["DEC(i, 1, 2)", "at most 2 arguments expected, got 3"]
  529. )
  530. ),
  531. "PACK": testWithContext(
  532. context(grammar.statement, "VAR r: REAL; i: INTEGER;"),
  533. pass("PACK(r, i)",
  534. "PACK(r, 3)"),
  535. fail(["PACK(r, r)", "type mismatch for argument 2: 'REAL' cannot be converted to 'INTEGER'"])
  536. ),
  537. "UNPK": testWithContext(
  538. context(grammar.statement, "VAR r: REAL; i: INTEGER;"),
  539. pass("UNPK(r, i)"),
  540. fail(["UNPK(r, r)", "type mismatch for argument 2: 'REAL' cannot be converted to 'INTEGER'"],
  541. ["UNPK(r, 3)", "expression cannot be used as VAR parameter"],
  542. ["UNPK(123.456, i)", "expression cannot be used as VAR parameter"]
  543. )
  544. ),
  545. "standard procedure cannot be referenced" : testWithContext(
  546. context(grammar.expression, "VAR chr: PROCEDURE(c: CHAR): INTEGER;"),
  547. pass(),
  548. fail(["CHR", "standard procedure CHR cannot be referenced"])
  549. ),
  550. "assignment statement": testWithContext(
  551. context(grammar.statement,
  552. "CONST c = 15;"
  553. + "VAR ch: CHAR; i, n: INTEGER; b: BOOLEAN;"
  554. + "proc1: PROCEDURE; proc2: PROCEDURE(): INTEGER;"
  555. + "a: ARRAY 5 OF INTEGER;"
  556. + "PROCEDURE p(): INTEGER; RETURN 1 END p;"
  557. + "PROCEDURE noResult(); END noResult;"),
  558. pass("i := 0",
  559. "i := n",
  560. "i := c",
  561. "b := TRUE",
  562. "ch := \"A\"",
  563. "i := p()",
  564. "proc1 := proc1",
  565. "proc2 := NIL",
  566. "a[1] := 2"),
  567. fail(["i = 0", "did you mean ':=' (statement expected, got expression)?"],
  568. ["i := b", "type mismatch: 'INTEGER' cannot be assigned to 'BOOLEAN' expression"],
  569. ["c := i", "cannot assign to constant"],
  570. ["ch := \"AB\"",
  571. "type mismatch: 'CHAR' cannot be assigned to 'multi-character string' expression"],
  572. ["ch := CHAR",
  573. "type name 'CHAR' cannot be used as an expression"],
  574. ["i := .1", "expression expected"],
  575. ["proc1 := proc2",
  576. "type mismatch: 'PROCEDURE' cannot be assigned to 'PROCEDURE(): INTEGER' expression"],
  577. ["i := noResult()", "procedure returning no result cannot be used in an expression"])
  578. ),
  579. "INTEGER number": testWithGrammar(
  580. grammar.expression,
  581. pass("0",
  582. "123",
  583. "1H",
  584. "1FH",
  585. "0FFH",
  586. "0H"),
  587. fail(["FFH", "undeclared identifier: 'FFH'"],
  588. ["FF", "undeclared identifier: 'FF'"],
  589. ["1HH", "not parsed"],
  590. ["1H0", "not parsed"],
  591. ["1 23", "not parsed"],
  592. ["1F FH", "integer constant looks like having hexadecimal format but 'H' suffix is missing"]
  593. )
  594. ),
  595. "INTEGER number in statement": testWithGrammar(
  596. grammar.statement,
  597. pass("IF 1 < 2345 THEN END"),
  598. fail(["IF 1 < 2345THEN END", "invalid operand"],
  599. ["IF 1 < 2345HTHEN END", "invalid operand"])
  600. ),
  601. "SET statement": testWithContext(
  602. context(grammar.statement, "VAR s: SET;"),
  603. pass("s := {}",
  604. "s := {0}",
  605. "s := {0, 1}",
  606. "s := {1 + 2, 5..10}")
  607. //fail("s := {32}", "0..31")
  608. ),
  609. "REAL number": testWithGrammar(
  610. grammar.expression,
  611. pass("1.2345",
  612. "1.",
  613. "1.2345E6",
  614. "1.2345E+6",
  615. "1.2345E-12"),
  616. fail(["1..", "not parsed"],
  617. ["1..2", "not parsed"],
  618. ["1. 2345E-12", "not parsed"],
  619. ["1.23 45E-12", "not parsed"],
  620. ["1.2345 E-12", "not parsed"],
  621. ["1.2345E-1 2", "not parsed"])
  622. ),
  623. "REAL number in statement": testWithGrammar(
  624. grammar.statement,
  625. pass("IF 1. < 1.2345 THEN END"),
  626. fail(["IF 1. < 1.2345THEN END", "invalid operand"])
  627. ),
  628. "IF statement": testWithContext(
  629. context(grammar.statement,
  630. "VAR b1: BOOLEAN; i1: INTEGER; p: POINTER TO RECORD END;"),
  631. pass("IF b1 THEN i1 := 0 END",
  632. "IF FALSE THEN i1 := 0 ELSE i1 := 1 END",
  633. "IF TRUE THEN i1 := 0 ELSIF FALSE THEN i1 := 1 ELSE i1 := 2 END"),
  634. fail(["IF i1 THEN i1 := 0 END", "'BOOLEAN' expression expected, got 'INTEGER'"],
  635. ["IF b1 THEN i1 := 0 ELSIF i1 THEN i1 := 2 END",
  636. "'BOOLEAN' expression expected, got 'INTEGER'"],
  637. ["IF p THEN i1 := 0 END",
  638. "'BOOLEAN' expression expected, got 'POINTER TO anonymous RECORD'"],
  639. ["IF b1 (*THEN*) i1 := 0 END", "THEN expected"],
  640. ["IF b1 THEN i1 := 0 ELSIF ~b1 (*THEN*) i1 := 0 END", "THEN expected"])
  641. ),
  642. "CASE statement with integer or char": testWithContext(
  643. context(grammar.statement,
  644. "CONST ci = 15; cc = \"A\"; cb = TRUE; cs = \"abc\";"
  645. + "TYPE T = RECORD END;"
  646. + "VAR c1: CHAR; b1: BOOLEAN; i1, i2: INTEGER; byte: BYTE; p: POINTER TO RECORD END;"),
  647. pass("CASE i1 OF END",
  648. "CASE i1 OF | END",
  649. "CASE i1 OF | 0: b1 := TRUE END",
  650. "CASE i1 OF 0: b1 := TRUE END",
  651. "CASE cc OF \"A\": b1 := TRUE END",
  652. "CASE \"A\" OF \"A\": b1 := TRUE END",
  653. "CASE c1 OF \"A\": b1 := TRUE END",
  654. "CASE byte OF 3: b1 := TRUE END",
  655. "CASE i1 OF 0: b1 := TRUE | 1: b1 := FALSE END",
  656. "CASE i1 OF 0, 1: b1 := TRUE END",
  657. "CASE c1 OF \"A\", \"B\": b1 := TRUE END",
  658. "CASE i1 OF 0..2: b1 := TRUE END",
  659. "CASE i1 OF ci..2: b1 := TRUE END",
  660. "CASE c1 OF cc..\"Z\": b1 := TRUE END",
  661. "CASE i1 OF 1, 2, 3: b1 := TRUE | 4..10: b1 := FALSE | 11: c1 := \"A\" END",
  662. "CASE i1 OF 1, 2, 5..9: b1 := TRUE END"
  663. ),
  664. fail(["CASE i1 OF undefined: b1 := TRUE END",
  665. "undeclared identifier: 'undefined'"],
  666. ["CASE i1 OF i2: b1 := TRUE END",
  667. "'i2' is not a constant"],
  668. ["CASE b1 OF END", "'RECORD' or 'POINTER' or 'INTEGER' or 'BYTE' or 'CHAR' expected as CASE expression"],
  669. ["CASE \"AA\" OF \"A\": b1 := TRUE END", "'RECORD' or 'POINTER' or 'INTEGER' or 'BYTE' or 'CHAR' expected as CASE expression"],
  670. ["CASE i1 OF \"A\": b1 := TRUE END",
  671. "label must be 'INTEGER' (the same as case expression), got 'CHAR'"],
  672. ["CASE i1 OF p: b1 := TRUE END",
  673. "'p' is not a constant"],
  674. ["CASE i1 OF T: b1 := TRUE END",
  675. "'T' is not a constant"],
  676. ["CASE c1 OF \"A\", 1: b1 := TRUE END",
  677. "label must be 'CHAR' (the same as case expression), got 'INTEGER'"],
  678. ["CASE c1 OF \"A\"..1: b1 := TRUE END",
  679. "label must be 'CHAR' (the same as case expression), got 'INTEGER'"],
  680. ["CASE c1 OF cs: b1 := TRUE END", "single-character string expected"],
  681. ["CASE ci OF cb: b1 := TRUE END", "label must be 'INTEGER' (the same as case expression), got 'BOOLEAN'"],
  682. ["CASE ci OF TRUE: b1 := TRUE END", "not parsed"]
  683. )
  684. ),
  685. "CASE statement with type guard": testWithContext(
  686. context(grammar.statement,
  687. "CONST c = 0;"
  688. + "TYPE Base = RECORD END;"
  689. + "Derived = RECORD (Base) i: INTEGER END; PDerived = POINTER TO Derived;"
  690. + "Derived2 = RECORD (Base) i2: INTEGER END; PDerived2 = POINTER TO Derived2;"
  691. + " T2 = RECORD i: INTEGER; b: Base END; PT2 = POINTER TO T2;"
  692. + "VAR b: Base; pb: POINTER TO Base; t2: T2;"),
  693. pass("CASE pb OF END",
  694. "CASE pb OF PDerived: pb.i := 0 END",
  695. "CASE pb OF PDerived: pb.i := 0 | PDerived2: pb.i2 := 0 END"
  696. ),
  697. fail(["CASE b OF END", "only records passed as VAR argument can be used to test type in CASE"],
  698. ["CASE t2.b OF END", "only records passed as VAR argument can be used to test type in CASE"],
  699. ["CASE pb OF Derived END", "invalid type test: POINTER type expected as an argument of POINTER type test, got 'Derived'"],
  700. ["CASE pb OF 123 END", "type's name expected in label, got expression: 123"],
  701. ["CASE pb OF \"a\" END", "type's name expected in label, got expression: \"a\""],
  702. ["CASE pb OF c END", "'c' is not a type"],
  703. ["CASE pb OF PT2: pb.i := 0 END", "invalid type test: 'T2' is not an extension of 'Base'"],
  704. ["CASE pb OF PDerived..PDerived2: END", "cannot use diapason (..) with type guard"],
  705. ["CASE pb OF PDerived..1: END", "type's name expected in label, got expression: 1"],
  706. ["CASE c OF 0..PDerived: END", "'PDerived' is not a constant"]
  707. )
  708. ),
  709. "CASE statement with type guard - pass as VAR argument": testWithContext(
  710. context(grammar.declarationSequence,
  711. "TYPE Base = RECORD END;"
  712. + "Derived = RECORD (Base) END; PDerived = POINTER TO Derived;"
  713. + "PROCEDURE passDerived(d: Derived); END passDerived;"
  714. + "PROCEDURE passDerivedVar(VAR d: Derived); END passDerivedVar;"
  715. ),
  716. pass("PROCEDURE p(VAR b: Base); BEGIN CASE b OF Derived: passDerived(b) END; END p;",
  717. "PROCEDURE p(VAR b: Base); BEGIN CASE b OF Derived: passDerivedVar(b) END; END p;"
  718. )
  719. ),
  720. "CASE statement with type guard for VAR argument": testWithContext(
  721. context(grammar.declarationSequence,
  722. "TYPE Base = RECORD END; Derived = RECORD (Base) i: INTEGER END; PBase = POINTER TO Base; PDerived = POINTER TO Derived;"),
  723. pass("PROCEDURE p(VAR b: Base); BEGIN CASE b OF END END p;",
  724. "PROCEDURE p(VAR b: Base); BEGIN CASE b OF Derived: END END p;",
  725. "PROCEDURE p(VAR b: PBase); BEGIN CASE b OF PDerived: END END p;",
  726. "PROCEDURE p(VAR b: Base); BEGIN CASE b OF Derived: b.i := 0 END END p;"
  727. ),
  728. fail(["PROCEDURE p(b: Base); BEGIN CASE b OF END END p;", "only records passed as VAR argument can be used to test type in CASE"],
  729. ["PROCEDURE p(VAR b: PBase); BEGIN CASE b OF PDerived: b.i := 0 END END p;", "type 'Base' has no 'i' field"])
  730. ),
  731. "CASE statement with type guard scope": testWithContext(
  732. context(grammar.declarationSequence,
  733. "TYPE Base = RECORD END; Derived = RECORD (Base) i: INTEGER END; Derived2 = RECORD (Base) i2: INTEGER END;"),
  734. pass(),
  735. fail(["PROCEDURE p(VAR b: Base); BEGIN CASE b OF Derived: END; b.i := 0; END p;", "type 'Base' has no 'i' field"],
  736. ["PROCEDURE p(VAR b: Base); BEGIN CASE b OF Derived: | Derived2: b.i := 0 END; END p;", "type 'Derived2' has no 'i' field"])
  737. ),
  738. "CASE statement with type guard and non-variable expression": testWithContext(
  739. context(grammar.statement,
  740. "TYPE Base = RECORD pb: POINTER TO Base END; Derived = RECORD (Base) i: INTEGER END; PDerived = POINTER TO Derived;"
  741. + " T2 = RECORD i: INTEGER END;"
  742. + "VAR b: Base; pb: POINTER TO Base;"),
  743. pass("CASE pb^ OF END",
  744. "CASE pb^ OF Derived: ASSERT(TRUE) END",
  745. "CASE b.pb OF END",
  746. "CASE b.pb OF PDerived: ASSERT(TRUE) END",
  747. "CASE b.pb^ OF END",
  748. "CASE b.pb^ OF Derived: ASSERT(TRUE) END"
  749. ),
  750. fail(["CASE pb^ OF Derived: pb.i := 0 END", "type 'Base' has no 'i' field"]
  751. )
  752. ),
  753. "CASE statement with type guard for imported type": testWithModule(
  754. "MODULE test; TYPE Base* = RECORD END; Derived* = RECORD(Base) END; Derived2 = RECORD(Base) END; END test.",
  755. pass("MODULE m; IMPORT test; PROCEDURE p(VAR b: test.Base); BEGIN CASE b OF test.Derived: END; END p; END m."),
  756. fail(["MODULE m; IMPORT test; PROCEDURE p(VAR b: test.Base); BEGIN CASE b OF test.Derived2: END; END p; END m.",
  757. "identifier 'Derived2' is not exported by module 'test'"]
  758. )),
  759. "CASE statement with imported constant": testWithModule(
  760. "MODULE test; CONST i* = 0; END test.",
  761. pass("MODULE m; IMPORT test; PROCEDURE p(j: INTEGER); BEGIN CASE j OF test.i: END; END p; END m."),
  762. fail(["MODULE m; IMPORT test; PROCEDURE p(j: INTEGER); BEGIN CASE j OF test.unknown: END; END p; END m.",
  763. "identifier 'unknown' is not exported by module 'test'"]
  764. )),
  765. "WHILE statement": testWithContext(
  766. context(grammar.statement,
  767. "VAR b1: BOOLEAN; i1: INTEGER;"),
  768. pass("WHILE TRUE DO i1 := 0 END",
  769. "WHILE b1 DO i1 := 0 ELSIF FALSE DO i1 := 1 END"),
  770. fail(["WHILE i1 DO i1 := 0 END", "'BOOLEAN' expression expected, got 'INTEGER'"],
  771. ["WHILE b1 DO i1 := 0 ELSIF i1 DO i1 := 1 END", "'BOOLEAN' expression expected, got 'INTEGER'"])
  772. ),
  773. "REPEAT statement": testWithContext(
  774. context(grammar.statement,
  775. "VAR b1: BOOLEAN; i1: INTEGER;"),
  776. pass("REPEAT i1 := 0 UNTIL TRUE",
  777. "REPEAT i1 := 0 UNTIL b1"),
  778. fail(["REPEAT i1 := 0 UNTIL i1", "'BOOLEAN' expression expected, got 'INTEGER'"])
  779. ),
  780. "FOR statement": testWithContext(
  781. context(grammar.statement,
  782. "CONST c = 15; zero = 1 - 1;"
  783. + "VAR b: BOOLEAN; i, n: INTEGER; ch: CHAR; p: POINTER TO RECORD END;"),
  784. pass("FOR i := 0 TO 10 DO n := 1 END",
  785. "FOR i := 0 TO 10 BY 5 DO b := TRUE END",
  786. "FOR i := 0 TO n DO b := TRUE END",
  787. "FOR i := 0 TO n BY c DO n := 1; b := FALSE END",
  788. "FOR i := 0 TO 10 BY 0 DO b := TRUE END",
  789. "FOR i := 0 TO 10 BY zero DO b := TRUE END"
  790. ),
  791. fail(["FOR undefined := 0 TO 10 DO n := 1 END",
  792. "undeclared identifier: 'undefined'"],
  793. ["FOR b := TRUE TO 10 DO n := 1 END",
  794. "'b' is a 'BOOLEAN' variable, 'FOR' control variable must be 'INTEGER'"],
  795. ["FOR ch := 'a' TO 10 DO n := 1 END",
  796. "'ch' is a 'CHAR' variable, 'FOR' control variable must be 'INTEGER'"],
  797. ["FOR c := 0 TO 10 DO END", "'c' is not a variable"],
  798. ["FOR i := TRUE TO 10 DO n := 1 END",
  799. "'INTEGER' expression expected to assign 'i', got 'BOOLEAN'"],
  800. ["FOR i := p TO 10 DO n := 1 END",
  801. "'INTEGER' expression expected to assign 'i', got 'POINTER TO anonymous RECORD'"],
  802. ["FOR i := 0 TO p DO n := 1 END",
  803. "'INTEGER' expression expected as 'TO' parameter, got 'POINTER TO anonymous RECORD'"],
  804. ["FOR i := 0 TO TRUE DO END",
  805. "'INTEGER' expression expected as 'TO' parameter, got 'BOOLEAN'"],
  806. ["FOR i := 0 TO 10 BY n DO END",
  807. "constant expression expected as 'BY' parameter"],
  808. ["FOR i := 0 TO 10 BY p DO END",
  809. "'INTEGER' expression expected as 'BY' parameter, got 'POINTER TO anonymous RECORD'"],
  810. ["FOR i := 0 TO 10 BY TRUE DO END",
  811. "'INTEGER' expression expected as 'BY' parameter, got 'BOOLEAN'"],
  812. ["FOR i := 0 TO 10 DO - END", "END expected (FOR)"]
  813. )
  814. ),
  815. "logical operators": testWithContext(
  816. context(grammar.statement, "VAR b1, b2: BOOLEAN; i1: INTEGER; p: POINTER TO RECORD END;"),
  817. pass("b1 := b1 OR b2",
  818. "b1 := b1 & b2",
  819. "b1 := ~b2"),
  820. fail(["b1 := i1 OR b2", "BOOLEAN expected as operand of 'OR', got 'INTEGER'"],
  821. ["b1 := b1 OR i1", "type mismatch: expected 'BOOLEAN', got 'INTEGER'"],
  822. ["b1 := p OR b1", "BOOLEAN expected as operand of 'OR', got 'POINTER TO anonymous RECORD'"],
  823. ["b1 := i1 & b2", "BOOLEAN expected as operand of '&', got 'INTEGER'"],
  824. ["b1 := b1 & i1", "type mismatch: expected 'BOOLEAN', got 'INTEGER'"],
  825. ["b1 := ~i1", "type mismatch: expected 'BOOLEAN', got 'INTEGER'"])
  826. ),
  827. "arithmetic operators": testWithContext(
  828. context(grammar.statement,
  829. "VAR b1: BOOLEAN; i1, i2: INTEGER; r1, r2: REAL; c1: CHAR; s1: SET;"
  830. + "p1: PROCEDURE; ptr1: POINTER TO RECORD END;"),
  831. pass("i1 := i1 + i2",
  832. "i1 := i1 - i2",
  833. "i1 := i1 * i2",
  834. "i1 := i1 DIV i2",
  835. "i1 := i1 MOD i2",
  836. "r1 := r1 + r2",
  837. "r1 := r1 - r2",
  838. "r1 := r1 * r2",
  839. "r1 := r1 / r2"),
  840. fail(["i1 := i1 / i2", "operator DIV expected for integer division"],
  841. ["r1 := r1 DIV r1", "operator 'DIV' type mismatch: 'INTEGER' or 'BYTE' expected, got 'REAL'"],
  842. ["c1 := c1 - c1", "operator '-' type mismatch: numeric type or SET expected, got 'CHAR'"],
  843. ["p1 := p1 * p1", "operator '*' type mismatch: numeric type or SET expected, got 'PROCEDURE'"],
  844. ["ptr1 := ptr1 / ptr1", "operator '/' type mismatch: numeric type or SET expected, got 'POINTER TO anonymous RECORD'"],
  845. ["s1 := +s1", "operator '+' type mismatch: numeric type expected, got 'SET'"],
  846. ["b1 := -b1", "operator '-' type mismatch: numeric type or SET expected, got 'BOOLEAN'"],
  847. ["s1 := +b1", "operator '+' type mismatch: numeric type expected, got 'BOOLEAN'"])
  848. ),
  849. "relations are BOOLEAN": testWithContext(
  850. context(grammar.statement,
  851. "TYPE Base = RECORD END; Derived = RECORD (Base) END;"
  852. + "VAR pBase: POINTER TO Base; proc1, proc2: PROCEDURE;"
  853. + "set1, set2: SET;"
  854. + "b: BOOLEAN; i1, i2: INTEGER; r1, r2: REAL; c1, c2: CHAR; ca1, ca2: ARRAY 10 OF CHAR;"),
  855. pass("b := pBase^ IS Derived",
  856. "b := pBase = pBase",
  857. "b := proc1 # proc2",
  858. "b := set1 <= set2",
  859. "b := i1 IN set2",
  860. "b := i1 < i2",
  861. "IF i1 > i2 THEN END",
  862. "b := c1 > c2",
  863. "b := ca1 <= ca2",
  864. "b := r1 >= r2")
  865. ),
  866. "SET relations": testWithContext(
  867. context(grammar.expression,
  868. "CONST constSet1 = {}; constSet2 = {};"
  869. + "VAR set1, set2: SET; b: BOOLEAN; i: INTEGER;"),
  870. pass("set1 <= set2",
  871. "set1 >= set2",
  872. "set1 = set2",
  873. "set1 # set2",
  874. "constSet1 = constSet2",
  875. "constSet1 # constSet2",
  876. "i IN set1"),
  877. fail(["set1 <= i", "type mismatch: expected 'SET', got 'INTEGER'"],
  878. ["b IN set1", "'INTEGER' or 'BYTE' expected as an element of SET, got 'BOOLEAN'"],
  879. ["i IN b", "type mismatch: expected 'SET', got 'BOOLEAN'"],
  880. ["set1 < set2", "operator '<' type mismatch: numeric type or CHAR or character array expected, got 'SET'"],
  881. ["set1 > set2", "operator '>' type mismatch: numeric type or CHAR or character array expected, got 'SET'"]
  882. )
  883. ),
  884. "SET operators": testWithContext(
  885. context(grammar.expression,
  886. "VAR set1, set2: SET; b: BOOLEAN; i: INTEGER;"),
  887. pass("set1 + set2",
  888. "set1 - set2",
  889. "set1 * set2",
  890. "set1 / set2",
  891. "-set1"),
  892. fail(["set1 + i", "type mismatch: expected 'SET', got 'INTEGER'"],
  893. ["set1 - b", "type mismatch: expected 'SET', got 'BOOLEAN'"],
  894. ["set1 * b", "type mismatch: expected 'SET', got 'BOOLEAN'"],
  895. ["set1 / b", "type mismatch: expected 'SET', got 'BOOLEAN'"])
  896. ),
  897. "SET functions": testWithContext(
  898. context(grammar.statement,
  899. "VAR set1, set2: SET; b: BOOLEAN; i: INTEGER;"),
  900. pass("INCL(set1, 0)",
  901. "EXCL(set1, 3)",
  902. "INCL(set1, i)",
  903. "EXCL(set1, i)"),
  904. fail(["INCL({}, i)", "expression cannot be used as VAR parameter"],
  905. ["INCL(set1, 32)", "value (0..31) expected as a second argument of INCL, got 32"],
  906. ["EXCL(set1, -1)", "value (0..31) expected as a second argument of EXCL, got -1"]
  907. )
  908. ),
  909. "PROCEDURE relations": testWithContext(
  910. context(grammar.expression,
  911. "VAR p1: PROCEDURE; p2: PROCEDURE;"),
  912. pass("p1 = p2",
  913. "p1 # p2",
  914. "p1 = NIL",
  915. "NIL # p1"
  916. )
  917. ),
  918. "VAR parameter": testWithContext(
  919. context(grammar.statement,
  920. "CONST c = 123;"
  921. + "TYPE Base = RECORD END; Derived = RECORD (Base) END; PBase = POINTER TO Base; PDerived = POINTER TO Derived;"
  922. + "VAR i1: INTEGER; b1: BOOLEAN; a1: ARRAY 5 OF INTEGER;"
  923. + "r1: RECORD f1: INTEGER END;"
  924. + "pBase: PBase; pDerived: PDerived;"
  925. + "PROCEDURE p1(VAR i: INTEGER); END p1;"
  926. + "PROCEDURE p2(VAR b: BOOLEAN); END p2;"
  927. + "PROCEDURE procBasePointer(VAR p: PBase); END procBasePointer;"
  928. + "PROCEDURE int(): INTEGER; RETURN 0 END int;"
  929. ),
  930. pass("p1(i1)",
  931. "p1(a1[0])",
  932. "p1(r1.f1)"),
  933. fail(["p1(c)", "constant cannot be passed as VAR actual parameter"],
  934. ["p1(123)", "expression cannot be used as VAR parameter"],
  935. ["p2(TRUE)", "expression cannot be used as VAR parameter"],
  936. ["procBasePointer(NIL)", "expression cannot be used as VAR parameter"],
  937. ["p1(i1 + i1)", "expression cannot be used as VAR parameter"],
  938. ["p1(i1 * i1)", "expression cannot be used as VAR parameter"],
  939. ["p1(+i1)", "expression cannot be used as VAR parameter"],
  940. ["p1(-i1)", "expression cannot be used as VAR parameter"],
  941. ["p2(~b1)", "expression cannot be used as VAR parameter"],
  942. ["p1(int())", "expression cannot be used as VAR parameter"],
  943. ["procBasePointer(pDerived)",
  944. "type mismatch for argument 1: cannot pass 'PDerived' as VAR parameter of type 'PBase'"]
  945. )
  946. ),
  947. "procedure call": testWithContext(
  948. context(grammar.statement,
  949. "TYPE ProcType = PROCEDURE;" +
  950. "VAR notProcedure: INTEGER; ptr: POINTER TO RECORD END;" +
  951. "PROCEDURE p; END p;" +
  952. "PROCEDURE p1(i: INTEGER); END p1;" +
  953. "PROCEDURE p2(i: INTEGER; b: BOOLEAN); END p2;" +
  954. "PROCEDURE p3(): ProcType; RETURN p END p3;"),
  955. pass("p",
  956. "p()",
  957. "p1(1)",
  958. "p1(1 + 2)",
  959. "p2(1, TRUE)"),
  960. fail(["notProcedure", "PROCEDURE expected, got 'INTEGER'"],
  961. ["ptr()", "PROCEDURE expected, got 'POINTER TO anonymous RECORD'"],
  962. ["p2(TRUE, 1)", "type mismatch for argument 1: 'BOOLEAN' cannot be converted to 'INTEGER'"],
  963. ["p2(1, 1)", "type mismatch for argument 2: 'INTEGER' cannot be converted to 'BOOLEAN'"],
  964. ["p3", "procedure returning a result cannot be used as a statement"],
  965. ["p3()", "procedure returning a result cannot be used as a statement"],
  966. ["IF p() THEN END", "procedure returning no result cannot be used in an expression"],
  967. ["IF ~p() THEN END", "type mismatch: expected 'BOOLEAN', got no type (proper procedure call)"]
  968. )
  969. ),
  970. "procedure assignment": testWithContext(
  971. context(grammar.statement,
  972. "TYPE ProcType1 = PROCEDURE(): ProcType1;"
  973. + "ProcType2 = PROCEDURE(): ProcType2;"
  974. + "ProcType3 = PROCEDURE(p: ProcType3): ProcType3;"
  975. + "ProcType4 = PROCEDURE(p: ProcType4): ProcType4;"
  976. + "ProcType4VAR = PROCEDURE(VAR p: ProcType4VAR): ProcType4VAR;"
  977. + "ProcType5 = PROCEDURE(p: ProcType3): ProcType4;"
  978. + "ProcType6 = PROCEDURE(p: INTEGER);"
  979. + "ProcType7 = PROCEDURE(VAR p: INTEGER);"
  980. + "VAR v1: ProcType1; v2: ProcType2;"
  981. + "v3: PROCEDURE(i: INTEGER): ProcType1; v4: PROCEDURE(b: BOOLEAN): ProcType1;"
  982. + "v5: PROCEDURE(p: ProcType1); v6: PROCEDURE(p: ProcType2);"
  983. + "v7: ProcType3; v8: ProcType4; v8VAR: ProcType4VAR; v9: ProcType5; v10: ProcType6; v11: ProcType7;"
  984. + "vProcCharArray: PROCEDURE (a: ARRAY OF CHAR);"
  985. + "vProcInt: PROCEDURE (i: INTEGER);"
  986. + "vProcReturnInt: PROCEDURE(): INTEGER;"
  987. + "PROCEDURE p1(): ProcType1; RETURN p1 END p1;"
  988. + "PROCEDURE procCharArray(a: ARRAY OF CHAR); END procCharArray;"
  989. + "PROCEDURE procIntArray(a: ARRAY OF INTEGER); END procIntArray;"
  990. + "PROCEDURE procByte(b: BYTE); END procByte;"
  991. + "PROCEDURE procReturnByte(): BYTE; RETURN 0 END procReturnByte;"
  992. ),
  993. pass("v1 := v2",
  994. "v5 := v6",
  995. "v7 := v8",
  996. "v7 := v9",
  997. "v8 := v9",
  998. "v1 := p1",
  999. "vProcCharArray := procCharArray"),
  1000. fail(["p1 := v1", "cannot assign to procedure 'p1'"],
  1001. ["v3 := v1",
  1002. "type mismatch: 'PROCEDURE(INTEGER): ProcType1' cannot be assigned to 'ProcType1' expression"],
  1003. ["v3 := v4",
  1004. "type mismatch: 'PROCEDURE(INTEGER): ProcType1' cannot be assigned to 'PROCEDURE(BOOLEAN): ProcType1' expression"],
  1005. ["v10 := NEW",
  1006. "standard procedure NEW cannot be referenced"],
  1007. ["v10 := v11", "type mismatch: 'ProcType6' cannot be assigned to 'ProcType7' expression" ],
  1008. ["v8 := v8VAR", "type mismatch: 'ProcType4' cannot be assigned to 'ProcType4VAR' expression" ],
  1009. ["vProcCharArray := procIntArray",
  1010. "type mismatch: 'PROCEDURE(ARRAY OF CHAR)' cannot be assigned to 'PROCEDURE(ARRAY OF INTEGER)' expression"],
  1011. ["vProcInt := procByte",
  1012. "type mismatch: 'PROCEDURE(INTEGER)' cannot be assigned to 'PROCEDURE(BYTE)' expression"],
  1013. ["vProcReturnInt := procReturnByte",
  1014. "type mismatch: 'PROCEDURE(): INTEGER' cannot be assigned to 'PROCEDURE(): BYTE' expression"]
  1015. )
  1016. ),
  1017. "string assignment": testWithContext(
  1018. context(grammar.statement,
  1019. "VAR a1: ARRAY 3 OF CHAR;"
  1020. + "ch1: CHAR;"
  1021. + "intArray: ARRAY 10 OF INTEGER;"
  1022. ),
  1023. pass("a1 := \"abc\"",
  1024. "a1 := \"ab\"",
  1025. "a1 := \"a\"",
  1026. "a1 := 22X",
  1027. "ch1 := \"A\"",
  1028. "ch1 := 22X"),
  1029. fail(["a1 := \"abcd\"", "3-character ARRAY is too small for 4-character string"],
  1030. ["intArray := \"abcd\"",
  1031. "type mismatch: 'ARRAY 10 OF INTEGER' cannot be assigned to 'multi-character string' expression"])
  1032. ),
  1033. "string relations": testWithContext(
  1034. context(grammar.expression,
  1035. "VAR ch: CHAR;"),
  1036. pass("ch = \"a\"",
  1037. "\"a\" = ch",
  1038. "ch # \"a\"",
  1039. "\"a\" # ch"
  1040. ),
  1041. fail(["ch = \"ab\"", "type mismatch: expected 'CHAR', got 'multi-character string'"])
  1042. ),
  1043. "array assignment": testWithContext(
  1044. context(grammar.statement,
  1045. "VAR charArray: ARRAY 3 OF CHAR;"
  1046. + "intArray: ARRAY 10 OF INTEGER;"
  1047. + "intArray2: ARRAY 10 OF INTEGER;"
  1048. + "intArray3: ARRAY 5 OF INTEGER;"
  1049. + "intArray23m1: ARRAY 2 OF ARRAY 3 OF INTEGER;"
  1050. + "intArray23m2: ARRAY 2, 3 OF INTEGER;"
  1051. + "intArray24m: ARRAY 2, 4 OF INTEGER;"
  1052. + "intArray43m: ARRAY 4, 3 OF INTEGER;"
  1053. ),
  1054. pass("intArray := intArray2",
  1055. "intArray23m1 := intArray23m2",
  1056. "intArray23m2 := intArray23m1",
  1057. "intArray43m[0] := intArray23m1[0]"
  1058. ),
  1059. fail(["intArray := charArray",
  1060. "type mismatch: 'ARRAY 10 OF INTEGER' cannot be assigned to 'ARRAY 3 OF CHAR' expression"],
  1061. ["intArray2 := intArray3",
  1062. "type mismatch: 'ARRAY 10 OF INTEGER' cannot be assigned to 'ARRAY 5 OF INTEGER' expression"],
  1063. ["intArray3 := charArray",
  1064. "type mismatch: 'ARRAY 5 OF INTEGER' cannot be assigned to 'ARRAY 3 OF CHAR' expression"],
  1065. ["intArray24m := intArray23m1",
  1066. "type mismatch: 'ARRAY 2, 4 OF INTEGER' cannot be assigned to 'ARRAY 2, 3 OF INTEGER' expression"]
  1067. )
  1068. ),
  1069. "record assignment": testWithContext(
  1070. context(grammar.statement,
  1071. "TYPE Base1 = RECORD END;"
  1072. + "T1 = RECORD (Base1) END;"
  1073. + "T2 = RECORD END;"
  1074. + "VAR b1: Base1; r1: T1; r2: T2; pb1: POINTER TO Base1;"
  1075. ),
  1076. pass("r1 := r1",
  1077. "b1 := r1",
  1078. "pb1^ := b1",
  1079. "pb1^ := r1",
  1080. "pb1^ := pb1^"
  1081. ),
  1082. fail(["r1 := r2", "type mismatch: 'T1' cannot be assigned to 'T2' expression"],
  1083. ["r1 := b1", "type mismatch: 'T1' cannot be assigned to 'Base1' expression"])
  1084. ),
  1085. "string argument": testWithContext(
  1086. context(grammar.statement,
  1087. "PROCEDURE p1(s: ARRAY OF CHAR); END p1;"
  1088. + "PROCEDURE p2(VAR s: ARRAY OF CHAR); END p2;"
  1089. + "PROCEDURE p3(i: INTEGER); END p3;"
  1090. + "PROCEDURE p4(a: ARRAY OF INTEGER); END p4;"
  1091. ),
  1092. pass("p1(\"abc\")"),
  1093. fail(["p2(\"abc\")", "expression cannot be used as VAR parameter"],
  1094. ["p3(\"abc\")", "type mismatch for argument 1: 'multi-character string' cannot be converted to 'INTEGER'"],
  1095. ["p4(\"abc\")", "type mismatch for argument 1: 'multi-character string' cannot be converted to 'ARRAY OF INTEGER'"])
  1096. ),
  1097. "assert": testWithGrammar(
  1098. grammar.statement,
  1099. pass("ASSERT(TRUE)"),
  1100. fail(["ASSERT()", "1 argument(s) expected, got 0"],
  1101. ["ASSERT(TRUE, 123)", "1 argument(s) expected, got 2"],
  1102. ["ASSERT(123)", "type mismatch for argument 1: 'INTEGER' cannot be converted to 'BOOLEAN'"])
  1103. ),
  1104. "import module with reserved name": testWithContext(
  1105. { grammar: grammar.module,
  1106. source: "",
  1107. moduleReader: function(name){
  1108. TestUnitCommon.expectEq(name, "Math");
  1109. return "MODULE " + name + "; END " + name + ".";
  1110. }
  1111. },
  1112. pass("MODULE m; IMPORT Math; END m."),
  1113. fail()
  1114. ),
  1115. "imported module without exports": testWithModule(
  1116. "MODULE test; END test.",
  1117. pass("MODULE m; IMPORT test; END m."),
  1118. fail(["MODULE m; IMPORT test; BEGIN test.p(); END m.",
  1119. "identifier 'p' is not exported by module 'test'"],
  1120. ["MODULE m; IMPORT t := test; BEGIN t.p(); END m.",
  1121. "identifier 'p' is not exported by module 'test'"],
  1122. ["MODULE m; IMPORT test; BEGIN test(); END m.",
  1123. "PROCEDURE expected, got 'MODULE'"]
  1124. )),
  1125. "import record type": testWithModule(
  1126. "MODULE test; TYPE T* = RECORD f*: INTEGER; notExported: BOOLEAN END; END test.",
  1127. pass("MODULE m; IMPORT test; VAR r: test.T; BEGIN r.f := 0; END m."),
  1128. fail(["MODULE m; IMPORT test; VAR r: test.T; BEGIN r.notExported := FALSE; END m.",
  1129. "type 'T' has no 'notExported' field"]
  1130. )),
  1131. "imported variables are read-only": testWithModule(
  1132. "MODULE test; VAR i*: INTEGER; END test.",
  1133. pass("MODULE m; IMPORT test; PROCEDURE p(i: INTEGER); END p; BEGIN p(test.i); END m."),
  1134. fail(["MODULE m; IMPORT test; BEGIN test.i := 123; END m.",
  1135. "cannot assign to imported variable"],
  1136. ["MODULE m; IMPORT test; PROCEDURE p(VAR i: INTEGER); END p; BEGIN p(test.i); END m.",
  1137. "imported variable cannot be passed as VAR actual parameter"]
  1138. )
  1139. ),
  1140. "import pointer type": testWithModule(
  1141. "MODULE test;"
  1142. + "TYPE TPAnonymous1* = POINTER TO RECORD END; TPAnonymous2* = POINTER TO RECORD END;"
  1143. + "Base* = RECORD END; TPDerived* = POINTER TO RECORD(Base) END;"
  1144. + "END test.",
  1145. pass("MODULE m; IMPORT test; VAR p1: test.TPAnonymous1; p2: test.TPAnonymous2; END m.",
  1146. "MODULE m; IMPORT test;"
  1147. + "VAR pb: POINTER TO test.Base; pd: test.TPDerived;"
  1148. + "BEGIN pb := pd; END m."),
  1149. fail(["MODULE m; IMPORT test; VAR p1: test.TPAnonymous1; p2: test.TPAnonymous2; BEGIN p1 := p2; END m.",
  1150. "type mismatch: 'TPAnonymous1' cannot be assigned to 'TPAnonymous2' expression"]
  1151. )
  1152. ),
  1153. "import array type": testWithModule(
  1154. "MODULE test; TYPE TA* = ARRAY 3 OF INTEGER; END test.",
  1155. pass("MODULE m; IMPORT test; VAR a: test.TA; END m.")
  1156. ),
  1157. "import procedure type": testWithModule(
  1158. "MODULE test; TYPE TProc* = PROCEDURE; END test.",
  1159. pass("MODULE m; IMPORT test; VAR proc: test.TProc; END m.")
  1160. ),
  1161. "imported pointer type cannot be used in NEW if base type is not exported": testWithModule(
  1162. "MODULE test;"
  1163. + "TYPE T = RECORD END; TP* = POINTER TO T;"
  1164. + "TPAnonymous* = POINTER TO RECORD END; END test.",
  1165. pass(),
  1166. fail(["MODULE m; IMPORT test; VAR p: test.TPAnonymous; BEGIN NEW(p) END m.",
  1167. "non-exported RECORD type cannot be used in NEW"],
  1168. ["MODULE m; IMPORT test; VAR p: test.TP; BEGIN NEW(p) END m.",
  1169. "non-exported RECORD type cannot be used in NEW"])
  1170. ),
  1171. "imported pointer type cannot be dereferenced if base type is not exported (even if base of base type is exported)": testWithModule(
  1172. "MODULE test;"
  1173. + "TYPE B* = RECORD i: INTEGER END; T = RECORD(B) END; TP* = POINTER TO T;"
  1174. + "TPAnonymous* = POINTER TO RECORD(B) END;"
  1175. + "PROCEDURE makeTP*(): TP; VAR result: TP; BEGIN NEW(result); RETURN result END makeTP;"
  1176. + "PROCEDURE makeTPA*(): TPAnonymous; VAR result: TPAnonymous; BEGIN NEW(result); RETURN result END makeTPA;"
  1177. + "END test.",
  1178. pass(),
  1179. fail(["MODULE m; IMPORT test; VAR p: test.TPAnonymous; BEGIN p := test.makeTPA(); p.i := 123; END m.",
  1180. "POINTER TO non-exported RECORD type cannot be dereferenced"],
  1181. ["MODULE m; IMPORT test; VAR p: test.TP; BEGIN p := test.makeTP(); p.i := 123; END m.",
  1182. "POINTER TO non-exported RECORD type cannot be dereferenced"])
  1183. ),
  1184. "imported pointer type can be used as a base of derived type even if pointer's record type is not exported": testWithModule(
  1185. "MODULE test;"
  1186. + "TYPE B = RECORD END; PB* = POINTER TO B; T* = RECORD(B) END;"
  1187. + "END test.",
  1188. pass("MODULE m; IMPORT test; VAR pb: test.PB; p: POINTER TO test.T; BEGIN pb := p; END m."),
  1189. fail()
  1190. ),
  1191. "imported pointer variable: anonymous record field cannot be used": testWithModule(
  1192. "MODULE test; VAR p*: POINTER TO RECORD i: INTEGER END; END test.",
  1193. pass(),
  1194. fail(["MODULE m; IMPORT test; BEGIN ASSERT(test.p.i = 0) END m.",
  1195. "POINTER TO non-exported RECORD type cannot be dereferenced"])
  1196. ),
  1197. "procedure VAR section": testWithGrammar(
  1198. grammar.declarationSequence,
  1199. pass("VAR",
  1200. "VAR i: INTEGER;",
  1201. "VAR i, j: INTEGER;",
  1202. "VAR i, j: INTEGER; b: BOOLEAN;")
  1203. ),
  1204. "const declaration": testWithContext(
  1205. context(grammar.declarationSequence,
  1206. "CONST ci = 1; VAR v1: INTEGER;"),
  1207. pass("CONST i = 10;",
  1208. "CONST i = 1 + 2;",
  1209. "CONST i = ci + 2;",
  1210. "CONST i = ci * 2;",
  1211. "CONST i = ORD({0..5});",
  1212. "CONST i = ORD({0..5} <= {0..8});",
  1213. "CONST b = TRUE;",
  1214. "CONST b = {0..5} <= {0..8};",
  1215. "CONST c = \"a\";",
  1216. "CONST s = \"abc\";",
  1217. "CONST s0 = \"\";",
  1218. "CONST set = {};",
  1219. "CONST set = {1 + 2};",
  1220. "CONST set = {0..32 - 1};",
  1221. "CONST set = {ci};",
  1222. "CONST i1 = 1; b1 = TRUE;",
  1223. "CONST i1 = 1; i2 = i1 + 1;",
  1224. "CONST i1 = 1; i2 = i1 + 1; i3 = i2 + 2;"),
  1225. fail(["CONST i1 = v1;", "constant expression expected"],
  1226. ["CONST i1 = v1 * 2;", "constant expression expected"],
  1227. ["CONST i1 = v1 - 10;", "constant expression expected"],
  1228. ["CONST i1 = 10 - v1;", "constant expression expected"],
  1229. ["CONST s = {v1};", "constant expression expected"],
  1230. ["CONST s = {1, v1};", "constant expression expected"],
  1231. ["CONST s = {1..v1};", "constant expression expected"],
  1232. ["CONST s = {10 - v1..15};", "constant expression expected"])
  1233. ),
  1234. "POINTER forward declaration": testWithContext(
  1235. context(grammar.module, ""),
  1236. pass("MODULE m; TYPE T = POINTER TO NotDeclaredYet; NotDeclaredYet = RECORD END; END m.",
  1237. "MODULE m; TYPE T1 = POINTER TO NotDeclaredYet; T2 = POINTER TO NotDeclaredYet; NotDeclaredYet = RECORD END; END m."
  1238. ),
  1239. fail(["MODULE m; TYPE T = POINTER TO NotDeclaredYet; END m.",
  1240. "no declaration found for 'NotDeclaredYet'"],
  1241. ["MODULE m; TYPE T1 = POINTER TO NotDeclaredYet1; T2 = POINTER TO NotDeclaredYet2; END m.",
  1242. "no declaration found for 'NotDeclaredYet1', 'NotDeclaredYet2'"],
  1243. ["MODULE m; TYPE T1 = POINTER TO Forward; Forward = PROCEDURE; END m.",
  1244. "'Forward' must be of RECORD type because it was used before in the declation of POINTER"])
  1245. ),
  1246. "typeguard for VAR argument": testWithContext(
  1247. context(grammar.procedureDeclaration,
  1248. "TYPE Base = RECORD END; Derived = RECORD (Base) i: INTEGER END;"
  1249. + "T = RECORD END; TD = RECORD(T) b: Base END;"),
  1250. pass("PROCEDURE proc(VAR p: Base); BEGIN p(Derived).i := 1; END proc"),
  1251. fail(["PROCEDURE proc(p: Base); BEGIN p(Derived).i := 1; END proc",
  1252. "invalid type cast: a value variable cannot be used"],
  1253. ["PROCEDURE proc(p: TD); BEGIN p.b(Derived).i := 1; END proc",
  1254. "invalid type cast: a value variable cannot be used"],
  1255. ["PROCEDURE proc(VAR p: T); BEGIN p(TD).b(Derived).i := 1; END proc",
  1256. "invalid type cast: a value variable cannot be used"])
  1257. ),
  1258. "NEW for read only array element fails": testWithContext(
  1259. context(grammar.procedureDeclaration,
  1260. "TYPE P = POINTER TO RECORD END;"),
  1261. pass(),
  1262. fail(["PROCEDURE readOnlyPointers(a: ARRAY OF P); BEGIN NEW(a[0]) END readOnlyPointers",
  1263. "read-only array's element cannot be passed as VAR actual parameter"])
  1264. ),
  1265. "LEN": testWithGrammar(
  1266. grammar.procedureDeclaration,
  1267. pass("PROCEDURE p(a: ARRAY OF INTEGER): INTEGER; RETURN LEN(a) END p",
  1268. "PROCEDURE p(VAR a: ARRAY OF BOOLEAN): INTEGER; RETURN LEN(a) END p",
  1269. "PROCEDURE p(): INTEGER; RETURN LEN(\"abc\") END p"),
  1270. fail(["PROCEDURE p(a: ARRAY OF INTEGER): INTEGER; RETURN LEN(a[0]) END p",
  1271. "ARRAY or string is expected as an argument of LEN, got 'INTEGER'"])
  1272. ),
  1273. "array expression": testWithGrammar(
  1274. grammar.procedureBody,
  1275. pass("VAR a: ARRAY 10 OF INTEGER; BEGIN a[0] := 1 END",
  1276. "VAR a: ARRAY 10 OF INTEGER; BEGIN a[0] := 1; a[1] := a[0] END",
  1277. "VAR a1, a2: ARRAY 3 OF CHAR; BEGIN ASSERT(a1 = a2); END",
  1278. "VAR a1: ARRAY 2 OF CHAR; a2: ARRAY 3 OF CHAR; BEGIN ASSERT(a1 = a2); END",
  1279. "CONST cs = \"a\"; VAR a: ARRAY 3 OF CHAR; BEGIN ASSERT(a = cs); ASSERT(cs # a); ASSERT(a < cs); ASSERT(cs > a); END",
  1280. "CONST cs = \"a\"; BEGIN ASSERT(cs[0] = \"a\"); END"
  1281. ),
  1282. fail(["VAR a: ARRAY 10 OF INTEGER; BEGIN a[0] := TRUE END",
  1283. "type mismatch: 'INTEGER' cannot be assigned to 'BOOLEAN' expression"],
  1284. ["VAR a: ARRAY 10 OF INTEGER; BEGIN a[TRUE] := 1 END",
  1285. "'INTEGER' or 'BYTE' expression expected, got 'BOOLEAN'"],
  1286. ["VAR a: ARRAY 10 OF INTEGER; p: POINTER TO RECORD END; BEGIN a[p] := 1 END",
  1287. "'INTEGER' or 'BYTE' expression expected, got 'POINTER TO anonymous RECORD'"],
  1288. ["VAR i: INTEGER; BEGIN i[0] := 1 END",
  1289. "ARRAY or string expected, got 'INTEGER'"],
  1290. ["VAR p: POINTER TO RECORD END; BEGIN p[0] := 1 END",
  1291. "ARRAY or string expected, got 'POINTER TO anonymous RECORD'"],
  1292. ["VAR a: ARRAY 10 OF INTEGER; BEGIN a[0][0] := 1 END",
  1293. "ARRAY or string expected, got 'INTEGER'"],
  1294. ["VAR a: ARRAY 10 OF BOOLEAN; BEGIN a[0,0] := TRUE END",
  1295. "ARRAY or string expected, got 'BOOLEAN'"],
  1296. ["VAR a: ARRAY 10, 20 OF BOOLEAN; BEGIN a[0] := TRUE END",
  1297. "type mismatch: 'ARRAY 20 OF BOOLEAN' cannot be assigned to 'BOOLEAN' expression"],
  1298. ["VAR a: ARRAY 10 OF INTEGER; BEGIN a[10] := 0 END",
  1299. "index out of bounds: maximum possible index is 9, got 10"],
  1300. ["CONST c1 = 5; VAR a: ARRAY 10 OF INTEGER; BEGIN a[10 + c1] := 0 END",
  1301. "index out of bounds: maximum possible index is 9, got 15"],
  1302. ["VAR a1, a2: ARRAY 3 OF INTEGER; BEGIN ASSERT(a1 = a2); END",
  1303. "operator '=' type mismatch: numeric type or SET or BOOLEAN or CHAR or character array or POINTER or PROCEDURE expected, got 'ARRAY 3 OF INTEGER'"],
  1304. ["CONST cs = \"\"; BEGIN ASSERT(cs[0] = \"a\"); END",
  1305. "cannot index empty string"],
  1306. ["CONST cs = \"\"; VAR i: INTEGER; BEGIN ASSERT(cs[i] = \"a\"); END",
  1307. "cannot index empty string"],
  1308. ["CONST cs = \"a\"; BEGIN ASSERT(cs[1] = \"a\"); END",
  1309. "index out of bounds: maximum possible index is 0, got 1"],
  1310. ["CONST ci = -1; VAR a: ARRAY 10 OF INTEGER; BEGIN ASSERT(a[ci] = 0); END",
  1311. "index is negative: -1"],
  1312. ["CONST ci = -1; PROCEDURE p(a: ARRAY OF INTEGER); BEGIN ASSERT(a[ci] = 0); END p; END",
  1313. "index is negative: -1"]
  1314. )
  1315. ),
  1316. "multi-dimensional array expression": testWithGrammar(
  1317. grammar.procedureBody,
  1318. pass("VAR a: ARRAY 10 OF ARRAY 5 OF INTEGER; BEGIN a[0][0] := 1 END",
  1319. "VAR a: ARRAY 10, 5 OF BOOLEAN; BEGIN a[0][0] := TRUE END",
  1320. "VAR a: ARRAY 10, 5 OF BOOLEAN; BEGIN a[0, 0] := TRUE END")
  1321. ),
  1322. "selector": testWithContext(
  1323. context(grammar.expression,
  1324. "TYPE T = RECORD field: INTEGER END; VAR r: T; i: INTEGER;"),
  1325. pass("r.field"),
  1326. fail(["i.field",
  1327. "selector '.field' cannot be applied to 'INTEGER'"],
  1328. ["T.field", "selector '.field' cannot be applied to 'type T'"])
  1329. ),
  1330. "procedure body": testWithGrammar(
  1331. grammar.procedureBody,
  1332. pass("END",
  1333. "VAR END",
  1334. "VAR i: INTEGER; END",
  1335. "VAR a: ARRAY 10 OF INTEGER; END",
  1336. "VAR i: INTEGER; BEGIN i := 1 END",
  1337. "VAR b: BOOLEAN; BEGIN b := TRUE END",
  1338. "VAR i, j: INTEGER; BEGIN i := 1; j := 2; i := 1 + i + j - 2 END",
  1339. "TYPE T = RECORD field: INTEGER END; VAR v: T; BEGIN v.field := 1 END",
  1340. "TYPE T1 = RECORD field: INTEGER END; T2 = RECORD field: T1 END; VAR v1: T1; v2: T2; BEGIN v1.field := v2.field.field END",
  1341. "TYPE T1 = RECORD field1: INTEGER END; T2 = RECORD (T1) field2: INTEGER END; VAR v: T2; BEGIN v.field2 := v.field1 END"),
  1342. fail(["VAR i: INTEGER;", "END expected (PROCEDURE)"],
  1343. ["VAR i: INTEGER; i := 1; END", "END expected (PROCEDURE)"],
  1344. ["VAR i: INTEGER; BEGIN j := 1 END", "undeclared identifier: 'j'"],
  1345. ["VAR i: INTEGER; BEGIN i := j END", "undeclared identifier: 'j'"],
  1346. ["TYPE T = RECORD field: INTEGER END; VAR v: T; BEGIN v := 1 END",
  1347. "type mismatch: 'T' cannot be assigned to 'INTEGER' expression"],
  1348. ["TYPE T = RECORD field: INTEGER END; VAR v: T; BEGIN v.unknown := 1 END",
  1349. "type 'T' has no 'unknown' field"],
  1350. ["TYPE T1 = RECORD field1: INTEGER END; T2 = RECORD (T1) field1: INTEGER END; END",
  1351. "base record already has field: 'field1'"])
  1352. ),
  1353. "procedure": testWithContext(
  1354. context(grammar.procedureDeclaration,
  1355. "TYPE ProcType = PROCEDURE(): ProcType;"),
  1356. pass("PROCEDURE p; END p",
  1357. "PROCEDURE p; VAR i: INTEGER; BEGIN i := i + 1 END p",
  1358. "PROCEDURE p(a1, a2: INTEGER); END p",
  1359. "PROCEDURE p; BEGIN p() END p",
  1360. "PROCEDURE p(a: INTEGER); BEGIN p(a) END p",
  1361. "PROCEDURE p(a: INTEGER; b: BOOLEAN); BEGIN p(a, b) END p",
  1362. "PROCEDURE p(): ProcType; RETURN p END p"),
  1363. fail(["PROCEDURE p1; END p2",
  1364. "mismatched procedure names: 'p1' at the begining and 'p2' at the end"],
  1365. ["PROCEDURE p(a: INTEGER); VAR a: INTEGER END p", "'a' already declared"],
  1366. ["PROCEDURE p(a: INTEGER); BEGIN p() END p", "1 argument(s) expected, got 0"],
  1367. ["PROCEDURE p(a: INTEGER); BEGIN p(1, 2) END p", "1 argument(s) expected, got 2"],
  1368. ["PROCEDURE p(a: INTEGER; b: BOOLEAN); BEGIN p(b, a) END p",
  1369. "type mismatch for argument 1: 'BOOLEAN' cannot be converted to 'INTEGER'"],
  1370. ["PROCEDURE p; BEGIN p1() END p", "undeclared identifier: 'p1'"],
  1371. ["PROCEDURE p(a1: INTEGER; a1: BOOLEAN)", "'a1' already declared"],
  1372. ["PROCEDURE p(p: INTEGER)", "argument 'p' has the same name as procedure"]
  1373. )
  1374. ),
  1375. "procedure RETURN": testWithContext(
  1376. context(
  1377. grammar.procedureDeclaration,
  1378. "TYPE A = ARRAY 3 OF INTEGER; R = RECORD END; PR = POINTER TO R;"
  1379. + "VAR i: INTEGER; PROCEDURE int(): INTEGER; RETURN 1 END int;"),
  1380. pass("PROCEDURE p(): BOOLEAN; RETURN TRUE END p",
  1381. "PROCEDURE p(): BOOLEAN; RETURN int() = 1 END p",
  1382. "PROCEDURE p; BEGIN END p" ,
  1383. "PROCEDURE p(): INTEGER; BEGIN RETURN 0 END p"),
  1384. fail(["PROCEDURE p; RETURN TRUE END p", "unexpected RETURN in PROCEDURE declared with no result type"],
  1385. ["PROCEDURE p(): BOOLEAN; END p", "RETURN expected at the end of PROCEDURE declared with 'BOOLEAN' result type"],
  1386. ["PROCEDURE p(): undeclared; END p", "undeclared identifier: 'undeclared'"],
  1387. ["PROCEDURE p(): i; END p", "type name expected"],
  1388. ["PROCEDURE p(): INTEGER; RETURN TRUE END p", "RETURN 'INTEGER' expected, got 'BOOLEAN'"],
  1389. ["PROCEDURE p(a: A): A; RETURN a END p", "procedure cannot return ARRAY 3 OF INTEGER"],
  1390. ["PROCEDURE p(): A; VAR a: A; RETURN a END p", "procedure cannot return ARRAY 3 OF INTEGER"],
  1391. ["PROCEDURE p(r: R): R; RETURN r END p", "procedure cannot return R"],
  1392. ["PROCEDURE p(): R; VAR r: R; RETURN r END p", "procedure cannot return R"],
  1393. ["PROCEDURE p(pr: PR): R; RETURN pr END p", "procedure cannot return R"]
  1394. )
  1395. ),
  1396. "pass VAR argument as VAR parameter": testWithContext(
  1397. context(grammar.procedureDeclaration,
  1398. "PROCEDURE p1(VAR i: INTEGER); END p1;"
  1399. + "PROCEDURE p2(VAR b: BOOLEAN); END p2;"),
  1400. pass("PROCEDURE p(VAR i1: INTEGER); BEGIN p1(i1) END p"),
  1401. fail(["PROCEDURE p(VAR b: BOOLEAN); BEGIN p2(~b) END p", "expression cannot be used as VAR parameter"])
  1402. ),
  1403. "ARRAY parameter": testWithContext(
  1404. context(grammar.procedureDeclaration,
  1405. "TYPE T = RECORD i: INTEGER; p: POINTER TO T END;"
  1406. + "PROCEDURE p1(i: INTEGER); END p1;"
  1407. + "PROCEDURE varInteger(VAR i: INTEGER); END varInteger;"
  1408. + "PROCEDURE p2(a: ARRAY OF INTEGER); END p2;"
  1409. + "PROCEDURE p3(VAR a: ARRAY OF INTEGER); END p3;"
  1410. ),
  1411. pass("PROCEDURE p(a: ARRAY OF INTEGER); END p",
  1412. "PROCEDURE p(a: ARRAY OF ARRAY OF INTEGER); END p",
  1413. "PROCEDURE p(a: ARRAY OF ARRAY OF INTEGER); BEGIN p1(a[0][0]) END p",
  1414. "PROCEDURE p(a: ARRAY OF INTEGER); BEGIN p2(a) END p",
  1415. "PROCEDURE p(a: ARRAY OF T); BEGIN varInteger(a[0].p.i) END p"),
  1416. fail(["PROCEDURE p(a: ARRAY OF INTEGER); BEGIN a[0] := 0 END p",
  1417. "cannot assign to read-only array's element"],
  1418. ["PROCEDURE p(a: ARRAY OF T); BEGIN a[0].i := 0 END p",
  1419. "cannot assign to read-only record's field"],
  1420. ["PROCEDURE p(a: ARRAY OF T); BEGIN varInteger(a[0].i) END p",
  1421. "read-only record's field cannot be passed as VAR actual parameter"])
  1422. ),
  1423. "RECORD parameter": testWithContext(
  1424. context(grammar.procedureDeclaration,
  1425. "TYPE T = RECORD i: INTEGER; p: POINTER TO T END;"
  1426. + "PROCEDURE intValue(i: INTEGER); END intValue;"
  1427. + "PROCEDURE intVar(VAR i: INTEGER); END intVar;"
  1428. + "PROCEDURE recordValue(r: T); END recordValue;"
  1429. + "PROCEDURE recordVar(VAR r: T); END recordVar;"
  1430. ),
  1431. pass("PROCEDURE p(VAR r: T); BEGIN r.i := 0; intVar(r.i); END p",
  1432. "PROCEDURE p(VAR r: T); BEGIN recordValue(r); recordVar(r); END p",
  1433. "PROCEDURE p(r: T); BEGIN intValue(r.i); recordValue(r); END p"
  1434. ),
  1435. fail(["PROCEDURE p(r: T); BEGIN r.i := 0 END p",
  1436. "cannot assign to read-only record's field"],
  1437. ["PROCEDURE p(r: T); BEGIN intVar(r.i); END p",
  1438. "read-only record's field cannot be passed as VAR actual parameter"]
  1439. )
  1440. ),
  1441. "local procedure": testWithContext(
  1442. context(grammar.procedureDeclaration,
  1443. "TYPE ProcType = PROCEDURE;" +
  1444. "VAR procVar: ProcType;" +
  1445. "PROCEDURE procWithProcArg(p: ProcType); END procWithProcArg;"),
  1446. pass("PROCEDURE p; PROCEDURE innerP; END innerP; END p",
  1447. "PROCEDURE p; PROCEDURE innerP; END innerP; BEGIN innerP() END p"),
  1448. fail(["PROCEDURE p; PROCEDURE innerP; END innerP; BEGIN procVar := innerP END p",
  1449. "local procedure 'innerP' cannot be referenced"],
  1450. ["PROCEDURE p; PROCEDURE innerP; END innerP; BEGIN procWithProcArg(innerP) END p",
  1451. "local procedure 'innerP' cannot be referenced"],
  1452. ["PROCEDURE p; PROCEDURE innerP; VAR innerV: INTEGER; END innerP; BEGIN innerV := 0 END p",
  1453. "undeclared identifier: 'innerV'"])
  1454. ),
  1455. "open array assignment fails": testWithGrammar(
  1456. grammar.procedureDeclaration,
  1457. pass(),
  1458. fail(["PROCEDURE p(VAR s1, s2: ARRAY OF CHAR); BEGIN s1 := s2 END p",
  1459. "open 'ARRAY OF CHAR' cannot be assigned"],
  1460. ["PROCEDURE p(s1: ARRAY OF CHAR); VAR s2: ARRAY 10 OF CHAR; BEGIN s2 := s1 END p",
  1461. "type mismatch: 'ARRAY 10 OF CHAR' cannot be assigned to 'ARRAY OF CHAR' expression"])
  1462. ),
  1463. "open array type as procedure parameter": testWithContext(
  1464. context(grammar.procedureDeclaration,
  1465. "TYPE A = ARRAY 3 OF INTEGER;"
  1466. ),
  1467. pass("PROCEDURE p(a: ARRAY OF INTEGER); BEGIN END p",
  1468. "PROCEDURE p(a: ARRAY OF ARRAY OF INTEGER); BEGIN END p",
  1469. "PROCEDURE p(a: ARRAY OF A); BEGIN END p"
  1470. ),
  1471. fail(["PROCEDURE p(a: ARRAY OF ARRAY 3 OF INTEGER); BEGIN END p",
  1472. "')' expected"]
  1473. )
  1474. ),
  1475. "non-open array type as procedure parameter": testWithContext(
  1476. context(grammar.procedureDeclaration,
  1477. "TYPE A = ARRAY 2 OF INTEGER;"
  1478. + "VAR a: A;"
  1479. + "PROCEDURE pa(a: A); BEGIN END pa;"
  1480. ),
  1481. pass("PROCEDURE p(a: A); BEGIN END p",
  1482. "PROCEDURE p(); VAR a: A; BEGIN pa(a) END p",
  1483. "PROCEDURE p(); VAR a: ARRAY 2 OF INTEGER; BEGIN pa(a) END p"
  1484. ),
  1485. fail(["PROCEDURE p(a: ARRAY 3 OF INTEGER); BEGIN END p",
  1486. "')' expected"],
  1487. ["PROCEDURE p(a: A): INTEGER; BEGIN RETURN a[2] END p",
  1488. "index out of bounds: maximum possible index is 1, got 2"],
  1489. ["PROCEDURE p(); VAR a: ARRAY 1 OF INTEGER; BEGIN pa(a) END p",
  1490. "type mismatch for argument 1: 'ARRAY 1 OF INTEGER' cannot be converted to 'ARRAY 2 OF INTEGER'"],
  1491. ["PROCEDURE p(a: ARRAY OF INTEGER); BEGIN pa(a) END p",
  1492. "type mismatch for argument 1: 'ARRAY OF INTEGER' cannot be converted to 'ARRAY 2 OF INTEGER'"]
  1493. )
  1494. ),
  1495. "string assignment to open array fails": testWithGrammar(
  1496. grammar.procedureDeclaration,
  1497. pass(),
  1498. fail(["PROCEDURE p(VAR s: ARRAY OF CHAR); BEGIN s := \"abc\" END p", "string cannot be assigned to open ARRAY OF CHAR"])
  1499. ),
  1500. "scope": testWithGrammar(
  1501. grammar.declarationSequence,
  1502. pass("PROCEDURE p1(a1: INTEGER); END p1; PROCEDURE p2(a1: BOOLEAN); END p2;")
  1503. ),
  1504. "module": testWithGrammar(
  1505. grammar.module,
  1506. pass("MODULE m; END m."),
  1507. fail(["MODULE m; END undeclared.",
  1508. "original module name 'm' expected, got 'undeclared'"],
  1509. ["MODULE m; BEGIN - END m.", "END expected (MODULE)"])
  1510. ),
  1511. "export": testWithGrammar(
  1512. grammar.declarationSequence,
  1513. pass("CONST i* = 1;",
  1514. "TYPE T* = RECORD END;",
  1515. "VAR i*: INTEGER;",
  1516. "PROCEDURE p*; END p;"
  1517. ),
  1518. fail(["TYPE T = RECORD f*: INTEGER END;",
  1519. "field 'f' can be exported only if record 'T' itself is exported too"],
  1520. ["TYPE PT* = POINTER TO RECORD f*: INTEGER END;",
  1521. "cannot export anonymous RECORD field: 'f'"],
  1522. ["VAR p: POINTER TO RECORD f*: INTEGER END;",
  1523. "cannot export anonymous RECORD field: 'f'"],
  1524. ["VAR p*: POINTER TO RECORD r: RECORD f*: INTEGER END END;",
  1525. "field 'f' can be exported only if field 'r' itself is exported too"],
  1526. ["VAR i*: POINTER TO RECORD f*: INTEGER END;",
  1527. "cannot export anonymous RECORD field: 'f'"],
  1528. ["VAR i*: POINTER TO RECORD r*: RECORD f*: INTEGER END END;",
  1529. "cannot export anonymous RECORD field: 'r'"],
  1530. ["PROCEDURE p*; VAR i*: INTEGER; END p;",
  1531. "cannot export from within procedure: variable 'i'"]
  1532. //["TYPE PT = POINTER TO RECORD END; PROCEDURE p*(): PT; RETURN NIL END p;",
  1533. //"exported PROCEDURE 'p' uses non-exported type 'PT'"]
  1534. )
  1535. ),
  1536. "import JS": testWithGrammar(
  1537. grammar.module,
  1538. pass("MODULE m; IMPORT JS; END m.",
  1539. "MODULE m; IMPORT JS; BEGIN JS.alert(\"test\") END m.",
  1540. "MODULE m; IMPORT JS; BEGIN JS.console.info(123) END m.",
  1541. "MODULE m; IMPORT JS; BEGIN JS.do(\"throw new Error()\") END m."
  1542. ),
  1543. fail(["MODULE m; IMPORT JS; BEGIN JS.do(123) END m.",
  1544. "string is expected as an argument of JS predefined procedure 'do', got INTEGER"],
  1545. ["MODULE m; IMPORT JS; BEGIN JS.do(\"a\", \"b\") END m.",
  1546. "1 argument(s) expected, got 2"],
  1547. ["MODULE m; IMPORT JS; VAR s: ARRAY 10 OF CHAR; BEGIN JS.do(s) END m.",
  1548. "string is expected as an argument of JS predefined procedure 'do', got ARRAY 10 OF CHAR"]
  1549. )
  1550. ),
  1551. "JS.var": testWithGrammar(
  1552. grammar.module,
  1553. pass("MODULE m; IMPORT JS; VAR v: JS.var; END m.",
  1554. "MODULE m; IMPORT JS; VAR v: JS.var; BEGIN v := JS.f(); END m.",
  1555. "MODULE m; IMPORT JS; VAR v: JS.var; BEGIN v := JS.f1(); JS.f2(v); END m.",
  1556. "MODULE m; IMPORT JS; VAR v: JS.var; i: INTEGER; BEGIN v := i; END m."
  1557. ),
  1558. fail(["MODULE m; IMPORT JS; VAR v: JS.var; i: INTEGER; BEGIN i := v; END m.",
  1559. "type mismatch: 'INTEGER' cannot be assigned to 'JS.var' expression"])
  1560. ),
  1561. "import unknown module": testWithGrammar(
  1562. grammar.module,
  1563. pass(),
  1564. fail(["MODULE m; IMPORT unknown; END m.", "module not found: unknown"],
  1565. ["MODULE m; IMPORT unknown1, unknown2; END m.", "modules not found: unknown1, unknown2"]
  1566. )
  1567. ),
  1568. "self import is failed": testWithGrammar(
  1569. grammar.module,
  1570. pass(),
  1571. fail(["MODULE test; IMPORT test; END test.", "module 'test' cannot import itself"])
  1572. ),
  1573. "import aliases": testWithGrammar(
  1574. grammar.module,
  1575. pass("MODULE m; IMPORT J := JS; END m.",
  1576. "MODULE m; IMPORT J := JS; BEGIN J.alert(\"test\") END m."),
  1577. fail(["MODULE m; IMPORT u1 := unknown1, unknown2; END m.", "modules not found: unknown1, unknown2"],
  1578. ["MODULE m; IMPORT a1 := m1, a2 := m1; END m.", "module already imported: 'm1'"],
  1579. ["MODULE m; IMPORT a1 := u1, a1 := u2; END m.", "duplicated alias: 'a1'"],
  1580. ["MODULE m; IMPORT J := JS; BEGIN JS.alert(\"test\") END m.", "undeclared identifier: 'JS'"]
  1581. )
  1582. ),
  1583. "variables exported as read-only": testWithModule(
  1584. "MODULE test; TYPE T* = RECORD i*: INTEGER END; VAR r*: T; p*: POINTER TO T; i*: INTEGER; END test.",
  1585. pass("MODULE m; IMPORT test; BEGIN ASSERT(test.r.i = 0); END m.",
  1586. "MODULE m; IMPORT test; BEGIN ASSERT(test.i = 0); END m.",
  1587. "MODULE m; IMPORT test; BEGIN test.p.i := 0; END m."
  1588. ),
  1589. fail(["MODULE m; IMPORT test; BEGIN test.i := 0; END m.", "cannot assign to imported variable"],
  1590. ["MODULE m; IMPORT test; BEGIN test.r.i := 0; END m.", "cannot assign to read-only record's field"]
  1591. )),
  1592. "syntax errors": testWithGrammar(
  1593. grammar.module,
  1594. pass(),
  1595. fail(["MODULE m; CONST c = 1 END m.",
  1596. "';' expected"],
  1597. ["MODULE m; TYPE T = RECORD END END m.",
  1598. "';' expected"],
  1599. ["MODULE m; VAR v: INTEGER END m.",
  1600. "';' expected"],
  1601. ["MODULE m; PROCEDURE p(INTEGER) END m.",
  1602. "')' expected"])
  1603. )
  1604. };
  1605. }
  1606. function run(){
  1607. return Test.run({
  1608. "common": {
  1609. "oberon": makeSuiteForGrammar(oberon),
  1610. "eberon": makeSuiteForGrammar(eberon)
  1611. },
  1612. "eberon": TestUnitEberon.suite,
  1613. "oberon": TestUnitOberon.suite
  1614. });
  1615. }
  1616. exports.run = run;