summaryrefslogtreecommitdiff
path: root/test/racc/assets/csspool.y
diff options
context:
space:
mode:
Diffstat (limited to 'test/racc/assets/csspool.y')
-rw-r--r--test/racc/assets/csspool.y729
1 files changed, 729 insertions, 0 deletions
diff --git a/test/racc/assets/csspool.y b/test/racc/assets/csspool.y
new file mode 100644
index 0000000000..3d6af25d85
--- /dev/null
+++ b/test/racc/assets/csspool.y
@@ -0,0 +1,729 @@
+class CSSPool::CSS::Parser
+
+token CHARSET_SYM IMPORT_SYM STRING SEMI IDENT S COMMA LBRACE RBRACE STAR HASH
+token LSQUARE RSQUARE EQUAL INCLUDES DASHMATCH LPAREN RPAREN FUNCTION GREATER PLUS
+token SLASH NUMBER MINUS LENGTH PERCENTAGE ANGLE TIME FREQ URI
+token IMPORTANT_SYM MEDIA_SYM NOT ONLY AND NTH_PSEUDO_CLASS
+token DOCUMENT_QUERY_SYM FUNCTION_NO_QUOTE
+token TILDE
+token PREFIXMATCH SUFFIXMATCH SUBSTRINGMATCH
+token NOT_PSEUDO_CLASS
+token KEYFRAMES_SYM
+token MATCHES_PSEUDO_CLASS
+token NAMESPACE_SYM
+token MOZ_PSEUDO_ELEMENT
+token RESOLUTION
+token COLON
+token SUPPORTS_SYM
+token OR
+token VARIABLE_NAME
+token CALC_SYM
+token FONTFACE_SYM
+token UNICODE_RANGE
+token RATIO
+
+rule
+ document
+ : { @handler.start_document }
+ stylesheet
+ { @handler.end_document }
+ ;
+ stylesheet
+ : charset stylesheet
+ | import stylesheet
+ | namespace stylesheet
+ | charset
+ | import
+ | namespace
+ | body
+ |
+ ;
+ charset
+ : CHARSET_SYM STRING SEMI { @handler.charset interpret_string(val[1]), {} }
+ ;
+ import
+ : IMPORT_SYM import_location medium SEMI {
+ @handler.import_style val[2], val[1]
+ }
+ | IMPORT_SYM import_location SEMI {
+ @handler.import_style [], val[1]
+ }
+ ;
+ import_location
+ : import_location S
+ | STRING { result = Terms::String.new interpret_string val.first }
+ | URI { result = Terms::URI.new interpret_uri val.first }
+ ;
+ namespace
+ : NAMESPACE_SYM ident import_location SEMI {
+ @handler.namespace val[1], val[2]
+ }
+ | NAMESPACE_SYM import_location SEMI {
+ @handler.namespace nil, val[1]
+ }
+ ;
+ medium
+ : medium COMMA IDENT {
+ result = val[0] << MediaType.new(val[2])
+ }
+ | IDENT {
+ result = [MediaType.new(val[0])]
+ }
+ ;
+ media_query_list
+ : media_query { result = MediaQueryList.new([ val[0] ]) }
+ | media_query_list COMMA media_query { result = val[0] << val[2] }
+ | { result = MediaQueryList.new }
+ ;
+ media_query
+ : optional_only_or_not media_type optional_and_exprs { result = MediaQuery.new(val[0], val[1], val[2]) }
+ | media_expr optional_and_exprs { result = MediaQuery.new(nil, val[0], val[1]) }
+ ;
+ optional_only_or_not
+ : ONLY { result = :only }
+ | NOT { result = :not }
+ | { result = nil }
+ ;
+ media_type
+ : IDENT { result = MediaType.new(val[0]) }
+ ;
+ media_expr
+ : LPAREN optional_space IDENT optional_space RPAREN { result = MediaType.new(val[2]) }
+ | LPAREN optional_space IDENT optional_space COLON optional_space expr RPAREN { result = MediaFeature.new(val[2], val[6][0]) }
+ ;
+ optional_space
+ : S { result = val[0] }
+ | { result = nil }
+ ;
+ optional_and_exprs
+ : optional_and_exprs AND media_expr { result = val[0] << val[2] }
+ | { result = [] }
+ ;
+ resolution
+ : RESOLUTION {
+ unit = val.first.gsub(/[\s\d.]/, '')
+ number = numeric(val.first)
+ result = Terms::Resolution.new(number, unit)
+ }
+ ;
+ body
+ : ruleset body
+ | conditional_rule body
+ | keyframes_rule body
+ | fontface_rule body
+ | ruleset
+ | conditional_rule
+ | keyframes_rule
+ | fontface_rule
+ ;
+ conditional_rule
+ : media
+ | document_query
+ | supports
+ ;
+ body_in_media
+ : body
+ | empty_ruleset
+ ;
+ media
+ : start_media body_in_media RBRACE { @handler.end_media val.first }
+ ;
+ start_media
+ : MEDIA_SYM media_query_list LBRACE {
+ result = val[1]
+ @handler.start_media result
+ }
+ ;
+ document_query
+ : start_document_query body RBRACE { @handler.end_document_query(before_pos(val), after_pos(val)) }
+ | start_document_query RBRACE { @handler.end_document_query(before_pos(val), after_pos(val)) }
+ ;
+ start_document_query
+ : start_document_query_pos url_match_fns LBRACE {
+ @handler.start_document_query(val[1], after_pos(val))
+ }
+ ;
+ start_document_query_pos
+ : DOCUMENT_QUERY_SYM {
+ @handler.node_start_pos = before_pos(val)
+ }
+ ;
+ url_match_fns
+ : url_match_fn COMMA url_match_fns {
+ result = [val[0], val[2]].flatten
+ }
+ | url_match_fn {
+ result = val
+ }
+ ;
+ url_match_fn
+ : function_no_quote
+ | function
+ | uri
+ ;
+ supports
+ : start_supports body RBRACE { @handler.end_supports }
+ | start_supports RBRACE { @handler.end_supports }
+ ;
+ start_supports
+ : SUPPORTS_SYM supports_condition_root LBRACE {
+ @handler.start_supports val[1]
+ }
+ ;
+ supports_condition_root
+ : supports_negation { result = val.join('') }
+ | supports_conjunction_or_disjunction { result = val.join('') }
+ | supports_condition_in_parens { result = val.join('') }
+ ;
+ supports_condition
+ : supports_negation { result = val.join('') }
+ | supports_conjunction_or_disjunction { result = val.join('') }
+ | supports_condition_in_parens { result = val.join('') }
+ ;
+ supports_condition_in_parens
+ : LPAREN supports_condition RPAREN { result = val.join('') }
+ | supports_declaration_condition { result = val.join('') }
+ ;
+ supports_negation
+ : NOT supports_condition_in_parens { result = val.join('') }
+ ;
+ supports_conjunction_or_disjunction
+ : supports_conjunction
+ | supports_disjunction
+ ;
+ supports_conjunction
+ : supports_condition_in_parens AND supports_condition_in_parens { result = val.join('') }
+ | supports_conjunction_or_disjunction AND supports_condition_in_parens { result = val.join('') }
+ ;
+ supports_disjunction
+ : supports_condition_in_parens OR supports_condition_in_parens { result = val.join('') }
+ | supports_conjunction_or_disjunction OR supports_condition_in_parens { result = val.join('') }
+ ;
+ supports_declaration_condition
+ : LPAREN declaration_internal RPAREN { result = val.join('') }
+ | LPAREN S declaration_internal RPAREN { result = val.join('') }
+ ;
+ keyframes_rule
+ : start_keyframes_rule keyframes_blocks RBRACE
+ | start_keyframes_rule RBRACE
+ ;
+ start_keyframes_rule
+ : KEYFRAMES_SYM IDENT LBRACE {
+ @handler.start_keyframes_rule val[1]
+ }
+ ;
+ keyframes_blocks
+ : keyframes_block keyframes_blocks
+ | keyframes_block
+ ;
+ keyframes_block
+ : start_keyframes_block declarations RBRACE { @handler.end_keyframes_block }
+ | start_keyframes_block RBRACE { @handler.end_keyframes_block }
+ ;
+ start_keyframes_block
+ : keyframes_selectors LBRACE {
+ @handler.start_keyframes_block val[0]
+ }
+ ;
+ keyframes_selectors
+ | keyframes_selector COMMA keyframes_selectors {
+ result = val[0] + ', ' + val[2]
+ }
+ | keyframes_selector
+ ;
+ keyframes_selector
+ : IDENT
+ | PERCENTAGE { result = val[0].strip }
+ ;
+ fontface_rule
+ : start_fontface_rule declarations RBRACE { @handler.end_fontface_rule }
+ | start_fontface_rule RBRACE { @handler.end_fontface_rule }
+ ;
+ start_fontface_rule
+ : FONTFACE_SYM LBRACE {
+ @handler.start_fontface_rule
+ }
+ ;
+ ruleset
+ : start_selector declarations RBRACE {
+ @handler.end_selector val.first
+ }
+ | start_selector RBRACE {
+ @handler.end_selector val.first
+ }
+ ;
+ empty_ruleset
+ : optional_space {
+ start = @handler.start_selector([])
+ @handler.end_selector(start)
+ }
+ ;
+ start_selector
+ : S start_selector { result = val.last }
+ | selectors LBRACE {
+ @handler.start_selector val.first
+ }
+ ;
+ selectors
+ : selector COMMA selectors
+ {
+ sel = Selector.new(val.first, {})
+ result = [sel].concat(val[2])
+ }
+ | selector
+ {
+ result = [Selector.new(val.first, {})]
+ }
+ ;
+ selector
+ : simple_selector combinator selector
+ {
+ val.flatten!
+ val[2].combinator = val.delete_at 1
+ result = val
+ }
+ | simple_selector
+ ;
+ combinator
+ : S { result = :s }
+ | GREATER { result = :> }
+ | PLUS { result = :+ }
+ | TILDE { result = :~ }
+ ;
+ simple_selector
+ : element_name hcap {
+ selector = val.first
+ selector.additional_selectors = val.last
+ result = [selector]
+ }
+ | element_name { result = val }
+ | hcap
+ {
+ ss = Selectors::Simple.new nil, nil
+ ss.additional_selectors = val.flatten
+ result = [ss]
+ }
+ ;
+ simple_selectors
+ : simple_selector COMMA simple_selectors { result = [val[0], val[2]].flatten }
+ | simple_selector
+ ;
+ ident_with_namespace
+ : IDENT { result = [interpret_identifier(val[0]), nil] }
+ | IDENT '|' IDENT { result = [interpret_identifier(val[2]), interpret_identifier(val[0])] }
+ | '|' IDENT { result = [interpret_identifier(val[1]), nil] }
+ | STAR '|' IDENT { result = [interpret_identifier(val[2]), '*'] }
+ ;
+ element_name
+ : ident_with_namespace { result = Selectors::Type.new val.first[0], nil, val.first[1] }
+ | STAR { result = Selectors::Universal.new val.first }
+ | '|' STAR { result = Selectors::Universal.new val[1] }
+ | STAR '|' STAR { result = Selectors::Universal.new val[2], nil, val[0] }
+ | IDENT '|' STAR { result = Selectors::Universal.new val[2], nil, interpret_identifier(val[0]) }
+ ;
+ hcap
+ : hash { result = val }
+ | class { result = val }
+ | attrib { result = val }
+ | pseudo { result = val }
+ | hash hcap { result = val.flatten }
+ | class hcap { result = val.flatten }
+ | attrib hcap { result = val.flatten }
+ | pseudo hcap { result = val.flatten }
+ ;
+ hash
+ : HASH {
+ result = Selectors::Id.new interpret_identifier val.first.sub(/^#/, '')
+ }
+ class
+ : '.' IDENT {
+ result = Selectors::Class.new interpret_identifier val.last
+ }
+ ;
+ attrib
+ : LSQUARE ident_with_namespace EQUAL IDENT RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_identifier(val[3]),
+ Selectors::Attribute::EQUALS,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace EQUAL STRING RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_string(val[3]),
+ Selectors::Attribute::EQUALS,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace INCLUDES STRING RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_string(val[3]),
+ Selectors::Attribute::INCLUDES,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace INCLUDES IDENT RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_identifier(val[3]),
+ Selectors::Attribute::INCLUDES,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace DASHMATCH IDENT RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_identifier(val[3]),
+ Selectors::Attribute::DASHMATCH,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace DASHMATCH STRING RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_string(val[3]),
+ Selectors::Attribute::DASHMATCH,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace PREFIXMATCH IDENT RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_identifier(val[3]),
+ Selectors::Attribute::PREFIXMATCH,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace PREFIXMATCH STRING RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_string(val[3]),
+ Selectors::Attribute::PREFIXMATCH,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace SUFFIXMATCH IDENT RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_identifier(val[3]),
+ Selectors::Attribute::SUFFIXMATCH,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace SUFFIXMATCH STRING RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_string(val[3]),
+ Selectors::Attribute::SUFFIXMATCH,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace SUBSTRINGMATCH IDENT RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_identifier(val[3]),
+ Selectors::Attribute::SUBSTRINGMATCH,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace SUBSTRINGMATCH STRING RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ interpret_string(val[3]),
+ Selectors::Attribute::SUBSTRINGMATCH,
+ val[1][1]
+ )
+ }
+ | LSQUARE ident_with_namespace RSQUARE {
+ result = Selectors::Attribute.new(
+ val[1][0],
+ nil,
+ Selectors::Attribute::SET,
+ val[1][1]
+ )
+ }
+ ;
+ pseudo
+ : COLON IDENT {
+ result = Selectors::pseudo interpret_identifier(val[1])
+ }
+ | COLON COLON IDENT {
+ result = Selectors::PseudoElement.new(
+ interpret_identifier(val[2])
+ )
+ }
+ | COLON FUNCTION RPAREN {
+ result = Selectors::PseudoClass.new(
+ interpret_identifier(val[1].sub(/\($/, '')),
+ ''
+ )
+ }
+ | COLON FUNCTION IDENT RPAREN {
+ result = Selectors::PseudoClass.new(
+ interpret_identifier(val[1].sub(/\($/, '')),
+ interpret_identifier(val[2])
+ )
+ }
+ | COLON NOT_PSEUDO_CLASS simple_selector RPAREN {
+ result = Selectors::PseudoClass.new(
+ 'not',
+ val[2].first.to_s
+ )
+ }
+ | COLON NTH_PSEUDO_CLASS {
+ result = Selectors::PseudoClass.new(
+ interpret_identifier(val[1].sub(/\(.*/, '')),
+ interpret_identifier(val[1].sub(/.*\(/, '').sub(/\).*/, ''))
+ )
+ }
+ | COLON MATCHES_PSEUDO_CLASS simple_selectors RPAREN {
+ result = Selectors::PseudoClass.new(
+ val[1].split('(').first.strip,
+ val[2].join(', ')
+ )
+ }
+ | COLON MOZ_PSEUDO_ELEMENT optional_space any_number_of_idents optional_space RPAREN {
+ result = Selectors::PseudoElement.new(
+ interpret_identifier(val[1].sub(/\($/, ''))
+ )
+ }
+ | COLON COLON MOZ_PSEUDO_ELEMENT optional_space any_number_of_idents optional_space RPAREN {
+ result = Selectors::PseudoElement.new(
+ interpret_identifier(val[2].sub(/\($/, ''))
+ )
+ }
+ ;
+ any_number_of_idents
+ :
+ | multiple_idents
+ ;
+ multiple_idents
+ : IDENT
+ | IDENT COMMA multiple_idents
+ ;
+ # declarations can be separated by one *or more* semicolons. semi-colons at the start or end of a ruleset are also allowed
+ one_or_more_semis
+ : SEMI
+ | SEMI one_or_more_semis
+ ;
+ declarations
+ : declaration one_or_more_semis declarations
+ | one_or_more_semis declarations
+ | declaration one_or_more_semis
+ | declaration
+ | one_or_more_semis
+ ;
+ declaration
+ : declaration_internal { @handler.property val.first }
+ ;
+ declaration_internal
+ : property COLON expr prio
+ { result = Declaration.new(val.first, val[2], val[3]) }
+ | property COLON S expr prio
+ { result = Declaration.new(val.first, val[3], val[4]) }
+ | property S COLON expr prio
+ { result = Declaration.new(val.first, val[3], val[4]) }
+ | property S COLON S expr prio
+ { result = Declaration.new(val.first, val[4], val[5]) }
+ ;
+ prio
+ : IMPORTANT_SYM { result = true }
+ | { result = false }
+ ;
+ property
+ : IDENT { result = interpret_identifier val[0] }
+ | STAR IDENT { result = interpret_identifier val.join }
+ | VARIABLE_NAME { result = interpret_identifier val[0] }
+ ;
+ operator
+ : COMMA
+ | SLASH
+ | EQUAL
+ ;
+ expr
+ : term operator expr {
+ result = [val.first, val.last].flatten
+ val.last.first.operator = val[1]
+ }
+ | term expr { result = val.flatten }
+ | term { result = val }
+ ;
+ term
+ : ident
+ | ratio
+ | numeric
+ | string
+ | uri
+ | hexcolor
+ | calc
+ | function
+ | resolution
+ | VARIABLE_NAME
+ | uranges
+ ;
+ function
+ : function S { result = val.first }
+ | FUNCTION expr RPAREN {
+ name = interpret_identifier val.first.sub(/\($/, '')
+ if name == 'rgb'
+ result = Terms::Rgb.new(*val[1])
+ else
+ result = Terms::Function.new name, val[1]
+ end
+ }
+ | FUNCTION RPAREN {
+ name = interpret_identifier val.first.sub(/\($/, '')
+ result = Terms::Function.new name
+ }
+ ;
+ function_no_quote
+ : function_no_quote S { result = val.first }
+ | FUNCTION_NO_QUOTE {
+ parts = val.first.split('(')
+ name = interpret_identifier parts.first
+ result = Terms::Function.new(name, [Terms::String.new(interpret_string_no_quote(parts.last))])
+ }
+ ;
+ uranges
+ : UNICODE_RANGE COMMA uranges
+ | UNICODE_RANGE
+ ;
+ calc
+ : CALC_SYM calc_sum RPAREN optional_space {
+ result = Terms::Math.new(val.first.split('(').first, val[1])
+ }
+ ;
+ # plus and minus are supposed to have whitespace around them, per http://dev.w3.org/csswg/css-values/#calc-syntax, but the numbers are eating trailing whitespace, so we inject it back in
+ calc_sum
+ : calc_product
+ | calc_product PLUS calc_sum { val.insert(1, ' '); result = val.join('') }
+ | calc_product MINUS calc_sum { val.insert(1, ' '); result = val.join('') }
+ ;
+ calc_product
+ : calc_value
+ | calc_value optional_space STAR calc_value { result = val.join('') }
+ | calc_value optional_space SLASH calc_value { result = val.join('') }
+ ;
+ calc_value
+ : numeric { result = val.join('') }
+ | function { result = val.join('') } # for var() variable references
+ | LPAREN calc_sum RPAREN { result = val.join('') }
+ ;
+ hexcolor
+ : hexcolor S { result = val.first }
+ | HASH { result = Terms::Hash.new val.first.sub(/^#/, '') }
+ ;
+ uri
+ : uri S { result = val.first }
+ | URI { result = Terms::URI.new interpret_uri val.first }
+ ;
+ string
+ : string S { result = val.first }
+ | STRING { result = Terms::String.new interpret_string val.first }
+ ;
+ numeric
+ : unary_operator numeric {
+ result = val[1]
+ val[1].unary_operator = val.first
+ }
+ | NUMBER {
+ result = Terms::Number.new numeric val.first
+ }
+ | PERCENTAGE {
+ result = Terms::Number.new numeric(val.first), nil, '%'
+ }
+ | LENGTH {
+ unit = val.first.gsub(/[\s\d.]/, '')
+ result = Terms::Number.new numeric(val.first), nil, unit
+ }
+ | ANGLE {
+ unit = val.first.gsub(/[\s\d.]/, '')
+ result = Terms::Number.new numeric(val.first), nil, unit
+ }
+ | TIME {
+ unit = val.first.gsub(/[\s\d.]/, '')
+ result = Terms::Number.new numeric(val.first), nil, unit
+ }
+ | FREQ {
+ unit = val.first.gsub(/[\s\d.]/, '')
+ result = Terms::Number.new numeric(val.first), nil, unit
+ }
+ ;
+ ratio
+ : RATIO {
+ result = Terms::Ratio.new(val[0], val[1])
+ }
+ ;
+ unary_operator
+ : MINUS { result = :minus }
+ | PLUS { result = :plus }
+ ;
+ ident
+ : ident S { result = val.first }
+ | IDENT { result = Terms::Ident.new interpret_identifier val.first }
+ ;
+
+---- inner
+
+def numeric thing
+ thing = thing.gsub(/[^\d.]/, '')
+ Integer(thing) rescue Float(thing)
+end
+
+def interpret_identifier s
+ interpret_escapes s
+end
+
+def interpret_uri s
+ interpret_escapes s.match(/^url\((.*)\)$/mui)[1].strip.match(/^(['"]?)((?:\\.|.)*)\1$/mu)[2]
+end
+
+def interpret_string_no_quote s
+ interpret_escapes s.match(/^(.*)\)$/mu)[1].strip.match(/^(['"]?)((?:\\.|.)*)\1$/mu)[2]
+end
+
+def interpret_string s
+ interpret_escapes s.match(/^(['"])((?:\\.|.)*)\1$/mu)[2]
+end
+
+def interpret_escapes s
+ token_exp = /\\(?:([0-9a-fA-F]{1,6}(?:\r\n|\s)?)|(.))/mu
+ return s.gsub(token_exp) do |escape_sequence|
+ if !$1.nil?
+ code = $1.chomp.to_i 16
+ code = 0xFFFD if code > 0x10FFFF
+ next [code].pack('U')
+ end
+ next '' if $2 == "\n"
+ next $2
+ end
+end
+
+# override racc's on_error so we can have context in our error messages
+def on_error(t, val, vstack)
+ errcontext = (@ss.pre_match[-10..-1] || @ss.pre_match) +
+ @ss.matched + @ss.post_match[0..9]
+ line_number = @ss.pre_match.lines.count
+ raise ParseError, sprintf("parse error on value %s (%s) " +
+ "on line %s around \"%s\"",
+ val.inspect, token_to_str(t) || '?',
+ line_number, errcontext)
+end
+
+def before_pos(val)
+ # don't include leading whitespace
+ return current_pos - val.last.length + val.last[/\A\s*/].size
+end
+
+def after_pos(val)
+ # don't include trailing whitespace
+ return current_pos - val.last[/\s*\z/].size
+end
+
+# charpos will work with multibyte strings but is not available until ruby 2
+def current_pos
+ @ss.respond_to?('charpos') ? @ss.charpos : @ss.pos
+end