Sha256: 8b94c6950f2ee623241da71a25b8945ceda1cba38fd50fbd23e2c5ace8422d4c

Contents?: true

Size: 2 KB

Versions: 1

Compression:

Stored size: 2 KB

Contents

require 'strscan'

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

      case byte.chr
      when '\\'
        escape_char = true
        next
      when '('
        if !in_string_literal
          paren_count += 1
        end
      when ')'
        if !in_string_literal
          paren_count -= 1
        end
      when '"'
        in_string_literal = !in_string_literal
      end
    end

    if paren_count > 0
      raise Exception, "Missing closing parentheses"
    elsif paren_count < 0
      raise Exception, "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
      end
    end
    exp
  end

  def fetch_token
    skip(/\s+/)
    return nil if(eos?)

    @token =
    # Match parentheses
    if scan(/[\(\)]/)
      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)
      matched.to_f
    # Match an integer literal
    elsif scan(/[\-\+]?[0-9]+ (\s|$)/x)
      matched.to_i
    # Match a comma (for comma quoting)
    elsif scan(/'/)
      matched.to_sym
    # Match a symbol
    elsif scan(/[^\(\)\s]+/)
      matched.to_sym
    # 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

end

Version data entries

1 entries across 1 versions & 1 rubygems

Version Path
qcmd-0.1.12 lib/vendor/sexpistol/sexpistol/sexpistol_parser.rb