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