RSpec.describe AST::FunctionCall do it 'evaluates built-in functions' do expect { AST::FunctionCall.new( AST::Identifier.new('print'), [AST::String.new('hello world')] ).execute(Environment.new) }.to output("hello world\n").to_stdout end it 'evaluates user-defined functions' do env = Environment.new AST::FunctionDefinition.new( AST::Identifier.new('add_one'), [AST::Identifier.new('n')], AST::Block.new([ AST::Binary.new( AST::Operators::ADD, AST::Identifier.new('n'), AST::Number.new(1.0) ) ]) ).execute(env) expect( AST::FunctionCall.new( AST::Identifier.new('add_one'), [AST::Number.new(5.0)] ).execute(env) ).to eq(6.0) end # This corresponds to the program # function factorial(n) { # if n == 0 { # 1; # } else { # n * factorial(n - 1); # } # } # factorial(5); it 'evaluates recursive functions' do env = Environment.new AST::FunctionDefinition.new( AST::Identifier.new('factorial'), [AST::Identifier.new('n')], AST::Block.new([ AST::Conditional.new( [ AST::Branch.new( AST::Binary.new( AST::Operators::DOUBLE_EQUALS, AST::Identifier.new('n'), AST::Number.new(0.0), ), AST::Block.new([ AST::Number.new(1.0), ]) ), AST::Branch.new( AST::Boolean.new(true), AST::Block.new([ AST::Binary.new( AST::Operators::MULTIPLY, AST::Identifier.new('n'), AST::FunctionCall.new( AST::Identifier.new('factorial'), [ AST::Binary.new( AST::Operators::SUBTRACT, AST::Identifier.new('n'), AST::Number.new(1.0), ) ] ) ) ]) ), ] ) ]) ).execute(env) expect( AST::FunctionCall.new( AST::Identifier.new('factorial'), [AST::Number.new(5.0)] ).execute(env) ).to eq(120.0) end # This is equivalent to the program # # function add_one(n) { # n + 1; # } # add_one(5); # print(n); # # This test is ensuring that `n` is not still defined outside the scope of # add_one after the function finishes executing it 'destroys variables when they go out of scope' do env = Environment.new AST::FunctionDefinition.new( AST::Identifier.new('add_one'), [AST::Identifier.new('n')], AST::Block.new([ AST::Binary.new( AST::Operators::ADD, AST::Identifier.new('n'), AST::Number.new(1.0) ) ]) ).execute(env) AST::FunctionCall.new( AST::Identifier.new('add_one'), [ AST::Number.new(5) ] ).execute(env) expect do AST::FunctionCall.new( AST::Identifier.new('print'), [ AST::Identifier.new('n') ] ).execute(env) end.to raise_error('Undefined variable n') end end