A toy dynamic programming language written in Ruby
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

parser_spec.rb 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. RSpec.describe Parser do
  2. def parse(source)
  3. Parser.new(Lexer.new(source)).parse
  4. end
  5. it 'parses null' do
  6. expect(parse('null;')).to eq([AST::Null.new])
  7. end
  8. it 'parses booleans' do
  9. expect(parse('true;')).to eq([AST::Boolean.new(true)])
  10. expect(parse('false;')).to eq([AST::Boolean.new(false)])
  11. end
  12. it 'parses numbers' do
  13. expect(parse('5;')).to eq([AST::Number.new(5.0)])
  14. end
  15. it 'parses strings' do
  16. expect(parse('"hello world";')).to eq([AST::String.new('hello world')])
  17. end
  18. it 'parses addition' do
  19. expect(parse('1 + 2;')).to eq(
  20. [
  21. AST::Binary.new(
  22. AST::Operators::ADD,
  23. AST::Number.new(1.0),
  24. AST::Number.new(2.0)
  25. )
  26. ]
  27. )
  28. end
  29. it 'parses subtraction' do
  30. expect(parse('1 - 2;')).to eq(
  31. [
  32. AST::Binary.new(
  33. AST::Operators::SUBTRACT,
  34. AST::Number.new(1.0),
  35. AST::Number.new(2.0)
  36. )
  37. ]
  38. )
  39. end
  40. it 'parses multiplication' do
  41. expect(parse('1 * 2;')).to eq(
  42. [
  43. AST::Binary.new(
  44. AST::Operators::MULTIPLY,
  45. AST::Number.new(1.0),
  46. AST::Number.new(2.0)
  47. )
  48. ]
  49. )
  50. end
  51. it 'parses division' do
  52. expect(parse('1 / 2;')).to eq(
  53. [
  54. AST::Binary.new(
  55. AST::Operators::DIVIDE,
  56. AST::Number.new(1.0),
  57. AST::Number.new(2.0)
  58. )
  59. ]
  60. )
  61. end
  62. it 'gives multiplication higher precedence than addition' do
  63. expect(parse('1 + 2 * 3;')).to eq(
  64. [
  65. AST::Binary.new(
  66. AST::Operators::ADD,
  67. AST::Number.new(1.0),
  68. AST::Binary.new(
  69. AST::Operators::MULTIPLY,
  70. AST::Number.new(2.0),
  71. AST::Number.new(3.0)
  72. )
  73. )
  74. ]
  75. )
  76. end
  77. it 'gives multiplication higher precedence than subtraction' do
  78. expect(parse('1 - 2 / 3;')).to eq(
  79. [
  80. AST::Binary.new(
  81. AST::Operators::SUBTRACT,
  82. AST::Number.new(1.0),
  83. AST::Binary.new(
  84. AST::Operators::DIVIDE,
  85. AST::Number.new(2.0),
  86. AST::Number.new(3.0)
  87. )
  88. )
  89. ]
  90. )
  91. end
  92. it 'parses if statement' do
  93. expect(parse('if true { "true"; }')).to eq(
  94. [
  95. AST::Conditional.new(
  96. [
  97. AST::Branch.new(
  98. AST::Boolean.new(true),
  99. AST::Block.new([AST::String.new('true')])
  100. )
  101. ]
  102. )
  103. ]
  104. )
  105. end
  106. it 'parses if else statement' do
  107. expect(parse('if true { "true"; } else { "false"; }')).to eq(
  108. [
  109. AST::Conditional.new(
  110. [
  111. AST::Branch.new(
  112. AST::Boolean.new(true),
  113. AST::Block.new([AST::String.new('true')])
  114. ),
  115. AST::Branch.new(
  116. AST::Boolean.new(true),
  117. AST::Block.new([AST::String.new('false')])
  118. )
  119. ]
  120. )
  121. ]
  122. )
  123. end
  124. it 'parses if elseif else statement' do
  125. expect(
  126. parse('if true { "true"; } elseif false { "false"; } else { "neither"; }')
  127. ).to eq(
  128. [
  129. AST::Conditional.new(
  130. [
  131. AST::Branch.new(
  132. AST::Boolean.new(true),
  133. AST::Block.new([AST::String.new('true')])
  134. ),
  135. AST::Branch.new(
  136. AST::Boolean.new(false),
  137. AST::Block.new([AST::String.new('false')])
  138. ),
  139. AST::Branch.new(
  140. AST::Boolean.new(true),
  141. AST::Block.new([AST::String.new('neither')])
  142. )
  143. ]
  144. )
  145. ]
  146. )
  147. end
  148. it 'parses comparisons' do
  149. expect(parse('1 < 2;')).to eq(
  150. [
  151. AST::Binary.new(
  152. AST::Operators::LESS_THAN,
  153. AST::Number.new(1.0),
  154. AST::Number.new(2.0)
  155. )
  156. ]
  157. )
  158. expect(parse('1 > 2;')).to eq(
  159. [
  160. AST::Binary.new(
  161. AST::Operators::GREATER_THAN,
  162. AST::Number.new(1.0),
  163. AST::Number.new(2.0)
  164. )
  165. ]
  166. )
  167. expect(parse('1 <= 2;')).to eq(
  168. [
  169. AST::Binary.new(
  170. AST::Operators::LESS_THAN_OR_EQUAL,
  171. AST::Number.new(1.0),
  172. AST::Number.new(2.0)
  173. )
  174. ]
  175. )
  176. expect(parse('1 >= 2;')).to eq(
  177. [
  178. AST::Binary.new(
  179. AST::Operators::GREATER_THAN_OR_EQUAL,
  180. AST::Number.new(1.0),
  181. AST::Number.new(2.0)
  182. )
  183. ]
  184. )
  185. expect(parse('1 or 2;')).to eq(
  186. [
  187. AST::Binary.new(
  188. AST::Operators::OR,
  189. AST::Number.new(1.0),
  190. AST::Number.new(2.0)
  191. )
  192. ]
  193. )
  194. expect(parse('1 and 2;')).to eq(
  195. [
  196. AST::Binary.new(
  197. AST::Operators::AND,
  198. AST::Number.new(1.0),
  199. AST::Number.new(2.0)
  200. )
  201. ]
  202. )
  203. end
  204. it 'parses identifier' do
  205. expect(parse('x;')).to eq([AST::Identifier.new('x')])
  206. end
  207. it 'parses variable declaration' do
  208. expect(parse('let x = 1;')).to eq(
  209. [
  210. AST::VariableDeclaration.new(
  211. AST::Identifier.new('x'),
  212. AST::Number.new(1.0)
  213. )
  214. ]
  215. )
  216. end
  217. it 'parses function definition' do
  218. result = [
  219. AST::FunctionDefinition.new(
  220. AST::Identifier.new('add_one'),
  221. [AST::Identifier.new('x')],
  222. AST::Block.new(
  223. [
  224. AST::Binary.new(
  225. AST::Operators::ADD,
  226. AST::Identifier.new('x'),
  227. AST::Number.new(1.0)
  228. )
  229. ]
  230. )
  231. )
  232. ]
  233. expect(parse('function add_one(x) { x + 1; }')).to eq(result)
  234. expect(parse('function add_one(x,) { x + 1; }')).to eq(result)
  235. end
  236. it 'parses function call' do
  237. result = [
  238. AST::FunctionCall.new(
  239. AST::Identifier.new('func'),
  240. [AST::Number.new(1.0), AST::Number.new(2.0)]
  241. )
  242. ]
  243. expect(parse('func(1, 2);')).to eq(result)
  244. expect(parse('func(1, 2,);')).to eq(result)
  245. end
  246. it 'parses array literal' do
  247. result = [
  248. AST::Array.new(
  249. [AST::Number.new(1.0), AST::Number.new(2.0), AST::Number.new(3.0)]
  250. )
  251. ]
  252. expect(parse('[1, 2, 3];')).to eq(result)
  253. expect(parse('[1, 2, 3,];')).to eq(result)
  254. end
  255. it 'parses a class definition do' do
  256. expect(
  257. parse(<<~CLASS)
  258. class Class {
  259. public foo, bar;
  260. private baz;
  261. method my_method() {
  262. "my method";
  263. }
  264. classmethod my_classmethod() {
  265. "my classmethod";
  266. }
  267. }
  268. CLASS
  269. ).to eq(
  270. [
  271. AST::ClassDefinition.new(
  272. AST::Identifier.new('Class'),
  273. [
  274. AST::PropertyDeclaration.new(AST::Identifier.new('foo'), true),
  275. AST::PropertyDeclaration.new(AST::Identifier.new('bar'), true),
  276. AST::PropertyDeclaration.new(AST::Identifier.new('baz'), false)
  277. ],
  278. [
  279. AST::FunctionDefinition.new(
  280. AST::Identifier.new('my_method'),
  281. [],
  282. AST::Block.new([AST::String.new('method')])
  283. )
  284. ],
  285. [
  286. AST::FunctionDefinition.new(
  287. AST::Identifier.new('my_classmethod'),
  288. [],
  289. AST::Block.new([AST::String.new('my classmethod')])
  290. )
  291. ]
  292. )
  293. ]
  294. )
  295. end
  296. it 'gives function calls higher precedence than binary operations' do
  297. expect(parse('x + func(y);')).to eq(
  298. [
  299. AST::Binary.new(
  300. AST::Operators::ADD,
  301. AST::Identifier.new('x'),
  302. AST::FunctionCall.new(
  303. AST::Identifier.new('func'),
  304. [AST::Identifier.new('y')]
  305. )
  306. )
  307. ]
  308. )
  309. end
  310. it 'parses hashes' do
  311. expect(parse('{ :a => 1, :b => 2 };')).to eq(
  312. [AST::Hash.new({ a: AST::Number.new(1.0), b: AST::Number.new(2.0) })]
  313. )
  314. end
  315. it 'parses bracket indexing' do
  316. expect(parse('array[0];')).to eq(
  317. [AST::Index.new(AST::Identifier.new('array'), AST::Number.new(0))]
  318. )
  319. end
  320. it 'parses atoms' do
  321. expect(parse(':atom;')).to eq([AST::Atom.new(:atom)])
  322. end
  323. it 'parses unary expressions' do
  324. expect(parse('-1;')).to eq([AST::Unary.new(:-, AST::Number.new(1.0))])
  325. expect(parse('!true;')).to eq([AST::Unary.new(:!, AST::Boolean.new(true))])
  326. end
  327. it 'gives parenthetical expressions higher precedence' do
  328. expect(parse('(1 + 2) * 3;')).to eq(
  329. [
  330. AST::Binary.new(
  331. AST::Operators::MULTIPLY,
  332. AST::Binary.new(
  333. AST::Operators::ADD,
  334. AST::Number.new(1.0),
  335. AST::Number.new(2.0)
  336. ),
  337. AST::Number.new(3.0)
  338. )
  339. ]
  340. )
  341. end
  342. it 'parses for loops' do
  343. expect(parse('for x in xs { print(x); }')).to eq(
  344. [
  345. AST::ForLoop.new(
  346. AST::Identifier.new('x'),
  347. AST::Identifier.new('xs'),
  348. AST::Block.new(
  349. [
  350. AST::FunctionCall.new(
  351. AST::Identifier.new('print'),
  352. [AST::Identifier.new('x')]
  353. )
  354. ]
  355. )
  356. )
  357. ]
  358. )
  359. end
  360. it 'parses a function expression' do
  361. expect(parse('let sum = function(x, y) { x + y; };')).to eq(
  362. [
  363. AST::VariableDeclaration.new(
  364. AST::Identifier.new('sum'),
  365. AST::FunctionDefinition.new(
  366. nil,
  367. [AST::Identifier.new('x'), AST::Identifier.new('y')],
  368. AST::Block.new(
  369. [
  370. AST::Binary.new(
  371. AST::Operators::ADD,
  372. AST::Identifier.new('x'),
  373. AST::Identifier.new('y')
  374. )
  375. ]
  376. )
  377. )
  378. )
  379. ]
  380. )
  381. end
  382. it 'allows returning an anonymous function from another function' do
  383. expect(
  384. parse('function outer() { function() { print("Hello world"); }; }')
  385. ).to eq(
  386. [
  387. AST::FunctionDefinition.new(
  388. AST::Identifier.new('outer'),
  389. [],
  390. AST::Block.new(
  391. [
  392. AST::FunctionDefinition.new(
  393. nil,
  394. [],
  395. AST::Block.new(
  396. [
  397. AST::FunctionCall.new(
  398. AST::Identifier.new('print'),
  399. [AST::String.new('Hello world')]
  400. )
  401. ]
  402. )
  403. )
  404. ]
  405. )
  406. )
  407. ]
  408. )
  409. end
  410. it 'allows toplevel function expressions' do
  411. expect(parse('function() { print("Hello world"); };')).to eq(
  412. [
  413. AST::FunctionDefinition.new(
  414. nil,
  415. [],
  416. AST::Block.new(
  417. [
  418. AST::FunctionCall.new(
  419. AST::Identifier.new('print'),
  420. [AST::String.new('Hello world')]
  421. )
  422. ]
  423. )
  424. )
  425. ]
  426. )
  427. end
  428. it 'parses assignments' do
  429. expect(parse('x = 5;')).to eq(
  430. [AST::Assignment.new(AST::Identifier.new('x'), AST::Number.new(5.0))]
  431. )
  432. end
  433. it 'allows calling the result of a function call as a function' do
  434. expect(parse('func()();')).to eq(
  435. [
  436. AST::FunctionCall.new(
  437. AST::FunctionCall.new(AST::Identifier.new('func'), []),
  438. []
  439. )
  440. ]
  441. )
  442. end
  443. it 'allows arbitrarily chaining function calls and indexes' do
  444. expect(parse('func()[0]()[1][2]();')).to eq(
  445. [
  446. AST::FunctionCall.new(
  447. AST::Index.new(
  448. AST::Index.new(
  449. AST::FunctionCall.new(
  450. AST::Index.new(
  451. AST::FunctionCall.new(AST::Identifier.new('func'), []),
  452. AST::Number.new(0.0)
  453. ),
  454. []
  455. ),
  456. AST::Number.new(1.0)
  457. ),
  458. AST::Number.new(2.0)
  459. ),
  460. []
  461. )
  462. ]
  463. )
  464. end
  465. it 'parses property access' do
  466. expect(parse('hello#world;')).to eq(
  467. [
  468. AST::Property.new(
  469. AST::Identifier.new('hello'),
  470. AST::Identifier.new('world')
  471. )
  472. ]
  473. )
  474. end
  475. it 'parses allows calling properties (methods)' do
  476. expect(parse('hello#world();')).to eq(
  477. [
  478. AST::FunctionCall.new(
  479. AST::Property.new(
  480. AST::Identifier.new('hello'),
  481. AST::Identifier.new('world')
  482. ),
  483. []
  484. )
  485. ]
  486. )
  487. end
  488. end