myco 0.1.0.dev

Sign up to get free protection for your applications and to get access to all the features.
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
+ }