lib/vendor/sexpistol/sexpistol/sexpistol_parser.rb in qcmd-0.1.13 vs lib/vendor/sexpistol/sexpistol/sexpistol_parser.rb in qcmd-0.1.14

- old
+ new

@@ -1,14 +1,18 @@ require 'strscan' +class ParseException < Exception +end + class SexpistolParser < StringScanner def initialize(string) # step through string counting closing parens, exclude parens in string literals in_string_literal = false escape_char = false paren_count = 0 + string.bytes.each do |byte| if escape_char escape_char = false next end @@ -29,35 +33,35 @@ in_string_literal = !in_string_literal end end if paren_count > 0 - raise Exception, "Missing closing parentheses" + raise ParseException.new("Missing closing parentheses") elsif paren_count < 0 - raise Exception, "Missing opening parentheses" + raise ParseException.new("Missing opening parentheses") end super(string) end def parse exp = [] while true case fetch_token - when '(' - exp << parse - when ')' - break - when :"'" - case fetch_token - when '(' then exp << [:quote].concat([parse]) - else exp << [:quote, @token] - end - when String, Fixnum, Float, Symbol - exp << @token - when nil - break + when '(' + exp << parse + when ')' + break + when :"'" + case fetch_token + when '(' then exp << [:quote].concat([parse]) + else exp << [:quote, @token] + end + when String, Fixnum, Float, Symbol + exp << @token + when nil + break end end exp end @@ -71,14 +75,14 @@ matched # Match a string literal elsif scan(/"([^"\\]|\\.)*"/) eval(matched) # Match a float literal - elsif scan(/[\-\+]? [0-9]+ ((e[0-9]+) | (\.[0-9]+(e[0-9]+)?))(\)| )(\s|$)/x) + elsif scan(/[\-\+]? [0-9]+ ((e[0-9]+) | (\.[0-9]+(e[0-9]+)?))#{ expression_ender }/x) matched.to_f # Match an integer literal - elsif scan(/[\-\+]?[0-9]+(\)| )(\s|$)/x) + elsif scan(/[\-\+]?[0-9]+#{ expression_ender }/) matched.to_i # Match a comma (for comma quoting) elsif scan(/'/) matched.to_sym # Match a symbol @@ -87,8 +91,16 @@ # If we've gotten here then we have an invalid token else near = scan %r{.{0,20}} raise "Invalid character at position #{pos} near '#{near}'." end + end + + def expression_ender + # end Fixnum and Float matchers with a non-grouping positive lookahead + # assertion that matches a closing paren, whitespace, or string end. The + # positive lookahead (?=...) ensures the string scanner includes the ending + # character in next fetch_token call. + '(?=(?:\)|\s|$))' end end