Clarifications pour le TP1 * Vous n'êtes pas particulièrement en retard pour le TP1. La première partie ne devrait pas prendre plus de quelques heures, une fois que vous avez une bonne idée de ce que vous allez coder. * Vous devez utiliser Java 5 pour compiler les fichiers du TP. Si vous êtes parvenus à les modifier pour compiler en Java 1.4, il est fort probable que les changements ne soient pas équivalents, ou même que votre code ne compile pas sans ces changements (n'oubliez pas que, a priori, vous n'avez que deux fichiers à remettre). Si vous n'arrivez pas à faire fonctionner votre système en Java 5 (`javac -target 1.5'), il est plus simple d'utiliser celui de l'université (tapez `inclure jdk-1.6' pour avoir accès à la bonne version). * Vous n'avez pas à écrire un programme complet. On vous fournit un programme complet, dont deux morceaux (Parser.java, Reduce.java) ne sont pas encore fonctionnels. En particulier, vous n'avez pas à écrire de `main'. Le `main' du programme se trouve dans REPL.java. Vous pouvez donc compiler tous les fichiers java et démarrer `java REPL'. * ExprTokenizer est la classe que recevra votre Parser en argument. Vous l'utiliserez principalement à travers les 4 méthodes: - peek: Retourne le prochain caractere lu, sans le consommer - read: Retourne le prochain caractere lu, et le consomme - skipWhitespace: Consomme tous les caracteres d'espace, jusqu'au prochain caractere non-espace (qui n'est pas consommé). - readToken: Passe tous les espaces jusqu'au prochain non-espace, et consomme et retourne le plus long lexème (parenthèse, opérateur, nombre ou nom de variable) * Soit l'expression arithmétique "(1 + 2)". Votre programme doit la reconnaître comme étant une opération binaire, Add, avec un Number (1.0) à gauche et un autre (2.0) à droite. Cela donnerait donc l'arbre d'objets: +--------------+ +-------+ .->| Number | | Add | / | m_value: 1.0 | | | / +--------------+ | left -+-' | right-+-. +--------------+ +-------+ \ | Number | `-->| m_value: 2.0 | +--------------+ Plus précisément, la méthode `parseExpression' de la classe Parser recevra un ExprTokenizer contenant la chaîne "(1 + 2)", et devra retourner un object comme décrit ci-haut. * La solution suggérée pour Parser est récursive. La récursion se fait sur la consommation de caractères: si l'on consomme toujours au moins un caractère avant de récurser, on est assuré de ne pas boucler (et de terminer sur une chaine finie). * Notez que tout noeud de l'arbre syntaxique (représentant une production de la grammaire) `est-un' Expr. Ainsi, les constructeurs des classes Plus, Minus, Add, Subtract, Multiply et Divide prennent des Expr arbitraires (soit une instance des classes précédemment nommées ou de Number ou, plus tard, Variable). * Afin de produire l'arbre d'objets décrit ci-haut, l'exécution de `parseExpression' ressemblerait à cette trace (qui n'est en rien une méthode, mais une description de l'exécution d'une méthode): parseExpression left <- parseExpression // parseExpression retournera un Expr créé avec `new Number(1.0)' op <- readToken right <- parseExpression // parseExpression retournera un Expr créé avec `new Number(2.0)' skip <- readToken return new Add(left, right) * Reduce doit implanter la partie intéressante d'un évaluateur simple via le pattern visiteur. L'interface d'un visiteur est définie par ExprVisitor. Chaque cas de figure dans la récursion (si l'objet visité un Multiply, Divide, Subtract, etc.,) doit être implanté par la méthode correspondante (visitMultiply, visitDivide, divideSubtract, etc), qui sera appelé par la méthode `accept', ou, plus communément, `walkOperand' (pour un UnOp), ou `walkLeft' et `walkRight' (pour un BinOp). * Pour un exemple de visiteur, lisez PrintInfix.java. Les cas intéressants sont surtout implantés dans `visitBinOp' et `visitUnOp', des méthodes appelées par les visitMultiply, visitDivide, visitPlus, ... On y voit le travaille qu'un cas de visiteur fait: traiter le noeud actuel, et récurser sur les enfants (walkOperand, walkLeft ou walkRight), habituellement avec le même objet visiteur. * Reduce doit implanter un visiteur qui retourne un Number correspondant à la valeur de l'expression visitée. Par exemple, sur l'arbre correspondant à "(1 + 2)", l'exécution ressemblerait à: visitAdd(Add a) leftValue <- walkLeft(this) visitNumber(Number n) return new Number(n.getValue()); rightValue <- walkRight(this) visitNumber(Number n) return new Number(n.getValue()); return new Number(leftValue.getValue() + rightValue.getValue());