Class: SexpistolParser

Inherits:
StringScanner
  • Object
show all
Defined in:
lib/vendor/sexpistol/sexpistol/sexpistol_parser.rb

Instance Method Summary collapse

Constructor Details

#initialize(string) ⇒ SexpistolParser

Returns a new instance of SexpistolParser.



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/vendor/sexpistol/sexpistol/sexpistol_parser.rb', line 8

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 ParseException.new("Missing closing parentheses")
  elsif paren_count < 0
    raise ParseException.new("Missing opening parentheses")
  end

  super(string)
end

Instance Method Details

#expression_enderObject



98
99
100
101
102
103
104
# File 'lib/vendor/sexpistol/sexpistol/sexpistol_parser.rb', line 98

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

#fetch_tokenObject



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/vendor/sexpistol/sexpistol/sexpistol_parser.rb', line 68

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]+)?))#{ expression_ender }/x)
    matched.to_f
  # Match an integer literal
  elsif scan(/[\-\+]?[0-9]+#{ expression_ender }/)
    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

#parseObject



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/vendor/sexpistol/sexpistol/sexpistol_parser.rb', line 46

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