2012-01-08 42 views
1

我正在開發一個項目,將用Second Life的LSL語言編寫的腳本轉換爲Lua。我正在學這個項目的flex和btyacc。事實上,這是一個更大的項目,這只是其中的一部分。 http://www.sqlite.org/cgi/src 作爲第一步,我想確保我生成的AST是輸入的準確反映。所以我的想法是從AST生成一個新文件,然後比較它們。這意味着我需要在AST中包含空格和註釋,以便當我使用它來激發結果文件時,它包含完全相同的空格和註釋。驗證您的flex和yacc代碼是否通過生成源代碼來與原始代碼進行比較

我一直有處理空白的問題。搜索和試驗幾天而沒有獲得任何地方。我看到的每個示例都只是忽略了空白區域,並未將其存儲以供以後使用。我想我會對評論有完全相同的問題,因爲它們基本上只是另一種被忽略的空間形式。

我原以爲這是一個標準的事情,但我找不到任何例子。任何人都知道類似的事情的例子?

源代碼在github上,如果有人想看看它並提出一種方法。

https://github.com/onefang/SledjHamr/blob/experimental/LuaSL/srcLuaSL_yaccer.lLuaSL_yaccer.yLuaSL_LSL_tree.hLuaSL_LSL_tree.c

識別空間柔性線有它的作用註釋掉。如果我取消註釋,我會得到一個解析錯誤。

SOLUTION

我利用bdonlan的解決方案,但我調到這個項目交給執行它的中間用檸檬代替btyacc。這就是我所做的。下面的源代碼被簡化了。

用檸檬創建一個調用詞法分析器的循環,然後您將詞法分析器調用的結果傳遞給解析器。我的循環每次都會分配一個新的yylval結構,該結構包含一個用於空格或註釋的char指針。我也使用這個yylval作爲我的AST節點結構,因爲它已經包含了我需要的大部分內容,並且不必花費時間重新分配內存或複製東西。

struct AST_node 
{ 
    struct AST_node *left; 
    struct AST_node *right; 
    char    *white_space; 
    struct AST_token *token; /* common() stashes a pointer to a token structure here. */ 
    int    line, column; 
    union 
    { 
     /* The various types of symbols are stored here as per usual, including this one - */ 
     int operationValue; 
    } value; 
} 

struct AST_node *lval = calloc(1, sizeof(struct AST_node); 

while((yv = yylex(lval)) != 0) 
{ 
    Parse(pParser, yv, lval); 
    lval = calloc(1, sizeof(struct AST_node)); 
} 

在詞法分析器中,我有一個從每個正則表達式操作部分調用的common()函數。除此之外,如果它是註釋或空白區域,我將文本保存到靜態累加器。如果它不是註釋或空格,那麼我將存儲器(如果存在)存儲在yylval結構中,並清除累加器。這個累加器將空白和註釋聚集在一起,因此yylval中的任何給定的一個都可以包含兩者。

詞法分析器如果只是空白/註釋,它不會返回符號,因此它會積累它們,直到發現一個實際的符號。

在檸檬中,所有終端和非終端都是yylval使用的類型,所以我可以將它傳遞給操作部分中的C代碼。例如 -

expr(A) ::= expr(B) LSL_ADD(D) expr(C). { A = addOperation(D, LSL_ADD, B, C); } 

LSL_ADD是從詞法分析器發射的符號,並且d是它的價值,這是我在主循環中創建的的yylval傳遞給詞法分析器。在這種情況下,addOperation()將指向左右AST節點(B和C)的指針添加到AST結構(D)中,並將其中的符號集合到(以便後來我知道這個特定操作是一個加法) 。

struct AST_node *addOperation(struct AST_node *lval, int type, struct AST_node *left, struct AST_node *right) 
{ 
    lval->left = left; 
    lval->right = right; 
    lval->value.operationValue = type; 
    return lval; 
} 

後來,當我從AST重建原始的源代碼,我只輸出白色的空間/在同AST節點輸出符號之前的評論。

是的,我知道在上面的代碼中有一些重複,不需要存儲在令牌成員和聯合中的類型。我稍後會清理掉我的代碼。但現在,它用服務器來說明發生了什麼。

+0

我是這個網站的新手,不確定Sangeeth編輯的內容是什麼。我喜歡我的空間,就像他們在哪裏一樣,添加連字符來「取消註釋」,對我來說似乎過於迂腐。聳聳肩 – 2012-01-09 11:21:55

+0

是啊,如果你問我的一切,但文件名標記是過度的。這是'建議編輯'功能的缺點 - 相對較新的用戶進行了過度的編輯,只要他們不直接看到_wrong_他們往往會經歷...:/如果您不喜歡編輯,總是隨時回滾你自己的帖子。 – bdonlan 2012-01-10 00:34:59

+0

查看我的回答,瞭解如何從AST重新生成源文本。它討論了你必須捕獲的內容,並提供了一些如何實現的想法。請參閱http://stackoverflow.com/a/5834775/120163 – 2016-05-07 20:48:41

回答

3

讓AST成爲原始來源的精確的可逆轉化是很少見的。例如,括號可能在解析過程中丟失(僅用於優先級,但在最終的AST中消失),或者可能會消除空白。

存儲令牌的行和/或字符偏移量以便錯誤消息可以引用它們的來源是相當常見的,但這對於完全可逆性來說是不夠的。

我會建議沒有一個完全可逆的AST,而是有一個已知的AST結果和輸入生成它們的測試套件。但是,如果你要,你可以所有空白存儲與終端令牌一起 - 例如,如果你有這樣的代碼:

1 + /* this is a comment */ 2 

則對應於+ AST的節點將包含+之前的空單,和節點對於2將包含/* this is a comment */作爲額外的空白數據。然後,當您轉換轉換時,您可以隨時恢復這個空白。當然,這也需要語法特徵的明確編碼,例如括號。使用lex/yacc,你可以通過維護一個單獨的空白/註釋(或索引到輸入緩衝區)累加器來實現這一點。當你看到空格或註釋時,更新這個累加器,但不會發出令牌。當您擊中任何其他令牌(包括EOF)時,將此數據移至不同的累加器並重置主累加器。一旦你回到yacc,你的yacc終端可以檢查這個二級累加器,並將它們存儲到你分配給終端的任何數據結構中。

+0

我正在跟蹤括號之類的內容,並會在我整理出空白空間後跟蹤評論。考慮到這些腳本的性質,我也有大量預先存在的腳本,但「已知結果」對它們來說很難編碼。因此,簡單的做法是逆轉AST,並與大量現有的腳本進行比較。 – 2012-01-09 11:12:23

+0

我喜歡你的蓄電池的想法。我會試驗一下,看看我是怎麼走的。 – 2012-01-09 11:17:48

+0

我是這個網站的新手,顯然我沒有足夠的聲望去投票。 – 2012-01-09 11:19:14