commit 08fcff36cf9b22e9487fa681b0b195090290257d
Author: Charles Oliver Nutter <headius@headius.com>
Date: Sun Oct 25 04:05:59 2009 -0500
Add basic optional argument support; no tests yet, and only literal expressions seem to work right.
diff --git a/lib/duby/ast/method.rb b/lib/duby/ast/method.rb
index 6cba226..36fb28e 100644
--- a/lib/duby/ast/method.rb
+++ b/lib/duby/ast/method.rb
@@ -55,10 +55,29 @@ module Duby::AST
include Scoped
attr_accessor :child
- def initialize(parent, line_number, &block)
+ def initialize(parent, line_number, name, &block)
super(parent, line_number, &block)
@child = children[0]
- @name = @child.name
+ @name = name
+ end
+
+ def infer(typer)
+ unless @inferred_type
+ # if not already typed, check parent of parent (MethodDefinition) for signature info
+ method_def = parent.parent
+ signature = method_def.signature
+
+ # if signature, search for this argument
+ @inferred_type = child.infer(typer)
+ if @inferred_type
+ typer.learn_local_type(scope, name, @inferred_type)
+ signature[name.intern] = @inferred_type
+ else
+ typer.defer(self)
+ end
+ end
+
+ @inferred_type
end
end
@@ -96,8 +115,8 @@ module Duby::AST
def infer(typer)
@defining_class ||= typer.self_type
- typer.infer_signature(self)
typer.infer(arguments)
+ typer.infer_signature(self)
forced_type = signature[:return]
inferred_type = body ? typer.infer(body) : typer.no_type
@@ -122,6 +141,21 @@ module Duby::AST
end
@inferred_type = typer.learn_method_type(defining_class, name, arguments.inferred_type, actual_type, signature[:throws])
+
+ # learn the other overloads as well
+ args_for_opt = []
+ if arguments.args
+ arguments.args.each do |arg|
+ if OptionalArgument === arg
+ arg_types_for_opt = args_for_opt.map do |arg_for_opt|
+ arg_for_opt.infer(typer)
+ end
+ typer.learn_method_type(defining_class, name, arg_types_for_opt, actual_type, signature[:throws])
+ end
+ args_for_opt << arg
+ end
+ end
+
signature[:return] = @inferred_type
end
diff --git a/lib/duby/jvm/compiler.rb b/lib/duby/jvm/compiler.rb
index cef9128..fa01d05 100644
--- a/lib/duby/jvm/compiler.rb
+++ b/lib/duby/jvm/compiler.rb
@@ -130,6 +130,59 @@ module Duby
@method.stop
end
+ arg_types_for_opt = []
+ args_for_opt = []
+ if args.args
+ args.args.each do |arg|
+ if AST::OptionalArgument === arg
+ if @static || force_static
+ method = @class.public_static_method(name.to_s, exceptions, return_type, *arg_types_for_opt)
+ else
+ if name == "initialize"
+ method = @class.public_constructor(exceptions, *arg_types_for_opt)
+ method.start
+ method.aload 0
+ method.invokespecial @class.superclass, "<init>", [@method.void]
+ started = true
+ else
+ method = @class.public_method(name.to_s, exceptions, return_type, *arg_types_for_opt)
+ end
+ end
+
+ with :method => method, :static => @static || force_static do
+ log "Starting new method #{name}(#{arg_types_for_opt})"
+
+ @method.start unless started
+
+ # declare all args so they get their values
+
+ expression = signature[:return] != Types::Void
+
+ @method.aload(0) unless @static
+ args_for_opt.each {|req_arg| @method.local(req_arg.name, req_arg.inferred_type)}
+ arg.children[0].compile(self, true)
+
+ # invoke the next one in the chain
+ if @static
+ @method.invokestatic(@class, name.to_s, [return_type] + arg_types_for_opt + [arg.inferred_type])
+ else
+ @method.invokevirtual(@class, name.to_s, [return_type] + arg_types_for_opt + [arg.inferred_type])
+ end
+
+ if name == "initialize"
+ @method.returnvoid
+ else
+ signature[:return].return(@method)
+ end
+
+ @method.stop
+ end
+ end
+ arg_types_for_opt << arg.inferred_type
+ args_for_opt << arg
+ end
+ end
+
log "Method #{name}(#{arg_types}) complete!"
end
diff --git a/lib/duby/transform.rb b/lib/duby/transform.rb
index 3e82685..35e9848 100644
--- a/lib/duby/transform.rb
+++ b/lib/duby/transform.rb
@@ -221,7 +221,11 @@ module Duby
def transform(transformer, parent)
Arguments.new(parent, position) do |args_node|
arg_list = args.child_nodes.map do |node|
- RequiredArgument.new(args_node, node.position, node.name)
+ if !node.respond_to?(:type_node) || node.type_node.respond_to?(:type_reference)
+ RequiredArgument.new(args_node, node.position, node.name)
+ else
+ OptionalArgument.new(args_node, node.position, node.name) {|opt_arg| [transformer.transform(node, opt_arg)]}
+ end
# argument nodes will have type soon
#RequiredArgument.new(args_node, node.name, node.type)
end if args
@@ -399,7 +403,7 @@ module Duby
if args_node && args_node.args
args_node.args.child_nodes.each do |arg|
- if arg.respond_to? :type_node
+ if arg.respond_to?(:type_node) && arg.type_node.respond_to?(:type_reference)
signature[arg.name.intern] =
arg.type_node.type_reference(parent)
end
@@ -812,6 +816,9 @@ module Duby
end
class TypedArgumentNode
+ def transform(transformer, parent)
+ type_node.transform(transformer, parent)
+ end
end
def transform(transformer, parent)