Syngram Helper開(kāi)始設(shè)計(jì):一個(gè)能用來(lái)寫(xiě)編譯器的工具
大概一年前曾經(jīng)用C++開(kāi)發(fā)了一個(gè)可以在C++中直接寫(xiě)上下文無(wú)關(guān)文法的上下文無(wú)關(guān)文法分析器。這玩意兒叫Syngram。Syngram曾經(jīng)做了兩次,第一次做成了用一個(gè)類(lèi)去讀文法文件,后來(lái)不爽就改成了直接在C++里面寫(xiě)的。我弄了一個(gè)叫Term的類(lèi),重載了一些操作符,于是你可以搞分支、可選、錯(cuò)誤處理等復(fù)雜的文法推導(dǎo)式。現(xiàn)在打算做一個(gè)周邊工具。
這個(gè)周邊工具的由來(lái)是這樣的。后來(lái)我用Syngram開(kāi)發(fā)了一個(gè)支持namespace、class和函數(shù)的動(dòng)態(tài)語(yǔ)言Vczh Free Script 2.0 beta,這個(gè)動(dòng)態(tài)語(yǔ)言支持面向?qū)ο蟆⒎盒鸵约昂瘮?shù)式等若干范式,語(yǔ)言可以調(diào)用編譯器編譯一個(gè)字符串并操作。虛擬機(jī)被我做成了兩個(gè)dll,一個(gè)是給native c++調(diào)用的,另一個(gè)是給C# 3.0調(diào)用的。編譯器我用了Syngram,在上面那個(gè)鏈接所提供的代碼里就有一個(gè)文件,布滿(mǎn)了文法推導(dǎo)式以及每一個(gè)式子的語(yǔ)義函數(shù)。旁邊的一個(gè)文件寫(xiě)了一個(gè)很大的語(yǔ)法樹(shù)數(shù)據(jù)結(jié)構(gòu),是一組C++類(lèi)。
這門(mén)腳本語(yǔ)言的第一個(gè)版本的編譯器開(kāi)發(fā)長(zhǎng)達(dá)三天,因?yàn)槲乙ㄒ惶鞎r(shí)間做文法,一天時(shí)間做語(yǔ)法樹(shù),一天時(shí)間生成指令。第一版只支持閉包和數(shù)組。這么小的語(yǔ)言寫(xiě)一個(gè)編譯器花了三天實(shí)在是浪費(fèi)的太多了,因?yàn)楹髞?lái)我發(fā)現(xiàn)語(yǔ)法樹(shù)是一個(gè)很有規(guī)律的東西。我就在想,能不能我就寫(xiě)一點(diǎn)點(diǎn)東西,然后有一個(gè)工具就幫我把這些代碼寫(xiě)出來(lái)呢?前幾天洗澡的時(shí)候想到了一個(gè)解決方案。
我可以用一種很簡(jiǎn)便的方法寫(xiě)一個(gè)可以被轉(zhuǎn)換成C++的類(lèi)、列表、字典和枚舉類(lèi)型的語(yǔ)法樹(shù)模型,然后將一點(diǎn)點(diǎn)東西添加在推導(dǎo)式里面告訴推導(dǎo)式創(chuàng)建什么樣的對(duì)象、如何修改對(duì)象、列表和字典等。錯(cuò)誤信息的產(chǎn)生以及錯(cuò)誤恢復(fù)已經(jīng)在Syngram里面實(shí)現(xiàn)了,很容易支持進(jìn)去。于是現(xiàn)在又變成了類(lèi)似讀文法文件產(chǎn)生代碼的東西了,不過(guò)跟Syngram第一版有很大區(qū)別。
首先,Syngram作為一個(gè)C++的類(lèi)庫(kù),仍然可以支持在C++里直接寫(xiě)文法、錯(cuò)誤處理以及語(yǔ)義函數(shù)。
第二,這個(gè)新的工具讀入文法文件之后,產(chǎn)生的并不是一個(gè)完整的語(yǔ)法分析器,而是一些基于Syngram類(lèi)庫(kù)的代碼。
第三,這個(gè)新的工具的最大好處在于可以在自己寫(xiě)的語(yǔ)義函數(shù)(將分析結(jié)果轉(zhuǎn)換成自己想要的語(yǔ)法樹(shù))中和自己寫(xiě)語(yǔ)法樹(shù)的數(shù)據(jù)結(jié)構(gòu)(特別是相應(yīng)的一些虛函數(shù)族)這兩個(gè)沒(méi)有挑戰(zhàn)性而且很煩的工作中解放出來(lái)。工具將根據(jù)輸入的文件生成用于表達(dá)數(shù)據(jù)結(jié)構(gòu)的一堆類(lèi)(通常是幾十個(gè)),根據(jù)需求自動(dòng)分成幾個(gè)繼承的組,為他們添加一兩個(gè)使用了Visitor模式的接口。于是虛函數(shù)的方便被轉(zhuǎn)移到了Visitor模式的產(chǎn)物中,自己的代碼跟生成的代碼完全隔離。
最后,說(shuō)不定這個(gè)工具有GUI。
前幾天完成了Syngram所屬的Vczh Library ++ 2.0中的GUI Framework預(yù)覽版。這個(gè)東西的初衷是寫(xiě)給我自己用的,因?yàn)槲液懿幌矚gMFC等有BEGIN_MESSAGE_MAP或者需要我處理WPARAM和LPARAM的界面庫(kù),于是自己弄了一個(gè)。不過(guò)GUI Framework實(shí)在是龐大,一個(gè)人做起來(lái)也是比較吃力的。如果我心情好GUI 1.0 beta比這個(gè)Syngram Helper早完成的話(huà),那么Syngram Helper就會(huì)有GUI用了。今天寫(xiě)了個(gè)簡(jiǎn)單的語(yǔ)法文件的文法,貼出來(lái)先。里面的注釋用英語(yǔ)僅僅是因?yàn)槲覒械膩?lái)回切換輸入法,估計(jì)有一堆語(yǔ)法錯(cuò)誤,不要計(jì)較。
KEYWORD ->class
->bool
->int
->double
->string
->list
->map
->term
->infer
OPERATOR ->[
->]
-><
->>
->,
->=
->|
->:
->#
->@
IDENT ->[a-zA-Z_]\w*
REGEX ->[^\r\n]+\r\n
COMMENT ->'[^\r\n]*\r\n
STRING ->"([^\\"]|\\\.)*"
CLASS ->class IDENT [: IDENT] "{" MEMBERS "}"
ENUM ->enum IDENT "{" IDENTS "}"
MEMBER ->TYPE IDENT
->STRING IDENT //external member, treat STRING as type, can not be accessed in semantic rule
TYPE ->bool
->int
->double
->string
->token //store a token
->list<TYPE>
->map<TYPE,TYPE>
->multimap<TYPE,TYPE>
LEXICAL ->term IDENT REGEX
SYNTAX ->infer TYPE IDENT RULE_EXP
RULE_EXP ->[ RULE_EXP "|" ]RULE_SERIES
RULE_SERIES ->[ RULE_SERIES ]RULE_UNIT
RULE_UNIT ->RULE_TERM
->"[" RULE_EXP "]"
RULE_TERM ->IDENT [ "[" SEMANTICS "]" ]
SEMANTIC ->SEMANTIC_TARGET [ "=" SEMANTIC_SOURCE ]
SEMANTIC_TARGET ->:IDENT // Field of result, new map item or new list item
SEMANTIC_TARGET ->#key // add item in result map using this key
SEMANTIC_TARGET ->#value // add item in result map using this value
SEMANTIC_TARGET ->#item // add item in result list using this value
SEMANTIC_TARGET ->#result // treat this value as the result, result map or result list
// new item in a path is not required if there is at least a #result term in the same path
SEMANTIC_TARGET ->#alias // name this value as SEMANTIC_SOURCE
SEMANTIC_TARGET ->#error // treat SEMANTIC_SOURCE as error message
SEMANTIC_TARGET ->#keyerror // treat SEMANTIC_SOURCE as error message and raise an error if the specified key exists
SEMANTIC_TARGET ->#past // past this term when error occurs
SEMANTIC_TARGET ->#read // read a token when error occurs
SEMANTIC_TARGET ->#exit // terminate parsing when error occurs
SEMANTIC_SOURCE ->IDENT // value of alias or enumeration item
SEMANTIC_SOURCE ->STRING // value of error message
SEMANTIC_SOURCE ->@IDENT // value of external string
/*
#key, #value, #item, #result : can only appears once in a path
#alias : can not be conflict in a path
#error, #past, #read, #exit : can only appears once in a term
#alias, #error : must be used with SEMANTIC_SOURCE
#keyerror : can only appears once in a term, must be used with #key
:IDENT : can not be conflict in a path
*/
IDENTS ->IDENT [IDENTS]
MEMBER ->MEMBER [MEMBERS]
SEMANTICS ->SEMANTIC ["," SEMANTICS]
TYPES ->( CLASS | ENUM )[TYPES]
LEXICALS ->LEXICAL[LEXICALS]
SYNTAXES ->SYNTAX[SYNTAXES]
PROGRAM ->TYPES LEXICALS SYNTAXES
浙公網(wǎng)安備 33010602011771號(hào)