File indexing completed on 2024-11-10 03:30:46
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