From 8d76cbc3af24bc1c24b4102bfaf2c3c549324c38 Mon Sep 17 00:00:00 2001 From: Jon Egeland Date: Mon, 23 Oct 2017 22:00:44 -0400 Subject: [PATCH] [interpreter] (#18) Dynamically resolve types of Values. 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. --- spec/{ => interpreter}/value_spec.cr | 34 +--- src/myst/interpreter.cr | 36 ++-- src/myst/interpreter/native_lib.cr | 2 +- src/myst/interpreter/native_lib/boolean.cr | 59 +++--- src/myst/interpreter/native_lib/float.cr | 176 +++++++++--------- src/myst/interpreter/native_lib/integer.cr | 199 +++++++++++---------- src/myst/interpreter/native_lib/io.cr | 34 ++-- src/myst/interpreter/native_lib/list.cr | 25 ++- src/myst/interpreter/native_lib/map.cr | 23 +++ src/myst/interpreter/native_lib/nil.cr | 59 +++--- src/myst/interpreter/native_lib/string.cr | 105 ++++++----- src/myst/interpreter/native_lib/symbol.cr | 61 ++++--- src/myst/interpreter/nodes/call.cr | 6 +- src/myst/interpreter/nodes/def.cr | 2 +- src/myst/interpreter/nodes/include.cr | 7 +- src/myst/interpreter/nodes/module_def.cr | 2 +- src/myst/interpreter/util.cr | 67 +++++++ src/myst/interpreter/value.cr | 120 ++++--------- 18 files changed, 540 insertions(+), 477 deletions(-) rename spec/{ => interpreter}/value_spec.cr (91%) create mode 100644 src/myst/interpreter/native_lib/map.cr create mode 100644 src/myst/interpreter/util.cr diff --git a/spec/value_spec.cr b/spec/interpreter/value_spec.cr similarity index 91% rename from spec/value_spec.cr rename to spec/interpreter/value_spec.cr index 1bdcc0c..15f1fde 100644 --- a/spec/value_spec.cr +++ b/spec/interpreter/value_spec.cr @@ -1,4 +1,4 @@ -require "./spec_helper.cr" +require "../spec_helper.cr" describe "Values" do describe "::from_literal" do @@ -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 @@ -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 @@ -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) @@ -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) @@ -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 @@ -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") @@ -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 @@ -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 diff --git a/src/myst/interpreter.cr b/src/myst/interpreter.cr index 43e2d9a..d250472 100644 --- a/src/myst/interpreter.cr +++ b/src/myst/interpreter.cr @@ -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 @@ -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 @@ -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 diff --git a/src/myst/interpreter/native_lib.cr b/src/myst/interpreter/native_lib.cr index 28a00d1..4d8bacf 100644 --- a/src/myst/interpreter/native_lib.cr +++ b/src/myst/interpreter/native_lib.cr @@ -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 diff --git a/src/myst/interpreter/native_lib/boolean.cr b/src/myst/interpreter/native_lib/boolean.cr index a32e66b..2d491f3 100644 --- a/src/myst/interpreter/native_lib/boolean.cr +++ b/src/myst/interpreter/native_lib/boolean.cr @@ -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 diff --git a/src/myst/interpreter/native_lib/float.cr b/src/myst/interpreter/native_lib/float.cr index d019359..e405e45 100644 --- a/src/myst/interpreter/native_lib/float.cr +++ b/src/myst/interpreter/native_lib/float.cr @@ -1,94 +1,102 @@ module Myst - FLOAT_TYPE.instance_scope["+"] = TFunctor.new([ - TNativeDef.new(1) do |this, (arg), _block, _itr| - this = this.as(TFloat) - case arg - when TInteger, TFloat - TFloat.new(this.value + arg.value) - else - raise "invalid argument for Float#+: #{arg.type.name}" - end - end - ] of Callable) + class Interpreter + def init_float + float_type = TType.new("Float") + float_type.instance_scope["+"] = TFunctor.new([ + TNativeDef.new(1) do |this, (arg), _block, _itr| + this = this.as(TFloat) + case arg + when TInteger, TFloat + TFloat.new(this.value + arg.value) + else + raise "invalid argument for Float#+: #{__typeof(arg).name}" + end + end + ] of Callable) - FLOAT_TYPE.instance_scope["-"] = TFunctor.new([ - TNativeDef.new(1) do |this, (arg), _block, _itr| - this = this.as(TFloat) - case arg - when TInteger, TFloat - TFloat.new(this.value - arg.value) - else - raise "invalid argument for Float#-: #{arg.type.name}" - end - end - ] of Callable) + float_type.instance_scope["-"] = TFunctor.new([ + TNativeDef.new(1) do |this, (arg), _block, _itr| + this = this.as(TFloat) + case arg + when TInteger, TFloat + TFloat.new(this.value - arg.value) + else + raise "invalid argument for Float#-: #{__typeof(arg).name}" + end + end + ] of Callable) - FLOAT_TYPE.instance_scope["*"] = TFunctor.new([ - TNativeDef.new(1) do |this, (arg), _block, _itr| - this = this.as(TFloat) - case arg - when TInteger, TFloat - TFloat.new(this.value * arg.value) - else - raise "invalid argument for Float#*: #{arg.type.name}" - end - end - ] of Callable) + float_type.instance_scope["*"] = TFunctor.new([ + TNativeDef.new(1) do |this, (arg), _block, _itr| + this = this.as(TFloat) + case arg + when TInteger, TFloat + TFloat.new(this.value * arg.value) + else + raise "invalid argument for Float#*: #{__typeof(arg).name}" + end + end + ] of Callable) - FLOAT_TYPE.instance_scope["/"] = TFunctor.new([ - TNativeDef.new(1) do |this, (arg), _block, _itr| - this = this.as(TFloat) - case arg - when TInteger, TFloat - raise "Division by zero" if arg.value == 0 - TFloat.new(this.value / arg.value) - else - raise "invalid argument for Float#/: #{arg.type.name}" - end - end - ] of Callable) + float_type.instance_scope["/"] = TFunctor.new([ + TNativeDef.new(1) do |this, (arg), _block, _itr| + this = this.as(TFloat) + case arg + when TInteger, TFloat + raise "Division by zero" if arg.value == 0 + TFloat.new(this.value / arg.value) + else + raise "invalid argument for Float#/: #{__typeof(arg).name}" + end + end + ] of Callable) - FLOAT_TYPE.instance_scope["%"] = TFunctor.new([ - TNativeDef.new(1) do |this, (arg), _block, _itr| - this = this.as(TFloat) - case arg - when TInteger, TFloat - raise "Division by zero" if arg.value == 0 - TFloat.new(this.value % arg.value) - else - raise "invalid argument for Float#%: #{arg.type.name}" - end - end - ] of Callable) + float_type.instance_scope["%"] = TFunctor.new([ + TNativeDef.new(1) do |this, (arg), _block, _itr| + this = this.as(TFloat) + case arg + when TInteger, TFloat + raise "Division by zero" if arg.value == 0 + TFloat.new(this.value % arg.value) + else + raise "invalid argument for Float#%: #{__typeof(arg).name}" + end + end + ] of Callable) - FLOAT_TYPE.instance_scope["to_s"] = TFunctor.new([ - TNativeDef.new(0) do |this, _args, _block, _itr| - this = this.as(TFloat) - TString.new(this.value.to_s) - end - ] of Callable) + float_type.instance_scope["to_s"] = TFunctor.new([ + TNativeDef.new(0) do |this, _args, _block, _itr| + this = this.as(TFloat) + TString.new(this.value.to_s) + end + ] of Callable) + + float_type.instance_scope["=="] = TFunctor.new([ + TNativeDef.new(1) do |this, (arg), _block, _itr| + this = this.as(TFloat) + case arg + when TFloat, TInteger + TBoolean.new(this.value == arg.value) + else + TBoolean.new(false) + end + end + ] of Callable) + + float_type.instance_scope["!="] = TFunctor.new([ + TNativeDef.new(1) do |this, (arg), _block, _itr| + this = this.as(TFloat) + case arg + when TFloat, TInteger + TBoolean.new(this.value != arg.value) + else + TBoolean.new(true) + end + end + ] of Callable) - FLOAT_TYPE.instance_scope["=="] = TFunctor.new([ - TNativeDef.new(1) do |this, (arg), _block, _itr| - this = this.as(TFloat) - case arg - when TFloat, TInteger - TBoolean.new(this.value == arg.value) - else - TBoolean.new(false) - end - end - ] of Callable) - FLOAT_TYPE.instance_scope["!="] = TFunctor.new([ - TNativeDef.new(1) do |this, (arg), _block, _itr| - this = this.as(TFloat) - case arg - when TFloat, TInteger - TBoolean.new(this.value != arg.value) - else - TBoolean.new(true) - end + @kernel.scope["Float"] = float_type end - ] of Callable) + end end diff --git a/src/myst/interpreter/native_lib/integer.cr b/src/myst/interpreter/native_lib/integer.cr index 73e714b..3b11a32 100644 --- a/src/myst/interpreter/native_lib/integer.cr +++ b/src/myst/interpreter/native_lib/integer.cr @@ -1,107 +1,114 @@ module Myst - INTEGER_TYPE.instance_scope["+"] = TFunctor.new([ - TNativeDef.new(1) do |this, (arg), _block, _itr| - this = this.as(TInteger) - case arg - when TInteger - TInteger.new(this.value + arg.value) - when TFloat - TFloat.new(this.value + arg.value) - else - raise "invalid argument for Integer#+: #{arg.type.name}" - end - end - ] of Callable) + class Interpreter + def init_integer + integer_type = TType.new("Integer") + integer_type.instance_scope["+"] = TFunctor.new([ + TNativeDef.new(1) do |this, (arg), _block, _itr| + this = this.as(TInteger) + case arg + when TInteger + TInteger.new(this.value + arg.value) + when TFloat + TFloat.new(this.value + arg.value) + else + raise "invalid argument for Integer#+: #{__typeof(arg).name}" + end + end + ] of Callable) - INTEGER_TYPE.instance_scope["-"] = TFunctor.new([ - TNativeDef.new(1) do |this, (arg), _block, _itr| - this = this.as(TInteger) - case arg - when TInteger - TInteger.new(this.value - arg.value) - when TFloat - TFloat.new(this.value - arg.value) - else - raise "invalid argument for Integer#-: #{arg.type.name}" - end - end - ] of Callable) + integer_type.instance_scope["-"] = TFunctor.new([ + TNativeDef.new(1) do |this, (arg), _block, _itr| + this = this.as(TInteger) + case arg + when TInteger + TInteger.new(this.value - arg.value) + when TFloat + TFloat.new(this.value - arg.value) + else + raise "invalid argument for Integer#-: #{__typeof(arg).name}" + end + end + ] of Callable) - INTEGER_TYPE.instance_scope["*"] = TFunctor.new([ - TNativeDef.new(1) do |this, (arg), _block, _itr| - this = this.as(TInteger) - case arg - when TInteger - TInteger.new(this.value * arg.value) - when TFloat - TFloat.new(this.value * arg.value) - else - raise "invalid argument for Integer#*: #{arg.type.name}" - end - end - ] of Callable) + integer_type.instance_scope["*"] = TFunctor.new([ + TNativeDef.new(1) do |this, (arg), _block, _itr| + this = this.as(TInteger) + case arg + when TInteger + TInteger.new(this.value * arg.value) + when TFloat + TFloat.new(this.value * arg.value) + else + raise "invalid argument for Integer#*: #{__typeof(arg).name}" + end + end + ] of Callable) - INTEGER_TYPE.instance_scope["/"] = TFunctor.new([ - TNativeDef.new(1) do |this, (arg), _block, _itr| - this = this.as(TInteger) - case arg - when TInteger - raise "Division by zero" if arg.value == 0 - TInteger.new(this.value / arg.value) - when TFloat - raise "Division by zero" if arg.value == 0 - TFloat.new(this.value / arg.value) - else - raise "invalid argument for Integer#/: #{arg.type.name}" - end - end - ] of Callable) + integer_type.instance_scope["/"] = TFunctor.new([ + TNativeDef.new(1) do |this, (arg), _block, _itr| + this = this.as(TInteger) + case arg + when TInteger + raise "Division by zero" if arg.value == 0 + TInteger.new(this.value / arg.value) + when TFloat + raise "Division by zero" if arg.value == 0 + TFloat.new(this.value / arg.value) + else + raise "invalid argument for Integer#/: #{__typeof(arg).name}" + end + end + ] of Callable) - INTEGER_TYPE.instance_scope["%"] = TFunctor.new([ - TNativeDef.new(1) do |this, (arg), _block, _itr| - this = this.as(TInteger) - case arg - when TInteger - raise "Division by zero" if arg.value == 0 - TInteger.new(this.value % arg.value) - when TFloat - raise "Division by zero" if arg.value == 0 - TFloat.new(this.value.to_f % arg.value) - else - raise "invalid argument for Integer#%: #{arg.type.name}" - end - end - ] of Callable) + integer_type.instance_scope["%"] = TFunctor.new([ + TNativeDef.new(1) do |this, (arg), _block, _itr| + this = this.as(TInteger) + case arg + when TInteger + raise "Division by zero" if arg.value == 0 + TInteger.new(this.value % arg.value) + when TFloat + raise "Division by zero" if arg.value == 0 + TFloat.new(this.value.to_f % arg.value) + else + raise "invalid argument for Integer#%: #{__typeof(arg).name}" + end + end + ] of Callable) - INTEGER_TYPE.instance_scope["to_s"] = TFunctor.new([ - TNativeDef.new(0) do |this, _args, _block, _itr| - this = this.as(TInteger) - TString.new(this.value.to_s) - end - ] of Callable) + integer_type.instance_scope["to_s"] = TFunctor.new([ + TNativeDef.new(0) do |this, _args, _block, _itr| + this = this.as(TInteger) + TString.new(this.value.to_s) + end + ] of Callable) - INTEGER_TYPE.instance_scope["=="] = TFunctor.new([ - TNativeDef.new(1) do |this, (arg), _block, _itr| - this = this.as(TInteger) - case arg - when TInteger, TFloat - TBoolean.new(this.value == arg.value) - else - TBoolean.new(false) - end - end - ] of Callable) + integer_type.instance_scope["=="] = TFunctor.new([ + TNativeDef.new(1) do |this, (arg), _block, _itr| + this = this.as(TInteger) + case arg + when TInteger, TFloat + TBoolean.new(this.value == arg.value) + else + TBoolean.new(false) + end + end + ] of Callable) + + integer_type.instance_scope["!="] = TFunctor.new([ + TNativeDef.new(1) do |this, (arg), _block, _itr| + this = this.as(TInteger) + case arg + when TInteger, TFloat + TBoolean.new(this.value != arg.value) + else + TBoolean.new(true) + end + end + ] of Callable) - INTEGER_TYPE.instance_scope["!="] = TFunctor.new([ - TNativeDef.new(1) do |this, (arg), _block, _itr| - this = this.as(TInteger) - case arg - when TInteger, TFloat - TBoolean.new(this.value != arg.value) - else - TBoolean.new(true) - end + @kernel.scope["Integer"] = integer_type end - ] of Callable) + end end diff --git a/src/myst/interpreter/native_lib/io.cr b/src/myst/interpreter/native_lib/io.cr index e9c7bff..8027d75 100644 --- a/src/myst/interpreter/native_lib/io.cr +++ b/src/myst/interpreter/native_lib/io.cr @@ -1,21 +1,27 @@ module Myst - IO_MODULE = TModule.new - IO_MODULE.scope["puts"] = TFunctor.new([ - TNativeDef.new(-1) do |_this, args, _block, itr| - if args.size == 0 - itr.output.puts - else - args.each do |arg| - string = NativeLib.call_func_by_name(itr, arg, "to_s", [] of Value) - if string.is_a?(TString) - itr.output.puts(string.value) + class Interpreter + def init_io + io_module = TModule.new("IO") + io_module.scope["puts"] = TFunctor.new([ + TNativeDef.new(-1) do |_this, args, _block, itr| + if args.size == 0 + itr.output.puts else - raise RuntimeError.new(TString.new("expected String argument. Got #{string.type.name}")) + args.each do |arg| + string = NativeLib.call_func_by_name(itr, arg, "to_s", [] of Value) + if string.is_a?(TString) + itr.output.puts(string.value) + else + raise RuntimeError.new(TString.new("expected String argument. Got #{__typeof(string).name}")) + end + end end + + TNil.new end - end + ] of Callable) - TNil.new + @kernel.scope["IO"] = io_module end - ] of Callable) + end end diff --git a/src/myst/interpreter/native_lib/list.cr b/src/myst/interpreter/native_lib/list.cr index a615279..85b770a 100644 --- a/src/myst/interpreter/native_lib/list.cr +++ b/src/myst/interpreter/native_lib/list.cr @@ -1,15 +1,22 @@ module Myst - LIST_TYPE.instance_scope["each"] = TFunctor.new([ - TNativeDef.new(0) do |this, _args, block, itr| - this = this.as(TList) + class Interpreter + def init_list + list_type = TType.new("List") + list_type.instance_scope["each"] = TFunctor.new([ + TNativeDef.new(0) do |this, _args, block, itr| + this = this.as(TList) - if block - this.elements.each do |elem| - NativeLib.call_func(itr, block, [elem], nil) + if block + this.elements.each do |elem| + NativeLib.call_func(itr, block, [elem], nil) + end + end + + this end - end + ] of Callable) - this + @kernel.scope["List"] = list_type end - ] of Callable) + end end diff --git a/src/myst/interpreter/native_lib/map.cr b/src/myst/interpreter/native_lib/map.cr new file mode 100644 index 0000000..9af8f1b --- /dev/null +++ b/src/myst/interpreter/native_lib/map.cr @@ -0,0 +1,23 @@ +module Myst + class Interpreter + def init_map + map_type = TType.new("Map") + + map_type.instance_scope["each"] = TFunctor.new([ + TNativeDef.new(0) do |this, _args, block, itr| + this = this.as(TMap) + + if block + this.entries.each do |(key, val)| + NativeLib.call_func(itr, block, [key, val], nil) + end + end + + this + end + ] of Callable) + + @kernel.scope["Map"] = map_type + end + end +end diff --git a/src/myst/interpreter/native_lib/nil.cr b/src/myst/interpreter/native_lib/nil.cr index cace17a..f239810 100644 --- a/src/myst/interpreter/native_lib/nil.cr +++ b/src/myst/interpreter/native_lib/nil.cr @@ -1,31 +1,38 @@ module Myst - NIL_TYPE.instance_scope["to_s"] = TFunctor.new([ - TNativeDef.new(0) do |_this, _args, _block, _itr| - TString.new("nil") - end - ] of Callable) + class Interpreter + def init_nil + nil_type = TType.new("Nil") + nil_type.instance_scope["to_s"] = TFunctor.new([ + TNativeDef.new(0) do |_this, _args, _block, _itr| + TString.new("nil") + end + ] of Callable) - NIL_TYPE.instance_scope["=="] = TFunctor.new([ - TNativeDef.new(1) do |this, (arg), _block, _itr| - this = this.as(TNil) - case arg - when TNil - TBoolean.new(true) - else - TBoolean.new(false) - end - end - ] of Callable) + nil_type.instance_scope["=="] = TFunctor.new([ + TNativeDef.new(1) do |this, (arg), _block, _itr| + this = this.as(TNil) + case arg + when TNil + TBoolean.new(true) + else + TBoolean.new(false) + end + end + ] of Callable) + + nil_type.instance_scope["!="] = TFunctor.new([ + TNativeDef.new(1) do |this, (arg), _block, _itr| + this = this.as(TNil) + case arg + when TNil + TBoolean.new(false) + else + TBoolean.new(true) + end + end + ] of Callable) - NIL_TYPE.instance_scope["!="] = TFunctor.new([ - TNativeDef.new(1) do |this, (arg), _block, _itr| - this = this.as(TNil) - case arg - when TNil - TBoolean.new(false) - else - TBoolean.new(true) - end + @kernel.scope["Nil"] = nil_type end - ] of Callable) + end end diff --git a/src/myst/interpreter/native_lib/string.cr b/src/myst/interpreter/native_lib/string.cr index af63cca..e9da8c9 100644 --- a/src/myst/interpreter/native_lib/string.cr +++ b/src/myst/interpreter/native_lib/string.cr @@ -1,56 +1,63 @@ module Myst - STRING_TYPE.instance_scope["+"] = TFunctor.new([ - TNativeDef.new(1) do |this, (arg), _block, _itr| - this = this.as(TString) - case arg - when TString - TString.new(this.value + arg.value) - else - raise "invalid argument for String#+: #{arg.type.name}" - end - end - ] of Callable) + class Interpreter + def init_string + string_type = TType.new("String") + string_type.instance_scope["+"] = TFunctor.new([ + TNativeDef.new(1) do |this, (arg), _block, _itr| + this = this.as(TString) + case arg + when TString + TString.new(this.value + arg.value) + else + raise "invalid argument for String#+: #{__typeof(arg).name}" + end + end + ] of Callable) - STRING_TYPE.instance_scope["*"] = TFunctor.new([ - TNativeDef.new(1) do |this, (arg), _block, _itr| - this = this.as(TString) - case arg - when TInteger - # String multiplication repeats `this` `arg` times. - TString.new(this.value * arg.value) - else - raise "invalid argument for String#*: #{arg.type.name}" - end - end - ] of Callable) + string_type.instance_scope["*"] = TFunctor.new([ + TNativeDef.new(1) do |this, (arg), _block, _itr| + this = this.as(TString) + case arg + when TInteger + # String multiplication repeats `this` `arg` times. + TString.new(this.value * arg.value) + else + raise "invalid argument for String#*: #{__typeof(arg).name}" + end + end + ] of Callable) - STRING_TYPE.instance_scope["to_s"] = TFunctor.new([ - TNativeDef.new(0) do |this, _args, _block, _itr| - this.as(TString) - end - ] of Callable) + string_type.instance_scope["to_s"] = TFunctor.new([ + TNativeDef.new(0) do |this, _args, _block, _itr| + this.as(TString) + end + ] of Callable) - STRING_TYPE.instance_scope["=="] = TFunctor.new([ - TNativeDef.new(1) do |this, (arg), _block, _itr| - this = this.as(TString) - case arg - when TString - TBoolean.new(this.value == arg.value) - else - TBoolean.new(false) - end - end - ] of Callable) + string_type.instance_scope["=="] = TFunctor.new([ + TNativeDef.new(1) do |this, (arg), _block, _itr| + this = this.as(TString) + case arg + when TString + TBoolean.new(this.value == arg.value) + else + TBoolean.new(false) + end + end + ] of Callable) + + string_type.instance_scope["!="] = TFunctor.new([ + TNativeDef.new(1) do |this, (arg), _block, _itr| + this = this.as(TString) + case arg + when TString + TBoolean.new(this.value != arg.value) + else + TBoolean.new(true) + end + end + ] of Callable) - STRING_TYPE.instance_scope["!="] = TFunctor.new([ - TNativeDef.new(1) do |this, (arg), _block, _itr| - this = this.as(TString) - case arg - when TString - TBoolean.new(this.value != arg.value) - else - TBoolean.new(true) - end + @kernel.scope["String"] = string_type end - ] of Callable) + end end diff --git a/src/myst/interpreter/native_lib/symbol.cr b/src/myst/interpreter/native_lib/symbol.cr index d5f0b20..cd006f1 100644 --- a/src/myst/interpreter/native_lib/symbol.cr +++ b/src/myst/interpreter/native_lib/symbol.cr @@ -1,33 +1,40 @@ module Myst - SYMBOL_TYPE.instance_scope["to_s"] = TFunctor.new([ - TNativeDef.new(0) do |this, _args, _block, _itr| - this = this.as(TSymbol) - TString.new(this.name) - end - ] of Callable) + class Interpreter + def init_symbol + symbol_type = TType.new("Symbol") + symbol_type.instance_scope["to_s"] = TFunctor.new([ + TNativeDef.new(0) do |this, _args, _block, _itr| + this = this.as(TSymbol) + TString.new(this.name) + end + ] of Callable) - SYMBOL_TYPE.instance_scope["=="] = TFunctor.new([ - TNativeDef.new(1) do |this, (arg), _block, _itr| - this = this.as(TSymbol) - case arg - when TSymbol - TBoolean.new(this.value == arg.value) - else - TBoolean.new(false) - end - end - ] of Callable) + symbol_type.instance_scope["=="] = TFunctor.new([ + TNativeDef.new(1) do |this, (arg), _block, _itr| + this = this.as(TSymbol) + case arg + when TSymbol + TBoolean.new(this.value == arg.value) + else + TBoolean.new(false) + end + end + ] of Callable) + + symbol_type.instance_scope["!="] = TFunctor.new([ + TNativeDef.new(1) do |this, (arg), _block, _itr| + this = this.as(TSymbol) + case arg + when TSymbol + TBoolean.new(this.value != arg.value) + else + TBoolean.new(true) + end + end + ] of Callable) - SYMBOL_TYPE.instance_scope["!="] = TFunctor.new([ - TNativeDef.new(1) do |this, (arg), _block, _itr| - this = this.as(TSymbol) - case arg - when TSymbol - TBoolean.new(this.value != arg.value) - else - TBoolean.new(true) - end + @kernel.scope["Symbol"] = symbol_type end - ] of Callable) + end end diff --git a/src/myst/interpreter/nodes/call.cr b/src/myst/interpreter/nodes/call.cr index 2f21d34..c0990f7 100644 --- a/src/myst/interpreter/nodes/call.cr +++ b/src/myst/interpreter/nodes/call.cr @@ -12,9 +12,9 @@ module Myst end func = current_scope[node.name] if current_scope.has_key?(node.name) - func ||= receiver.scope[node.name]? - func ||= receiver.ancestors.each do |anc| - if found = anc.scope[node.name]? + func ||= __scopeof(receiver)[node.name]? + func ||= __typeof(receiver).ancestors.each do |anc| + if found = __scopeof(anc)[node.name]? break found end end diff --git a/src/myst/interpreter/nodes/def.cr b/src/myst/interpreter/nodes/def.cr index ea01f66..4ce3976 100644 --- a/src/myst/interpreter/nodes/def.cr +++ b/src/myst/interpreter/nodes/def.cr @@ -9,7 +9,7 @@ module Myst type.instance_scope when {Value, true} # Any other kind of value is not allowed to define static methods. - raise "Cannot define static method on #{current_self.type.name}" + raise "Cannot define static method on #{__typeof(current_self).name}" else current_scope end diff --git a/src/myst/interpreter/nodes/include.cr b/src/myst/interpreter/nodes/include.cr index b7d9267..c950bb0 100644 --- a/src/myst/interpreter/nodes/include.cr +++ b/src/myst/interpreter/nodes/include.cr @@ -7,7 +7,12 @@ module Myst raise "Cannot include non-module value. Got #{_module}" end - current_self.insert_ancestor(_module) + slf = current_self + if slf.is_a?(ContainerType) + slf.insert_ancestor(_module) + else + raise "Cannot include in non-container type." + end # The result of an Include is the module that was included. stack.push(_module) diff --git a/src/myst/interpreter/nodes/module_def.cr b/src/myst/interpreter/nodes/module_def.cr index e6d0b03..38dbe73 100644 --- a/src/myst/interpreter/nodes/module_def.cr +++ b/src/myst/interpreter/nodes/module_def.cr @@ -6,7 +6,7 @@ module Myst if current_scope.has_key?(node.name) _module = current_scope[node.name].as(TModule) else - _module = TModule.new(current_scope) + _module = TModule.new(node.name, current_scope) current_scope.assign(node.name, _module) end diff --git a/src/myst/interpreter/util.cr b/src/myst/interpreter/util.cr new file mode 100644 index 0000000..58ae45d --- /dev/null +++ b/src/myst/interpreter/util.cr @@ -0,0 +1,67 @@ +module Myst + class Interpreter + # 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 + # Types and Modules, the value itself is returned. + def __typeof(value : Value) + case value + when TNil + @kernel.scope["Nil"].as(TType) + when TBoolean + @kernel.scope["Boolean"].as(TType) + when TInteger + @kernel.scope["Integer"].as(TType) + when TFloat + @kernel.scope["Float"].as(TType) + when TString + @kernel.scope["String"].as(TType) + when TSymbol + @kernel.scope["Symbol"].as(TType) + when TList + @kernel.scope["List"].as(TType) + when TMap + @kernel.scope["Map"].as(TType) + when TFunctor + @kernel.scope["Functor"].as(TType) + when TFunctorDef + @kernel.scope["FunctorDef"].as(TType) + when TNativeDef + @kernel.scope["NativeDef"].as(TType) + when TInstance + value.type + when TType + value + when TModule + value + else + raise "Can't resolve type of #{value}" + end + end + + # 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) : Scope + case value + when TInstance + value.scope + when ContainerType + value.scope + else + __typeof(value).as(TType).instance_scope + end + end + + # Primitive types have some restrictions on functionality. This method will + # 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) + raise {{operation || "Operation disallowed on primitive types"}} + end + end + end +end diff --git a/src/myst/interpreter/value.cr b/src/myst/interpreter/value.cr index e8a623a..2caaa81 100644 --- a/src/myst/interpreter/value.cr +++ b/src/myst/interpreter/value.cr @@ -25,35 +25,10 @@ module Myst # level types (IO, File, etc.), all values have an `ivars` property. property ivars : Scope = Scope.new - # Ancestors are the modules that have been included inside of a Type. For - # example, if a module includes Enumerable, then the ancestors for that - # module will contain Enumerable. The order of ancestors is from most to - # least recent (the last `include` will be first in this list). - property included_modules = [] of TModule - - def ancestors : Array(TModule) - @included_modules.reduce(Set(TModule).new) do |acc, mod| - acc.add(mod) - acc.concat(mod.ancestors) - end.to_a - end - - def insert_ancestor(anc : TModule) - @included_modules.unshift(anc) - end - def truthy? true end - - def type - raise "Compiler bug: Unknown type for value #{self}" - end - - def scope - self.type.instance_scope - end end @@ -72,8 +47,38 @@ module Myst MODULE_TYPE = TType.new("Module", KERNEL.scope) TYPE_TYPE = TType.new("Type", KERNEL.scope) - class TType < Value - property name : String + abstract class ContainerType < Value + 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 + # module will contain Enumerable. The order of ancestors is from most to + # least recent (the last `include` will be first in this list). + property included_modules = [] of TModule + + def ancestors : Array(TModule) + @included_modules.reduce(Set(TModule).new) do |acc, mod| + acc.add(mod) + acc.concat(mod.ancestors) + end.to_a + end + + def insert_ancestor(anc : TModule) + @included_modules.unshift(anc) + end + end + + class TModule < ContainerType + property scope : Scope + + def initialize(name : String? = nil, parent : Scope? = nil) + @name = name if name + @scope = Scope.new(parent) + end + + def_equals_and_hash scope + end + + class TType < ContainerType property scope : Scope property instance_scope : Scope @@ -82,10 +87,6 @@ module Myst @instance_scope = Scope.new(parent) end - def type - TYPE_TYPE - end - def_equals_and_hash name, scope, instance_scope end @@ -132,10 +133,6 @@ module Myst false end - def type - NIL_TYPE - end - def_equals_and_hash end @@ -147,36 +144,21 @@ module Myst def truthy? @value end - - def type - BOOLEAN_TYPE - end end class TInteger < TPrimitive(Int64) def ==(other : TFloat) self.value == other.value end - - def type - INTEGER_TYPE - end end class TFloat < TPrimitive(Float64) def ==(other : TInteger) self.value == other.value end - - def type - FLOAT_TYPE - end end class TString < TPrimitive(String) - def type - STRING_TYPE - end end class TSymbol < TPrimitive(UInt64) @@ -195,10 +177,6 @@ module Myst instance end end - - def type - SYMBOL_TYPE - end end @@ -208,10 +186,6 @@ module Myst def initialize(@elements=[] of Value) end - def type - LIST_TYPE - end - def_equals_and_hash elements end @@ -221,10 +195,6 @@ module Myst def initialize(@entries={} of Value => Value) end - def type - MAP_TYPE - end - def_equals_and_hash entries end @@ -246,10 +216,6 @@ module Myst clauses.push(definition) end - def type - FUNCTOR_TYPE - end - def_equals_and_hash clauses, lexical_scope, parent? end @@ -261,10 +227,6 @@ module Myst def initialize(@definition : Def) end - def type - FUNCTOR_DEF_TYPE - end - def_equals_and_hash definition end @@ -276,24 +238,6 @@ module Myst def initialize(@arity : Int32, &@impl : FuncT) end - def type - NATIVE_DEF_TYPE - end - def_equals_and_hash impl end - - class TModule < Value - property scope : Scope - - def initialize(parent : Scope? = nil) - @scope = Scope.new(parent) - end - - def type - MODULE_TYPE - end - - def_equals_and_hash scope - end end