File indexing completed on 2024-12-01 06:36:20

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