Files
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
DEV/DEV3.4/ControleMachine/test/ShuntingYard.java

274 lines
8.9 KiB
Java
Raw Normal View History

2025-03-13 12:01:03 +01:00
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();
}
}