-
Notifications
You must be signed in to change notification settings - Fork 22
/
method_def.rb
116 lines (100 loc) · 3.53 KB
/
method_def.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
class Fancy
class AST
class MethodDef < Rubinius::AST::Define
def initialize(line, method_ident, args, body, access = :public)
body = AST::ExpressionList.new(line) unless body
@line = line
@name = method_ident.method_name
@arguments = args
@body = body
@access = access
generate_ivar_assignment
if body.empty?
body.unshift_expression Rubinius::AST::NilLiteral.new(line)
end
end
def generate_ivar_assignment
@arguments.required.reverse.each do |name|
if name.to_s =~ /^@/
ident = Fancy::AST::Identifier.new(line, name.to_s)
value = Rubinius::AST::LocalVariableAccess.new(line, name)
asign = Fancy::AST::Assignment.new(line, ident, value)
body.unshift_expression(asign)
end
end
end
def bytecode(g)
if @name.to_s =~ /^initialize:(\S)+/
define_constructor_class_method g
end
if @name.to_s =~ /^unknown_message:with_params:$/
define_method_missing(g)
end
docstring = @body.shift_docstring
super(g)
MethodDef.set_docstring(g, docstring, @line, @arguments.names)
end
# Sets fancy documentation for the object currently
# on top of the stack
def self.set_docstring(g, docstring, line, argnames = [])
# prevent invoking documentation: when doesnt makes sense.
return unless docstring
local = StackLocal.new
local.set!(g)
ms = MessageSend.new(line,
Identifier.new(line, "Fancy::Documentation"),
Identifier.new(line, "for:is:"),
MessageArgs.new(line, local, docstring))
ms.bytecode(g)
meta = HashLiteral.new(line,
SymbolLiteral.new(line, :argnames),
ArrayLiteral.new(line, *argnames.map{|arg|StringLiteral.new(line, arg.to_s)})
)
ms = MessageSend.new(line,
Nothing.new,
Identifier.new(line, "meta:"),
MessageArgs.new(line, meta))
ms.bytecode(g)
g.pop
end
# defines a class method names "new:foo:" if we're defining a
# method named e.g. "initialize:foo:" (a constructor method).
def define_constructor_class_method(g)
method_ident = Rubinius::AST::StringLiteral.new(@line, @name.to_s[11..-1])
ms = MessageSend.new(@line,
Rubinius::AST::Self.new(@line),
Identifier.new(@line, "define_constructor_class_method:"),
MessageArgs.new(@line, method_ident))
ms.bytecode(g)
g.pop
end
def define_method_missing(g)
MessageSend.new(@line,
Rubinius::AST::Self.new(@line),
Identifier.new(@line, "define_forward_method_missing"),
MessageArgs.new(@line)).bytecode(g)
g.pop
end
end
SymbolLiteral = Rubinius::AST::SymbolLiteral
class Nothing
def bytecode(g)
end
end
class StackLocal
def set!(g)
# expects value to be on top of stack
@local = g.new_stack_local
g.set_stack_local @local
end
def bytecode(g)
g.push_stack_local @local
end
end
class MethodArgs < Rubinius::AST::FormalArguments
def initialize(line, *args)
super(line, args.map(&:to_sym), nil, nil)
end
end
end
end