fdlint 0.1.0

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 (218) hide show
  1. data/Gemfile +8 -0
  2. data/Gemfile.lock +14 -0
  3. data/README.md +68 -0
  4. data/Rakefile +92 -0
  5. data/bin/fdlint +17 -0
  6. data/lib/base_parser.rb +143 -0
  7. data/lib/cmd_runner.rb +145 -0
  8. data/lib/context.rb +31 -0
  9. data/lib/css/parser.rb +186 -0
  10. data/lib/css/reader.rb +30 -0
  11. data/lib/css/rule/check_compression_rule.rb +48 -0
  12. data/lib/css/rule/checklist.rb +45 -0
  13. data/lib/css/struct.rb +111 -0
  14. data/lib/encoding_error.rb +6 -0
  15. data/lib/file_validator.rb +38 -0
  16. data/lib/helper/code_type.rb +50 -0
  17. data/lib/helper/color_string.rb +44 -0
  18. data/lib/helper/file_reader.rb +22 -0
  19. data/lib/helper/strenc.rb +65 -0
  20. data/lib/html/parser.rb +212 -0
  21. data/lib/html/query.rb +96 -0
  22. data/lib/html/rule/check_tag_rule.rb +80 -0
  23. data/lib/html/struct.rb +291 -0
  24. data/lib/js/expr/expr.rb +66 -0
  25. data/lib/js/expr/left_hand.rb +63 -0
  26. data/lib/js/expr/operate.rb +92 -0
  27. data/lib/js/expr/primary.rb +166 -0
  28. data/lib/js/parser.rb +116 -0
  29. data/lib/js/rule/all.rb +35 -0
  30. data/lib/js/rule/checklist.rb +41 -0
  31. data/lib/js/rule/file_checker.rb +42 -0
  32. data/lib/js/rule/helper.rb +96 -0
  33. data/lib/js/rule/no_global.rb +87 -0
  34. data/lib/js/stat/if.rb +25 -0
  35. data/lib/js/stat/iter.rb +85 -0
  36. data/lib/js/stat/stat.rb +117 -0
  37. data/lib/js/stat/switch.rb +65 -0
  38. data/lib/js/stat/try.rb +28 -0
  39. data/lib/js/stat/var.rb +40 -0
  40. data/lib/js/struct.rb +248 -0
  41. data/lib/log_entry.rb +49 -0
  42. data/lib/node.rb +28 -0
  43. data/lib/parse_error.rb +13 -0
  44. data/lib/parser_visitable.rb +138 -0
  45. data/lib/position_info.rb +46 -0
  46. data/lib/printer/base_printer.rb +24 -0
  47. data/lib/printer/console_printer.rb +66 -0
  48. data/lib/printer/nocolor_printer.rb +27 -0
  49. data/lib/printer/vim_printer.rb +19 -0
  50. data/lib/rule.rb +241 -0
  51. data/lib/rule_helper.rb +14 -0
  52. data/lib/runner.rb +225 -0
  53. data/rules.d/css.rule +127 -0
  54. data/rules.d/html.dtd.rule +22 -0
  55. data/rules.d/html.prop.rule +51 -0
  56. data/rules.d/html.tag.rule +136 -0
  57. data/rules.d/js.file.rule +13 -0
  58. data/rules.d/js.jquery.rule +56 -0
  59. data/rules.d/js.mergefile.rule +71 -0
  60. data/rules.d/js.rule +84 -0
  61. data/test/all_tests.rb +84 -0
  62. data/test/cli/cli_test.rb +70 -0
  63. data/test/cli/log_level_test.rb +51 -0
  64. data/test/cli/output_format_test.rb +47 -0
  65. data/test/cli/type_test.rb +77 -0
  66. data/test/css/mac_line_end_support_test.rb +38 -0
  67. data/test/css/parser_test.rb +276 -0
  68. data/test/css/rule/check_encoding_test.rb +66 -0
  69. data/test/css/rule/check_list_rule_test.rb +167 -0
  70. data/test/css/rule/compression_test.rb +53 -0
  71. data/test/css/rule/file_name_test.rb +76 -0
  72. data/test/fixtures/css/broken.css +4 -0
  73. data/test/fixtures/css/cbu/36.css +52 -0
  74. data/test/fixtures/css/cbu/china_top.css +324 -0
  75. data/test/fixtures/css/cbu/default-merge.css +3 -0
  76. data/test/fixtures/css/cbu/default.css +13 -0
  77. data/test/fixtures/css/cbu/diy-merge.css +25 -0
  78. data/test/fixtures/css/cbu/fns-v1.css +27 -0
  79. data/test/fixtures/css/cbu/index_v0.1.css +12 -0
  80. data/test/fixtures/css/cbu/merge.css +11 -0
  81. data/test/fixtures/css/cbu/min.css +2 -0
  82. data/test/fixtures/css/cbu/my_home_admin.css +126 -0
  83. data/test/fixtures/css/cbu/nav.css +95 -0
  84. data/test/fixtures/css/cbu/pic_list.css +386 -0
  85. data/test/fixtures/css/cbu/quote-edit.css +18 -0
  86. data/test/fixtures/css/cbu/selloffer.shopwindow.css +1 -0
  87. data/test/fixtures/css/cbu/v1.css +9 -0
  88. data/test/fixtures/css/css3.css +30 -0
  89. data/test/fixtures/css/empty-min.css +0 -0
  90. data/test/fixtures/css/empty.css +0 -0
  91. data/test/fixtures/css/font-family.css +4 -0
  92. data/test/fixtures/css/gb-good.css +14 -0
  93. data/test/fixtures/css/gb_using_star.css +4 -0
  94. data/test/fixtures/css/import.css +18 -0
  95. data/test/fixtures/css/mac-line-sep-err-min.css +1 -0
  96. data/test/fixtures/css/mac-line-sep-err.css +1 -0
  97. data/test/fixtures/css/mac-line-sep-good-min.css +1 -0
  98. data/test/fixtures/css/mac-line-sep-good.css +1 -0
  99. data/test/fixtures/css/multi-encoding-in-a-file.css +0 -0
  100. data/test/fixtures/css/simple.css +1 -0
  101. data/test/fixtures/css/using_expr.css +8 -0
  102. data/test/fixtures/css/using_hack.css +21 -0
  103. data/test/fixtures/css/using_id.css +1 -0
  104. data/test/fixtures/css/using_star.css +4 -0
  105. data/test/fixtures/css/utf8_good.css +6 -0
  106. data/test/fixtures/css/utf8_good_declaring_charset.css +7 -0
  107. data/test/fixtures/css/utf8_using_star.css +5 -0
  108. data/test/fixtures/html/1-1.html +120 -0
  109. data/test/fixtures/html/1-2.html +120 -0
  110. data/test/fixtures/html/cms.html +373 -0
  111. data/test/fixtures/html/css_out_of_head.html +9 -0
  112. data/test/fixtures/html/fdev-template.html +22 -0
  113. data/test/fixtures/html/google.com.html +33 -0
  114. data/test/fixtures/html/mixed_log_levels.html +4 -0
  115. data/test/fixtures/html/mixed_types.html +13 -0
  116. data/test/fixtures/html/no_dtd.html +6 -0
  117. data/test/fixtures/html/readme.html +94 -0
  118. data/test/fixtures/html/review.board.html +163 -0
  119. data/test/fixtures/html/syntax_err.html +3 -0
  120. data/test/fixtures/html/train/detail/345/233/276/346/226/207/347/273/223/345/220/210.html +208 -0
  121. data/test/fixtures/html/train/detail/347/232/204Flash.html +212 -0
  122. data/test/fixtures/html/train/detail/347/232/204Vedio.html +212 -0
  123. data/test/fixtures/html/train/index.html +37 -0
  124. data/test/fixtures/html/train/test.html +1 -0
  125. data/test/fixtures/html/train//344/277/256/346/224/271/344/270/200/347/272/247/345/210/206/347/261/273.html +112 -0
  126. data/test/fixtures/html/train//344/277/256/346/224/271/345/255/220/345/210/206/347/261/273.html +108 -0
  127. data/test/fixtures/html/train//344/277/256/346/224/271/350/257/276/347/250/213.html +195 -0
  128. data/test/fixtures/html/train//345/215/232/345/256/242/350/256/276/347/275/256.html +142 -0
  129. data/test/fixtures/html/train//346/265/217/350/247/210/350/256/260/345/275/225.html +191 -0
  130. data/test/fixtures/html/train//346/267/273/345/212/240/344/270/200/347/272/247/345/210/206/347/261/273.html +113 -0
  131. data/test/fixtures/html/train//346/267/273/345/212/240/345/255/220/345/210/206/347/261/273.html +112 -0
  132. data/test/fixtures/html/train//346/267/273/345/212/240/350/257/276/347/250/213.html +195 -0
  133. data/test/fixtures/html/train//347/231/273/345/275/225.html +20 -0
  134. data/test/fixtures/html/train//347/256/241/347/220/206/345/210/206/347/261/273.html +210 -0
  135. data/test/fixtures/html/train//347/256/241/347/220/206/345/217/215/351/246/210.html +222 -0
  136. data/test/fixtures/html/train//347/256/241/347/220/206/350/257/276/347/250/213.html +284 -0
  137. data/test/fixtures/html/train//347/256/241/347/220/206/350/264/246/346/210/267.html +107 -0
  138. data/test/fixtures/html/train//347/275/221/344/270/212/345/237/271/350/256/255home/351/241/265.html +354 -0
  139. data/test/fixtures/html/train//347/275/221/345/225/206/345/237/271/350/256/255list/351/241/265.html +255 -0
  140. data/test/fixtures/html/train//350/256/276/347/275/256/351/246/226/351/241/265/346/216/250/350/215/220.html +168 -0
  141. data/test/fixtures/html/train//350/257/264/346/230/216.txt +3 -0
  142. data/test/fixtures/html/train//351/246/226/351/241/265/345/271/277/345/221/212/350/256/276/347/275/256.html +297 -0
  143. data/test/fixtures/html/unescaped.html +2 -0
  144. data/test/fixtures/html/view.vm +916 -0
  145. data/test/fixtures/js/jquery-1.7.js +9300 -0
  146. data/test/fixtures/js/scope-test.js +22 -0
  147. data/test/helper.rb +41 -0
  148. data/test/html/mixed_type_test.rb +35 -0
  149. data/test/html/parser/parse_comment_test.rb +47 -0
  150. data/test/html/parser/parse_dtd_test.rb +46 -0
  151. data/test/html/parser/parse_script_tag_test.rb +55 -0
  152. data/test/html/parser/parse_with_auto_close_tag_test.rb +41 -0
  153. data/test/html/parser/parse_with_diff_case_test.rb +38 -0
  154. data/test/html/parser/parse_with_emtpy_test.rb +22 -0
  155. data/test/html/parser/parse_with_multi_children_test.rb +27 -0
  156. data/test/html/parser/parse_with_multi_line_test.rb +41 -0
  157. data/test/html/parser/parse_with_prop_test.rb +88 -0
  158. data/test/html/parser/parse_with_script_tag_test.rb +26 -0
  159. data/test/html/parser/parse_with_selfclosing_test.rb +39 -0
  160. data/test/html/parser/parse_with_simple_tag_test.rb +44 -0
  161. data/test/html/parser/parse_with_simple_tree_test.rb +40 -0
  162. data/test/html/parser/parse_with_style_tag_test.rb +22 -0
  163. data/test/html/parser/parse_with_text_test.rb +45 -0
  164. data/test/html/parser_test.rb +52 -0
  165. data/test/html/query_test.rb +52 -0
  166. data/test/html/rule/check_block_level_element_test.rb +52 -0
  167. data/test/html/rule/check_button_test.rb +45 -0
  168. data/test/html/rule/check_class_count_test.rb +36 -0
  169. data/test/html/rule/check_css_in_head_test.rb +53 -0
  170. data/test/html/rule/check_dtd_test.rb +46 -0
  171. data/test/html/rule/check_form_element_name_test.rb +49 -0
  172. data/test/html/rule/check_head_contain_meta_and_title_test.rb +52 -0
  173. data/test/html/rule/check_html_template_test.rb +103 -0
  174. data/test/html/rule/check_hyperlink_with_target_test.rb +40 -0
  175. data/test/html/rule/check_hyperlink_with_title_test.rb +43 -0
  176. data/test/html/rule/check_id_n_class_downcase_test.rb +40 -0
  177. data/test/html/rule/check_img_with_alt_prop_test.rb +33 -0
  178. data/test/html/rule/check_no_import_css_test.rb +36 -0
  179. data/test/html/rule/check_prop_have_value_test.rb +32 -0
  180. data/test/html/rule/check_prop_seperator_test.rb +32 -0
  181. data/test/html/rule/check_style_prop_test.rb +30 -0
  182. data/test/html/rule/check_tag_closed_test.rb +59 -0
  183. data/test/html/rule/check_tag_downcase_test.rb +51 -0
  184. data/test/html/rule/check_unescape_char_test.rb +35 -0
  185. data/test/html/rule/check_unique_import_test.rb +56 -0
  186. data/test/html/rule_test.rb +62 -0
  187. data/test/js/expr/expr.rb +57 -0
  188. data/test/js/expr/left_hand.rb +25 -0
  189. data/test/js/expr/operate.rb +145 -0
  190. data/test/js/expr/primary.rb +89 -0
  191. data/test/js/parser_test.rb +98 -0
  192. data/test/js/rule/alert_check_test.rb +37 -0
  193. data/test/js/rule/all_test.rb +23 -0
  194. data/test/js/rule/base_test.rb +34 -0
  195. data/test/js/rule/file_checker_test.rb +131 -0
  196. data/test/js/rule/jq_check_test.rb +90 -0
  197. data/test/js/rule/nest_try_catch_test.rb +71 -0
  198. data/test/js/rule/new_object_and_new_array_test.rb +38 -0
  199. data/test/js/rule/no_eval_test.rb +34 -0
  200. data/test/js/rule/no_global_test.rb +88 -0
  201. data/test/js/rule/private_method_check_test.rb +58 -0
  202. data/test/js/rule/semicolon_test.rb +63 -0
  203. data/test/js/rule/stat_if_with_brace_test.rb +68 -0
  204. data/test/js/rule/stat_if_with_muti_else_test.rb +68 -0
  205. data/test/js/rule/use_strict_equal_test.rb +44 -0
  206. data/test/js/rule_test.rb +47 -0
  207. data/test/js/stat/if.rb +26 -0
  208. data/test/js/stat/iter.rb +115 -0
  209. data/test/js/stat/stat.rb +91 -0
  210. data/test/js/stat/switch.rb +37 -0
  211. data/test/js/stat/try.rb +32 -0
  212. data/test/js/stat/var.rb +38 -0
  213. data/test/parser_visitable_test.rb +102 -0
  214. data/test/position_info_test.rb +66 -0
  215. data/test/rule_dsl/dsl_basic_test.rb +91 -0
  216. data/test/rule_dsl/importing_test.rb +48 -0
  217. data/test/runner/log_level_test.rb +58 -0
  218. metadata +317 -0
data/lib/html/query.rb ADDED
@@ -0,0 +1,96 @@
1
+ require 'strscan'
2
+
3
+ module XRay
4
+ module HTML
5
+
6
+ class Element
7
+
8
+ WORD = /[A-Za-z][-_\w]*/
9
+ CLASS = %r(\.#{WORD})
10
+ ID = %r(##{WORD})
11
+ PROP_PAIR = %r(\s*,?\s*#{WORD}(==[^\s,])?)
12
+ PROP = %r(\[#{WORD}.*?\])
13
+
14
+ ###
15
+ # This method implemented CSS selector for
16
+ # HTML (like Sizzle) very simply. It is not
17
+ # fully supported CSS selector.
18
+ #
19
+ # TODO: support full CSS3 selector
20
+ ###
21
+ def match?(str)
22
+ return false if is_a?(TextElement)
23
+ obj = query_obj(str)
24
+ tag = obj[:tag]
25
+ cls = obj[:classes]
26
+ props = obj[:properties]
27
+ unless tag.nil? or tag_name_equal? tag
28
+ return false
29
+ end
30
+ classes = prop_value(:class)
31
+ cls.each { |c| return false unless classes.include? c }
32
+ props.each do |n, v|
33
+ if v.nil?
34
+ return false unless has_prop? n
35
+ else
36
+ return false unless prop_value(n) == v
37
+ end
38
+ end
39
+ true
40
+ end
41
+
42
+ alias_method :===, :match?
43
+
44
+ private
45
+ def query_obj(str)
46
+ classes = []
47
+ props = {}
48
+ sc = StringScanner.new(str)
49
+ tag = sc.scan WORD
50
+ if sc.check ID
51
+ sc.skip /#/
52
+ id = sc.scan WORD
53
+ end
54
+ while sc.check CLASS
55
+ sc.skip /\./
56
+ classes << (sc.scan WORD)
57
+ end
58
+ if sc.check PROP
59
+ sc.skip /\[/
60
+ while sc.check PROP_PAIR
61
+ sc.skip /\s*/
62
+ sc.skip /,/
63
+ sc.skip /\s*/
64
+ prop = sc.scan WORD
65
+ sc.skip /\==/
66
+ sc.skip /['"]/
67
+ value = sc.scan /[^\]]+/
68
+ props[prop] = value
69
+ end
70
+ sc.skip /\]/
71
+ end
72
+ props["id"] = id if id
73
+ {:tag => tag, :classes => classes, :properties => props }
74
+ end
75
+
76
+ public
77
+ def query( selector, &block )
78
+ ret = []
79
+ if match?(selector)
80
+ ret << self
81
+ yield self if block_given?
82
+ end
83
+
84
+ children && children.each do |node|
85
+ ret += node.query(selector, &block)
86
+ end
87
+
88
+ ret
89
+ end
90
+
91
+ alias_method :*, :query
92
+
93
+ end
94
+
95
+ end
96
+ end
@@ -0,0 +1,80 @@
1
+ # encoding: utf-8
2
+ require_relative '../../rule'
3
+ require_relative '../struct'
4
+ require_relative '../query'
5
+ require_relative '../../context'
6
+
7
+ module XRay
8
+ module HTML
9
+ module Rule
10
+
11
+ class CheckTagRule
12
+
13
+ include ::XRay::Rule, Context
14
+
15
+ attr_reader :imported_scripts, :imported_css
16
+
17
+ def initialize(opt=nil)
18
+ @imported_scripts, @imported_css = [], []
19
+ end
20
+
21
+ def script_used?( src )
22
+ @imported_scripts.include? src
23
+ end
24
+
25
+ def style_used?( src )
26
+ @imported_css.include? src
27
+ end
28
+
29
+ def visit_doc(html)
30
+ check_html_doc html
31
+ end
32
+
33
+ def visit_dtd(dtd)
34
+ @have_dtd = true
35
+ check_html_dtd dtd
36
+ end
37
+
38
+ def visit_tag(tag)
39
+ results = check_html_tag tag
40
+ record_script(tag) or record_style(tag)
41
+ results
42
+ end
43
+
44
+ def visit_text(tag)
45
+ check_html_text tag
46
+ end
47
+
48
+ def visit_property(prop)
49
+ check_html_property prop
50
+ end
51
+
52
+ def record_script( tag )
53
+ if tag.tag_name_equal? 'script'
54
+ src = tag.prop_value(:src).to_s
55
+ unless src.empty? or script_used? src
56
+ @imported_scripts << src
57
+ end
58
+ end
59
+ end
60
+
61
+ def record_style( tag )
62
+ if tag.tag_name_equal? 'link' and tag.prop_value(:rel) =~ /stylesheet/i
63
+ src = tag.prop_value(:href).to_s
64
+ unless src.empty? or style_used? src
65
+ @imported_css << src
66
+ end
67
+ end
68
+ end
69
+
70
+ def has_dtd?
71
+ @have_dtd
72
+ end
73
+
74
+ alias_method :have_dtd?, :has_dtd?
75
+
76
+ end
77
+
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,291 @@
1
+ require_relative '../node'
2
+
3
+ module XRay
4
+ module HTML
5
+
6
+ Node = XRay::Node
7
+ AUTO_CLOSE_TAGS = %w(area base basefont br col frame hr img input link meta param)
8
+ INLINE_ELEMENTS = %w(a br label abbr legend address link
9
+ area mark audio meter bm nav cite optgroup
10
+ code option del q details small dfn select
11
+ command source datalist span em strong
12
+ font sub i summary iframe sup img tbody
13
+ input td ins time kbd var)
14
+
15
+ class Element < Node
16
+
17
+ attr_reader :tag, :props, :children
18
+ attr_accessor :parent, :close_type, :ending, :scopes
19
+
20
+ def initialize(tag, props=[], children=[], close_type=:after, ending=nil)
21
+ @tag, @props, @children, @close_type, @ending = tag, to_props(props), Array.[](children).flatten || [], close_type, ending
22
+ @position = @tag.position.dup if tag.is_a?(Node) and tag.position
23
+ @children.each { |el| el.parent = self }
24
+ @scopes = []
25
+ end
26
+
27
+ def has_scope?
28
+ !(parent.nil? and @scopes.empty?)
29
+ end
30
+
31
+ def in_scope?(scp)
32
+ if parent
33
+ parent.tag_name_equal? scp
34
+ else
35
+ scopes.include?(scp)
36
+ end
37
+ end
38
+
39
+ def text
40
+ children.inject("") do |s,child|
41
+ s + child.text
42
+ end
43
+ end
44
+
45
+ def tag_name
46
+ @tag.is_a?(Node) ? @tag.text : @tag.to_s
47
+ end
48
+
49
+ def tag_name_equal?(name)
50
+ tag_name.downcase == name.downcase
51
+ end
52
+
53
+ def inner_html
54
+ @children.inject('') { |s,c| s + c.outer_html }
55
+ end
56
+
57
+ def outer_html
58
+ if children.empty?
59
+ "<#{@tag}#{prop_text} />"
60
+ else
61
+ cls = ending || "</#{@tag}>"
62
+ "<#{@tag}#{prop_text}>#{inner_html}#{cls}"
63
+ end
64
+ end
65
+
66
+ def inner_text
67
+ @children.inject('') { |s,c| s + c.inner_text }
68
+ end
69
+
70
+ alias_method :text, :inner_text
71
+
72
+ def prop_text
73
+ props.inject('') { |s, p| s << " " << p.to_s }
74
+ end
75
+
76
+ def ==(other)
77
+ other.is_a?(Element) and tag_name == tag_name.to_s && prop_text == other.prop_text && inner_html == other.inner_html
78
+ end
79
+
80
+ def has_prop?(name)
81
+ @props.any? { |p| p.name_equal? name }
82
+ end
83
+
84
+ def prop(name)
85
+ @props.find { |p| p.name_equal? name }
86
+ end
87
+
88
+ def prop_value(*arg)
89
+ name, value = *arg
90
+ if arg.size > 1
91
+ unless @props.find { |p| p.value = value if p.name_equal? name }
92
+ @props << Property.new(name, value)
93
+ end
94
+ nil
95
+ else
96
+ p = prop(name)
97
+ p.value if p
98
+ end
99
+ end
100
+
101
+ def to_s
102
+ "[HTML: #{outer_html}]"
103
+ end
104
+
105
+ def inline?
106
+ INLINE_ELEMENTS.include? tag_name.downcase
107
+ end
108
+
109
+ def auto_close?
110
+ AUTO_CLOSE_TAGS.include? tag_name.downcase
111
+ end
112
+
113
+ def closed?
114
+ @close_type != :none
115
+ end
116
+
117
+ def self_closed?
118
+ @close_type == :self
119
+ end
120
+
121
+ def each(&block)
122
+ children.each {|node| yield node }
123
+ end
124
+
125
+ def empty?
126
+ false
127
+ end
128
+
129
+ protected
130
+ def parse(text)
131
+ @outer_html = text
132
+ end
133
+
134
+ private
135
+
136
+ def to_props(src)
137
+ case src
138
+ when Array
139
+ src
140
+ when Hash
141
+ src.map do |n, v|
142
+ Property.new(n, v)
143
+ end
144
+ else
145
+ []
146
+ end
147
+ end
148
+
149
+ end
150
+
151
+
152
+ class Document < Element
153
+ def initialize( children=[] )
154
+ super(nil, {}, children || [])
155
+ @position = Position.new(0,0,0)
156
+ end
157
+
158
+ def ==(other)
159
+ if other.is_a?(Array)
160
+ return children == other
161
+ elsif other.is_a?(Document)
162
+ return children == other.children
163
+ else
164
+ super
165
+ end
166
+ end
167
+
168
+ def empty?
169
+ children.empty?
170
+ end
171
+
172
+ end
173
+
174
+ class TextElement < Element
175
+
176
+ def initialize(text="")
177
+ super(nil)
178
+ @text = text.to_s
179
+ end
180
+
181
+ def text; @text; end
182
+ def tag_name; nil; end
183
+
184
+ alias_method :inner_text, :text
185
+ alias_method :inner_html, :text
186
+ alias_method :outer_html, :text
187
+
188
+ def ==(other)
189
+ text == other.text
190
+ end
191
+
192
+ def tag_name_equal?(name); false; end
193
+ def inline?; true; end
194
+ def auto_close? ; false; end
195
+ def closed? ; true; end
196
+
197
+ def to_s
198
+ "[TEXT: #{text}]"
199
+ end
200
+
201
+ end
202
+
203
+
204
+ class CommentElement < Element
205
+ def initialize(text)
206
+ super(nil)
207
+ @text = text.to_s
208
+ end
209
+
210
+ def content; @text; end
211
+ def tag_name; nil; end
212
+
213
+ alias_method :inner_text, :text
214
+ alias_method :inner_html, :text
215
+
216
+ def outer_html
217
+ "<!--#{text}-->"
218
+ end
219
+
220
+ def ==(other)
221
+ text == other.text
222
+ end
223
+
224
+ def tag_name_equal?(name); false; end
225
+ def inline? ; true; end
226
+ def auto_close? ; false; end
227
+ def closed? ; true; end
228
+
229
+ def to_s
230
+ "[Comment: #{text}]"
231
+ end
232
+
233
+ end
234
+
235
+ class DTDElement < Element
236
+
237
+ attr_accessor :type
238
+
239
+ def initialize(type, pre="DOCTYPE", pos=nil)
240
+ @type, @pre = type, pre
241
+ @position = pos || Position.new(0,0,0)
242
+ end
243
+
244
+ def to_s
245
+ "<!#{@pre} #{type}>"
246
+ end
247
+
248
+ def inline?
249
+ true
250
+ end
251
+
252
+ alias_method :outer_html, :to_s
253
+
254
+ def ==(other)
255
+ other.is_a?(DTDElement) and other.type == type
256
+ end
257
+
258
+ def =~(other)
259
+ to_s =~ other
260
+ end
261
+
262
+ end
263
+
264
+ class Property < Node
265
+
266
+ attr_reader :name, :sep
267
+ attr_accessor :value
268
+
269
+ def initialize(name, value, sep='"')
270
+ @name, @value, @sep = name, value, sep
271
+ end
272
+
273
+ def position; name.position; end
274
+
275
+ def name_equal?(text)
276
+ @name.to_s.downcase == text.to_s.downcase
277
+ end
278
+
279
+ def to_s
280
+ if value.nil?
281
+ name.to_s
282
+ else
283
+ "#{name}=#{sep}#{value}#{sep}"
284
+ end
285
+ end
286
+
287
+ end
288
+
289
+
290
+ end
291
+ end
@@ -0,0 +1,66 @@
1
+ require_relative 'primary'
2
+ require_relative 'left_hand'
3
+ require_relative 'operate'
4
+
5
+
6
+ module XRay
7
+ module JS
8
+ module Expr
9
+
10
+ module Expr
11
+ include Primary, LeftHand, Operate
12
+
13
+ def parse_expression
14
+ log 'parse expression'
15
+ parse_expr_with_operate :parse_expr_assignment, /,/
16
+ end
17
+
18
+ def parse_expr_assignment
19
+ log 'parse expr assignment'
20
+
21
+ expr = parse_expr_condition
22
+
23
+ r = /=|\*=|\/=|%=|\+=|-=|<<=|>>=|>>>=|&=|\^=|\|=/
24
+ if expr.left_hand? && check(r)
25
+ op = scan r
26
+ expr = create_element Expression, op.text, expr, parse_expr_assignment
27
+ end
28
+ expr
29
+ end
30
+
31
+ def parse_expr_condition
32
+ log 'parse expr condition'
33
+ expr = parse_expr_logical_or
34
+ if check /\?/
35
+ skip /\?/
36
+ left = parse_expr_assignment
37
+ skip /:/
38
+ right = parse_expr_assignment
39
+
40
+ expr = create_element ConditionExpression, expr, left, right
41
+ end
42
+ expr
43
+ end
44
+
45
+ protected
46
+
47
+ def parse_expr_with_operate(left, pattern = nil, &block)
48
+ block = block || lambda {
49
+ if check pattern
50
+ op = scan pattern
51
+ [op.text, self.send(left)]
52
+ end
53
+ }
54
+
55
+ expr = self.send left
56
+ while (ret = block.call)
57
+ expr = create_element Expression, ret[0], expr, ret[1]
58
+ end
59
+ expr
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,63 @@
1
+ module XRay
2
+ module JS
3
+ module Expr
4
+
5
+ module LeftHand
6
+
7
+ def parse_expr_lefthand
8
+ log 'parse expr lefthand'
9
+ expr = check(/new\b/) ? parse_expr_new : parse_expr_member
10
+ expr.left_hand = true
11
+ expr
12
+ end
13
+
14
+ def parse_expr_new
15
+ log 'parse expr new'
16
+
17
+ pos = skip /new/
18
+ expr = check(/new\b/) ? parse_expr_new : parse_expr_member
19
+ args = check(/\(/) ? parse_arguments_list : nil
20
+
21
+ create_element Expression, 'new', expr, args, pos
22
+ end
23
+
24
+ def parse_expr_member
25
+ log 'parse expr member'
26
+ parse_expr_with_operate(:parse_expr_member_left) do
27
+ if check /[.]/
28
+ skip /[.]/
29
+ ['.', parse_expr_identifier]
30
+ elsif check /[\[]/
31
+ skip /\[/
32
+ expr = parse_expression
33
+ skip /\]/
34
+ ['[', expr]
35
+ elsif check /\(/
36
+ ['(', parse_arguments_list]
37
+ end
38
+ end
39
+ end
40
+
41
+ protected
42
+
43
+ def parse_expr_member_left
44
+ if check /function\b/
45
+ create_element FunctionExpression, parse_function_declaration(true)
46
+ else
47
+ parse_expr_primary
48
+ end
49
+ end
50
+
51
+
52
+ def parse_arguments_list
53
+ log 'parse arguments list'
54
+ skip /\(/
55
+ params = batch :parse_expr_assignment, /\)/, /,/
56
+ skip /\)/
57
+ Elements.new params
58
+ end
59
+
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,92 @@
1
+ module XRay
2
+ module JS
3
+ module Expr
4
+
5
+ module Operate
6
+
7
+ def parse_expr_postfix
8
+ log 'parse expr postfix'
9
+ expr = parse_expr_lefthand
10
+ if check(/[ \t]*(?:\+\+|--)/, true)
11
+ op = scan /\+\+|--/
12
+ expr = create_element Expression, op.text, expr
13
+ end
14
+ expr
15
+ end
16
+
17
+ def parse_expr_unary
18
+ log 'parse expr unary'
19
+ r = /delete|void|typeof|\+\+|--|\+|-|~|!/
20
+ if check r
21
+ op = scan r
22
+ create_element Expression, op.text, nil, parse_expr_unary
23
+ else
24
+ parse_expr_postfix
25
+ end
26
+ end
27
+
28
+ def parse_expr_mul
29
+ log 'parse expr mul'
30
+ parse_expr_with_operate :parse_expr_unary, /(?:\*|\/|%)(?!=)/
31
+ end
32
+
33
+ def parse_expr_add
34
+ log 'parse expr add'
35
+ parse_expr_with_operate :parse_expr_mul, /(?:\+|-)(?!=)/
36
+ end
37
+
38
+ def parse_expr_shift
39
+ log 'parse expr shift'
40
+ parse_expr_with_operate :parse_expr_add, /(?:<<|>>>|>>)(?!=)/
41
+ end
42
+
43
+ def parse_expr_relation
44
+ not_in = expr_operate_not_in?
45
+ log "parse expr relational#{not_in ? '(notin)' : ''}"
46
+ pattern = not_in ? (/>=|<=|>|<|\binstanceof\b/) : (/>=|<=|>|<|\binstanceof\b|\bin\b/)
47
+ parse_expr_with_operate :parse_expr_shift, pattern
48
+ end
49
+
50
+ def parse_expr_equal
51
+ log 'parse expr equal'
52
+ parse_expr_with_operate :parse_expr_relation, /===|!==|==|!=/
53
+ end
54
+
55
+ def parse_expr_bit_and
56
+ log 'parse expr bit and'
57
+ parse_expr_with_operate :parse_expr_equal, /&(?![&=])/
58
+ end
59
+
60
+ def parse_expr_bit_xor
61
+ log 'parse expr bit xor'
62
+ parse_expr_with_operate :parse_expr_bit_and, /\^(?!=)/
63
+ end
64
+
65
+ def parse_expr_bit_or
66
+ log 'parse expr bit or'
67
+ parse_expr_with_operate :parse_expr_bit_xor, /\|(?![|=])/
68
+ end
69
+
70
+ def parse_expr_logical_and
71
+ log 'parse expr logical and'
72
+ parse_expr_with_operate :parse_expr_bit_or, /&&/
73
+ end
74
+
75
+ def parse_expr_logical_or
76
+ log 'parse expr logical or'
77
+ parse_expr_with_operate :parse_expr_logical_and, /\|\|/
78
+ end
79
+
80
+ def expr_operate_not_in?
81
+ @operate_not_in || false
82
+ end
83
+
84
+ def expr_operate_not_in=(value)
85
+ @operate_not_in = !!value
86
+ end
87
+
88
+ end
89
+
90
+ end
91
+ end
92
+ end