A toy dynamic programming language written in Ruby
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

function_call_spec.rb 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. RSpec.describe AST::FunctionCall do
  2. it 'evaluates built-in functions' do
  3. expect {
  4. AST::FunctionCall.new(
  5. AST::Identifier.new('print'),
  6. [AST::String.new('hello world')]
  7. )
  8. .execute(Environment.new)
  9. }.to output("hello world\n").to_stdout
  10. end
  11. it 'evaluates user-defined functions' do
  12. env = Environment.new
  13. AST::FunctionDefinition.new(
  14. AST::Identifier.new('add_one'),
  15. [AST::Identifier.new('n')],
  16. AST::Block.new(
  17. [
  18. AST::Binary.new(
  19. AST::Operators::ADD,
  20. AST::Identifier.new('n'),
  21. AST::Number.new(1.0)
  22. )
  23. ]
  24. )
  25. )
  26. .execute(env)
  27. expect(
  28. AST::FunctionCall.new(
  29. AST::Identifier.new('add_one'),
  30. [AST::Number.new(5.0)]
  31. )
  32. .execute(env)
  33. ).to eq(6.0)
  34. end
  35. # This corresponds to the program
  36. # function factorial(n) {
  37. # if n == 0 {
  38. # 1;
  39. # } else {
  40. # n * factorial(n - 1);
  41. # }
  42. # }
  43. # factorial(5);
  44. it 'evaluates recursive functions' do
  45. env = Environment.new
  46. AST::FunctionDefinition.new(
  47. AST::Identifier.new('factorial'),
  48. [AST::Identifier.new('n')],
  49. AST::Block.new(
  50. [
  51. AST::Conditional.new(
  52. [
  53. AST::Branch.new(
  54. AST::Binary.new(
  55. AST::Operators::DOUBLE_EQUALS,
  56. AST::Identifier.new('n'),
  57. AST::Number.new(0.0)
  58. ),
  59. AST::Block.new([AST::Number.new(1.0)])
  60. ),
  61. AST::Branch.new(
  62. AST::Boolean.new(true),
  63. AST::Block.new(
  64. [
  65. AST::Binary.new(
  66. AST::Operators::MULTIPLY,
  67. AST::Identifier.new('n'),
  68. AST::FunctionCall.new(
  69. AST::Identifier.new('factorial'),
  70. [
  71. AST::Binary.new(
  72. AST::Operators::SUBTRACT,
  73. AST::Identifier.new('n'),
  74. AST::Number.new(1.0)
  75. )
  76. ]
  77. )
  78. )
  79. ]
  80. )
  81. )
  82. ]
  83. )
  84. ]
  85. )
  86. )
  87. .execute(env)
  88. expect(
  89. AST::FunctionCall.new(
  90. AST::Identifier.new('factorial'),
  91. [AST::Number.new(5.0)]
  92. )
  93. .execute(env)
  94. ).to eq(120.0)
  95. end
  96. # This is equivalent to the program
  97. #
  98. # function add_one(n) {
  99. # n + 1;
  100. # }
  101. # add_one(5);
  102. # print(n);
  103. #
  104. # This test is ensuring that `n` is not still defined outside the scope of
  105. # add_one after the function finishes executing
  106. it 'destroys variables when they go out of scope' do
  107. env = Environment.new
  108. AST::FunctionDefinition.new(
  109. AST::Identifier.new('add_one'),
  110. [AST::Identifier.new('n')],
  111. AST::Block.new(
  112. [
  113. AST::Binary.new(
  114. AST::Operators::ADD,
  115. AST::Identifier.new('n'),
  116. AST::Number.new(1.0)
  117. )
  118. ]
  119. )
  120. )
  121. .execute(env)
  122. AST::FunctionCall.new(AST::Identifier.new('add_one'), [AST::Number.new(5)])
  123. .execute(env)
  124. expect {
  125. AST::FunctionCall.new(
  126. AST::Identifier.new('print'),
  127. [AST::Identifier.new('n')]
  128. )
  129. .execute(env)
  130. }.to raise_error('Undefined variable n')
  131. end
  132. # let add_one = function(n) { n + 1; };
  133. # let do = function(f, n) { f(n); };
  134. # do(5);
  135. it 'executes higher order functions' do
  136. env = Environment.new
  137. AST::VariableDeclaration.new(
  138. AST::Identifier.new('add_one'),
  139. AST::FunctionDefinition.new(
  140. nil,
  141. [AST::Identifier.new('n')],
  142. AST::Block.new(
  143. [
  144. AST::Binary.new(
  145. AST::Operators::ADD,
  146. AST::Identifier.new('n'),
  147. AST::Number.new(1.0)
  148. )
  149. ]
  150. )
  151. )
  152. )
  153. .execute(env)
  154. AST::VariableDeclaration.new(
  155. AST::Identifier.new('do'),
  156. AST::FunctionDefinition.new(
  157. nil,
  158. [AST::Identifier.new('f'), AST::Identifier.new('x')],
  159. AST::Block.new(
  160. [
  161. AST::FunctionCall.new(
  162. AST::Identifier.new('f'),
  163. [AST::Identifier.new('x')]
  164. )
  165. ]
  166. )
  167. )
  168. )
  169. .execute(env)
  170. result =
  171. AST::FunctionCall.new(
  172. AST::Identifier.new('do'),
  173. [AST::Identifier.new('add_one'), AST::Number.new(5.0)]
  174. )
  175. .execute(env)
  176. expect(result).to eq(6)
  177. end
  178. it 'allows calling an anonymous function directly' do
  179. result =
  180. AST::FunctionCall.new(
  181. AST::FunctionDefinition.new(
  182. nil,
  183. [],
  184. AST::Block.new([AST::Number.new(5.0)])
  185. ),
  186. []
  187. )
  188. .execute(Environment.new)
  189. expect(result).to eq(5.0)
  190. end
  191. # function outer() {
  192. # let x = "hello world";
  193. # function () {
  194. # x;
  195. # };
  196. # }
  197. # let y = outer();
  198. # y();
  199. it "keeps a reference to a closure's environment" do
  200. env = Environment.new
  201. AST::FunctionDefinition.new(
  202. AST::Identifier.new('outer'),
  203. [],
  204. AST::Block.new(
  205. [
  206. AST::VariableDeclaration.new(
  207. AST::Identifier.new('x'),
  208. AST::String.new('hello world')
  209. ),
  210. AST::FunctionDefinition.new(
  211. nil,
  212. [],
  213. AST::Block.new([AST::Identifier.new('x')])
  214. )
  215. ]
  216. )
  217. )
  218. .execute(env)
  219. AST::VariableDeclaration.new(
  220. AST::Identifier.new('y'),
  221. AST::FunctionCall.new(AST::Identifier.new('outer'), [])
  222. )
  223. .execute(env)
  224. result = AST::FunctionCall.new(AST::Identifier.new('y'), []).execute(env)
  225. expect(result).to eq('hello world')
  226. end
  227. end