AndroidStudioProjects
Automate
ControleMachine1
ControleMachine2
ControleMachineJava
DEV1.1
DEV2.1
DEV3.1
DEV3.2
DEV3.4
ControleMachine
test
.bashrc
AbstractToken.java
Exemple.java
ExempleSY1.java
ExempleSY2.java
ExempleSY3.java
ExempleSY4.java
ExempleSY5.java
Operator.java
Questions.md
README.txt
ReservedWord.java
Separator.java
ShuntingYard.java
Shunting_yard.svg.png
TestShuntingYard1.java
TestShuntingYard2.java
TokenConstant.java
TokenOperator.java
TokenSeparator.java
TokenVariable.java
pourchot_dev34.tar.gz
test.tar.gz
TP2
TP4
TP5
TP6
TP7
TPnote
TP3.mdj
ex2.mdj
DEV32
DEV4.4
SAe
.gitignore
Ex1TP2.mdj
README.md
Vote.java
pourchot_dev32.tar.gz
274 lines
8.9 KiB
Java
274 lines
8.9 KiB
Java
|
import java.util.Deque;
|
|||
|
import java.util.ArrayDeque;
|
|||
|
|
|||
|
/**
|
|||
|
* Classe implémentant l'algorithme proposé par Dijkstra en 1961 pour transformer une expression en notation infixe, en expression postfixe.
|
|||
|
* https://en.wikipedia.org/wiki/Shunting_yard_algorithm
|
|||
|
*
|
|||
|
* Le constructeur prend en entrée l'entrée de l'algorithme (input) qui sera une file de AbstractToken et initialise
|
|||
|
* une pile pour les opérateurs et séparateurs, et une file de sortie.
|
|||
|
* Pour uniformiser, tout est modélisé avec l'interface Deque pour des AbstractToken.
|
|||
|
*
|
|||
|
* En pratique les seuls mouvements entre ces trois structures de données sont faits par trois primitives :
|
|||
|
* shuntFromInput() (input vers output)
|
|||
|
* shuntFromStack() (stack vers output)
|
|||
|
* pushToStack() (input vers stack)
|
|||
|
*
|
|||
|
* L'algorithme fonctionne par analogie au rangement des wagons dans une gare de triage (shunting yard veut dire gare de triage).
|
|||
|
*
|
|||
|
* output ================== input
|
|||
|
* \\ //
|
|||
|
* \ /
|
|||
|
* \/
|
|||
|
* ||
|
|||
|
* stack
|
|||
|
*
|
|||
|
* Le dessin en ASCII ci-dessus a été qualifié de "Jackson Pollock" du crobar par Luc Hernandez.
|
|||
|
* Pour un exemple plus explicite : regardez svp le dessin "Shunting_yard.svg.png"
|
|||
|
*
|
|||
|
*
|
|||
|
* Les constantes et les variables sont envoyées de droite à gauche avec shuntFromInput
|
|||
|
* Les symboles d'opérations ou de séparation sont envoyés sur la pile avec pushToStack
|
|||
|
* puis envoyés en sortie avec shuntFromStack quand c'est le bon moment.
|
|||
|
*
|
|||
|
* pour les opérateurs, en gros tout se décide avec la priorité des opérations
|
|||
|
* En cas d'égalité on regarde si l'opérateur est associatif à gauche :
|
|||
|
* ceci est le cas des 4 opérations binaires * \/ + - mais pas de l'exponentiation ^ (associativité à droite).
|
|||
|
* Ainsi a^b^c sera interprété convenablement comme a^(b^c)
|
|||
|
*
|
|||
|
* @author Florent Madelaine
|
|||
|
*/
|
|||
|
|
|||
|
|
|||
|
public class ShuntingYard {
|
|||
|
/**
|
|||
|
*
|
|||
|
*
|
|||
|
*/
|
|||
|
private Deque<AbstractToken> input;
|
|||
|
private Deque<AbstractToken> output;
|
|||
|
private Deque<AbstractToken> stack;
|
|||
|
|
|||
|
public ShuntingYard(Deque<AbstractToken> input){
|
|||
|
this.input = input;
|
|||
|
this.output = new ArrayDeque<AbstractToken>();
|
|||
|
this.stack = new ArrayDeque<AbstractToken>();
|
|||
|
}
|
|||
|
|
|||
|
public Deque<AbstractToken> getInput(){
|
|||
|
return this.input;
|
|||
|
}
|
|||
|
|
|||
|
public Deque<AbstractToken> getOutput(){
|
|||
|
return this.stack;
|
|||
|
}
|
|||
|
|
|||
|
public Deque<AbstractToken> getStack(){
|
|||
|
return this.output;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// input and stack are empty
|
|||
|
public boolean isOver(){
|
|||
|
return (this.input.isEmpty() || this.stack.isEmpty()); //BUG FIX 1
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// output <---------------- input
|
|||
|
//
|
|||
|
//
|
|||
|
// stack
|
|||
|
/**
|
|||
|
* Envoie la premiere constante ou variables de la file d'entrée vers la file de sortie.
|
|||
|
* Si le Token récupéré n'est pas TokenConstant ou un TokenVariable, il y a 2 cas possible:
|
|||
|
* Soit il s'agit d'un TokenOperator ou d'un TokenSeparator de gauche, dans ce cas on replace l'élément extrait au départ de la file d'entrée
|
|||
|
* puis on fait appel à la méthode shuntFromStack() plutôt que celle-ci.
|
|||
|
* Soit il s'agit d'un TokenSeparator de droite, dans ce cas on la replace au départ de la file d'entrée et on fait appel à la méthode crushParenthesis()
|
|||
|
* pour la supprimer.
|
|||
|
*/
|
|||
|
public void shuntFromInput() {
|
|||
|
x=this.input.getFirst();
|
|||
|
if(x instanceof(TokenConstant)||x instanceof(TokenVariable))
|
|||
|
this.output.addLast(x);
|
|||
|
else if(x instanceof(TokenOperator)){
|
|||
|
this.input.addFirst(x);
|
|||
|
shuntFromStack();
|
|||
|
}
|
|||
|
else if(x.getSeparator().equals(Separator.LB)){
|
|||
|
this.input.addFirst(x);
|
|||
|
shuntFromStack();
|
|||
|
}
|
|||
|
else{
|
|||
|
this.input.addFirst(x);
|
|||
|
crushParenthesis();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// output <--- input
|
|||
|
// \
|
|||
|
// |
|
|||
|
// stack
|
|||
|
/**
|
|||
|
* Envoie le dernier élément de la pile stack vers la file de sortie.
|
|||
|
*/
|
|||
|
public void shuntFromStack() {
|
|||
|
this.output.addFirst(this.stack.removeLast());
|
|||
|
}
|
|||
|
|
|||
|
// output ---------- input
|
|||
|
// /
|
|||
|
// V
|
|||
|
// stack
|
|||
|
/**
|
|||
|
* Envoie le premier opérateur ou parenthèse gauche de la file d'entrée vers la pile stack.
|
|||
|
* Si le Token récupéré n'est pas un TokenOperator ou un TokenSeparator de gauche, il y a 2 cas possible:
|
|||
|
* Soit le Token est un TokenConstant ou un TokenVariable, dans ce cas on replace le Token extrait au départ de la file d'entrée
|
|||
|
* puis on fait appel à la méthode shuntFromInput() plutôt que celle-ci.
|
|||
|
* Soit il s'agit d'un TokenSeparator de droite, dans ce cas on la replace au départ de ola file d'entrée et on fait appel à la méthode crushParenthesis()
|
|||
|
* pour la supprimer.
|
|||
|
*/
|
|||
|
public void pushToStack(){
|
|||
|
this.stack.addFirst(this.input.getFirst());
|
|||
|
}
|
|||
|
|
|||
|
// output ) input
|
|||
|
// ^
|
|||
|
// ( <------| both to be destroyed
|
|||
|
// stack
|
|||
|
//
|
|||
|
// throws IllegalStateException si ce n'est pas exactement ce cas.
|
|||
|
/**
|
|||
|
* Supprime le premier élément de la file d'entrée et le dernier de la pile stack.
|
|||
|
* @exception IllegalStateException Renvoyer si il y s'agit d'un cas particulier.
|
|||
|
*/
|
|||
|
public void crushParenthesis(){
|
|||
|
this.stack.removeLast();
|
|||
|
this.input.removeFirst();
|
|||
|
}
|
|||
|
|
|||
|
// does one step of Dijkstra Shunting algorithm
|
|||
|
// if is is done it throws the exception
|
|||
|
public void shunting(){
|
|||
|
if (isOver()) throw new IllegalStateException("the shunting is over, since both the input and the stack are empty.");
|
|||
|
else if (this.input.isEmpty()){
|
|||
|
if (this.stack.getFirst() instanceof TokenSeparator){
|
|||
|
TokenSeparator s = (TokenSeparator) this.stack.getFirst();
|
|||
|
if (s.getSeparator().equals(Separator.LB)){
|
|||
|
throw new IllegalArgumentException("the shunting is over with a parenthesis mismatch extra left bracket");}
|
|||
|
}
|
|||
|
else // should be an operator
|
|||
|
shuntFromStack();
|
|||
|
}
|
|||
|
else if (this.input.getFirst() instanceof TokenConstant) {
|
|||
|
shuntFromInput();
|
|||
|
shuntFromInput();
|
|||
|
}
|
|||
|
// else
|
|||
|
if (this.input.getFirst() instanceof TokenVariable) {
|
|||
|
shuntFromInput();
|
|||
|
}
|
|||
|
else if (this.input.getFirst() instanceof TokenSeparator) {
|
|||
|
TokenSeparator s = (TokenSeparator) this.input.getFirst();
|
|||
|
// test si LB left bracket
|
|||
|
if (s.getSeparator().equals(Separator.LB)){
|
|||
|
pushToStack();
|
|||
|
}
|
|||
|
else if (s.getSeparator().equals(Separator.RB)){
|
|||
|
if(stack.isEmpty()) throw new IllegalArgumentException("the shunting is over with a parenthesis mismatch extra right bracket");
|
|||
|
else {
|
|||
|
if (this.stack.getFirst() instanceof TokenOperator){
|
|||
|
shuntFromStack(); // should be the operator concerned by parenthesis
|
|||
|
}
|
|||
|
else {// it is a separator
|
|||
|
crushParenthesis(); // we discard the parenthesis.
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else if (this.input.getFirst() instanceof TokenOperator) {
|
|||
|
if (this.stack.isEmpty()){
|
|||
|
pushToStack();
|
|||
|
}
|
|||
|
else if (this.stack.getFirst() instanceof TokenSeparator){
|
|||
|
TokenSeparator s = (TokenSeparator) this.stack.getFirst();
|
|||
|
pushToStack();
|
|||
|
}
|
|||
|
else if (this.stack.getFirst() instanceof TokenOperator){
|
|||
|
TokenOperator o = (TokenOperator) this.input.getFirst();
|
|||
|
TokenOperator o2 = (TokenOperator) this.stack.getFirst();
|
|||
|
if (o2.takesPrecedenceOver(o)){
|
|||
|
shuntFromStack();
|
|||
|
}
|
|||
|
else pushToStack();// shuntFromInput();// input takes precedence
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
@Override
|
|||
|
public String toString(){
|
|||
|
StringBuilder b = new StringBuilder();
|
|||
|
b.append("\t\t\t\t");
|
|||
|
for(AbstractToken t : this.input ){
|
|||
|
b.append(t.toString());
|
|||
|
}
|
|||
|
b.append(" :input\noutput: ");
|
|||
|
for(AbstractToken t : this.output ){
|
|||
|
b.append(t.toString());
|
|||
|
}
|
|||
|
b.append("\n\t\t stack: ");
|
|||
|
for(AbstractToken t : this.stack ){
|
|||
|
b.append(t.toString());
|
|||
|
}
|
|||
|
b.append("\n");
|
|||
|
b.append("--------------------------------------------------------------\n");
|
|||
|
return b.toString();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
public static void main(String[] args){
|
|||
|
// 3 + 4 × (2 − 1)
|
|||
|
// pas de parenthèse encore, je fais 3 + 4 * 2
|
|||
|
Deque<AbstractToken> expression = new ArrayDeque<AbstractToken>();
|
|||
|
expression.addLast(new TokenConstant(3));
|
|||
|
expression.addLast(new TokenOperator(Operator.ADD));
|
|||
|
expression.addLast(new TokenConstant(4));
|
|||
|
expression.addLast(new TokenOperator(Operator.MUL));
|
|||
|
expression.addLast(new TokenSeparator(Separator.LB));
|
|||
|
expression.addLast(new TokenConstant(2));
|
|||
|
expression.addLast(new TokenOperator(Operator.SUB2));
|
|||
|
expression.addLast(new TokenConstant(1));
|
|||
|
expression.addLast(new TokenSeparator(Separator.RB));
|
|||
|
|
|||
|
ShuntingYard se = new ShuntingYard(expression);
|
|||
|
|
|||
|
System.out.println(se);
|
|||
|
se.shunting();
|
|||
|
System.out.println(se);
|
|||
|
se.shunting();
|
|||
|
System.out.println(se);
|
|||
|
se.shunting();
|
|||
|
System.out.println(se);
|
|||
|
se.shunting();
|
|||
|
System.out.println(se);
|
|||
|
se.shunting();
|
|||
|
System.out.println(se);
|
|||
|
se.shunting();
|
|||
|
System.out.println(se);
|
|||
|
se.shunting();
|
|||
|
System.out.println(se);
|
|||
|
se.shunting();
|
|||
|
System.out.println(se);
|
|||
|
se.shunting();
|
|||
|
System.out.println(se);
|
|||
|
se.shunting();
|
|||
|
System.out.println(se);
|
|||
|
se.shunting();
|
|||
|
System.out.println(se);
|
|||
|
se.shunting();
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|