Skip to content

Commit

Permalink
[interpreter,spec] Convert MTValue into an alias of native types an…
Browse files Browse the repository at this point in the history
…d `MutableValue`s.

Instead of using wrapper classes for the primitive types of `TInteger`, `TFloat`, `TString`, and `TBoolean`, these values are now implemented directly as the underlying Crystal types: `Int64`, `Float64`, `String`, and `Bool` respectively. `TList` and `TMap` may also be converted over to use `Array` and `Hash` directly in the near future.

`TNil` and `TSymbol` will remain non-native types. `TNil` will remain because it would conflict in many cases with Crystal's standard library returning `nil` in many cases (this is already enough of an issue with `false`). `TSymbol` needs to remain to be able to retain the name of the Symbol at run time.

There are two main reasons for implementing this change:
- **Performance improvements.** While not overly substantial (~0.1% speed-wise in a super-quick test benchmark), the interpreter no longer needs to allocate a whole object on the heap to represent integers, floats, or booleans (strings will still be allocated on the heap). In the long run, this will help with both speed and memory.
- **Cleaner native library work.** Native library methods no longer need to wrap and unwrap values to work with them. Since these values are literally the Crystal objects themselves, they can be manipulated directly.
  • Loading branch information
faultyserver committed Feb 24, 2018
1 parent 2efe944 commit d2bcada
Show file tree
Hide file tree
Showing 36 changed files with 416 additions and 608 deletions.
12 changes: 4 additions & 8 deletions spec/interpreter/callstack_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,7 @@ describe "Interpreter - Callstack" do
it "removes the last entry from the callstack" do
itr = Interpreter.new
add_breakpoint(itr, "breakpoint") do
expected_stack_size = __args[0].as(TInteger).value
itr.callstack.size.should eq(expected_stack_size)
itr.callstack.size.should eq(__args[0])
end

parse_and_interpret! %q(
Expand All @@ -100,8 +99,7 @@ describe "Interpreter - Callstack" do
it "removes the last entry from the callstack" do
itr = Interpreter.new
add_breakpoint(itr, "breakpoint") do
expected_stack_size = __args[0].as(TInteger).value
itr.callstack.size.should eq(expected_stack_size)
itr.callstack.size.should eq(__args[0])
end

parse_and_interpret! %q(
Expand All @@ -121,8 +119,7 @@ describe "Interpreter - Callstack" do
it "removes the `block` from the callstack" do
itr = Interpreter.new
add_breakpoint(itr, "breakpoint") do
expected_stack_size = __args[0].as(TInteger).value
itr.callstack.size.should eq(expected_stack_size)
itr.callstack.size.should eq(__args[0])
end

parse_and_interpret! %q(
Expand All @@ -137,8 +134,7 @@ describe "Interpreter - Callstack" do
it "removes the `next` from the callstack" do
itr = Interpreter.new
add_breakpoint(itr, "breakpoint") do
expected_stack_size = __args[0].as(TInteger).value
itr.callstack.size.should eq(expected_stack_size)
itr.callstack.size.should eq(__args[0])
end

parse_and_interpret! %q(
Expand Down
4 changes: 2 additions & 2 deletions spec/interpreter/closure_scope_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ describe "Interpreter - ClosureScope" do
scope["b"]
end

scope["b"]?.should be(nil)
scope["b"]?.should eq(nil)
end

describe "#[]" do
Expand Down Expand Up @@ -97,7 +97,7 @@ describe "Interpreter - ClosureScope" do
scope = ClosureScope.new(Scope.new)
scope["a"] = TNil.new
scope["Thing"] = TType.new("Thing")
scope["x"] = TInteger.new(100_i64)
scope["x"] = 100_i64

scope.values.size.should eq(3)

Expand Down
4 changes: 2 additions & 2 deletions spec/interpreter/invocation_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,13 @@ TYPE_DEFS = %q(
)


private def it_invokes(prelude, call, expected)
private def it_invokes(prelude, call, expected, file=__FILE__, line=__LINE__, end_line=__END_LINE__)
itr = parse_and_interpret(prelude)
# Running the prelude will leave the last definition on the stack. For
# clarity in the tests, the stack is cleared of any existing values before
# making any assertions.
itr.stack.clear
it_interprets(call, [expected] of MTValue, itr)
it_interprets(call, [expected] of MTValue, itr, file: file, line: line, end_line: end_line)
end

describe "Interpreter - Invocation" do
Expand Down
2 changes: 1 addition & 1 deletion spec/interpreter/nodes/call_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ describe "Interpreter - Call" do
# Operators can also be defined statically to do some type algebra.
it_interprets %Q(
deftype Foo
defstatic #{op}(other : Foo)
defstatic #{op}(other)
:called_op_on_type
end
end
Expand Down
14 changes: 7 additions & 7 deletions spec/interpreter/nodes/match_assign_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ require "../../support/nodes.cr"
require "../../support/interpret.cr"

private def it_matches(match, file=__FILE__, line=__LINE__, end_line=__END_LINE__)
it %Q(matches `#{match}`) do
it %Q(matches `#{match}`), file, line, end_line do
itr = Interpreter.new
program = parse_program(match)
result = itr.run(program)
end
end

private def it_does_not_match(match, file=__FILE__, line=__LINE__, end_line=__END_LINE__)
it %Q(does not match `#{match}`) do
it %Q(does not match `#{match}`), file, line, end_line do
itr = Interpreter.new(errput: IO::Memory.new)
program = parse_program(match)

Expand Down Expand Up @@ -50,7 +50,7 @@ describe "Interpreter - MatchAssign" do
distinct_types.each_with_index do |a, i|
distinct_types.each_with_index do |b, j|
next if i == j
it_does_not_match "#{a} =: #{b}", /match/
it_does_not_match "#{a} =: #{b}"
end
end

Expand All @@ -59,8 +59,8 @@ describe "Interpreter - MatchAssign" do
it_interprets %q(1 =: 1.0)
it_interprets %q(1.0 =: 1)

it_does_not_match %q(1 =: 1.1), /match/
it_does_not_match %q(1.1 =: 1), /match/
it_does_not_match %q(1 =: 1.1)
it_does_not_match %q(1.1 =: 1)


# Assignments at any level should either create or re-assign the variable
Expand Down Expand Up @@ -122,7 +122,7 @@ describe "Interpreter - MatchAssign" do
it_does_not_match %q(
A = false
A =: true
), /match/
)

# Both styles of matching with Consts works through interpolation.
it_interprets %q(<String> =: "hello")
Expand All @@ -140,5 +140,5 @@ describe "Interpreter - MatchAssign" do
it_does_not_match %q(
float_type = 1.5.type
<float_type> =: 1
), /match/
)
end
4 changes: 2 additions & 2 deletions spec/interpreter/scope_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ describe "Interpreter - Scope" do
scope["b"]
end

scope["b"]?.should be(nil)
scope["b"]?.should eq(nil)
end

describe "#[]" do
Expand Down Expand Up @@ -71,7 +71,7 @@ describe "Interpreter - Scope" do
scope = Scope.new
scope["a"] = TNil.new
scope["Thing"] = TType.new("Thing")
scope["x"] = TInteger.new(100_i64)
scope["x"] = 100_i64

scope.values.size.should eq(3)

Expand Down
Loading

0 comments on commit d2bcada

Please sign in to comment.