我一直在探索用javascript撰寫一個非常基本/有限的解釋器,作為一個練習。一切都很順利,直到我引入了LOOPs的概念。
。給定以下腳本:
LOOP 2
A
LOOP 3 A
B
END
LOOP4
C
LOOP 5
D
結束
E
END END
F
END END F
該演算法應按以下順序訪問內部標記:
ABBBCDDDDDECDDDECDDDECDDDEFABBBCDDDDDECDDDECDDDECDDDEF。
下面的方法可以解決這個問題,但它需要在標記上進行大量的迭代。與我之前使用的手動擴展回圈的切片方法相比,這是一個改進,但遠非最佳。
/***
* 在實踐中,我們會在閱讀腳本時抓取每個標記。
* 但為了保持簡單,并把重點放在回圈演算法上。
* 我們可以作弊,把所有的標記做成一個陣列。
*/
const getTokens = (s) => s。 replace(/[W_] /g, " ") 。 split(" ").filter(Boolean) 。
/* Temp vars - 理想情況下,我希望用陣列來解決這個問題。*/
const start = []; // 回圈的起始索引。
const end = []; //loop end indices
const counts = []; //回圈次數
const completed = []; //回圈完成。
for (let i = 0; i < tokens.length; i ) {
const token = tokens[i];
if (token =="LOOP") {
if (start.length == 0 || i > start[start.length - 1] ) {
//加入新的回圈索引,如果我們之前沒有看到它。
start.push(i); //儲存回圈索引。
counts.push(Number(tokens[i 1]); //回圈計數總是下一個LOOP標記。
completed.push(0); //在索引處以0完成初始化。
//找到回圈的結束索引。
//注意:這是最慢的部分。。
let skip = 0;
for (let j = i 2; j < tokens.length; j ) {
if (tokens[j] == "LOOP"/span>) {
skip ; //增加嵌套深度。
} else if (tokens[j] == "END") {
if ( skip == 0) {
end.push(j); // Found matching loop close.
break。
}
跳過--。
}
}
}
i ; // Skip over the loop count
continue。
} else if (token == "END") {
let j;
for (j = 0; j < end.length; j ) {
if (end[j] == i) break; //發現匹配的結束索引。
}
const isCompleted = completed[j] == counts[j] - 1;
if (!isCompleted) {
i = start[j] 1;
completed[j] ;
for (let k = j 1; k < start.length; k ) {
completed[k] = 0; // Reset nested loops in between.
}
}
continue;
}
console.log(tokens[i])。
}
https://jsfiddle.net/5wpa8t4n/
有什么更好的方法來完成這種基于陣列的方法,使用單次通過腳本,或者最差也要2次通過,但不是N-LOOP通過?
uj5u.com熱心網友回復:
在開始解釋回圈時,你不需要知道匹配的end的位置。你所需要記錄的是當遇到下一個end時要跳回的位置,但在那之前,只需繼續逐個令牌進行解釋即可。
這些位置,連同各自的計數器,可以被存盤在一個堆疊結構中。
。const script = `。
腳本
A
腳本
B
LOOP 3
練習
C
回圈3
D
LOOP 5
E
回圈4
F
LOOP 2
`
const parse = (script) =>
腳本
.replace(/[W_] /g, " "/span>)
.split(" ")
.filter(Boolean)。
const interpret=(code)=> {
let loops = []; // Active loops: iteration count and jump target
let ip = 0; //指令指標。
let result = ""/span>;
while (ip < code.length) {
const instruction = code[ip];
switch (instruction) {
case "DO"/span>: {
ip;
loops.push({count: 0, start: ip})。)
} break;
case "LOOP"/span>: {
const limit = Number(code[ ip])。
const {count, start} = loops.pop()。
if (count < limit) {
loops.push({count: count 1, start}) 。
ip = start; //跳回。
} else {
ip。
}
} break;
default: {
ip;
result = instruction; //一個列印陳述句。
} break;
}
}
return result。
};
console.log(interpret(parse(script));
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" class="snippet-box-edit snippet-box-result" frameborder="0"></iframe>
我對結構進行了一些簡化,使用do-while回圈,所以我永遠不必跳過回圈體。在真正的位元組碼中,由決議器發出的跳轉目標(包括前后)將是指令本身的一部分,只有計數 "變數 "需要存盤在堆疊中。跳轉目標永遠不會改變,所以你只需要在parse函式中生成它們一次。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/327355.html
標籤:
