[
TokenScanner.new(:whitespace, '\s+'),
TokenScanner.new(:numeric, '(\d+(\.\d+)?|\.\d+)', lambda { |raw| raw =~ /\./ ? raw.to_f : raw.to_i }),
TokenScanner.new(:string, '"[^"]*"', lambda { |raw| raw.gsub(/^"|"$/, '') }),
TokenScanner.new(:string, "'[^']*'", lambda { |raw| raw.gsub(/^'|'$/, '') }),
TokenScanner.new(:operator, '\+|-|\*|\/', lambda do |raw|
case raw
when '+' then :add
when '-' then :subtract
when '*' then :multiply
when '/' then :divide
end
end),
TokenScanner.new(:grouping, '\(|\)|,', lambda do |raw|
case raw
when '(' then :open
when ')' then :close
when ',' then :comma
end
end),
TokenScanner.new(:comparator, '<=|>=|!=|<>|<|>|=', lambda do |raw|
case raw
when '<=' then :le
when '>=' then :ge
when '!=' then :ne
when '<>' then :ne
when '<' then :lt
when '>' then :gt
when '=' then :eq
end
end),
TokenScanner.new(:combinator, '(and|or)\b', lambda { |raw| raw.strip.downcase.to_sym }),
TokenScanner.new(:function, '(if|round)\b', lambda { |raw| raw.strip.to_sym }),
TokenScanner.new(:identifier, '[a-z_]+', lambda { |raw| raw.downcase.to_sym })
]