<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                ## 如何使用 JavaScript 實現一門編程語言(4) —— Token stream tokenizer(標記器, 也稱為“詞法分析器”), 對?[字符輸入流](http://annn.me/implement-a-programming-language-3)?進行操作,并返回具有相同接口的流對象,但由 peek()/next() 返回的值一個個token。token是具有兩個屬性的對象:type和value。以下是我們所支持的token的一些示例: ``` { type:“ punc ”,value:“ (” } // 標點符號:parens,逗號,分號等等 { type:“ num ”,value:5 } // numbers { type:“ str ”,value:“ Hello World !“ } // 字符串 { type:” kw “,value:”lambda “ } // keywords { type: ” var “, value: ” a “ } // 標識符 { type: ” op “, value: ” != “ } // 運算符 ``` 空白符和注釋將被跳過,沒有令牌返回。 為了編寫tokenizer,我們需要更仔細地認識我們的語言的語法。有個辦法是,根據當前字符(由input.peek()返回的)來決定讀取哪種類型的token: 1. 首先,跳過空格。 2. 如果然后返回。input.eof()null 3. 如果它是一個井號(#),則跳過注釋(在行結束后重試)。 4. 如果它是一個引號,那么閱讀一個字符串。 5. 如果它是一個數字,那么我們繼續閱讀一個數字。 6. 如果它是“字母”,則讀取標識符或關鍵字token。 7. 如果它是標點符號之一,則返回標點符號token。 8. 如果它是運算符,則返回運算符token。 9. 如果以上都不是,那就拋出錯誤了。input.croak() “read_next”函數作為tokenizer的核心部分 ,它實現了上面的內容: ``` function read_next() { read_while(is_whitespace); if (input.eof()) return null; var ch = input.peek(); if (ch == "#") { skip_comment(); return read_next(); } if (ch == '"') return read_string(); if (is_digit(ch)) return read_number(); if (is_id_start(ch)) return read_ident(); if (is_punc(ch)) return { type : "punc", value : input.next() }; if (is_op_char(ch)) return { type : "op", value : read_while(is_op_char) }; input.croak("Can't handle character: " + ch); } ``` 這是一個充當了調度員覺得的函數,它將next()調用以獲取下一個token。請注意,它使用許多專注于特定token類型的函數,例如read_string(),read_number()等等。使用這些函數的代碼而導致調度程序復雜化是沒有意義的,即使我們從不在別處調用它們。 另外需要注意的是,我們并沒有在一個步驟中消耗所有的輸入流。解析器每次調用下一個token時,我們都會讀取一個token。如果出現分析錯誤,我們甚至不會到達流的末尾。 字符只要它們被允許作為標識符(is_id)的一部分,read_ident()就會讀取他們。標識符必須以字母、λ或_開頭,并且可以包含更多這樣的字符或數字,或者以下之一:?! - <> =。因此,foo-bar不會被看作是三個token,而是作為一個單一的標識符(一個token)。這條規則的原因是我希望能夠定義名為is-pair或者string>=的函數(對不起,這就是我的Lisper)。 此外,read_ident()函數將檢查已知關鍵字列表中的標識符,如果它存在,它將返回一個”kw”令牌. 這里是我們語言的完整tokenizer: ``` function TokenStream(input) { var current = null; var keywords = " if then else lambda λ true false "; return { next : next, peek : peek, eof : eof, croak : input.croak }; function is_keyword(x) { return keywords.indexOf(" " + x + " ") >= 0; } function is_digit(ch) { return /[0-9]/i.test(ch); } function is_id_start(ch) { return /[a-zλ_]/i.test(ch); } function is_id(ch) { return is_id_start(ch) || "?!-<>=0123456789".indexOf(ch) >= 0; } function is_op_char(ch) { return "+-*/%=&|<>!".indexOf(ch) >= 0; } function is_punc(ch) { return ",;(){}[]".indexOf(ch) >= 0; } function is_whitespace(ch) { return " \t\n".indexOf(ch) >= 0; } function read_while(predicate) { var str = ""; while (!input.eof() && predicate(input.peek())) str += input.next(); return str; } function read_number() { var has_dot = false; var number = read_while(function(ch){ if (ch == ".") { if (has_dot) return false; has_dot = true; return true; } return is_digit(ch); }); return { type: "num", value: parseFloat(number) }; } function read_ident() { var id = read_while(is_id); return { type : is_keyword(id) ? "kw" : "var", value : id }; } function read_escaped(end) { var escaped = false, str = ""; input.next(); while (!input.eof()) { var ch = input.next(); if (escaped) { str += ch; escaped = false; } else if (ch == "\\") { escaped = true; } else if (ch == end) { break; } else { str += ch; } } return str; } function read_string() { return { type: "str", value: read_escaped('"') }; } function skip_comment() { read_while(function(ch){ return ch != "\n" }); input.next(); } function read_next() { read_while(is_whitespace); if (input.eof()) return null; var ch = input.peek(); if (ch == "#") { skip_comment(); return read_next(); } if (ch == '"') return read_string(); if (is_digit(ch)) return read_number(); if (is_id_start(ch)) return read_ident(); if (is_punc(ch)) return { type : "punc", value : input.next() }; if (is_op_char(ch)) return { type : "op", value : read_while(is_op_char) }; input.croak("Can't handle character: " + ch); } function peek() { return current || (current = read_next()); } function next() { var tok = current; current = null; return tok || read_next(); } function eof() { return peek() == null; } } ``` - 該next()函數并不總是調用read_next(),因為它可能在之前被peek過(在這種情況下,read_next()已經被調用并且stream被提前)。因此我們需要一個current變量跟蹤當前token。 - 我們只支持十進制數與通常的符號(沒有1E5的東西,沒有十六進制,沒有八進制)。但是如果我們需要更多,這些更改只能在read_number()中進行,并且很容易實現。 - 與JavaScript不同,唯一不能在字符串中引用的字符是引號字符本身和反斜杠。你需要反斜杠轉義他們。而且,字符串可能包含換行符,制表符等。我們太長不會解析像\n,\t等的轉義。 現在,我們有足夠強大的工具來方便地編寫解析器(parser)了,但我建議您首先看下?[AST](http://annn.me/implement-a-programming-language-5)?的描述。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看