myco 0.1.0.dev
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +2 -0
- data/bin/myco +7 -0
- data/lib/myco/backtrace.rb +56 -0
- data/lib/myco/bootstrap/component.rb +142 -0
- data/lib/myco/bootstrap/empty_object.rb +4 -0
- data/lib/myco/bootstrap/file_toplevel.rb +5 -0
- data/lib/myco/bootstrap/find_constant.rb +86 -0
- data/lib/myco/bootstrap/instance.rb +52 -0
- data/lib/myco/bootstrap/meme.rb +160 -0
- data/lib/myco/bootstrap/void.rb +40 -0
- data/lib/myco/bootstrap.my +15 -0
- data/lib/myco/bootstrap.rb +10 -0
- data/lib/myco/command.my +33 -0
- data/lib/myco/core/BasicObject.my +46 -0
- data/lib/myco/core/Category.my +5 -0
- data/lib/myco/core/Decorator.my +18 -0
- data/lib/myco/core/FileToplevel.my +23 -0
- data/lib/myco/core/Object.my +24 -0
- data/lib/myco/core/Switch.my +31 -0
- data/lib/myco/eval.rb +63 -0
- data/lib/myco/parser/ast/constant_access.rb +29 -0
- data/lib/myco/parser/ast/constant_define.rb +40 -0
- data/lib/myco/parser/ast/constant_reopen.rb +47 -0
- data/lib/myco/parser/ast/declare_category.rb +51 -0
- data/lib/myco/parser/ast/declare_decorator.rb +35 -0
- data/lib/myco/parser/ast/declare_file.rb +54 -0
- data/lib/myco/parser/ast/declare_meme.rb +44 -0
- data/lib/myco/parser/ast/declare_object.rb +75 -0
- data/lib/myco/parser/ast/declare_string.rb +37 -0
- data/lib/myco/parser/ast/invoke.rb +66 -0
- data/lib/myco/parser/ast/local_variable_access_ambiguous.rb +38 -0
- data/lib/myco/parser/ast/misc.rb +61 -0
- data/lib/myco/parser/ast/myco_module_scope.rb +58 -0
- data/lib/myco/parser/ast/quest.rb +82 -0
- data/lib/myco/parser/ast.rb +15 -0
- data/lib/myco/parser/builder.output +3995 -0
- data/lib/myco/parser/builder.racc +585 -0
- data/lib/myco/parser/builder.rb +1592 -0
- data/lib/myco/parser/lexer.rb +2306 -0
- data/lib/myco/parser/lexer.rl +393 -0
- data/lib/myco/parser/lexer_char_classes.rl +56 -0
- data/lib/myco/parser/lexer_common.rb +95 -0
- data/lib/myco/parser/lexer_skeleton.rl +154 -0
- data/lib/myco/parser/peg_parser.kpeg +759 -0
- data/lib/myco/parser/peg_parser.rb +7094 -0
- data/lib/myco/parser.rb +40 -0
- data/lib/myco/tools/OptionParser.my +38 -0
- data/lib/myco/tools/mycompile.my +51 -0
- data/lib/myco/toolset.rb +16 -0
- data/lib/myco/version.rb +22 -0
- data/lib/myco.rb +15 -0
- metadata +247 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3da0c78adac1604211cf6cd16c6eacd6e714591c
|
4
|
+
data.tar.gz: b6444fe6690546c9ed22b96fedc9a3bb1083030e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e9b05edc6272eee428e038f4fae2b91d73aa83c0dd7e5941c50ec5d457a88d0920fc453826161606d8374d5fae0d9fc8bc3cea6d28bb394d1586ec87cb107195
|
7
|
+
data.tar.gz: 8b6193c91a85706f18cb09a529e575b571b4895a4e18f9f767b4e43ff525d84f871fd5d422219b28f372bbbcb8972afe275bcc086a4b6b3a895d1501b9896a11
|
data/LICENSE
ADDED
data/bin/myco
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
|
2
|
+
class Rubinius::Backtrace
|
3
|
+
def self.backtrace locations
|
4
|
+
::Myco::Backtrace.new(locations || [Rubinius::Location::Missing.new])
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
class Myco::Backtrace < Rubinius::Backtrace
|
10
|
+
def initialize(*)
|
11
|
+
super
|
12
|
+
@gem_color = "\033[0;36m"
|
13
|
+
@gem_paths = [Rubinius::GEMS_PATH, Rubinius::RUNTIME_PATH]
|
14
|
+
end
|
15
|
+
|
16
|
+
def show(sep="\n", show_color=true)
|
17
|
+
show_color &&= @colorize
|
18
|
+
clear = show_color ? "\033[0m" : ""
|
19
|
+
bold = show_color ? "\033[1m" : ""
|
20
|
+
sbullet = "{"
|
21
|
+
ebullet = "}"
|
22
|
+
|
23
|
+
@locations.map do |loc|
|
24
|
+
file = (loc.position(Dir.getwd) || "").sub /(\:\d+)$/, ' \1'
|
25
|
+
color = show_color ? color_from_loc(file, false) : ""
|
26
|
+
color = @gem_color if try_gem_path file
|
27
|
+
file_width = file.length + 1 + sbullet.length
|
28
|
+
|
29
|
+
place = loc.instance_variable_get(:@method_module).to_s + '#'
|
30
|
+
place += loc.describe_method
|
31
|
+
place_width = place.length + 1 + ebullet.length
|
32
|
+
|
33
|
+
padding = @width - file_width - place_width
|
34
|
+
padding += @width until padding >= 0
|
35
|
+
|
36
|
+
file_line = bold + color + sbullet + ' ' + clear + color + file
|
37
|
+
place_line = color + bold + place + ' ' + ebullet + clear
|
38
|
+
|
39
|
+
output = file_line + ' '*padding + place_line
|
40
|
+
output = nil if file == "(myco_internal) :1"
|
41
|
+
output
|
42
|
+
end.compact.reverse.join sep
|
43
|
+
end
|
44
|
+
|
45
|
+
# If file is in one of the GEM_PATHs, mutate the string and return true
|
46
|
+
def try_gem_path file
|
47
|
+
@gem_paths.each do |gem_path|
|
48
|
+
if file.start_with? gem_path and not gem_path.empty?
|
49
|
+
file.sub! File.join(gem_path, 'gems'), ''
|
50
|
+
file.sub! %r{/[^/]*/}, ''
|
51
|
+
return true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
return false
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
|
2
|
+
module Myco
|
3
|
+
class Component < Module
|
4
|
+
attr_accessor :__last__
|
5
|
+
attr_accessor :__name__
|
6
|
+
|
7
|
+
attr_reader :parent
|
8
|
+
attr_reader :parent_meme
|
9
|
+
attr_reader :memes
|
10
|
+
attr_reader :categories
|
11
|
+
|
12
|
+
attr_reader :constant_scope
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
if defined?(::Myco::Category) && (self < ::Myco::Category)
|
16
|
+
"#{parent.to_s}[#{@__name__}]"
|
17
|
+
elsif @__name__
|
18
|
+
@__name__.to_s
|
19
|
+
else
|
20
|
+
"#{@super_components.map(&:to_s).join(',')}" \
|
21
|
+
"(#{@basename}:#{@line.to_s} 0x#{object_id.to_s 16})"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.new super_components=[], parent=nil, filename=nil, line=nil
|
26
|
+
locations = Rubinius::VM.backtrace(1,false)
|
27
|
+
|
28
|
+
# Walk backwards on the backtrace until a lexical parent meme is found
|
29
|
+
i = 0
|
30
|
+
parent_meme = nil
|
31
|
+
current = nil
|
32
|
+
while true
|
33
|
+
current = locations[i]
|
34
|
+
break unless current
|
35
|
+
parent_meme = current.constant_scope.myco_meme
|
36
|
+
break if parent_meme
|
37
|
+
i += 1
|
38
|
+
end
|
39
|
+
@constant_scope = current.constant_scope if current
|
40
|
+
|
41
|
+
# Get the filename and line from the VM if not specified
|
42
|
+
if !filename || !line
|
43
|
+
location = locations.first
|
44
|
+
filename ||= location.file
|
45
|
+
line ||= location.line
|
46
|
+
end
|
47
|
+
|
48
|
+
this = super()
|
49
|
+
|
50
|
+
this.instance_eval {
|
51
|
+
@super_components = super_components
|
52
|
+
@memes = { }
|
53
|
+
@parent = parent
|
54
|
+
@filename = filename
|
55
|
+
@line = line
|
56
|
+
@basename = File.basename @filename
|
57
|
+
@dirname = File.dirname @filename
|
58
|
+
@categories = { nil => this }
|
59
|
+
@parent_meme = parent_meme
|
60
|
+
}
|
61
|
+
|
62
|
+
all_categories = Hash.new { |h,k| h[k] = Array.new }
|
63
|
+
|
64
|
+
super_components.each do |other|
|
65
|
+
this.include other if other
|
66
|
+
other.categories.each { |name, cat| all_categories[name] << cat }
|
67
|
+
end
|
68
|
+
|
69
|
+
all_categories.each do |name, supers|
|
70
|
+
if name.nil?
|
71
|
+
this.categories[name] = this
|
72
|
+
else
|
73
|
+
this.categories[name] = this.__new_category__ name, supers, filename, line
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
this
|
78
|
+
end
|
79
|
+
|
80
|
+
def __category__ name
|
81
|
+
@categories[name] ||= __new_category__(name)
|
82
|
+
end
|
83
|
+
|
84
|
+
def __new_category__ name, super_cats=[Category], filename=nil, line=nil
|
85
|
+
# Get the filename and line from the VM if not specified
|
86
|
+
if !filename || !line
|
87
|
+
location = Rubinius::VM.backtrace(2,false).first
|
88
|
+
filename ||= location.file
|
89
|
+
line ||= location.line
|
90
|
+
end
|
91
|
+
|
92
|
+
category = Component.new super_cats, self, filename, line
|
93
|
+
category.__name__ = name
|
94
|
+
category_instance = category.instance
|
95
|
+
declare_meme(name) { category_instance }
|
96
|
+
@memes[name].cache = true
|
97
|
+
|
98
|
+
category
|
99
|
+
end
|
100
|
+
|
101
|
+
def declare_meme name, decorations=[], body=nil, scope=nil, varscope=nil, &blk
|
102
|
+
body.scope = scope.dup if scope && body.respond_to?(:scope=)
|
103
|
+
meme = Meme.new self, name, body, &blk
|
104
|
+
|
105
|
+
decorations.each do |decoration, arguments|
|
106
|
+
search_component = self<Category ? parent : self
|
107
|
+
decorators = search_component.categories[:decorators].instance
|
108
|
+
|
109
|
+
raise KeyError, "Unknown decorator for #{self}##{name}: #{decoration}" \
|
110
|
+
unless decorators.respond_to?(decoration)
|
111
|
+
decorator = decorators.send(decoration)
|
112
|
+
decorator.transforms.apply meme, *arguments
|
113
|
+
decorator.apply meme, *arguments
|
114
|
+
end
|
115
|
+
meme.bind
|
116
|
+
|
117
|
+
meme
|
118
|
+
end
|
119
|
+
|
120
|
+
def instance
|
121
|
+
if !@instance
|
122
|
+
@instance = Instance.new(self)
|
123
|
+
@instance.extend self
|
124
|
+
yield @instance if block_given?
|
125
|
+
@instance.__signal__ :creation if @instance.respond_to? :__signal__
|
126
|
+
else
|
127
|
+
yield @instance if block_given?
|
128
|
+
end
|
129
|
+
|
130
|
+
@instance
|
131
|
+
end
|
132
|
+
|
133
|
+
def new parent=nil, **kwargs
|
134
|
+
loc = Rubinius::VM.backtrace(1,false).first
|
135
|
+
|
136
|
+
Component.new([self], parent, loc.file, loc.line).instance { |instance|
|
137
|
+
kwargs.each { |key,val| instance.send :"#{key}=", val }
|
138
|
+
}
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
|
2
|
+
# TODO: Move monkey patch to different file
|
3
|
+
module Rubinius
|
4
|
+
class ConstantScope
|
5
|
+
attr_reader :myco_file
|
6
|
+
attr_reader :myco_component
|
7
|
+
attr_reader :myco_category
|
8
|
+
attr_reader :myco_meme
|
9
|
+
|
10
|
+
def myco_levels
|
11
|
+
@myco_levels ||= (parent ? parent.myco_levels.dup : [])
|
12
|
+
end
|
13
|
+
|
14
|
+
def set_myco_file
|
15
|
+
raise "myco_file already set for thie ConstantScope" \
|
16
|
+
if @myco_file
|
17
|
+
@myco_component = self.module
|
18
|
+
@myco_file = self.module
|
19
|
+
|
20
|
+
@myco_file.instance_variable_set(:@constant_scope, self)
|
21
|
+
|
22
|
+
myco_levels << @myco_file
|
23
|
+
end
|
24
|
+
|
25
|
+
def set_myco_component
|
26
|
+
raise "myco_component already set for thie ConstantScope" \
|
27
|
+
if @myco_component
|
28
|
+
@myco_component = self.module
|
29
|
+
@myco_file = parent.myco_file
|
30
|
+
|
31
|
+
myco_levels << @myco_component
|
32
|
+
end
|
33
|
+
|
34
|
+
def set_myco_category
|
35
|
+
raise "myco_category already set for thie ConstantScope" \
|
36
|
+
if @myco_category
|
37
|
+
@myco_category = self.module
|
38
|
+
@myco_component = parent.myco_component
|
39
|
+
@myco_file = parent.myco_file
|
40
|
+
|
41
|
+
myco_levels << @myco_category
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_myco_meme value
|
45
|
+
raise "myco_meme already set for thie ConstantScope" \
|
46
|
+
if @myco_meme
|
47
|
+
@myco_meme = value
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
module Myco
|
53
|
+
|
54
|
+
def self.find_constant(name, scope)
|
55
|
+
name = ::Rubinius::Type.coerce_to_constant_name name
|
56
|
+
|
57
|
+
category = scope.myco_category
|
58
|
+
component = scope.myco_component
|
59
|
+
file = scope.myco_file
|
60
|
+
|
61
|
+
# TODO: optimize this constant search
|
62
|
+
# (it currently searches each ancestor of each nested component scope)
|
63
|
+
bucket = nil
|
64
|
+
scope.myco_levels.detect { |level|
|
65
|
+
bucket = find_constant_bucket_in_module(level, name)
|
66
|
+
}
|
67
|
+
bucket ? bucket.constant : ::Rubinius::Type.const_get(::Myco, name)
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.find_constant_bucket_in_module(mod, name, inherit=true)
|
71
|
+
current, constant = mod, nil
|
72
|
+
|
73
|
+
while current and ::Rubinius::Type.object_kind_of? current, Module
|
74
|
+
if bucket = current.constant_table.lookup(name)
|
75
|
+
return bucket
|
76
|
+
end
|
77
|
+
|
78
|
+
return nil unless inherit
|
79
|
+
|
80
|
+
current = current.direct_superclass
|
81
|
+
end
|
82
|
+
|
83
|
+
return nil
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
|
2
|
+
module Myco
|
3
|
+
class Instance < ::BasicObject
|
4
|
+
include ::Kernel
|
5
|
+
|
6
|
+
# TODO: clean this up
|
7
|
+
prepend (::Module.new {
|
8
|
+
def method_missing name, *args
|
9
|
+
msg = "#{to_s} has no method called '#{name}'"
|
10
|
+
::Kernel.raise ::NoMethodError.new(msg, name, args)
|
11
|
+
end
|
12
|
+
})
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
"#<#{@component.to_s}>"
|
16
|
+
end
|
17
|
+
|
18
|
+
def inspect
|
19
|
+
to_s
|
20
|
+
end
|
21
|
+
|
22
|
+
# TODO: remove (for now it makes debugging easier with RSpec)
|
23
|
+
def pretty_print str
|
24
|
+
p str
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize component
|
28
|
+
@component = component
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_reader :component
|
32
|
+
|
33
|
+
def parent
|
34
|
+
@component.parent && @component.parent.instance
|
35
|
+
end
|
36
|
+
|
37
|
+
def parent_meme
|
38
|
+
@component.parent_meme
|
39
|
+
end
|
40
|
+
|
41
|
+
def memes
|
42
|
+
@component.memes
|
43
|
+
end
|
44
|
+
|
45
|
+
# Commandeer a few methods implemented in Kernel
|
46
|
+
%i{ extend respond_to? method_missing hash }.each do |sym|
|
47
|
+
define_method sym do |*args, &blk|
|
48
|
+
::Kernel.instance_method(sym).bind(self).call(*args)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
|
2
|
+
module Myco
|
3
|
+
class Meme
|
4
|
+
attr_accessor :target
|
5
|
+
attr_accessor :name
|
6
|
+
attr_accessor :body
|
7
|
+
attr_accessor :cache
|
8
|
+
attr_accessor :expose
|
9
|
+
|
10
|
+
attr_reader :caches # TODO: don't expose; dynamic meth should send privately
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
"#<#{self.class}:#{self.name.to_s}>"
|
14
|
+
end
|
15
|
+
|
16
|
+
def inspect
|
17
|
+
to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize target, name, body=nil, &blk
|
21
|
+
self.target = target
|
22
|
+
self.name = name
|
23
|
+
self.body = body || blk
|
24
|
+
self.cache = false
|
25
|
+
self.expose = true
|
26
|
+
|
27
|
+
@caches = {}
|
28
|
+
end
|
29
|
+
|
30
|
+
def body= value
|
31
|
+
case value
|
32
|
+
when Rubinius::Executable
|
33
|
+
@body = value
|
34
|
+
@body.scope.set_myco_meme self # Set meme for lexical scope
|
35
|
+
when Proc
|
36
|
+
block_env = value.block.dup
|
37
|
+
block_env.change_name name
|
38
|
+
@body = Rubinius::BlockEnvironment::AsMethod.new block_env
|
39
|
+
value = value.dup
|
40
|
+
value.lambda_style!
|
41
|
+
else
|
42
|
+
raise ArgumentError,
|
43
|
+
"Meme body must be a Rubinius::Executable or a Proc"
|
44
|
+
end
|
45
|
+
|
46
|
+
@body
|
47
|
+
end
|
48
|
+
|
49
|
+
def bind
|
50
|
+
return if not @expose
|
51
|
+
|
52
|
+
meme = self
|
53
|
+
target.memes[@name] = meme
|
54
|
+
|
55
|
+
##
|
56
|
+
# This dynamic method is nearly the same as Meme#result_for
|
57
|
+
# (but written from the perspective of the receiver)
|
58
|
+
# implemented in bytecode to avoid forwarding to another method
|
59
|
+
# on the call stack.
|
60
|
+
# TODO: move this bytecode generation to a helper method
|
61
|
+
target.dynamic_method @name, '(myco_internal)' do |g|
|
62
|
+
g.splat_index = 0 # *args
|
63
|
+
|
64
|
+
invoke = g.new_label
|
65
|
+
ret = g.new_label
|
66
|
+
|
67
|
+
##
|
68
|
+
# meme = <this meme>
|
69
|
+
#
|
70
|
+
g.push_literal meme
|
71
|
+
g.set_local 1 # meme
|
72
|
+
g.pop
|
73
|
+
|
74
|
+
##
|
75
|
+
# caches = <this meme's @caches>
|
76
|
+
#
|
77
|
+
g.push_literal @caches
|
78
|
+
g.set_local 2 # caches
|
79
|
+
g.pop
|
80
|
+
|
81
|
+
##
|
82
|
+
# cache_key = [obj.hash, args.hash, blk.hash]
|
83
|
+
#
|
84
|
+
g.push_self; g.send :hash, 0
|
85
|
+
g.push_local 0; g.send :hash, 0 # args
|
86
|
+
g.push_block; g.send :hash, 0
|
87
|
+
g.make_array 3
|
88
|
+
g.set_local 3 # cache_key
|
89
|
+
g.pop
|
90
|
+
|
91
|
+
##
|
92
|
+
# if meme.cache.false? || !caches.has_key?(cache_key)
|
93
|
+
# return caches[cache_key]
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
g.push_local 1 # meme
|
97
|
+
g.send :cache, 0
|
98
|
+
g.send :false?, 0
|
99
|
+
g.goto_if_true invoke
|
100
|
+
|
101
|
+
g.push_local 2 # caches
|
102
|
+
g.push_local 3 # cache_key
|
103
|
+
g.send :has_key?, 1
|
104
|
+
g.goto_if_false invoke
|
105
|
+
|
106
|
+
g.push_local 2 # caches
|
107
|
+
g.push_local 3 # cache_key
|
108
|
+
g.send :[], 1
|
109
|
+
g.goto ret
|
110
|
+
|
111
|
+
##
|
112
|
+
# result = meme.body.invoke meme.name, @target, obj, args, blk
|
113
|
+
#
|
114
|
+
invoke.set!
|
115
|
+
|
116
|
+
g.push_local 1 # meme
|
117
|
+
g.send :body, 0
|
118
|
+
g.push_local 1; g.send :name, 0 # meme.name
|
119
|
+
g.push_local 1; g.send :target, 0 # meme.target
|
120
|
+
g.push_self
|
121
|
+
g.push_local 0 # args
|
122
|
+
g.push_block
|
123
|
+
g.send :invoke, 5
|
124
|
+
g.set_local 4 # result
|
125
|
+
g.pop
|
126
|
+
|
127
|
+
##
|
128
|
+
# return (caches[cache_key] = result)
|
129
|
+
#
|
130
|
+
g.push_local 2 # caches
|
131
|
+
g.push_local 3 # cache_key
|
132
|
+
g.push_local 4 # result
|
133
|
+
g.send :[]=, 2
|
134
|
+
|
135
|
+
ret.set!
|
136
|
+
g.ret
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def result *args, &blk
|
141
|
+
result_for target.instance, *args, &blk
|
142
|
+
end
|
143
|
+
|
144
|
+
def result_for obj, *args, &blk
|
145
|
+
cache_key = [obj.hash, args.hash, blk.hash]
|
146
|
+
if @cache && @caches.has_key?(cache_key)
|
147
|
+
return @caches[cache_key]
|
148
|
+
end
|
149
|
+
|
150
|
+
result = @body.invoke @name, @target, obj, args, blk
|
151
|
+
|
152
|
+
@caches[cache_key] = result
|
153
|
+
end
|
154
|
+
|
155
|
+
def set_result_for obj, result, *args, &blk
|
156
|
+
cache_key = [obj.hash, args.hash, blk.hash]
|
157
|
+
@caches[cache_key] = result
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
|
2
|
+
module Myco
|
3
|
+
class VoidClass < ::BasicObject
|
4
|
+
def self.new
|
5
|
+
@singleton ||= super
|
6
|
+
end
|
7
|
+
|
8
|
+
def inspect
|
9
|
+
"void"
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
""
|
14
|
+
end
|
15
|
+
|
16
|
+
def false?
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
def void?
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing *args
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def hash
|
29
|
+
nil.hash
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
Void = VoidClass.new
|
34
|
+
end
|
35
|
+
|
36
|
+
# Patch the base classes to make them respond_to :false?
|
37
|
+
class ::NilClass; def false?; true end; def void?; false end end
|
38
|
+
class ::TrueClass; def false?; false end; def void?; false end end
|
39
|
+
class ::FalseClass; def false?; true end; def void?; false end end
|
40
|
+
class ::BasicObject; def false?; false end; def void?; false end end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
RubyEval < EmptyObject {
|
3
|
+
from_string: |string| ::Kernel.instance_method(:eval).bind(self).call(string)
|
4
|
+
}
|
5
|
+
|
6
|
+
RubyEval @@@
|
7
|
+
Myco.eval_file("core/Category.my", [Myco::CoreLoadPath])
|
8
|
+
Myco.eval_file("core/BasicObject.my", [Myco::CoreLoadPath])
|
9
|
+
Myco.eval_file("core/Decorator.my", [Myco::CoreLoadPath])
|
10
|
+
Myco.eval_file("core/Object.my", [Myco::CoreLoadPath])
|
11
|
+
Myco.eval_file("core/FileToplevel.my", [Myco::CoreLoadPath])
|
12
|
+
|
13
|
+
# Below are not necessary for bootstrapping; TODO: move out of RubyEval
|
14
|
+
Myco.eval_file("core/Switch.my", [Myco::CoreLoadPath])
|
15
|
+
@@@
|
@@ -0,0 +1,10 @@
|
|
1
|
+
|
2
|
+
require_relative 'bootstrap/find_constant'
|
3
|
+
|
4
|
+
require_relative 'bootstrap/void'
|
5
|
+
require_relative 'bootstrap/meme'
|
6
|
+
require_relative 'bootstrap/instance'
|
7
|
+
require_relative 'bootstrap/component'
|
8
|
+
|
9
|
+
require_relative 'bootstrap/file_toplevel'
|
10
|
+
require_relative 'bootstrap/empty_object'
|
data/lib/myco/command.my
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
import "tools/OptionParser.my"
|
3
|
+
|
4
|
+
Object {
|
5
|
+
var options: OptionParser {
|
6
|
+
storage results: null
|
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
|
+
}
|
26
|
+
|
27
|
+
run: |*argv| {
|
28
|
+
files = options.parse(argv)
|
29
|
+
files.uniq.each |file| { Myco.eval_file(file, [::Dir.pwd]) }
|
30
|
+
}
|
31
|
+
|
32
|
+
on creation: run(*ARGV)
|
33
|
+
}
|
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
::Myco::BasicObject < ::Myco::EmptyObject {
|
3
|
+
# Basic conditional handling
|
4
|
+
if: |cond, &blk| cond && blk.call
|
5
|
+
unless: |cond, &blk| cond || blk.call
|
6
|
+
switch: |input,comparator=:"=="|
|
7
|
+
Switch.new(input:input, comparator:comparator)
|
8
|
+
|
9
|
+
puts: |*args| STDOUT.puts(*args)
|
10
|
+
p: |*args| STDOUT.puts(args.map |a| { a.inspect }.join(', '))
|
11
|
+
|
12
|
+
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
|
+
}
|