123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- 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 {
- AST::FunctionCall.new(
- AST::Identifier.new('print'),
- [AST::Identifier.new('n')]
- )
- .execute(env)
- }.to raise_error('Undefined variable n')
- end
-
- # let add_one = function(n) { n + 1; };
- # let do = function(f, n) { f(n); };
- # do(5);
- it 'executes higher order functions' do
- env = Environment.new
- AST::VariableDeclaration.new(
- AST::Identifier.new('add_one'),
- AST::FunctionDefinition.new(
- nil,
- [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::VariableDeclaration.new(
- AST::Identifier.new('do'),
- AST::FunctionDefinition.new(
- nil,
- [AST::Identifier.new('f'), AST::Identifier.new('x')],
- AST::Block.new(
- [
- AST::FunctionCall.new(
- AST::Identifier.new('f'),
- [AST::Identifier.new('x')]
- )
- ]
- )
- )
- )
- .execute(env)
-
- result =
- AST::FunctionCall.new(
- AST::Identifier.new('do'),
- [AST::Identifier.new('add_one'), AST::Number.new(5.0)]
- )
- .execute(env)
-
- expect(result).to eq(6)
- end
- end
|