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.

function_call_spec.rb 3.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  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. ).execute(Environment.new)
  8. }.to output("hello world\n").to_stdout
  9. end
  10. it 'evaluates user-defined functions' do
  11. env = Environment.new
  12. AST::FunctionDefinition.new(
  13. AST::Identifier.new('add_one'),
  14. [AST::Identifier.new('n')],
  15. AST::Block.new([
  16. AST::Binary.new(
  17. AST::Operators::ADD,
  18. AST::Identifier.new('n'),
  19. AST::Number.new(1.0)
  20. )
  21. ])
  22. ).execute(env)
  23. expect(
  24. AST::FunctionCall.new(
  25. AST::Identifier.new('add_one'),
  26. [AST::Number.new(5.0)]
  27. ).execute(env)
  28. ).to eq(6.0)
  29. end
  30. # This corresponds to the program
  31. # function factorial(n) {
  32. # if n == 0 {
  33. # 1;
  34. # } else {
  35. # n * factorial(n - 1);
  36. # }
  37. # }
  38. # factorial(5);
  39. it 'evaluates recursive functions' do
  40. env = Environment.new
  41. AST::FunctionDefinition.new(
  42. AST::Identifier.new('factorial'),
  43. [AST::Identifier.new('n')],
  44. AST::Block.new([
  45. AST::Conditional.new(
  46. [
  47. AST::Branch.new(
  48. AST::Binary.new(
  49. AST::Operators::DOUBLE_EQUALS,
  50. AST::Identifier.new('n'),
  51. AST::Number.new(0.0),
  52. ),
  53. AST::Block.new([
  54. AST::Number.new(1.0),
  55. ])
  56. ),
  57. AST::Branch.new(
  58. AST::Boolean.new(true),
  59. AST::Block.new([
  60. AST::Binary.new(
  61. AST::Operators::MULTIPLY,
  62. AST::Identifier.new('n'),
  63. AST::FunctionCall.new(
  64. AST::Identifier.new('factorial'),
  65. [
  66. AST::Binary.new(
  67. AST::Operators::SUBTRACT,
  68. AST::Identifier.new('n'),
  69. AST::Number.new(1.0),
  70. )
  71. ]
  72. )
  73. )
  74. ])
  75. ),
  76. ]
  77. )
  78. ])
  79. ).execute(env)
  80. expect(
  81. AST::FunctionCall.new(
  82. AST::Identifier.new('factorial'),
  83. [AST::Number.new(5.0)]
  84. ).execute(env)
  85. ).to eq(120.0)
  86. end
  87. # This is equivalent to the program
  88. #
  89. # function add_one(n) {
  90. # n + 1;
  91. # }
  92. # add_one(5);
  93. # print(n);
  94. #
  95. # This test is ensuring that `n` is not still defined outside the scope of
  96. # add_one after the function finishes executing
  97. it 'destroys variables when they go out of scope' do
  98. env = Environment.new
  99. AST::FunctionDefinition.new(
  100. AST::Identifier.new('add_one'),
  101. [AST::Identifier.new('n')],
  102. AST::Block.new([
  103. AST::Binary.new(
  104. AST::Operators::ADD,
  105. AST::Identifier.new('n'),
  106. AST::Number.new(1.0)
  107. )
  108. ])
  109. ).execute(env)
  110. AST::FunctionCall.new(
  111. AST::Identifier.new('add_one'),
  112. [
  113. AST::Number.new(5)
  114. ]
  115. ).execute(env)
  116. expect do
  117. AST::FunctionCall.new(
  118. AST::Identifier.new('print'),
  119. [
  120. AST::Identifier.new('n')
  121. ]
  122. ).execute(env)
  123. end.to raise_error('Undefined variable n')
  124. end
  125. end