diff --git a/src/main/java/RArray.java b/src/main/java/RArray.java index e2cf867..44a2279 100644 --- a/src/main/java/RArray.java +++ b/src/main/java/RArray.java @@ -1,14 +1,36 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; import java.util.List; -import java.util.ListIterator; public class RArray extends RObject { public final ArrayList impl; + public static class ArrayMeta extends ObjectMeta { + public ArrayMeta(String name) { + super(name); + } + + public ArrayMeta() { + super("Array"); + } + + @Override + public RObject $new(RObject... args) { + return new RArray(args); + } + + @Override + public RObject $new() { + return new RArray(); + } + + @Override + public RObject $new(RObject arg) { + return new RArray(arg); + } + } + public RArray() { impl = new ArrayList(); } @@ -21,6 +43,10 @@ public RArray(List impl) { this.impl = new ArrayList(impl); } + public RClass $class() { + return RArray; + } + public RObject $less$less(RObject what) { impl.add(what); diff --git a/src/main/java/RBoolean.java b/src/main/java/RBoolean.java index 47083da..2a4d3db 100644 --- a/src/main/java/RBoolean.java +++ b/src/main/java/RBoolean.java @@ -1,8 +1,19 @@ public class RBoolean extends RObject { public final boolean bool; + + public static class BooleanMeta extends ObjectMeta { + public BooleanMeta() { + super("Boolean"); + } + } + public RBoolean(boolean bool) { this.bool = bool; } + + public RClass $class() { + return RBoolean; + } public RObject to_s() { return new RString(Boolean.toString(bool)); diff --git a/src/main/java/RClass.java b/src/main/java/RClass.java new file mode 100644 index 0000000..87edf5a --- /dev/null +++ b/src/main/java/RClass.java @@ -0,0 +1,41 @@ +public abstract class RClass extends RObject { + protected final RString name; + + public RClass(String name) { + this.name = new RString(name); + } + + public String toString() { + return name.toString(); + } + + public RObject to_s() { + return name; + } + + public RObject $new(RObject... args) { + RObject object = allocate(); + object.initialize(args); + return object; + } + + public RObject $new() { + RObject object = allocate(); + object.initialize(); + return object; + } + + public RObject $new(RObject arg) { + RObject object = allocate(); + object.initialize(arg); + return object; + } + + public RObject allocate() { + throw new RuntimeException("not allocatable: " + name); + } + + public RObject name() { + return new RString(name); + } +} diff --git a/src/main/java/RFixnum.java b/src/main/java/RFixnum.java index 93e8428..bb48c87 100644 --- a/src/main/java/RFixnum.java +++ b/src/main/java/RFixnum.java @@ -3,9 +3,24 @@ public class RFixnum extends RObject { public final long fix; + + public static final RFixnum ZERO = new RFixnum(0); + public static final RFixnum ONE = new RFixnum(1); + public static final RFixnum TWO = new RFixnum(2); + + public static final class FixnumMeta extends ObjectMeta { + public FixnumMeta() { + super("Fixnum"); + } + } + public RFixnum(long fix) { this.fix = fix; } + + public RClass $class() { + return RFixnum; + } public RObject to_s() { return new RString(Long.toString(fix)); diff --git a/src/main/java/RFloat.java b/src/main/java/RFloat.java index 4d07d60..f49acd4 100644 --- a/src/main/java/RFloat.java +++ b/src/main/java/RFloat.java @@ -1,10 +1,20 @@ public class RFloat extends RObject { public final double flo; + public static class FloatMeta extends ObjectMeta { + public FloatMeta() { + super("Float"); + } + } + public RFloat(double flo) { this.flo = flo; } + public RClass $class() { + return RFloat; + } + public RObject to_s() { return new RString(Double.toString(flo)); } diff --git a/src/main/java/RKernel.java b/src/main/java/RKernel.java index 4f985f7..da7fb50 100644 --- a/src/main/java/RKernel.java +++ b/src/main/java/RKernel.java @@ -1,7 +1,31 @@ public class RKernel { - public static final RObject RNil = new RObject(); + public static final RObject RNil = new RNil(); public static final RObject RTrue = new RBoolean(true); public static final RObject RFalse = new RBoolean(false); + public static final RString.StringMeta RString = new RString.StringMeta(); + public static final RArray.ArrayMeta RArray = new RArray.ArrayMeta(); + public static final RTime.TimeMeta RTime = new RTime.TimeMeta(); + public static final RBoolean.BooleanMeta RBoolean = new RBoolean.BooleanMeta(); + public static final RFixnum.FixnumMeta RFixnum = new RFixnum.FixnumMeta(); + public static final RFloat.FloatMeta RFloat = new RFloat.FloatMeta(); + public static final RNil.NilMeta NilClass = new RNil.NilMeta(); + + public static final RObject[] NULL_ARRAY = new RObject[0]; + + public static class ObjectMeta extends RClass { + public ObjectMeta(String name) { + super(name); + } + + public ObjectMeta() { + super("Object"); + } + + public RObject allocate() { + return new RObject(); + } + } + public static final ObjectMeta RObject = new ObjectMeta(); public RObject puts(RObject... objects) { if (objects.length == 0) { @@ -36,7 +60,7 @@ public RObject to_f() { } public RObject to_s() { - return new RString("#<" + getClass().getName() + ">"); + return new RString("#<" + $class().name() + ">"); } public Object to_java() { @@ -55,4 +79,18 @@ public boolean toBoolean() { public RObject method_missing(RObject name, RObject args) { throw new RuntimeException("Method '" + name + "' not defined on type " + getClass().getName()); } + + public RClass $class() { + Thread.dumpStack(); + System.out.println(getClass()); + return RObject; + } + + public RObject initialize() { + return RNil; + } + + public RObject initialize(RObject... args) { + throw new RuntimeException("too many args for #initialize (" + args.length + " for 0)"); + } } diff --git a/src/main/java/RNil.java b/src/main/java/RNil.java new file mode 100644 index 0000000..0fe3ba3 --- /dev/null +++ b/src/main/java/RNil.java @@ -0,0 +1,24 @@ +public class RNil extends RObject { + public static class NilMeta extends RClass { + public NilMeta(String name) { + super(name); + } + + public NilMeta() { + super("NilClass"); + } + } + + public RClass $class() { + return NilClass; + } + + @Override + public RString to_s() { + return new RString("nil"); + } + + public RFixnum to_i() { + return new RFixnum(0); + } +} diff --git a/src/main/java/RString.java b/src/main/java/RString.java index 07bbea3..f03ebaa 100644 --- a/src/main/java/RString.java +++ b/src/main/java/RString.java @@ -1,9 +1,40 @@ public class RString extends RObject implements CharSequence { private final String str; + + public static class StringMeta extends ObjectMeta { + public StringMeta() { + super("String"); + } + + @Override + public RObject $new(RObject... args) { + return new RString(args); + } + + @Override + public RObject $new(RObject arg) { + return new RString(arg); + } + } public RString(String str) { this.str = str; } + + public RString(RObject arg) { + this.str = arg.toString(); + } + + public RString(RObject... args) { + if (args.length > 1) { + throw new RuntimeException("too many arguments for String.new (" + args.length + " for 1)"); + } + this.str = args[0].toString(); + } + + public RClass $class() { + return RString; + } public String toString() { return str; diff --git a/src/main/java/RTime.java b/src/main/java/RTime.java index 74634f6..70c7194 100644 --- a/src/main/java/RTime.java +++ b/src/main/java/RTime.java @@ -5,6 +5,30 @@ public class RTime extends RObject { public final Calendar cal; + public static class TimeMeta extends ObjectMeta { + public TimeMeta(String name) { + super(name); + } + + public TimeMeta() { + super("Time"); + } + + @Override + public RObject $new() { + return new RTime(); + } + + @Override + public RObject $new(RObject arg) { + Object asJava = arg.to_java(); + if (asJava instanceof Calendar) { + return new RTime((Calendar)asJava); + } + throw new RuntimeException("invalid argument type: " + arg.$class().name()); + } + } + public RTime() { this(new GregorianCalendar()); } @@ -13,6 +37,10 @@ public RTime(Calendar cal) { this.cal = cal; } + public RClass $class() { + return RTime; + } + public RObject to_s() { return new RString(cal.toString()); } diff --git a/src/main/ruby/ruby_flux.rb b/src/main/ruby/ruby_flux.rb index 85bf28d..634fdf3 100644 --- a/src/main/ruby/ruby_flux.rb +++ b/src/main/ruby/ruby_flux.rb @@ -11,7 +11,7 @@ require 'ruby_flux/expression_compiler' module RubyFlux - BUILTINS = %w[puts print] + BUILTINS = %w[puts print $class to_i to_int to_s to_f initialize $new $equal$equal] Char = java.lang.Character java_import org.eclipse.jdt.core.dom.AST @@ -26,6 +26,7 @@ module RubyFlux InfixOperator = InfixExpression::Operator COPIED_SOURCES = %w[ + RNil.java RKernel.java RFixnum.java RBoolean.java @@ -33,6 +34,7 @@ module RubyFlux RFloat.java RArray.java RTime.java + RClass.java ] end diff --git a/src/main/ruby/ruby_flux/class_compiler.rb b/src/main/ruby/ruby_flux/class_compiler.rb index e9c6a64..c9de5f2 100644 --- a/src/main/ruby/ruby_flux/class_compiler.rb +++ b/src/main/ruby/ruby_flux/class_compiler.rb @@ -12,17 +12,31 @@ def initialize(compiler, ast, source, class_name, node) attr_accessor :compiler, :ast, :source attr_accessor :class_decl, :class_name + attr_accessor :metaclass_decl + attr_accessor :imports def start @class_decl = ast.new_type_declaration + @metaclass_decl = ast.new_type_declaration + @imports = {} + class_decl.interface = false class_decl.modifiers << ast.new_modifier(ModifierKeyword::PUBLIC_KEYWORD) class_decl.name = ast.new_simple_name(@class_name) class_decl.superclass_type = ast.new_simple_type(ast.new_simple_name("RObject")) + + metaclass_decl.interface = false + metaclass_decl.modifiers << ast.new_modifier(ModifierKeyword::PUBLIC_KEYWORD) + metaclass_decl.modifiers << ast.new_modifier(ModifierKeyword::STATIC_KEYWORD) + metaclass_decl.name = ast.new_simple_name(@class_name + "Meta") + metaclass_decl.superclass_type = ast.new_simple_type(ast.new_simple_name("ObjectMeta")) source.types << class_decl + class_decl.body_declarations << metaclass_decl - define_main + define_main if org.jruby.ast.RootNode === @node + + define_metaclass MethodCompiler.new(ast, self, @node).start end @@ -62,5 +76,50 @@ def define_main class_decl.body_declarations << main_method end + + def define_metaclass + construct = ast.new_method_declaration + construct.name = ast.new_simple_name(@class_name + "Meta") + construct.constructor = true + + construct.body = ast.new_block.tap do |body| + super_call = ast.new_super_constructor_invocation + super_call.arguments << ast.new_string_literal.tap do |string_literal| + string_literal.literal_value = @class_name + end + body.statements << super_call + end + + allocate_method = ast.new_method_declaration + allocate_method.name = ast.new_simple_name("allocate") + allocate_method.return_type2 = ast.new_simple_type(ast.new_simple_name("RObject")) + allocate_method.modifiers << ast.new_modifier(ModifierKeyword::PUBLIC_KEYWORD) + + allocate_method.body = ast.new_block.tap do |body| + body.statements << ast.new_return_statement.tap do |return_statement| + new_obj = ast.new_class_instance_creation + new_obj.type = ast.new_simple_type(ast.new_simple_name(@class_name)) + return_statement.expression = new_obj + end + end + + class_method = ast.new_method_declaration + class_method.name = ast.new_simple_name("$class") + class_method.return_type2 = ast.new_simple_type(ast.new_simple_name("RClass")) + class_method.modifiers << ast.new_modifier(ModifierKeyword::PUBLIC_KEYWORD) + + class_method.body = ast.new_block.tap do |body| + body.statements << ast.new_return_statement.tap do |return_statement| + new_obj = ast.new_simple_name(@class_name) + return_statement.expression = new_obj + end + end + + metaclass_decl.body_declarations << construct + metaclass_decl.body_declarations << allocate_method + class_decl.body_declarations << class_method + + compiler.classes << @class_name + end end end diff --git a/src/main/ruby/ruby_flux/compiler.rb b/src/main/ruby/ruby_flux/compiler.rb index 5d9df92..7323dd7 100644 --- a/src/main/ruby/ruby_flux/compiler.rb +++ b/src/main/ruby/ruby_flux/compiler.rb @@ -8,10 +8,11 @@ def initialize(files, source = nil) @files = files @source = source @sources = [] + @classes = [] @methods = {} end - attr_accessor :sources, :methods + attr_accessor :sources, :methods, :classes def compile if !@source @@ -66,6 +67,19 @@ def build_robject robject_cls.superclass_type = ast.new_simple_type(ast.new_simple_name("RKernel")) source.types << robject_cls + + classes.each do |class_name| + metaclass_fragment = ast.new_variable_declaration_fragment + metaclass_fragment.name = ast.new_simple_name(class_name) + metaclass_init = ast.new_class_instance_creation + metaclass_init.type = ast.new_simple_type(ast.new_name([class_name, class_name + "Meta"].to_java(:string))) + metaclass_fragment.initializer = metaclass_init + metaclass_field = ast.new_field_declaration(metaclass_fragment) + metaclass_field.type = ast.new_simple_type(ast.new_name([class_name, class_name + "Meta"].to_java(:string))) + metaclass_field.modifiers << ast.new_modifier(ModifierKeyword::PUBLIC_KEYWORD) + metaclass_field.modifiers << ast.new_modifier(ModifierKeyword::STATIC_KEYWORD) + robject_cls.body_declarations << metaclass_field + end methods.each do |name, arity| next if BUILTINS.include? name diff --git a/src/main/ruby/ruby_flux/expression_compiler.rb b/src/main/ruby/ruby_flux/expression_compiler.rb index d70b07e..c14916d 100644 --- a/src/main/ruby/ruby_flux/expression_compiler.rb +++ b/src/main/ruby/ruby_flux/expression_compiler.rb @@ -76,13 +76,13 @@ def visitArrayNode(node) end def visitCallNode(node) - method_invocation = case node.name - when "new" - ast.new_class_instance_creation.tap do |construct| - construct.type = ast.new_simple_type(ast.new_simple_name(proper_class(node.receiver_node.name))) - end - else - ast.new_method_invocation.tap do |method_invocation| +# method_invocation = case node.name +# when "new" +# ast.new_class_instance_creation.tap do |construct| +# construct.type = ast.new_simple_type(ast.new_simple_name(proper_class(node.receiver_node.name))) +# end +# else + method_invocation = ast.new_method_invocation.tap do |method_invocation| method_invocation.name = ast.new_simple_name(safe_name(node.name)) if org.jruby.ast.ConstNode === node.receiver_node method_invocation.expression = ast.new_name(proper_class(node.receiver_node.name)) @@ -91,7 +91,7 @@ def visitCallNode(node) method_invocation.expression = ExpressionCompiler.new(ast, body_compiler, node.receiver_node).start end end - end +# end node.args_node && node.args_node.child_nodes.each do |arg| arg_expression = ExpressionCompiler.new(ast, body_compiler, arg).start @@ -134,6 +134,17 @@ def visitConstDeclNode(node) end def visitConstNode(node) +# if class_compiler.imports.include? node.name +# # java import; handle via RJavaClass +# ast.new_class_instance_creation.tap do |construct| +# construct.type = ast.new_simple_type(ast.new_simple_name('RJavaClass')) +# construct.arguments << ast.new_type_literal +# arg_expression = ExpressionCompiler.new(ast, body_compiler, arg).start +# method_invocation.arguments << arg_expression +# end +# end +# ast.new_qualified_name( ast.new_simple_name(node.name)) +# end ast.new_qualified_name(ast.new_simple_name(class_compiler.class_name), ast.new_simple_name(node.name)) end @@ -149,6 +160,20 @@ def visitFalseNode(node) end def visitFCallNode(node) + case node.name + when "import" + class_name = node.args_node.child_nodes[0].value + class_elts = class_name.split('.') + + class_compiler.imports[short_name] = class_name + ast.new_import_declaration.tap do |import_declaration| + import_declaration.name = ast.new_name(class_elts.to_java(:string)) + class_compiler.compiler.sources[0].imports.add 0, import_declaration + end + + return nil + end + class_compiler.compiler.methods[safe_name(node.name)] = node.args_node ? node.args_node.child_nodes.size : 0 ast.new_method_invocation.tap do |method_invocation| @@ -297,53 +322,5 @@ def visitZArrayNode(node) ary.type = ast.new_simple_type(ast.new_simple_name('RArray')) end end - - def safe_name(name) - new_name = '' - - name.chars.each do |ch| - new_name << case ch - when '+'; '$plus' - when '-'; '$minus' - when '*'; '$times' - when '/'; '$div' - when '<'; '$less' - when '>'; '$greater' - when '='; '$equal' - when '&'; '$tilde' - when '!'; '$bang' - when '%'; '$percent' - when '^'; '$up' - when '?'; '$qmark' - when '|'; '$bar' - when '['; '$lbrack' - when ']'; '$rbrack' - else; ch; - end - end - - new_name - end - - def proper_class(name) - case name - when 'String' - 'RString' - when 'Array' - 'RArray' - when 'Fixnum' - 'RFixnum' - when 'Boolean' - 'RBoolean' - when 'Float' - 'RFloat' - when 'Time' - 'RTime' - when 'Object' - 'RObject' - else - name - end - end end end diff --git a/src/main/ruby/ruby_flux/jdt_utils.rb b/src/main/ruby/ruby_flux/jdt_utils.rb index 3780733..08f8676 100644 --- a/src/main/ruby/ruby_flux/jdt_utils.rb +++ b/src/main/ruby/ruby_flux/jdt_utils.rb @@ -23,5 +23,58 @@ def new_source cu end + + def safe_name(name) + new_name = '' + + case name + when 'new'; new_name = '$new' + when 'class'; new_name = '$class' + else + name.chars.each do |ch| + new_name << case ch + when '+'; '$plus' + when '-'; '$minus' + when '*'; '$times' + when '/'; '$div' + when '<'; '$less' + when '>'; '$greater' + when '='; '$equal' + when '&'; '$tilde' + when '!'; '$bang' + when '%'; '$percent' + when '^'; '$up' + when '?'; '$qmark' + when '|'; '$bar' + when '['; '$lbrack' + when ']'; '$rbrack' + else; ch; + end + end + end + + new_name + end + + def proper_class(name) + case name + when 'String' + 'RString' + when 'Array' + 'RArray' + when 'Fixnum' + 'RFixnum' + when 'Boolean' + 'RBoolean' + when 'Float' + 'RFloat' + when 'Time' + 'RTime' + when 'Object' + 'RObject' + else + name + end + end end end diff --git a/src/main/ruby/ruby_flux/method_compiler.rb b/src/main/ruby/ruby_flux/method_compiler.rb index 28028b3..1029ac8 100644 --- a/src/main/ruby/ruby_flux/method_compiler.rb +++ b/src/main/ruby/ruby_flux/method_compiler.rb @@ -82,21 +82,5 @@ def define_args(method_decl) arity end - - def safe_name(name) - case name - when '+'; '_plus_' - when '-'; '_minus_' - when '*'; '_times_' - when '/'; '_divide_' - when '<'; '_lt_' - when '>'; '_gt_' - when '<='; '_le_' - when '>='; '_ge_' - when '=='; '_equal_' - when '<=>'; '_cmp_' - else; name - end - end end end