module Gorgyrella class Parser %%{ machine gorgyrella_parser; action raise_error { context = data[p - 30, 60].pack("c*") rescue context ||= data[p - 10, 20].pack("c*") rescue context ||= data[p-10, 10].pack("c*") context ||= "not available" raise "an error occurred parsing character " + p.to_s + " code: " + data[p].to_s + " chr: ->" + data[p, 1].pack("c*") + "<- \ncontext: " + context } action s { start = p } action l { text = data[start, p - start].pack("c*").strip } action reset_vars { filename = nil section = nil format = nil language = nil command = nil } action process_command { case command when :include builder.append_section( :command => 'include', :file => filename, :section => section, :format => format, :language => language ) when :export builder.append_section(:section => section, :language => language) when :end builder.append_section else raise command end } action start_line { location = "start" line_start_pos = p } action finish_line { current_line_text = data[line_start_pos, p - line_start_pos].pack("c*") line_data << [p, cs, current_line_text] } action trace { $stdout.write "\nchar #{ sprintf("%03d", p)} #{data[p, 1].pack("c*")} state #{cs} #{location}" } ws = [ \t]; eol = "\n" | "\r\n"; identifier = (alpha (alpha | '.' | '_' | '-')*) >s %l; string = '"' . (any - '"' - eol)* >s %l . '"'; # token STRING: '\'([^\\n\'\\\\]|\\\\.)*\'|"([^\\n"\\\\]|\\\\.)*"' number = digit+ >s %l; filename = string %{filename = text } ; section = (identifier|string|number) %{section = text} ; format= identifier %{format = text} ; language = identifier %{language = text} ; section_format_language = (section) | (section ws+ format) | (section ws+ format ws+ language) ; include_section = 'include' ws+ filename ws+ section_format_language; export_section = 'export' ws+ section (ws+ language)?; end_section = 'end' ws*; command = include_section >{location = 'include command'} >reset_vars %{ command = :include } | export_section >{location = 'export command'} >reset_vars %{ command = :export } | end_section >{location = 'end command'} %{ command = :end } ; starter = ("###"|'%%%'|'///') . " @"; non_starter = [^#%/] - eol; false_start_hash = '#'{1,2} . ([^#] - eol) ; false_start_percent = '%'{1,2} . ([^%] - eol) ; false_start_fwd = '/'{1,2} . ([^/] - eol) ; followed_by_zlen = ('%'{1,3} | '#' {1,3} | '/' {1,3}) . eol; no_at = ("###"|'%%%'|'///') . ' ' . [^@]; more_than_three = ("####"|'%%%%'|'////'); not_starter = non_starter | false_start_hash | false_start_percent | false_start_fwd | no_at | followed_by_zlen | more_than_three; statement = (ws* . starter . command %process_command) ; junk = (ws* . not_starter . (any - eol)* ) ; line = (statement | junk | zlen) >start_line . eol %finish_line %{ builder.append_line(current_line_text.chomp) unless location =~ /command/ } ; document := line* $!raise_error; }%% %% write data; ### @export "run-machine" def self.run_machine(data, test_mode = false) # puts "Running the state machine with input #{data}..." data += "\n\n" data = data.unpack("c*") if data.is_a?(String) line_counter = 0 line_data = [] builder = Builder.new eof = -1 %% write init; %% write exec; if test_mode [builder, line_data] else builder end end ### @end end end