cog 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,59 @@
1
+ module Cog
2
+ module DSL
3
+
4
+ # DSL for defining cog features
5
+ class FeatureDSL
6
+
7
+ # @api developer
8
+ # @return [Cog::Seed] the seed which is defined by this DSL object
9
+ attr_reader :feature
10
+
11
+ # @api developer
12
+ def initialize(seed, name, opt={})
13
+ @feature = Seed::Feature.new seed, name
14
+ end
15
+
16
+ # Describe what this method does in one line
17
+ # @param value [String] a short description
18
+ # @return [nil]
19
+ def desc(value)
20
+ feature_eval { @desc = value }
21
+ nil
22
+ end
23
+
24
+ # Declare this method abstract
25
+ # @return [nil]
26
+ def abstract
27
+ feature_eval { @abstract = true }
28
+ nil
29
+ end
30
+
31
+ # Add a parameter to the feature
32
+ # @param type [Symbol] the type of the parameter
33
+ # @param name [String] name of the parameter
34
+ # @option opt [String] :desc (nil) optional description which will be used in documentation
35
+ # @option opt [Seed] :scope (nil) optional scope to use when rendering the variable in a qualified way
36
+ # @return [nil]
37
+ def param(type, name, opt={})
38
+ v = Seed::Var.new type, name, opt
39
+ feature_eval { @params << v }
40
+ nil
41
+ end
42
+
43
+ # Define the return type for the method.
44
+ # Will be `:void` if this method is never called.
45
+ # @param type [Symbol] a type
46
+ # @return [nil]
47
+ def return(type)
48
+ feature_eval { @return_type = type }
49
+ nil
50
+ end
51
+
52
+ private
53
+
54
+ def feature_eval(&block)
55
+ @feature.instance_eval &block
56
+ end
57
+ end
58
+ end
59
+ end
@@ -48,12 +48,20 @@ module Cog
48
48
 
49
49
  # Define file extensions
50
50
  # @param values [Array<String>] list of file extensions for this language
51
+ # @return [nil]
51
52
  def extension(*values)
52
53
  lang_eval do
53
54
  @extensions = values.collect {|key| key.to_s.downcase}
54
55
  end
55
56
  end
56
57
 
58
+ def seed_extension(ext, opt={})
59
+ lang_eval do
60
+ @seed_extension = ext.to_s.downcase
61
+ @seed_header = opt[:header].to_s.downcase if opt[:header]
62
+ end
63
+ end
64
+
57
65
  # Define a block to call when using a named scopes in this language
58
66
  # @yieldparam name [String] name of the scope to use
59
67
  # @yieldreturn [String] a using named scope statement in this language
@@ -106,6 +114,86 @@ module Cog
106
114
  lang_eval { @include_guard_style = lang_key.to_s.downcase }
107
115
  nil
108
116
  end
117
+
118
+ # Enumerate reserved identifiers in the language
119
+ # @param words [Array<String>] a list of words which must not be used as identifiers in the language
120
+ # @return [nil]
121
+ def reserved(words)
122
+ lang_eval { @reserved = words }
123
+ nil
124
+ end
125
+
126
+ # Map a cog primitive type to a native type in this language
127
+ # @param name [Symbol] name of the cog primitive type
128
+ # @param ident [String] identifier of the mapped type in this language
129
+ # @yieldparam obj [Object] a ruby object
130
+ # @yieldreturn [String,nil] the literal representation of the given obj in this language, or +nil+ if the object can not be mapped
131
+ # @return [nil]
132
+ def map_primitive(name, ident, &block)
133
+ lang_eval do
134
+ @prim_ident[name.to_sym] = ident.to_s
135
+ @prim_to_lit[name.to_sym] = block
136
+ end
137
+ nil
138
+ end
139
+
140
+ # Map the cog boolean type to a native type in this language
141
+ # @param ident [String] identifier of the boolean type in this language
142
+ # @yieldparam obj [Object] a ruby object
143
+ # @yieldreturn [String,nil] the boolean literal representation of the given obj in this language, or +nil+ if the object can not be mapped
144
+ # @return [nil]
145
+ def map_boolean(ident, &block) ; map_primitive(:boolean, ident, &block) ; end
146
+
147
+ # Map the cog integer type to a native type in this language
148
+ # @param ident [String] identifier of the integer type in this language
149
+ # @yieldparam obj [Object] a ruby object
150
+ # @yieldreturn [String,nil] the integer literal representation of the given obj in this language, or +nil+ if the object can not be mapped
151
+ # @return [nil]
152
+ def map_integer(ident, &block) ; map_primitive(:integer, ident, &block) ; end
153
+
154
+ # Map the cog long type to a native type in this language
155
+ # @param ident [String] identifier of the long type in this language
156
+ # @yieldparam obj [Object] a ruby object
157
+ # @yieldreturn [String,nil] the long literal representation of the given obj in this language, or +nil+ if the object can not be mapped
158
+ # @return [nil]
159
+ def map_long(ident, &block) ; map_primitive(:long, ident, &block) ; end
160
+
161
+ # Map the cog float type to a native type in this language
162
+ # @param ident [String] identifier of the float type in this language
163
+ # @yieldparam obj [Object] a ruby object
164
+ # @yieldreturn [String,nil] the float literal representation of the given obj in this language, or +nil+ if the object can not be mapped
165
+ # @return [nil]
166
+ def map_float(ident, &block) ; map_primitive(:float, ident, &block) ; end
167
+
168
+ # Map the cog double type to a native type in this language
169
+ # @param ident [String] identifier of the double type in this language
170
+ # @yieldparam obj [Object] a ruby object
171
+ # @yieldreturn [String,nil] the double literal representation of the given obj in this language, or +nil+ if the object can not be mapped
172
+ # @return [nil]
173
+ def map_double(ident, &block) ; map_primitive(:double, ident, &block) ; end
174
+
175
+ # Map the cog char type to a native type in this language
176
+ # @param ident [String] identifier of the char type in this language
177
+ # @yieldparam obj [Object] a ruby object
178
+ # @yieldreturn [String,nil] the char literal representation of the given obj in this language, or +nil+ if the object can not be mapped
179
+ # @return [nil]
180
+ def map_char(ident, &block) ; map_primitive(:char, ident, &block) ; end
181
+
182
+ # Map the cog string type to a native type in this language
183
+ # @param ident [String] identifier of the string type in this language
184
+ # @yieldparam obj [Object] a ruby object
185
+ # @yieldreturn [String,nil] the string literal representation of the given obj in this language, or +nil+ if the object can not be mapped
186
+ # @return [nil]
187
+ def map_string(ident, &block) ; map_primitive(:string, ident, &block) ; end
188
+
189
+ # Map the cog null type to a native type in this language
190
+ # @param ident [String] identifier of the null type in this language
191
+ # @yieldparam obj [Object] a ruby object
192
+ # @yieldreturn [String,nil] the null literal representation of the given obj in this language, or +nil+ if the object can not be mapped
193
+ # @return [nil]
194
+ def map_null(ident, &block) ; map_primitive(:null, ident, &block) ; end
195
+
196
+ def map_void(ident) ; map_primitive(:void, ident) ; end
109
197
 
110
198
  # @api developer
111
199
  # Compute the comment pattern
@@ -137,6 +225,7 @@ module Cog
137
225
  def lang_eval(&block)
138
226
  @lang.instance_eval &block
139
227
  end
228
+
140
229
  end
141
230
  end
142
231
  end
@@ -0,0 +1,51 @@
1
+ module Cog
2
+ module DSL
3
+
4
+ # DSL for defining cog seeds
5
+ class SeedDSL
6
+
7
+ # @api developer
8
+ # @return [Cog::Seed] the seed which is defined by this DSL object
9
+ attr_reader :seed
10
+
11
+ # @api developer
12
+ def initialize(name)
13
+ @seed = Cog::Seed.new name
14
+ end
15
+
16
+ # Place classes generated by this seed in the given scope
17
+ # @param name [String] name of the scope
18
+ # @return [nil]
19
+ def in_scope(name)
20
+ seed_eval { @in_scope = name.to_s }
21
+ nil
22
+ end
23
+
24
+ def constructor(&block)
25
+ feature :constructor, &block
26
+ end
27
+
28
+ def destructor(&block)
29
+ feature :destructor, &block
30
+ end
31
+
32
+ # Define a new feature for this seed
33
+ # @param name [String] name of the feature
34
+ # @yieldparam f [FeatureDSL] use this to further specify the feature
35
+ # @return [nil]
36
+ def feature(name, opt={}, &block)
37
+ dsl = FeatureDSL.new @seed, name, opt
38
+ block.call dsl unless block.nil?
39
+ f = dsl.feature
40
+ seed_eval { @features << f }
41
+ nil
42
+ end
43
+
44
+ private
45
+
46
+ def seed_eval(&block)
47
+ @seed.instance_eval &block
48
+ end
49
+ end
50
+ end
51
+ end
data/lib/cog/embeds.rb CHANGED
@@ -12,26 +12,34 @@ module Cog
12
12
  end
13
13
  end
14
14
 
15
+ # @param original [String] file in which to search for keep statements
16
+ # @param scratch [String] file to which keep bodies will be copied (used to create the {EmbedContext} objects)
17
+ # @return [Hash] mapping from hooks to {EmbedContext} objects
18
+ def gather_keeps(original, scratch)
19
+ keeps = {}
20
+ gather_from_file(original, :type => 'keep').each_pair do |hook, count|
21
+ c = keeps[hook] = EmbedContext.new(hook, scratch, count[original])
22
+ raise Errors::DuplicateKeep.new :hook => hook if c.count > 1
23
+ Helpers::FileScanner.scan(original, statement('keep', hook)) do |s|
24
+ c.keep_body = if s.match[4] == '{'
25
+ s.capture_until end_statement('keep')
26
+ s.captured_text
27
+ else
28
+ ''
29
+ end
30
+ end
31
+ end
32
+ keeps
33
+ end
34
+
15
35
  # Copy keep bodies from the original file to the scratch file
16
36
  # @param original [String] file in which to search for keep statements. If the original does not exist, then scratch will serve as the original (we do this so that the keeps will get expanded in any case)
17
37
  # @param scratch [String] file to which keep bodies will be copied
18
38
  # @return [nil]
19
39
  def copy_keeps(original, scratch)
20
- keeps = {}
21
40
  Cog.activate_language(:filename => original) do
22
41
  original = scratch unless File.exists? original
23
- gather_from_file(original, :type => 'keep').each_pair do |hook, count|
24
- c = keeps[hook] = EmbedContext.new(hook, scratch, count[original])
25
- raise Errors::DuplicateKeep.new :hook => hook if c.count > 1
26
- Helpers::FileScanner.scan(original, statement('keep', hook)) do |s|
27
- c.keep_body = if s.match[4] == '{'
28
- s.capture_until end_statement('keep')
29
- s.captured_text
30
- else
31
- ''
32
- end
33
- end
34
- end
42
+ keeps = gather_keeps original, scratch
35
43
  keeps.each_pair do |hook, c|
36
44
  result = update c, :type => 'keep' do |c|
37
45
  c.keep_body
@@ -128,7 +136,7 @@ module Cog
128
136
  end
129
137
  c.body = s.captured_text
130
138
  value = block.call(c).rstrip
131
- if c.once? || value != s.captured_text
139
+ if c.once? || value.normalize_eol != s.captured_text.rstrip.normalize_eol
132
140
  s.replace_captured_text(value + "\n", :once => c.once?)
133
141
  else
134
142
  false
data/lib/cog/errors.rb CHANGED
@@ -34,6 +34,8 @@ module Cog
34
34
  end
35
35
  end
36
36
 
37
+ define_error :ActiveLanguageDoesNotSupportSeeds
38
+
37
39
  define_error :ActionRequiresProjectGeneratorPath
38
40
  define_error :ActionRequiresProjectTemplatePath
39
41
  define_error :ActionRequiresProjectPluginPath
@@ -58,6 +60,8 @@ module Cog
58
60
 
59
61
  define_error :PluginPathIsNotADirectory
60
62
 
63
+ define_error :PrimitiveNotSupported
64
+
61
65
  define_error :ScopeStackUnderflow do
62
66
  "scope stack underflow: this can happen if you have too many *_end calls in a template"
63
67
  end
@@ -1,3 +1,9 @@
1
+ class String
2
+ def normalize_eol
3
+ gsub /(\r|\n|\r\n)/, "\n"
4
+ end
5
+ end
6
+
1
7
  module Cog
2
8
  module Helpers
3
9
 
@@ -17,6 +23,13 @@ module Cog
17
23
  @cap_begin_pos = nil
18
24
  @cap_end_pos = nil
19
25
  @match = nil # The last match object
26
+ @win_fudge = if RUBY_PLATFORM =~ /(mswin|mingw)/
27
+ x = @f.readline.end_with? '\r\n'
28
+ @f.seek 0
29
+ x ? 0 : 1
30
+ else
31
+ 0
32
+ end
20
33
  end
21
34
 
22
35
  # @api developer
@@ -43,10 +56,14 @@ module Cog
43
56
  val
44
57
  end
45
58
 
59
+ def win_fudge
60
+ (@f.lineno - 1) * @win_fudge
61
+ end
62
+
46
63
  # Remember this position. A later call to insert_at_mark will insert at this marked position
47
64
  # @return [nil]
48
65
  def mark!
49
- @mark = @f.pos
66
+ @mark = @f.pos - win_fudge
50
67
  @marked_line_number = @f.lineno + 1
51
68
  end
52
69
 
@@ -91,7 +108,7 @@ module Cog
91
108
  # @option opt [Array<Regexp>, Regexp] :but_not (nil) if a line matching any of the provided patterns is found before the desired pattern :bad_pattern_found will be thrown
92
109
  # @return [Boolean] whether or not the pattern was found
93
110
  def capture_until(pattern, opt={})
94
- @cap_begin_pos = @f.pos
111
+ @cap_begin_pos = @f.pos - win_fudge
95
112
  but_not = opt[:but_not] || []
96
113
  but_not = [but_not] unless but_not.is_a?(Array)
97
114
  while line = @f.readline
@@ -102,7 +119,7 @@ module Cog
102
119
  end
103
120
  return true if line =~ pattern
104
121
  @lines << line
105
- @cap_end_pos = @f.pos
122
+ @cap_end_pos = @f.pos # win_fudge not needed here
106
123
  end
107
124
  rescue EOFError
108
125
  false
@@ -111,7 +128,6 @@ module Cog
111
128
  # @return [String] text captured during capture_until. The last newline is stripped
112
129
  def captured_text
113
130
  x = @lines.join('')
114
- x = x.slice(0..-2) if x =~ /\n$/
115
131
  x
116
132
  end
117
133
 
@@ -121,11 +137,11 @@ module Cog
121
137
  def replace_captured_text(value, opt={})
122
138
  return false if @cap_begin_pos.nil? || @cap_end_pos.nil?
123
139
  @f.seek 0
124
- tmp.write @f.read(opt[:once] ? @mark : @cap_begin_pos)
125
- tmp.write value
140
+ tmp.write @f.read(opt[:once] ? @mark : @cap_begin_pos).normalize_eol
141
+ tmp.write value.normalize_eol
126
142
  @f.seek @cap_end_pos
127
143
  @f.readline if opt[:once]
128
- tmp.write @f.read
144
+ tmp.write @f.read.normalize_eol
129
145
  tmp.close
130
146
  close
131
147
  FileUtils.mv tmp_filename, @filename
@@ -137,10 +153,10 @@ module Cog
137
153
  def insert_at_mark(value)
138
154
  return false if @mark.nil?
139
155
  @f.seek 0
140
- tmp.write @f.read(@mark)
141
- tmp.write value
156
+ tmp.write @f.read(@mark).normalize_eol
157
+ tmp.write value.normalize_eol
142
158
  @f.readline # discard original
143
- tmp.write @f.read
159
+ tmp.write @f.read.normalize_eol
144
160
  tmp.close
145
161
  close
146
162
  FileUtils.mv tmp_filename, @filename
data/lib/cog/language.rb CHANGED
@@ -5,7 +5,10 @@ module Cog
5
5
 
6
6
  # @return [Array<String>] list of file extensions
7
7
  attr_reader :extensions
8
-
8
+
9
+ attr_reader :seed_extension
10
+ attr_reader :seed_header
11
+
9
12
  # @return [String] unique lower case identifier
10
13
  attr_reader :key
11
14
 
@@ -35,6 +38,9 @@ module Cog
35
38
  @named_scope_end_block = identibitch
36
39
  @include_guard_begin_block = identibitch
37
40
  @include_guard_end_block = identibitch
41
+ @reserved = []
42
+ @prim_ident = {} # :name => 'ident'
43
+ @prim_to_lit = {} # :name => to_literal_block
38
44
  end
39
45
 
40
46
  # @param nested_pattern [String] regular expression pattern (as a string) to embed in the regular expression which matches one line comments in this language
@@ -101,7 +107,7 @@ module Cog
101
107
  w ||= @name.length
102
108
  "#{@name.ljust w} -> #{@extensions.collect {|x| x.to_s}.sort.join ', '}"
103
109
  end
104
-
110
+
105
111
  # Sort by name
106
112
  def <=>(other)
107
113
  @name <=> other.name
@@ -136,5 +142,75 @@ module Cog
136
142
  def include_guard_end(name)
137
143
  @include_guard_end_block.call name
138
144
  end
145
+
146
+ # @param name [String] a potential identifier name
147
+ # @return [String] an escaped version of the identifier, if it conflicted with a reserved word in the language
148
+ def to_ident(name)
149
+ if @reserved.member? name.to_s
150
+ "#{name}_"
151
+ else
152
+ name.to_s
153
+ end
154
+ end
155
+
156
+ # @param name [Symbol] name of a primitive cog type
157
+ # @return [String] the representation of a primitive cog type in the native language
158
+ # @example
159
+ # # For Objective-C
160
+ # lang.to_prim :boolean # => 'BOOL'
161
+ def to_prim(name)
162
+ ident = @prim_ident[name.to_sym]
163
+ raise Errors::PrimitiveNotSupported.new :type => name, :language => @name unless ident
164
+ ident
165
+ end
166
+
167
+ # @param obj [Object] a ruby object
168
+ # @return [String] boolean literal representation of the object in this language
169
+ def to_boolean(obj) ; try_to_lit(:boolean, obj) ; end
170
+
171
+ # @param obj [Object] a ruby object
172
+ # @return [String] integer literal representation of the object in this language
173
+ def to_integer(obj) ; try_to_lit(:integer, obj) ; end
174
+
175
+ # @param obj [Object] a ruby object
176
+ # @return [String] long literal representation of the object in this language
177
+ def to_long(obj) ; try_to_lit(:long, obj) ; end
178
+
179
+ # @param obj [Object] a ruby object
180
+ # @return [String] float literal representation of the object in this language
181
+ def to_float(obj) ; try_to_lit(:float, obj) ; end
182
+
183
+ # @param obj [Object] a ruby object
184
+ # @return [String] double literal representation of the object in this language
185
+ def to_double(obj) ; try_to_lit(:double, obj) ; end
186
+
187
+ # @param obj [Object] a ruby object
188
+ # @return [String] char literal representation of the object in this language
189
+ def to_char(obj) ; try_to_lit(:char, obj) ; end
190
+
191
+ # @param obj [Object] a ruby object
192
+ # @return [String] string literal representation of the object in this language
193
+ def to_string(obj) ; try_to_lit(:string, obj) ; end
194
+
195
+ # @param obj [Object] a ruby object
196
+ # @return [String] null literal representation of the object in this language
197
+ def to_null(obj) ; try_to_lit(:null, obj) ; end
198
+
199
+ # @param obj [Object] a ruby object
200
+ # @return [String] literal representation of the object in this language
201
+ def to_lit(obj)
202
+ return obj.to_lit if obj.respond_to?(:to_lit)
203
+ raise Errors::PrimitiveNotSupported.new :object => obj
204
+ end
205
+
206
+ private
207
+
208
+ # @api developer
209
+ def try_to_lit(name, obj)
210
+ to_lit_block = @prim_to_lit[name]
211
+ raise Errors::PrimitiveNotSupported.new :type => name, :language => @name unless to_lit_block
212
+ to_lit_block.call obj if to_lit_block
213
+ end
214
+
139
215
  end
140
216
  end