Class: Slim::Parser Private
- Inherits:
-
Object
- Object
- Slim::Parser
- Includes:
- Temple::Mixins::Options
- Defined in:
- lib/slim/parser.rb
Overview
This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.
Parses Slim code and transforms it to a Temple expression
Defined Under Namespace
Classes: SyntaxError
Constant Summary collapse
- DELIMITERS =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
{ '(' => ')', '[' => ']', '{' => '}', }.freeze
- DELIMITER_REGEX =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
/^[\(\[\{]/
- CLOSE_DELIMITER_REGEX =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
/^[\)\]\}]/
Instance Method Summary collapse
-
#compile(str) ⇒ Array
private
Compile string to Temple expression.
-
#initialize(options = {}) ⇒ Parser
constructor
private
A new instance of Parser.
Constructor Details
#initialize(options = {}) ⇒ Parser
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns a new instance of Parser.
29 30 31 32 |
# File 'lib/slim/parser.rb', line 29 def initialize( = {}) super @tab = ' ' * @options[:tabsize] end |
Instance Method Details
#compile(str) ⇒ Array
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Compile string to Temple expression
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 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/slim/parser.rb', line 38 def compile(str) lineno = 0 result = [:multi] # Since you can indent however you like in Slim, we need to keep a list # of how deeply indented you are. For instance, in a template like this: # # doctype # 0 spaces # html # 0 spaces # head # 1 space # title # 4 spaces # # indents will then contain [0, 1, 4] (when it's processing the last line.) # # We uses this information to figure out how many steps we must "jump" # out when we see an de-indented line. indents = [0] # Whenever we want to output something, we'll *always* output it to the # last stack in this array. So when there's a line that expects # indentation, we simply push a new stack onto this array. When it # processes the next line, the content will then be outputted into that # stack. stacks = [result] # String buffer used for broken line (Lines ending with \) broken_line = nil # We have special treatment for text blocks: # # | # Hello # World! # block_indent, text_indent, in_comment = nil, nil, false str.each_line do |line| lineno += 1 # Remove the newline at the end line.chomp! # Handle broken lines if broken_line if broken_line[-1] == ?\\ broken_line << "\n" << line next end broken_line = nil end if line.strip.empty? # This happens to be an empty line, so we'll just have to make sure # the generated code includes a newline (so the line numbers in the # stack trace for an exception matches the ones in the template). stacks.last << [:newline] next end # Figure out the indentation. Kinda ugly/slow way to support tabs, # but remember that this is only done at parsing time. indent = line[/^[ \t]*/].gsub("\t", @tab).size # Remove the indentation line.lstrip! # Handle blocks with multiple lines if block_indent if indent > block_indent # This line happens to be indented deeper (or equal) than the block start character (|, ', /). # This means that it's a part of the block. if !in_comment # The indentation of first line of the text block determines the text base indentation. newline = text_indent ? "\n" : '' text_indent ||= indent # The text block lines must be at least indented as deep as the first line. offset = indent - text_indent syntax_error! 'Unexpected text indentation', line, lineno if offset < 0 # Generate the additional spaces in front. stacks.last << [:slim, :text, newline + (' ' * offset) + line] end stacks.last << [:newline] next end # It's guaranteed that we're now *not* in a block, because # the indent was less than the block start indent. block_indent = text_indent = nil in_comment = false end # If there's more stacks than indents, it means that the previous # line is expecting this line to be indented. expecting_indentation = stacks.size > indents.size if indent > indents.last # This line was actually indented, so we'll have to check if it was # supposed to be indented or not. syntax_error! 'Unexpected indentation', line, lineno unless expecting_indentation indents << indent else # This line was *not* indented more than the line before, # so we'll just forget about the stack that the previous line pushed. stacks.pop if expecting_indentation # This line was deindented. # Now we're have to go through the all the indents and figure out # how many levels we've deindented. while indent < indents.last indents.pop stacks.pop end # This line's indentation happens lie "between" two other line's # indentation: # # hello # world # this # <- This should not be possible! syntax_error! 'Malformed indentation', line, lineno if indents.last < indent end case line[0] when ?|, ?', ?/ # Found a block. ch = line.slice!(0) # We're now expecting the next line to be indented, so we'll need # to push a block to the stack. block = [:multi] stacks.last << if ch == ?' # Additional whitespace in front [:multi, block, [:slim, :text, ' ']] elsif ch == ?/ && line[0] == ?! # HTML comment line.slice!(0) [:slim, :comment, block] else in_comment = ch == ?/ block end stacks << block block_indent = indent if !in_comment && !line.strip.empty? block << [:slim, :text, line.sub(/^( )/, '')] text_indent = block_indent + ($1 ? 2 : 1) end when ?- # Found a code block. # We expect the line to be broken or the next line to be indented. block = [:multi] broken_line = line[1..-1].strip stacks.last << [:slim, :control, broken_line, block] stacks << block when ?= # Found an output block. # We expect the line to be broken or the next line to be indented. block = [:multi] escape = line[1] != ?= broken_line = escape ? line[1..-1].strip : line[2..-1].strip stacks.last << [:slim, :output, escape, broken_line, block] stacks << block when ?! # Found a directive (currently only used for doctypes) directive = line[1..-1].strip.split(/\s+/, 2) stacks.last << [:slim, :directive, directive[0].downcase, directive[1]] else if line =~ /^(\w+):\s*$/ # Embedded template detected. It is treated as block. block = [:slim, :embedded, $1] stacks.last << [:newline] << block stacks << block block_indent = indent next elsif line =~ /^doctype\s+/i stacks.last << [:slim, :directive, 'doctype', $'.strip] else # Found a HTML tag. tag, block, broken_line, text_indent = parse_tag(line, lineno) stacks.last << tag stacks << block if block if text_indent block_indent = indent text_indent += indent end end end stacks.last << [:newline] end result end |