Common << {
Parser < BasicObject { State < BasicObject { var start_idx var end_idx var rule var result var error_idx raise_error: self.error_idx && raise(SyntaxError "Syntax error with parser: "self.rule" at index: "self.error_idx"" ) } var debug_mode: false var grammar: Grammar.new var builder: null new_worker: set_up_worker(prototype.new) new_processor: Processor.new(builder: builder) var prototype: compile_prototype refresh: self.prototype = compile_prototype compile_prototype: { proto = Component.new grammar.construct_all_rules grammar.rule_table.each |name, rule| { compile_rule_into(proto, name, rule) } proto } compile_rule_into: |proto, name, rule| { construct = rule.inner.construct Myco.add_dynamic_method(proto, name, "(compiled parser)") |g| { g.extend(BytecodeHelpers) g.debug_mode = self.debug_mode g.rule_start construct.bytecode(g) g.rule_finish } } compile_generator: |name, file, line=1, &block| { g = Myco::ToolSet::Generator.new g.name = name g.file = file g.set_line(line) block.call(g) g.ret g.close g.use_detected g } compile_code: |name, file, line=1, &block| { g = compile_generator(name, file, line, &block) g.encode g.package(Rubinius::CompiledCode) } save_prototype: |filename| { code = compile_code(:__script__, :"(snippet)") |g| { g.push_cpath_top; g.find_const(:Class) g.create_block(compile_generator(:__block__, :"(snippet)") |g| { prototype.instance_methods.each |name| { code = prototype.instance_method(name).executable if(code.is_a?(Rubinius::CompiledCode)) { g.push_rubinius g.push_literal(name) g.push_literal(code) g.push_scope g.push_variables; g.send(:method_visibility, 0) g.send(:add_defn_method, 4) } } }) g.send_with_block(:new, 0) } Myco::ToolSet::CompiledFile.dump(code, filename, Rubinius::Signature, Rubinius::RUBY_LIB_VERSION) } load_prototype: |filename| { loader = Myco::CodeLoader::BytecodeLoader.new(filename) loader.bind_to(call_depth:1) self.prototype = loader.load } set_up_worker: |worker| { worker.__set_ivar__(:"@captures", []) worker.__set_ivar__(:"@highest_idx", 0) worker } captures_of_worker: |worker| worker.__get_ivar__(:"@captures") highest_idx_of_worker: |worker| worker.__get_ivar__(:"@highest_idx") parse: { raise("The parse method is not implemented for "self"") } # Match and process the given string using the current grammar parse: |string, rule:"root", start_idx:0| { worker = new_worker processor = new_processor state = State.new( string: string.to_s rule: rule.to_sym start_idx: start_idx.to_i ) state.end_idx = worker.__send__(state.rule, state.string, state.start_idx) state.end_idx &? ( processor.string = state.string processor.capture_items = captures_of_worker(worker) grammar.?tokenizer && (processor.tokenizer = grammar.tokenizer) state.result = processor.process ) ?? ( state.error_idx = highest_idx_of_worker(worker) pos = processor.position_of(state.error_idx, state.string) state.error_row = pos.first state.error_col = pos.last ) state } } Parser << { # Find the first occurrence of the pattern that can be matched, # (not necessarily starting at index zero). # Returns the Parser::State, or null if no occurrences were found. find: |subject, start_idx: 0, **kwargs| { result = null Loop.run { start_idx >= subject.size && Loop.break state = parse(subject, kwargs.merge(start_idx: start_idx)) state.end_idx && (result = state; Loop.break) start_idx = start_idx + 1 } result } # Find each non-overlapping occurrence of the pattern that can be matched, # starting with the first occurrence as found by the find method. # Returns the array of Parser::State objects that were found. scan: |subject, start_idx: 0, **kwargs| { [].tap |result| { Loop.run { state = find(subject, kwargs.merge(start_idx: start_idx)) state || Loop.break start_idx = state.end_idx result.push(state) } } } # For each scan result, replace the matched part with the rule result. # Returns the copied subject with replacements performed. scan_replace: |subject, start_idx: 0, rule: "root", **kwargs| { subject.class.new.tap |result| { rule = rule.to_sym kwargs = kwargs.merge(start_idx: start_idx, rule: rule) start_idx = 0 # always return the entire array scan(subject, kwargs).each |state| { range = Range.new(start_idx, state.start_idx, true) result.concat(subject.slice(range)) result.send(:"<<", state.result[rule]) start_idx = state.end_idx } range = Range.new(start_idx, subject.size, true) result.concat(subject.slice(range)) } } }
}