myco 0.1.0.dev → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -7
- data/LICENSE +1 -1
- data/README.md +79 -0
- data/lib/myco/backtrace.rb +1 -1
- data/lib/myco/bootstrap/component.rb +78 -39
- data/lib/myco/bootstrap/find_constant.rb +12 -1
- data/lib/myco/bootstrap/instance.rb +5 -12
- data/lib/myco/bootstrap/meme.rb +176 -28
- data/lib/myco/bootstrap.my +8 -7
- data/lib/myco/code_loader.rb +332 -0
- data/lib/myco/command/inoculate.my +83 -0
- data/lib/myco/command.my +26 -26
- data/lib/myco/core/BasicDecorators.my +62 -0
- data/lib/myco/core/BasicObject.my +12 -34
- data/lib/myco/core/Decorator.my +1 -0
- data/lib/myco/core/FileToplevel.my +0 -3
- data/lib/myco/core/Myco.my +4 -0
- data/lib/myco/core/Object.my +6 -4
- data/lib/myco/eval.rb +17 -18
- data/lib/myco/misc.rb +16 -0
- data/lib/myco/parser/ast/argument_assembly.rb +76 -0
- data/lib/myco/parser/ast/array_assembly.rb +57 -0
- data/lib/myco/parser/ast/branch_operator.rb +73 -0
- data/lib/myco/parser/ast/constant_access.rb +4 -18
- data/lib/myco/parser/ast/constant_define.rb +3 -3
- data/lib/myco/parser/ast/constant_reopen.rb +12 -13
- data/lib/myco/parser/ast/declare_category.rb +8 -6
- data/lib/myco/parser/ast/declare_decorator.rb +4 -4
- data/lib/myco/parser/ast/declare_file.rb +4 -4
- data/lib/myco/parser/ast/declare_meme.rb +53 -11
- data/lib/myco/parser/ast/declare_object.rb +9 -7
- data/lib/myco/parser/ast/declare_string.rb +5 -5
- data/lib/myco/parser/ast/invoke.rb +18 -36
- data/lib/myco/parser/ast/invoke_method.rb +28 -0
- data/lib/myco/parser/ast/local_variable_access_ambiguous.rb +9 -13
- data/lib/myco/parser/ast/misc.rb +128 -33
- data/lib/myco/parser/ast/myco_module_scope.rb +26 -0
- data/lib/myco/parser/ast/quest.rb +3 -3
- data/lib/myco/parser/ast/to_ruby/array_assembly.rb +15 -0
- data/lib/myco/parser/ast/to_ruby/block.rb +14 -0
- data/lib/myco/parser/ast/to_ruby/block_pass.rb +6 -0
- data/lib/myco/parser/ast/to_ruby/branch_operator.rb +9 -0
- data/lib/myco/parser/ast/to_ruby/constant_access.rb +10 -0
- data/lib/myco/parser/ast/to_ruby/constant_assignment.rb +6 -0
- data/lib/myco/parser/ast/to_ruby/constant_define.rb +9 -0
- data/lib/myco/parser/ast/to_ruby/constant_reopen.rb +6 -0
- data/lib/myco/parser/ast/to_ruby/declare_category.rb +7 -0
- data/lib/myco/parser/ast/to_ruby/declare_decorator.rb +6 -0
- data/lib/myco/parser/ast/to_ruby/declare_file.rb +6 -0
- data/lib/myco/parser/ast/to_ruby/declare_meme.rb +16 -0
- data/lib/myco/parser/ast/to_ruby/declare_object.rb +8 -0
- data/lib/myco/parser/ast/to_ruby/declare_string.rb +6 -0
- data/lib/myco/parser/ast/to_ruby/dynamic_string.rb +14 -0
- data/lib/myco/parser/ast/to_ruby/dynamic_symbol.rb +7 -0
- data/lib/myco/parser/ast/to_ruby/eval_expression.rb +6 -0
- data/lib/myco/parser/ast/to_ruby/false_literal.rb +6 -0
- data/lib/myco/parser/ast/to_ruby/hash_literal.rb +16 -0
- data/lib/myco/parser/ast/to_ruby/invoke.rb +6 -0
- data/lib/myco/parser/ast/to_ruby/invoke_method.rb +35 -0
- data/lib/myco/parser/ast/to_ruby/iter.rb +10 -0
- data/lib/myco/parser/ast/to_ruby/local_variable_access_ambiguous.rb +16 -0
- data/lib/myco/parser/ast/to_ruby/local_variable_assignment.rb +8 -0
- data/lib/myco/parser/ast/to_ruby/myco_module_scope.rb +8 -0
- data/lib/myco/parser/ast/to_ruby/null_literal.rb +6 -0
- data/lib/myco/parser/ast/to_ruby/parameters.rb +60 -0
- data/lib/myco/parser/ast/to_ruby/quest.rb +13 -0
- data/lib/myco/parser/ast/to_ruby/return.rb +7 -0
- data/lib/myco/parser/ast/to_ruby/scoped_constant.rb +11 -0
- data/lib/myco/parser/ast/to_ruby/self.rb +6 -0
- data/lib/myco/parser/ast/to_ruby/splat_value.rb +33 -0
- data/lib/myco/parser/ast/to_ruby/string_literal.rb +6 -0
- data/lib/myco/parser/ast/to_ruby/symbol_literal.rb +6 -0
- data/lib/myco/parser/ast/to_ruby/toplevel_constant.rb +11 -0
- data/lib/myco/parser/ast/to_ruby/true_literal.rb +6 -0
- data/lib/myco/parser/ast/to_ruby/void_literal.rb +6 -0
- data/lib/myco/parser/ast/to_ruby.rb +138 -0
- data/lib/myco/parser/ast.rb +6 -0
- data/lib/myco/parser/peg_parser.rb +361 -181
- data/lib/myco/parser.rb +27 -11
- data/lib/myco/tools/BasicCommand.my +42 -0
- data/lib/myco/tools/Generator.my +18 -0
- data/lib/myco/toolset.rb +0 -3
- data/lib/myco/version.rb +1 -4
- data/lib/myco.rb +2 -0
- metadata +230 -160
- data/lib/myco/parser/builder.output +0 -3995
- data/lib/myco/parser/builder.racc +0 -585
- data/lib/myco/parser/builder.rb +0 -1592
- data/lib/myco/parser/lexer.rb +0 -2306
- data/lib/myco/parser/lexer.rl +0 -393
- data/lib/myco/parser/lexer_char_classes.rl +0 -56
- data/lib/myco/parser/lexer_common.rb +0 -95
- data/lib/myco/parser/lexer_skeleton.rl +0 -154
- data/lib/myco/parser/peg_parser.kpeg +0 -759
- data/lib/myco/tools/OptionParser.my +0 -38
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
|
|
2
|
+
module Myco
|
|
3
|
+
|
|
4
|
+
class CodeLoader
|
|
5
|
+
|
|
6
|
+
class << self
|
|
7
|
+
attr_accessor :emit_rb # Whether to cache generated ruby code to disk.
|
|
8
|
+
attr_accessor :emit_rbc # Whether to cache rubinius bytecode to disk.
|
|
9
|
+
attr_accessor :precedence # Order of precedence for file types.
|
|
10
|
+
end
|
|
11
|
+
self.emit_rb = false # Do not emit ruby code by default.
|
|
12
|
+
self.emit_rbc = true # Do cache rubinius bytecode by default.
|
|
13
|
+
|
|
14
|
+
# Use cached rubinius bytecode files if they are up to date.
|
|
15
|
+
# Use Myco files otherwise.
|
|
16
|
+
# Use generated ruby files only when Myco files cannot be found or loaded.
|
|
17
|
+
self.precedence = [:rbc, :myco, :rb]
|
|
18
|
+
|
|
19
|
+
# TODO: a more elegant solution than env vars
|
|
20
|
+
# Emit ruby if env var indicates
|
|
21
|
+
# Load from ruby exclusively if env var indicates
|
|
22
|
+
self.emit_rb = true if ENV['MYCO_TO_RUBY'] == 'PRE'
|
|
23
|
+
self.precedence = [:myco] if ENV['MYCO_TO_RUBY'] == 'PRE'
|
|
24
|
+
|
|
25
|
+
# Try to resolve the given file path
|
|
26
|
+
# in the current working directory or in the given load paths.
|
|
27
|
+
def self.resolve_file path, load_paths=[]
|
|
28
|
+
tmp_path = File.expand_path(path)
|
|
29
|
+
use_path = File.file?(tmp_path) && tmp_path
|
|
30
|
+
load_paths.each { |load_path|
|
|
31
|
+
break if use_path
|
|
32
|
+
tmp_path = File.expand_path(path, load_path)
|
|
33
|
+
use_path = File.file?(tmp_path) && tmp_path
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
use_path
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Select a loader and file path for the given path,
|
|
40
|
+
# considering the currently selected order of precedence for file types.
|
|
41
|
+
def self.loader_for_file path, load_paths=[]
|
|
42
|
+
use_path = resolve_file(path, load_paths)
|
|
43
|
+
|
|
44
|
+
# TODO: This logic could be refactored to look cleaner
|
|
45
|
+
if use_path
|
|
46
|
+
# Try to find an implementation with higher precedence than :myco
|
|
47
|
+
# With a file that has been modified at least as recently as
|
|
48
|
+
# the resolved file in use_path.
|
|
49
|
+
ref_mtime = File.mtime(use_path)
|
|
50
|
+
precedence.each { |type|
|
|
51
|
+
begin
|
|
52
|
+
if type==:myco
|
|
53
|
+
return loader_for(type, use_path)
|
|
54
|
+
else
|
|
55
|
+
alt_path = use_path + ".#{type}"
|
|
56
|
+
if File.file?(alt_path) && (File.mtime(alt_path) >= ref_mtime)
|
|
57
|
+
return loader_for(type, alt_path)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
rescue NotImplementedError # Skip loader if not implemented
|
|
61
|
+
end
|
|
62
|
+
}
|
|
63
|
+
else
|
|
64
|
+
# Try to find any implementation other than :myco, in precedence order.
|
|
65
|
+
precedence.each { |type|
|
|
66
|
+
if type != :myco
|
|
67
|
+
alt_path = resolve_file(path + ".#{type}", load_paths)
|
|
68
|
+
if alt_path && File.file?(alt_path)
|
|
69
|
+
return loader_for(type, alt_path)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
}
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
raise ArgumentError, "Couldn't resolve file: #{path.inspect} \n" \
|
|
76
|
+
"in load_paths: #{load_paths.inspect}" \
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Return a loader of the given type with the given arguments
|
|
80
|
+
def self.loader_for type, *args
|
|
81
|
+
case type
|
|
82
|
+
when :myco; MycoLoader.new(*args)
|
|
83
|
+
when :rbc; BytecodeLoader.new(*args)
|
|
84
|
+
when :rb; RubyLoader.new(*args)
|
|
85
|
+
else; raise NotImplementedError
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Load from the given path and load_paths and call
|
|
90
|
+
# under the given ConstantScope, VariableScope, and receiver.
|
|
91
|
+
# If cscope or vscope or receiver are nil, they are pulled from
|
|
92
|
+
# the given call_depth, corresponding to one of the calling frames.
|
|
93
|
+
#
|
|
94
|
+
def self.load path, load_paths=[], call_depth:1, **kwargs
|
|
95
|
+
begin
|
|
96
|
+
loader = loader_for_file(path, load_paths)
|
|
97
|
+
loader.bind_to(call_depth:call_depth+1, **kwargs)
|
|
98
|
+
loader.compile
|
|
99
|
+
loader.emit_rb! if self.emit_rb
|
|
100
|
+
loader.emit_rbc! if self.emit_rbc
|
|
101
|
+
loader.load
|
|
102
|
+
rescue Rubinius::InvalidRBC
|
|
103
|
+
retry
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
class AbstractLoader
|
|
108
|
+
attr_accessor :filename, :line
|
|
109
|
+
attr_accessor :constant_scope, :variable_scope, :receiver
|
|
110
|
+
|
|
111
|
+
attr_accessor :string
|
|
112
|
+
attr_accessor :ast
|
|
113
|
+
attr_accessor :generator
|
|
114
|
+
attr_accessor :compiled_code
|
|
115
|
+
attr_accessor :block_environment
|
|
116
|
+
|
|
117
|
+
def initialize filename, line = 1
|
|
118
|
+
@filename = filename
|
|
119
|
+
@line = line
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def bind_to cscope:nil, vscope:nil, receiver:nil,
|
|
123
|
+
call_depth:1
|
|
124
|
+
loc = Rubinius::VM.backtrace(call_depth, true).first
|
|
125
|
+
@constant_scope = cscope || loc.constant_scope
|
|
126
|
+
@variable_scope = vscope || loc.variables
|
|
127
|
+
@receiver = receiver || loc.instance_variable_get(:@receiver)
|
|
128
|
+
|
|
129
|
+
self
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def make_string
|
|
133
|
+
@string = File.read(filename)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def make_ast
|
|
137
|
+
@string || make_string
|
|
138
|
+
|
|
139
|
+
parser = parser_type.new(@filename, @line, [])
|
|
140
|
+
ast = parser.parse_string(@string)
|
|
141
|
+
|
|
142
|
+
ast = ast_root_type.new(ast) if ast_root_type
|
|
143
|
+
ast.file = filename.to_sym
|
|
144
|
+
ast.variable_scope = @variable_scope
|
|
145
|
+
|
|
146
|
+
@ast = ast
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def make_generator
|
|
150
|
+
@ast || make_ast
|
|
151
|
+
|
|
152
|
+
g = generator_type.new
|
|
153
|
+
@ast.bytecode(g)
|
|
154
|
+
|
|
155
|
+
g.close
|
|
156
|
+
g.encode
|
|
157
|
+
|
|
158
|
+
@generator = g
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def make_compiled_code
|
|
162
|
+
@generator || make_generator
|
|
163
|
+
|
|
164
|
+
code = @generator.package(Rubinius::CompiledCode)
|
|
165
|
+
|
|
166
|
+
@compiled_code = code
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def make_block_environment
|
|
170
|
+
@compiled_code || make_compiled_code
|
|
171
|
+
code = @compiled_code
|
|
172
|
+
|
|
173
|
+
code.scope = @constant_scope
|
|
174
|
+
script = Rubinius::CompiledCode::Script.new(code, @filename, true)
|
|
175
|
+
script.eval_source = @string
|
|
176
|
+
code.scope.script = script
|
|
177
|
+
|
|
178
|
+
be = Rubinius::BlockEnvironment.new
|
|
179
|
+
be.under_context(@variable_scope, code)
|
|
180
|
+
|
|
181
|
+
@block_environment = be
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def compile
|
|
185
|
+
@block_environment || make_block_environment
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def load
|
|
189
|
+
compile
|
|
190
|
+
@block_environment.call_on_instance(@receiver)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def emit_rb! filename=nil
|
|
194
|
+
@ast || make_ast
|
|
195
|
+
|
|
196
|
+
filename = emit_filename('.rb', filename)
|
|
197
|
+
return nil unless filename
|
|
198
|
+
|
|
199
|
+
File.open(filename, "w+") { |file| file.write(@ast.to_ruby_code) }
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def emit_rbc! filename=nil
|
|
203
|
+
@compiled_code || make_compiled_code
|
|
204
|
+
|
|
205
|
+
filename = emit_filename('.rbc', filename)
|
|
206
|
+
return nil unless filename
|
|
207
|
+
|
|
208
|
+
compiled_file_type.dump(
|
|
209
|
+
@compiled_code, filename, Rubinius::Signature, 0
|
|
210
|
+
)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def is_rb?; false end
|
|
214
|
+
def is_rbc?; false end
|
|
215
|
+
|
|
216
|
+
private
|
|
217
|
+
|
|
218
|
+
# Return @filename, stripped of any .rbc or .rb file extension
|
|
219
|
+
def myco_filename
|
|
220
|
+
@filename.sub(/\.rbc?$/, '')
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def mkdir_p dir
|
|
224
|
+
# TODO: do manually, without depending on FileUtils
|
|
225
|
+
require 'fileutils'
|
|
226
|
+
FileUtils.mkdir_p(dir)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Return the filename to emit, or nil if the file is already current
|
|
230
|
+
# relative to modification time of the file at the myco_filename.
|
|
231
|
+
def emit_filename file_ext, override=nil
|
|
232
|
+
if override
|
|
233
|
+
filename = override
|
|
234
|
+
else
|
|
235
|
+
orig_filename = myco_filename
|
|
236
|
+
filename ||= orig_filename + file_ext
|
|
237
|
+
|
|
238
|
+
if File.file?(myco_filename)
|
|
239
|
+
ref_mtime = File.mtime(myco_filename)
|
|
240
|
+
if File.file?(filename) && (File.mtime(filename) >= ref_mtime)
|
|
241
|
+
return nil
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
mkdir_p(File.dirname(filename))
|
|
247
|
+
return filename
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
class MycoLoader < AbstractLoader
|
|
252
|
+
def initialize *args
|
|
253
|
+
# TODO: a more elegant solution than env vars
|
|
254
|
+
raise NotImplementedError if ENV['MYCO_TO_RUBY'] == 'POST'
|
|
255
|
+
super
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def ast_root_type
|
|
259
|
+
Myco::ToolSet::AST::EvalExpression
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def parser_type
|
|
263
|
+
Myco::ToolSet::Parser
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def generator_type
|
|
267
|
+
Myco::ToolSet::Generator
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def compiled_file_type
|
|
271
|
+
Myco::ToolSet::CompiledFile
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
class RubyLoader < AbstractLoader
|
|
276
|
+
def is_rb?; true end
|
|
277
|
+
|
|
278
|
+
def emit_rb!; nil end
|
|
279
|
+
|
|
280
|
+
def initialize *args
|
|
281
|
+
super *args
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def ast_root_type
|
|
285
|
+
Rubinius::ToolSets::Runtime::AST::EvalExpression
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def parser_type
|
|
289
|
+
Rubinius::ToolSets::Runtime::Melbourne
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def generator_type
|
|
293
|
+
Rubinius::ToolSets::Runtime::Generator
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def compiled_file_type
|
|
297
|
+
Rubinius::ToolSets::Runtime::CompiledFile
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
class BytecodeLoader < AbstractLoader
|
|
302
|
+
def is_rbc?; true end
|
|
303
|
+
|
|
304
|
+
def emit_rb!; nil end
|
|
305
|
+
def emit_rbc!; nil end
|
|
306
|
+
|
|
307
|
+
def initialize *args
|
|
308
|
+
# TODO: a more elegant solution than env vars
|
|
309
|
+
raise NotImplementedError if ENV['MYCO_TO_RUBY'] == 'POST'
|
|
310
|
+
super
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def make_compiled_code
|
|
314
|
+
begin
|
|
315
|
+
@compiled_code = primitive_load_file \
|
|
316
|
+
@filename, Rubinius::Signature, Rubinius::RUBY_LIB_VERSION
|
|
317
|
+
rescue Rubinius::InvalidRBC => e
|
|
318
|
+
File.delete @filename
|
|
319
|
+
raise e
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
private
|
|
324
|
+
|
|
325
|
+
def primitive_load_file(path, signature, version)
|
|
326
|
+
Rubinius.primitive :compiledfile_load
|
|
327
|
+
raise Rubinius::InvalidRBC, "Invalid RBC file: #{path}"
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
|
|
2
|
+
import "../tools/BasicCommand.my"
|
|
3
|
+
|
|
4
|
+
BasicCommand {
|
|
5
|
+
banner: "Usage: myco inoculate [options]"
|
|
6
|
+
|
|
7
|
+
# TODO: move this out of here
|
|
8
|
+
shell: |*a,&b| Kernel.instance_method(:system).bind(self).call(*a,&b)
|
|
9
|
+
|
|
10
|
+
run: |*argv| {
|
|
11
|
+
destinations = options_parse(*argv)
|
|
12
|
+
destinations.size == 1
|
|
13
|
+
|? show_help
|
|
14
|
+
?? destinations.each |dest| {
|
|
15
|
+
config.dest = dest
|
|
16
|
+
|
|
17
|
+
run_operation(:copy, files(config.source, 'bin'))
|
|
18
|
+
run_operation(:copy, files(config.source, 'lib', '.rb'))
|
|
19
|
+
run_operation(:copy, files(config.source, 'lib', '.my'))
|
|
20
|
+
run_operation(:myrb, files(config.dest, 'lib', '.my'))
|
|
21
|
+
|
|
22
|
+
if(config.verbose) {
|
|
23
|
+
prog = Rubinius::Globals[:"$PROGRAM_NAME"]
|
|
24
|
+
puts("DONE "prog" "ARGV.join(" ")"")
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
run_operation: |opcode, source_list| {
|
|
30
|
+
source_list.each |source| {
|
|
31
|
+
dest = transforms.send(opcode, source)
|
|
32
|
+
|
|
33
|
+
unless(operation_is_unnecessary(source, dest)) {
|
|
34
|
+
if(config.verbose) {
|
|
35
|
+
puts(""opcode.upcase" "source"")
|
|
36
|
+
puts(" => "dest"")
|
|
37
|
+
}
|
|
38
|
+
operations.send(opcode, source, dest)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
operation_is_unnecessary: |source, dest|
|
|
44
|
+
File.file?(dest) && File.mtime(dest) >= File.mtime(source)
|
|
45
|
+
|
|
46
|
+
files: |prefix, subdir, ext=''| {
|
|
47
|
+
prefix && (subdir = File.join(prefix, subdir))
|
|
48
|
+
Dir.glob(File.join(subdir, "**", "*"ext""))
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
[operations]
|
|
52
|
+
|
|
53
|
+
copy: |source, dest|
|
|
54
|
+
shell("mkdir -p "File.dirname(dest)" && cp "source" "dest"")
|
|
55
|
+
myrb: |source, dest|
|
|
56
|
+
Myco::CodeLoader::MycoLoader.new(source).emit_rb!
|
|
57
|
+
|
|
58
|
+
[transforms]
|
|
59
|
+
|
|
60
|
+
copy: |filename| File.join(config.dest, filename)
|
|
61
|
+
myrb: |filename| ""filename".rb"
|
|
62
|
+
|
|
63
|
+
[config]
|
|
64
|
+
|
|
65
|
+
var source
|
|
66
|
+
var dest
|
|
67
|
+
var verbose
|
|
68
|
+
|
|
69
|
+
[options]
|
|
70
|
+
|
|
71
|
+
"-d": Option {
|
|
72
|
+
description: "The destination directory to clone material into."
|
|
73
|
+
long_form: "--dest"
|
|
74
|
+
argument: "STRING"
|
|
75
|
+
do: |arg| parent.config.dest = arg
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
"-v": Option {
|
|
79
|
+
description: "Show details about material cloning operations."
|
|
80
|
+
long_form: "--verbose"
|
|
81
|
+
do: |arg| parent.config.verbose = true
|
|
82
|
+
}
|
|
83
|
+
}
|
data/lib/myco/command.my
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
|
|
2
|
-
import "tools/
|
|
2
|
+
import "tools/BasicCommand.my"
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
banner: "Usage: myco [options] [files]"
|
|
9
|
-
|
|
10
|
-
[options]
|
|
11
|
-
|
|
12
|
-
"-E": Option {
|
|
13
|
-
description: "Evaluate a string of declarative Myco"
|
|
14
|
-
long_form: "--eval"
|
|
15
|
-
argument: "STRING"
|
|
16
|
-
do: |arg| Myco.eval(arg)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
"-e": Option {
|
|
20
|
-
description: "Evaluate a string of procedural Myco inside an Object"
|
|
21
|
-
long_form: "--eval-meme"
|
|
22
|
-
argument: "STRING"
|
|
23
|
-
do: |arg| Myco.eval("Object { on creation: "arg" }")
|
|
24
|
-
}
|
|
25
|
-
}
|
|
4
|
+
BasicCommand {
|
|
5
|
+
banner: "Usage: myco [subcommand] [options] [files]"
|
|
6
|
+
|
|
7
|
+
on creation: run(*ARGV)
|
|
26
8
|
|
|
27
9
|
run: |*argv| {
|
|
28
|
-
files =
|
|
29
|
-
files.uniq.each |file| { Myco.eval_file(file, [::Dir.pwd]) }
|
|
10
|
+
files = options_parse(*argv)
|
|
11
|
+
files && files.uniq.each |file| { Myco.eval_file(file, [::Dir.pwd]) }
|
|
30
12
|
}
|
|
31
13
|
|
|
32
|
-
|
|
14
|
+
[options]
|
|
15
|
+
|
|
16
|
+
"-E": Option {
|
|
17
|
+
description: "Evaluate a string of declarative Myco"
|
|
18
|
+
long_form: "--eval"
|
|
19
|
+
argument: "STRING"
|
|
20
|
+
do: |arg| Myco.eval(arg)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
"-e": Option {
|
|
24
|
+
description: "Evaluate a string of procedural Myco inside an Object"
|
|
25
|
+
long_form: "--eval-meme"
|
|
26
|
+
argument: "STRING"
|
|
27
|
+
do: |arg| Myco.eval("Object { on creation: { "arg" } }")
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
[commands]
|
|
31
|
+
|
|
32
|
+
"inoculate": Myco.eval_file("command/inoculate.my")
|
|
33
33
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
|
|
2
|
+
::Myco::BasicDecorators < ::Myco::EmptyObject {
|
|
3
|
+
[decorators]
|
|
4
|
+
|
|
5
|
+
# The 'storage' decorator acts like a set of var decorators
|
|
6
|
+
# TODO: consolidate with 'var'
|
|
7
|
+
storage: Decorator {
|
|
8
|
+
# Create a corresponding "writer" meme to go with this "reader" meme
|
|
9
|
+
apply: |meme| {
|
|
10
|
+
meme.target.declare_meme(:""meme.name"=") |new_value, *args| {
|
|
11
|
+
meme.set_result_for(self, new_value, *args)
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
[transforms]
|
|
16
|
+
cache: true # Enable caching of the value to act as storage
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
# The 'var' decorator creates an instance variable getter and setter:
|
|
20
|
+
var: Decorator {
|
|
21
|
+
[transforms]
|
|
22
|
+
var: true
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# The 'memoize' decorator enables caching of the result
|
|
26
|
+
memoize: Decorator {
|
|
27
|
+
[transforms]
|
|
28
|
+
cache: true
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# The 'sclass' decorator makes the component's singleton class the target
|
|
32
|
+
sclass: Decorator {
|
|
33
|
+
[transforms]
|
|
34
|
+
target: |meme| meme.target.singleton_class
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# The 'before' decorator defines a wrapper that runs before the existing meme
|
|
38
|
+
before: Decorator {
|
|
39
|
+
apply: |meme| {
|
|
40
|
+
orig_meme = meme.target.memes[meme.name]
|
|
41
|
+
wrap_meme = meme.dup
|
|
42
|
+
meme.body = Proc.new |*a,&b| {
|
|
43
|
+
wrap_meme.result_for(self,*a,&b)
|
|
44
|
+
orig_meme.result_for(self,*a,&b)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# The 'after' decorator defines a wrapper that runs after the existing meme
|
|
50
|
+
after: Decorator {
|
|
51
|
+
apply: |meme| {
|
|
52
|
+
orig_meme = meme.target.memes[meme.name]
|
|
53
|
+
wrap_meme = meme.dup
|
|
54
|
+
meme.body = Proc.new |*a,&b| {
|
|
55
|
+
result = \
|
|
56
|
+
orig_meme.result_for(self,*a,&b)
|
|
57
|
+
wrap_meme.result_for(self,*a,&b)
|
|
58
|
+
result
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -1,46 +1,24 @@
|
|
|
1
1
|
|
|
2
|
-
::Myco::BasicObject < ::Myco::EmptyObject {
|
|
2
|
+
::Myco::BasicObject < ::Myco::EmptyObject, ::Myco::BasicDecorators {
|
|
3
3
|
# Basic conditional handling
|
|
4
4
|
if: |cond, &blk| cond && blk.call
|
|
5
5
|
unless: |cond, &blk| cond || blk.call
|
|
6
6
|
switch: |input,comparator=:"=="|
|
|
7
7
|
Switch.new(input:input, comparator:comparator)
|
|
8
8
|
|
|
9
|
+
# TODO: alias more efficiently
|
|
10
|
+
# alias(::Kernel, :raise) raise
|
|
11
|
+
# alias(::Kernel, :loop) loop
|
|
12
|
+
raise: |*args| ::Kernel.instance_method(:raise).bind(self).call(*args)
|
|
13
|
+
loop: |&block| ::Kernel.instance_method(:loop).bind(self).call(&block)
|
|
14
|
+
break: raise(::StopIteration)
|
|
15
|
+
|
|
9
16
|
puts: |*args| STDOUT.puts(*args)
|
|
10
17
|
p: |*args| STDOUT.puts(args.map |a| { a.inspect }.join(', '))
|
|
11
18
|
|
|
12
19
|
ruby_require: |arg| Object.send(:require, arg)
|
|
13
|
-
|
|
14
|
-
[decorators]
|
|
15
|
-
|
|
16
|
-
# The 'var' decorator creates what is effectively an instance variable by:
|
|
17
|
-
# 1) enabling caching, keeping the given block from being re-run,
|
|
18
|
-
# unless run from an inheriting object (which would get its own "copy")
|
|
19
|
-
# 2) TODO: prohibiting sending of any arguments
|
|
20
|
-
# 3) creating a "writer" function in addition to the "reader"
|
|
21
|
-
var: Decorator {
|
|
22
|
-
# Create a corresponding "writer" meme to go with this "reader" meme
|
|
23
|
-
apply: |meme| {
|
|
24
|
-
meme.target.declare_meme(:""meme.name"=") |new_value| {
|
|
25
|
-
meme.set_result_for(self, new_value)
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
[transforms]
|
|
30
|
-
cache: true # Enable caching of the value to act as storage
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
# The 'storage' decorator acts like a set of var decorators
|
|
34
|
-
# TODO: consolidate with 'var'
|
|
35
|
-
storage: Decorator {
|
|
36
|
-
# Create a corresponding "writer" meme to go with this "reader" meme
|
|
37
|
-
apply: |meme| {
|
|
38
|
-
meme.target.declare_meme(:""meme.name"=") |new_value, *args| {
|
|
39
|
-
meme.set_result_for(self, new_value, *args)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
[transforms]
|
|
44
|
-
cache: true # Enable caching of the value to act as storage
|
|
45
|
-
}
|
|
46
20
|
}
|
|
21
|
+
|
|
22
|
+
# Because ::Myco::Instance is a ::BasicObject, we must shadow ::BasicObject here
|
|
23
|
+
::Myco::Instance::BasicObject: ::Myco::BasicObject
|
|
24
|
+
|
data/lib/myco/core/Decorator.my
CHANGED
|
@@ -11,9 +11,6 @@
|
|
|
11
11
|
# importing the constants defined therein into the current namespace.
|
|
12
12
|
import: Decorator {
|
|
13
13
|
apply: |meme, *args| {
|
|
14
|
-
# TODO: shouldn't have to use meme.target here;
|
|
15
|
-
# should be able to use 'parent' to reach the outer objects
|
|
16
|
-
# while still referring to distinct instances rather than the originals.
|
|
17
14
|
load_paths = [meme.target.instance.dirname]
|
|
18
15
|
scope = meme.target.constant_scope
|
|
19
16
|
component = Myco.eval_file(meme.name.to_s, load_paths, false, scope)
|
data/lib/myco/core/Object.my
CHANGED
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
# Send the named signal to all handlers for this object
|
|
4
4
|
__signal__: |name, *args, &block| {
|
|
5
5
|
component.ancestors.reverse.each |other| {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
other.is_a?(Component) && (
|
|
7
|
+
inst = (component == other) &? self ?? other.instance
|
|
8
|
+
inst.?decorators.?on.?signal_handlers(name).each |meme| {
|
|
9
|
+
meme.result_for(self, *args, &block)
|
|
10
|
+
}
|
|
11
|
+
)
|
|
10
12
|
}
|
|
11
13
|
}
|
|
12
14
|
|
data/lib/myco/eval.rb
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
module Myco
|
|
3
3
|
|
|
4
4
|
# Most of method is stolen from Rubinius implementation of Kernel#eval
|
|
5
|
-
|
|
5
|
+
# TODO: remove in favor of CodeLoader
|
|
6
|
+
def self.eval(string, scope=nil, filename=nil, lineno=nil, type=:myco)
|
|
6
7
|
string = StringValue(string)
|
|
7
8
|
filename = StringValue(filename) if filename
|
|
8
9
|
lineno = Rubinius::Type.coerce_to lineno, Fixnum, :to_i if lineno
|
|
@@ -20,34 +21,32 @@ module Myco
|
|
|
20
21
|
existing_scope = binding.constant_scope
|
|
21
22
|
binding.constant_scope = existing_scope.dup
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
compiler_class = case type
|
|
25
|
+
when :myco; Myco::ToolSet::Compiler
|
|
26
|
+
when :ruby; Rubinius::ToolSets::Runtime::Compiler
|
|
27
|
+
else; raise NotImplementedError
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
be = compiler_class.construct_block string, binding, filename, lineno
|
|
25
31
|
|
|
26
32
|
result = be.call_on_instance(binding.self)
|
|
27
33
|
binding.constant_scope = existing_scope
|
|
28
34
|
result
|
|
29
35
|
end
|
|
30
36
|
|
|
31
|
-
# TODO:
|
|
37
|
+
# TODO: deprecate with proper import set of functions
|
|
32
38
|
def self.eval_file path, load_paths=nil, get_last=true, scope=nil
|
|
33
39
|
load_paths ||= [File.dirname(Rubinius::VM.backtrace(1).first.file)]
|
|
34
|
-
|
|
35
|
-
tmp_path = File.expand_path(path)
|
|
36
|
-
use_path = File.file?(tmp_path) && tmp_path
|
|
37
|
-
load_paths.each do |load_path|
|
|
38
|
-
break if use_path
|
|
39
|
-
tmp_path = File.expand_path(path, load_path)
|
|
40
|
-
use_path = File.file?(tmp_path) && tmp_path
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
raise ArgumentError, "Couldn't resolve file: #{path.inspect} \n" \
|
|
44
|
-
"in load_paths: #{load_paths.inspect}" \
|
|
45
|
-
unless use_path
|
|
46
|
-
|
|
47
|
-
file_toplevel = Myco.eval File.read(use_path), scope, use_path, 1
|
|
40
|
+
file_toplevel = CodeLoader.load(path, load_paths, cscope:scope, call_depth:1)
|
|
48
41
|
get_last ? file_toplevel.component.__last__ : file_toplevel.component
|
|
49
42
|
end
|
|
50
43
|
|
|
44
|
+
def self.file_to_ruby use_path
|
|
45
|
+
parser = Myco::ToolSet::Parser.new(use_path, 1, [])
|
|
46
|
+
ast = parser.parse_string File.read(use_path)
|
|
47
|
+
ast.to_ruby_code
|
|
48
|
+
end
|
|
49
|
+
|
|
51
50
|
def self.rescue
|
|
52
51
|
begin
|
|
53
52
|
yield
|