Skip to content

Commit

Permalink
[interpreter] (#18) Dynamically resolve types of Values.
Browse files Browse the repository at this point in the history
Instead of having each `Value` type define `.type`, the type is now determined via a call to `__typeof` (and the scope via `__scopeof`). This allows the interpreter to be fully encapsulated, meaning that programs can freely modify the Kernel types (e.g., define new methods on `List`) without affecting other instances of the Interpreter.
  • Loading branch information
faultyserver committed Oct 24, 2017
1 parent 5628e61 commit 8d76cbc
Show file tree
Hide file tree
Showing 18 changed files with 540 additions and 477 deletions.
34 changes: 1 addition & 33 deletions spec/value_spec.cr → spec/interpreter/value_spec.cr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
require "./spec_helper.cr"
require "../spec_helper.cr"

describe "Values" do
describe "::from_literal" do
Expand Down Expand Up @@ -40,10 +40,6 @@ describe "Values" do


describe "TNil" do
it "has a type name of Nil" do
TNil.new.type.name.should eq("Nil")
end

it "has a string representation of `nil`" do
TNil.new.to_s.should eq("nil")
end
Expand All @@ -66,10 +62,6 @@ describe "Values" do
end

describe "TBoolean" do
it "has a type name of Boolean" do
TBoolean.new(false).type.name.should eq("Boolean")
end

it "always equates FALSE and FALSE" do
TBoolean.new(false).should eq(TBoolean.new(false))
end
Expand Down Expand Up @@ -108,10 +100,6 @@ describe "Values" do
end

describe "TInteger" do
it "has a type name of Integer" do
TInteger.new(1_i64).type.name.should eq("Integer")
end

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)
Expand Down Expand Up @@ -146,10 +134,6 @@ describe "Values" do
end

describe "TFloat" do
it "has a type name of Float" do
TFloat.new(1.0).type.name.should eq("Float")
end

it "can contain any 64-bit float value" do
TFloat.new( 1.7976931348623157e+308)
TFloat.new(-1.7976931348623157e+308)
Expand Down Expand Up @@ -184,10 +168,6 @@ describe "Values" do
end

describe "TString" do
it "has a type name of String" do
TString.new("").type.name.should eq("String")
end

it "can contain strings of arbitrary length" do
TString.new("hello"*1000)
end
Expand Down Expand Up @@ -227,10 +207,6 @@ hi
end

describe "TSymbol" do
it "has a type name of Symbol" do
TSymbol.new("").type.name.should eq("Symbol")
end

it "can be created from any string value" do
TSymbol.new("hello"*1000)
TSymbol.new("hello\n\t\0")
Expand Down Expand Up @@ -258,10 +234,6 @@ hi


describe "TList" do
it "has a type name of List" do
TList.new.type.name.should eq("List")
end

it "can be created with no elements" do
TList.new
end
Expand Down Expand Up @@ -296,10 +268,6 @@ hi


describe "TMap" do
it "has a type name of Map" do
TMap.new.type.name.should eq("Map")
end

it "can be created with no elements" do
TMap.new
end
Expand Down
36 changes: 18 additions & 18 deletions src/myst/interpreter.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module Myst
class Interpreter
property stack : Array(Value)
property self_stack : Array(Value)
property kernel = TModule.new("Kernel")

property output : IO
property errput : IO
Expand All @@ -14,32 +15,31 @@ module Myst
def initialize(@output : IO = STDOUT, @errput : IO = STDERR)
@stack = [] of Value
@scope_stack = [] of Scope
@self_stack = [KERNEL] of Value
@self_stack = [@kernel] of Value

init_kernel
end

private def init_kernel
KERNEL.scope.clear
KERNEL.scope["Nil"] = NIL_TYPE
KERNEL.scope["Boolean"] = BOOLEAN_TYPE
KERNEL.scope["Integer"] = INTEGER_TYPE
KERNEL.scope["Float"] = FLOAT_TYPE
KERNEL.scope["String"] = STRING_TYPE
KERNEL.scope["Symbol"] = SYMBOL_TYPE
KERNEL.scope["List"] = LIST_TYPE
KERNEL.scope["Map"] = MAP_TYPE
KERNEL.scope["Functor"] = FUNCTOR_TYPE
KERNEL.scope["FunctorDef"] = FUNCTOR_DEF_TYPE
KERNEL.scope["NativeDef"] = NATIVE_DEF_TYPE
KERNEL.scope["Module"] = MODULE_TYPE
KERNEL.scope["Type"] = TYPE_TYPE
KERNEL.scope["IO"] = IO_MODULE
init_nil
init_boolean
init_integer
init_float
init_string
init_symbol
init_list
init_map
init_io
# @kernel.scope["Functor"] = FUNCTOR_TYPE
# @kernel.scope["FunctorDef"] = FUNCTOR_DEF_TYPE
# @kernel.scope["NativeDef"] = NATIVE_DEF_TYPE
# @kernel.scope["Module"] = MODULE_TYPE
# @kernel.scope["Type"] = TYPE_TYPE
end


def current_scope
scope_override || current_self.scope
scope_override || __scopeof(current_self)
end

def scope_override
Expand Down Expand Up @@ -74,7 +74,7 @@ module Myst
end

def put_error(error : RuntimeError)
value_to_s = error.value.scope["to_s"].as(TFunctor)
value_to_s = __scopeof(error.value)["to_s"].as(TFunctor)
result = Invocation.new(self, value_to_s, error.value, [] of Value, nil).invoke
@errput.puts(result.as(TString).value)
end
Expand Down
2 changes: 1 addition & 1 deletion src/myst/interpreter/native_lib.cr
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module Myst
# 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))
func = receiver.scope[name].as(TFunctor)
func = itr.__scopeof(receiver)[name].as(TFunctor)
Invocation.new(itr, func, receiver, args, nil).invoke
end
end
Expand Down
59 changes: 33 additions & 26 deletions src/myst/interpreter/native_lib/boolean.cr
Original file line number Diff line number Diff line change
@@ -1,31 +1,38 @@
module Myst
BOOLEAN_TYPE.instance_scope["to_s"] = TFunctor.new([
TNativeDef.new(0) do |this, _args, _block, _itr|
TString.new(this.as(TBoolean).value ? "true" : "false")
end
] of Callable)
class Interpreter
def init_boolean
bool_type = TType.new("Boolean")
bool_type.instance_scope["to_s"] = TFunctor.new([
TNativeDef.new(0) do |this, _args, _block, _itr|
TString.new(this.as(TBoolean).value ? "true" : "false")
end
] of Callable)

BOOLEAN_TYPE.instance_scope["=="] = TFunctor.new([
TNativeDef.new(1) do |this, (arg), _block, _itr|
this = this.as(TBoolean)
case arg
when TBoolean
TBoolean.new(this.value == arg.value)
else
TBoolean.new(false)
end
end
] of Callable)
bool_type.instance_scope["=="] = TFunctor.new([
TNativeDef.new(1) do |this, (arg), _block, _itr|
this = this.as(TBoolean)
case arg
when TBoolean
TBoolean.new(this.value == arg.value)
else
TBoolean.new(false)
end
end
] of Callable)

bool_type.instance_scope["!="] = TFunctor.new([
TNativeDef.new(1) do |this, (arg), _block, _itr|
this = this.as(TBoolean)
case arg
when TBoolean
TBoolean.new(this.value != arg.value)
else
TBoolean.new(true)
end
end
] of Callable)

BOOLEAN_TYPE.instance_scope["!="] = TFunctor.new([
TNativeDef.new(1) do |this, (arg), _block, _itr|
this = this.as(TBoolean)
case arg
when TBoolean
TBoolean.new(this.value != arg.value)
else
TBoolean.new(true)
end
@kernel.scope["Boolean"] = bool_type
end
] of Callable)
end
end
Loading

0 comments on commit 8d76cbc

Please sign in to comment.