File indexing completed on 2024-05-12 05:46:28

0001 #!/usr/bin/ruby
0002 
0003 #  Copyright (C) 2005-2006 by Cies Breijs
0004 #
0005 #  This program is free software; you can redistribute it and/or
0006 #  modify it under the terms of version 2 of the GNU General Public
0007 #  License as published by the Free Software Foundation.
0008 #
0009 #  This program is distributed in the hope that it will be useful,
0010 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
0011 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0012 #  GNU General Public License for more details.
0013 #
0014 #  You should have received a copy of the GNU General Public
0015 #  License along with this program; if not, write to the Free
0016 #  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017 #  Boston, MA 02110-1301, USA.
0018 
0019 
0020 require 'cgi'
0021 
0022 
0023 @type_dict = {
0024         :bool   => ["Bool",   "bool()",   "bool"],
0025         :number => ["Number", "number()", "double"],
0026         :string => ["String", "string()", "const QString&"]
0027 }
0028 
0029 @cat_hash = {}
0030 @total_generated_lines = 0
0031 
0032 def make_headers()
0033         puts "making headers"
0034 
0035         c_warning =
0036 <<EOS
0037 /* The code between the line that start with "//BEGIN GENERATED" and "//END GENERATED"
0038  * is generated by "generate.rb" according to the definitions specified in
0039  * "definitions.rb". Please make all changes in the "definitions.rb" file, since all
0040  * all change you make here will be overwritten the next time "generate.rb" is run.
0041  * Thanks for looking at the code!
0042  */\n
0043 EOS
0044 
0045 
0046         # fills the 'enum Token::Type' in token.h
0047         @token_type_h           = c_warning
0048 
0049         # fills the 'enum Token::Category' in token.h
0050         @token_category_h       = c_warning
0051 
0052         # fills the switch statement in the typeToCategory() method
0053         @token_switch_cpp       = c_warning
0054 
0055         # fills the 'stringType2intType()'
0056         @translator_cpp         = c_warning
0057 
0058         # for in the statement switch
0059         @parser_statements_cpp  = c_warning
0060 
0061         # declaration of the parse* methods
0062         @parser_h               = c_warning
0063 
0064         # definition of the parse* methods
0065         @parser_cpp             = c_warning
0066 
0067         # declaration of the execute* methods
0068         @executer_h             = c_warning
0069 
0070         # fills the signal declaration block in the executer.h
0071         @executer_emits_h       = c_warning
0072 
0073         # definition of the execute* methods
0074         @executer_cpp           = c_warning
0075 
0076         # fills the switch statement in the execute(TreeNode* node) method
0077         @executer_switch_cpp    = c_warning
0078 
0079         # fills the connectAllSlots method of the dummy signal receiver
0080         @echoer_connect_h       = c_warning
0081 
0082         # definition of the slots in the dummy signal receiver
0083         @echoer_slots_h         = c_warning
0084 
0085         # simple includes file that contains the connect statements for the mainwindow
0086         @gui_connect_inc        = c_warning
0087 
0088         # will become the help file generation
0089         @help_docbook           = ''
0090 end
0091 
0092 
0093 
0094 def make_footers()
0095         puts "making footers\n\n"
0096 
0097         # helpfile footer here
0098 end
0099 
0100 
0101 
0102 def new_item()
0103 # reset the variables
0104 
0105 # @type,    the internal name of the item
0106 # @look,    the en_US look for the item
0107 # @ali,     the alias (another en_US look)
0108 # @p_def,   method definition for the parser
0109 # @e_def,   method definition for the executer
0110 # @funct,   the functionality the item should be given
0111 # @cat,     the category the item belongs to
0112 # @help,    .docbook formatted text for in a help file
0113 
0114         @type  = ''
0115         @look  = ''
0116         @localize = true
0117         @ali   = ''
0118         @p_def = ''
0119         @e_def = ''
0120         @emit  = ''
0121         @funct = ''
0122         @cat   = nil
0123         @args  = []
0124         @help  = ''
0125 end
0126 
0127 
0128 def same_args(args)
0129         result = true
0130         for item in @args do
0131                 result = (item == @args[0])
0132         end
0133         return result
0134 end
0135 
0136 
0137 def parse_item()
0138         puts "parsing item '#{@type}'"
0139 
0140         @token_type_h += "\t\t\t#{@type},\n"
0141 
0142         @cat_hash[@type] = @cat if @cat
0143 
0144         unless @look.empty?
0145                 if @localize
0146                         def translate_cpp_string(type, what, look)
0147                                 return <<EOS
0148         localizedCommandLook = ki18nc(
0149                 "You are about to translate the '#{type}' #{what}, there are some rules on how to translate it."
0150                 "Please see http://edu.kde.org/kturtle/translator.php to learn how to properly translate it.",
0151                 "#{look}").toString(localizer);
0152         default2localizedMap["#{look}"] = localizedCommandLook;
0153         look2typeMap[localizedCommandLook] = Token::#{type};
0154 
0155 EOS
0156                         end
0157                         @translator_cpp += translate_cpp_string(@type, 'COMMAND', @look)
0158                         unless @ali.empty?
0159                                 @translator_cpp += translate_cpp_string(@type, 'COMMAND ALIAS', @ali)
0160                         end
0161                 else
0162                         escaped_look = (@look == '"') ? '\"' : @look
0163                         @translator_cpp += "\tlook2typeMap[\"#{escaped_look}\"] = Token::#{@type};\n\n"
0164                 end
0165         end
0166 
0167         if @funct =~ /statement/
0168 
0169                 if @p_def.empty?
0170                         if @args[0] == :none
0171                                 @p_def =
0172 <<EOS
0173         TreeNode* node = new TreeNode(currentToken);
0174         nextToken();
0175         skipToken(Token::EndOfLine, *node->token());
0176         return node;
0177 EOS
0178                         end
0179                 
0180                         if @args[0] != :none and !@args.empty?
0181                                 @p_def =
0182 <<EOS
0183         TreeNode* node = new TreeNode(currentToken);
0184         nextToken();
0185         appendArguments(node);
0186         skipToken(Token::EndOfLine, *node->token());
0187         return node;
0188 EOS
0189                         end
0190                 end
0191 
0192                 if @p_def.empty? # if still empty...
0193                         @p_def = "\treturn 0;  // this is a stud\n"
0194                 end
0195 
0196                 @parser_statements_cpp += "\t\tcase Token::#{@type}".ljust(33) + " : return parse#{@type}();\n"
0197         end
0198 
0199         if !@p_def.empty?
0200                 @parser_h += "\t\tTreeNode* parse#{@type}();\n"
0201                 @p_def =
0202 <<EOS
0203 TreeNode* Parser::parse#{@type}() {
0204 //      qDebug() << "called";
0205 #{@p_def}}
0206 EOS
0207                 @parser_cpp += @p_def
0208         end
0209 
0210         if @funct =~ /node/
0211                 if @funct =~ /constant/
0212                         @executer_switch_cpp += "\t\tcase Token::#{@type}".ljust(33) + " : /* a constant; do nothing */".ljust(37) + "break;\n"
0213                 else
0214                         @executer_switch_cpp += "\t\tcase Token::#{@type}".ljust(33) + " : execute#{@type}(node);".ljust(37) + "break;\n"
0215                         @executer_h += "\t\tvoid execute#{@type}(TreeNode* node);\n"
0216                 end
0217 
0218                 if @e_def.empty? and @args.length() > 0
0219                                 if @args[0] == :none
0220                                         @e_def += "\tif (!checkParameterQuantity(node, 0, 20000+Token::#{@type}*100+90)) return;\n"
0221                                 elsif same_args(@args)
0222                                                 @e_def += "\tif (!checkParameterQuantity(node, #{@args.length()}, 20000+Token::#{@type}*100+90) ||\n\t\t!checkParameterType(node, Value::#{@type_dict[@args[0]][0]}, 20000+Token::#{@type}*100+91)) return;\n"
0223                                 end
0224                 end
0225 
0226                 if @funct =~ /auto-emit/ and @args.length() > 0 and @emit.empty?
0227                         # this build the emit statement for executer.cpp and the signal declaration for the executer.h
0228 
0229                         # find the method name:
0230                         method_name_str = @type[0].chr.downcase + @type[1..-1]
0231 
0232                         # now parse the arguments (if any) into a arguments_str and a e_def_addition:
0233                         arguments_str = ''
0234                         e_def_emit_call_args = ''
0235                         named_arguments_str = ''
0236                         output_arguments_code = ''
0237                         if @args[0] != :none
0238                                 i = 0
0239                                 for arg in @args
0240                                         e_def_emit_call_args += "node->child(#{i})->value()->#{@type_dict[arg][1]}, "
0241                                         arguments_str += @type_dict[arg][2] + ", "
0242                                         named_arguments_str += "#{@type_dict[arg][2]} arg#{i}, "
0243                                         if arg == :string
0244                                                 output_arguments_code += "qPrintable(arg#{i}) << \",\" << "
0245                                         else
0246                                                 output_arguments_code += "arg#{i} << \",\" << "
0247                                         end
0248                                         i = i + 1
0249                                 end
0250                                 e_def_emit_call_args = e_def_emit_call_args[0..-3]  # strip the extra ', '
0251                                 arguments_str = arguments_str[0..-3]
0252                                 named_arguments_str = named_arguments_str[0..-3]
0253                                 output_arguments_code = output_arguments_code[0..-8]
0254                         end
0255 
0256                         @e_def += "\temit #{method_name_str}(#{e_def_emit_call_args});\n"
0257                         @executer_emits_h += "\t\tvoid #{method_name_str}(#{arguments_str});\n"
0258                         @echoer_connect_h += "\t\t\tconnect(executer, SIGNAL(#{method_name_str}(#{arguments_str})),\n\t\t\t\tSLOT(#{method_name_str}(#{arguments_str})));\n"
0259                         @echoer_slots_h   += "\t\tvoid #{method_name_str}(#{named_arguments_str}) { qDebug() << \"SIG> \" << \"#{method_name_str}\" << \"(\" << #{output_arguments_code}\")\"; }\n"
0260                         @gui_connect_inc  += "\tconnect(executer, SIGNAL(#{method_name_str}(#{arguments_str})), \n\t\tcanvas, SLOT(slot#{method_name_str[0..0].upcase+method_name_str[1..-1]}(#{arguments_str})));\n"
0261                 end
0262 
0263                 if @e_def.empty?
0264                         @e_def = "\tnode = node; // stop the warnings // this is a stud\n"
0265                 end
0266 
0267                 unless @funct =~ /constant/
0268                         @executer_cpp +=
0269 <<EOS
0270 void Executer::execute#{@type}(TreeNode* node) {
0271 //      qDebug() << "called";
0272 #{@e_def}}
0273 EOS
0274                 end
0275         end
0276 
0277 end
0278 
0279 
0280 
0281 def write_files(diff)
0282         if diff
0283                 puts "diff'ing files (not writing):\n\n"
0284         else
0285                 puts "writing files:\n\n"
0286         end
0287 
0288         @cat_hash.values.uniq.each do |v1|
0289                 @token_category_h += "\t\t\t#{v1}Category,\n"
0290                 @cat_hash.each_pair do |k,v2|
0291                         if v2 == v1
0292                                 @token_switch_cpp += "\t\tcase #{k}:\n"
0293                         end
0294                 end
0295                 @token_switch_cpp += "\t\t\treturn #{v1}Category;\n\n"
0296         end
0297 
0298         parse_and_write("./token.h", @token_type_h[0..-3]+"\n", "token_type_h", diff);
0299         parse_and_write("./token.h", @token_category_h[0..-3]+"\n", "token_category_h", diff);
0300         parse_and_write("./token.cpp", @token_switch_cpp, "token_switch_cpp", diff);
0301         parse_and_write("./translator.cpp", @translator_cpp, "translator_cpp", diff);
0302         parse_and_write("./parser.h", @parser_h, "parser_h", diff);
0303         parse_and_write("./parser.cpp", @parser_cpp, "parser_cpp", diff);
0304         parse_and_write("./parser.cpp", @parser_statements_cpp, "parser_statements_cpp", diff);
0305         parse_and_write("./executer.h", @executer_h, "executer_h", diff);
0306         parse_and_write("./executer.h", @executer_emits_h, "executer_emits_h", diff);
0307         parse_and_write("./executer.cpp", @executer_cpp, "executer_cpp", diff);
0308         parse_and_write("./executer.cpp", @executer_switch_cpp, "executer_switch_cpp", diff);
0309         parse_and_write("./echoer.h", @echoer_connect_h, "echoer_connect_h", diff);
0310         parse_and_write("./echoer.h", @echoer_slots_h, "echoer_slots_h", diff);
0311         parse_and_write("./gui_connect.inc", @gui_connect_inc, "gui_connect_inc", diff);
0312         #          write("./?.docbook", @help_docbook);
0313         #          write("./?.xml", @highlighttheme);
0314 end
0315 
0316 
0317 
0318 def parse_and_write(file_name, string, identifier, diff)
0319         string.each_line { @total_generated_lines += 1 }
0320 
0321         puts "parsing '#{file_name}'"
0322 
0323         file = File.new(file_name)
0324 
0325         rx_begin = Regexp.new("^//BEGIN GENERATED " + identifier + " CODE")
0326         rx_end   = Regexp.new("^//END GENERATED "   + identifier + " CODE")
0327         parsed = ""
0328         g = false  # true if we're in the parse scope
0329         i = 0      # line count
0330         file.each_line { |line|
0331                 i += 1
0332 
0333                 if line =~ rx_end
0334                         puts "found '//END GENERATED #{identifier} CODE' in '" + file_name + "' on line " + i.to_s
0335                         parsed += "\n" + string + "\n"
0336                         g = false
0337                 end
0338 
0339                 if !g
0340                         parsed += line
0341                 end
0342 
0343                 if line =~ rx_begin
0344                         puts "found '//BEGIN GENERATED #{identifier} CODE' in '" + file_name + "' on line " + i.to_s
0345                         g = true
0346                 end
0347         }
0348         file.close()
0349         write(file_name, parsed, diff)
0350 end
0351 
0352 
0353 
0354 def write(file_name, string, diff)
0355         if same(file_name, string)
0356                 if diff
0357                         puts "nothing has changed between '#{file_name}' and what is in the definitions\n\n"
0358                 else
0359                         puts "not writing to '#{file_name}', nothing has changed\n\n"
0360                 end
0361         else
0362                 if diff
0363                         puts "##### differences between the definition and '#{file_name}' are:\n"
0364                         difference(file_name, string)
0365                         puts "##### end of diff\n\n"
0366                 else
0367                         puts "writing '#{file_name}'\n\n"
0368                 
0369                         file = File.new(file_name, 'w')
0370                         file.write(string)
0371                         file.close()
0372                 end
0373         end
0374 end
0375 
0376 def same(file_name, string)
0377         file = File.new(file_name)
0378         result = (file.read() == string)
0379         file.close()
0380         return result
0381 end
0382 
0383 def difference(file_name, string)
0384         tmp_name = ".generate.tmp"
0385         gen_file = File.new(tmp_name, "w")
0386         gen_file.write(string)
0387         gen_file.close()
0388 
0389         system("diff #{file_name} #{tmp_name}")
0390 end
0391 
0392 
0393 puts <<EOS
0394 
0395         Code and documentation generating script for the interpreter of KTurtle
0396         by Cies Breijs
0397         
0398         This script, "generate.rb", makes sure the definitions, as stored in
0399         "definitions.rb", are parsed into the code of the interpreter.
0400         This scripts is only used by developers, and should not be
0401         distributed in binary packages.\n
0402         All changes to the definitions should be made in "definitions.rb".\n
0403         Use the 'diff' command line option (./generate.rb diff) to see the
0404         differences.\n
0405         For more information on the workings see the source of "generate.rb".
0406 \n
0407 EOS
0408 
0409 make_headers()
0410 require('./definitions.rb') # here the definitions of the commands are stored
0411 make_footers()
0412 
0413 
0414 for arg in ARGV
0415         if arg =~ /diff/
0416                 write_files(true)
0417                 exit
0418         end
0419 end
0420 write_files(false)
0421 
0422 puts "\nTotal generated lines of code: #{@total_generated_lines.to_s}\n\n"
0423