Class: WLang::Dialect

Inherits:
Object
  • Object
show all
Defined in:
lib/wlang/dialect.rb,
lib/wlang/dialect_dsl.rb,
lib/wlang/dialect_loader.rb

Overview

Implements the dialect abstraction (see README). A dialect instance is an aggregation of encoders and ruleset (through EncoderSet and RuleSet classes). A dialect is also a node in the dialect tree and has a qualified name through this tree. For example wlang/xhtml is the qualified name of a xhtml dialect which is a child dialect of wlang.

Users are not intended to use this class directly. Use the Domain Specific Language instead (see WLang::Dialect::DSL).

For developers only

In order to avoid having users to install all required gems of all dialects wlang implements a lazy load design pattern on the dialect tree, through the WLang::Dialect::DSL and WLang::Dialect::Loader classes. The former only creates Dialect instances as tree nodes (by chaining dialects through @parent) and installs mapping with file extensions. Rules and encoders are not initially installed (precisely: WLang::Dialect::DSL#require_ruby is simply ignored). When a given dialect is needed by wlang it is first built (through the build! method and the WLang::Dialect::Loader class).

Standard dialect obtention methods (WLang#dialect as well as WLang::Dialect#dialect) ensure that returned dialects are built. If you obtain dialects another way, be sure that they are built before using them (is_built? and build! are your friends to achieve that goal).

Moreover, child dialects may require tools of their ancestors. The following invariant should always be respected: if a dialect is built, all its ancestors are built as well. This invariant is not enforced by the build! method because it is trivially respected by the way WLang::Dialect#dialect is implemented.

Defined Under Namespace

Classes: DSL, Loader

Instance Attribute Summary (collapse)

Instance Method Summary (collapse)

Constructor Details

- (Dialect) initialize(name, parent, &builder)

Creates a dialect instance. builder block is a chunk of code of the DSL that will be executed twice: once at construction time to create sub dialects nodes and install file extensions and once at building time to install ruleset and encoders.



63
64
65
66
67
68
69
70
# File 'lib/wlang/dialect.rb', line 63

def initialize(name, parent, &builder)
  @name, @parent = name, parent 
  @builder, @built = builder, builder.nil?
  @dialects = nil
  @encoders = nil
  @ruleset = nil
  DSL.new(self).instance_eval(&builder) unless builder.nil?
end

Instance Attribute Details

- (Object) dialects (readonly)

Sub dialects by name



52
53
54
# File 'lib/wlang/dialect.rb', line 52

def dialects
  @dialects
end

- (Object) encoders (readonly)

Underlying encoders



43
44
45
# File 'lib/wlang/dialect.rb', line 43

def encoders
  @encoders
end

- (Object) name (readonly)

Dialect name



46
47
48
# File 'lib/wlang/dialect.rb', line 46

def name
  @name
end

- (Object) parent (readonly)

Parent dialect



49
50
51
# File 'lib/wlang/dialect.rb', line 49

def parent
  @parent
end

- (Object) post_transformer

Post transformer



55
56
57
# File 'lib/wlang/dialect.rb', line 55

def post_transformer
  @post_transformer
end

- (Object) ruleset (readonly)

Underlying ruleset



40
41
42
# File 'lib/wlang/dialect.rb', line 40

def ruleset
  @ruleset
end

Instance Method Details

- (Object) add_child_dialect(name, child)

Adds a child dialect under name. name cannot be qualified and must be a valid dialect name according to the wlang specification (see WLang::DIALECT_NAME_REGEXP). child must be a Dialect instance.

Raises:

  • (ArgumentError)


99
100
101
102
103
104
# File 'lib/wlang/dialect.rb', line 99

def add_child_dialect(name, child)
  raise(ArgumentError, "Invalid dialect name") unless WLang::DIALECT_NAME_REGEXP =~ name
  raise(ArgumentError, "Dialect expected") unless Dialect===child
  @dialects = {} if @dialects.nil?
  @dialects[name] = child
end

- (Object) add_encoder(name, &block)

See EncoderSet#add_encoder



107
108
109
110
# File 'lib/wlang/dialect.rb', line 107

def add_encoder(name, &block)
  @encoders = EncoderSet.new if @encoders.nil?
  @encoders.add_encoder(name, &block)
end

- (Object) add_encoders(mod, pairs)

See EncoderSet#add_encoders



113
114
115
116
# File 'lib/wlang/dialect.rb', line 113

def add_encoders(mod, pairs)
  @encoders = EncoderSet.new if @encoders.nil?
  @encoders.add_encoders(mod, pairs)
end

- (Object) add_rule(name, &block)

See RuleSet::add_rule



119
120
121
122
# File 'lib/wlang/dialect.rb', line 119

def add_rule(name, &block)
  @ruleset = RuleSet.new if @ruleset.nil?
  @ruleset.add_rule(name, &block)
end

- (Object) add_rules(mod, pairs)

See RuleSet::add_rules



125
126
127
128
# File 'lib/wlang/dialect.rb', line 125

def add_rules(mod, pairs)
  @ruleset = RuleSet.new if @ruleset.nil?
  @ruleset.add_rules(mod, pairs)
end

- (Object) apply_post_transform(text)

Applies post transformation



175
176
177
178
179
180
181
182
183
184
# File 'lib/wlang/dialect.rb', line 175

def apply_post_transform(text)
  case self.post_transformer
    when String
      WLang::encode(text, self.post_transformer, {})
    when Proc
      self.post_transformer.call(text)
    else
      text
  end
end

- (Object) build!

Force the dialect to be built. Has no effect if it is already built. Invokes the DSL chunk of code through WLang::DSL::Loader otherwise.



78
79
80
81
82
83
84
# File 'lib/wlang/dialect.rb', line 78

def build!
  unless is_built?
    WLang::Dialect::Loader.new(self).instance_eval(&@builder)
    @built = true
  end
  self
end

- (Object) dialect(name)

Finds a child dialect by name. name can be a String denoting a qualified name as well as an Array of strings, resulting from a qualified name split. This method should always be invoked on built dialects, it always returns nil otherwise. When found, returned dialect is automatically built as well as all its ancestors. When not found, the method returns nil.



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/wlang/dialect.rb', line 145

def dialect(name)
  # implement argument conventions
  if String===name
    raise(ArgumentError, "Invalid dialect name #{name}") unless WLang::QUALIFIED_DIALECT_NAME_REGEXP =~ name
    name = name.split('/')
  elsif not(Array===name)
    raise(ArgumentError,"Invalid dialect name #{name}")
  end

  # not built or no child at all
  return nil if @dialects.nil?

  # get first child name and find it
  child_name = name[0]
  child_dialect = @dialects[child_name]

  if child_dialect.nil?
    # unexisting, return nil
    return nil
  elsif name.length==1
    # found and last of qualified name -> build it
    return child_dialect.build!
  else
    # found but not last of qualified name -> build it and delegate
    child_dialect.build!
    return child_dialect.dialect(name[1..-1]) 
  end
end

- (Object) encoder(name)

Finds an encoder by name.



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/wlang/dialect.rb', line 189

def encoder(name)
  # implement argument conventions
  if String===name
    raise(ArgumentError, "Invalid encoder name #{name}") unless WLang::QUALIFIED_ENCODER_NAME_REGEXP =~ name
    name = name.split('/')
  elsif not(Array===name)
    raise(ArgumentError,"Invalid encoder name #{name}")
  end

  # last name in qualified?
  if name.length==1
    # delegate to encoders
    return nil if @encoders.nil?
    return @encoders.get_encoder(name[0])
  else
    # find sub dialect, and delegate
    child_name = name[0]
    child_dialect = dialect(child_name)
    if child_dialect.nil?
      return nil
    else
      return child_dialect.encoder(name[1..-1]) 
    end
  end
end

- (Object) factor_buffer

Factors a spacing friendly buffer for instantiation in this dialect



238
239
240
241
# File 'lib/wlang/dialect.rb', line 238

def factor_buffer
  #IntelligentBuffer.new
  ""
end

- (Object) find_encoder(name)

Finds a encoder in dialect tree

Raises:

  • (ArgumentError)


216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/wlang/dialect.rb', line 216

def find_encoder(name)
  raise(ArgumentError, "Invalid (relative) encoder name #{name}") unless String===name
  raise(ArgumentError, "Invalid (relative) encoder name #{name}") if name.include?("/")
  return nil if @encoders.nil?
  if @encoders.has_encoder?(name)
    @encoders.get_encoder(name)
  elsif @parent
    @parent.find_encoder(name)
  else
    nil
  end
end

- (Boolean) is_built?

Returns true if this dialect is already built, false otherwise.

Returns:

  • (Boolean)


87
88
89
# File 'lib/wlang/dialect.rb', line 87

def is_built?
  return @built
end

- (Object) pattern(block_symbols)

See RuleSet#pattern.



230
231
232
233
# File 'lib/wlang/dialect.rb', line 230

def pattern(block_symbols)
  return RuleSet.new.pattern(block_symbols) if @ruleset.nil?
  @ruleset.pattern(block_symbols)
end

- (Object) qualified_name

Returns qualified name of this dialect



133
134
135
136
# File 'lib/wlang/dialect.rb', line 133

def qualified_name
  parentname = @parent.nil? ? "" : @parent.to_s
  return ""==parentname ? @name : parentname + '/' + @name 
end

- (Object) to_s

Returns a string representation



244
245
246
# File 'lib/wlang/dialect.rb', line 244

def to_s
  qualified_name
end