Class: Reco::Scanner

Inherits:
Object
  • Object
show all
Defined in:
lib/reco/scanner.rb

Constant Summary collapse

MODE_PATTERNS =
{
  :data => /(.*?)(<%%|<%\s*(\#)|<%(([=-])?)|\n|$)/,
  :code => /(.*?)((((:|(->|=>))\s*))?%>|\n|$)/,
  :comment => /(.*?)(%>|\n|$)/
}
DEDENTABLE_PATTERN =
/^(end|when|else|catch|finally)(?:\W|$)/

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source) ⇒ Scanner

Returns a new instance of Scanner.



18
19
20
21
22
23
24
# File 'lib/reco/scanner.rb', line 18

def initialize(source)
  @source = source
  @scanner = StringScanner.new source
  @mode = :data
  @buffer = ''
  @done = false
end

Class Method Details

.scan(source) ⇒ Object



11
12
13
14
15
16
# File 'lib/reco/scanner.rb', line 11

def self.scan(source)
  tokens = []
  scanner = new source
  scanner.scan { |token| tokens << token } until scanner.done?
  tokens
end

Instance Method Details

#advanceObject



46
47
48
49
50
51
52
53
# File 'lib/reco/scanner.rb', line 46

def advance
  @scanner.scan_until MODE_PATTERNS[@mode]
  @buffer += @scanner[1]
  @tail = @scanner[2]
  @comment = @scanner[3]
  @directive = @scanner[5]
  @arrow = @scanner[6]
end

#done?Boolean

Returns:

  • (Boolean)


107
108
109
# File 'lib/reco/scanner.rb', line 107

def done?
  @done
end

#flushObject



101
102
103
104
105
# File 'lib/reco/scanner.rb', line 101

def flush
  buffer = @buffer
  @buffer = ''
  buffer
end

#is_dedentable?(code) ⇒ Boolean

Returns:

  • (Boolean)


97
98
99
# File 'lib/reco/scanner.rb', line 97

def is_dedentable?(code)
  code.match DEDENTABLE_PATTERN
end

#scan(callback = nil, &block) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/reco/scanner.rb', line 26

def scan(callback = nil, &block)
  callback ||= block
  
  if @scanner.eos?
    @done = true
    callback.call @mode == :data ? ["print_string", flush] : ["fail", "unexpected end of template"]
  else
    advance
    
    case @mode
    when :data
      scan_data callback
    when :code
      scan_code callback
    when :comment
      scan_comment callback
    end
  end
end

#scan_code(callback) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/reco/scanner.rb', line 74

def scan_code(callback)
  if @tail == "\n"
    callback.call ["fail", "unexpected newline in code block"]
  elsif !@tail.empty?
    @mode = :data
    code = flush.strip
    code += " #{@arrow}" if @arrow
    
    callback.call ["dedent"] if is_dedentable?(code)
    callback.call ["record_code", code]
    callback.call ["indent", @arrow] if @directive
  end
end

#scan_comment(callback) ⇒ Object



88
89
90
91
92
93
94
95
# File 'lib/reco/scanner.rb', line 88

def scan_comment(callback)
  if @tail == "\n"
    callback.call ['fail', 'unexpected newline in code block']
  elsif @tail
    @mode = :data
    @buffer = ''
  end
end

#scan_data(callback) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/reco/scanner.rb', line 55

def scan_data(callback)
  if @tail == "<%%"
    @buffer += "<%"
    scan callback
  elsif @tail == "\n"
    @buffer += @tail
    scan callback
  elsif @tail
    callback.call ["print_string", flush]
    
    if @comment
      @mode = :comment
    else
      @mode = :code
      callback.call ["begin_code", {:print => !!@directive, :safe => @directive == '-'}]
    end
  end
end