摘要:即創(chuàng)建一個文件,并寫下如下內(nèi)容。然后以此緩存一個字符串,然后,通過調(diào)用來將這個字符串分割成一個一個運算符,并生成。語言中根據(jù)運算符長度分開儲存的。具體來說,如果運算符對應(yīng)的字符串最短的是,最長的是。
上一章留下的那個大大的 TODO 表明,似乎還有什么東西沒寫完。沒錯,所以這一章我們來寫 Sign 狀態(tài)下的代碼。
所謂 Sign 狀態(tài),即是用來處理和生成 Sign 類型的 Token ,即運算符。諸如此類,都是運算符:
+、-、*、/、>=、==、&&、||
相對于 String 字符串啊、Identifier 標(biāo)示符之類的可以通過字符本身來劃分開端和結(jié)尾的類型,Sign 運算符運算符類型卻只能以既定形式來劃分開端和結(jié)尾。
例如,
"hello world" +-&& str_hello_wold
這段“源碼”可以被分割成如下一組 Token,
[""hello world"", " ", "+", "-", "&&", "str_hello_world"]
注意 Sign 運算符類型和 String 字符串類型、Identifier 標(biāo)示符劃分的區(qū)別。對于 “+”、“-” 兩個字符被各自分在一個 Token 中,而 “&&” 卻是由兩個字符組合成一個 Token。因為 tao 語言中沒有 “&” 運算符(沒有與位運算,也沒有短路與非短路與之分),卻擁有 “&&”。
此外,
>=
對于這個字符串,到底應(yīng)該劃分成
[">", "="]
這樣兩個運算符呢,還是應(yīng)該劃分成
[">="]
這樣一個運算符呢?
因為 “>"、"="、">=" 這三個都是合法的 tao 語言運算符。這里為了消除歧義,我規(guī)定必須劃分成 ">=" 。另外,如果存在多種劃分方式,盡量將短的運算符湊成長運算符。
OK,我現(xiàn)在決定把有關(guān) Sign 運算符類型的代碼多帶帶寫一個類。即創(chuàng)建一個 SignParser.java 文件,并寫下如下內(nèi)容。
package com.taozeyu.taolan.analysis; class SignParser { static boolean inCharSet(char c) { //TODO } static Listparse(String str) { //TODO } }
對于,LexicalAnalysis.java ,它只會調(diào)用 SignParser.inCharset(...) 來判斷當(dāng)前讀入的字符是不是某個既定運算符的某個字符。然后以此緩存一個字符串,然后,通過調(diào)用 SignParser.parse(str) 來將這個字符串分割成一個一個運算符,并生成 Token。
現(xiàn)把 LexicalAnalysis.java 中 boolean readChar(char c) 函數(shù)中TODO 消滅掉吧。
} else if(state == State.Sign) { if(SignParser.inCharSet(c)) { readBuffer.append(c); } else { Listlist = SignParser.parse(readBuffer.toString()); for(String signStr:list) { createToken(Type.Sign, signStr); } createType = null; state = State.Normal; moveCursor = false; } }
當(dāng)然,這里需要重載 createToken 一下。
private void createToken(Type type, String value) { Token token = new Token(type, value); tokenBuffer.addFirst(token); readBuffer = null; }
OK,我們在回過頭來寫 SignParser.java 吧。首先,定義幾個靜態(tài)常量。
package com.taozeyu.taolan.analysis; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; import java.util.List; class SignParser { private final static List> signSetList; private final static HashSet signCharSet; private final static int MaxLength, MinLength; static { String[] signArray = new String[] { "+", "-", "*", "/", "%", ">", "<", ">=", "<=", "=", "!=", "==", "=~", "+=", "-=", "*=", "/=", "%=", "&&", "||", "!", "^", "&&=", "||=", "^=", "<<", ">>", "->", "<-", "?", ":", ".", ",", ";", "..", "(", ")", "[", "]", "{", "}", "@", "@@", "$", }; int maxLength = Integer.MIN_VALUE, minLength = Integer.MAX_VALUE; signCharSet = new HashSet<>(); for(String sign:signArray) { int length = sign.length(); if(length > maxLength) { maxLength = length; } if(length < minLength) { minLength = length; } for(int i=0; i (maxLength - minLength + 1); for(int i=0; i< maxLength - minLength + 1; ++i) { signSetList.add(new HashSet<>()); } for(String sign:signArray) { int length = sign.length(); HashSet signSet = signSetList.get(length - minLength); signSet.add(sign); } MaxLength = maxLength; MinLength = minLength; } static boolean inCharSet(char c) { //TODO } static List parse(String str) { //TODO } }
static 塊中的 signArray 變量定義了 tao 語言所用到的所有運算符。之后的代碼解析 signArray 并生成 4 個靜態(tài)常量。
signCharSet:tao 語言所有運算符中可能出現(xiàn)的字符集合。
signSetList:tao 語言中根據(jù)運算符長度分開儲存的 List。具體來說,如果運算符對應(yīng)的字符串最短的 length 是 n,最長的是 m。那么 List 的索引范圍從 0 到 (m - n) 分別儲存字符 length 從 n 到 m 的所有運算符。
MinLength:運算符中字符串 length 的最小長度。
MaxLength:運算符中字符串 length 的最大長度。
然后是填滿那兩個寫著 TODO 的函數(shù)。
static boolean inCharSet(char c) { return signCharSet.contains(c); } static Listparse(String str) throws LexicalAnalysisException { LinkedList rsContainer = new LinkedList<>(); int startIndex = 0; while(startIndex < str.length()) { String matchStr = match(startIndex, str); if(matchStr == null) { throw new LexicalAnalysisException(str.substring(startIndex)); } else { rsContainer.add(matchStr); startIndex += matchStr.length(); } } return rsContainer; } private static String match(int startIndex, String str) { String matchStr = null; int length = str.length() - startIndex; length = Math.min(length, MaxLength); if(length >= MinLength) { for(int i=length - MinLength; i>=0; i--) { int matchLength = i + MinLength; HashSet signSet = signSetList.get(i); matchStr = str.substring(startIndex, startIndex + matchLength); if(signSet.contains(matchStr)) { break; } matchStr = null; } } return matchStr; }
OK,搞定。特別的,如果 parse 劃分運算符過程中發(fā)現(xiàn)留下一個尾巴匹配不了,那么將拋出一個詞法錯誤異常。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/64265.html
摘要:是的,這個系列將呈現(xiàn)一個完整的編譯器從無到有的過程。但在寫這個編譯器的過程中,我可不會偷工減料,該有的一定會寫上的。該語言的虛擬機將運行于之上,同時編譯器將使用實現(xiàn)。我早有寫編譯器的想法之前沒寫過,故希望一邊寫編譯器一邊完成這個系列。 是的,這個系列將呈現(xiàn)一個完整的編譯器從無到有的過程。當(dāng)然,為了保證該系列內(nèi)容的簡潔(也為了降低難度),僅僅保證編譯器的最低要求,即僅能用。但在寫這個編譯...
摘要:在之前的章節(jié)第章從零開始寫個編譯器吧開始寫詞法分析器中我有說,我將函數(shù)設(shè)計成主動調(diào)用的形式,而則是被動調(diào)用的形式。接下來本系列將進(jìn)入編寫語法分析器的階段,不過在此之前,我將抽出一點時間介紹一下語言本身。 上周周末旅游去了,就沒更新了,雖然回到海拔0m的地區(qū),不過目前似乎還在缺氧,所以本次就少更點吧。 這章將結(jié)束詞法分析的部分。 在之前的章節(jié)(第7章從零開始寫個編譯器吧 - 開始寫詞...
摘要:要為語言設(shè)計詞法分析器,首先得知道語言是一種什么樣的語言。,不過首先我們得把詞法分析器能生成的單詞類型定義好了。 要為 tao 語言設(shè)計詞法分析器,首先得知道 tao 語言是一種什么樣的語言。不過呢,我腦海里還沒有 tao 語言具體形象。我還是先貼一段 tao 語言的代碼,大概展示下這是怎么回事吧。 def say_hello_world(who) print hello ...
摘要:現(xiàn)在,讓我們來動手寫編譯器的第一個個文件吧。如其名字所示,這個類實例化的對象用于表示詞法分析器的產(chǎn)物。我希望詞法分析器從源代碼中提取出語素,并根據(jù)上下文推測出單詞類型,從而構(gòu)造出對象。只需要構(gòu)造出類型即可,進(jìn)一步細(xì)分將在的構(gòu)造函數(shù)中進(jìn)行。 現(xiàn)在,讓我們來動手寫編譯器的第一個個java文件吧。本章要寫的類,是Token類。如其名字所示,這個類實例化的對象用于表示詞法分析器 Tokeniz...
摘要:這樣的程序或稱工具有很多現(xiàn)成的可供選擇包括在平臺上可用的,但既然我這個系列叫做從零開始寫個編譯器吧,那顯然如果我用現(xiàn)成的工具,那是犯規(guī)行為。 Parser(語法分析器)的編寫相對于 Tokenizer (詞法分析器)要復(fù)雜得多,因此,在編寫之前可能也會鋪墊得更多一些。當(dāng)然,本系列旨在寫出一個編譯器,所以理論方面只會簡單介紹 tao 語言所涉及的部分。 之前的幾章中,我純手寫了tao 語...
閱讀 1769·2021-11-24 09:39
閱讀 1564·2021-11-16 11:54
閱讀 3504·2021-11-11 16:55
閱讀 1672·2021-10-14 09:43
閱讀 1454·2019-08-30 15:55
閱讀 1242·2019-08-30 15:54
閱讀 3430·2019-08-30 15:53
閱讀 1350·2019-08-30 14:18