2012-09-20 72 views
1

我使用pygments的一個Python插件的詞法分析器。我想爲C++代碼獲取標記,特別是當聲明一個新變量時,例如分段詞法分析器多個標記

int a=3,b=5,c=4; 

這裏A,B,C應是給定類型 「聲明的變量」,它是從

a=3,b=5,c=4; 

這裏A,B,C必須簡單地被給定類型的 「變量」,因爲不同的他們已經被宣佈過。

我想使用詞法分析器的能力,一次掃描多令牌(See Pygments documentation)我想寫沿

(int)(\s)(?:([a-z]+)(=)([0-9]+)(,))*, bygroups(Type,Space,Name,Equal,Number,Comma) 

的線(一個正則表達式「?:」僅僅是告訴Pygments這個分組不應該用在groupgroup中。)

但是,不是匹配行中的任何數量的聲明,它只返回行中最後一個聲明的標記(在這種情況下, c = 4「部分)。我怎樣才能讓它返回行中所有聲明的標記?

回答

2

你需要的是一個有狀態的詞法分析器。之所以你的正則表達式不會 工作是因爲這些組不連續。

int a=3,b=5,c=4; 

在這裏,你想要的字符0..2是類型,3..3空間,4..7名稱,平等 號碼和逗號然後再次名稱,平等,數量和逗號。那不是 好。

解決方法是記住何時看到類型聲明, 進入一個新的詞法分析器模式,該模式一直持續到下一個分號。在pygments文檔中參見 Changing states

下面是一個使用CFamilyLexer的解決方案,並添加了三個新的詞法分析器 個狀態。所以當它看到這樣一行,而在function 狀態:

int m = 3 * a + b, x = /* comments ; everywhere */ a * a; 

首先它消耗:

int 

它匹配我添加了新的規則,所以它進入vardecl狀態:

m 

哦,一個變量的名字!由於詞法分析器處於vardecl狀態,因此這是一個新定義的變量。將其作爲NameDecl令牌發出。然後 進入varvalue狀態。

3 

只是一個數字。

* 

只是一個操作員。

a 

哦,一個變量的名字!但是現在我們處於varvalue狀態,所以它 是而不是一個變量聲明,只是一個常規的變量引用。

+ b 

一個運算符和另一個變量引用。

, 

變量m的值完全聲明。回到vardecl狀態。

x = 

新的變量聲明。

/* comments ; everywhere */ 

另一個狀態被壓入堆棧。在評論標記,將 否則有意義,如;被忽略。

a * a 

值爲x變量。

; 

返回到function狀態。特殊變量聲明規則 已完成。

from pygments import highlight 
from pygments.formatters import HtmlFormatter, TerminalFormatter 
from pygments.formatters.terminal import TERMINAL_COLORS 
from pygments.lexer import inherit 
from pygments.lexers.compiled import CFamilyLexer 
from pygments.token import * 

# New token type for variable declarations. Red makes them stand out 
# on the console. 
NameDecl = Token.NameDecl 
STANDARD_TYPES[NameDecl] = 'ndec' 
TERMINAL_COLORS[NameDecl] = ('red', 'red') 

class CDeclLexer(CFamilyLexer): 
    tokens = { 
     # Only touch variables declared inside functions. 
     'function': [ 
      # The obvious fault that is hard to get around is that 
      # user-defined types won't be cathed by this regexp. 
      (r'(?<=\s)(bool|int|long|float|short|double|char|unsigned|signed|void|' 
      r'[a-z_][a-z0-9_]*_t)\b', 
      Keyword.Type, 'vardecl'), 
      inherit 
     ], 
     'vardecl' : [ 
      (r'\s+', Text), 
      # Comments 
      (r'/(\\\n)?[*](.|\n)*?[*](\\\n)?/', Comment.Multiline), 
      (r';', Punctuation, '#pop'), 
      (r'[~!%^&*+=|?:<>/-]', Operator), 
      # After the name of the variable has been tokenized enter 
      # a new mode for the value. 
      (r'[a-zA-Z_][a-zA-Z0-9_]*', NameDecl, 'varvalue'), 
     ], 
     'varvalue' : [ 
      (r'\s+', Text), 
      (r',', Punctuation, '#pop'), 
      (r';', Punctuation, '#pop:2'), 
      # Comments 
      (r'/(\\\n)?[*](.|\n)*?[*](\\\n)?/', Comment.Multiline),     
      (r'[~!%^&*+=|?:<>/-\[\]]', Operator), 
      (r'\d+[LlUu]*', Number.Integer),    
      # Rules for strings and chars. 
      (r'L?"', String, 'string'), 
      (r"L?'(\\.|\\[0-7]{1,3}|\\x[a-fA-F0-9]{1,2}|[^\\\'\n])'", String.Char), 
      (r'[a-zA-Z_][a-zA-Z0-9_]*', Name), 
      # Getting arrays right is tricky. 
      (r'{', Punctuation, 'arrvalue'), 
     ], 
     'arrvalue' : [ 
      (r'\s+', Text), 
      (r'\d+[LlUu]*', Number.Integer), 
      (r'}', Punctuation, '#pop'), 
      (r'[~!%^&*+=|?:<>/-\[\]]', Operator), 
      (r',', Punctuation), 
      (r'[a-zA-Z_][a-zA-Z0-9_]*', Name), 
      (r'{', Punctuation, '#push'), 
     ] 
    } 

code = ''' 
#include <stdio.h> 

void main(int argc, char *argv[]) 
{ 
    int vec_a, vec_b; 
    int a = 3, /* Mo;yo */ b=5, c=7; 
    int m = 3 * a + b, x = /* comments everywhere */ a * a; 
    char *myst = "hi;there"; 
    char semi = ';'; 
    time_t now = /* Null; */ NULL; 
    int arr[10] = {1, 2, 9/c}; 
    int foo[][2] = {{1, 2}}; 

    a = b * 9; 
    c = 77; 
    d = (int) 99; 
} 
''' 
for formatter in [TerminalFormatter, HtmlFormatter]: 
    print highlight(code, CDeclLexer(), formatter())