Aby wąż giętki robił to co wymyśli głowa, czyli słów kilka o analizie leksykalnej i TDD w...

23
Aby wąż giętki robił to co wymyśli głowa, czyli słów kilka o analizie leksykalnej i TDD w Pythonie Adam Przybyła <[email protected]> (mail i JID) @AdamPrzybyla (Creative Commons CC-by-nd) WrocPY - Wrocław 2013

Transcript of Aby wąż giętki robił to co wymyśli głowa, czyli słów kilka o analizie leksykalnej i TDD w...

Aby wąż giętki robił to co wymyśli głowa, czyli słów kilka o analizie leksykalnej i TDD

w Pythonie

Adam Przybyła <[email protected]>(mail i JID)

@AdamPrzybyla

(Creative Commons CC-by-nd)WrocPY - Wrocław 2013

Pliki weryfikowalne - XML● SAX● DOM● PULL● XSL● DTD● YAML

Język dziedzinowy - DSL● Graphviz - DOT● Yacc● SQL● HTML● VHDL

Opis CSV w EBNFCSV file

--------

file ::= [ header ] { line }

header ::= [ { entry separator } entry ] newline

line ::= [ { entry separator } entry ] newline

entry ::= character+ | { character* " separator " } | " entry newline entry "

newline ::= \n

separator ::= ,

character ::= a|b|..|A|B|..|0|1|..

escapedQuote ::= ""

Flex i Bison● Flex – analizator leksykalny● Rozszerzona wersja programu lex● Generuje tablice przejść● Parsery LR i LL

Gramatyka pliku definicji

{definicje}

%%

{reguły}

%%

{procedury użytkownika}

Plik konfiguracyjny analizatora leksylalnego%%

, {return COMMA;}

\n {return NEWLINE;}

\r\n {return NEWLINE;}

[^,\n\r]+ {return ITEM;}

Kod Bisona%token COMMA NEWLINE ITEM

%%

csv_file : line_list | line_list line;

line_list : line_list line newline | line newline | line_list newline | newline;

line : line comma | line item | item | comma;

comma : COMMA ;

newline : NEWLINE;

item : ITEM ;

Program wynikowy we Bisonstatic const yytype_uint8 yytranslate[] =

{

0, 2, 2, 2, 2, 2, 2, 2, 2, 2,

2, 2, 2, 2, 2, 2, 2, 2, 2, 2,

2, 2, 2, 2, 2, 2, 2, 2, 2, 2,

2, 2, 2, 2, 2, 2, 2, 2, 2, 2,

2, 2, 2, 2, 2, 2, 2, 2, 2, 2,

2, 2, 2, 2, 2, 2, 2, 2, 2, 2,

2, 2, 2, 2, 2, 2, 2, 2, 2, 2,

2, 2, 2, 2, 2, 2, 2, 2, 2, 2,

2, 2, 2, 2, 2, 2, 2, 2, 2, 2,

Problemy analizy● Shift/Reduce● Reduce/reduce● Stany początkowe● Semantyczne predykaty a[i] (Ruby)

Flex z kernelem● Filtr klasyfikujący ruch w sieci w kernelu● https://github.com/AdamPrzybyla/layer7-flex● Kilka razy szybsze rozwiązanie niż

oryginalne, oparte o wyrażenia regularne

Analizator leksykalny ANTLR● PCTTS● Dla leniwych● Analizator LL(*)● Przejrzysty kod● Generacja kodu Java,c#,Python,C++

Tryby pracy ANTLR● Analizator leksykalny● Tworzenie AST● Walker AST● Generowanie plików graphviz● Przetwarzanie AST● Język template

Przetwarzanie drzew AST

● Kompilator c kiedyś:● Źródło w C● C z rozwiniętymi makrami● Asembler● Plik .o + linkowanie

● Kompilator C teraz:● Kolejne modyfikacje drzew AST

Przepływ w parserze ANTLR

Edytor ANTLRWorks

Gramatyka CSV w ANTLRgrammar csv;

options { language=Python; }

file : record (NEWLINE record)* EOF ;

record : (quoted_field | unquoted_field) (COMMA (quoted_field | unquoted_field))* ;

quoted_field : DQUOTE ( CHAR | COMMA | DQUOTE DQUOTE | NEWLINE)* DQUOTE ;

unquoted_field : CHAR* ;

CHAR : '\u0000' .. '\u0009' | '\u000b' .. '\u000c' | '\u000e' .. '\u0021' | '\u0023' .. '\u002b' | '\u002d' .. '\uffff' ;

COMMA : '\u002c' ;

DQUOTE : '\u0022' ;

NEWLINE : '\u000d'? '\u000a' | '\u000d' ;

Zrozumiały kod (Python) def liczba(self, ):

w = None; p = None

try:

try:

# WrocPy.g:9:19: (p= CYFRY ) # WrocPy.g:9:21: p= CYFRY

p = self.match(self.input, CYFRY, self.FOLLOW_CYFRY_in_liczba33)

if self._state.backtracking == 0: w=p

except RecognitionException, re:

self.reportError(re)

self.recover(self.input, re)

finally:

pass

return w

Wywołanie funkcji – jak w ręcznych parserach

def mIDS(self, ):

try:

_type = IDS

_channel = DEFAULT_CHANNEL

# WrocPy.g:14:4: ( ( 'a' .. 'z' )+ ( '0' .. '9' )* )

# WrocPy.g:14:9: ( 'a' .. 'z' )+ ( '0' .. '9' )*

pass

# WrocPy.g:14:9: ( 'a' .. 'z' )+

cnt1 = 0

while True: #loop1

alt1 = 2

LA1_0 = self.input.LA(1)

Testowanie● GUnit● Unittest z Pythona● Test Driven Development

Test ANTLR3 def test_liczba(self): s="1234" w=parserek(s).liczba() self.assertEqual(w.text,s)

def test_nie_liczba(self): s="A1234" w=parserek(s).liczba() self.assertFalse(w)

Definicja symboli gramatykigrammar WrocPy;options { language=Python; backtrack=true; }@members { def emitErrorMessage(self, msg): pass}liczba returns [w]: p=CYFRY {w=p};zmienna returns [w]: p=IDS {w=p};przypisanie returns [w]: p=IDS '=' p1=CYFRY {w=[p,p1]};IDS: 'a'..'z' + '0'..'9' * ;CYFRY: '0'..'9' + ;WS: .;;

Pytania?