#!/usr/bin/ruby def error(msg) puts "ERROR: #{msg}" exit 0 end TokenTypes = { :blackspace => /\S+/, :whitespace => /\s+/ } BasicTokens = { :open_brace => /{/, :closed_brace => /}/, :open_paren => /\(/, :closed_paren => /\)/, :open_bracket => /\[/, :closed_bracket => /\]/, :comma => /,/, :dot => /\./, :double_quote => /\"/, :single_quote => /\'/, :plus => /\+/, :minus => /\-/, :mult => /\*/, :div => /\//, :number => /[0-9]+/, :colon => /:/, :semicolon => /;/, :text => /\S/, :whitespace => /\s+/, :doubleq => /\"/, } KeywordTokens = { :intf_decl => /@interface/, :impl_decl => /@implementation/, :end => /@end/, :property => /@property/, :synthezize => /@synthesize/, } class Token attr_reader :str attr_accessor :type attr_accessor :matcher def initialize(str, hash) @matcher = Matcher.new(hash) @str, @type = str, @matcher.match(str) end def to_s "#{@type}: #{@str}" end def combine(token) @str << token.str end def match_with(hash) return self unless Token.new(str, hash).matcher.match(@str) Token.new(str, hash) end end class Matcher def initialize(hash) @hash = hash end def match(val) @hash.each {|k,v| return k if val =~ v } return nil end end class ModeMatcher def initialize(hash) @hash = hash end def match(val) @hash.each {|k,v| return k if val == v } return nil end end Modes = { :intf_decl => :intf_decl, :impl_decl => :impl_decl, :end => :end, } class VarDecl def initialize(type, name) @type = type @name = name end def to_s "#{@type} #{@name};" end end class VariableDecl def initialize(stream) @stream = stream @modes = [:type, :var, :semicolon] @mode = :type end def new_from(type, vars) vars.map { |v| VarDecl.new(type, v) } end def parse vars = [] until @stream.empty? token = @stream.shift @mode = token.type if token.type == :semicolon case @mode when :type type = token @mode = var when :var vars << token when :semicolon return [VariableDecl.new_from(type, vars), @stream] end end end end class InterfaceDeclHeader def initialize(name, superclass) @name, @superclass = name, superclass @type = :intf_decl_header @expect = [:intf_struct_member, :intf_property, :intf_method] @microexpect = { :open_brace => :intf_struct_member, :kw_property => :intf_property, :minus => :intf_method, :plus => :class_method, :end => :any, } end def self.new_from(par) first = par.shift name = par.shift par.shift superclass = par.shift InterfaceDeclHeader.new(name, superclass) end def parse(stream) token = stream.shift expected = @microexpect[token] end end class InterfaceStructMember def initialize(type, name) @vartype, @name = vartype, name @type = :intf_struct_member @expect = [:intf_struct_member, :intf_property, :intf_method] end end class InterfaceMethod def initialize(return_type, name, args) @return_type, @name, @args = return_type, name, args @type = :intf_method @expect [:intf_method, :end] end end class InterfaceDecl def initialize(name, superclass) @name, @superclass = name, superclass end def self.new_from(args) first = args.shift puts first if first != "@interface" name = args.shift args.shift superclass = args.shift InterfaceDecl.new(name, superclass) end def to_s "@interface #{@name.str} : #{@superclass.str}" end end class ModeSwitch def initialize(state) @state = state end def switch(mode) @state.mode = mode if @state.modes.include? mode end end class SyntaxTape attr_accessor :processed attr_accessor :remainder def initialize(processed, remainder) @processed, @remainder = processed, remainder end def process(parser) #this should return yet another syntaxtape parser.parse(@remainder) end end class StateMachineA attr_accessor :mode attr_reader :modes def initialize(stream) @stream = stream @modes = [:intf_decl, :impl_decl, :end] @mode = nil end def parse matcher = ModeSwitch.new(self) parameters = [] class_type = nil until @stream.empty? token = @stream.shift matcher.switch(token.type) case @mode when :intf_decl class_type = InterfaceDeclHeader parameters << token unless token.type == :whitespace when :impl_decl class_type = ImplDeclHeader parameters << token unless token.type == :whitespace when :end parameters << token return SyntaxTape.new(class_type.new_from(parameters), @stream) else puts "unknown state: #{@mode}" end end end end class Lexer include Enumerable def keywords [/@interface/] end def initialize(file) error("expected input file") if file.nil? @source = File.read(file) end def each(&block) @source.each_char(&block) end def blackmap self.map {|c| Token.new(c, c=~/\s/ ? :whitespace : :blackspace) } end def matchmap tokens = self.map {|c| Token.new(c, BasicTokens) } bleed = 0 tokens.filter_map.with_index {|token, idx| #skips iterations that would result in repeated segments of string if bleed != 0 bleed -= 1 next end next token unless token.type == :text #combine text into single token while tokens[idx += 1].type == :text token.combine(tokens[idx]) bleed += 1 next end next token }.map { |token| token.match_with(KeywordTokens) } end def each_token token = "" end end source = Lexer.new(ARGV[0]) a = StateMachineA.new source.matchmap tape = a.parse puts tape.processed