myco 0.1.0.dev → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|