Class: Emerald::PreProcessor

Inherits:
Object
  • Object
show all
Defined in:
lib/emerald/preprocessor.rb

Overview

Preprocess the emerald code and add notion of indentation so it may be parsed by a context free grammar. Removes all whitespace and adds braces to denote indentation.

Instance Method Summary collapse

Constructor Details

#initializePreProcessor



14
15
16
# File 'lib/emerald/preprocessor.rb', line 14

def initialize
  reset
end

Instance Method Details

#check_and_enter_literal(line) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/emerald/preprocessor.rb', line 126

def check_and_enter_literal(line)
  if line.rstrip.end_with?('->')
    @in_literal = true
    @current_indent += 2
    @templateless_literal = false
    @preserve_html_literal = false
  elsif line.rstrip.end_with?('=>')
    @in_literal = true
    @current_indent += 2
    @templateless_literal = true
    @preserve_html_literal = false
  elsif line.rstrip.end_with?('~>')
    @in_literal = true
    @current_indent += 2
    @templateless_literal = true
    @preserve_html_literal = true
  end
end

#check_new_indent(new_indent) ⇒ Object

Compares the value of the new_indent with the old indentation. Invoked by: process_emerald



60
61
62
63
64
65
66
# File 'lib/emerald/preprocessor.rb', line 60

def check_new_indent(new_indent)
  if new_indent > @current_indent
    open_tags(new_indent)
  elsif new_indent < @current_indent
    close_tags(new_indent)
  end
end

#close_entered_tags(new_indent) ⇒ Object

Append closing braces if not in literal and new indent is less than old one



96
97
98
99
100
101
# File 'lib/emerald/preprocessor.rb', line 96

def close_entered_tags(new_indent)
  (1..((@current_indent - new_indent) / 2)).each do
    @output += "}\n"
    @b_count -= 1
  end
end

#close_literal(new_indent) ⇒ Object

Append closing braces if in literal and new indent is less than old one



85
86
87
88
89
90
91
92
93
# File 'lib/emerald/preprocessor.rb', line 85

def close_literal(new_indent)
  @output += "$\n"
  @in_literal = false

  (2..((@current_indent - new_indent) / 2)).each do
    @output += "}\n"
    @b_count -= 1
  end
end

#close_tags(new_indent) ⇒ Object



75
76
77
78
79
80
81
82
# File 'lib/emerald/preprocessor.rb', line 75

def close_tags(new_indent)
  if @in_literal
    close_literal(new_indent)
  else
    close_entered_tags(new_indent)
  end
  @current_indent = new_indent
end

#open_tags(new_indent) ⇒ Object



68
69
70
71
72
73
# File 'lib/emerald/preprocessor.rb', line 68

def open_tags(new_indent)
  return if @in_literal
  @output += "{\n"
  @b_count += 1
  @current_indent = new_indent
end

#process_emerald(input) ⇒ Object

Process the emerald to remove indentation and replace with brace convention for an easier time parsing with context free grammar



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
# File 'lib/emerald/preprocessor.rb', line 31

def process_emerald(input)
  input.each_line.with_index do |line, line_number|
    if @in_literal
      if line[0...-1].empty?
        new_indent = @current_indent
        line = " "*@current_indent + "\n"
      else
        new_indent = line.length - line.lstrip.length
      end
    else 
      next if line.lstrip.empty?
      new_indent = line.length - line.lstrip.length
    end

    check_new_indent(new_indent)
    @output += remove_indent_whitespace(line)
    @source_map[@output.lines.length] = {
      source_line: line_number + 1
    }
    check_and_enter_literal(line)
  end

  close_tags(0)

  [@output, @source_map]
end

#remove_indent_whitespace(line) ⇒ Object

Crop off only Emerald indent whitespace to preserve whitespace in the literal. Since $ is the end character, we need to escape it in the literal.



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/emerald/preprocessor.rb', line 105

def remove_indent_whitespace(line)
  if @in_literal
    # Ignore indent whitespace, only count post-indent as the literal,
    # but keep any extra whitespace that might exist for literals
    cropped = line[@current_indent..-1] || ''
    if @templateless_literal
      # this is a fun one https://www.ruby-forum.com/topic/143645
      cropped = cropped.gsub("\\"){ "\\\\" }
    end

    unless @preserve_html_literal
      cropped = @encoder.encode cropped
    end

    # Escape $ since we use it as a terminator for literals, and encode HTML
    cropped.gsub('$', "\\$")
  else
    line.lstrip
  end
end

#resetObject

Reset class variables, used for testing



19
20
21
22
23
24
25
26
27
# File 'lib/emerald/preprocessor.rb', line 19

def reset
  @in_literal = false
  @templateless_literal = false
  @current_indent = 0
  @b_count = 0
  @output = ''
  @encoder = HTMLEntities.new
  @source_map = {}
end