Class: JsDuck::JsParser

Inherits:
JsLiteralParser show all
Defined in:
lib/jsduck/js_parser.rb

Instance Method Summary collapse

Methods inherited from JsLiteralParser

#array_literal, #literal, #look, #match, #object_literal, #object_literal_pair

Constructor Details

#initialize(input, options = {}) ⇒ JsParser

Returns a new instance of JsParser.



9
10
11
12
13
14
# File 'lib/jsduck/js_parser.rb', line 9

def initialize(input, options = {})
  super(input)
  @doc_parser = DocParser.new
  @docs = []
  @ext_namespaces = options[:ext_namespaces] || ["Ext"]
end

Instance Method Details

#code_blockObject

<code-block> := <function> | <var-declaration> | <ext-define> |

<assignment> | <property-literal>


75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/jsduck/js_parser.rb', line 75

def code_block
  if look(:function)
    function
  elsif look(:var)
    var_declaration
  elsif ext_look(:ns, ".", "define", "(", :string)
    ext_define
  elsif ext_look(:ns, ".", "ClassManager", ".", "create", "(", :string)
    ext_define
  elsif look(:ident, ":") || look(:string, ":")
    property_literal
  elsif look(",", :ident, ":") || look(",", :string, ":")
    match(",")
    property_literal
  elsif look(:ident) || look(:this)
    maybe_assignment
  elsif look(:string)
    {:type => :assignment, :left => [match(:string)[:value]]}
  else
    {:type => :nop}
  end
end

#expressionObject

<expression> := <function> | <ext-extend> | <ext-emptyfn> | <ext-base-css-prefix> | <literal>



180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/jsduck/js_parser.rb', line 180

def expression
  if look(:function)
    function
  elsif ext_look(:ns, ".", "extend")
    ext_extend
  elsif ext_look(:ns, ".", "emptyFn")
    ext_emptyfn
  elsif ext_look(:ns, ".", "baseCSSPrefix", "+", :string)
    ext_base_css_prefix
  else
    my_literal
  end
end

#ext_base_css_prefixObject

<ext-base-css-prefix> := “Ext” “.” “baseCSSPrefix” “+” <string>



237
238
239
240
241
242
243
244
# File 'lib/jsduck/js_parser.rb', line 237

def ext_base_css_prefix
  match(:ident, ".", "baseCSSPrefix", "+")
  return {
    :type => :literal,
    :class => "String",
    :value => '"x-' + match(:string)[:value] + '"',
  }
end

#ext_defineObject

<ext-define> := “Ext” “.” [“define” | “ClassManager” “.” “create” ] “(” <string> “,” <ext-define-cfg>



247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/jsduck/js_parser.rb', line 247

def ext_define
  match(:ident, ".");
  look("define") ? match("define") : match("ClassManager", ".", "create");
  name = match("(", :string)[:value]

  if look(",", "{")
    match(",")
    cfg = ext_define_cfg
  else
    cfg = {}
  end

  cfg[:type] = :ext_define
  cfg[:name] = name

  cfg
end

#ext_define_aliasObject

<alias> := “alias” “:” <string-or-list>



328
329
330
331
332
333
# File 'lib/jsduck/js_parser.rb', line 328

def ext_define_alias
  if look("alias", ":")
    match("alias", ":")
    string_or_list
  end
end

#ext_define_alternate_class_nameObject

<alternate-class-name> := “alternateClassName” “:” <string-or-list>



320
321
322
323
324
325
# File 'lib/jsduck/js_parser.rb', line 320

def ext_define_alternate_class_name
  if look("alternateClassName", ":")
    match("alternateClassName", ":")
    string_or_list
  end
end

#ext_define_cfgObject

<ext-define-cfg> := “{” ( <extend> | <mixins> | <alternate-class-name> | <alias> |

<xtype> | <requires> | <uses> | <singleton> | <?> )*


267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/jsduck/js_parser.rb', line 267

def ext_define_cfg
  match("{")
  cfg = {}
  found = true
  while found
    found = false
    if found = ext_define_extend
      cfg[:extend] = found
    elsif found = ext_define_mixins
      cfg[:mixins] = found
    elsif found = ext_define_alternate_class_name
      cfg[:alternateClassNames] = found
    elsif found = ext_define_alias
      cfg[:alias] = found
    elsif found = ext_define_xtype
      cfg[:xtype] = found
    elsif found = ext_define_requires
      cfg[:requires] = found
    elsif found = ext_define_uses
      cfg[:uses] = found
    elsif found = ext_define_singleton
      cfg[:singleton] = found
    elsif found = ext_define_whatever
      # ignore this
    end
    match(",") if look(",")
  end
  cfg
end

#ext_define_extendObject

<extend> := “extend” “:” <string>



298
299
300
301
302
# File 'lib/jsduck/js_parser.rb', line 298

def ext_define_extend
  if look("extend", ":", :string)
    match("extend", ":", :string)[:value]
  end
end

#ext_define_mixinsObject

<mixins> := “mixins” “:” [ <object-literal> | <array-literal> ]



305
306
307
308
309
310
311
312
313
314
315
316
317
# File 'lib/jsduck/js_parser.rb', line 305

def ext_define_mixins
  if look("mixins", ":")
    match("mixins", ":")
    lit = literal
    if lit && lit[:type] == :object
      lit[:value].map {|x| x[:value][:value] }
    elsif lit && lit[:type] == :array
      lit[:value].map {|x| x[:value] }
    else
      nil
    end
  end
end

#ext_define_requiresObject

<requires> := “requires” “:” <string-or-list>



344
345
346
347
348
349
# File 'lib/jsduck/js_parser.rb', line 344

def ext_define_requires
  if look("requires", ":")
    match("requires", ":")
    string_or_list
  end
end

#ext_define_singletonObject

<singleton> := “singleton” “:” “true”



360
361
362
363
364
365
# File 'lib/jsduck/js_parser.rb', line 360

def ext_define_singleton
  if look("singleton", ":", "true")
    match("singleton", ":", "true")
    true
  end
end

#ext_define_usesObject

<uses> := “uses” “:” <string-or-list>



352
353
354
355
356
357
# File 'lib/jsduck/js_parser.rb', line 352

def ext_define_uses
  if look("uses", ":")
    match("uses", ":")
    string_or_list
  end
end

#ext_define_whateverObject

<?> := <ident> “:” <literal>



368
369
370
371
372
373
# File 'lib/jsduck/js_parser.rb', line 368

def ext_define_whatever
  if look(:ident, ":")
    match(:ident, ":")
    literal
  end
end

#ext_define_xtypeObject

<xtype> := “xtype” “:” <string-or-list>



336
337
338
339
340
341
# File 'lib/jsduck/js_parser.rb', line 336

def ext_define_xtype
  if look("xtype", ":")
    match("xtype", ":")
    string_or_list
  end
end

#ext_emptyfnObject

<ext-emptyfn> := “Ext” “.” “emptyFn”



110
111
112
113
114
115
116
117
# File 'lib/jsduck/js_parser.rb', line 110

def ext_emptyfn
  match(:ident, ".", "emptyFn")
  return {
    :type => :function,
    :name => "",
    :params => [],
  }
end

#ext_extendObject

<ext-extend> := “Ext” “.” “extend” “(” <ident-chain> “,” …



228
229
230
231
232
233
234
# File 'lib/jsduck/js_parser.rb', line 228

def ext_extend
  match(:ident, ".", "extend", "(")
  return {
    :type => :ext_extend,
    :extend => ident_chain,
  }
end

#ext_look(placeholder, *args) ⇒ Object

Like look() but tries to match as the first argument all the names listed in @ext_namespaces



401
402
403
404
405
406
# File 'lib/jsduck/js_parser.rb', line 401

def ext_look(placeholder, *args)
  @ext_namespaces.each do |ns|
    return true if look(ns, *args)
  end
  return false
end

#functionObject

<function> := “function” [ <ident> ] <function-parameters> <function-body>



99
100
101
102
103
104
105
106
107
# File 'lib/jsduck/js_parser.rb', line 99

def function
  match(:function)
  return {
    :type => :function,
    :name => look(:ident) ? match(:ident)[:value] : "",
    :params => function_parameters,
    :body => function_body,
  }
end

#function_bodyObject

<function-body> := “{” …



131
132
133
# File 'lib/jsduck/js_parser.rb', line 131

def function_body
  match("{")
end

#function_parametersObject

<function-parameters> := “(” [ <ident> [ “,” <ident> ]* ] “)”



120
121
122
123
124
125
126
127
128
# File 'lib/jsduck/js_parser.rb', line 120

def function_parameters
  match("(")
  params = look(:ident) ? [{:name => match(:ident)[:value]}] : []
  while look(",", :ident) do
    params << {:name => match(",", :ident)[:value]}
  end
  match(")")
  return params
end

#ident_chainObject

<ident-chain> := [ “this” | <ident> ] [ “.” <ident> ]*



165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/jsduck/js_parser.rb', line 165

def ident_chain
  if look(:this)
    match(:this)
    chain = ["this"]
  else
    chain = [match(:ident)[:value]]
  end

  while look(".", :ident) do
    chain << match(".", :ident)[:value]
  end
  return chain
end

#literal_expression_end?Boolean

True when we’re at the end of literal expression. “,”, “;” and “}” are the normal closing symbols, but for our docs purposes doc-comment and file end work too.

Returns:

  • (Boolean)


223
224
225
# File 'lib/jsduck/js_parser.rb', line 223

def literal_expression_end?
  look(",") || look(";") || look("}") || look(:doc_comment) || @lex.empty?
end

#maybe_assignmentObject

<maybe-assignment> := <ident-chain> ( “=” <expression> | “;” | “,” )



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/jsduck/js_parser.rb', line 142

def maybe_assignment
  left = ident_chain
  if look("=")
    match("=")
    right = expression
  elsif look(";")
    match(";")
    right = nil
  elsif look(",")
    match(",")
    right = nil
  else
    return {:type => :nop}
  end

  return {
    :type => :assignment,
    :left => left,
    :right => right,
  }
end

#my_literalObject

<literal> := …see JsLiteralParser…



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/jsduck/js_parser.rb', line 195

def my_literal
  lit = literal
  return unless lit && literal_expression_end?

  cls_map = {
    :string => "String",
    :number => "Number",
    :regex => "RegExp",
    :array => "Array",
    :object => "Object",
  }

  if cls_map[lit[:type]]
    cls = cls_map[lit[:type]]
  elsif lit[:type] == :ident && (lit[:value] == "true" || lit[:value] == "false")
    cls = "Boolean"
  else
    cls = nil
  end

  value = JsLiteralBuilder.new.to_s(lit)

  {:type => :literal, :class => cls, :value => value}
end

#parseObject

Parses the whole JavaScript block and returns array where for each doc-comment there is a hash of three values: the comment structure created by DocParser, number of the line where the comment starts, and parsed structure of the code that immediately follows the comment.

For example with the following JavaScript input:

/**

* @param {String} foo
*/

MyClass.doIt = function(foo, bar) { }

The return value of this function will be:

[

{
  :comment => [
    {:tagname => :default, :doc => "Method description"},
    {:tagname => :return, :type => "Number", :doc => ""},
  ],
  :linenr => 1,
  :code => {
    :type => :assignment,
    :left => ["MyClass", "doIt"],
    :right => {
      :type => :function,
      :name => nil,
      :params => [
        {:name => "foo"},
        {:name => "bar"}
      ]
    }
  }
}

]



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/jsduck/js_parser.rb', line 54

def parse
  while !@lex.empty? do
    if look(:doc_comment)
      comment = @lex.next(true)
      @docs << {
        :comment => @doc_parser.parse(comment[:value]),
        :linenr => comment[:linenr],
        :code => code_block
      }
    else
      @lex.next
    end
  end
  @docs
end

#property_literalObject

<property-literal> := ( <ident> | <string> ) “:” <expression>



388
389
390
391
392
393
394
395
396
397
# File 'lib/jsduck/js_parser.rb', line 388

def property_literal
  left = look(:ident) ? match(:ident)[:value] : match(:string)[:value]
  match(":")
  right = expression
  return {
    :type => :assignment,
    :left => [left],
    :right => right,
  }
end

#string_or_listObject

<string-or-list> := ( <string> | <array-literal> )



376
377
378
379
380
381
382
383
384
385
# File 'lib/jsduck/js_parser.rb', line 376

def string_or_list
  lit = literal
  if lit && lit[:type] == :string
    [ lit[:value] ]
  elsif lit && lit[:type] == :array
    lit[:value].map {|x| x[:value] }
  else
    []
  end
end

#var_declarationObject

<var-declaration> := “var” <assignment>



136
137
138
139
# File 'lib/jsduck/js_parser.rb', line 136

def var_declaration
  match(:var)
  maybe_assignment
end