現在編譯器處理那種「用換行代替分號」的語句邏輯是怎麼做的?
原本以為很簡單,今天在寫Kotlin的Parser的時候簡直想砸牆。
因為換行既是Whitespace也是end of statement,有可能會被stringToken給吃掉,換行就沒了,我還怎麼判斷語句結束?話題加了個Haskell是因為我這個Parser是Haskell寫的。
你可以把字元串先斷行,然後一行一行來。
對於JS那種,你一行parse完發現還不能結束,就接另一行。
對於VB哪種,你一行parse完發現有_,就強制接另一行。
Kotlin不知道是哪種,反正你自己看著辦吧,都很簡單。
Kotlin語言的語法精確定義隱藏在源碼中,一部分在tokener, 一部分在 parser.
使用回車做語句結束比較複雜,只有在:
不是一個字元串的延續不是一個聲明語句的結束不是賦值語句的結束不是函數參數的結束…光是靠語言描述就很長,何況是代碼。
所以,不先寫出精確的語法描述,寫這些語言的Parser, 會讓你的腦袋成為一鍋粥。我感覺你可以去看一下語言標準,一個靠譜的語言都會說明的,什麼時候算分格什麼時候算省略分號
在 Scala 里,滿足這三個條件的換行符會作為語句的結尾:
- 換行符前的 Token 能作為語句的結尾
- 換行符後的 Token 能作為語句的開頭
- Token 出現在允許多條語句的區域中
字面量,標識符,這些關鍵字
- this
- null
- true
- false
- return
- type
- &
以及這些分隔符
- _
- )
- ]
- }
都能作為語句的結尾部分。
而除了這些關鍵字
- catch
- else
- extends
- finally
- forSome
- match
- with
- yield
還有這些分隔符
- ,
- .
- ;
- :
- =
- =&>
- &<-
- &<:
- &<%
- &>:
- #
- [
- )
- ]
- }
剩下的的 Token 都能作為語句的開頭
=========
先去睡覺了,明天繼續寫
某語言使用嚴格的縮進…在標準某語言里,語句只分為複合句和簡單句,簡單句只佔一行,複合句開頭的位置一定要和當前縮進級別相吻合。使用分行的話,其實就是忽略NEWLINE和INDENT這些token。這種過於簡單的設計,天生地製造了表達式和語句的天塹。有時覺得簡直就像一種新的位元組碼,一板一眼。
我 知 道 了
我發現
吃掉前面的Whitespace就沒有這些破事了
請大家原諒我的無知
向前探測一下,不就知道了嗎?符合機器狀態的下個符號,推入,不符合的就不推了。編譯器都會容錯的,一遍編譯儘可能的發現所有編譯錯誤
用parsec簡單粗暴的解決的話,可以自己寫一個處理換行符的組合子來做sepBy,這樣在一個 statement 正常結束之後就可以通過分號或者換行來分隔下一句
isSpaceNotNewline c = isSpace c c /= "
" c /= "
"
-- Cannot use `lexeme` or `space` since they have already included newline
lineBreaker :: Parser String
lineBreaker = many $ skipMany (satisfy isSpaceNotNewline) &>&> endOfLine
stmtBreaker :: Parser String
stmtBreaker = try semi &<|&> lineBreaker
stmtSep :: Parser a -&> Parser [a]
stmtSep p = lexeme $ p `sepBy` stmtBreaker
token 拿來一個換行符,parser能找到一個匹配就是語句結束唄,找不到就是要忽略的唄。
比如
a = b(c,
d)
顯然你吃到換行的時候完不成任何匹配,因此你忽略換行接著下一行啊。
然鵝
a = b
b = c
你吃到換行的時候, 換行是你某個產生式的組成部分, 正常吃,所以a=b就處理完了,所以你接著下一行唄。
如果你產生式都正確的話,那麼把換行當一個token,然後這個不就是「expected里沒有換行token的時候吃到換行token不報錯」 嗎?
就把"
"當成";"用唄發現沒有結束就繼續解析下一行或者發現行末字元前是:就繼續解析直到限定符(比如end)出現唄
我有一個不成熟的小建議
咱們學js吧 末尾加分號 hhh
(有點 沒審題hhh )
那先用預處理器處理一邊全加分號 然後再parse?推薦閱讀:
※程序如何根據變數名在內存中找到存放這個變數的地址?
※如何減小GacLib生成的可執行文件大小?
※OCaml pattern有哪些葵花寶典?
※C語言if與else if寫成的這樣一段代碼效率上或者編譯完成後的結構上是否有區別(主要看補充內容中的詳細)?
※如何實現 C 語言編譯器?