mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
729 lines
21 KiB
Text
729 lines
21 KiB
Text
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
|