您可以利用正則表達式分組來執行此操作。您需要一個結合了不同可能令牌的正則表達式,然後重複應用它。
我喜歡分離出不同的部分;它可以更容易維護和擴展:
var tokens = [
"sin",
"cos",
"tan",
"\\(",
"\\)",
"\\+",
"-",
"\\*",
"/",
"\\d+(?:\\.\\d*)?"
];
你粘上這些都連成一個大的正則表達式與|
每個標記之間:
var rtok = new RegExp("\\s*(?:(" + tokens.join(")|(") + "))\\s*", "g");
然後,您可以標記化使用您的源字符串的正則表達式操作:
function tokenize(expression) {
var toks = [], p;
rtok.lastIndex = p = 0; // reset the regex
while (rtok.lastIndex < expression.length) {
var match = rtok.exec(expression);
// Make sure we found a token, and that we found
// one without skipping garbage
if (!match || rtok.lastIndex - match[0].length !== p)
throw "Oops - syntax error";
// Figure out which token we matched by finding the non-null group
for (var i = 1; i < match.length; ++i) {
if (match[i]) {
toks.push({
type: i,
txt: match[i]
});
// remember the new position in the string
p = rtok.lastIndex;
break;
}
}
}
return toks;
}
這只是重複匹配字符串的令牌正則表達式。正則表達式是用「g」標誌創建的,所以正則表達式機器會在每次匹配後自動跟蹤從哪裏開始匹配。當它看不到匹配,或者它沒有看到匹配時,但必須跳過無效內容才能找到它,我們知道有一個語法錯誤。當它匹配時,它在令牌數組中記錄它匹配的令牌(非空組的索引)和匹配的文本。通過記住匹配的標記索引,它可以節省您在標記後得出每個標記字符串意味着的麻煩;你只需要做一個簡單的數字比較。
因此主叫tokenize("sin(4+3) * cos(25/3)")
回報:
[ { type: 1, txt: 'sin' },
{ type: 4, txt: '(' },
{ type: 10, txt: '4' },
{ type: 6, txt: '+' },
{ type: 10, txt: '3' },
{ type: 5, txt: ')' },
{ type: 8, txt: '*' },
{ type: 2, txt: 'cos' },
{ type: 4, txt: '(' },
{ type: 10, txt: '25' },
{ type: 9, txt: '/' },
{ type: 10, txt: '3' },
{ type: 5, txt: ')' } ]
令牌類型1是sin
功能,類型4是左括號,類型10是一個數字,等
編輯,如果你想匹配如「x」和「y」標識 —,那麼我可能會使用一組不同的標記圖案,其中一個正好匹配任何標識。這意味着解析器不會直接從詞法分析器中找出關於「罪」和「cos」等的信息,但沒關係。下面是令牌模式的備選列表:
var tokens = [
"[A-Za-z_][A-Za-z_\d]*",
"\\(",
"\\)",
"\\+",
"-",
"\\*",
"/",
"\\d+(?:\\.\\d*)?"
];
現在任何標識符都將是類型1標記。
對不起,但是當我在http://jsfiddle.net/P5vmY/上運行它時,它只是一直拋出一個語法錯誤。我執行錯了嗎? – scrblnrd3
@ scrblnrd3我沒有包含任何符合「x」的內容。它必須是一組稍微不同的令牌模式;而不是明確地查找「罪惡」等,你只需要尋找標識符,並在解析器中單獨定義這些標識符。 – Pointy
@ scrblnrd3哦也有一個錯誤:)我會編輯它。 – Pointy