Class: Moron_Text
- Inherits:
-
Object
- Object
- Moron_Text
- Defined in:
- lib/moron_text.rb
Constant Summary collapse
- SPACE =
' '.freeze
- DELIM =
"\\s+" + Regexp.escape("/*")
- PATTERNS =
{ :command__arg => [ %r!\A\s*(.+)#{DELIM}\s+(.+)\Z!, :value, :arg ], :command => [ /\A\s*(.+)#{DELIM}\s*\Z/, :value ] }
- NEW_LINE_REG_EXP =
/\r?\n/
- NL =
"\n".freeze
- TYPO =
Class.new(RuntimeError) do attr_reader :moron, :line_number def initialize moron, text = "Typo" super text @moron = moron @line_number = @moron.line_number end def line moron.lines[line_number-1] end def line_context start = line_number - 3 - 1 stop = line_number + 3 - 1 start = 0 if start < 0 i = start moron.lines.slice(start, stop - start).map { |o| i += 1 [i, o] } end end
- MISSING_KEY =
Class TYPO
lambda { |hash, key| fail RuntimeError, "Missing key: #{key.inspect}" }
Instance Attribute Summary collapse
-
#defs ⇒ Object
readonly
class self ===.
-
#lines ⇒ Object
readonly
class self ===.
-
#parsed_lines ⇒ Object
readonly
class self ===.
-
#stack ⇒ Object
readonly
class self ===.
Class Method Summary collapse
Instance Method Summary collapse
- #[](k) ⇒ Object
- #[]=(k, v) ⇒ Object
- #current ⇒ Object
- #fulfills?(cond) ⇒ Boolean
- #grab_prev_text ⇒ Object
- #grab_text ⇒ Object
-
#initialize(str) ⇒ Moron_Text
constructor
A new instance of Moron_Text.
- #line_number ⇒ Object
- #meta_line(type, val) ⇒ Object
- #next ⇒ Object
- #numbers ⇒ Object
- #off?(sym) ⇒ Boolean
- #on?(sym) ⇒ Boolean
- #parse ⇒ Object
- #prev_text ⇒ Object
- #return(*args) ⇒ Object
- #run(l = nil) ⇒ Object
- #split ⇒ Object
- #text ⇒ Object
- #turn_off(sym) ⇒ Object
-
#turn_on(sym) ⇒ Object
def initialize.
- #typo(msg) ⇒ Object
- #typo! ⇒ Object
Constructor Details
#initialize(str) ⇒ Moron_Text
Returns a new instance of Moron_Text.
82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/moron_text.rb', line 82 def initialize str @str = str @lines = nil @parsed_lines = nil @stack = nil @has_run = false @defs = {} @line_number = nil @parsed_line_number = nil @next_parse_line = nil @settings = {} @settings.default_proc = MISSING_KEY end |
Instance Attribute Details
#defs ⇒ Object (readonly)
class self ===
80 81 82 |
# File 'lib/moron_text.rb', line 80 def defs @defs end |
#lines ⇒ Object (readonly)
class self ===
80 81 82 |
# File 'lib/moron_text.rb', line 80 def lines @lines end |
#parsed_lines ⇒ Object (readonly)
class self ===
80 81 82 |
# File 'lib/moron_text.rb', line 80 def parsed_lines @parsed_lines end |
#stack ⇒ Object (readonly)
class self ===
80 81 82 |
# File 'lib/moron_text.rb', line 80 def stack @stack end |
Class Method Details
.run(*args) ⇒ Object
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/moron_text.rb', line 59 def run *args case when block_given? @run_lambda = lambda { |*args| yield(*args) } when args.size == 3 args.last.next unless @run_lambda @run_lambda.call(*args) when args.size == 1 && args.first.is_a?(Proc) @run_lambda = args.first else fail "Unknown args: #{args.inspect}" end # === case end |
.standard_name(n) ⇒ Object
55 56 57 |
# File 'lib/moron_text.rb', line 55 def standard_name n n.split.map(&:upcase).join(SPACE) end |
Instance Method Details
#[](k) ⇒ Object
128 129 130 |
# File 'lib/moron_text.rb', line 128 def [] k @settings[k] end |
#[]=(k, v) ⇒ Object
124 125 126 |
# File 'lib/moron_text.rb', line 124 def []= k, v @settings[k] = v end |
#current ⇒ Object
144 145 146 |
# File 'lib/moron_text.rb', line 144 def current @parsed_lines[@parsed_line_number] end |
#fulfills?(cond) ⇒ Boolean
188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/moron_text.rb', line 188 def fulfills? cond parsed = current About_Pos.Forward(cond).all? { |v,i,m| args = m.grab case v when :on args.any? { |on_name| on?(on_name) } when :value args.any? { |v| parsed[:value] == v } else fail "Typo: #{v.inspect}" end } end |
#grab_prev_text ⇒ Object
148 149 150 |
# File 'lib/moron_text.rb', line 148 def grab_prev_text prev_text end |
#grab_text ⇒ Object
159 160 161 162 163 |
# File 'lib/moron_text.rb', line 159 def grab_text val = text @seq.grab val end |
#line_number ⇒ Object
140 141 142 |
# File 'lib/moron_text.rb', line 140 def line_number current[:line_number] end |
#meta_line(type, val) ⇒ Object
274 275 276 277 278 279 280 281 282 283 |
# File 'lib/moron_text.rb', line 274 def type, val { :type =>type, :value =>val, :original =>@lines[@parse_index-1], :line_number =>@parse_index, :is_closed =>false, :arg =>nil } end |
#next ⇒ Object
203 204 205 |
# File 'lib/moron_text.rb', line 203 def next throw :moron_flow, :next end |
#numbers ⇒ Object
178 179 180 181 182 183 184 185 186 |
# File 'lib/moron_text.rb', line 178 def numbers split.map { |u| begin Float(u) rescue ArguementError fail typo("Numerical typo.") end } end |
#off?(sym) ⇒ Boolean
116 117 118 119 120 121 122 |
# File 'lib/moron_text.rb', line 116 def off? sym if !@settings.has_key?(sym) @settings[sym] = false end !on?[sym] end |
#on?(sym) ⇒ Boolean
104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/moron_text.rb', line 104 def on? sym if !@settings.has_key?(sym) @settings[sym] = false end if @settings[sym] != true && @settings[sym] != false fail "Invalid type for: #{sym.inspect}: #{settings[sym].inspect}" end @settings[sym] end |
#parse ⇒ Object
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
# File 'lib/moron_text.rb', line 285 def parse return @parsed_lines if @parsed_lines @lines = @str.split(NEW_LINE_REG_EXP) @parse_index = 0 @parsed_lines = [] # === Pass 1: Create an array of commands and text. @lines.each { |line| @parse_index += 1 parsed = nil is_command = PATTERNS.detect { |name, pattern| match = line.match pattern.first next unless match captures = match.captures shift_capture = lambda { fail "Captures already empty: #{name.inspect}" if captures.empty? captures.shift } parsed = (:command, nil) start = 0 step = start stop = pattern.size capture_i = 0 while step < (stop-1) grab_next = lambda { step += 1 fail("No more items.") if step >= stop pattern[step] } val = grab_next.call next if step == start case val when :value if parsed[:type] == :command parsed[:value] = Moron_Text.standard_name shift_capture.call else parsed[:value] = shift_capture.call end when :arg parsed[:arg] = shift_capture.call when :is_closed parsed[:is_closed] = true when :allow parsed[:allow] = grab_next.call when :grab_all_text parsed[:grab_all_text] = true parsed[:text] = captures.compact.join ' '.freeze else fail "Typo: unknown pattern command: #{val.inspect}" end # case val end # while step < stop match } # detect if is command? if !is_command parsed = (:text, line.dup) end parsed.default_proc = MISSING_KEY @parsed_lines << parsed } # === PASS 2: combine text, strip it lines = [] About_Pos.Forward(@parsed_lines) { |o, i, m| if o[:type] == :text while m.next? && m.next.value[:type] == :text o[:value] << NL o[:value] << m.grab[:value] end o[:value].strip! end lines << o } @parsed_lines = lines end |
#prev_text ⇒ Object
152 153 154 155 156 157 |
# File 'lib/moron_text.rb', line 152 def prev_text fail typo("Missing previous text for line.") unless @seq.prev? prev = @seq.prev.value fail typo("Missing previous text for line.") unless prev[:type] == :text prev[:value] end |
#return(*args) ⇒ Object
207 208 209 210 |
# File 'lib/moron_text.rb', line 207 def return *args @stack.concat args throw :moron_flow, :ignore end |
#run(l = nil) ⇒ Object
212 213 214 215 216 217 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 |
# File 'lib/moron_text.rb', line 212 def run l = nil return @stack if @has_run parse @stack = [] @line_number = 0 About_Pos.Forward(@parsed_lines) { |line, i, m| @parsed_line_number = i @seq = m case line[:type] when :command, :text if line.has_key?(:grab_all_text) line[:text] = [ (line.has_key?(:text) ? line[:text] : nil), (m.next? && m.next.value[:type] == :text ? m.grab[:value] : nil) ]. compact. join(NL) end if line.has_key?(:allow) case when line[:allow].all? { |c| c.is_a?(Array) } line[:allow].detect { |cond,i,m| fulfills? cond } else fulfills? line[:allow] end end args = [(line[:type] == :text ? :text : line[:value]), line, self] val = nil do_next = :next if block_given? do_next = catch(:moron_flow) { val = yield(*args) } end if do_next == :next && l do_next = catch(:moron_flow) { val = l.call(*args) } end if do_next == :next do_next = catch(:moron_flow) { val = self.class.run(*args) } end fail(typo "Typo: #{line[:value]}") if do_next == :typo || do_next == :next (@stack << val) unless do_next == :ignore else fail "Programmer error: #{line[:type].inspect}" end # case line[:type] } # === About_Pos @has_run = true @stack end |
#split ⇒ Object
174 175 176 |
# File 'lib/moron_text.rb', line 174 def split current[:arg].split end |
#text ⇒ Object
165 166 167 168 169 170 171 172 |
# File 'lib/moron_text.rb', line 165 def text fail typo("Missing text for line.") unless @seq.next? next_ = @seq.next.value fail typo("Missing text for line.") unless next_[:type] == :text next_[:value] end |
#turn_off(sym) ⇒ Object
100 101 102 |
# File 'lib/moron_text.rb', line 100 def turn_off sym @settings[sym] = false end |
#turn_on(sym) ⇒ Object
def initialize
96 97 98 |
# File 'lib/moron_text.rb', line 96 def turn_on sym @settings[sym] = true end |
#typo(msg) ⇒ Object
136 137 138 |
# File 'lib/moron_text.rb', line 136 def typo msg TYPO.new(self, msg) end |
#typo! ⇒ Object
132 133 134 |
# File 'lib/moron_text.rb', line 132 def typo! throw :moron_flow, :typo end |