#! /usr/bin/env ruby require "getoptlong" ## # IFace data stucture # class IFace attr_accessor :cat def initialize @cat = [] changeCat(nil) end def changeCat(_name) @currentCat = IFaceCat.new(_name) @cat << @currentCat end def addEmpty @currentCat.addEntry(IFaceEmpty.new) end def addComment(_text) @currentCat.addEntry(IFaceComment.new(_text)) end def addVal(_name, _code) @currentCat.addEntry(IFaceVal.new(_name, _code)) end def addFun(_name, _code, _return, _args) @currentCat.addEntry(IFaceFun.new(_name, _code, _return, _args)) end def addGet(_name, _code, _return, _args) @currentCat.addEntry(IFaceGet.new(_name, _code, _return, _args)) end def addSet(_name, _code, _return, _args) @currentCat.addEntry(IFaceSet.new(_name, _code, _return, _args)) end def addEvt(_name, _code, _return, _args) @currentCat.addEntry(IFaceEvt.new(_name, _code, _return, _args)) end end class IFaceCat attr_reader :name, :entries def initialize(_name) @name = _name @entries = [] end def addEntry(_entry) @entries << _entry end end class IFaceEmpty def accept(visitor) visitor.visitIFaceEmpty(self) end end class IFaceComment attr_reader :text def initialize(_text) @text = _text end def accept(visitor) visitor.visitIFaceComment(self) end end class IFaceVal attr_reader :name, :code def initialize(_name, _code) @name = _name @code = _code end def accept(visitor) visitor.visitIFaceVal(self) end end class IFaceFunArg attr_reader :name, :type def initialize(_name, _type) @name = _name @type = _type end end class IFaceFun < IFaceVal attr_reader :return, :args def initialize(_name, _code, _return, _args) super(_name, _code) @return = _return @args = [] _args.each do |arg| @args << IFaceFunArg.new(arg[1], arg[0]) end end def accept(visitor) visitor.visitIFaceFun(self) end end class IFaceGet < IFaceFun def initialize(_name, _code, _return, _args) super end def accept(visitor) visitor.visitIFaceGet(self) end end class IFaceSet < IFaceFun def initialize(_name, _code, _return, _args) super end def accept(visitor) visitor.visitIFaceSet(self) end end class IFaceEvt < IFaceFun def initialize(_name, _code, _return, _args) super end def accept(visitor) visitor.visitIFaceEvt(self) end end ## # IFace parser # class IFaceParser def initialize(_input, _output) @input = _input @output = _output @iface = IFace.new end def process @input.each_line do |line| case line when /^##/ when /^$/ processEmpty when /^#(.*)/ processComment $1 else command, args = line.split(/\s+/, 2) processCommand(command, args) end end @iface end def processEmpty @iface.addEmpty end def processComment(_comment) @iface.addComment(_comment) end def processCommand(_cmd, _args) unless _cmd.empty? process = "process" + _cmd[0..0].upcase + _cmd[1..-1] if respond_to?(process) method(process).call(_args) else puts "*** Unknown cmd: #{_cmd} #{_args}" end end end def processCat(_cat) @iface.changeCat(_cat) end def processVal(_val) name, value = _val.split(/\s*=\s*/) @iface.addVal(name, value) end def parseFun(_fun) returnType, _fun = _fun.split(/\s+/, 2) name, _fun = _fun.split(/\s*=\s*/, 2) _fun =~ /(\d+)\s*\((.*)\)/ code = $1 _fun = $2.split(/\s*,\s*/, -1) args = _fun.collect do |arg| argType, argName = arg.split argType, argName = nil, nil if argName == "" [argType, argName] end [name, code, returnType, args] end def processFun(_fun) name, code, returnType, args = parseFun(_fun) @iface.addFun(name, code, returnType, args) end def processGet(_fun) name, code, returnType, args = parseFun(_fun) @iface.addGet(name, code, returnType, args) end def processSet(_fun) name, code, returnType, args = parseFun(_fun) @iface.addSet(name, code, returnType, args) end def processEvt(_fun) name, code, returnType, args = parseFun(_fun) @iface.addEvt(name, code, returnType, args) end def processEnu(_enu) end def processLex(_lex) end end ## # Code to generate Scintilla.rb from an IFace data structure # class ScintillaIFaceToRuby def initialize(_iface, _output) @iface = _iface @output = _output @reserved = Hash["end", "last", "setFocus", "setFocusFlag"] end def generateHeader @output.puts("# This file is automatically generated from Scintilla.iface") @output.puts("# DO NOT MODIFY") @output.puts end def generate generateHeader @output.puts("module Fox") @output.puts(" class FXScintilla") @iface.cat.each do |cat| @output.puts(" # #{cat.name}") cat.entries.each do |entry| entry.accept(self) end end @output.puts(" end") @output.puts("end") end def visitIFaceEmpty(_empty) @output.puts end def visitIFaceComment(_comment) @output.puts(" ##{_comment.text}") end def visitIFaceVal(_val) return if _val.name == "KeyMod" return if _val.name == "Lexer" @output.puts(" #{name(_val.name)} = #{_val.code}") end def visitIFaceFun(_fun) stringresult = _fun.args[1].type == "stringresult" and _fun.return == "int" stringresult1 = (stringresult and _fun.args[0].name == nil) name = name(_fun.name[0..0].downcase + _fun.name[1..-1]) @output.print(" def #{name}") args = _fun.args.collect do |arg| if stringresult and arg.type == "stringresult" nil else name(arg.name) end end args.compact! @output.print("(#{args.join(', ')})") unless args.empty? @output.puts if stringresult and !stringresult1 @output.puts(" buffer = \"\".ljust(#{_fun.args[0].name})") end returnValue = "sendMessage(#{_fun.code}" _fun.args.each do |arg| if stringresult and !stringresult1 and arg.type == "stringresult" returnValue += ", buffer" else returnValue += ", #{arg.name ? typeArg(arg.type, name(arg.name)) : 0}" end end returnValue += ")" if stringresult and !stringresult1 @output.puts(" #{returnValue}") @output.puts(" buffer") else @output.puts(" #{typeRet(_fun.return, returnValue)}") end @output.puts(" end") end def visitIFaceGet(_get) visitIFaceFun(_get) end def visitIFaceSet(_set) visitIFaceFun(_set) end def visitIFaceEvt(_evt) name = "SCN_" + _evt.name.upcase @output.puts(" #{name} = #{_evt.code}") end def typeArg(_type, _value) case _type when "colour" "#{_value} & 0xffffff" else _value end end def typeRet(_type, _value) case _type when "bool" "#{_value} == 1 ? true : false" else _value end end def name(_name) name = @reserved[_name] name ? name : _name end end def main options = GetoptLong.new(["-i", GetoptLong::REQUIRED_ARGUMENT], ["-o", GetoptLong::REQUIRED_ARGUMENT]) input = output = nil options.each do |opt, arg| case opt when "-i" input = File.open(arg, File::RDONLY) when "-o" output = File.open(arg, File::CREAT|File::WRONLY|File::TRUNC) end end input = $stdin unless input output = $stdout unless output parser = IFaceParser.new(input, output) iface = parser.process gen = ScintillaIFaceToRuby.new(iface, output) gen.generate ensure input.close output.close end main