001/* A visitor for parse trees of the expression language that infers types. 002 003 Copyright (c) 1998-2018 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 025 */ 026package ptolemy.data.expr; 027 028import java.lang.reflect.Field; 029import java.lang.reflect.Method; 030import java.lang.reflect.Modifier; 031import java.util.Arrays; 032import java.util.HashMap; 033import java.util.HashSet; 034import java.util.Iterator; 035import java.util.List; 036import java.util.Map; 037import java.util.Set; 038 039import ptolemy.data.ScalarToken; 040import ptolemy.data.StringToken; 041import ptolemy.data.type.ArrayType; 042import ptolemy.data.type.BaseType; 043import ptolemy.data.type.FixType; 044import ptolemy.data.type.FunctionType; 045import ptolemy.data.type.MatrixType; 046import ptolemy.data.type.ObjectType; 047import ptolemy.data.type.RecordType; 048import ptolemy.data.type.Type; 049import ptolemy.data.type.TypeConstant; 050import ptolemy.data.type.TypeLattice; 051import ptolemy.graph.InequalityTerm; 052import ptolemy.kernel.CompositeEntity; 053import ptolemy.kernel.Entity; 054import ptolemy.kernel.util.IllegalActionException; 055import ptolemy.kernel.util.InternalErrorException; 056import ptolemy.kernel.util.NamedObj; 057import ptolemy.math.Precision; 058 059/////////////////////////////////////////////////////////////////// 060//// ParseTreeTypeInference 061 062/** 063 This class visits parse trees and infers a type for each node in the 064 parse tree. This type is stored in the parse tree. 065 066 @author Steve Neuendorffer 067 @version $Id$ 068 @since Ptolemy II 2.1 069 @Pt.ProposedRating Green (neuendor) 070 @Pt.AcceptedRating Yellow (neuendor) 071 @see ptolemy.data.expr.ASTPtRootNode 072 */ 073public class ParseTreeTypeInference extends AbstractParseTreeVisitor { 074 /////////////////////////////////////////////////////////////////// 075 //// public methods //// 076 077 /** Infer the type of the parse tree with the specified root node. 078 * @param node The root of the parse tree. 079 * @return The result of evaluation. 080 * @exception IllegalActionException If an evaluation error occurs. 081 */ 082 public Type inferTypes(ASTPtRootNode node) throws IllegalActionException { 083 node.visit(this); 084 return _inferredChildType; 085 } 086 087 /** Infer the type of the parse tree with the specified root node using 088 * the specified scope to resolve the values of variables. 089 * @param node The root of the parse tree. 090 * @param scope The scope for evaluation. 091 * @return The result of evaluation. 092 * @exception IllegalActionException If an error occurs during 093 * evaluation. 094 */ 095 public Type inferTypes(ASTPtRootNode node, ParserScope scope) 096 throws IllegalActionException { 097 _scope = scope; 098 node.visit(this); 099 _scope = null; 100 return _inferredChildType; 101 } 102 103 /** Set the type of the given node to be an ArrayType that is the 104 * least upper bound of the types of the node's children. 105 * @param node The specified node. 106 * @exception IllegalActionException If an inference error occurs. 107 */ 108 @Override 109 public void visitArrayConstructNode(ASTPtArrayConstructNode node) 110 throws IllegalActionException { 111 Type[] childTypes = _inferAllChildren(node); 112 113 _setType(node, 114 new ArrayType( 115 (Type) TypeLattice.lattice().leastUpperBound( 116 new HashSet<Type>(Arrays.asList(childTypes))), 117 childTypes.length)); 118 } 119 120 /** Set the type of the given node to be the type that is the 121 * least upper bound of the types of the node's children. 122 * @param node The specified node. 123 * @exception IllegalActionException If an inference error occurs. 124 */ 125 @Override 126 public void visitBitwiseNode(ASTPtBitwiseNode node) 127 throws IllegalActionException { 128 Type[] childTypes = _inferAllChildren(node); 129 130 // FIXME: not consistent with expression evaluator. 131 _setType(node, (Type) TypeLattice.lattice() 132 .leastUpperBound(new HashSet<Type>(Arrays.asList(childTypes)))); 133 } 134 135 /** Set the type of the given node to be the return type of the 136 * function determined for the given node. 137 * @param node The specified node. 138 * @exception IllegalActionException If an inference error occurs. 139 */ 140 @Override 141 public void visitFunctionApplicationNode(ASTPtFunctionApplicationNode node) 142 throws IllegalActionException { 143 int argCount = node.jjtGetNumChildren() - 1; 144 final String functionName = node.getFunctionName(); 145 146 // Get the child types. 147 Type[] childTypes = new Type[argCount]; 148 149 for (int i = 0; i < argCount; i++) { 150 childTypes[i] = _inferChild(node, i + 1); 151 152 if (childTypes[i] == null) { 153 throw new RuntimeException("node " + node + " has null type."); 154 } 155 } 156 157 Type type = null; 158 Type baseType = null; 159 160 if (_scope != null && functionName != null) { 161 type = _scope.getType(functionName); 162 if (!(type instanceof ObjectType)) { 163 // Pretend that we cannot resolve the type if it is an 164 // ObjectType. 165 baseType = type; 166 } 167 } 168 169 if (baseType != null || functionName == null) { 170 baseType = _inferChild(node, 0); 171 172 // Handle as an array or matrix index into a named 173 // variable reference. 174 if (baseType instanceof FunctionType) { 175 _setType(node, ((FunctionType) baseType).getReturnType()); 176 return; 177 } else if (argCount == 1) { 178 if (baseType instanceof ArrayType) { 179 _setType(node, ((ArrayType) baseType).getElementType()); 180 return; 181 } else { 182 // Child node is not an array, but it can be 183 // losslessly converted to an array (anything can be). 184 // Note: parse tree evaluator also support 185 // constructs like (1)(0), where the constant 1 186 // gets converted automatically to an array. 187 ASTPtRootNode child = (ASTPtRootNode) node.jjtGetChild(0); 188 _setType(child, new ArrayType(baseType)); 189 return; 190 } 191 } else if (argCount == 2) { 192 if (baseType instanceof MatrixType) { 193 _setType(node, ((MatrixType) baseType).getElementType()); 194 return; 195 } else { 196 // Child node is not a matrix, but it might be 197 // losslessly convertible to a matrix. Attempt to do that. 198 // Note: parse tree evaluator also support 199 // constructs like (1)(0,0), where the constant 1 200 // gets converted automatically to a matrix. 201 ASTPtRootNode child = (ASTPtRootNode) node.jjtGetChild(0); 202 _setType(child, 203 MatrixType.getMatrixTypeForElementType(baseType)); 204 return; 205 } 206 } 207 208 throw new IllegalActionException("Wrong number of indices " 209 + "when referencing function \"" + functionName 210 + "\". The number of indices was " + argCount 211 + ". For arrays, the number of indices should be " 212 + "1. For matrices, the number of indices should be 2. " 213 + " The type of the function was \"" + baseType + "\"."); 214 } 215 216 // Note that an alternative to searching for functions by name 217 // is to define fooReturnType() in data.expr.UtilityFunctions 218 // so that fooReturnType() is found by CacheMethod. 219 // Psuedo-temporary hack for casts.... 220 if (functionName.compareTo("cast") == 0 && argCount == 2) { 221 ASTPtRootNode castTypeNode = (ASTPtRootNode) node 222 .jjtGetChild(0 + 1); 223 ParseTreeEvaluator parseTreeEvaluator = new ParseTreeEvaluator(); 224 225 try { 226 ptolemy.data.Token t = parseTreeEvaluator 227 .evaluateParseTree(castTypeNode, _scope); 228 _setType(node, t.getType()); 229 } catch (IllegalActionException ex) { 230 _setType(node, childTypes[0]); 231 } 232 233 return; 234 235 // Note: We used to just do this, but in some case is it 236 // useful to have functions which are type constructors... 237 // Hence the above code. 238 // _setType(node, 239 // ((ASTPtRootNode) node.jjtGetChild(0 + 1)).getType()); 240 // return; 241 } 242 243 // A hack, because the result of the 'fix' function is 244 // dependent on its arguments, which should be constant. 245 if (functionName.compareTo("fix") == 0 && argCount == 3) { 246 ASTPtRootNode lengthNode = (ASTPtRootNode) node.jjtGetChild(1 + 1); 247 ASTPtRootNode integerBitsNode = (ASTPtRootNode) node 248 .jjtGetChild(2 + 1); 249 ParseTreeEvaluator parseTreeEvaluator = new ParseTreeEvaluator(); 250 251 try { 252 ptolemy.data.Token length = parseTreeEvaluator 253 .evaluateParseTree(lengthNode, _scope); 254 255 ptolemy.data.Token integerBits = parseTreeEvaluator 256 .evaluateParseTree(integerBitsNode, _scope); 257 _setType(node, 258 new FixType(new Precision( 259 ((ScalarToken) length).intValue(), 260 ((ScalarToken) integerBits).intValue()))); 261 return; 262 } catch (Throwable throwable) { 263 // Do nothing... rely on the regular method resolution 264 // to generate the right type. 265 } 266 } 267 268 if (functionName.compareTo("eval") == 0) { 269 // We can't infer the type of eval expressions... 270 _setType(node, BaseType.GENERAL); 271 return; 272 } 273 274 if (functionName.compareTo("matlab") == 0) { 275 // We can't infer the type of matlab expressions... 276 _setType(node, BaseType.GENERAL); 277 return; 278 } 279 280 if (functionName.compareTo("fold") == 0) { 281 if (argCount == 3) { 282 if (childTypes[0] instanceof FunctionType) { 283 FunctionType function = (FunctionType) childTypes[0]; 284 if (function.getArgCount() != 2) { 285 throw new IllegalActionException("The first argument " 286 + "to the function \"fold\" must be a function " 287 + "that accepts two arguments."); 288 } 289 if (!function.getArgType(0).isCompatible(childTypes[1])) { 290 throw new IllegalActionException("The second " 291 + "argument of the function \"fold\" is not " 292 + "compatible with the first parameter to the " 293 + "function provided to \"fold\"."); 294 } 295 // Do not check the type of the second argument of the 296 // function, because if the collection is a Java collection, 297 // then it is impossible to infer the types of the elements 298 // in it without actually knowing the collection. 299 _setType(node, function.getReturnType()); 300 return; 301 } 302 } 303 304 throw new IllegalActionException("The function \"fold\" is " 305 + "a higher-order function that takes exactly 3 " 306 + "arguments. The first argument must be a function " 307 + "that takes 2 arguments. The second must be a value " 308 + "that can be passed to the function as its first " 309 + "argument. The third must be a list of values that " 310 + "can be passed to the function as its second " 311 + "argument."); 312 } 313 314 if (functionName.equals("object") && argCount == 1) { 315 ASTPtRootNode classNameNode = (ASTPtRootNode) node.jjtGetChild(1); 316 if (classNameNode instanceof ASTPtLeafNode) { 317 ptolemy.data.Token token = ((ASTPtLeafNode) classNameNode) 318 .getToken(); 319 if (token != null && token instanceof StringToken) { 320 String className = ((StringToken) token).stringValue(); 321 try { 322 Class clazz = Class.forName(className); 323 _setType(node, new ObjectType(clazz)); 324 return; 325 } catch (ClassNotFoundException e) { 326 throw new IllegalActionException( 327 "Unable to load class " + className); 328 } 329 } else if (token == null) { 330 _setType(node, new ObjectType(Object.class)); 331 return; 332 } 333 } 334 } 335 336 // Otherwise, try to reflect the method name. 337 CachedMethod cachedMethod; 338 339 try { 340 cachedMethod = CachedMethod.findMethod(functionName, childTypes, 341 CachedMethod.FUNCTION); 342 } catch (Exception ex) { 343 // Deal with what happens if the method is not found. 344 // FIXME: hopefully this is monotonic??? 345 _setType(node, BaseType.UNKNOWN); 346 return; 347 } 348 349 if (cachedMethod.isValid()) { 350 baseType = cachedMethod.getReturnType(); 351 _setType(node, baseType); 352 return; 353 } 354 355 if (type instanceof ObjectType) { 356 // If it is ObjectType, set it here. 357 _setType(node, type); 358 return; 359 } 360 361 // If we reach this point it means the function was not found on the 362 // search path. 363 StringBuffer buffer = new StringBuffer(); 364 365 for (int i = 0; i < childTypes.length; i++) { 366 if (i == 0) { 367 buffer.append(childTypes[i].toString()); 368 } else { 369 buffer.append(", " + childTypes[i].toString()); 370 } 371 } 372 373 throw new IllegalActionException( 374 "No matching function " + functionName + "( " + buffer + " )."); 375 } 376 377 /** Set the type of the given node to be a function type whose 378 * argument types are determined by the children of the node. 379 * The return type of the function type is determined by 380 * inferring the type of function's expression in a scope that 381 * adds identifiers for each argument to the current scope. 382 * @param node The specified node. 383 * @exception IllegalActionException If an inference error occurs. 384 */ 385 @Override 386 public void visitFunctionDefinitionNode(ASTPtFunctionDefinitionNode node) 387 throws IllegalActionException { 388 final Map map = new HashMap(); 389 390 for (int i = 0; i < node._argTypes.length; i++) { 391 map.put(node.getArgumentNameList().get(i), 392 node.getArgumentTypes()[i]); 393 } 394 395 // Push the current scope. 396 final ParserScope currentScope = _scope; 397 ParserScope functionScope = new ParserScope() { 398 @Override 399 public ptolemy.data.Token get(String name) { 400 return null; 401 } 402 403 @Override 404 public Type getType(String name) throws IllegalActionException { 405 Type type = (Type) map.get(name); 406 407 if (type == null && currentScope != null) { 408 return currentScope.getType(name); 409 } else { 410 return type; 411 } 412 } 413 414 @Override 415 public InequalityTerm getTypeTerm(String name) 416 throws IllegalActionException { 417 Type type = (Type) map.get(name); 418 419 if (type == null && currentScope != null) { 420 return currentScope.getTypeTerm(name); 421 } else { 422 return new TypeConstant(type); 423 } 424 } 425 426 @Override 427 public Set identifierSet() throws IllegalActionException { 428 Set set = currentScope.identifierSet(); 429 set.addAll(map.keySet()); 430 return set; 431 } 432 }; 433 434 _scope = functionScope; 435 node.getExpressionTree().visit(this); 436 437 Type returnType = _inferredChildType; 438 FunctionType type = new FunctionType(node._argTypes, returnType); 439 _setType(node, type); 440 _scope = currentScope; 441 return; 442 } 443 444 /** Set the type of the given node to be the least upper bound of 445 * the types of the two branches of the if. 446 * @param node The specified node. 447 * @exception IllegalActionException If an inference error occurs. 448 */ 449 @Override 450 public void visitFunctionalIfNode(ASTPtFunctionalIfNode node) 451 throws IllegalActionException { 452 Type conditionalType = _inferChild(node, 0); 453 454 if (conditionalType != BaseType.BOOLEAN) { 455 throw new IllegalActionException( 456 "Functional-if must branch on a boolean, " 457 + "but instead type was " + conditionalType); 458 } 459 460 Type trueType = _inferChild(node, 1); 461 Type falseType = _inferChild(node, 2); 462 463 _setType(node, (Type) TypeLattice.lattice().leastUpperBound(trueType, 464 falseType)); 465 } 466 467 /** Set the type of the given node to be the type of constant the 468 * variable refers to, if the node represents a constant, or the 469 * type of the identifier the node refers to in the current 470 * scope. 471 * @param node The specified node. 472 * @exception IllegalActionException If an inference error 473 * occurs, or an identifier is not bound in the current scope. 474 */ 475 @Override 476 public void visitLeafNode(ASTPtLeafNode node) 477 throws IllegalActionException { 478 if (node.isConstant() && node.isEvaluated()) { 479 _setType(node, node.getToken().getType()); 480 return; 481 } 482 483 String name = node.getName(); 484 485 Type type = null; 486 if (_scope != null) { 487 type = _scope.getType(name); 488 489 if (type != null && !(type instanceof ObjectType)) { 490 // Pretend that we cannot resolve the type if it is an 491 // ObjectType. 492 _setType(node, type); 493 return; 494 } 495 } 496 497 // Look up for constants. 498 if (Constants.get(name) != null) { 499 // A named constant that is recognized by the parser. 500 _setType(node, Constants.get(name).getType()); 501 return; 502 } 503 504 if (type != null) { 505 _setType(node, type); 506 return; 507 } 508 509 throw new IllegalActionException("The ID " + name + " is undefined."); 510 } 511 512 /** Set the type of the given node to be boolean. 513 * @param node The specified node. 514 * @exception IllegalActionException If an inference error occurs. 515 */ 516 @Override 517 public void visitLogicalNode(ASTPtLogicalNode node) 518 throws IllegalActionException { 519 _inferAllChildren(node); 520 521 // FIXME: check arguments are valid? 522 _setType(node, BaseType.BOOLEAN); 523 } 524 525 /** Set the type of the given node to be an MatrixType based on the 526 * least upper bound of the types of the node's children. 527 * @param node The specified node. 528 * @exception IllegalActionException If an inference error occurs. 529 */ 530 @Override 531 public void visitMatrixConstructNode(ASTPtMatrixConstructNode node) 532 throws IllegalActionException { 533 Type[] childTypes = _inferAllChildren(node); 534 535 Type elementType = (Type) TypeLattice.lattice() 536 .leastUpperBound(new HashSet<Type>(Arrays.asList(childTypes))); 537 538 Type matrixType = MatrixType.getMatrixTypeForElementType(elementType); 539 _setType(node, matrixType); 540 } 541 542 /** Set the type of the given node to be the return type of the 543 * method determined for the given node. 544 * @param node The specified node. 545 * @exception IllegalActionException If an inference error occurs. 546 */ 547 @Override 548 public void visitMethodCallNode(ASTPtMethodCallNode node) 549 throws IllegalActionException { 550 551 Type[] childTypes = _inferAllChildren(node); 552 553 // Handle indexing into a record. 554 if (childTypes.length == 1 && childTypes[0] instanceof RecordType) { 555 RecordType type = (RecordType) childTypes[0]; 556 557 if (type.labelSet().contains(node.getMethodName())) { 558 _setType(node, type.get(node.getMethodName())); 559 return; 560 } 561 } 562 563 // Fix to Kepler bug #6629: 564 // Depending on the order in which type constraints are satisfied, 565 // some child types may remain UNKNOWN for one or more iterations 566 // of Mogensen's algorithm. Hence, keep the type of the node 567 // to UNKNOWN until the types for the children are resolved. 568 if (childTypes.length == 1 && childTypes[0] == BaseType.UNKNOWN) { 569 _setType(node, BaseType.UNKNOWN); 570 } else { 571 _setType(node, _methodCall(node.getMethodName(), childTypes)); 572 } 573 } 574 575 /** Set the type of the given node to be the type of the first 576 * child of the given node. 577 * @param node The specified node. 578 * @exception IllegalActionException If an inference error occurs. 579 */ 580 @Override 581 public void visitPowerNode(ASTPtPowerNode node) 582 throws IllegalActionException { 583 Type[] childTypes = _inferAllChildren(node); 584 585 // FIXME: Check that exponents are valid?? 586 Type baseType = childTypes[0]; 587 _setType(node, baseType); 588 } 589 590 /** Set the type of the given node to be the least upper bound 591 * type of the types of the node's children. 592 * @param node The specified node. 593 * @exception IllegalActionException If an inference error occurs. 594 */ 595 @Override 596 public void visitProductNode(ASTPtProductNode node) 597 throws IllegalActionException { 598 Type[] childTypes = _inferAllChildren(node); 599 600 List lexicalTokenList = node.getLexicalTokenList(); 601 int numChildren = node.jjtGetNumChildren(); 602 603 Type resultType = childTypes[0]; 604 for (int i = 1; i < numChildren; i++) { 605 Token operator = (Token) lexicalTokenList.get(i - 1); 606 Type nextType = childTypes[i]; 607 if (operator.kind == PtParserConstants.MULTIPLY) { 608 resultType = resultType.multiply(nextType); 609 } else if (operator.kind == PtParserConstants.DIVIDE) { 610 resultType = resultType.divide(nextType); 611 } else if (operator.kind == PtParserConstants.MODULO) { 612 resultType = resultType.modulo(nextType); 613 } else { 614 _assert(false, node, "Invalid operation"); 615 } 616 } 617 _setType(node, resultType); 618 } 619 620 /** Set the type of the given node to be a record token that 621 * contains fields for each name in the record construction, 622 * where the type of each field in the record is determined by 623 * the corresponding type of the child nodes. 624 * @param node The specified node. 625 * @exception IllegalActionException If an inference error occurs. 626 */ 627 @Override 628 public void visitRecordConstructNode(ASTPtRecordConstructNode node) 629 throws IllegalActionException { 630 Type[] childTypes = _inferAllChildren(node); 631 632 String[] names = (String[]) node.getFieldNames() 633 .toArray(new String[node.jjtGetNumChildren()]); 634 635 _setType(node, new RecordType(names, childTypes)); 636 } 637 638 /** Set the type of the given node to be boolean. 639 * @param node The specified node. 640 * @exception IllegalActionException If an inference error occurs. 641 */ 642 @Override 643 public void visitRelationalNode(ASTPtRelationalNode node) 644 throws IllegalActionException { 645 /* Type[] childTypes = */_inferAllChildren(node); 646 647 // FIXME: Check args are booleans? 648 _setType(node, BaseType.BOOLEAN); 649 } 650 651 /** Set the type of the given node to be the type of the first 652 * child of the given node. 653 * @param node The specified node. 654 * @exception IllegalActionException If an inference error occurs. 655 */ 656 @Override 657 public void visitShiftNode(ASTPtShiftNode node) 658 throws IllegalActionException { 659 Type[] childTypes = _inferAllChildren(node); 660 661 // FIXME: Check others are scalars? 662 Type baseType = childTypes[0]; 663 _setType(node, baseType); 664 } 665 666 /** Set the type of the given node to be the least upper bound 667 * type of the types of the node's children. 668 * @param node The specified node. 669 * @exception IllegalActionException If an inference error occurs. 670 */ 671 @Override 672 public void visitSumNode(ASTPtSumNode node) throws IllegalActionException { 673 Type[] childTypes = _inferAllChildren(node); 674 675 List lexicalTokenList = node.getLexicalTokenList(); 676 int numChildren = node.jjtGetNumChildren(); 677 678 Type resultType = childTypes[0]; 679 for (int i = 1; i < numChildren; i++) { 680 Token operator = (Token) lexicalTokenList.get(i - 1); 681 Type nextType = childTypes[i]; 682 if (operator.kind == PtParserConstants.PLUS) { 683 resultType = resultType.add(nextType); 684 } else if (operator.kind == PtParserConstants.MINUS) { 685 resultType = resultType.subtract(nextType); 686 } else { 687 _assert(false, node, "Invalid operation"); 688 } 689 } 690 _setType(node, resultType); 691 } 692 693 /** Set the type of the given node to be the type of the 694 * child of the given node. 695 * @param node The specified node. 696 * @exception IllegalActionException If an inference error occurs. 697 */ 698 @Override 699 public void visitUnaryNode(ASTPtUnaryNode node) 700 throws IllegalActionException { 701 Type[] childTypes = _inferAllChildren(node); 702 Type baseType = childTypes[0]; 703 if (node.isMinus()) { 704 _setType(node, baseType.zero().subtract(baseType)); 705 } else { 706 _setType(node, baseType); 707 } 708 } 709 710 /////////////////////////////////////////////////////////////////// 711 //// protected methods //// 712 713 /** 714 * Assert that the given boolean value, which describes the given 715 * parse tree node is true. If it is false, then throw a new 716 * InternalErrorException that describes the node that includes 717 * the given message. 718 * @param flag If false, then throw an InternalErrorException 719 * @param node The node 720 * @param message The message included in the exception 721 */ 722 protected void _assert(boolean flag, ASTPtRootNode node, String message) { 723 if (!flag) { 724 throw new InternalErrorException(message + ": " + node.toString()); 725 } 726 } 727 728 /** Get the return type of a method belonging to the specified class, or the 729 * type of a field belonging to it. 730 */ 731 protected Type _getMethodReturnType(Class<?> clazz, String methodName, 732 Type[] argTypes) throws IllegalActionException { 733 Class<?> result = null; 734 735 if (argTypes.length == 1) { 736 Field[] fields = clazz.getFields(); 737 for (Field field : fields) { 738 if (field.getName().equals(methodName) 739 && Modifier.isPublic(field.getModifiers())) { 740 result = field.getType(); 741 break; 742 } 743 } 744 } 745 746 Method[] methods = clazz.getMethods(); 747 int argCount = argTypes.length - 1; 748 for (Method method : methods) { 749 if (method.getName().equals(methodName) 750 && Modifier.isPublic(method.getModifiers())) { 751 Class<?>[] parameterTypes = method.getParameterTypes(); 752 if (parameterTypes.length != argCount) { 753 continue; 754 } 755 boolean compatible = true; 756 for (int i = 0; compatible && i < argCount; i++) { 757 Class<?> argumentType = ConversionUtilities 758 .convertTokenTypeToJavaType(argTypes[i + 1]); 759 if (!parameterTypes[i].isAssignableFrom(argumentType)) { 760 compatible = false; 761 } 762 } 763 if (compatible) { 764 result = method.getReturnType(); 765 break; 766 } 767 } 768 } 769 770 if (result == null) { 771 return null; 772 } else { 773 if (Variable.class.isAssignableFrom(result)) { 774 return BaseType.UNKNOWN; 775 } else { 776 return ConversionUtilities.convertJavaTypeToTokenType(result); 777 } 778 } 779 } 780 781 /** Return the type of the identifier with the given name. 782 * @exception IllegalActionException If the identifier is undefined. 783 */ 784 protected Type _getTypeForName(String name) throws IllegalActionException { 785 Type type = null; 786 if (_scope != null) { 787 type = _scope.getType(name); 788 789 if (type != null && !(type instanceof ObjectType)) { 790 // Pretend that we cannot resolve the type if it is an 791 // ObjectType. 792 return type; 793 } 794 } 795 796 // Look up for constants. 797 if (Constants.get(name) != null) { 798 // A named constant that is recognized by the parser. 799 return Constants.get(name).getType(); 800 } 801 802 if (type != null) { 803 return type; 804 } 805 806 throw new IllegalActionException("The ID " + name + " is undefined."); 807 808 // return BaseType.GENERAL; 809 } 810 811 /** Loop through all of the children of this node, 812 * visiting each one of them, which will cause their token 813 * value to be determined. 814 */ 815 protected Type[] _inferAllChildren(ASTPtRootNode node) 816 throws IllegalActionException { 817 Type[] types = new Type[node.jjtGetNumChildren()]; 818 int numChildren = node.jjtGetNumChildren(); 819 820 for (int i = 0; i < numChildren; i++) { 821 _inferChild(node, i); 822 823 Type type = _inferredChildType; 824 825 if (type == null) { 826 throw new RuntimeException( 827 "node " + node.jjtGetChild(i) + " has no type."); 828 } 829 830 types[i] = type; 831 } 832 833 return types; 834 } 835 836 /** Visit the child with the given index of the given node. 837 * This is usually called while visiting the given node. 838 */ 839 protected Type _inferChild(ASTPtRootNode node, int i) 840 throws IllegalActionException { 841 ASTPtRootNode child = (ASTPtRootNode) node.jjtGetChild(i); 842 child.visit(this); 843 return _inferredChildType; 844 } 845 846 /** Test if the given identifier is valid. 847 */ 848 protected boolean _isValidName(String name) throws IllegalActionException { 849 if (_scope != null) { 850 try { 851 return _scope.getType(name) != null; 852 } catch (Exception ex) { 853 return false; 854 } 855 } else { 856 return false; 857 } 858 } 859 860 /** Infer the type of the specified method. The type of the object on which 861 * the method is evaluated should be the first argument. 862 * @param methodName The method name. 863 * @param argTypes An array of argument types. 864 * @exception IllegalActionException If an evaluation error occurs. 865 * @see ParseTreeEvaluator#_methodCall(String, Type[], Object[]) 866 */ 867 protected Type _methodCall(String methodName, Type[] argTypes) 868 throws IllegalActionException { 869 CachedMethod cachedMethod = CachedMethod.findMethod(methodName, 870 argTypes, CachedMethod.METHOD); 871 872 if (cachedMethod.isValid()) { 873 Type type = cachedMethod.getReturnType(); 874 return type; 875 } 876 877 if (argTypes[0] instanceof ObjectType) { 878 Object object = ((ObjectType) argTypes[0]).getValue(); 879 if (object != null) { 880 if (object instanceof NamedObj) { 881 Object result = ((NamedObj) object) 882 .getAttribute(methodName); 883 if (result == null && object instanceof Entity) { 884 result = ((Entity) object).getPort(methodName); 885 } 886 if (result == null && object instanceof CompositeEntity) { 887 result = ((CompositeEntity) object) 888 .getEntity(methodName); 889 if (result == null) { 890 result = ((CompositeEntity) object) 891 .getRelation(methodName); 892 } 893 } 894 895 if (result == null) { 896 List attributes = ((NamedObj) object) 897 .attributeList(ContainmentExtender.class); 898 Iterator attrIterator = attributes.iterator(); 899 while (result == null && attrIterator.hasNext()) { 900 ContainmentExtender extender = (ContainmentExtender) attrIterator 901 .next(); 902 result = extender.getContainedObject(methodName); 903 } 904 } 905 if (result != null) { 906 if (result instanceof Variable) { 907 // If the variable is a Matrix or an Array, the return type 908 // will be the element type, not the Matrix or Array. 909 Type type = ((Variable) result).getType(); 910 if (type instanceof MatrixType) { 911 return ((MatrixType) type).getElementType(); 912 } 913 if (type instanceof ArrayType) { 914 return ((ArrayType) type).getElementType(); 915 } 916 return type; 917 } else { 918 return new ObjectType(result, result.getClass()); 919 } 920 } 921 } 922 } 923 924 Class<?> valueClass = ((ObjectType) argTypes[0]).getValueClass(); 925 if (valueClass == null) { 926 valueClass = Object.class; 927 } 928 Set<Class<?>> classes = new HashSet<Class<?>>(); 929 classes.add(valueClass); 930 while (!classes.isEmpty()) { 931 Iterator<Class<?>> iterator = classes.iterator(); 932 valueClass = iterator.next(); 933 iterator.remove(); 934 935 if (!Modifier.isPublic(valueClass.getModifiers())) { 936 for (Class<?> interf : valueClass.getInterfaces()) { 937 classes.add(interf); 938 } 939 Class<?> superclass = valueClass.getSuperclass(); 940 if (superclass != null) { 941 classes.add(superclass); 942 } 943 } else { 944 Type result = _getMethodReturnType(valueClass, methodName, 945 argTypes); 946 if (result != null) { 947 return result; 948 } 949 } 950 } 951 } 952 953 // If we reach this point it means the function was not found on 954 // the search path. 955 StringBuffer buffer = new StringBuffer(); 956 957 for (int i = 1; i < argTypes.length; i++) { 958 if (i == 1) { 959 buffer.append(argTypes[i].toString()); 960 } else { 961 buffer.append(", " + argTypes[i].toString()); 962 } 963 } 964 965 throw new IllegalActionException("No matching method " + argTypes[0] 966 + "." + methodName + "( " + buffer + " )."); 967 } 968 969 protected void _setType(ASTPtRootNode node, Type type) { 970 // System.out.println("type of " + node + " is " + type); 971 _inferredChildType = type; 972 node.setType(type); 973 } 974 975 protected Type _inferredChildType; 976 977 protected ParserScope _scope; 978}