test_unit_eberon.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. "use strict";
  2. var language = require("eberon/eberon_grammar.js").language;
  3. var TestUnitCommon = require("test_unit_common.js");
  4. var pass = TestUnitCommon.pass;
  5. var fail = TestUnitCommon.fail;
  6. var context = TestUnitCommon.context;
  7. var grammar = language.grammar;
  8. function testWithContext(context, pass, fail){
  9. return TestUnitCommon.testWithContext(context, grammar.declarationSequence, language, pass, fail);
  10. }
  11. function testWithModule(src, pass, fail){
  12. return TestUnitCommon.testWithModule(src, language, pass, fail);
  13. }
  14. function testWithGrammar(parser, pass, faile){
  15. return TestUnitCommon.testWithGrammar(parser, language, pass, fail);
  16. }
  17. exports.suite = {
  18. "arithmetic operators": testWithContext(
  19. context(grammar.statement, "VAR b1: BOOLEAN;"),
  20. pass(),
  21. fail(["b1 := b1 + b1", "operator '+' type mismatch: numeric type or SET or STRING expected, got 'BOOLEAN'"])
  22. ),
  23. "key words": testWithGrammar(
  24. grammar.variableDeclaration,
  25. pass(),
  26. fail(["SELF: INTEGER", "not parsed"],
  27. ["SUPER: INTEGER", "not parsed"],
  28. ["STRING: INTEGER", "not parsed"]
  29. )
  30. ),
  31. "abstract method declaration": testWithContext(
  32. context(grammar.declarationSequence,
  33. "TYPE T = RECORD PROCEDURE p() END;"
  34. + "D = RECORD(T) END;"
  35. + "T2 = RECORD PROCEDURE p1(); PROCEDURE p2(i: INTEGER): BOOLEAN END;"
  36. ),
  37. pass(),
  38. fail(["VAR r: T;",
  39. "cannot instantiate 'T' because it has abstract method(s): p"],
  40. ["VAR r: T2;",
  41. "cannot instantiate 'T2' because it has abstract method(s): p1, p2"],
  42. ["PROCEDURE p(); VAR p: POINTER TO T; BEGIN NEW(p); END p;",
  43. "cannot instantiate 'T' because it has abstract method(s): p"],
  44. ["PROCEDURE p(); TYPE LocalT = RECORD(T) END; VAR r: LocalT; END p;",
  45. "cannot instantiate 'LocalT' because it has abstract method(s): p"],
  46. ["PROCEDURE p(); TYPE LocalT = RECORD(T) END; VAR p: POINTER TO LocalT; BEGIN NEW(p) END p;",
  47. "cannot instantiate 'LocalT' because it has abstract method(s): p"],
  48. ["VAR r: D;",
  49. "cannot instantiate 'D' because it has abstract method(s): p"],
  50. ["PROCEDURE p(); VAR p: POINTER TO D; BEGIN NEW(p); END p;",
  51. "cannot instantiate 'D' because it has abstract method(s): p"]
  52. )
  53. ),
  54. "new method declaration": testWithContext(
  55. context(grammar.declarationSequence,
  56. "TYPE T = RECORD PROCEDURE p(); intField: INTEGER END; A = ARRAY 1 OF INTEGER;"),
  57. pass("PROCEDURE T.p(); END T.p;"
  58. ),
  59. fail(["PROCEDURE TUnk.p(), NEW; END TUnk.p;", "undeclared identifier: 'TUnk'"],
  60. ["PROCEDURE A.p(), NEW; END A.p;",
  61. "RECORD type expected in method declaration, got 'ARRAY 1 OF INTEGER'"],
  62. ["PROCEDURE T.p(), NEW; END;", "not parsed"],
  63. ["PROCEDURE T.p(); END p;",
  64. "mismatched procedure names: 'T.p' at the begining and 'p.' at the end"],
  65. ["PROCEDURE T.p(); END T2.p;",
  66. "mismatched procedure names: 'T.p' at the begining and 'T2.p' at the end"],
  67. ["PROCEDURE T.p(); END T.p2;",
  68. "mismatched procedure names: 'T.p' at the begining and 'T.p2' at the end"],
  69. ["PROCEDURE T.intField(); END T.intField;",
  70. "'T' has no declaration for method 'intField'"],
  71. ["PROCEDURE T.p(); END T.p; PROCEDURE T.p(), NEW; END T.p;",
  72. "'T.p' already declared"],
  73. ["PROCEDURE p(); TYPE T = RECORD PROCEDURE m(); PROCEDURE m() END; END p;",
  74. "cannot declare a new method 'm': method already was declared"],
  75. ["PROCEDURE p(); TYPE T = RECORD m: INTEGER; PROCEDURE m() END; END p;",
  76. "cannot declare method, record already has field 'm'"],
  77. ["PROCEDURE p(); TYPE T = RECORD PROCEDURE m(); m: INTEGER END; END p;",
  78. "cannot declare field, record already has method 'm'"]
  79. )
  80. ),
  81. "overridden method declaration": testWithContext(
  82. context(grammar.declarationSequence,
  83. "TYPE Base = RECORD PROCEDURE p() END; T = RECORD (Base) END;"
  84. + "PROCEDURE Base.p(); END Base.p;"),
  85. pass("PROCEDURE T.p(); END T.p;"),
  86. fail(["PROCEDURE T.pUnk(); END T.pUnk;",
  87. "'T' has no declaration for method 'pUnk'"],
  88. ["PROCEDURE proc(); TYPE T = RECORD (Base) PROCEDURE p() END; END proc;",
  89. "cannot declare a new method 'p': method already was declared"],
  90. ["PROCEDURE T.p(); END T.p; PROCEDURE T.p(); END T.p;",
  91. "'T.p' already declared"],
  92. ["PROCEDURE T.p(a: INTEGER); END T.p;",
  93. "overridden method 'p' signature mismatch: should be 'PROCEDURE', got 'PROCEDURE(INTEGER)'"],
  94. ["PROCEDURE p(); PROCEDURE T.p(); END T.p; END p;",
  95. "method should be defined in the same scope as its bound type 'T'"]
  96. )
  97. ),
  98. "SELF": testWithContext(
  99. context(grammar.declarationSequence,
  100. "TYPE T = RECORD PROCEDURE p(); i: INTEGER END;"
  101. + "PROCEDURE proc(i: INTEGER); END proc;"),
  102. pass("PROCEDURE T.p(); BEGIN SELF.i := 0; END T.p;",
  103. "PROCEDURE T.p(); BEGIN proc(SELF.i); END T.p;"
  104. ),
  105. fail(["PROCEDURE p(); BEGIN SELF.i := 0; END p;",
  106. "SELF can be used only in methods"])
  107. ),
  108. "method call": testWithContext(
  109. context(grammar.expression,
  110. "TYPE T = RECORD PROCEDURE p(); PROCEDURE f(): INTEGER END;"
  111. + "VAR o: T;"
  112. + "PROCEDURE T.p(); END T.p;"
  113. + "PROCEDURE T.f(): INTEGER; RETURN 0 END T.f;"
  114. ),
  115. pass("o.f()"),
  116. fail(["o.p()", "procedure returning no result cannot be used in an expression"])
  117. ),
  118. "cannot assign to method": testWithContext(
  119. context(grammar.statement,
  120. "TYPE T = RECORD PROCEDURE p() END;"
  121. + "VAR o: T;"
  122. + "PROCEDURE T.p(); END T.p;"
  123. ),
  124. pass(),
  125. fail(["o.p := o.p", "cannot assign to method"],
  126. ["o.p := NIL", "cannot assign to method"])
  127. ),
  128. "method cannot be referenced": testWithContext(
  129. context(grammar.statement,
  130. "TYPE T = RECORD PROCEDURE p() END;"
  131. + "Proc = PROCEDURE();"
  132. + "VAR o: T;"
  133. + "PROCEDURE T.p(); END T.p;"
  134. + "PROCEDURE proc(p: Proc); END proc;"
  135. ),
  136. pass(),
  137. fail(["proc(o.p)", "type mismatch for argument 1: 'method p' cannot be converted to 'Proc'"])
  138. ),
  139. "method super call": testWithContext(
  140. context(grammar.declarationSequence,
  141. "TYPE T = RECORD PROCEDURE p(); PROCEDURE pAbstract(); PROCEDURE pAbstract2() END;"
  142. + "D = RECORD(T) PROCEDURE pNoSuper() END;"
  143. + "PROCEDURE T.p(); END T.p;"
  144. ),
  145. pass("PROCEDURE D.p(); BEGIN SUPER() END D.p;"),
  146. fail(["PROCEDURE D.pNoSuper(); BEGIN SUPER() END D.pNoSuper;",
  147. "there is no method 'pNoSuper' in base type(s)"],
  148. ["PROCEDURE p(); BEGIN SUPER() END p;",
  149. "SUPER can be used only in methods"],
  150. ["PROCEDURE T.pNoBase(); BEGIN SUPER() END T.pNoBase;",
  151. "'T' has no base type - SUPER cannot be used"],
  152. ["PROCEDURE D.pAbstract(); BEGIN SUPER() END D.pAbstract;",
  153. "cannot use abstract method(s) in SUPER calls: pAbstract"],
  154. ["PROCEDURE D.pAbstract(); BEGIN SUPER() END D.pAbstract; PROCEDURE D.pAbstract2(); BEGIN SUPER() END D.pAbstract2;",
  155. "cannot use abstract method(s) in SUPER calls: pAbstract, pAbstract2"]
  156. )
  157. ),
  158. "export method": testWithContext(
  159. context(grammar.declarationSequence,
  160. "TYPE T = RECORD PROCEDURE p() END;"
  161. ),
  162. pass(),
  163. fail(["PROCEDURE T.p*(); END T.p;",
  164. "method implementation cannot be exported: p"])
  165. ),
  166. "import method": testWithModule(
  167. "MODULE test;"
  168. + "TYPE T* = RECORD PROCEDURE m*(); PROCEDURE mNotExported() END;"
  169. + "PROCEDURE T.m(); END T.m; PROCEDURE T.mNotExported(); END T.mNotExported;"
  170. + "END test.",
  171. pass("MODULE m; IMPORT test; VAR r: test.T; BEGIN r.m(); END m.",
  172. "MODULE m; IMPORT test; TYPE T = RECORD(test.T) END; PROCEDURE T.m(); END T.m; END m."
  173. ),
  174. fail(["MODULE m; IMPORT test; VAR r: test.T; BEGIN r.mNotExported(); END m.",
  175. "type 'T' has no 'mNotExported' field"],
  176. ["MODULE m; IMPORT test; TYPE T = RECORD(test.T) END; PROCEDURE T.mNotExported(); END T.mNotExported; END m.",
  177. "'T' has no declaration for method 'mNotExported'"])
  178. ),
  179. "non-scalar variables can be exported": testWithContext(
  180. context(grammar.declarationSequence,
  181. "TYPE T = RECORD END; A = ARRAY 3 OF INTEGER;"
  182. ),
  183. pass("VAR r*: T;",
  184. "VAR a*: A;"),
  185. fail()
  186. ),
  187. "export as read-only": testWithContext(
  188. context(grammar.declarationSequence, ""),
  189. pass("TYPE T* = RECORD i-: INTEGER END;"),
  190. fail(["TYPE T- = RECORD END;",
  191. "type cannot be exported as read-only using '-' mark (did you mean '*'?)"],
  192. ["PROCEDURE p-(); END p;",
  193. "procedure cannot be exported as read-only using '-' mark (did you mean '*'?)"],
  194. ["CONST c- = 123;",
  195. "constant cannot be exported as read-only using '-' mark (did you mean '*'?)"],
  196. ["VAR i-: INTEGER;",
  197. "variable cannot be exported as read-only using '-' mark (did you mean '*'?)"],
  198. ["TYPE T* = RECORD PROCEDURE p-() END;",
  199. "method cannot be exported as read-only using '-' mark (did you mean '*'?)"]
  200. )
  201. ),
  202. "field exported as read-only is writable in current module": testWithContext(
  203. context(grammar.statement,
  204. "TYPE T* = RECORD i-: INTEGER END;"
  205. + "VAR r: T;"
  206. ),
  207. pass("r.i := 123"),
  208. fail()
  209. ),
  210. "import as read-only": testWithModule(
  211. "MODULE test; TYPE T* = RECORD f-: INTEGER END; END test.",
  212. pass(),
  213. fail(["MODULE m; IMPORT test; VAR r: test.T; BEGIN r.f := 123; END m.",
  214. "cannot assign to read-only variable"],
  215. ["MODULE m; IMPORT test; TYPE D = RECORD(test.T) END; VAR r: D; BEGIN r.f := 123; END m.",
  216. "cannot assign to read-only variable"]
  217. )),
  218. "STRING variable": testWithGrammar(
  219. grammar.variableDeclaration,
  220. pass("s: STRING")
  221. ),
  222. "STRING expression": testWithContext(
  223. context(grammar.expression,
  224. "VAR s1, s2: STRING; a: ARRAY 10 OF CHAR;"),
  225. pass("s1 + s2",
  226. "s1 + \"abc\"",
  227. "\"abc\" + s1",
  228. "s1 = s2",
  229. "s1 # s2",
  230. "s1 < s2",
  231. "s1 > s2",
  232. "s1 <= s2",
  233. "s1 >= s2"
  234. ),
  235. fail(["s1 = NIL", "type mismatch: expected 'STRING', got 'NIL'"],
  236. ["s1 = a", "type mismatch: expected 'STRING', got 'ARRAY 10 OF CHAR'"],
  237. ["a = s1", "type mismatch: expected 'ARRAY 10 OF CHAR', got 'STRING'"]
  238. )
  239. ),
  240. "STRING literal expression": testWithContext(
  241. context(grammar.expression,
  242. "CONST cs = \"abc\";"
  243. + "PROCEDURE pString(s: STRING): STRING; RETURN s END pString;"
  244. + "PROCEDURE pStringByRef(VAR s: STRING): STRING; RETURN s END pStringByRef;"
  245. ),
  246. pass("\"abc\" + \"cde\"",
  247. "cs + cs",
  248. "cs + \"abc\"",
  249. "cs = \"abc\"",
  250. "cs # \"abc\"",
  251. "cs < \"abc\"",
  252. "cs > \"abc\"",
  253. "cs <= \"abc\"",
  254. "cs >= \"abc\"",
  255. "pString(cs)",
  256. "pString(\"abc\")"
  257. ),
  258. fail(["pStringByRef(cs)", "type mismatch for argument 1: cannot pass 'multi-character string' as VAR parameter of type 'STRING'"],
  259. ["pStringByRef(\"abc\")", "type mismatch for argument 1: cannot pass 'multi-character string' as VAR parameter of type 'STRING'"]
  260. )
  261. ),
  262. "STRING assignment": testWithContext(
  263. context(grammar.statement,
  264. "VAR s1, s2: STRING; a: ARRAY 10 OF CHAR;"),
  265. pass("s1 := s2",
  266. "s1 := \"abc\"",
  267. "s1 := 22X"
  268. ),
  269. fail(["a := s1", "type mismatch: 'a' is 'ARRAY 10 OF CHAR' and cannot be assigned to 'STRING' expression"],
  270. ["s1 := a", "type mismatch: 's1' is 'STRING' and cannot be assigned to 'ARRAY 10 OF CHAR' expression"]
  271. )
  272. ),
  273. "STRING and ARRAY OF CHAR": testWithContext(
  274. context(grammar.expression,
  275. "VAR s: STRING; a: ARRAY 10 OF CHAR;"
  276. + "PROCEDURE pArray(a: ARRAY OF CHAR): BOOLEAN; RETURN FALSE END pArray;"
  277. + "PROCEDURE pString(s: STRING): BOOLEAN; RETURN FALSE END pString;"
  278. + "PROCEDURE pVar(VAR a: ARRAY OF CHAR): BOOLEAN; RETURN FALSE END pVar;"
  279. + "PROCEDURE pIntArray(a: ARRAY OF INTEGER): BOOLEAN; RETURN FALSE END pIntArray;"
  280. ),
  281. pass("pArray(s)"),
  282. fail(["pVar(s)", "type mismatch for argument 1: cannot pass 'STRING' as VAR parameter of type 'ARRAY OF CHAR'"],
  283. ["pString(a)", "type mismatch for argument 1: 'ARRAY 10 OF CHAR' cannot be converted to 'STRING'"],
  284. ["pIntArray(s)", "type mismatch for argument 1: 'STRING' cannot be converted to 'ARRAY OF INTEGER'"]
  285. )
  286. ),
  287. "STRING LEN": testWithContext(
  288. context(grammar.expression,
  289. "VAR s: STRING;"),
  290. pass("LEN(s)"),
  291. fail()
  292. ),
  293. "STRING indexing": testWithContext(
  294. context(grammar.expression,
  295. "VAR s: STRING;"
  296. + "PROCEDURE pCharByVar(VAR c: CHAR): CHAR; RETURN c END pCharByVar;"),
  297. pass("s[0]"),
  298. fail(["s[-1]", "index is negative: -1"],
  299. ["pCharByVar(s[0])", "string element cannot be used as VAR parameter"]
  300. )
  301. ),
  302. "designate call result in expression": testWithContext(
  303. context(grammar.expression,
  304. "TYPE PT = POINTER TO RECORD field: INTEGER END;"
  305. + "VAR p: PT;"
  306. + "PROCEDURE proc(): PT; RETURN p END proc;"
  307. + "PROCEDURE int(): INTEGER; RETURN 0 END int;"
  308. + "PROCEDURE intVar(VAR i: INTEGER): INTEGER; RETURN i END intVar;"),
  309. pass("proc().field",
  310. "intVar(proc().field)"),
  311. fail(["intVar(int())", "expression cannot be used as VAR parameter"])
  312. ),
  313. "designate call result in statement": testWithContext(
  314. context(grammar.statement,
  315. "TYPE PT = POINTER TO RECORD field: INTEGER; proc: PROCEDURE END;"
  316. + "ProcType = PROCEDURE;"
  317. + "VAR p: PT;"
  318. + "PROCEDURE procVoid(); END procVoid;"
  319. + "PROCEDURE proc(): PT; RETURN p END proc;"
  320. + "PROCEDURE int(): INTEGER; RETURN 0 END int;"
  321. + "PROCEDURE intVar(VAR i: INTEGER); END intVar;"
  322. + "PROCEDURE returnProc(): ProcType; RETURN procVoid END returnProc;"
  323. ),
  324. pass("proc().field := 0",
  325. "proc().proc()",
  326. "proc().proc"
  327. ),
  328. fail(["int() := 0", "cannot assign to procedure call result"],
  329. ["intVar(int())", "expression cannot be used as VAR parameter"],
  330. ["procVoid()()", "PROCEDURE expected, got 'procedure call statement'"],
  331. ["int()()", "PROCEDURE expected, got 'INTEGER'"],
  332. ["returnProc()", "procedure returning a result cannot be used as a statement"] // call is not applied implicitly to result
  333. )
  334. ),
  335. "temporary values": {
  336. "initialization": testWithContext(
  337. context(grammar.statement,
  338. "VAR i: INTEGER;"
  339. + "PROCEDURE p(): BOOLEAN; RETURN FALSE END p;"
  340. + "PROCEDURE void(); END void;"
  341. ),
  342. pass("v <- 0",
  343. "v <- 1.23",
  344. "v <- \"abc\"",
  345. "v <- TRUE",
  346. "v <- i",
  347. "v <- i + i",
  348. "v <- p()",
  349. "v <- void" // procedure type
  350. ),
  351. fail(["v <-", "initialization expression expected"],
  352. ["v <- void()", "procedure returning no result cannot be used in an expression"])
  353. ),
  354. "scope": testWithContext(
  355. context(grammar.declarationSequence,
  356. "VAR i: INTEGER;"),
  357. pass("PROCEDURE p(); BEGIN v1 <- 0; v2 <-0; END p;",
  358. "PROCEDURE p(); BEGIN i <- 0; END p;",
  359. "PROCEDURE p(); BEGIN WHILE FALSE DO v <- 0; END; WHILE FALSE DO v <- 0; END; END p;"
  360. ),
  361. fail(["PROCEDURE p(); BEGIN v <- 0; v <-0; END p;", "'v' already declared"],
  362. ["PROCEDURE p(); VAR v: INTEGER; BEGIN v <- 0; END p;", "'v' already declared"],
  363. ["PROCEDURE p(); BEGIN v <- 0; WHILE FALSE DO v <- 0; END; END p;", "'v' already declared"]
  364. )
  365. ),
  366. "read-only": testWithContext(
  367. context(grammar.declarationSequence,
  368. ""),
  369. pass(),
  370. fail(["PROCEDURE p(); BEGIN v <- 0; v := 0; END p;", "cannot assign to read-only variable"]
  371. )
  372. )
  373. }
  374. };