File: utils.rb

package info (click to toggle)
ruby-rsec 0.4.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 272 kB
  • sloc: ruby: 2,130; lisp: 13; makefile: 3
file content (117 lines) | stat: -rw-r--r-- 2,814 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# coding: utf-8

module Rsec #:nodoc:

  # error class for rescue
  class SyntaxError < StandardError
    attr_reader :msg, :line_text, :line, :col

    # constructor
    def initialize msg, line_text, line, col
      @msg, @line_text, @line, @col = msg, line_text, line, col
    end

    # info with source position
    def to_s
      %Q<#@msg\n#@line_text\n#{' ' * @col}^>
    end
  end

  # parse context inherits from StringScanner<br/>
  # <br/>
  # attributes:<br/>
  # <pre>
  #   [R]  string: string to parse
  #   [RW] pos: current position
  #   [R]  source: source file name
  #   [R]  current_line_text: current line text
  #   [R]  cache: for memoization
  # </pre>
  class ParseContext < StringScanner
    attr_reader :source, :cache, :last_fail_pos
    attr_accessor :attr_names

    def initialize str, source
      super(str)
      @source = source
      @cache = {}
      @last_fail_pos = 0
      @last_fail_mask = 0
    end
    
    # clear packrat parser cache
    def clear_cache
      @cache.clear
    end

    # add fail message
    def on_fail mask
      if pos > @last_fail_pos
        @last_fail_pos = pos
        @last_fail_mask = mask
      elsif pos == @last_fail_pos
        @last_fail_mask |= mask
      end
    end

    # generate parse error
    def generate_error source
      if self.pos <= @last_fail_pos
        line = line @last_fail_pos
        col = col @last_fail_pos
        line_text = line_text @last_fail_pos
        expect_tokens = Fail.get_tokens @last_fail_mask
        expects = ", expect token [ #{expect_tokens.join ' | '} ]"
      else
        line = line pos
        col = col pos
        line_text = line_text pos
        expects = nil
      end
      msg = "\nin #{source}:#{line} at #{col}#{expects}"
      SyntaxError.new msg, line_text, line, col
    end

    # get line number
    def line pos
      string[0...pos].count("\n") + 1
    end

    # get column number: position in line
    def col pos
      return 1 if pos == 0
      newline_pos = string.rindex "\n", pos - 1
      if newline_pos
        pos - newline_pos
      else
        pos + 1
      end
    end

    # get line text containing pos
    # the text is 80 at most
    def line_text pos
      from = string.rindex "\n", pos
      (from = string.rindex "\n", pos - 1) if from == pos
      from = from ? from + 1 : 0
      from = pos - 40 if (from < pos - 40)

      to = string.index("\n", pos)
      to = to ? to - 1 : string.size
      to = pos + 40 if (to > pos + 40)

      string[from..to]
    end
  end

  # the invalid token
  INVALID = Object.new
  class << INVALID
    def to_str
      'INVALID_TOKEN'
    end
    alias :[] :==
    alias inspect to_str
  end

end