myco 0.1.0.dev

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.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +2 -0
  3. data/bin/myco +7 -0
  4. data/lib/myco/backtrace.rb +56 -0
  5. data/lib/myco/bootstrap/component.rb +142 -0
  6. data/lib/myco/bootstrap/empty_object.rb +4 -0
  7. data/lib/myco/bootstrap/file_toplevel.rb +5 -0
  8. data/lib/myco/bootstrap/find_constant.rb +86 -0
  9. data/lib/myco/bootstrap/instance.rb +52 -0
  10. data/lib/myco/bootstrap/meme.rb +160 -0
  11. data/lib/myco/bootstrap/void.rb +40 -0
  12. data/lib/myco/bootstrap.my +15 -0
  13. data/lib/myco/bootstrap.rb +10 -0
  14. data/lib/myco/command.my +33 -0
  15. data/lib/myco/core/BasicObject.my +46 -0
  16. data/lib/myco/core/Category.my +5 -0
  17. data/lib/myco/core/Decorator.my +18 -0
  18. data/lib/myco/core/FileToplevel.my +23 -0
  19. data/lib/myco/core/Object.my +24 -0
  20. data/lib/myco/core/Switch.my +31 -0
  21. data/lib/myco/eval.rb +63 -0
  22. data/lib/myco/parser/ast/constant_access.rb +29 -0
  23. data/lib/myco/parser/ast/constant_define.rb +40 -0
  24. data/lib/myco/parser/ast/constant_reopen.rb +47 -0
  25. data/lib/myco/parser/ast/declare_category.rb +51 -0
  26. data/lib/myco/parser/ast/declare_decorator.rb +35 -0
  27. data/lib/myco/parser/ast/declare_file.rb +54 -0
  28. data/lib/myco/parser/ast/declare_meme.rb +44 -0
  29. data/lib/myco/parser/ast/declare_object.rb +75 -0
  30. data/lib/myco/parser/ast/declare_string.rb +37 -0
  31. data/lib/myco/parser/ast/invoke.rb +66 -0
  32. data/lib/myco/parser/ast/local_variable_access_ambiguous.rb +38 -0
  33. data/lib/myco/parser/ast/misc.rb +61 -0
  34. data/lib/myco/parser/ast/myco_module_scope.rb +58 -0
  35. data/lib/myco/parser/ast/quest.rb +82 -0
  36. data/lib/myco/parser/ast.rb +15 -0
  37. data/lib/myco/parser/builder.output +3995 -0
  38. data/lib/myco/parser/builder.racc +585 -0
  39. data/lib/myco/parser/builder.rb +1592 -0
  40. data/lib/myco/parser/lexer.rb +2306 -0
  41. data/lib/myco/parser/lexer.rl +393 -0
  42. data/lib/myco/parser/lexer_char_classes.rl +56 -0
  43. data/lib/myco/parser/lexer_common.rb +95 -0
  44. data/lib/myco/parser/lexer_skeleton.rl +154 -0
  45. data/lib/myco/parser/peg_parser.kpeg +759 -0
  46. data/lib/myco/parser/peg_parser.rb +7094 -0
  47. data/lib/myco/parser.rb +40 -0
  48. data/lib/myco/tools/OptionParser.my +38 -0
  49. data/lib/myco/tools/mycompile.my +51 -0
  50. data/lib/myco/toolset.rb +16 -0
  51. data/lib/myco/version.rb +22 -0
  52. data/lib/myco.rb +15 -0
  53. 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
@@ -0,0 +1,2 @@
1
+ Copyright 2014 Joe McIlvain.
2
+ All rights reserved.
data/bin/myco ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/myco'
4
+
5
+ Myco.rescue do
6
+ Myco.eval_file("../lib/myco/command.my")
7
+ end
@@ -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,4 @@
1
+
2
+ module Myco
3
+ EmptyObject = Component.new
4
+ end
@@ -0,0 +1,5 @@
1
+
2
+ module Myco
3
+ # This empty, barebones component will be replaced after bootstrapping.
4
+ FileToplevel = Component.new
5
+ 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'
@@ -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
+ }
@@ -0,0 +1,5 @@
1
+
2
+ ::Myco::Category < ::Myco::EmptyObject {
3
+ # Forward unknown methods to parent
4
+ method_missing: |sym, *a, &b| parent.send(sym, *a, &b)
5
+ }