syntax.ml 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115
  1. module Tree = Qsp_syntax.Tree
  2. module Ast = Tree.Ast
  3. module Check = Qsp_syntax.Check
  4. module S = Qsp_syntax.S
  5. module T = Qsp_syntax.T
  6. let location_id, e1 = Check.build (module Tree)
  7. module Parser = Check.Make (struct
  8. let t = [| e1 |]
  9. end)
  10. let _position = (Lexing.dummy_pos, Lexing.dummy_pos)
  11. type 'a location = 'a * 'a Ast.statement list [@@deriving eq, show]
  12. let get_location :
  13. (S.pos location, Qsp_syntax.Report.t) result -> S.pos location = function
  14. | Ok e -> e
  15. | Error e ->
  16. let msg = Format.asprintf "%a" Qsp_syntax.Report.pp e in
  17. raise (Failure msg)
  18. (** Run the parser with the given expression and return the result *)
  19. let parse : string -> (S.pos location, Qsp_syntax.Report.t) result =
  20. fun content ->
  21. let lexing =
  22. Sedlexing.Latin1.from_string content |> Qparser.Lexbuf.from_lexbuf
  23. in
  24. let context = Parser.initialize () in
  25. Qparser.Analyzer.parse (module Parser) lexing context
  26. |> Result.map (fun v ->
  27. (* Uncatched excteptions here, but we are in the tests…
  28. If it’s fail here I have an error in the code. *)
  29. Array.get v.Qparser.Analyzer.content 0
  30. |> Check.get location_id |> Option.get)
  31. let location : S.pos location Alcotest.testable =
  32. let equal = equal_location (fun _ _ -> true) in
  33. let pp =
  34. pp_location (fun formater _ -> Format.fprintf formater "_position")
  35. in
  36. Alcotest.testable pp equal
  37. let test_empty_location () =
  38. let expected = (_position, [])
  39. and actual = get_location @@ parse {|# Location
  40. ------- |}
  41. and msg = "Empty location" in
  42. Alcotest.(check' location ~msg ~expected ~actual)
  43. let test_location_without_space () =
  44. let expected = (_position, [])
  45. and actual = get_location @@ parse {|#Location
  46. ------- |}
  47. and msg = "Empty location" in
  48. Alcotest.(check' location ~msg ~expected ~actual)
  49. let test_location_without_database () =
  50. let expected = (_position, [])
  51. and actual = get_location @@ parse {|# $Location
  52. ------- |}
  53. and msg = "Location without database" in
  54. let () = Alcotest.(check' location ~msg ~expected ~actual) in
  55. let actual = get_location @@ parse {|# !Location
  56. ------- |} in
  57. let () = Alcotest.(check' location ~msg ~expected ~actual) in
  58. let actual = get_location @@ parse {|# ^Location
  59. ------- |} in
  60. Alcotest.(check' location ~msg ~expected ~actual)
  61. let _test_instruction : string -> S.pos Ast.statement list -> unit =
  62. fun literal expected ->
  63. let expected = (_position, expected)
  64. and _location = Printf.sprintf {|# Location
  65. %s
  66. ------- |} literal in
  67. let actual = get_location @@ parse _location and msg = literal in
  68. Alcotest.(check' location ~msg ~expected ~actual)
  69. let test_numeric_expression () =
  70. _test_instruction "123" [ Expression (Integer (_position, "123")) ]
  71. let test_negative_numeric_expression () =
  72. _test_instruction "-123"
  73. [
  74. Tree.Ast.Expression
  75. (Tree.Ast.Op (_position, T.Neg, Tree.Ast.Integer (_position, "123")));
  76. ]
  77. let test_negative_numeric_expression2 () =
  78. let index = None in
  79. let var =
  80. { Ast.pos = _position; name = "CURTIMESUN"; index; local = false }
  81. in
  82. _test_instruction "-(780-CurTimeSun)"
  83. Ast.
  84. [
  85. Expression
  86. (Op
  87. ( _position,
  88. Neg,
  89. BinaryOp (_position, Minus, Integer (_position, "780"), Ident var)
  90. ));
  91. ]
  92. let test_str_variable () =
  93. let index = None in
  94. let var = { Ast.pos = _position; name = "$VALUE"; index; local = false } in
  95. _test_instruction "$value" [ Expression (Ident var) ]
  96. let test_variable () =
  97. let index = None in
  98. let var = { Ast.pos = _position; name = "VALUE"; index; local = false } in
  99. _test_instruction "value" [ Expression (Ident var) ]
  100. let test_indexed_variable () =
  101. let index = Some Ast.(Integer (_position, "1")) in
  102. let var = { Ast.pos = _position; name = "VALUE"; index; local = false } in
  103. _test_instruction "value[1]" [ Expression (Ident var) ]
  104. let test_let_literal () =
  105. let index = None in
  106. let var = { Ast.pos = _position; name = "VALUE"; index; local = false } in
  107. _test_instruction "let value = '123'"
  108. Ast.
  109. [
  110. Declaration (_position, var, Eq', Literal (_position, [ T.Text "123" ]));
  111. ]
  112. let test_set_array_append () =
  113. let var =
  114. { Ast.pos = _position; name = "$VALUE"; index = None; local = false }
  115. in
  116. _test_instruction "set $value[] = ''"
  117. Ast.
  118. [ Declaration (_position, var, Eq', Literal (_position, [ T.Text "" ])) ]
  119. let test_direct_assignation () =
  120. let index = None in
  121. let var = { Ast.pos = _position; name = "VALUE"; index; local = false } in
  122. _test_instruction "value = '123'"
  123. Ast.
  124. [
  125. Declaration (_position, var, Eq', Literal (_position, [ T.Text "123" ]));
  126. ]
  127. let test_command_assignation () =
  128. let index = None in
  129. let st_1 = { Ast.pos = _position; name = "ST_1"; index; local = false } in
  130. _test_instruction "st_1 = input 'Enter the amount'"
  131. Ast.
  132. [
  133. Declaration
  134. ( _position,
  135. st_1,
  136. Eq',
  137. Function
  138. ( _position,
  139. Input,
  140. [ Literal (_position, [ T.Text "Enter the amount" ]) ] ) );
  141. ]
  142. let test_assignation2 () =
  143. let index = None in
  144. let var = { Ast.pos = _position; name = "VALUE"; index; local = false } in
  145. _test_instruction "set value += 123"
  146. Ast.[ Declaration (_position, var, Inc, Integer (_position, "123")) ]
  147. let test_multilie_literal () =
  148. let index = None in
  149. let var = { Ast.pos = _position; name = "VALUE"; index; local = false } in
  150. _test_instruction {|
  151. value = {
  152. $a = '123'
  153. }|}
  154. Ast.
  155. [
  156. Declaration
  157. (_position, var, Eq', Literal (_position, [ T.Text "\n$a = '123'\n" ]));
  158. ]
  159. let test_nested_literal () =
  160. _test_instruction
  161. {|
  162. value = {
  163. {
  164. }
  165. }|}
  166. [
  167. Ast.Declaration
  168. ( _position,
  169. { Ast.pos = _position; name = "VALUE"; index = None; local = false },
  170. Qsp_syntax.T.Eq',
  171. Ast.Literal (_position, [ T.Text "\n\n {\n\n }\n" ]) );
  172. ]
  173. let test_concat_literal () =
  174. _test_instruction {|
  175. '123'
  176. +'456'
  177. |}
  178. [
  179. Ast.Expression (Ast.Literal (_position, [ T.Text "123" ]));
  180. Ast.Expression
  181. (Ast.Op
  182. ( _position,
  183. Qsp_syntax.T.Add,
  184. Ast.Literal (_position, [ T.Text "456" ]) ));
  185. ]
  186. let test_literal () =
  187. _test_instruction "'123'"
  188. [ Expression (Literal (_position, [ T.Text "123" ])) ]
  189. let test_qutoted_literal () =
  190. _test_instruction "'12''3'"
  191. [ Expression (Literal (_position, [ T.Text "12'3" ])) ]
  192. let test_multiline1 () =
  193. let content = {|
  194. apples = 5
  195. pears = 10
  196. |} in
  197. let index = None in
  198. let apples = { Ast.pos = _position; name = "APPLES"; index; local = false }
  199. and pears = { Ast.pos = _position; name = "PEARS"; index; local = false }
  200. and value_5 = Ast.Integer (_position, "5")
  201. and value_10 = Ast.Integer (_position, "10") in
  202. _test_instruction content
  203. Ast.
  204. [
  205. Declaration (_position, apples, Eq', value_5);
  206. Declaration (_position, pears, Eq', value_10);
  207. ]
  208. let test_multiline2 () =
  209. let content = "apples = 5 & pears = 10" in
  210. let index = None in
  211. let apples = { Ast.pos = _position; name = "APPLES"; index; local = false }
  212. and pears = { Ast.pos = _position; name = "PEARS"; index; local = false }
  213. and value_5 = Ast.Integer (_position, "5")
  214. and value_10 = Ast.Integer (_position, "10") in
  215. _test_instruction content
  216. [
  217. Declaration (_position, apples, Eq', value_5);
  218. Declaration (_position, pears, Eq', value_10);
  219. ]
  220. let test_equality () =
  221. let content = "apples = 5 = pears" in
  222. let index = None in
  223. let apples = { Ast.pos = _position; name = "APPLES"; index; local = false }
  224. and pears = { Ast.pos = _position; name = "PEARS"; index; local = false }
  225. and value_5 = Ast.Integer (_position, "5") in
  226. _test_instruction content
  227. [
  228. Declaration
  229. (_position, apples, Eq', BinaryOp (_position, Eq, value_5, Ident pears));
  230. ]
  231. let test_plus () =
  232. let content = {|
  233. apples = 5 + pears
  234. |} in
  235. let index = None in
  236. let apples = { Ast.pos = _position; name = "APPLES"; index; local = false }
  237. and pears = { Ast.pos = _position; name = "PEARS"; index; local = false }
  238. and value_5 = Ast.Integer (_position, "5") in
  239. _test_instruction content
  240. [
  241. Declaration
  242. ( _position,
  243. apples,
  244. Eq',
  245. BinaryOp (_position, Plus, value_5, Ident pears) );
  246. ]
  247. let test_plus_litt () =
  248. let content = {|
  249. 'five'+ pears
  250. |} in
  251. let index = None in
  252. let pears = { Ast.pos = _position; name = "PEARS"; index; local = false } in
  253. _test_instruction content
  254. [
  255. Ast.(
  256. Expression
  257. (BinaryOp
  258. ( _position,
  259. Plus,
  260. Literal (_position, [ T.Text "five" ]),
  261. Ident pears )));
  262. ]
  263. let test_concat () =
  264. let content = {|
  265. $firstName + ' ' + $lastName
  266. |} in
  267. _test_instruction content
  268. [
  269. Tree.Ast.Expression
  270. (Tree.Ast.BinaryOp
  271. ( _position,
  272. Plus,
  273. Tree.Ast.BinaryOp
  274. ( _position,
  275. Plus,
  276. Tree.Ast.Ident
  277. {
  278. Tree.Ast.pos = _position;
  279. name = "$FIRSTNAME";
  280. index = None;
  281. local = false;
  282. },
  283. Tree.Ast.Literal (_position, [ T.Text " " ]) ),
  284. Tree.Ast.Ident
  285. {
  286. Tree.Ast.pos = _position;
  287. name = "$LASTNAME";
  288. index = None;
  289. local = false;
  290. } ));
  291. ]
  292. let test_mod () =
  293. _test_instruction {|2 mod 1|}
  294. [
  295. Tree.Ast.Expression
  296. (Tree.Ast.BinaryOp
  297. ( _position,
  298. T.Mod,
  299. Tree.Ast.Integer (_position, "2"),
  300. Tree.Ast.Integer (_position, "1") ));
  301. ]
  302. let test_comment () = _test_instruction "! Comment" [ Comment _position ]
  303. let test_comment2 () =
  304. let index = None in
  305. let a = { Ast.pos = _position; name = "A"; index; local = false }
  306. and value_0 = Ast.Integer (_position, "0") in
  307. _test_instruction "a = 0 &! Comment"
  308. Ast.[ Declaration (_position, a, Eq', value_0); Comment _position ]
  309. let test_comment3 () = _test_instruction {|!!1234
  310. |} [ Comment _position ]
  311. (** The exclamation mark here is an operation and not a comment *)
  312. let test_comment4 () =
  313. let index = None in
  314. let a = { Ast.pos = _position; name = "A"; index; local = false }
  315. and value_0 = Ast.Integer (_position, "0") in
  316. _test_instruction "a = rand(0, 1) ! 0"
  317. [
  318. Ast.(
  319. Declaration
  320. ( _position,
  321. a,
  322. Eq',
  323. BinaryOp
  324. ( _position,
  325. Neq,
  326. Function
  327. ( _position,
  328. Rand,
  329. [ Integer (_position, "0"); Integer (_position, "1") ] ),
  330. value_0 ) ));
  331. ]
  332. let test_comment5 () =
  333. _test_instruction "a = rand() &! Comment"
  334. [
  335. Ast.Declaration
  336. ( _position,
  337. { Ast.pos = _position; name = "A"; index = None; local = false },
  338. Qsp_syntax.T.Eq',
  339. Ast.Function (_position, Rand, []) );
  340. Ast.Comment _position;
  341. ]
  342. let test_comment6 () =
  343. _test_instruction
  344. "gs 'stat' &!! It should be here, because some of the strigs have to be \
  345. initialized"
  346. [
  347. Ast.Call
  348. ( _position,
  349. Qsp_syntax.T.Gosub,
  350. [ Ast.Literal (_position, [ T.Text "stat" ]) ] );
  351. Ast.Comment _position;
  352. ]
  353. let test_long_comment () =
  354. _test_instruction
  355. {| !'this part of the comment is inside
  356. single quotes' but "this is still part
  357. of the same comment because sometimes
  358. life is unfair." Oh yeah, {curly brackets
  359. also count}. This is still the same comment. |}
  360. [ Comment _position ]
  361. (** This test ensure that the unary operator is applied to the whole expression
  362. *)
  363. let test_precedence () =
  364. let index = None in
  365. let x = Ast.Ident { Ast.pos = _position; name = "X"; index; local = false }
  366. and y = Ast.Ident { Ast.pos = _position; name = "Y"; index; local = false } in
  367. _test_instruction "no x = y"
  368. Ast.[ Expression (Op (_position, No, BinaryOp (_position, Eq, x, y))) ]
  369. (** This test ensure that a ! is not considered as a comment in an
  370. expression *)
  371. let test_precedence2 () =
  372. let index = None in
  373. let x = { Ast.pos = _position; name = "X"; index; local = false }
  374. and y = Ast.Ident { Ast.pos = _position; name = "Y"; index; local = false } in
  375. _test_instruction "x = y ! 0"
  376. Ast.
  377. [
  378. Declaration
  379. ( _position,
  380. x,
  381. Eq',
  382. BinaryOp (_position, Neq, y, Integer (_position, "0")) );
  383. ]
  384. let test_if () =
  385. let index = Some Ast.(Integer (_position, "0")) in
  386. let args =
  387. Ast.(Ident { pos = _position; name = "$ARGS"; index; local = false })
  388. and expr1 = Ast.(Literal (_position, [ T.Text "blockA" ]))
  389. and expr2 =
  390. Ast.(Expression (Literal (_position, [ T.Text "You are in block A" ])))
  391. in
  392. _test_instruction {| if $ARGS[0] = 'blockA':
  393. 'You are in block A'
  394. end |}
  395. Ast.
  396. [
  397. If
  398. {
  399. loc = _position;
  400. then_ = (_position, BinaryOp (_position, Eq, args, expr1), [ expr2 ]);
  401. elifs = [];
  402. else_ = [];
  403. };
  404. ]
  405. let test_if_chained () =
  406. let value_0 = Ast.Integer (_position, "0") in
  407. _test_instruction {| if 0:
  408. 0
  409. end &! -- |}
  410. Ast.
  411. [
  412. If
  413. {
  414. loc = _position;
  415. then_ = (_position, value_0, [ Expression value_0 ]);
  416. elifs = [];
  417. else_ = [];
  418. };
  419. Comment _position;
  420. ]
  421. let test_if_equality () =
  422. _test_instruction {|
  423. if 0 = 0:
  424. end &! --
  425. |}
  426. [
  427. Ast.If
  428. {
  429. loc = _position;
  430. then_ =
  431. ( _position,
  432. Ast.BinaryOp
  433. ( _position,
  434. Qsp_syntax.T.Eq,
  435. Ast.Integer (_position, "0"),
  436. Ast.Integer (_position, "0") ),
  437. [] );
  438. elifs = [];
  439. else_ = [];
  440. };
  441. Ast.Comment _position;
  442. ]
  443. let test_if_inline () =
  444. let value_0 = Ast.Integer (_position, "0") in
  445. _test_instruction "if 0: 0 else 0"
  446. Ast.
  447. [
  448. If
  449. {
  450. loc = _position;
  451. then_ = (_position, value_0, [ Expression value_0 ]);
  452. elifs = [];
  453. else_ = [ Expression value_0 ];
  454. };
  455. ]
  456. let test_if_inline_comment () =
  457. let value_0 = Ast.Integer (_position, "0") in
  458. _test_instruction "if 0: 0 else 0 &! comment "
  459. Ast.
  460. [
  461. If
  462. {
  463. loc = _position;
  464. then_ = (_position, value_0, [ Expression value_0 ]);
  465. elifs = [];
  466. else_ = [ Expression value_0 ];
  467. };
  468. Comment _position;
  469. ]
  470. let test_if_inline_comment2 () =
  471. _test_instruction "if 0: 1 & !! Comment"
  472. [
  473. Ast.If
  474. {
  475. loc = _position;
  476. then_ =
  477. ( _position,
  478. Ast.Integer (_position, "0"),
  479. [
  480. Ast.Expression (Ast.Integer (_position, "1"));
  481. Ast.Comment _position;
  482. ] );
  483. elifs = [];
  484. else_ = [];
  485. };
  486. ]
  487. let test_if_inline_act () =
  488. _test_instruction "if 1 and hour >= 8: minut += 1 & act 'go': gt 'go'"
  489. [
  490. Ast.If
  491. {
  492. loc = _position;
  493. then_ =
  494. ( _position,
  495. Ast.BinaryOp
  496. ( _position,
  497. Qsp_syntax.T.And,
  498. Ast.Integer (_position, "1"),
  499. Ast.BinaryOp
  500. ( _position,
  501. Qsp_syntax.T.Gte,
  502. Ast.Ident
  503. {
  504. Ast.pos = _position;
  505. name = "HOUR";
  506. index = None;
  507. local = false;
  508. },
  509. Ast.Integer (_position, "8") ) ),
  510. [
  511. Ast.Declaration
  512. ( _position,
  513. {
  514. Ast.pos = _position;
  515. name = "MINUT";
  516. index = None;
  517. local = false;
  518. },
  519. Qsp_syntax.T.Inc,
  520. Ast.Integer (_position, "1") );
  521. Ast.Act
  522. {
  523. loc = _position;
  524. label = Ast.Literal (_position, [ T.Text "go" ]);
  525. statements =
  526. [
  527. Ast.Call
  528. ( _position,
  529. Qsp_syntax.T.Goto,
  530. [ Ast.Literal (_position, [ T.Text "go" ]) ] );
  531. ];
  532. };
  533. ] );
  534. elifs = [];
  535. else_ = [];
  536. };
  537. ]
  538. let test_if_multiline () =
  539. _test_instruction {|if 1 _
  540. and _hour >= 8: 1|}
  541. [
  542. Ast.If
  543. {
  544. loc = _position;
  545. then_ =
  546. ( _position,
  547. Ast.BinaryOp
  548. ( _position,
  549. Qsp_syntax.T.And,
  550. Ast.Integer (_position, "1"),
  551. Ast.BinaryOp
  552. ( _position,
  553. Qsp_syntax.T.Gte,
  554. Ast.Ident
  555. {
  556. Ast.pos = _position;
  557. name = "_HOUR";
  558. index = None;
  559. local = false;
  560. },
  561. Ast.Integer (_position, "8") ) ),
  562. [ Tree.Ast.Expression (Tree.Ast.Integer (_position, "1")) ] );
  563. elifs = [];
  564. else_ = [];
  565. };
  566. ]
  567. let test_if_inline_act2 () =
  568. _test_instruction "if 1: act 'go': gt 'go' &! comment "
  569. [
  570. Ast.If
  571. {
  572. loc = _position;
  573. then_ =
  574. ( _position,
  575. Ast.Integer (_position, "1"),
  576. [
  577. Ast.Act
  578. {
  579. loc = _position;
  580. label = Ast.Literal (_position, [ T.Text "go" ]);
  581. statements =
  582. [
  583. Ast.Call
  584. ( _position,
  585. Qsp_syntax.T.Goto,
  586. [ Ast.Literal (_position, [ T.Text "go" ]) ] );
  587. Ast.Comment _position;
  588. ];
  589. };
  590. ] );
  591. elifs = [];
  592. else_ = [];
  593. };
  594. ]
  595. let test_precedence3 () =
  596. let index = Some Ast.(Integer (_position, "0")) in
  597. let args =
  598. Ast.(Ident { pos = _position; name = "$ARGS"; index; local = false })
  599. and expr1 = Ast.(Literal (_position, [ T.Text "blockA" ]))
  600. and expr2 =
  601. Ast.(Expression (Literal (_position, [ T.Text "You are in block A" ])))
  602. and expr3 = Ast.(Expression (Integer (_position, "0"))) in
  603. _test_instruction {| if $ARGS[0] = 'blockA': 'You are in block A' & 0|}
  604. Ast.
  605. [
  606. If
  607. {
  608. loc = _position;
  609. then_ =
  610. ( _position,
  611. BinaryOp (_position, Eq, args, expr1),
  612. [ expr2; expr3 ] );
  613. elifs = [];
  614. else_ = [];
  615. };
  616. ]
  617. let test_gs () =
  618. _test_instruction "gs '123'"
  619. [
  620. Ast.(
  621. Call
  622. ( _position,
  623. Qsp_syntax.T.Gosub,
  624. [ Literal (_position, [ T.Text "123" ]) ] ));
  625. ]
  626. let test_gt () =
  627. _test_instruction "gt $curloc"
  628. [
  629. Ast.Call
  630. ( _position,
  631. Qsp_syntax.T.Goto,
  632. [
  633. Ast.Ident
  634. {
  635. Ast.pos = _position;
  636. name = "$CURLOC";
  637. index = None;
  638. local = false;
  639. };
  640. ] );
  641. ]
  642. let test_nl () =
  643. _test_instruction "*NL 'It'"
  644. [
  645. Ast.Call
  646. ( _position,
  647. Qsp_syntax.T.Nl',
  648. [ Ast.Literal (_position, [ T.Text "It" ]) ] );
  649. ]
  650. let test_function () =
  651. _test_instruction "iif(123, 1, 0)"
  652. [
  653. Ast.(
  654. Expression
  655. (Function
  656. ( _position,
  657. Iif,
  658. [
  659. Integer (_position, "123");
  660. Integer (_position, "1");
  661. Integer (_position, "0");
  662. ] )));
  663. ]
  664. (** Include a space before the parameters *)
  665. let test_function2 () =
  666. _test_instruction "rand (0, 1)"
  667. [
  668. Ast.(
  669. Expression
  670. (Function
  671. ( _position,
  672. Rand,
  673. [ Integer (_position, "0"); Integer (_position, "1") ] )));
  674. ]
  675. (** The RND function does not have parens after the name *)
  676. let test_rnd () =
  677. _test_instruction "rnd"
  678. [ Tree.Ast.Expression (Tree.Ast.Function (_position, T.Rnd, [])) ]
  679. let test_precedence4 () =
  680. _test_instruction "trim()" Ast.[ Expression (Function (_position, Trim, [])) ]
  681. (** This should not be a keyword without arguments, followed by an expression *)
  682. let test_precedence5 () =
  683. _test_instruction "clear()" Ast.[ Call (_position, Qsp_syntax.T.Clear, []) ]
  684. let test_precedence6 () =
  685. _test_instruction "(1 = 0 and 2 ! 3)"
  686. [
  687. Ast.Expression
  688. (Ast.BinaryOp
  689. ( _position,
  690. And,
  691. Ast.BinaryOp
  692. ( _position,
  693. Eq,
  694. Ast.Integer (_position, "1"),
  695. Ast.Integer (_position, "0") ),
  696. Ast.BinaryOp
  697. ( _position,
  698. Neq,
  699. Ast.Integer (_position, "2"),
  700. Ast.Integer (_position, "3") ) ));
  701. ]
  702. (** An identifier cannot start by a number *0 is a product and not an
  703. identifier *)
  704. let test_operator () =
  705. let index = None in
  706. let a = { Ast.pos = _position; name = "A"; index; local = false }
  707. and value_0 = Ast.Integer (_position, "0") in
  708. _test_instruction "a *0"
  709. Ast.[ Expression (BinaryOp (_position, Product, Ident a, value_0)) ]
  710. let test_operator2 () =
  711. let value_0 = Ast.Integer (_position, "0") in
  712. _test_instruction "0 *rand()"
  713. Ast.
  714. [
  715. Expression
  716. (BinaryOp (_position, Product, value_0, Function (_position, Rand, [])));
  717. ]
  718. let test_dyneval () =
  719. _test_instruction "dyneval ''"
  720. [
  721. Ast.Expression
  722. (Ast.Function
  723. (_position, Dyneval, [ Ast.Literal (_position, [ T.Text "" ]) ]));
  724. ]
  725. (** The parens after input are considered as arguments for the function, not a
  726. following expression.
  727. This expression is a boolean.
  728. *)
  729. let test_input () =
  730. _test_instruction "( input('') = '' )"
  731. [
  732. Tree.Ast.Expression
  733. (Tree.Ast.BinaryOp
  734. ( _position,
  735. Eq,
  736. Tree.Ast.Function
  737. ( _position,
  738. Input,
  739. [ Tree.Ast.Literal (_position, [ T.Text "" ]) ] ),
  740. Tree.Ast.Literal (_position, [ T.Text "" ]) ));
  741. ]
  742. let test_mutiple_inline_ifs () =
  743. _test_instruction "if 1 > 0: 1 else if 1 < 0: 0"
  744. [
  745. Tree.Ast.If
  746. {
  747. loc = _position;
  748. then_ =
  749. ( _position,
  750. Tree.Ast.BinaryOp
  751. ( _position,
  752. Gt,
  753. Tree.Ast.Integer (_position, "1"),
  754. Tree.Ast.Integer (_position, "0") ),
  755. [ Tree.Ast.Expression (Tree.Ast.Integer (_position, "1")) ] );
  756. elifs = [];
  757. else_ =
  758. [
  759. Tree.Ast.If
  760. {
  761. loc = _position;
  762. then_ =
  763. ( _position,
  764. Tree.Ast.BinaryOp
  765. ( _position,
  766. Lt,
  767. Tree.Ast.Integer (_position, "1"),
  768. Tree.Ast.Integer (_position, "0") ),
  769. [
  770. Tree.Ast.Expression (Tree.Ast.Integer (_position, "0"));
  771. ] );
  772. elifs = [];
  773. else_ = [];
  774. };
  775. ];
  776. };
  777. ]
  778. (** The boolean comparaison has greater precedence than arithmetic operator *)
  779. let test_precedence7 () =
  780. _test_instruction "(1 + 1 = '')"
  781. [
  782. Tree.Ast.Expression
  783. (Tree.Ast.BinaryOp
  784. ( _position,
  785. Eq,
  786. Tree.Ast.BinaryOp
  787. ( _position,
  788. Plus,
  789. Tree.Ast.Integer (_position, "1"),
  790. Tree.Ast.Integer (_position, "1") ),
  791. Tree.Ast.Literal (_position, [ T.Text "" ]) ));
  792. ]
  793. (** The OR operator has greater precedence than boolean comparaison *)
  794. let test_precedence8 () =
  795. _test_instruction "(0 = 1 or 0 = 1)"
  796. [
  797. Tree.Ast.Expression
  798. (Tree.Ast.BinaryOp
  799. ( _position,
  800. Or,
  801. Tree.Ast.BinaryOp
  802. ( _position,
  803. Eq,
  804. Tree.Ast.Integer (_position, "0"),
  805. Tree.Ast.Integer (_position, "1") ),
  806. Tree.Ast.BinaryOp
  807. ( _position,
  808. Eq,
  809. Tree.Ast.Integer (_position, "0"),
  810. Tree.Ast.Integer (_position, "1") ) ));
  811. ]
  812. (** Test showing the - should be considered as an operator and cannot be
  813. aggregated inside the integer value. *)
  814. let minus_operator () =
  815. _test_instruction {|day-7|}
  816. [
  817. Tree.Ast.Expression
  818. (Tree.Ast.BinaryOp
  819. ( _position,
  820. T.Minus,
  821. Tree.Ast.Ident
  822. {
  823. Tree.Ast.pos = _position;
  824. name = "DAY";
  825. index = None;
  826. local = false;
  827. },
  828. Tree.Ast.Integer (_position, "7") ));
  829. ]
  830. (** STATTXT was considered as a function and raised an error in the syntax *)
  831. let test_stattxt () =
  832. _test_instruction "$value = $stattxt"
  833. [
  834. Tree.Ast.Declaration
  835. ( _position,
  836. {
  837. Tree.Ast.pos = _position;
  838. name = "$VALUE";
  839. index = None;
  840. local = false;
  841. },
  842. T.Eq',
  843. Tree.Ast.Ident
  844. {
  845. Tree.Ast.pos = _position;
  846. name = "$STATTXT";
  847. index = None;
  848. local = false;
  849. } );
  850. ]
  851. let test_for_end () =
  852. _test_instruction {|for a = 1 to 10:
  853. end|}
  854. [
  855. Tree.Ast.For
  856. {
  857. loc = _position;
  858. variable =
  859. {
  860. Tree.Ast.pos = _position;
  861. name = "A";
  862. index = None;
  863. local = false;
  864. };
  865. start = Tree.Ast.Integer (_position, "1");
  866. to_ = Tree.Ast.Integer (_position, "10");
  867. step = None;
  868. statements = [];
  869. };
  870. ]
  871. let test_for_end_with_step () =
  872. _test_instruction {|for a = 1 to 10 step rnd:
  873. end|}
  874. [
  875. Tree.Ast.For
  876. {
  877. loc = _position;
  878. variable =
  879. {
  880. Tree.Ast.pos = _position;
  881. name = "A";
  882. index = None;
  883. local = false;
  884. };
  885. start = Tree.Ast.Integer (_position, "1");
  886. to_ = Tree.Ast.Integer (_position, "10");
  887. step = Some (Tree.Ast.Function (_position, T.Rnd, []));
  888. statements = [];
  889. };
  890. ]
  891. let test_local () =
  892. _test_instruction {|local tempora = 12|}
  893. [
  894. Tree.Ast.Declaration
  895. ( _position,
  896. {
  897. Tree.Ast.pos = _position;
  898. name = "TEMPORA";
  899. index = None;
  900. local = true;
  901. },
  902. T.Eq',
  903. Tree.Ast.Integer (_position, "12") );
  904. ]
  905. let test_local_for () =
  906. _test_instruction {|for local a = 1 to 10:
  907. end|}
  908. [
  909. Tree.Ast.For
  910. {
  911. loc = _position;
  912. variable =
  913. { Tree.Ast.pos = _position; name = "A"; index = None; local = true };
  914. start = Tree.Ast.Integer (_position, "1");
  915. to_ = Tree.Ast.Integer (_position, "10");
  916. step = None;
  917. statements = [];
  918. };
  919. ]
  920. let inline_for () =
  921. _test_instruction {|for i = 0 to 5: msg i|}
  922. [
  923. Tree.Ast.For
  924. {
  925. loc = _position;
  926. variable =
  927. {
  928. Tree.Ast.pos = _position;
  929. name = "I";
  930. index = None;
  931. local = false;
  932. };
  933. start = Tree.Ast.Integer (_position, "0");
  934. to_ = Tree.Ast.Integer (_position, "5");
  935. step = None;
  936. statements =
  937. [
  938. Tree.Ast.Call
  939. ( _position,
  940. T.Msg,
  941. [
  942. Tree.Ast.Ident
  943. {
  944. Tree.Ast.pos = _position;
  945. name = "I";
  946. index = None;
  947. local = false;
  948. };
  949. ] );
  950. ];
  951. };
  952. ]
  953. let test =
  954. ( "Syntax",
  955. [
  956. Alcotest.test_case "Location" `Quick test_empty_location;
  957. Alcotest.test_case "Location" `Quick test_location_without_space;
  958. Alcotest.test_case "Location" `Quick test_location_without_database;
  959. Alcotest.test_case " Numeric expression" `Quick test_numeric_expression;
  960. Alcotest.test_case "-Numeric expression" `Quick
  961. test_negative_numeric_expression;
  962. Alcotest.test_case "-Numeric expression2" `Quick
  963. test_negative_numeric_expression2;
  964. Alcotest.test_case "Minus op" `Quick minus_operator;
  965. Alcotest.test_case "$Variable expression" `Quick test_str_variable;
  966. Alcotest.test_case " Variable expression" `Quick test_variable;
  967. Alcotest.test_case "Indexed Variable expression" `Quick
  968. test_indexed_variable;
  969. Alcotest.test_case "Let instruction" `Quick test_let_literal;
  970. Alcotest.test_case "Set array_append" `Quick test_set_array_append;
  971. Alcotest.test_case "Variable_assignation" `Quick test_direct_assignation;
  972. Alcotest.test_case "Command assignation" `Quick test_command_assignation;
  973. Alcotest.test_case "Variable_assignation2" `Quick test_assignation2;
  974. Alcotest.test_case "Literal" `Quick test_literal;
  975. Alcotest.test_case "Literal2" `Quick test_qutoted_literal;
  976. Alcotest.test_case "Literal3" `Quick test_multilie_literal;
  977. Alcotest.test_case "Concat Literal" `Quick test_concat_literal;
  978. Alcotest.test_case "Nested Literal" `Quick test_nested_literal;
  979. Alcotest.test_case "Multiline1" `Quick test_multiline1;
  980. Alcotest.test_case "Multiline2" `Quick test_multiline2;
  981. Alcotest.test_case "Equality" `Quick test_equality;
  982. Alcotest.test_case "Plus" `Quick test_plus;
  983. Alcotest.test_case "Plus_litt" `Quick test_plus_litt;
  984. Alcotest.test_case "PlusChained" `Quick test_concat;
  985. Alcotest.test_case "Mod operator" `Quick test_mod;
  986. Alcotest.test_case "Comment" `Quick test_comment;
  987. Alcotest.test_case "Comment2" `Quick test_comment2;
  988. Alcotest.test_case "Comment3" `Quick test_comment3;
  989. Alcotest.test_case "Comment4" `Quick test_comment4;
  990. Alcotest.test_case "Comment5" `Quick test_comment5;
  991. Alcotest.test_case "Comment6" `Quick test_comment6;
  992. Alcotest.test_case "Multiline Comment" `Quick test_long_comment;
  993. Alcotest.test_case "If" `Quick test_if;
  994. Alcotest.test_case "If_chained" `Quick test_if_chained;
  995. Alcotest.test_case "If_equality" `Quick test_if_equality;
  996. Alcotest.test_case "If inline" `Quick test_if_inline;
  997. Alcotest.test_case "If inline &!" `Quick test_if_inline_comment;
  998. Alcotest.test_case "If inline & !!" `Quick test_if_inline_comment2;
  999. Alcotest.test_case "If : act" `Quick test_if_inline_act;
  1000. Alcotest.test_case "If _ and " `Quick test_if_multiline;
  1001. Alcotest.test_case "If : act: &!" `Quick test_if_inline_act2;
  1002. Alcotest.test_case "Precedence1" `Quick test_precedence;
  1003. Alcotest.test_case "Precedence2" `Quick test_precedence2;
  1004. Alcotest.test_case "Precedence3" `Quick test_precedence3;
  1005. Alcotest.test_case "Call gs" `Quick test_gs;
  1006. Alcotest.test_case "Call gt" `Quick test_gt;
  1007. Alcotest.test_case "Call nl" `Quick test_nl;
  1008. Alcotest.test_case "Function iif" `Quick test_function;
  1009. Alcotest.test_case "Function rand" `Quick test_function2;
  1010. Alcotest.test_case "Function rnd" `Quick test_rnd;
  1011. Alcotest.test_case "Precedence4" `Quick test_precedence4;
  1012. Alcotest.test_case "Precedence5" `Quick test_precedence5;
  1013. Alcotest.test_case "Precedence6" `Quick test_precedence6;
  1014. Alcotest.test_case "Operator" `Quick test_operator;
  1015. Alcotest.test_case "Operator2" `Quick test_operator2;
  1016. Alcotest.test_case "Dyneval" `Quick test_dyneval;
  1017. Alcotest.test_case "Input" `Quick test_input;
  1018. Alcotest.test_case "inline if else if" `Quick test_mutiple_inline_ifs;
  1019. Alcotest.test_case "Precedence7" `Quick test_precedence7;
  1020. Alcotest.test_case "Precedence8" `Quick test_precedence8;
  1021. Alcotest.test_case "stattxt" `Quick test_stattxt;
  1022. Alcotest.test_case "for ... end" `Quick test_for_end;
  1023. Alcotest.test_case "for step ... end" `Quick test_for_end_with_step;
  1024. Alcotest.test_case "local variable" `Quick test_local;
  1025. Alcotest.test_case "local variable in for loop" `Quick test_local_for;
  1026. Alcotest.test_case "inline for" `Quick inline_for;
  1027. ] )