Class: RSyntaxTree::StringParser
- Inherits:
-
Object
- Object
- RSyntaxTree::StringParser
- Defined in:
- lib/rsyntaxtree/string_parser.rb
Instance Attribute Summary collapse
-
#data ⇒ Object
Returns the value of attribute data.
-
#elist ⇒ Object
Returns the value of attribute elist.
-
#id ⇒ Object
Returns the value of attribute id.
-
#level ⇒ Object
Returns the value of attribute level.
-
#pos ⇒ Object
Returns the value of attribute pos.
Class Method Summary collapse
Instance Method Summary collapse
- #get_elementlist ⇒ Object
- #get_next_token ⇒ Object
-
#initialize(str, fontset, fontsize, global) ⇒ StringParser
constructor
A new instance of StringParser.
- #make_tree(parent) ⇒ Object
- #parse ⇒ Object
-
#restore_rule_names_without_a_rule ⇒ Object
A rule name names the step that produced a node from its daughters.
-
#space_is_the_cause?(token, parent) ⇒ Boolean
Whether the raw space that split this token is what stopped it parsing.
Constructor Details
#initialize(str, fontset, fontsize, global) ⇒ StringParser
Returns a new instance of StringParser.
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 |
# File 'lib/rsyntaxtree/string_parser.rb', line 19 def initialize(str, fontset, fontsize, global) @global = global # Clean up the data a little to make processing easier # repeated newlines => a newline string = str.gsub(/[\n\r]+/m, "\n") # a backslash followed by a newline => a backslash followed by an 'n' string.gsub!(/\\\n\s*/m, "\\n") # repeated whitespace characters => " " string.gsub!(/\s+/, " ") string.gsub!(/\]\s+\[/, "][") string.gsub!(/\s+\[/, "[") string.gsub!(/\[\s+/, "[") string.gsub!(/\s+\]/, "]") string.gsub!(/\]\s+/, "]") string.gsub!(/<(\d*)>/) do num_padding = $1.to_i result = if num_padding.positive? WHITESPACE_BLOCK * num_padding else WHITESPACE_BLOCK end result end @data = string # Store it for later... @elist = ElementList.new # Initialize internal element list @pos = 0 # Position in the sentence @id = 1 # ID for the next element @level = 0 # Level in the diagram @fontset = fontset @fontsize = fontsize end |
Instance Attribute Details
#data ⇒ Object
Returns the value of attribute data.
17 18 19 |
# File 'lib/rsyntaxtree/string_parser.rb', line 17 def data @data end |
#elist ⇒ Object
Returns the value of attribute elist.
17 18 19 |
# File 'lib/rsyntaxtree/string_parser.rb', line 17 def elist @elist end |
#id ⇒ Object
Returns the value of attribute id.
17 18 19 |
# File 'lib/rsyntaxtree/string_parser.rb', line 17 def id @id end |
#level ⇒ Object
Returns the value of attribute level.
17 18 19 |
# File 'lib/rsyntaxtree/string_parser.rb', line 17 def level @level end |
#pos ⇒ Object
Returns the value of attribute pos.
17 18 19 |
# File 'lib/rsyntaxtree/string_parser.rb', line 17 def pos @pos end |
Class Method Details
.valid?(data) ⇒ Boolean
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 |
# File 'lib/rsyntaxtree/string_parser.rb', line 52 def self.valid?(data) raise RSTError.new(+"Error: input text is empty", code: :empty_input, retryable: false) if data.empty? if /\[\s*\]/m =~ data raise RSTError.new(+"Error: inside the brackets is empty", code: :empty_brackets, hint: "A pair of brackets has no label between them. Give the node a label, or remove the pair.", retryable: true) end text = data.strip text_r = text.split(//) open_br = [] close_br = [] escape = false text_r.each do |chr| if chr == "\\" escape = if escape false else true end next end if escape && /[\[\]]/ =~ chr escape = false next elsif chr == '[' open_br.push(chr) elsif chr == ']' close_br.push(chr) break if open_br.length < close_br.length end escape = false end # No brackets at all is a label on its own, which draws as a single # leaf; only a count that does not match is a mistake. if open_br.length == close_br.length true else raise RSTError.new(+"Error: open and close brackets do not match", code: :unbalanced_brackets, hint: "Count the brackets: every '[' needs one ']'. A bracket meant as text is written \\[ or \\].", retryable: true) end end |
Instance Method Details
#get_elementlist ⇒ Object
139 140 141 |
# File 'lib/rsyntaxtree/string_parser.rb', line 139 def get_elementlist @elist; end |
#get_next_token ⇒ Object
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 |
# File 'lib/rsyntaxtree/string_parser.rb', line 143 def get_next_token data = @data.split(//) gottoken = false token = "" i = 0 return "" if (@pos + 1) >= data.length escape = false while ((@pos + i) < data.length) && !gottoken ch = data[@pos + i] case ch when "[" if escape token += '\\[' # Preserve as escaped bracket escape = false elsif i.positive? gottoken = true else token += ch end when "]" if escape token += '\\]' # Preserve as escaped bracket escape = false else token += ch if i.zero? gottoken = true end when "\\" if escape token += '\\\\' escape = false else escape = true end when " " if escape token += '\\n' escape = false else token += ch end # The characters a backslash may take, which is the grammar's own list # (Markup's `escaped` rule) plus the two letters that name a break. A # backslash before anything else is dropped here, and '#' was missing: # `\#` arrived at the grammar as a bare '#', which opens an enclosure, # so a label written with a hash in it lost the hash and everything # after it went inside brackets instead. when /[nt{}<>^+*_=~|%\-#]/ if escape token += '\\' + ch escape = false else token += ch end else if escape token += ch escape = false else token += ch end end i += 1 end @pos += if i > 1 i - 1 else 1 end token end |
#make_tree(parent) ⇒ Object
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 |
# File 'lib/rsyntaxtree/string_parser.rb', line 218 def make_tree(parent) token = get_next_token.strip parts = [] while token != "" && token != "]" token_r = token.split(//) case token_r[0] when "[" # Check for escaped square brackets if token =~ /\A\\\[/ || token =~ /\A\\\]/ # Treat escaped brackets as regular text element = Element.new(@id, parent, token, @level, @fontset, @fontsize, @global) @id += 1 @elist.add(element) else # Existing processing below tl = token_r.length token_r = token_r[1, tl - 1] spaceat = token_r.index(" ") newparent = -1 if spaceat parts[0] = token_r[0, spaceat].join tl = token_r.length parts[1] = token_r[spaceat, tl - spaceat].join element = begin Element.new(@id, parent, parts[0], @level, @fontset, @fontsize, @global, true) rescue RSTError => e # The first raw space splits a token into the node's label # and its children (that split is the notation's core rule, # so it stays). When the part before the space will not # parse, the likeliest story is that the space belongs # inside the label and cut a construct in two — say so, # unless a more specific cause is already known. # # Which story is right is not a thing to guess at: ask # whether the space is the one that breaks it. Put the whole # token back together with the spaces written as the notation # writes them, and try again. If it parses, the space was # cutting a construct in two and that is what to say. If it # fails the same way, the space is a red herring and the cause # already named is the one to keep. # # It used to keep :bare_hyphen and relabel every other cause, # so one error said two things: the message naming an unknown # colour while the code and the hint talked about spaces. A # caller acting on the code — which is what these are for — # was sent to fix what was not wrong. raise e unless space_is_the_cause?(token_r.join, parent) raise RSTError.new(e., code: :label_split, label: e.label, position: e.position, hint: "A raw space split this into a label and its children. If the space belongs inside the label, write it as <> (e.g. 'a<>toy').", retryable: true) end @id += 1 @elist.add(element) newparent = element.id element = Element.new(@id, @id - 1, parts[1], @level + 1, @fontset, @fontsize, @global) @id += 1 else joined = token_r.join element = Element.new(@id, parent, joined, @level, @fontset, @fontsize, @global, true) @id += 1 newparent = element.id end @elist.add(element) @level += 1 make_tree(newparent) end else if token.strip != "" element = Element.new(@id, parent, token, @level, @fontset, @fontsize, @global) @id += 1 @elist.add(element) end end token = get_next_token end @level -= 1 end |
#parse ⇒ Object
110 111 112 113 114 |
# File 'lib/rsyntaxtree/string_parser.rb', line 110 def parse make_tree(0); @elist.set_hierarchy restore_rule_names_without_a_rule end |
#restore_rule_names_without_a_rule ⇒ Object
A rule name names the step that produced a node from its daughters. A node with no daughters is the product of no step, so what looked like a name is a column of the label like any other, and it goes back.
Whether a node will have daughters is not known where the label is read —
they arrive as later tokens — so the label is read first and put right
here, once the tree is built. Left alone, turning the option on deleted a
column: [A\tfoo] drew "A foo" with derivation off and "A" with it on,
and said nothing about the difference.
125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/rsyntaxtree/string_parser.rb', line 125 def restore_rule_names_without_a_rule @elist.elements.each_with_index do |e, i| next if e.rule_name.nil? || e.rule_name.empty? next unless e.children.empty? next if e.label_with_rule_name.nil? restored = Element.new(e.id, e.parent, e.label_with_rule_name, e.level, @fontset, @fontsize, @global) restored.children = e.children restored.type = e.type @elist.elements[i] = restored end end |
#space_is_the_cause?(token, parent) ⇒ Boolean
Whether the raw space that split this token is what stopped it parsing.
Asked of the parser rather than reasoned about: the whole token, with its
spaces written as <>, either reads as one label or it does not.
102 103 104 105 106 107 108 |
# File 'lib/rsyntaxtree/string_parser.rb', line 102 def space_is_the_cause?(token, parent) Element.new(-1, parent, token.gsub(" ", WHITESPACE_BLOCK), @level, @fontset, @fontsize, @global, true) true rescue StandardError false end |