Pour un projet d'école, moi et un camarade de classe écrivent un langage spécifique au domaine en javascript (node). La langue contient des instructions qui nécessitent une entrée de l'utilisateur via une connexion Websocket.Interprétation inter-utilisateur dans un environnement monothread non-bloquant (difficile)
Lorsqu'une instruction nécessite une entrée utilisateur, l'interpréteur doit arrêter l'exécution et attendre un événement.
Normalement on mettrait le thread en pause et attendrait que l'entrée d'utilisateur soit reçue avant de continuer le thread, mais nous ne pouvons pas faire cela puisque node.js est single-threaded, et n'offre aucune option de sommeil sans bloquer le processeur.
Nous avons essayé de nombreuses façons de contourner ce problème, mais nous avons échoué. :-(
La réponse à cette question pourrait être une suggestion à la façon de faire un interprète pausable.
Ci-dessous, nous courons à travers une simplification de l'interprète (avec des erreurs)
Nous construisons un résumé arbre de syntaxe avec ces nœuds
var Print = function(str){
this.str = str;
}
var Block = function(stats){
this.stats = stats;
}
var Delayed = function(stats){
this.stats = stats;
}
var Loop = function(times, stats){
this.times = times;
this.stats = stats;
}
- Imprimer -. une simple déclaration, qui ne doit jamais être mis en pause
- Bloc - une séquence d'instructions
- Retardé - une séquence d'instructions à exécuter après un certain temps.
- Loop - de multiples itérations d'une séquence d'instructions
L'arbre ressemble à ceci:
var ast = new Block([
new Delayed([
new Print("blah blah"),
new Delayed([])
]),
new Loop(3,[
new Delayed([
new Print("loop delayed")
])
])
]);
L'interprète utilisé pour évaluer les déclarations. Notez que ce code ne fonctionne pas correctement. Il ne fait jamais une pause pour attendre l'entrée.
var Interpreter = function(ast){
this.ast = ast;
}
Interpreter.prototype.run = function(){
this.handle(this.ast);
}
Interpreter.prototype.handleAll = function(stats){
for(var i = 0; i < stats.length; i++){
this.handle(stats[i]);
}
}
Interpreter.prototype.handle = function(stat){
var t = this;
/*-----------------------------------------------*
* Simple statement - no need for pause here *
*-----------------------------------------------*/
if(stat instanceof Print){
sys.puts(stat.str);
}
/*-----------------------------------------------------*
* Delayed - this might contain more delayed stats *
*-----------------------------------------------------*/
else if(stat instanceof Delayed){
sys.debug("waiting for user input");
// this represents a user input with a string
setTimeout(function(str){
sys.debug("done waiting");
sys.puts(str);
// this might contain delayed stats
t.handleAll(stat.stats);
}, 2000, "some string");
}
// ============================================
// = Block - this might contain delayed stats =
// ============================================
else if(stat instanceof Block){
sys.debug("doing a block - before");
this.handleAll(stat.stats);
sys.debug("doing a block - after");
}
// ===========================================
// = Loop - this might contain delayed stats =
// ===========================================
else if(stat instanceof Loop){
sys.debug("before loop");
for(var i = 0; i < stat.times; i++){
sys.debug("inside loop[" + i + "] - begin");
// this will maybe contain delayed stats
this.handleAll(stat.stats);
sys.debug("inside loop[" + i + "] - end");
}
sys.debug("after loop");
}
else {
throw "error.. statement not recognized"
}
}
L'interprète doit faire une pause lorsqu'une instruction « retardée » est rencontré, puis a continué lorsque le retard est fait.
Le code ci-dessus ne fait jamais de pause. Lorsqu'une instruction "Delayed" est rencontrée, les sous-états sont retardés, mais les autres instructions après "Delayed" sont exécutées.
Pour une version non fragmentée du code, Sé http://pastie.org/1317023
Nous vous remercions de votre réponse. Ce truc de continuation-passing pourrait fonctionner. Nous allons vérifier. –