From 2efe9443780500c73cc08669ef4a2080f22b2352 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 24 Feb 2018 13:43:44 -0500 Subject: [PATCH 1/2] [interpreter,spec] Rename `Value` to `MTValue`. In the past, collision between the Myst-level type `Value` and Crystal's native `Value` class has been cumbersome (requiring namespacing as `Myst::Value`). Renaming to `MTValue` clears that confusion, and makes it more obvious where we're talking about Myst values. --- spec/interpreter/invocation_spec.cr | 2 +- spec/interpreter/nodes/interpolation_spec.cr | 4 +- spec/interpreter/nodes/literals_spec.cr | 4 +- spec/interpreter/value_spec.cr | 32 +++++++-------- spec/support/breakpoints.cr | 4 +- spec/support/interpret.cr | 14 +++---- src/myst/interpreter.cr | 12 +++--- src/myst/interpreter/closure_scope.cr | 6 +-- src/myst/interpreter/exceptions.cr | 4 +- src/myst/interpreter/invocation.cr | 8 ++-- src/myst/interpreter/matcher.cr | 8 ++-- src/myst/interpreter/native_lib.cr | 14 +++---- src/myst/interpreter/native_lib/boolean.cr | 4 +- src/myst/interpreter/native_lib/float.cr | 22 +++++----- src/myst/interpreter/native_lib/integer.cr | 22 +++++----- src/myst/interpreter/native_lib/io.cr | 4 +- src/myst/interpreter/native_lib/list.cr | 6 +-- src/myst/interpreter/native_lib/map.cr | 8 ++-- src/myst/interpreter/native_lib/nil.cr | 4 +- src/myst/interpreter/native_lib/random.cr | 8 ++-- src/myst/interpreter/native_lib/string.cr | 12 +++--- src/myst/interpreter/native_lib/symbol.cr | 4 +- src/myst/interpreter/native_lib/time.cr | 8 ++-- src/myst/interpreter/native_lib/top_level.cr | 6 +-- src/myst/interpreter/nodes/call.cr | 6 +-- src/myst/interpreter/nodes/def.cr | 2 +- .../interpreter/nodes/exception_handler.cr | 2 +- src/myst/interpreter/nodes/literals.cr | 10 ++--- src/myst/interpreter/nodes/unary_ops.cr | 6 +-- src/myst/interpreter/scope.cr | 12 +++--- src/myst/interpreter/util.cr | 12 +++--- src/myst/interpreter/value.cr | 40 +++++++++---------- 32 files changed, 155 insertions(+), 155 deletions(-) diff --git a/spec/interpreter/invocation_spec.cr b/spec/interpreter/invocation_spec.cr index 6ba594f..0546ede 100644 --- a/spec/interpreter/invocation_spec.cr +++ b/spec/interpreter/invocation_spec.cr @@ -88,7 +88,7 @@ private def it_invokes(prelude, call, expected) # clarity in the tests, the stack is cleared of any existing values before # making any assertions. itr.stack.clear - it_interprets(call, [expected] of Myst::Value, itr) + it_interprets(call, [expected] of MTValue, itr) end describe "Interpreter - Invocation" do diff --git a/spec/interpreter/nodes/interpolation_spec.cr b/spec/interpreter/nodes/interpolation_spec.cr index 7fed289..1c715c1 100644 --- a/spec/interpreter/nodes/interpolation_spec.cr +++ b/spec/interpreter/nodes/interpolation_spec.cr @@ -17,11 +17,11 @@ describe "Interpreter - Interpolation" do it_interprets %q(<[1]>), [TList.new([val(1)])] it_interprets %q(<[1, 2, 3]>), [TList.new([val(1), val(2), val(3)])] it_interprets %q(<[nil, :hi]>), [TList.new([val(nil), val(:hi)])] - it_interprets %q(<[[1, 2], [3, 4]]>), [TList.new([TList.new([val(1), val(2)]), TList.new([val(3), val(4)])] of Myst::Value)] + it_interprets %q(<[[1, 2], [3, 4]]>), [TList.new([TList.new([val(1), val(2)]), TList.new([val(3), val(4)])] of MTValue)] it_interprets %q(<{a: 1 }>), [TMap.new({ val(:a) => val(1) })] it_interprets %q(<{<1>: "int", : :nil }>), [TMap.new({ val(1) => val("int"), val(nil) => val(:nil) })] - it_interprets %q(<{<{a: 1}>: {b: 2}}>), [TMap.new({ TMap.new({ val(:a) => val(1) }) => TMap.new({ val(:b) => val(2) }) } of Myst::Value => Myst::Value)] + it_interprets %q(<{<{a: 1}>: {b: 2}}>), [TMap.new({ TMap.new({ val(:a) => val(1) }) => TMap.new({ val(:b) => val(2) }) } of MTValue => MTValue)] it_interprets %q(<(true && true)>), [val(true)] it_interprets %q(<(false && true)>), [val(false)] diff --git a/spec/interpreter/nodes/literals_spec.cr b/spec/interpreter/nodes/literals_spec.cr index af7e31f..a7986e6 100644 --- a/spec/interpreter/nodes/literals_spec.cr +++ b/spec/interpreter/nodes/literals_spec.cr @@ -17,9 +17,9 @@ describe "Interpreter - Literals" do it_interprets %q([1]), [TList.new([val(1)])] it_interprets %q([1, 2, 3]), [TList.new([val(1), val(2), val(3)])] it_interprets %q([nil, :hi]), [TList.new([val(nil), val(:hi)])] - it_interprets %q([[1, 2], [3, 4]]), [TList.new([TList.new([val(1), val(2)]), TList.new([val(3), val(4)])] of Myst::Value)] + it_interprets %q([[1, 2], [3, 4]]), [TList.new([TList.new([val(1), val(2)]), TList.new([val(3), val(4)])] of MTValue)] it_interprets %q({a: 1}), [TMap.new({ val(:a) => val(1) })] it_interprets %q({<1>: "int", : :nil}), [TMap.new({ val(1) => val("int"), val(nil) => val(:nil) })] - it_interprets %q({<{a: 1}>: {b: 2}}), [TMap.new({ TMap.new({ val(:a) => val(1) }) => TMap.new({ val(:b) => val(2) }) } of Myst::Value => Myst::Value)] + it_interprets %q({<{a: 1}>: {b: 2}}), [TMap.new({ TMap.new({ val(:a) => val(1) }) => TMap.new({ val(:b) => val(2) }) } of MTValue => MTValue)] end diff --git a/spec/interpreter/value_spec.cr b/spec/interpreter/value_spec.cr index 1f46180..b7fb4d4 100644 --- a/spec/interpreter/value_spec.cr +++ b/spec/interpreter/value_spec.cr @@ -3,38 +3,38 @@ require "../spec_helper.cr" describe "Values" do describe "::from_literal" do it "maps NilLiteral to TNil" do - Myst::Value.from_literal(NilLiteral.new).should be_a(TNil) + MTValue.from_literal(NilLiteral.new).should be_a(TNil) end it "maps BooleanLiteral to TBoolean" do - Myst::Value.from_literal(BooleanLiteral.new(false)).should be_a(TBoolean) + MTValue.from_literal(BooleanLiteral.new(false)).should be_a(TBoolean) end it "maps IntegerLiteral to TInteger" do - Myst::Value.from_literal(IntegerLiteral.new("0")).should be_a(TInteger) + MTValue.from_literal(IntegerLiteral.new("0")).should be_a(TInteger) end it "maps FloatLiteral to TFloat" do - Myst::Value.from_literal(FloatLiteral.new("0.0")).should be_a(TFloat) + MTValue.from_literal(FloatLiteral.new("0.0")).should be_a(TFloat) end it "maps StringLiteral to TString" do - Myst::Value.from_literal(StringLiteral.new("hello")).should be_a(TString) + MTValue.from_literal(StringLiteral.new("hello")).should be_a(TString) end it "maps SymbolLiteral to TSymbol" do - Myst::Value.from_literal(SymbolLiteral.new("hi")).should be_a(TSymbol) + MTValue.from_literal(SymbolLiteral.new("hi")).should be_a(TSymbol) end # Container values like List and Map require some effort from the # interpreter to be generated. As such, Value::from_literal cannot generate # them automatically from a node. it "does not map ListLiterals" do - expect_raises(Exception) { Myst::Value.from_literal(ListLiteral.new).should be_a(TList) } + expect_raises(Exception) { MTValue.from_literal(ListLiteral.new).should be_a(TList) } end it "does not map MapLiterals" do - expect_raises(Exception) { Myst::Value.from_literal(MapLiteral.new).should be_a(TMap) } + expect_raises(Exception) { MTValue.from_literal(MapLiteral.new).should be_a(TMap) } end end @@ -239,7 +239,7 @@ hi end it "can be created with initial elements" do - TList.new([TNil.new, TNil.new] of Myst::Value) + TList.new([TNil.new, TNil.new] of MTValue) end it "can contain any mixture of Values" do @@ -247,7 +247,7 @@ hi end it "can contain other lists within itself" do - TList.new([TList.new, TList.new] of Myst::Value) + TList.new([TList.new, TList.new] of MTValue) end it "can dynamically adjust its size" do @@ -260,8 +260,8 @@ hi it "is always truthy" do TList.new.truthy?.should eq(true) - TList.new([TNil.new] of Myst::Value).truthy?.should eq(true) - TList.new([TBoolean.new(false)] of Myst::Value).truthy?.should eq(true) + TList.new([TNil.new] of MTValue).truthy?.should eq(true) + TList.new([TBoolean.new(false)] of MTValue).truthy?.should eq(true) TList.new([TInteger.new(1_i64), TNil.new]).truthy?.should eq(true) end end @@ -273,7 +273,7 @@ hi end it "can be created with initial elements" do - TMap.new({ TNil.new => TNil.new } of Myst::Value => Myst::Value) + TMap.new({ TNil.new => TNil.new } of MTValue => MTValue) end it "can contain any mixture of Values" do @@ -281,7 +281,7 @@ hi end it "can contain other maps within itself" do - TMap.new({ TMap.new => TMap.new } of Myst::Value => Myst::Value) + TMap.new({ TMap.new => TMap.new } of MTValue => MTValue) end it "can dynamically adjust its size" do @@ -294,8 +294,8 @@ hi it "is always truthy" do TMap.new.truthy?.should eq(true) - TMap.new({ TNil.new => TNil.new } of Myst::Value => Myst::Value).truthy?.should eq(true) - TMap.new({ TSymbol.new("") => TInteger.new(1_i64) } of Myst::Value => Myst::Value).truthy?.should eq(true) + TMap.new({ TNil.new => TNil.new } of MTValue => MTValue).truthy?.should eq(true) + TMap.new({ TSymbol.new("") => TInteger.new(1_i64) } of MTValue => MTValue).truthy?.should eq(true) end end end diff --git a/spec/support/breakpoints.cr b/spec/support/breakpoints.cr index b62325f..e7a3a37 100644 --- a/spec/support/breakpoints.cr +++ b/spec/support/breakpoints.cr @@ -28,12 +28,12 @@ require "../spec_helper.cr" # end # ), interpreter: itr macro add_breakpoint(itr, name) - %handler = ->(this : Myst::Value, __args : Array(Myst::Value), block : TFunctor?) do + %handler = ->(this : MTValue, __args : Array(MTValue), block : TFunctor?) do %result = begin {{ yield }} end - %result.is_a?(Myst::Value) ? %result : TNil.new.as(Myst::Value) + %result.is_a?(MTValue) ? %result : TNil.new.as(MTValue) end {{itr}}.kernel.scope[{{name}}] = TFunctor.new({{name}}, [%handler] of Callable) diff --git a/spec/support/interpret.cr b/spec/support/interpret.cr index 6dc0855..47614c8 100644 --- a/spec/support/interpret.cr +++ b/spec/support/interpret.cr @@ -1,7 +1,7 @@ require "../spec_helper.cr" require "./nodes.cr" -def it_interprets(node : String, expected_stack : Array(Myst::Value), itr=Interpreter.new, file=__FILE__, line=__LINE__, end_line=__END_LINE__) +def it_interprets(node : String, expected_stack : Array(MTValue), itr=Interpreter.new, file=__FILE__, line=__LINE__, end_line=__END_LINE__) it %Q(interprets #{node}), file, line, end_line do program = parse_program(node) itr.run(program) @@ -31,11 +31,11 @@ def it_interprets(node : String, file=__FILE__, line=__LINE__, end_line=__END_LI end def it_interprets(node : String, file=__FILE__, line=__LINE__, end_line=__END_LINE__) - it_interprets(node, [] of Myst::Value, Interpreter.new, file, line, end_line) + it_interprets(node, [] of MTValue, Interpreter.new, file, line, end_line) end -def it_interprets_with_assignments(node : String, assignments : Hash(String, Myst::Value), itr=Interpreter.new, file=__FILE__, line=__LINE__, end_line=__END_LINE__) +def it_interprets_with_assignments(node : String, assignments : Hash(String, MTValue), itr=Interpreter.new, file=__FILE__, line=__LINE__, end_line=__END_LINE__) it %Q(interprets #{node}), file, line, end_line do program = parse_program(node) itr.run(program) @@ -99,21 +99,21 @@ end # val(node) # -# Run `Value.from_literal` on the given node and return the result. If `node` +# Run `MTValue.from_literal` on the given node and return the result. If `node` # is not already a Node, it will be run through `l` first. def val(node : Node) - Myst::Value.from_literal(node).as(Myst::Value) + MTValue.from_literal(node).as(MTValue) end def val(node : Array(T)) forall T - TList.new(node.map{ |n| val(n) }).as(Myst::Value) + TList.new(node.map{ |n| val(n) }).as(MTValue) end def val(node : Hash(K, V)) forall K, V node.reduce(TMap.new) do |map, (k, v)| map.entries[val(k)] = val(v) map - end.as(Myst::Value) + end.as(MTValue) end def val(node); val(l(node)); end diff --git a/src/myst/interpreter.cr b/src/myst/interpreter.cr index 17a2e9a..2da3608 100644 --- a/src/myst/interpreter.cr +++ b/src/myst/interpreter.cr @@ -4,8 +4,8 @@ require "./interpreter/native_lib" module Myst class Interpreter - property stack : Array(Value) - property self_stack : Array(Value) + property stack : Array(MTValue) + property self_stack : Array(MTValue) property scope_stack : Array(Scope) property callstack : Callstack property kernel : TModule @@ -22,11 +22,11 @@ module Myst 2 => errput }) - @stack = [] of Value + @stack = [] of MTValue @scope_stack = [] of Scope @callstack = Callstack.new @kernel = create_kernel - @self_stack = [@kernel] of Value + @self_stack = [@kernel] of MTValue @warnings = 0 end @@ -85,7 +85,7 @@ module Myst self_stack.last end - def push_self(new_self : Value) + def push_self(new_self : MTValue) self_stack.push(new_self) end @@ -119,7 +119,7 @@ module Myst def put_error(error : RuntimeError) value_to_s = __scopeof(error.value)["to_s"].as(TFunctor) - result = Invocation.new(self, value_to_s, error.value, [] of Value, nil).invoke + result = Invocation.new(self, value_to_s, error.value, [] of MTValue, nil).invoke errput.puts("Uncaught Exception: " + result.as(TString).value) errput.puts(error.trace) end diff --git a/src/myst/interpreter/closure_scope.cr b/src/myst/interpreter/closure_scope.cr index acc162f..7f02fa8 100644 --- a/src/myst/interpreter/closure_scope.cr +++ b/src/myst/interpreter/closure_scope.cr @@ -9,7 +9,7 @@ module Myst property closed_scope : Scope def initialize(@closed_scope : Scope, @parent : Scope? = nil) - @values = {} of String => Value + @values = {} of String => MTValue end def []?(key : String) @@ -20,7 +20,7 @@ module Myst end end - def []=(key : String, value : Value) + def []=(key : String, value : MTValue) if closed_scope.has_key?(key) closed_scope.assign(key, value) else @@ -32,7 +32,7 @@ module Myst !!@values[key]? || closed_scope.has_key?(key) end - def assign(key : String, value : Value) + def assign(key : String, value : MTValue) if closed_scope.has_key?(key) closed_scope.assign(key, value) else diff --git a/src/myst/interpreter/exceptions.cr b/src/myst/interpreter/exceptions.cr index b7b7760..2b9aa34 100644 --- a/src/myst/interpreter/exceptions.cr +++ b/src/myst/interpreter/exceptions.cr @@ -14,10 +14,10 @@ module Myst # The containing error type for any error raised within the language. class RuntimeError < Exception - property value : Value + property value : MTValue property trace : Callstack - def initialize(@value : Value, @trace : Callstack) + def initialize(@value : MTValue, @trace : Callstack) end end diff --git a/src/myst/interpreter/invocation.cr b/src/myst/interpreter/invocation.cr index adefe05..5b9e08d 100644 --- a/src/myst/interpreter/invocation.cr +++ b/src/myst/interpreter/invocation.cr @@ -11,14 +11,14 @@ module Myst struct Invocation property itr : Interpreter property func : TFunctor - property! receiver : Value? - property args : Array(Value) + property! receiver : MTValue? + property args : Array(MTValue) property! block : TFunctor? @selfstack_size_at_entry : Int32 = -1 @scopestack_size_at_entry : Int32 = -1 @callstack_size_at_entry : Int32 = -1 - def initialize(@itr : Interpreter, @func : TFunctor, @receiver : Value?, @args : Array(Value), @block : TFunctor?) + def initialize(@itr : Interpreter, @func : TFunctor, @receiver : MTValue?, @args : Array(MTValue), @block : TFunctor?) end def invoke @@ -113,7 +113,7 @@ module Myst return @itr.stack.pop end - private def do_call(func : TNativeDef, receiver : Value, args : Array(Value), block : TFunctor?) + private def do_call(func : TNativeDef, receiver : MTValue, args : Array(MTValue), block : TFunctor?) func.call(receiver, args, block) end diff --git a/src/myst/interpreter/matcher.cr b/src/myst/interpreter/matcher.cr index 110b53c..4dcdf83 100644 --- a/src/myst/interpreter/matcher.cr +++ b/src/myst/interpreter/matcher.cr @@ -2,7 +2,7 @@ require "./exceptions.cr" module Myst class Interpreter - def match(pattern : Node, value : Value) + def match(pattern : Node, value : MTValue) case pattern when ListLiteral match_list(pattern, value) @@ -31,7 +31,7 @@ module Myst # For simplicity and efficiency, the equality of values according to a # match operation is determined by the native equality of the values, not # by any override of `==`. - private def match_value(pattern : Node, right : Value) + private def match_value(pattern : Node, right : MTValue) visit(pattern) left = stack.pop success = @@ -51,7 +51,7 @@ module Myst success || __raise_runtime_error(MatchError.new(callstack)) end - private def match_list(pattern : ListLiteral, value : Value) + private def match_list(pattern : ListLiteral, value : MTValue) __raise_runtime_error(MatchError.new(callstack)) unless value.is_a?(TList) left, splat, right = chunk_list_pattern(pattern) @@ -68,7 +68,7 @@ module Myst end end - private def match_map(pattern : MapLiteral, value : Value) + private def match_map(pattern : MapLiteral, value : MTValue) __raise_runtime_error(MatchError.new(callstack)) unless value.is_a?(TMap) pattern.entries.each do |entry| diff --git a/src/myst/interpreter/native_lib.cr b/src/myst/interpreter/native_lib.cr index 06506a6..3259820 100644 --- a/src/myst/interpreter/native_lib.cr +++ b/src/myst/interpreter/native_lib.cr @@ -6,19 +6,19 @@ module Myst # function. The function can be either a native function or a source-level # function. The results do not affect the stack, the result of calling the # function will be returned directly. - def call_func(itr, func : TFunctor, args : Array(Value), receiver : Value?=nil) + def call_func(itr, func : TFunctor, args : Array(MTValue), receiver : MTValue?=nil) Invocation.new(itr, func, receiver, args, nil).invoke end # Same as `call_func`, but the function to call is given as a name to # look up on the given receiver. - def call_func_by_name(itr, receiver : Value, name : String, args : Array(Value)) + def call_func_by_name(itr, receiver : MTValue, name : String, args : Array(MTValue)) func = itr.__scopeof(receiver)[name].as(TFunctor) Invocation.new(itr, func, receiver, args, nil).invoke end # Instantiate a given type and invoke its initializer - def instantiate(itr, type : TType, params : Array(Value)) : TInstance + def instantiate(itr, type : TType, params : Array(MTValue)) : TInstance instance = TInstance.new(type) if (initializer = instance.scope["initialize"]?) && initializer.is_a?(TFunctor) @@ -29,7 +29,7 @@ module Myst end macro method(name, this_type, *params, &block) - def {{name.id}}(this : Value, __args : Array(Value), block : TFunctor?) : Value + def {{name.id}}(this : MTValue, __args : Array(MTValue), block : TFunctor?) : MTValue this = this.as({{this_type}}) {% for type, index in params %} @@ -40,19 +40,19 @@ module Myst {{block.body}} end - %result.as(Value) + %result.as(MTValue) end end macro def_method(type, name, impl_name) {{type}}.scope["{{name.id}}"] = TFunctor.new("{{name.id}}", [ - ->{{impl_name.id}}(Value, Array(Value), TFunctor?).as(Callable) + ->{{impl_name.id}}(MTValue, Array(MTValue), TFunctor?).as(Callable) ] of Callable) end macro def_instance_method(type, name, impl_name) {{type}}.instance_scope["{{name.id}}"] = TFunctor.new("{{name.id}}", [ - ->{{impl_name.id}}(Value, Array(Value), TFunctor?).as(Callable) + ->{{impl_name.id}}(MTValue, Array(MTValue), TFunctor?).as(Callable) ] of Callable) end end diff --git a/src/myst/interpreter/native_lib/boolean.cr b/src/myst/interpreter/native_lib/boolean.cr index ce3eefb..6613059 100644 --- a/src/myst/interpreter/native_lib/boolean.cr +++ b/src/myst/interpreter/native_lib/boolean.cr @@ -4,7 +4,7 @@ module Myst TString.new(this.value ? "true" : "false") end - NativeLib.method :bool_eq, TBoolean, other : Value do + NativeLib.method :bool_eq, TBoolean, other : MTValue do case other when TBoolean TBoolean.new(this.value == other.value) @@ -13,7 +13,7 @@ module Myst end end - NativeLib.method :bool_not_eq, TBoolean, other : Value do + NativeLib.method :bool_not_eq, TBoolean, other : MTValue do case other when TBoolean TBoolean.new(this.value != other.value) diff --git a/src/myst/interpreter/native_lib/float.cr b/src/myst/interpreter/native_lib/float.cr index 52455ce..1d499db 100644 --- a/src/myst/interpreter/native_lib/float.cr +++ b/src/myst/interpreter/native_lib/float.cr @@ -1,6 +1,6 @@ module Myst class Interpreter - NativeLib.method :float_add, TFloat, other : Value do + NativeLib.method :float_add, TFloat, other : MTValue do case other when TInteger, TFloat TFloat.new(this.value + other.value) @@ -9,7 +9,7 @@ module Myst end end - NativeLib.method :float_subtract, TFloat, other : Value do + NativeLib.method :float_subtract, TFloat, other : MTValue do case other when TInteger, TFloat TFloat.new(this.value - other.value) @@ -18,7 +18,7 @@ module Myst end end - NativeLib.method :float_multiply, TFloat, other : Value do + NativeLib.method :float_multiply, TFloat, other : MTValue do case other when TInteger, TFloat TFloat.new(this.value * other.value) @@ -27,7 +27,7 @@ module Myst end end - NativeLib.method :float_divide, TFloat, other : Value do + NativeLib.method :float_divide, TFloat, other : MTValue do case other when TInteger, TFloat __raise_runtime_error("Division by zero") if other.value == 0 @@ -37,7 +37,7 @@ module Myst end end - NativeLib.method :float_modulo, TFloat, other : Value do + NativeLib.method :float_modulo, TFloat, other : MTValue do case other when TInteger, TFloat __raise_runtime_error("Division by zero") if other.value == 0 @@ -51,7 +51,7 @@ module Myst TString.new(this.value.to_s) end - NativeLib.method :float_eq, TFloat, other : Value do + NativeLib.method :float_eq, TFloat, other : MTValue do case other when TFloat, TInteger TBoolean.new(this.value == other.value) @@ -60,7 +60,7 @@ module Myst end end - NativeLib.method :float_not_eq, TFloat, other : Value do + NativeLib.method :float_not_eq, TFloat, other : MTValue do case other when TFloat, TInteger TBoolean.new(this.value != other.value) @@ -73,7 +73,7 @@ module Myst TFloat.new(-this.value) end - NativeLib.method :float_lt, TFloat, other : Value do + NativeLib.method :float_lt, TFloat, other : MTValue do case other when TInteger, TFloat TBoolean.new(this.value < other.value) @@ -82,7 +82,7 @@ module Myst end end - NativeLib.method :float_lte, TFloat, other : Value do + NativeLib.method :float_lte, TFloat, other : MTValue do case other when TInteger, TFloat TBoolean.new(this.value <= other.value) @@ -91,7 +91,7 @@ module Myst end end - NativeLib.method :float_gt, TFloat, other : Value do + NativeLib.method :float_gt, TFloat, other : MTValue do case other when TInteger, TFloat TBoolean.new(this.value > other.value) @@ -100,7 +100,7 @@ module Myst end end - NativeLib.method :float_gte, TFloat, other : Value do + NativeLib.method :float_gte, TFloat, other : MTValue do case other when TInteger, TFloat TBoolean.new(this.value >= other.value) diff --git a/src/myst/interpreter/native_lib/integer.cr b/src/myst/interpreter/native_lib/integer.cr index d29cec4..4b7eef3 100644 --- a/src/myst/interpreter/native_lib/integer.cr +++ b/src/myst/interpreter/native_lib/integer.cr @@ -1,6 +1,6 @@ module Myst class Interpreter - NativeLib.method :int_add, TInteger, other : Value do + NativeLib.method :int_add, TInteger, other : MTValue do case other when TInteger TInteger.new(this.value + other.value) @@ -11,7 +11,7 @@ module Myst end end - NativeLib.method :int_subtract, TInteger, other : Value do + NativeLib.method :int_subtract, TInteger, other : MTValue do case other when TInteger TInteger.new(this.value - other.value) @@ -22,7 +22,7 @@ module Myst end end - NativeLib.method :int_multiply, TInteger, other : Value do + NativeLib.method :int_multiply, TInteger, other : MTValue do case other when TInteger TInteger.new(this.value * other.value) @@ -33,7 +33,7 @@ module Myst end end - NativeLib.method :int_divide, TInteger, other : Value do + NativeLib.method :int_divide, TInteger, other : MTValue do case other when TInteger __raise_runtime_error("Division by zero") if other.value == 0 @@ -46,7 +46,7 @@ module Myst end end - NativeLib.method :int_modulo, TInteger, other : Value do + NativeLib.method :int_modulo, TInteger, other : MTValue do case other when TInteger __raise_runtime_error("Division by zero") if other.value == 0 @@ -63,7 +63,7 @@ module Myst TString.new(this.value.to_s) end - NativeLib.method :int_eq, TInteger, other : Value do + NativeLib.method :int_eq, TInteger, other : MTValue do case other when TInteger, TFloat TBoolean.new(this.value == other.value) @@ -72,7 +72,7 @@ module Myst end end - NativeLib.method :int_not_eq, TInteger, other : Value do + NativeLib.method :int_not_eq, TInteger, other : MTValue do case other when TInteger, TFloat TBoolean.new(this.value != other.value) @@ -85,7 +85,7 @@ module Myst TInteger.new(-this.value) end - NativeLib.method :int_lt, TInteger, other : Value do + NativeLib.method :int_lt, TInteger, other : MTValue do case other when TInteger, TFloat TBoolean.new(this.value < other.value) @@ -94,7 +94,7 @@ module Myst end end - NativeLib.method :int_lte, TInteger, other : Value do + NativeLib.method :int_lte, TInteger, other : MTValue do case other when TInteger, TFloat TBoolean.new(this.value <= other.value) @@ -103,7 +103,7 @@ module Myst end end - NativeLib.method :int_gt, TInteger, other : Value do + NativeLib.method :int_gt, TInteger, other : MTValue do case other when TInteger, TFloat TBoolean.new(this.value > other.value) @@ -112,7 +112,7 @@ module Myst end end - NativeLib.method :int_gte, TInteger, other : Value do + NativeLib.method :int_gte, TInteger, other : MTValue do case other when TInteger, TFloat TBoolean.new(this.value >= other.value) diff --git a/src/myst/interpreter/native_lib/io.cr b/src/myst/interpreter/native_lib/io.cr index ad13901..c63be3f 100644 --- a/src/myst/interpreter/native_lib/io.cr +++ b/src/myst/interpreter/native_lib/io.cr @@ -1,10 +1,10 @@ module Myst class Interpreter - NativeLib.method :io_read, Value, size : TInteger do + NativeLib.method :io_read, MTValue, size : TInteger do __raise_runtime_error("`IO#read` must be implemented by inheriting types.") end - NativeLib.method :io_write, Value, content : Value do + NativeLib.method :io_write, MTValue, content : MTValue do __raise_runtime_error("`IO#write` must be implemented by inheriting types.") end diff --git a/src/myst/interpreter/native_lib/list.cr b/src/myst/interpreter/native_lib/list.cr index beca4f7..1571895 100644 --- a/src/myst/interpreter/native_lib/list.cr +++ b/src/myst/interpreter/native_lib/list.cr @@ -18,7 +18,7 @@ module Myst this end - NativeLib.method :list_eq, TList, other : Value do + NativeLib.method :list_eq, TList, other : MTValue do return TBoolean.new(false) unless other.is_a?(TList) return TBoolean.new(true) if this == other return TBoolean.new(false) if this.elements.size != other.elements.size @@ -30,7 +30,7 @@ module Myst TBoolean.new(true) end - NativeLib.method :list_not_eq, TList, other : Value do + NativeLib.method :list_not_eq, TList, other : MTValue do return TBoolean.new(true) unless other.is_a?(TList) return TBoolean.new(false) if this == other return TBoolean.new(true) if this.elements.size != other.elements.size @@ -54,7 +54,7 @@ module Myst end end - NativeLib.method :list_access_assign, TList, index : TInteger, value : Value do + NativeLib.method :list_access_assign, TList, index : TInteger, value : MTValue do this.ensure_capacity(index.value + 1) this.elements[index.value] = value end diff --git a/src/myst/interpreter/native_lib/map.cr b/src/myst/interpreter/native_lib/map.cr index db794a6..6246594 100644 --- a/src/myst/interpreter/native_lib/map.cr +++ b/src/myst/interpreter/native_lib/map.cr @@ -18,7 +18,7 @@ module Myst TMap.new(this.entries.merge(other.entries)) end - NativeLib.method :map_eq, TMap, other : Value do + NativeLib.method :map_eq, TMap, other : MTValue do return TBoolean.new(false) unless other.is_a?(TMap) return TBoolean.new(true) if this == other return TBoolean.new(false) if this.entries.size != other.entries.size @@ -35,7 +35,7 @@ module Myst TBoolean.new(true) end - NativeLib.method :map_not_eq, TMap, other : Value do + NativeLib.method :map_not_eq, TMap, other : MTValue do return TBoolean.new(true) unless other.is_a?(TMap) return TBoolean.new(false) if this == other return TBoolean.new(true) if this.entries.size != other.entries.size @@ -52,11 +52,11 @@ module Myst TBoolean.new(true) end - NativeLib.method :map_access, TMap, index : Value do + NativeLib.method :map_access, TMap, index : MTValue do this.entries[index]? || TNil.new end - NativeLib.method :map_access_assign, TMap, index : Value, value : Value do + NativeLib.method :map_access_assign, TMap, index : MTValue, value : MTValue do this.entries[index] = value end diff --git a/src/myst/interpreter/native_lib/nil.cr b/src/myst/interpreter/native_lib/nil.cr index bdf3bdf..0f9b244 100644 --- a/src/myst/interpreter/native_lib/nil.cr +++ b/src/myst/interpreter/native_lib/nil.cr @@ -4,7 +4,7 @@ module Myst TString.new("") end - NativeLib.method :nil_eq, TNil, other : Value do + NativeLib.method :nil_eq, TNil, other : MTValue do case other when TNil TBoolean.new(true) @@ -13,7 +13,7 @@ module Myst end end - NativeLib.method :nil_not_eq, TNil, other : Value do + NativeLib.method :nil_not_eq, TNil, other : MTValue do case other when TNil TBoolean.new(false) diff --git a/src/myst/interpreter/native_lib/random.cr b/src/myst/interpreter/native_lib/random.cr index 699f528..e718384 100644 --- a/src/myst/interpreter/native_lib/random.cr +++ b/src/myst/interpreter/native_lib/random.cr @@ -1,7 +1,7 @@ module Myst - class Interpreter - NativeLib.method :random_rand, TModule, max : Value? = nil do - if max.is_a? TInteger + class Interpreter + NativeLib.method :random_rand, TModule, max : MTValue? = nil do + if max.is_a? TInteger TInteger.new(rand(max.value)) elsif max.is_a? TFloat TFloat.new(rand(max.value)) @@ -13,7 +13,7 @@ module Myst def init_random(kernel : TModule) random_module = TModule.new("Random", kernel.scope) - NativeLib.def_method(random_module, :rand, :random_rand) + NativeLib.def_method(random_module, :rand, :random_rand) random_module end diff --git a/src/myst/interpreter/native_lib/string.cr b/src/myst/interpreter/native_lib/string.cr index 58f314d..7174b75 100644 --- a/src/myst/interpreter/native_lib/string.cr +++ b/src/myst/interpreter/native_lib/string.cr @@ -1,6 +1,6 @@ module Myst class Interpreter - NativeLib.method :string_add, TString, other : Value do + NativeLib.method :string_add, TString, other : MTValue do case other when TString TString.new(this.value + other.value) @@ -9,7 +9,7 @@ module Myst end end - NativeLib.method :string_multiply, TString, other : Value do + NativeLib.method :string_multiply, TString, other : MTValue do case other when TInteger # String multiplication repeats `this` `arg` times. @@ -23,7 +23,7 @@ module Myst this.as(TString) end - NativeLib.method :string_eq, TString, other : Value do + NativeLib.method :string_eq, TString, other : MTValue do case other when TString TBoolean.new(this.value == other.value) @@ -32,7 +32,7 @@ module Myst end end - NativeLib.method :string_not_eq, TString, other : Value do + NativeLib.method :string_not_eq, TString, other : MTValue do case other when TString TBoolean.new(this.value != other.value) @@ -50,7 +50,7 @@ module Myst delim_arg.value end - TList.new(this.value.split(delimiter).map{ |s| TString.new(s).as(Value) }) + TList.new(this.value.split(delimiter).map{ |s| TString.new(s).as(MTValue) }) end NativeLib.method :string_size, TString do @@ -58,7 +58,7 @@ module Myst end NativeLib.method :string_chars, TString do - TList.new(this.value.chars.map { |c| TString.new(c.to_s).as Value }) + TList.new(this.value.chars.map { |c| TString.new(c.to_s).as MTValue }) end NativeLib.method :string_downcase, TString do diff --git a/src/myst/interpreter/native_lib/symbol.cr b/src/myst/interpreter/native_lib/symbol.cr index aa9c07e..4904c09 100644 --- a/src/myst/interpreter/native_lib/symbol.cr +++ b/src/myst/interpreter/native_lib/symbol.cr @@ -4,7 +4,7 @@ module Myst TString.new(this.name) end - NativeLib.method :symbol_eq, TSymbol, other : Value do + NativeLib.method :symbol_eq, TSymbol, other : MTValue do case other when TSymbol TBoolean.new(this.value == other.value) @@ -13,7 +13,7 @@ module Myst end end - NativeLib.method :symbol_not_eq, TSymbol, other : Value do + NativeLib.method :symbol_not_eq, TSymbol, other : MTValue do case other when TSymbol TBoolean.new(this.value != other.value) diff --git a/src/myst/interpreter/native_lib/time.cr b/src/myst/interpreter/native_lib/time.cr index c2663c1..466696b 100644 --- a/src/myst/interpreter/native_lib/time.cr +++ b/src/myst/interpreter/native_lib/time.cr @@ -1,13 +1,13 @@ module Myst class Interpreter - NativeLib.method :static_time_now, Value do + NativeLib.method :static_time_now, MTValue do seconds, nanoseconds = Crystal::System::Time.compute_utc_seconds_and_nanoseconds offset = Crystal::System::Time.compute_utc_offset(seconds) - + instance = NativeLib.instantiate(self, this.as(TType), [ - TInteger.new(seconds + offset), + TInteger.new(seconds + offset), TInteger.new(nanoseconds.to_i64) - ] of Value) + ] of MTValue) instance end diff --git a/src/myst/interpreter/native_lib/top_level.cr b/src/myst/interpreter/native_lib/top_level.cr index 2f6188e..7f37c4b 100644 --- a/src/myst/interpreter/native_lib/top_level.cr +++ b/src/myst/interpreter/native_lib/top_level.cr @@ -1,6 +1,6 @@ module Myst class Interpreter - NativeLib.method :mt_exit, Value, status : TInteger? do + NativeLib.method :mt_exit, MTValue, status : TInteger? do real_status = if status.is_a?(TInteger) status.value @@ -11,8 +11,8 @@ module Myst exit(real_status.to_i32) end - NativeLib.method :mt_sleep, Value, time : TInteger | TFloat? = nil do - + NativeLib.method :mt_sleep, MTValue, time : TInteger | TFloat? = nil do + if time.is_a? TInteger || time.is_a? TFloat sleep(time.value) else diff --git a/src/myst/interpreter/nodes/call.cr b/src/myst/interpreter/nodes/call.cr index 300f1a4..a85fc97 100644 --- a/src/myst/interpreter/nodes/call.cr +++ b/src/myst/interpreter/nodes/call.cr @@ -16,7 +16,7 @@ module Myst end - private def lookup_call(node : Call) : Tuple(Value, Value?) + private def lookup_call(node : Call) : Tuple(MTValue, MTValue?) # If the Call has a receiver, lookup the Call on that receiver, otherwise # search the current scope. receiver, check_current = @@ -49,7 +49,7 @@ module Myst private def visit_call(node, receiver, func : TFunctor) - args = [] of Value + args = [] of MTValue node.args.each do |elem| elem.accept(self) @@ -78,7 +78,7 @@ module Myst stack.push(result) end - private def visit_call(_node, _receiver, value : Value) + private def visit_call(_node, _receiver, value : MTValue) stack.push(value) end end diff --git a/src/myst/interpreter/nodes/def.cr b/src/myst/interpreter/nodes/def.cr index f2e9fba..06bce39 100644 --- a/src/myst/interpreter/nodes/def.cr +++ b/src/myst/interpreter/nodes/def.cr @@ -7,7 +7,7 @@ module Myst type.scope when {TType, false} type.instance_scope - when {Value, true} + when {MTValue, true} # Any other kind of value is not allowed to define static methods. __raise_runtime_error("Cannot define static method on #{__typeof(current_self).name}") else diff --git a/src/myst/interpreter/nodes/exception_handler.cr b/src/myst/interpreter/nodes/exception_handler.cr index 525bdf7..d5553e3 100644 --- a/src/myst/interpreter/nodes/exception_handler.cr +++ b/src/myst/interpreter/nodes/exception_handler.cr @@ -48,7 +48,7 @@ module Myst end end - private def rescue_matches?(param : Param, arg : Value) + private def rescue_matches?(param : Param, arg : MTValue) self.match(param.pattern, arg) if param.pattern? self.match(Var.new(param.name), arg) if param.name? self.match(param.restriction, arg) if param.restriction? diff --git a/src/myst/interpreter/nodes/literals.cr b/src/myst/interpreter/nodes/literals.cr index 1640e5e..e6056a9 100644 --- a/src/myst/interpreter/nodes/literals.cr +++ b/src/myst/interpreter/nodes/literals.cr @@ -1,7 +1,7 @@ module Myst class Interpreter def visit(node : ListLiteral) - elements = [] of Value + elements = [] of MTValue node.elements.each do |elem| elem.accept(self) @@ -22,7 +22,7 @@ module Myst end def visit(node : MapLiteral) - entries = node.entries.reduce(Hash(Value, Value).new) do |map, entry| + entries = node.entries.reduce(Hash(MTValue, MTValue).new) do |map, entry| entry.key.accept(self) key = stack.pop entry.value.accept(self) @@ -38,7 +38,7 @@ module Myst strs = node.components.map do |piece| case piece when StringLiteral - Value.from_literal(piece).as(TString) + MTValue.from_literal(piece).as(TString) else visit(piece) expr_result = stack.pop @@ -47,7 +47,7 @@ module Myst self, value_to_s, expr_result, - [] of Value, + [] of MTValue, nil ).invoke.as(TString) end @@ -58,7 +58,7 @@ module Myst end def visit(node : Literal) - stack.push(Value.from_literal(node)) + stack.push(MTValue.from_literal(node)) end end end diff --git a/src/myst/interpreter/nodes/unary_ops.cr b/src/myst/interpreter/nodes/unary_ops.cr index e1711a2..111ddf4 100644 --- a/src/myst/interpreter/nodes/unary_ops.cr +++ b/src/myst/interpreter/nodes/unary_ops.cr @@ -4,7 +4,7 @@ module Myst visit(node.value) value = stack.pop() negate = self.__scopeof(value)["negate"].as(TFunctor) - result = Invocation.new(self, negate, value, [] of Value, nil).invoke + result = Invocation.new(self, negate, value, [] of MTValue, nil).invoke stack.push(result) end @@ -15,7 +15,7 @@ module Myst result = if not_method = self.__scopeof(value)["!"]? not_method = not_method.as(TFunctor) - Invocation.new(self, not_method, value, [] of Value , nil).invoke + Invocation.new(self, not_method, value, [] of MTValue , nil).invoke else TBoolean.new(!value.truthy?) end @@ -29,7 +29,7 @@ module Myst if splat_method = recursive_lookup(value, "*").as?(TFunctor) splat_method = splat_method.as(TFunctor) - result = Invocation.new(self, splat_method, value, [] of Value, nil).invoke + result = Invocation.new(self, splat_method, value, [] of MTValue, nil).invoke else __raise_not_found("* (splat)", value) end diff --git a/src/myst/interpreter/scope.cr b/src/myst/interpreter/scope.cr index 4d393f9..0ca786f 100644 --- a/src/myst/interpreter/scope.cr +++ b/src/myst/interpreter/scope.cr @@ -3,17 +3,17 @@ require "./value.cr" module Myst class Scope property parent : Scope? - property values : Hash(String, Value) + property values : Hash(String, MTValue) def initialize(@parent : Scope? = nil) - @values = {} of String => Value + @values = {} of String => MTValue end # The shorthand access notations (`[]?`, `[]`, `[]=`) will all fall back to # the parent scope if the value does not exist in this scope. # # The longhand `has_key?` and `assign` only operate on this scope. - def []?(key : String) : Value? + def []?(key : String) : MTValue? @values[key]? || @parent.try(&.[key]?) end @@ -21,11 +21,11 @@ module Myst # it is not considered a "public" exception (it is not meant to be # reachable by userland code). Any instance where the exception propogates # outside of the interpreter should be considered a bug. - def [](key : String) : Value + def [](key : String) : MTValue self[key]? || raise IndexError.new("Interpeter Bug: Unmanaged, failed attempt to access `#{key}` from scope: #{self.inspect}") end - def []=(key : String, value : Value) : Value + def []=(key : String, value : MTValue) : MTValue scope = self while scope if scope.has_key?(key) @@ -43,7 +43,7 @@ module Myst !!@values[key]? end - def assign(key : String, value : Value) + def assign(key : String, value : MTValue) @values[key] = value end diff --git a/src/myst/interpreter/util.cr b/src/myst/interpreter/util.cr index b4aa95e..00cf016 100644 --- a/src/myst/interpreter/util.cr +++ b/src/myst/interpreter/util.cr @@ -4,13 +4,13 @@ module Myst # types, these are _always_ looked up in the Kernel. For Instances, the # type is looked up from the type reference on the instance itself. For # Types and Modules, the value itself is returned. - def __typeof(value : Value) + def __typeof(value : MTValue) case value when ContainerType value when TInstance value.type - when Value + when MTValue @kernel.scope[value.type_name].as(TType) else __raise_runtime_error("Can't resolve type of #{value}") @@ -20,7 +20,7 @@ module Myst # Resolve the Scope for `value`. For primitives, this returns the instance # scope of the Type for that value. For Instances, Types, and Modules, this # just returns `.scope` for that value. - def __scopeof(value : Value, prefer_instance_scope = false) : Scope + def __scopeof(value : MTValue, prefer_instance_scope = false) : Scope case value when TInstance value.scope @@ -83,12 +83,12 @@ module Myst end - def __raise_not_found(name, value : Value?) + def __raise_not_found(name, value : MTValue?) type_name = __typeof(value).name error_message = "No variable or method `#{name}` for #{type_name}" if value_to_s = __scopeof(value)["to_s"]? - value_str = NativeLib.call_func_by_name(self, value, "to_s", [] of Value).as(TString).value + value_str = NativeLib.call_func_by_name(self, value, "to_s", [] of MTValue).as(TString).value error_message = "No variable or method `#{name}` for #{value_str}:#{type_name}" end @@ -105,7 +105,7 @@ module Myst raise RuntimeError.new(TString.new(message), callstack) end - def __raise_runtime_error(value : Value) + def __raise_runtime_error(value : MTValue) raise RuntimeError.new(value, callstack) end diff --git a/src/myst/interpreter/value.cr b/src/myst/interpreter/value.cr index 821bf8f..28131e5 100644 --- a/src/myst/interpreter/value.cr +++ b/src/myst/interpreter/value.cr @@ -1,6 +1,6 @@ module Myst - abstract class Value - def Value.from_literal(literal : Node) + abstract class MTValue + def MTValue.from_literal(literal : Node) case literal when IntegerLiteral TInteger.new(literal.value.to_i64) @@ -15,7 +15,7 @@ module Myst when NilLiteral TNil.new else - raise "Interpreter Bug: Attempting to create a Value from a #{literal.class}, which is not a valid Literal type." + raise "Interpreter Bug: Attempting to create an MTValue from a #{literal.class}, which is not a valid Literal type." end end @@ -35,7 +35,7 @@ module Myst end end - abstract class ContainerType < Value + abstract class ContainerType < MTValue property name : String = "" # Ancestors are the modules that have been included inside of a Type. For # example, if a module includes Enumerable, then the ancestors for that @@ -82,11 +82,11 @@ module Myst # TODO: revist this when base object for TType is in place # Currently this prevents to_s from being overriden on Types @scope["to_s"] = TFunctor.new("to_s", [ - ->ttype_to_s(Value, Array(Value), TFunctor?)] of Callable) + ->ttype_to_s(MTValue, Array(MTValue), TFunctor?)] of Callable) end def ttype_to_s(_a, _b, _c) - TString.new(@name).as(Value) + TString.new(@name).as(MTValue) end def type_name @@ -128,7 +128,7 @@ module Myst def_equals_and_hash name, scope, instance_scope end - class TInstance < Value + class TInstance < MTValue property type : TType property scope : Scope @@ -149,7 +149,7 @@ module Myst # Primitives are immutable objects - abstract class TPrimitive(T) < Value + abstract class TPrimitive(T) < MTValue property value : T def initialize(@value : T); end @@ -161,7 +161,7 @@ module Myst def_equals_and_hash value end - class TNil < Value + class TNil < MTValue # All instances of Nil in a program refer to the same object. NIL_OBJECT = TNil.allocate @@ -255,10 +255,10 @@ module Myst end - class TList < Value - property elements : Array(Value) + class TList < MTValue + property elements : Array(MTValue) - def initialize(@elements=[] of Value) + def initialize(@elements=[] of MTValue) end def ensure_capacity(size : Int) @@ -273,10 +273,10 @@ module Myst def_equals_and_hash elements end - class TMap < Value - property entries : Hash(Value, Value) + class TMap < MTValue + property entries : Hash(MTValue, MTValue) - def initialize(@entries={} of Value => Value) + def initialize(@entries={} of MTValue => MTValue) end def type_name @@ -287,7 +287,7 @@ module Myst end - class TFunctorDef < Value + class TFunctorDef < MTValue property definition : Def delegate params, block_param, block_param?, body, splat_index?, splat_index, to: definition @@ -298,20 +298,20 @@ module Myst def_equals_and_hash definition end - alias TNativeDef = Value, Array(Value), TFunctor? -> Value + alias TNativeDef = MTValue, Array(MTValue), TFunctor? -> MTValue alias Callable = TFunctorDef | TNativeDef # A Functor is a container for multiple functor definitions, which can either # be language-level or native. - class TFunctor < Value + class TFunctor < MTValue property name : String property clauses : Array(Callable) property lexical_scope : Scope property? closure : Bool - property! closed_self : Value? + property! closed_self : MTValue? - def initialize(@name : String, @clauses=[] of Callable, @lexical_scope : Scope=Scope.new, @closure : Bool=false, @closed_self : Value?=nil) + def initialize(@name : String, @clauses=[] of Callable, @lexical_scope : Scope=Scope.new, @closure : Bool=false, @closed_self : MTValue?=nil) end def add_clause(definition : Callable) From d2bcada926ecdd3a763685c2022a9254ffec033c Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 24 Feb 2018 16:32:07 -0500 Subject: [PATCH 2/2] [interpreter,spec] Convert `MTValue` into an alias of native types and `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. --- spec/interpreter/callstack_spec.cr | 12 +- spec/interpreter/closure_scope_spec.cr | 4 +- spec/interpreter/invocation_spec.cr | 4 +- spec/interpreter/nodes/call_spec.cr | 2 +- spec/interpreter/nodes/match_assign_spec.cr | 14 +- spec/interpreter/scope_spec.cr | 4 +- spec/interpreter/value_spec.cr | 191 +++--------------- spec/support/breakpoints.cr | 2 +- spec/support/interpret.cr | 4 +- src/myst/interpreter.cr | 2 +- src/myst/interpreter/closure_scope.cr | 2 +- src/myst/interpreter/exceptions.cr | 4 +- src/myst/interpreter/invocation.cr | 8 +- src/myst/interpreter/native_lib/boolean.cr | 22 +- src/myst/interpreter/native_lib/file.cr | 18 +- src/myst/interpreter/native_lib/float.cr | 84 ++++---- src/myst/interpreter/native_lib/integer.cr | 99 ++++----- src/myst/interpreter/native_lib/io.cr | 4 +- .../native_lib/io/file_descriptor.cr | 20 +- src/myst/interpreter/native_lib/list.cr | 46 ++--- src/myst/interpreter/native_lib/map.cr | 40 ++-- src/myst/interpreter/native_lib/nil.cr | 10 +- src/myst/interpreter/native_lib/random.cr | 10 +- src/myst/interpreter/native_lib/string.cr | 106 +++++----- src/myst/interpreter/native_lib/symbol.cr | 10 +- src/myst/interpreter/native_lib/time.cr | 14 +- src/myst/interpreter/native_lib/top_level.cr | 16 +- src/myst/interpreter/nodes/call.cr | 11 +- src/myst/interpreter/nodes/literals.cr | 10 +- src/myst/interpreter/nodes/magic_const.cr | 6 +- src/myst/interpreter/nodes/references.cr | 11 +- src/myst/interpreter/nodes/require.cr | 8 +- src/myst/interpreter/nodes/unary_ops.cr | 2 +- src/myst/interpreter/scope.cr | 14 +- src/myst/interpreter/util.cr | 60 ++++-- src/myst/interpreter/value.cr | 150 ++++++-------- 36 files changed, 416 insertions(+), 608 deletions(-) diff --git a/spec/interpreter/callstack_spec.cr b/spec/interpreter/callstack_spec.cr index e300b42..ff8889d 100644 --- a/spec/interpreter/callstack_spec.cr +++ b/spec/interpreter/callstack_spec.cr @@ -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( @@ -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( @@ -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( @@ -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( diff --git a/spec/interpreter/closure_scope_spec.cr b/spec/interpreter/closure_scope_spec.cr index fbd3c9d..04baca1 100644 --- a/spec/interpreter/closure_scope_spec.cr +++ b/spec/interpreter/closure_scope_spec.cr @@ -9,7 +9,7 @@ describe "Interpreter - ClosureScope" do scope["b"] end - scope["b"]?.should be(nil) + scope["b"]?.should eq(nil) end describe "#[]" do @@ -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) diff --git a/spec/interpreter/invocation_spec.cr b/spec/interpreter/invocation_spec.cr index 0546ede..a4dcab8 100644 --- a/spec/interpreter/invocation_spec.cr +++ b/spec/interpreter/invocation_spec.cr @@ -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 diff --git a/spec/interpreter/nodes/call_spec.cr b/spec/interpreter/nodes/call_spec.cr index 38f80e0..cfe1597 100644 --- a/spec/interpreter/nodes/call_spec.cr +++ b/spec/interpreter/nodes/call_spec.cr @@ -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 diff --git a/spec/interpreter/nodes/match_assign_spec.cr b/spec/interpreter/nodes/match_assign_spec.cr index 6c178b2..597ee49 100644 --- a/spec/interpreter/nodes/match_assign_spec.cr +++ b/spec/interpreter/nodes/match_assign_spec.cr @@ -3,7 +3,7 @@ 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) @@ -11,7 +11,7 @@ private def it_matches(match, file=__FILE__, line=__LINE__, end_line=__END_LINE_ 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) @@ -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 @@ -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 @@ -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( =: "hello") @@ -140,5 +140,5 @@ describe "Interpreter - MatchAssign" do it_does_not_match %q( float_type = 1.5.type =: 1 - ), /match/ + ) end diff --git a/spec/interpreter/scope_spec.cr b/spec/interpreter/scope_spec.cr index 75e0c01..0baf0b4 100644 --- a/spec/interpreter/scope_spec.cr +++ b/spec/interpreter/scope_spec.cr @@ -9,7 +9,7 @@ describe "Interpreter - Scope" do scope["b"] end - scope["b"]?.should be(nil) + scope["b"]?.should eq(nil) end describe "#[]" do @@ -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) diff --git a/spec/interpreter/value_spec.cr b/spec/interpreter/value_spec.cr index b7fb4d4..38261b4 100644 --- a/spec/interpreter/value_spec.cr +++ b/spec/interpreter/value_spec.cr @@ -3,38 +3,38 @@ require "../spec_helper.cr" describe "Values" do describe "::from_literal" do it "maps NilLiteral to TNil" do - MTValue.from_literal(NilLiteral.new).should be_a(TNil) + Interpreter.__value_from_literal(NilLiteral.new).should be_a(TNil) end - it "maps BooleanLiteral to TBoolean" do - MTValue.from_literal(BooleanLiteral.new(false)).should be_a(TBoolean) + it "maps BooleanLiteral to Bool" do + Interpreter.__value_from_literal(BooleanLiteral.new(false)).should be_a(Bool) end - it "maps IntegerLiteral to TInteger" do - MTValue.from_literal(IntegerLiteral.new("0")).should be_a(TInteger) + it "maps IntegerLiteral to Int64" do + Interpreter.__value_from_literal(IntegerLiteral.new("0")).should be_a(Int64) end - it "maps FloatLiteral to TFloat" do - MTValue.from_literal(FloatLiteral.new("0.0")).should be_a(TFloat) + it "maps FloatLiteral to Float64" do + Interpreter.__value_from_literal(FloatLiteral.new("0.0")).should be_a(Float64) end - it "maps StringLiteral to TString" do - MTValue.from_literal(StringLiteral.new("hello")).should be_a(TString) + it "maps StringLiteral to String" do + Interpreter.__value_from_literal(StringLiteral.new("hello")).should be_a(String) end it "maps SymbolLiteral to TSymbol" do - MTValue.from_literal(SymbolLiteral.new("hi")).should be_a(TSymbol) + Interpreter.__value_from_literal(SymbolLiteral.new("hi")).should be_a(TSymbol) end # Container values like List and Map require some effort from the # interpreter to be generated. As such, Value::from_literal cannot generate # them automatically from a node. it "does not map ListLiterals" do - expect_raises(Exception) { MTValue.from_literal(ListLiteral.new).should be_a(TList) } + expect_raises(Exception) { Interpreter.__value_from_literal(ListLiteral.new).should be_a(TList) } end it "does not map MapLiterals" do - expect_raises(Exception) { MTValue.from_literal(MapLiteral.new).should be_a(TMap) } + expect_raises(Exception) { Interpreter.__value_from_literal(MapLiteral.new).should be_a(TMap) } end end @@ -61,151 +61,6 @@ describe "Values" do end end - describe "TBoolean" do - it "always equates FALSE and FALSE" do - TBoolean.new(false).should eq(TBoolean.new(false)) - end - - it "always equates TRUE and TRUE" do - TBoolean.new(true).should eq(TBoolean.new(true)) - end - - it "does not equate FALSE and TRUE" do - TBoolean.new(false).should_not eq(TBoolean.new(true)) - end - - it "always hashes FALSE to the same value" do - TBoolean.new(false).hash.should eq(TBoolean.new(false).hash) - end - - it "always hashes TRUE to the same value" do - TBoolean.new(true).hash.should eq(TBoolean.new(true).hash) - end - - it "has a string representation of TRUE as `true`" do - TBoolean.new(true).to_s.should eq("true") - end - - it "has a string representation of FALSE as `false`" do - TBoolean.new(false).to_s.should eq("false") - end - - it "is not truthy when FALSE" do - TBoolean.new(false).truthy?.should eq(false) - end - - it "is truthy when TRUE" do - TBoolean.new(true).truthy?.should eq(true) - end - end - - describe "TInteger" do - it "can contain any 64-bit integer value" do - TInteger.new( 9_223_372_036_854_775_807) - TInteger.new(-9_223_372_036_854_775_807) - end - - it "holds that an integer is equal to itself" do - TInteger.new(100_i64).should eq(TInteger.new(100_i64)) - end - - it "does not hold that two unique integers are equal" do - TInteger.new(100_i64).should_not eq(TInteger.new(101_i64)) - end - - it "always hashes equal integers to the same value" do - TInteger.new(100_i64).hash.should eq(TInteger.new(100_i64).hash) - end - - it "always hashes unique integers to different values" do - TInteger.new(100_i64).hash.should_not eq(TInteger.new(101_i64).hash) - end - - it "can represent its value as a String" do - TInteger.new( 100_i64).to_s.should eq("100") - TInteger.new(-100_i64).to_s.should eq("-100") - end - - it "is always truthy" do - TInteger.new(0_i64).truthy?.should eq(true) - TInteger.new(-100_i64).truthy?.should eq(true) - TInteger.new(1000_i64).truthy?.should eq(true) - end - end - - describe "TFloat" do - it "can contain any 64-bit float value" do - TFloat.new( 1.7976931348623157e+308) - TFloat.new(-1.7976931348623157e+308) - end - - it "holds that an integer is equal to itself" do - TFloat.new(100.0_f64).should eq(TFloat.new(100.0_f64)) - end - - it "does not hold that two unique integers are equal" do - TFloat.new(100.0_f64).should_not eq(TFloat.new(101_f64)) - end - - it "always hashes equal integers to the same value" do - TFloat.new(100.0_f64).hash.should eq(TFloat.new(100.0_f64).hash) - end - - it "always hashes unique integers to different values" do - TFloat.new(100.0_f64).hash.should_not eq(TFloat.new(101_f64).hash) - end - - it "can represent its value as a String" do - TFloat.new( 100.0_f64).to_s.should eq("100.0") - TFloat.new(-100.0_f64).to_s.should eq("-100.0") - end - - it "is always truthy" do - TFloat.new(0.0_f64).truthy?.should eq(true) - TFloat.new(-100.0_f64).truthy?.should eq(true) - TFloat.new(1000.0_f64).truthy?.should eq(true) - end - end - - describe "TString" do - it "can contain strings of arbitrary length" do - TString.new("hello"*1000) - end - - it "can contain escape sequences" do - TString.new("\n") - end - - it "holds that a string is equal to itself" do - TString.new("hi").should eq(TString.new("hi")) - end - - it "always hashes a string to the same value" do - TString.new("hi").hash.should eq(TString.new("hi").hash) - end - - it "always hashes unique strings to different values" do - TString.new("hi").hash.should_not eq(TString.new("hello")) - end - - it "uses its value as its string representation" do - TString.new("hello").to_s.should eq("hello") - end - - it "interprets escape sequences in its string representation" do - TString.new("\nhi\n").to_s.should eq(" -hi -") - end - - it "is always truthy" do - TString.new("").truthy?.should eq(true) - TString.new("\n").truthy?.should eq(true) - TString.new("\0").truthy?.should eq(true) - TString.new("hello world").truthy?.should eq(true) - end - end - describe "TSymbol" do it "can be created from any string value" do TSymbol.new("hello"*1000) @@ -243,7 +98,7 @@ hi end it "can contain any mixture of Values" do - TList.new([TInteger.new(1_i64), TBoolean.new(false), TString.new("hello")]) + TList.new([1_i64, false, "hello"] of MTValue) end it "can contain other lists within itself" do @@ -252,17 +107,17 @@ hi it "can dynamically adjust its size" do list = TList.new - list.elements << TInteger.new(0_i64) - list.elements << TString.new("hello") + list.elements << 0_i64 + list.elements << "hello" list.elements.size.should eq(2) end it "is always truthy" do - TList.new.truthy?.should eq(true) - TList.new([TNil.new] of MTValue).truthy?.should eq(true) - TList.new([TBoolean.new(false)] of MTValue).truthy?.should eq(true) - TList.new([TInteger.new(1_i64), TNil.new]).truthy?.should eq(true) + TList.new.truthy?.should eq(true) + TList.new([TNil.new] of MTValue).truthy?.should eq(true) + TList.new([false] of MTValue).truthy?.should eq(true) + TList.new([1_i64, TNil.new] of MTValue).truthy?.should eq(true) end end @@ -277,7 +132,7 @@ hi end it "can contain any mixture of Values" do - TMap.new({ TInteger.new(1_i64) => TBoolean.new(false), TString.new("hello") => TSymbol.new("hi")}) + TMap.new({ 1_i64 => false, "hello" => "hi" } of MTValue => MTValue) end it "can contain other maps within itself" do @@ -286,8 +141,8 @@ hi it "can dynamically adjust its size" do list = TMap.new - list.entries[TBoolean.new(false)] = TInteger.new(0_i64) - list.entries[TBoolean.new(true)] = TString.new("hello") + list.entries[false] = 0_i64 + list.entries[true] = "hello" list.entries.size.should eq(2) end @@ -295,7 +150,7 @@ hi it "is always truthy" do TMap.new.truthy?.should eq(true) TMap.new({ TNil.new => TNil.new } of MTValue => MTValue).truthy?.should eq(true) - TMap.new({ TSymbol.new("") => TInteger.new(1_i64) } of MTValue => MTValue).truthy?.should eq(true) + TMap.new({ TSymbol.new("") => 1_i64 } of MTValue => MTValue).truthy?.should eq(true) end end end diff --git a/spec/support/breakpoints.cr b/spec/support/breakpoints.cr index e7a3a37..5bce0a3 100644 --- a/spec/support/breakpoints.cr +++ b/spec/support/breakpoints.cr @@ -19,7 +19,7 @@ require "../spec_helper.cr" # # itr = Interpreter.new # add_breakpoint(itr, "breakpoint") do |this, args, block| -# args[0].should be_a(TInteger) +# args[0].should be_a(Int64) # end # # parse_and_interpret %q( diff --git a/spec/support/interpret.cr b/spec/support/interpret.cr index 47614c8..e5caf4b 100644 --- a/spec/support/interpret.cr +++ b/spec/support/interpret.cr @@ -99,10 +99,10 @@ end # val(node) # -# Run `MTValue.from_literal` on the given node and return the result. If `node` +# Run `__value_from_literal` on the given node and return the result. If `node` # is not already a Node, it will be run through `l` first. def val(node : Node) - MTValue.from_literal(node).as(MTValue) + Interpreter.__value_from_literal(node).as(MTValue) end def val(node : Array(T)) forall T diff --git a/src/myst/interpreter.cr b/src/myst/interpreter.cr index 2da3608..b396667 100644 --- a/src/myst/interpreter.cr +++ b/src/myst/interpreter.cr @@ -120,7 +120,7 @@ module Myst def put_error(error : RuntimeError) value_to_s = __scopeof(error.value)["to_s"].as(TFunctor) result = Invocation.new(self, value_to_s, error.value, [] of MTValue, nil).invoke - errput.puts("Uncaught Exception: " + result.as(TString).value) + errput.puts("Uncaught Exception: " + result.as(String)) errput.puts(error.trace) end diff --git a/src/myst/interpreter/closure_scope.cr b/src/myst/interpreter/closure_scope.cr index 7f02fa8..c3f525b 100644 --- a/src/myst/interpreter/closure_scope.cr +++ b/src/myst/interpreter/closure_scope.cr @@ -29,7 +29,7 @@ module Myst end def has_key?(key : String) - !!@values[key]? || closed_scope.has_key?(key) + @values.has_key?(key) || closed_scope.has_key?(key) end def assign(key : String, value : MTValue) diff --git a/src/myst/interpreter/exceptions.cr b/src/myst/interpreter/exceptions.cr index 2b9aa34..d5c930b 100644 --- a/src/myst/interpreter/exceptions.cr +++ b/src/myst/interpreter/exceptions.cr @@ -25,8 +25,8 @@ module Myst # to better show the intent of the raised errors, and ensure consistency # between them. class MatchError < RuntimeError - def initialize(@trace : Callstack, message : String = "match failure") - @value = TString.new(message) + def initialize(@trace : Callstack, @message : String = "match failure") + @value = message end end end diff --git a/src/myst/interpreter/invocation.cr b/src/myst/interpreter/invocation.cr index 5b9e08d..8f006ab 100644 --- a/src/myst/interpreter/invocation.cr +++ b/src/myst/interpreter/invocation.cr @@ -37,12 +37,16 @@ module Myst res = do_call(clause, @receiver, @args, @block) end @itr.pop_scope_override - break res if res + break res unless res.nil? end @itr.pop_callstack(to_size: @callstack_size_at_entry) - result || @itr.__raise_runtime_error("No clause matches with given arguments: #{@args.inspect}") + if result.nil? + @itr.__raise_runtime_error("No clause matches with given arguments: #{@args.inspect}") + else + result + end rescue ex : BreakException if ex.caught? return @itr.stack.pop diff --git a/src/myst/interpreter/native_lib/boolean.cr b/src/myst/interpreter/native_lib/boolean.cr index 6613059..281fec5 100644 --- a/src/myst/interpreter/native_lib/boolean.cr +++ b/src/myst/interpreter/native_lib/boolean.cr @@ -1,25 +1,15 @@ module Myst class Interpreter - NativeLib.method :bool_to_s, TBoolean do - TString.new(this.value ? "true" : "false") + NativeLib.method :bool_to_s, Bool do + this.to_s end - NativeLib.method :bool_eq, TBoolean, other : MTValue do - case other - when TBoolean - TBoolean.new(this.value == other.value) - else - TBoolean.new(false) - end + NativeLib.method :bool_eq, Bool, other : MTValue do + this == other end - NativeLib.method :bool_not_eq, TBoolean, other : MTValue do - case other - when TBoolean - TBoolean.new(this.value != other.value) - else - TBoolean.new(true) - end + NativeLib.method :bool_not_eq, Bool, other : MTValue do + this != other end def init_boolean(kernel : TModule) diff --git a/src/myst/interpreter/native_lib/file.cr b/src/myst/interpreter/native_lib/file.cr index 3999eca..f8eafe9 100644 --- a/src/myst/interpreter/native_lib/file.cr +++ b/src/myst/interpreter/native_lib/file.cr @@ -1,26 +1,26 @@ module Myst class Interpreter - NativeLib.method :file_init, TInstance, name : TString, mode : TString do - file = File.open(name.value, mode.value) + NativeLib.method :file_init, TInstance, name : String, mode : String do + file = File.open(name, mode) @fd_pool[file.fd] = file - this.ivars["@fd"] = TInteger.new(file.fd.to_i64) + this.ivars["@fd"] = file.fd.to_i64 this.ivars["@mode"] = mode this end NativeLib.method :file_close, TInstance do - fd = this.ivars["@fd"].as(TInteger) - file = @fd_pool[fd.value] + fd = this.ivars["@fd"].as(Int64) + file = @fd_pool[fd] file.close - @fd_pool.delete(fd.value) + @fd_pool.delete(fd) TNil.new end NativeLib.method :file_size, TInstance do - fd = this.ivars["@fd"].as(TInteger) - file = @fd_pool[fd.value].as(File) - TInteger.new(file.size.to_i64) + fd = this.ivars["@fd"].as(Int64) + file = @fd_pool[fd].as(File) + file.size.to_i64 end diff --git a/src/myst/interpreter/native_lib/float.cr b/src/myst/interpreter/native_lib/float.cr index 1d499db..8bc3c12 100644 --- a/src/myst/interpreter/native_lib/float.cr +++ b/src/myst/interpreter/native_lib/float.cr @@ -1,109 +1,99 @@ module Myst class Interpreter - NativeLib.method :float_add, TFloat, other : MTValue do + NativeLib.method :float_add, Float64, other : MTValue do case other - when TInteger, TFloat - TFloat.new(this.value + other.value) + when Int64, Float64 + this + other else __raise_runtime_error("invalid argument for Float#+: #{__typeof(other).name}") end end - NativeLib.method :float_subtract, TFloat, other : MTValue do + NativeLib.method :float_subtract, Float64, other : MTValue do case other - when TInteger, TFloat - TFloat.new(this.value - other.value) + when Int64, Float64 + this - other else __raise_runtime_error("invalid argument for Float#-: #{__typeof(other).name}") end end - NativeLib.method :float_multiply, TFloat, other : MTValue do + NativeLib.method :float_multiply, Float64, other : MTValue do case other - when TInteger, TFloat - TFloat.new(this.value * other.value) + when Int64, Float64 + this * other else __raise_runtime_error("invalid argument for Float#*: #{__typeof(other).name}") end end - NativeLib.method :float_divide, TFloat, other : MTValue do + NativeLib.method :float_divide, Float64, other : MTValue do case other - when TInteger, TFloat - __raise_runtime_error("Division by zero") if other.value == 0 - TFloat.new(this.value / other.value) + when Int64, Float64 + __raise_runtime_error("Division by zero") if other == 0 + this / other else __raise_runtime_error("invalid argument for Float#/: #{__typeof(other).name}") end end - NativeLib.method :float_modulo, TFloat, other : MTValue do + NativeLib.method :float_modulo, Float64, other : MTValue do case other - when TInteger, TFloat - __raise_runtime_error("Division by zero") if other.value == 0 - TFloat.new(this.value % other.value) + when Int64, Float64 + __raise_runtime_error("Division by zero") if other == 0 + this % other else __raise_runtime_error("invalid argument for Float#%: #{__typeof(other).name}") end end - NativeLib.method :float_to_s, TFloat do - TString.new(this.value.to_s) + NativeLib.method :float_to_s, Float64 do + this.to_s end - NativeLib.method :float_eq, TFloat, other : MTValue do - case other - when TFloat, TInteger - TBoolean.new(this.value == other.value) - else - TBoolean.new(false) - end + NativeLib.method :float_eq, Float64, other : MTValue do + this == other end - NativeLib.method :float_not_eq, TFloat, other : MTValue do - case other - when TFloat, TInteger - TBoolean.new(this.value != other.value) - else - TBoolean.new(true) - end + NativeLib.method :float_not_eq, Float64, other : MTValue do + this != other end - NativeLib.method :float_negate, TFloat do - TFloat.new(-this.value) + NativeLib.method :float_negate, Float64 do + -this end - NativeLib.method :float_lt, TFloat, other : MTValue do + NativeLib.method :float_lt, Float64, other : MTValue do case other - when TInteger, TFloat - TBoolean.new(this.value < other.value) + when Int64, Float64 + this < other else __raise_runtime_error("invalid argument for Float#<: #{__typeof(other).name}") end end - NativeLib.method :float_lte, TFloat, other : MTValue do + NativeLib.method :float_lte, Float64, other : MTValue do case other - when TInteger, TFloat - TBoolean.new(this.value <= other.value) + when Int64, Float64 + this <= other else __raise_runtime_error("invalid argument for Float#<=: #{__typeof(other).name}") end end - NativeLib.method :float_gt, TFloat, other : MTValue do + NativeLib.method :float_gt, Float64, other : MTValue do case other - when TInteger, TFloat - TBoolean.new(this.value > other.value) + when Int64, Float64 + this > other else __raise_runtime_error("invalid argument for Float#>: #{__typeof(other).name}") end end - NativeLib.method :float_gte, TFloat, other : MTValue do + NativeLib.method :float_gte, Float64, other : MTValue do case other - when TInteger, TFloat - TBoolean.new(this.value >= other.value) + when Int64, Float64 + this >= other else __raise_runtime_error("invalid argument for Float#>=: #{__typeof(other).name}") end diff --git a/src/myst/interpreter/native_lib/integer.cr b/src/myst/interpreter/native_lib/integer.cr index 4b7eef3..9a31d43 100644 --- a/src/myst/interpreter/native_lib/integer.cr +++ b/src/myst/interpreter/native_lib/integer.cr @@ -1,121 +1,102 @@ module Myst class Interpreter - NativeLib.method :int_add, TInteger, other : MTValue do + NativeLib.method :int_add, Int64, other : MTValue do case other - when TInteger - TInteger.new(this.value + other.value) - when TFloat - TFloat.new(this.value + other.value) + when Int64, Float64 + this + other else __raise_runtime_error("invalid argument for Integer#+: #{__typeof(other).name}") end end - NativeLib.method :int_subtract, TInteger, other : MTValue do + NativeLib.method :int_subtract, Int64, other : MTValue do case other - when TInteger - TInteger.new(this.value - other.value) - when TFloat - TFloat.new(this.value - other.value) + when Int64, Float64 + this - other else __raise_runtime_error("invalid argument for Integer#-: #{__typeof(other).name}") end end - NativeLib.method :int_multiply, TInteger, other : MTValue do + NativeLib.method :int_multiply, Int64, other : MTValue do case other - when TInteger - TInteger.new(this.value * other.value) - when TFloat - TFloat.new(this.value * other.value) + when Int64, Float64 + this * other else __raise_runtime_error("invalid argument for Integer#*: #{__typeof(other).name}") end end - NativeLib.method :int_divide, TInteger, other : MTValue do + NativeLib.method :int_divide, Int64, other : MTValue do case other - when TInteger - __raise_runtime_error("Division by zero") if other.value == 0 - TInteger.new(this.value / other.value) - when TFloat - __raise_runtime_error("Division by zero") if other.value == 0 - TFloat.new(this.value / other.value) + when Int64, Float64 + __raise_runtime_error("Division by zero") if other == 0 + this / other else __raise_runtime_error("invalid argument for Integer#/: #{__typeof(other).name}") end end - NativeLib.method :int_modulo, TInteger, other : MTValue do + NativeLib.method :int_modulo, Int64, other : MTValue do case other - when TInteger - __raise_runtime_error("Division by zero") if other.value == 0 - TInteger.new(this.value % other.value) - when TFloat - __raise_runtime_error("Division by zero") if other.value == 0 - TFloat.new(this.value.to_f % other.value) + when Int64 + __raise_runtime_error("Division by zero") if other == 0 + this % other + when Float64 + __raise_runtime_error("Division by zero") if other == 0 + this.to_f % other else __raise_runtime_error("invalid argument for Integer#%: #{__typeof(other).name}") end end - NativeLib.method :int_to_s, TInteger do - TString.new(this.value.to_s) + NativeLib.method :int_to_s, Int64 do + this.to_s end - NativeLib.method :int_eq, TInteger, other : MTValue do - case other - when TInteger, TFloat - TBoolean.new(this.value == other.value) - else - TBoolean.new(false) - end + NativeLib.method :int_eq, Int64, other : MTValue do + this == other end - NativeLib.method :int_not_eq, TInteger, other : MTValue do - case other - when TInteger, TFloat - TBoolean.new(this.value != other.value) - else - TBoolean.new(true) - end + NativeLib.method :int_not_eq, Int64, other : MTValue do + this != other end - NativeLib.method :int_negate, TInteger do - TInteger.new(-this.value) + NativeLib.method :int_negate, Int64 do + -this end - NativeLib.method :int_lt, TInteger, other : MTValue do + NativeLib.method :int_lt, Int64, other : MTValue do case other - when TInteger, TFloat - TBoolean.new(this.value < other.value) + when Int64, Float64 + this < other else __raise_runtime_error("invalid argument for Integer#<: #{__typeof(other).name}") end end - NativeLib.method :int_lte, TInteger, other : MTValue do + NativeLib.method :int_lte, Int64, other : MTValue do case other - when TInteger, TFloat - TBoolean.new(this.value <= other.value) + when Int64, Float64 + this <= other else __raise_runtime_error("invalid argument for Integer#<=: #{__typeof(other).name}") end end - NativeLib.method :int_gt, TInteger, other : MTValue do + NativeLib.method :int_gt, Int64, other : MTValue do case other - when TInteger, TFloat - TBoolean.new(this.value > other.value) + when Int64, Float64 + this > other else __raise_runtime_error("invalid argument for Integer#>: #{__typeof(other).name}") end end - NativeLib.method :int_gte, TInteger, other : MTValue do + NativeLib.method :int_gte, Int64, other : MTValue do case other - when TInteger, TFloat - TBoolean.new(this.value >= other.value) + when Int64, Float64 + this >= other else __raise_runtime_error("invalid argument for Integer#>=: #{__typeof(other).name}") end diff --git a/src/myst/interpreter/native_lib/io.cr b/src/myst/interpreter/native_lib/io.cr index c63be3f..7467dfa 100644 --- a/src/myst/interpreter/native_lib/io.cr +++ b/src/myst/interpreter/native_lib/io.cr @@ -1,6 +1,6 @@ module Myst class Interpreter - NativeLib.method :io_read, MTValue, size : TInteger do + NativeLib.method :io_read, MTValue, size : Int64 do __raise_runtime_error("`IO#read` must be implemented by inheriting types.") end @@ -10,7 +10,7 @@ module Myst private def make_io_fd(type : TType, id : Int) fd = TInstance.new(type) - fd.ivars["fd"] = TInteger.new(id.to_i64) + fd.ivars["fd"] = id.to_i64 fd end diff --git a/src/myst/interpreter/native_lib/io/file_descriptor.cr b/src/myst/interpreter/native_lib/io/file_descriptor.cr index a503491..add5659 100644 --- a/src/myst/interpreter/native_lib/io/file_descriptor.cr +++ b/src/myst/interpreter/native_lib/io/file_descriptor.cr @@ -1,26 +1,26 @@ module Myst class Interpreter - NativeLib.method :io_fd_init, TInstance, fd : TInteger do - id = fd.value.to_i32 + NativeLib.method :io_fd_init, TInstance, fd : Int64 do + id = fd.to_i32 @fd_pool[id] ||= IO::FileDescriptor.new(id) - this.ivars["fd"] = fd + this.ivars["fd"] = fd.to_i64 end - NativeLib.method :io_fd_read, TInstance, size : TInteger do - fd_id = this.ivars["fd"].as(TInteger).value.to_i32 + NativeLib.method :io_fd_read, TInstance, size : Int64 do + fd_id = this.ivars["fd"].as(Int64).to_i32 fd = @fd_pool[fd_id] - slice = Slice(UInt8).new(size.value) + slice = Slice(UInt8).new(size) fd.read(slice) - TString.new(String.new(slice)) + String.new(slice) end - NativeLib.method :io_fd_write, TInstance, content : TString do - fd_id = this.ivars["fd"].as(TInteger).value.to_i32 + NativeLib.method :io_fd_write, TInstance, content : String do + fd_id = this.ivars["fd"].as(Int64).to_i32 fd = @fd_pool[fd_id] - fd.write(content.value.to_slice) + fd.write(content.to_slice) TNil.new end diff --git a/src/myst/interpreter/native_lib/list.cr b/src/myst/interpreter/native_lib/list.cr index 1571895..dca0643 100644 --- a/src/myst/interpreter/native_lib/list.cr +++ b/src/myst/interpreter/native_lib/list.cr @@ -11,7 +11,7 @@ module Myst end NativeLib.method :list_size, TList do - TInteger.new(this.elements.size.to_i64) + this.elements.size.to_i64 end NativeLib.method :list_splat, TList do @@ -19,44 +19,44 @@ module Myst end NativeLib.method :list_eq, TList, other : MTValue do - return TBoolean.new(false) unless other.is_a?(TList) - return TBoolean.new(true) if this == other - return TBoolean.new(false) if this.elements.size != other.elements.size + return false unless other.is_a?(TList) + return true if this == other + return false if this.elements.size != other.elements.size this.elements.zip(other.elements).each do |a, b| - return TBoolean.new(false) unless NativeLib.call_func_by_name(self, a, "==", [b]).truthy? + return false unless NativeLib.call_func_by_name(self, a, "==", [b]).truthy? end - TBoolean.new(true) + true end NativeLib.method :list_not_eq, TList, other : MTValue do - return TBoolean.new(true) unless other.is_a?(TList) - return TBoolean.new(false) if this == other - return TBoolean.new(true) if this.elements.size != other.elements.size + return true unless other.is_a?(TList) + return false if this == other + return true if this.elements.size != other.elements.size this.elements.zip(other.elements).each do |a, b| - return TBoolean.new(true) if NativeLib.call_func_by_name(self, a, "==", [b]).truthy? + return true if NativeLib.call_func_by_name(self, a, "==", [b]).truthy? end - TBoolean.new(false) + false end NativeLib.method :list_add, TList, other : TList do TList.new(this.elements + other.elements) end - NativeLib.method :list_access, TList, index : TInteger do - if element = this.elements[index.value]? + NativeLib.method :list_access, TList, index : Int64 do + if element = this.elements[index]? element else TNil.new end end - NativeLib.method :list_access_assign, TList, index : TInteger, value : MTValue do - this.ensure_capacity(index.value + 1) - this.elements[index.value] = value + NativeLib.method :list_access_assign, TList, index : Int64, value : MTValue do + this.ensure_capacity(index + 1) + this.elements[index] = value end NativeLib.method :list_minus, TList, other : TList do @@ -64,23 +64,23 @@ module Myst end NativeLib.method :list_proper_subset, TList, other : TList do - return TBoolean.new(false) unless other.is_a?(TList) - return TBoolean.new(false) if this == other + return false unless other.is_a?(TList) + return false if this == other if (this.elements - other.elements).empty? - TBoolean.new(true) + true else - TBoolean.new(false) + false end end NativeLib.method :list_subset, TList, other : TList do - return TBoolean.new(false) unless other.is_a?(TList) + return false unless other.is_a?(TList) if (this.elements - other.elements).empty? - TBoolean.new(true) + true else - TBoolean.new(false) + false end end diff --git a/src/myst/interpreter/native_lib/map.cr b/src/myst/interpreter/native_lib/map.cr index 6246594..be66d10 100644 --- a/src/myst/interpreter/native_lib/map.cr +++ b/src/myst/interpreter/native_lib/map.cr @@ -11,7 +11,7 @@ module Myst end NativeLib.method :map_size, TMap do - TInteger.new(this.entries.size.to_i64) + this.entries.size.to_i64 end NativeLib.method :map_add, TMap, other : TMap do @@ -19,37 +19,37 @@ module Myst end NativeLib.method :map_eq, TMap, other : MTValue do - return TBoolean.new(false) unless other.is_a?(TMap) - return TBoolean.new(true) if this == other - return TBoolean.new(false) if this.entries.size != other.entries.size + return false unless other.is_a?(TMap) + return true if this == other + return false if this.entries.size != other.entries.size # At this point, `this` and `other` must have the same number of keys, # meaning that if `other` contains all of the keys that `this` does, it # also cannot contain any extra keys, so it's only necessary to iterate # one of the two maps' keys. this.entries.keys.zip(other.entries.keys).each do |a_key, b_key| - return TBoolean.new(false) unless NativeLib.call_func_by_name(self, a_key, "==", [b_key]).truthy? - return TBoolean.new(false) unless NativeLib.call_func_by_name(self, this.entries[a_key], "==", [other.entries[b_key]]).truthy? + return false unless NativeLib.call_func_by_name(self, a_key, "==", [b_key]).truthy? + return false unless NativeLib.call_func_by_name(self, this.entries[a_key], "==", [other.entries[b_key]]).truthy? end - TBoolean.new(true) + true end NativeLib.method :map_not_eq, TMap, other : MTValue do - return TBoolean.new(true) unless other.is_a?(TMap) - return TBoolean.new(false) if this == other - return TBoolean.new(true) if this.entries.size != other.entries.size + return true unless other.is_a?(TMap) + return false if this == other + return true if this.entries.size != other.entries.size # At this point, `this` and `other` must have the same number of keys, # meaning that if `other` contains all of the keys that `this` does, it # also cannot contain any extra keys, so it's only necessary to iterate # one of the two maps' keys. this.entries.keys.zip(other.entries.keys).each do |a_key, b_key| - return TBoolean.new(true) if NativeLib.call_func_by_name(self, a_key, "==", [b_key]).truthy? - return TBoolean.new(true) if NativeLib.call_func_by_name(self, this.entries[a_key], "==", [other.entries[b_key]]).truthy? + return true if NativeLib.call_func_by_name(self, a_key, "==", [b_key]).truthy? + return true if NativeLib.call_func_by_name(self, this.entries[a_key], "==", [other.entries[b_key]]).truthy? end - TBoolean.new(true) + true end NativeLib.method :map_access, TMap, index : MTValue do @@ -61,23 +61,23 @@ module Myst end NativeLib.method :map_proper_subset, TMap, other : TMap do - return TBoolean.new(false) unless other.is_a?(TMap) - return TBoolean.new(false) if this.entries.keys == other.entries.keys + return false unless other.is_a?(TMap) + return false if this.entries.keys == other.entries.keys if (this.entries.keys - other.entries.keys).empty? - TBoolean.new(true) + true else - TBoolean.new(false) + false end end NativeLib.method :map_subset, TMap, other : TMap do - return TBoolean.new(false) unless other.is_a?(TMap) + return false unless other.is_a?(TMap) if (this.entries.keys - other.entries.keys).empty? - TBoolean.new(true) + true else - TBoolean.new(false) + false end end diff --git a/src/myst/interpreter/native_lib/nil.cr b/src/myst/interpreter/native_lib/nil.cr index 0f9b244..350c854 100644 --- a/src/myst/interpreter/native_lib/nil.cr +++ b/src/myst/interpreter/native_lib/nil.cr @@ -1,24 +1,24 @@ module Myst class Interpreter NativeLib.method :nil_to_s, TNil do - TString.new("") + "" end NativeLib.method :nil_eq, TNil, other : MTValue do case other when TNil - TBoolean.new(true) + true else - TBoolean.new(false) + false end end NativeLib.method :nil_not_eq, TNil, other : MTValue do case other when TNil - TBoolean.new(false) + false else - TBoolean.new(true) + true end end diff --git a/src/myst/interpreter/native_lib/random.cr b/src/myst/interpreter/native_lib/random.cr index e718384..ac9148d 100644 --- a/src/myst/interpreter/native_lib/random.cr +++ b/src/myst/interpreter/native_lib/random.cr @@ -1,12 +1,12 @@ module Myst class Interpreter NativeLib.method :random_rand, TModule, max : MTValue? = nil do - if max.is_a? TInteger - TInteger.new(rand(max.value)) - elsif max.is_a? TFloat - TFloat.new(rand(max.value)) + if max.is_a? Int64 + rand(max) + elsif max.is_a? Float64 + rand(max) else - TFloat.new(rand()) + rand() end end diff --git a/src/myst/interpreter/native_lib/string.cr b/src/myst/interpreter/native_lib/string.cr index 7174b75..7611658 100644 --- a/src/myst/interpreter/native_lib/string.cr +++ b/src/myst/interpreter/native_lib/string.cr @@ -1,119 +1,111 @@ module Myst class Interpreter - NativeLib.method :string_add, TString, other : MTValue do + NativeLib.method :string_add, String, other : MTValue do case other - when TString - TString.new(this.value + other.value) + when String + this + other else __raise_runtime_error("invalid argument for String#+: #{__typeof(other).name}") end end - NativeLib.method :string_multiply, TString, other : MTValue do + NativeLib.method :string_multiply, String, other : MTValue do case other - when TInteger + when Int64 # String multiplication repeats `this` `arg` times. - TString.new(this.value * other.value) + this * other else __raise_runtime_error("invalid argument for String#*: #{__typeof(other).name}") end end - NativeLib.method :string_to_s, TString do - this.as(TString) + NativeLib.method :string_to_s, String do + this end - NativeLib.method :string_eq, TString, other : MTValue do - case other - when TString - TBoolean.new(this.value == other.value) - else - TBoolean.new(false) - end + NativeLib.method :string_eq, String, other : MTValue do + this == other end - NativeLib.method :string_not_eq, TString, other : MTValue do - case other - when TString - TBoolean.new(this.value != other.value) - else - TBoolean.new(true) - end + NativeLib.method :string_not_eq, String, other : MTValue do + this != other end - NativeLib.method :string_split, TString do + NativeLib.method :string_split, String do delimiter = - case delim_arg = __args[0]? + case delim = __args[0]? when nil " " - when TString - delim_arg.value + when String + delim + else + __raise_runtime_error("Delimiter for String#split must be a String value (got #{__typeof(delim).name}") end - TList.new(this.value.split(delimiter).map{ |s| TString.new(s).as(MTValue) }) + TList.new(this.split(delimiter).map(&.as(MTValue))) end - NativeLib.method :string_size, TString do - TInteger.new(this.value.size.to_i64) + NativeLib.method :string_size, String do + this.size.to_i64 end - NativeLib.method :string_chars, TString do - TList.new(this.value.chars.map { |c| TString.new(c.to_s).as MTValue }) + NativeLib.method :string_chars, String do + TList.new(this.chars.map { |c| c.to_s.as(MTValue) }) end - NativeLib.method :string_downcase, TString do - TString.new(this.value.downcase) + NativeLib.method :string_downcase, String do + this.downcase end - NativeLib.method :string_upcase, TString do - TString.new(this.value.upcase) + NativeLib.method :string_upcase, String do + this.upcase end - NativeLib.method :string_chomp, TString, other : TString? do - other && return TString.new(this.value.chomp(other.value)) - TString.new(this.value.chomp) + NativeLib.method :string_chomp, String, other : String? do + other && return this.chomp(other) + this.chomp end - NativeLib.method :string_strip, TString do - TString.new(this.value.strip) + NativeLib.method :string_strip, String do + this.strip end - NativeLib.method :string_rstrip, TString do - TString.new(this.value.rstrip) + NativeLib.method :string_rstrip, String do + this.rstrip end - NativeLib.method :string_lstrip, TString do - TString.new(this.value.lstrip) + NativeLib.method :string_lstrip, String do + this.lstrip end - NativeLib.method :string_includes?, TString, other : TString do - TBoolean.new(this.value.includes?(other.value)) + NativeLib.method :string_includes?, String, other : String do + this.includes?(other) end - NativeLib.method :string_at, TString, index : TInteger, length : TInteger? do - idx = index.value + NativeLib.method :string_at, String, index : Int64, length : Int64? do + idx = index result = case length - when TInteger + when Int64 # Explicitly check that `String#[start, count]` will not fail. - if idx < this.value.size && length.value >= 0 - TString.new(this.value[idx, length.value]) + if idx < this.size && length >= 0 + this[idx, length] else - TString.new("") + "" end else - # Use nil-checking to assert that `index.value` is valid. - if char = this.value[index.value]? - TString.new(char.to_s) + # Use nil-checking to assert that `index` is valid. + if char = this[index]? + char.to_s end end result || TNil.new end - NativeLib.method :string_reverse, TString do - TString.new(this.value.reverse) + NativeLib.method :string_reverse, String do + this.reverse end def init_string(kernel : TModule) diff --git a/src/myst/interpreter/native_lib/symbol.cr b/src/myst/interpreter/native_lib/symbol.cr index 4904c09..e011bda 100644 --- a/src/myst/interpreter/native_lib/symbol.cr +++ b/src/myst/interpreter/native_lib/symbol.cr @@ -1,24 +1,24 @@ module Myst class Interpreter NativeLib.method :symbol_to_s, TSymbol do - TString.new(this.name) + this.name end NativeLib.method :symbol_eq, TSymbol, other : MTValue do case other when TSymbol - TBoolean.new(this.value == other.value) + this == other else - TBoolean.new(false) + false end end NativeLib.method :symbol_not_eq, TSymbol, other : MTValue do case other when TSymbol - TBoolean.new(this.value != other.value) + this != other else - TBoolean.new(true) + true end end diff --git a/src/myst/interpreter/native_lib/time.cr b/src/myst/interpreter/native_lib/time.cr index 466696b..425ed9a 100644 --- a/src/myst/interpreter/native_lib/time.cr +++ b/src/myst/interpreter/native_lib/time.cr @@ -5,20 +5,20 @@ module Myst offset = Crystal::System::Time.compute_utc_offset(seconds) instance = NativeLib.instantiate(self, this.as(TType), [ - TInteger.new(seconds + offset), - TInteger.new(nanoseconds.to_i64) + seconds + offset, + nanoseconds.to_i64 ] of MTValue) instance end - NativeLib.method :time_to_s, TInstance, format : TString? do + NativeLib.method :time_to_s, TInstance, format : String? do crystal_time = to_crystal_time(this) if format - TString.new(crystal_time.to_s(format.value)) + crystal_time.to_s(format) else - TString.new(crystal_time.to_s) + crystal_time.to_s end end @@ -34,8 +34,8 @@ module Myst private def to_crystal_time(myst_time : TInstance) Time.new( - seconds: myst_time.ivars["@seconds"].as(TInteger).value, - nanoseconds: myst_time.ivars["@nanoseconds"].as(TInteger).value.to_i32, + seconds: myst_time.ivars["@seconds"].as(Int64), + nanoseconds: myst_time.ivars["@nanoseconds"].as(Int64).to_i32, kind: Time::Kind::Unspecified ) end diff --git a/src/myst/interpreter/native_lib/top_level.cr b/src/myst/interpreter/native_lib/top_level.cr index 7f37c4b..a690563 100644 --- a/src/myst/interpreter/native_lib/top_level.cr +++ b/src/myst/interpreter/native_lib/top_level.cr @@ -1,20 +1,14 @@ module Myst class Interpreter - NativeLib.method :mt_exit, MTValue, status : TInteger? do - real_status = - if status.is_a?(TInteger) - status.value - else - 0 - end + NativeLib.method :mt_exit, MTValue, status : Int64? do + real_status = status.is_a?(Int64) ? status : 0 exit(real_status.to_i32) end - NativeLib.method :mt_sleep, MTValue, time : TInteger | TFloat? = nil do - - if time.is_a? TInteger || time.is_a? TFloat - sleep(time.value) + NativeLib.method :mt_sleep, MTValue, time : Int64 | Float64? = nil do + if time.is_a?(Int64) || time.is_a?(Float64) + sleep(time) else sleep end diff --git a/src/myst/interpreter/nodes/call.cr b/src/myst/interpreter/nodes/call.cr index a85fc97..db9f6d3 100644 --- a/src/myst/interpreter/nodes/call.cr +++ b/src/myst/interpreter/nodes/call.cr @@ -3,8 +3,13 @@ module Myst def visit(node : Call) receiver, func = lookup_call(node) - if func + case func + when TFunctor visit_call(node, receiver, func) + when MTValue + # If `func` is _not_ a functor, it must just be a value from a variable + # that didn't get parsed as a Var/Const/etc, so it can + stack.push(func) else if (name = node.name).is_a?(String) __raise_not_found(name, receiver) @@ -77,9 +82,5 @@ module Myst pop_callstack(to_size: original_callstack_size) stack.push(result) end - - private def visit_call(_node, _receiver, value : MTValue) - stack.push(value) - end end end diff --git a/src/myst/interpreter/nodes/literals.cr b/src/myst/interpreter/nodes/literals.cr index e6056a9..44ec202 100644 --- a/src/myst/interpreter/nodes/literals.cr +++ b/src/myst/interpreter/nodes/literals.cr @@ -38,7 +38,7 @@ module Myst strs = node.components.map do |piece| case piece when StringLiteral - MTValue.from_literal(piece).as(TString) + Interpreter.__value_from_literal(piece) else visit(piece) expr_result = stack.pop @@ -49,16 +49,16 @@ module Myst expr_result, [] of MTValue, nil - ).invoke.as(TString) + ).invoke.as(String) end end - full_str = strs.map(&.value).join - stack.push(TString.new(full_str)) + full_str = strs.join + stack.push(full_str) end def visit(node : Literal) - stack.push(MTValue.from_literal(node)) + stack.push(Interpreter.__value_from_literal(node)) end end end diff --git a/src/myst/interpreter/nodes/magic_const.cr b/src/myst/interpreter/nodes/magic_const.cr index e95eeeb..6ea8cf2 100644 --- a/src/myst/interpreter/nodes/magic_const.cr +++ b/src/myst/interpreter/nodes/magic_const.cr @@ -3,11 +3,11 @@ module Myst def visit(node : MagicConst) case node.type when :"__FILE__" - stack.push(TString.new(node.file)) + stack.push(node.file) when :"__LINE__" - stack.push(TInteger.new(node.line.to_i64)) + stack.push(node.line.to_i64) when :"__DIR__" - stack.push(TString.new(node.dir)) + stack.push(node.dir) end end end diff --git a/src/myst/interpreter/nodes/references.cr b/src/myst/interpreter/nodes/references.cr index 9741b35..1736658 100644 --- a/src/myst/interpreter/nodes/references.cr +++ b/src/myst/interpreter/nodes/references.cr @@ -12,7 +12,7 @@ module Myst # reference to it will initialize it to `nil`. Because of that, a # reference to an instance variable will never fail to lookup (even when # spelled incorrectly). - unless ivar = current_self.ivars[node.name]? + if (ivar = current_self.ivars[node.name]?).nil? ivar = current_self.ivars.assign(node.name, TNil.new) end @@ -20,7 +20,14 @@ module Myst end def visit(node : Const) - if value = (current_scope[node.name]? || __typeof(current_self).scope[node.name]? || recursive_lookup(current_self, node.name)) + value = current_scope[node.name]? + if value.nil? + value = __typeof(current_self).scope[node.name]? + end + if value.nil? + value = recursive_lookup(current_self, node.name) + end + if !value.nil? stack.push(value) else __raise_not_found(node.name, current_self) diff --git a/src/myst/interpreter/nodes/require.cr b/src/myst/interpreter/nodes/require.cr index febcee8..f706a40 100644 --- a/src/myst/interpreter/nodes/require.cr +++ b/src/myst/interpreter/nodes/require.cr @@ -11,11 +11,11 @@ module Myst # The path for a require must be a String, otherwise, the require cannot # be successful. - unless path.is_a?(TString) + unless path.is_a?(String) __raise_runtime_error("Path for `require` must be a String. Got #{path}") end - path_str = path.value + path_str = path # The working directory for the require is always the directory of the @@ -30,7 +30,7 @@ module Myst full_path = resolve_path(path_str, working_dir) # If the file has already been loaded, return false. if @loaded_files[full_path]? - stack.push(TBoolean.new(false)) + stack.push(false) return end @@ -43,7 +43,7 @@ module Myst # the stack. Instead, the return value of a `require` should be either # `true` or `false`, so it must be replaced. @stack.pop - @stack.push(TBoolean.new(true)) + @stack.push(true) end diff --git a/src/myst/interpreter/nodes/unary_ops.cr b/src/myst/interpreter/nodes/unary_ops.cr index 111ddf4..2f3215e 100644 --- a/src/myst/interpreter/nodes/unary_ops.cr +++ b/src/myst/interpreter/nodes/unary_ops.cr @@ -17,7 +17,7 @@ module Myst not_method = not_method.as(TFunctor) Invocation.new(self, not_method, value, [] of MTValue , nil).invoke else - TBoolean.new(!value.truthy?) + !value.truthy? end stack.push(result) diff --git a/src/myst/interpreter/scope.cr b/src/myst/interpreter/scope.cr index 0ca786f..dd82392 100644 --- a/src/myst/interpreter/scope.cr +++ b/src/myst/interpreter/scope.cr @@ -14,7 +14,11 @@ module Myst # # The longhand `has_key?` and `assign` only operate on this scope. def []?(key : String) : MTValue? - @values[key]? || @parent.try(&.[key]?) + found = @values[key]? + if found.nil? + found = @parent.try(&.[key]?) + end + found end # A non-nilable variant of `[]?`. While this method may raise an exception, @@ -22,7 +26,11 @@ module Myst # reachable by userland code). Any instance where the exception propogates # outside of the interpreter should be considered a bug. def [](key : String) : MTValue - self[key]? || raise IndexError.new("Interpeter Bug: Unmanaged, failed attempt to access `#{key}` from scope: #{self.inspect}") + found = self[key]? + if found.nil? + raise IndexError.new("Interpeter Bug: Unmanaged, failed attempt to access `#{key}` from scope: #{self.inspect}") + end + found end def []=(key : String, value : MTValue) : MTValue @@ -40,7 +48,7 @@ module Myst def has_key?(key : String) - !!@values[key]? + @values.has_key?(key) end def assign(key : String, value : MTValue) diff --git a/src/myst/interpreter/util.cr b/src/myst/interpreter/util.cr index 00cf016..de3bb8f 100644 --- a/src/myst/interpreter/util.cr +++ b/src/myst/interpreter/util.cr @@ -1,5 +1,24 @@ module Myst class Interpreter + def self.__value_from_literal(literal : Node) + case literal + when IntegerLiteral + literal.value.to_i64 + when FloatLiteral + literal.value.to_f64 + when StringLiteral + literal.value + when SymbolLiteral + TSymbol.new(literal.value) + when BooleanLiteral + literal.value + when NilLiteral + TNil.new + else + raise "Interpreter Bug: Attempting to create an MTValue from a #{literal.class}, which is not a valid Literal type." + end + end + # Resolve the TType object representing the type of `value`. For primitive # types, these are _always_ looked up in the Kernel. For Instances, the # type is looked up from the type reference on the instance itself. For @@ -37,9 +56,9 @@ module Myst # raise an appropriate error if the given value is a primitive. # If `operation` is given, it will be used as the error message. macro __disallow_primitives(value, operation=nil) - if {{value}}.is_a?(TInteger) || {{value}}.is_a?(TFloat) || - {{value}}.is_a?(TNil) || {{value}}.is_a?(TBoolean) || - {{value}}.is_a?(TString) + if {{value}}.is_a?(Int64) || {{value}}.is_a?(Float64) || + {{value}}.is_a?(TNil) || {{value}}.is_a?(Bool) || + {{value}}.is_a?(String) __raise_runtime_error({{operation || "Operation disallowed on primitive types"}}) end end @@ -49,7 +68,7 @@ module Myst # ancestors. If the value is not found, a `No variable or method` # RuntimeError will be raised. def lookup(node) - if value = current_scope[node.name]? + unless (value = current_scope[node.name]?).nil? value else __raise_not_found(node.name, current_self) @@ -62,19 +81,24 @@ module Myst # # The method will return `nil` if no matching entry is found. def recursive_lookup(receiver, name, check_current = true) - func = current_scope[name] if check_current && current_scope.has_key?(name) - func ||= __scopeof(receiver)[name]? - case receiver - when TType - func ||= receiver.extended_ancestors.each do |anc| - if found = __scopeof(anc)[name]? - break found + func = current_scope[name] if check_current && current_scope.has_key?(name) + if func.nil? + func = __scopeof(receiver)[name]? + end + + if func.nil? + case receiver + when TType + func ||= receiver.extended_ancestors.each do |anc| + unless (found = __scopeof(anc)[name]?).nil? + break found + end end - end - else - func ||= __typeof(receiver).ancestors.each do |anc| - if found = __scopeof(anc, prefer_instance_scope: true)[name]? - break found + else + func ||= __typeof(receiver).ancestors.each do |anc| + unless (found = __scopeof(anc, prefer_instance_scope: true)[name]?).nil? + break found + end end end end @@ -88,7 +112,7 @@ module Myst error_message = "No variable or method `#{name}` for #{type_name}" if value_to_s = __scopeof(value)["to_s"]? - value_str = NativeLib.call_func_by_name(self, value, "to_s", [] of MTValue).as(TString).value + value_str = NativeLib.call_func_by_name(self, value, "to_s", [] of MTValue) error_message = "No variable or method `#{name}` for #{value_str}:#{type_name}" end @@ -102,7 +126,7 @@ module Myst # Multiple overloads of this function are provided for simplicity at the # call site. def __raise_runtime_error(message : String) - raise RuntimeError.new(TString.new(message), callstack) + raise RuntimeError.new(message, callstack) end def __raise_runtime_error(value : MTValue) diff --git a/src/myst/interpreter/value.cr b/src/myst/interpreter/value.cr index 28131e5..593842a 100644 --- a/src/myst/interpreter/value.cr +++ b/src/myst/interpreter/value.cr @@ -1,31 +1,43 @@ module Myst - abstract class MTValue - def MTValue.from_literal(literal : Node) - case literal - when IntegerLiteral - TInteger.new(literal.value.to_i64) - when FloatLiteral - TFloat.new(literal.value.to_f64) - when StringLiteral - TString.new(literal.value) - when SymbolLiteral - TSymbol.new(literal.value) - when BooleanLiteral - TBoolean.new(literal.value) - when NilLiteral - TNil.new - else - raise "Interpreter Bug: Attempting to create an MTValue from a #{literal.class}, which is not a valid Literal type." - end - end + alias MTValue = Int64 | Float64 | Bool | String | MutableValue + + # Define a few methods on the primitive types to allow the interpreter to + # treat them like any other value. + struct ::Int64 + def type_name; "Integer"; end + def truthy?; true; end + + def ivars; raise "Primitive values cannot have instance variables"; end + end + + struct ::Float64 + def type_name; "Float"; end + def truthy?; true; end + + def ivars; raise "Primitive values cannot have instance variables"; end + end + + struct ::Bool + def type_name; "Boolean"; end + def truthy?; self; end + + def ivars; raise "Primitive values cannot have instance variables"; end + end + + class ::String + def type_name; "String"; end + def truthy?; true; end + + def ivars; raise "Primitive values cannot have instance variables"; end + end + abstract class MutableValue # Instance variables are properties tied to the instance of an object. # For consistency between native (Integer, String, etc.) and language- # level types (IO, File, etc.), all values have an `ivars` property. property ivars : Scope = Scope.new - def truthy? true end @@ -35,7 +47,7 @@ module Myst end end - abstract class ContainerType < MTValue + abstract class ContainerType < MutableValue property name : String = "" # Ancestors are the modules that have been included inside of a Type. For # example, if a module includes Enumerable, then the ancestors for that @@ -86,7 +98,7 @@ module Myst end def ttype_to_s(_a, _b, _c) - TString.new(@name).as(MTValue) + @name.as(MTValue) end def type_name @@ -128,7 +140,7 @@ module Myst def_equals_and_hash name, scope, instance_scope end - class TInstance < MTValue + class TInstance < MutableValue property type : TType property scope : Scope @@ -147,21 +159,7 @@ module Myst def_equals_and_hash type, scope end - - # Primitives are immutable objects - abstract class TPrimitive(T) < MTValue - property value : T - - def initialize(@value : T); end - - def to_s - value.to_s - end - - def_equals_and_hash value - end - - class TNil < MTValue + class TNil < MutableValue # All instances of Nil in a program refer to the same object. NIL_OBJECT = TNil.allocate @@ -184,59 +182,10 @@ module Myst def_equals_and_hash end - class TBoolean < TPrimitive(Bool) - def to_s - @value ? "true" : "false" - end - - def truthy? - @value - end - - def type_name - "Boolean" - end - end - - class TInteger < TPrimitive(Int64) - def ==(other : TFloat) - self.value == other.value - end - - def type_name - "Integer" - end - end - - class TFloat < TPrimitive(Float64) - def ==(other : TInteger) - self.value == other.value - end - - def type_name - "Float" - end - end - - class TString < TPrimitive(String) - def type_name - "String" - end - end - - class TSymbol < TPrimitive(UInt64) + class TSymbol < MutableValue SYMBOLS = {} of String => TSymbol @@next_id = 0_u64 - property name : String - - def initialize(@value : UInt64, @name : String) - end - - def type_name - "Symbol" - end - def self.new(name) # TODO: Revert to the following once Crystal 0.24.0 is released. This # currently causes a bug (see crystal-lang/crystal#4600). @@ -252,10 +201,27 @@ module Myst SYMBOLS[name] = instance end end + + + property value : UInt64 + property name : String + + def initialize(@value : UInt64, @name : String) + end + + def to_s + value.to_s + end + + def type_name + "Symbol" + end + + def_equals_and_hash value end - class TList < MTValue + class TList < MutableValue property elements : Array(MTValue) def initialize(@elements=[] of MTValue) @@ -273,7 +239,7 @@ module Myst def_equals_and_hash elements end - class TMap < MTValue + class TMap < MutableValue property entries : Hash(MTValue, MTValue) def initialize(@entries={} of MTValue => MTValue) @@ -287,7 +253,7 @@ module Myst end - class TFunctorDef < MTValue + class TFunctorDef < MutableValue property definition : Def delegate params, block_param, block_param?, body, splat_index?, splat_index, to: definition @@ -304,7 +270,7 @@ module Myst # A Functor is a container for multiple functor definitions, which can either # be language-level or native. - class TFunctor < MTValue + class TFunctor < MutableValue property name : String property clauses : Array(Callable) property lexical_scope : Scope