haml--haml/lib/sass/script/lexer.rb

113 lines
2.7 KiB
Ruby
Raw Normal View History

require 'strscan'
module Sass
module Script
class Lexer
OPERATORS = {
'+' => :plus,
'-' => :minus,
'*' => :times,
'/' => :div,
'%' => :mod,
'(' => :lparen,
')' => :rparen,
',' => :comma,
'and' => :and,
'or' => :or,
'not' => :not,
'==' => :eq,
'!=' => :neq,
'>=' => :gte,
'<=' => :lte,
'>' => :gt,
'<' => :lt,
}
# We'll want to match longer names first
# so that > and < don't clobber >= and <=
OP_NAMES = OPERATORS.keys.sort_by {|o| -o.size}
def initialize(str)
@scanner = StringScanner.new(str)
end
def token
return if done?
if @tok
@tok, tok = nil, @tok
return tok
end
whitespace
variable || string || number || color || bool || op || ident ||
(raise SyntaxError.new("Syntax error in '#{@scanner.string}' at '#{@scanner.rest}'."))
end
def peek
@tok ||= token
end
def done?
whitespace
@scanner.eos? && @tok.nil?
end
def rest
@scanner.rest
end
private
def whitespace
@scanner.scan(/\s*/)
end
def variable
return unless @scanner.scan(/!(\w+)/)
[:const, @scanner[1]]
end
def ident
return unless s = @scanner.scan(/(\\.|[^\s\\+\-*\/%(),=!])+/)
[:ident, s.gsub(/\\(.)/, '\1')]
end
def string
return unless @scanner.scan(/"((?:\\.|[^"\\])*)"/)
[:string, Script::String.new(@scanner[1].gsub(/\\(.)/, '\1'))]
end
def number
return unless @scanner.scan(/(-)?(?:(\d*\.\d+)|(\d+))([a-zA-Z%]+)?/)
value = @scanner[2] ? @scanner[2].to_f : @scanner[3].to_i
value = -value if @scanner[1]
[:number, Script::Number.new(value, Array(@scanner[4]))]
end
COLOR = /\##{"([0-9a-fA-F]{1,2})" * 3}|(#{Color::HTML4_COLORS.keys.join("|")})/
def color
return unless @scanner.scan(COLOR)
value = if @scanner[4]
color = Color::HTML4_COLORS[@scanner[4].downcase]
(0..2).map {|n| color >> (n << 3) & 0xff}.reverse
else
(1..3).map {|i| @scanner[i]}.map {|num| num.ljust(2, num).to_i(16)}
end
[:color, Script::Color.new(value)]
end
def bool
return unless s = @scanner.scan(/(true|false)\b/)
[:bool, Script::Bool.new(s == 'true')]
end
def op
return unless op = OP_NAMES.detect do |s|
@scanner.scan(Regexp.new(Regexp.escape(s) + (s =~ /\w$/ ? '(\b|$)' : '')))
end
[OPERATORS[op]]
end
end
end
end