2012-04-12 19 views
8

मैं वर्तमान में पार्सिंग के बारे में सीख रहा हूं लेकिन मैं एएसटी उत्पन्न करने के तरीके के बारे में थोड़ा उलझन में हूं। मैंने एक पार्सर लिखा है जो सही ढंग से सत्यापित करता है कि क्या अभिव्यक्ति व्याकरण के अनुरूप होती है (जब यह अभिव्यक्ति अनुरूप होती है और जब यह नहीं होती है तो यह अपवाद उठाता है)। एएसटी बनाने के लिए मैं यहां से कहां से जाऊं? मुझे अपने एलएल (1) पार्सर के निर्माण पर बहुत सारी जानकारी मिली, लेकिन एएसटी बनाने के लिए बहुत कम चल रहा था।मैन्युअल रूप से एएसटी कैसे बनाएं?

मेरे वर्तमान कोड (बहुत सरल रूबी में लिखा है, और एक lexer और एक पार्सर सहित) GitHub पर यहाँ पाया जाता है: https://gist.github.com/e9d4081b7d3409e30a57

कोई व्याख्या कर सकते हैं मैं कैसे क्या मैं एक एएसटी को वर्तमान से जाना?

वैकल्पिक रूप से, यदि आप रुबी से अपरिचित हैं, लेकिन सी को जानते हैं, तो क्या आप मुझे बता सकते हैं कि मैं recursive descent parsing विकिपीडिया लेख में सी कोड के लिए एएसटी कैसे बना सकता हूं।

कृपया ध्यान दें, मैं अपने लिए काम करने के लिए yacc या antlr जैसे पार्सर जेनरेटर का उपयोग नहीं करना चाहता, मैं स्क्रैच से सबकुछ करना चाहता हूं।

धन्यवाद!

उत्तर

6

आपको उस कॉलम के साथ मेल खाने वाले प्रत्येक प्रतीक को जोड़ना होगा जो पेड़ के उस छोटे हिस्से को बनाता है। उदाहरण के लिए, आइए काफी आम निर्माण करें: नेस्टेड फ़ंक्शन कॉल।

a(b()) 

आपका टर्मिनल यहां टोकन हैं कुछ की तरह:

  • L_PAREN = '('
  • R_PAREN = ')'
  • IDENTIFIER = [az] +

और आपके nonterminal प्रतीकों कुछ हैं:

  • FUNCTION_CALL = पहचानकर्ता, L_PAREN, R_PAREN
  • या;
  • FUNCTION_CALL = पहचानकर्ता, L_PAREN, FUNCTION_CALL, R_PAREN

जाहिर है नियम FUNCTION_CALL के लिए ऊपर दूसरा विकल्प पुनरावर्ती है।

आपके पास पहले से एक पार्सर है जो जानता है कि उसे एक वैध प्रतीक मिला है। जिस बिट को आप याद कर रहे हैं वह नियम में कॉलबैक संलग्न करना है, जो इनपुट के रूप में इसके घटकों को प्राप्त करता है और एएसटी में उस नोड का प्रतिनिधित्व करने वाला एक मान (आमतौर पर) देता है।

कल्पना कीजिए अगर हमारे FUNCTION_CALL शासन से पहले विकल्प के ऊपर एक कॉलबैक था:

Proc.new do |id_tok, l_paren_tok, r_paren_tok| 
    { item: :function_call, name: id_tok, args: [] } 
end 

यही मतलब यह होगा कि एएसटी मिलान से उत्पन्न:

a() 

होगा:

{ 
    item: :function_call, 
    name: "a", 
    args: [] 
} 

अब इसे अधिक जटिल a(b()) पर निकालने के लिए ।क्योंकि पार्सर रिकर्सिव है, यह पहले b() को पहचान लेगा, कॉलबैक जो हमारे ऊपर है, लेकिन "ए" के बजाय "बी" के साथ।

अब चलिए नियम के साथ जुड़े कॉलबैक को परिभाषित करते हैं जो दूसरे विकल्प से मेल खाता है। यह बहुत समान है, सिवाय यह भी तर्क यह पारित किया गया था के साथ सौदों:

Proc.new do |id_tok, l_paren_tok, func_call_item, r_paren_tok| 
    { item: :function_call, name: id_tok, args: [ func_call_item ] } 
end 

क्योंकि पार्सर पहले से ही b() मान्यता प्रदान की है और AST का वह हिस्सा अपने कॉलबैक से वापस आ रहा था, जिसके परिणामस्वरूप पेड़ अब है:

{ 
    item: :function_call, 
    name: "a", 
    args: [ 
    { 
     item: :function_call, 
     name: "b", 
     args: [] 
    } 
    ] 
} 

उम्मीद है कि यह आपको विचार के लिए कुछ भोजन देता है। उन सभी टोकन को पार करें जो आप नियमित रूप से मेल खाते हैं जो आपके एएसटी के बहुत छोटे हिस्सों का निर्माण करता है।

+0

आपकी टिप्पणी के लिए धन्यवाद! मेरी यात्रा में मैंने सीखा कि एक 'पार्स पेड़' एक 'एएसटी' से अलग है - क्या आप मुझे बता सकते हैं कि आपने यहां जो भी उत्पन्न किया है वह 'पार्स पेड़' की बजाय एएसटी है? बस उत्सुक :) – horseyguy

+0

मैंने कभी भी दो चीजों के बारे में सोचा नहीं है, वास्तव में, क्षमा करें। यदि कोई फर्क पड़ता है, तो ऐसा कुछ नहीं है जिसे मैंने कभी सामना किया है। क्या आप एक पार्स पेड़ बनाम एक पार्स पेड़ के बारे में बात कर रहे हैं, शायद? – d11wtq

+0

'सार सिंटेक्स ट्री' के लिए विकिपीडिया पेज से: "कंप्यूटर विज्ञान में, एक सार वाक्यविन्यास पेड़ (एएसटी), या सिर्फ सिंटैक्स पेड़, एक प्रोग्रामिंग भाषा में लिखे गए स्रोत कोड की अमूर्त वाक्य रचनात्मक संरचना का वृक्ष प्रतिनिधित्व है।" – d11wtq

0

ठीक है, तो यहां मैं फिर से हूं (और नहीं, इस जवाब में स्किंटिला प्रति से कुछ लेना देना नहीं है, हालांकि यह एक बार प्रोग्रामिंग भाषा/कंपाइलर डिजाइन साहसिक का हिस्सा था)।

क्या आपने Lex/Yacc का उपयोग करने पर विचार किया है? अस्तित्व का उनका मुख्य कारण यही है (= पार्सिंग; लेक्सर्स और पार्सर्स लिखना, और इस प्रकार, AST एस बनाने का तरीका), साथ ही वे बिल्कुल सी-अनुकूल हैं।


यहाँ एक किसी न किसी उदाहरण (मेरे अपने मुक्त-स्रोत MathMachine संकलक से लिया गया) है।

mm_lexer.l (Lexer)

%{ 
/* 
MathMachine 
Copyright (C) 2009-2011 Dr.Kameleon 

This program is free software: you can redistribute it and/or modify 
it under the terms of the GNU General Public License as published by 
the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version. 

This program is distributed in the hope that it will be useful, 
but WITHOUT ANY WARRANTY; without even the implied warranty of 
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
GNU General Public License for more details. 

You should have received a copy of the GNU General Public License 
along with this program. If not, see <http://www.gnu.org/licenses/> 
*//* 

MM_LEXER.L 

*/ 

#include "mathmachine.h" 

#include <stdio.h> 
#include "y.tab.h" 

void count(); 
%} 

DIGIT   [0-9] 
LETTER   [a-zA-Z_] 
HEX   [a-fA-F0-9] 
BINARY   [0-1] 

%% 
^[ \t]*"//".*\n    { /* This is a '//' single-line comment */ } 
^[ \t]*"#!".*\n    { /* This is a '#!' single-line comment */ } 
"use"     { count(); return(USE); } 
"set"     { count(); return(SET); } 
"let"     { count(); return(LET); } 
"ret"     { count(); return(RET); } 
"put"     { count(); return(PUT); } 
"get"     { count(); return(GET); } 
"if"     { count(); return(IF); } 
"else"     { count(); return(ELSE); } 
"loop"     { count(); return(LOOP); } 
"save"     { count(); return(SAVE); } 
"exec"     { count(); return(EXEC); } 


"true"     { count(); return(TRUE); } 
"false"     { count(); return(FALSE); } 

{LETTER}({LETTER}|{DIGIT})*  { count(); return(ID); } 

{DIGIT}+    { count(); return(DECIMAL);  /* DECIMAL NUMBER */} 
0"h"{HEX}+    { count(); return(HEXADECIMAL); /* HEXADECIMAL NUMBER */} 
0"b"{BINARY}+    { count(); return(BINARY); /* BINARY NUMBER */} 
{DIGIT}+"."{DIGIT}+   { count(); return(REAL); /* REAL NUMBER */} 

\"(\\.|[^\\"])*\"   { count(); return(STRING); } 

"=="     { count(); return(EQ_OP); } 
"<="     { count(); return(LE_OP); } 
">="     { count(); return(GE_OP); } 
"<"     { count(); return(LT_OP); } 
">"     { count(); return(GT_OP); } 
"!="     { count(); return(NE_OP); } 

"-->"     { count(); return(RANGE); } 

"("     { count(); return('('); } 
")"     { count(); return(')'); } 
"{"     { count(); return('{'); } 
"}"     { count(); return('}'); } 
"["     { count(); return('['); } 
"]"     { count(); return(']'); } 

"-"     { count(); return('-'); } 
"+"     { count(); return('+'); } 
"*"     { count(); return('*'); } 
"/"     { count(); return('/'); } 

"="     { count(); return('='); } 
";"     { count(); return(';'); } 
","     { count(); return(','); } 
":"     { count(); return(':'); } 
"."     { count(); return('.'); } 
"?"     { count(); return('?'); } 
"%"     { count(); return('%'); } 
"&"     { count(); return('&'); } 
"$"     { count(); return('$'); } 
"#"     { count(); return('#'); } 
"@"     { count(); return('@'); } 
"|"     { count(); return('|'); } 
"!"     { count(); return('!'); } 
"~"     { count(); return('~'); } 
"^"     { count(); return('^'); } 

[ \t\v\n\f]    { count(); } 
.     { /* ignore it */ } 



%% 

int yycolumn = 0; 

void count() 
{ 
    int i; 

    for (i = 0; yytext[i] != '\0'; i++) 
     if (yytext[i] == '\n') 
      yycolumn = 0; 
     else if (yytext[i] == '\t') 
      yycolumn += 8 - (yycolumn % 8); 
     else 
      yycolumn++; 

    // ECHO; 
    yylval.str=strdup(yytext); 
} 

mm_parser.y (पार्सर)

%{ 
/* 
MathMachine 
Copyright (C) 2009-2011 Dr.Kameleon 

This program is free software: you can redistribute it and/or modify 
it under the terms of the GNU General Public License as published by 
the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version. 

This program is distributed in the hope that it will be useful, 
but WITHOUT ANY WARRANTY; without even the implied warranty of 
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
GNU General Public License for more details. 

You should have received a copy of the GNU General Public License 
along with this program. If not, see <http://www.gnu.org/licenses/> 
*//* 

MM_PARSER.Y 

*/ 

#include "mathmachine.h" 
#include <stdio.h> 
#include <string.h> 

void yyerror(const char *str) 
{ 
    fflush(stdout); 
    printf("\n%*s\n%*s\n", yycolumn, "^", yycolumn, str); 
} 

int yywrap() 
{ 
    return 1; 
} 
%} 

%union 
{ 
    char* str; 
    mm_st_exec*   _st_exec; 
    mm_st_use*   _st_use; 
    mm_st_set*   _st_set; 
    mm_st_ret*   _st_ret; 
    mm_st_let*   _st_let; 
    mm_st_get*   _st_get; 
    mm_st_loop*    _st_loop; 
    mm_st_if*   _st_if; 
    mm_st_put*   _st_put; 
    mm_st_save*   _st_save; 
    mm_condition*   _condition; 
    mm_argument*   _argument; 
    mm_function_call*  _function_call; 
    mm_expression_node*  _expression_node; 
    mm_statement*   _statement; 
    mm_statement_list*  _statement_list; 
    mm_expression_list*  _expression_list; 
    mm_id_list*   _id_list; 
    comparison_operator_type _comparison_op_type; 
} 

%token <str> SET LET PUT GET IF ELSE LOOP USE SAVE LOAD TIME RET EXEC 
%token <str> ID DECIMAL HEXADECIMAL BINARY REAL STRING 
%token <str> EQ_OP LE_OP GE_OP LT_OP GT_OP NE_OP RANGE 
%token <str> TRUE FALSE 

%type <str> number boolean 

%type <_comparison_op_type> comparison_operator 

%type <_function_call> function_call 

%type <_id_list> id_list 

%type <_condition> condition 
%type <_argument> argument 

%type <_expression_node> expression 

%type <_expression_list> expression_list 

%type <_st_exec> exec_statement 
%type <_st_use> use_statement 
%type <_st_ret> ret_statement 
%type <_st_let> let_statement 
%type <_st_get> get_statement 
%type <_st_loop> loop_statement 
%type <_st_if> if_statement 
%type <_st_put> put_statement 
%type <_st_set> set_statement 
%type <_st_save> save_statement 

%type <_statement> statement 

%type <_statement_list> statement_list block main 

%left '+' '-' 
%left '*' '/' '%' 
%nonassoc UMINUS 

%expect 11 

%start main 

%% 

//--------------------------- 
// The Basic Elements 
//--------------------------- 

number 
    : DECIMAL  { $$ = $1; }     
    | HEXADECIMAL { $$ = $1; }    
    | BINARY  { $$ = $1; }    
    | REAL  { $$ = $1; }    
    ; 

boolean 
    : TRUE  { $$ = $1; } 
    | FALSE  { $$ = $1; } 
    ; 

function_call 
    : ID '(' ')'   { $$ = new mm_function_call($1,NULL); } 
    | ID '(' expression_list ')' { $$ = new mm_function_call($1,$3); } 
    ; 

argument 
    : number  { $$ = new mm_argument($1,number); } 
    | STRING  { $$ = new mm_argument($1,alpha); } 
    | boolean  { $$ = new mm_argument($1,boolean); } 
    | function_call { $$ = new mm_argument($1,function); } 
    | ID  { $$ = new mm_argument($1,variable); } 
    ; 

comparison_operator 
    : EQ_OP  { $$ = eq_operator; } 
    | LT_OP  { $$ = lt_operator; } 
    | GT_OP  { $$ = gt_operator; } 
    | LE_OP  { $$ = le_operator; } 
    | GE_OP  { $$ = ge_operator; } 
    | NE_OP  { $$ = ne_operator; } 
    ; 

//--------------------------- 
// The Building Blocks 
//--------------------------- 

id_list 
    : ID    { $$ = new mm_id_list(); 
          $$->addId($1); }      
    | id_list ',' ID   { $1->addId($3); $$=$1; } 
    ; 

expression 
    : argument     { $$ = new mm_expression_node($1); } 
    | '(' expression ')'    { $$ = $2; } 
    | expression '+' expression   { $$ = new mm_expression_node(new mm_argument((char*)"+",oper),$1,$3,operator_node); } 
    | expression '-' expression   { $$ = new mm_expression_node(new mm_argument((char*)"-",oper),$1,$3,operator_node); } 
    | expression '*' expression   { $$ = new mm_expression_node(new mm_argument((char*)"*",oper),$1,$3,operator_node); } 
    | expression '/' expression   { $$ = new mm_expression_node(new mm_argument((char*)"/",oper),$1,$3,operator_node); } 
    | expression '%' expression   { $$ = new mm_expression_node(new mm_argument((char*)"%",oper),$1,$3,operator_node); } 
    | expression '^' expression   { $$ = new mm_expression_node(new mm_argument((char*)"^",oper),$1,$3,operator_node); } 
    | '-' argument %prec UMINUS   { } 
    ; 

expression_list 
    : expression    { $$ = new mm_expression_list(); 
           $$->addExpression(new mm_expression($1)); }      
    | expression_list ',' expression  { $1->addExpression(new mm_expression($3)); $$=$1; } 
    ; 

condition 
    : expression     { $$ = new mm_condition(new mm_expression($1),empty_operator,NULL); } 
    | expression comparison_operator expression  { $$ = new mm_condition(new mm_expression($1), $2, new mm_expression($3)); } 
    ; 

//--------------------------- 
// The Statements 
//--------------------------- 

exec_statement 
    : EXEC STRING ';'    { $$ = new mm_st_exec($2); } 
    ; 

use_statement 
    : USE STRING ';'    { $$ = new mm_st_use($2); /*printf("USE statement : %s\n",$2);*/ } 
    ; 

set_statement 
    : SET ID '(' id_list ')' '=' expression ';' { 
            mm_st_ret* rt = new mm_st_ret(new mm_expression($7)); 
            mm_statement_list* stlist = new mm_statement_list(); 
            mm_statement* st = new mm_statement(ret_statement,rt); 
            stlist->addStatement(*st); 
            $$ = new mm_st_set($2,$4,stlist); 
           } 
    | SET ID '(' id_list ')' '=' block  { $$ = new mm_st_set($2,$4,$7); } 
    ; 

let_statement 
    : LET ID '=' expression ';'   { $$ = new mm_st_let($2,new mm_expression($4)); } 
    ; 

get_statement 
    : GET ID ';'     { $$ = new mm_st_get($2); } 
    ; 

ret_statement 
    : RET expression ';'    { $$ = new mm_st_ret(new mm_expression($2)); } 
    ; 

put_statement 
    : PUT expression_list ';'    { $$ = new mm_st_put($2); } 
    ; 

if_statement 
    : IF '(' condition ')' block   { $$ = new mm_st_if($3,$5,NULL); } 
    | IF '(' condition ')' block ELSE block  { $$ = new mm_st_if($3,$5,$7); } 
    ; 

loop_statement 
    : LOOP '(' condition ')' block   { $$ = new mm_st_loop($3,$5); } 
    ; 

save_statement 
    : SAVE expression_list '@' STRING ';'  { $$ = new mm_st_save($2,$4); } 
    ; 

statement 
    : exec_statement    { $$ = new mm_statement(exec_statement,$1); } 
    | use_statement    { $$ = new mm_statement(use_statement,$1); } 
    | set_statement    { $$ = new mm_statement(set_statement,$1); } 
    | let_statement    { $$ = new mm_statement(let_statement,$1); } 
    | get_statement    { $$ = new mm_statement(get_statement,$1); } 
    | ret_statement    { $$ = new mm_statement(ret_statement,$1); } 
    | put_statement    { $$ = new mm_statement(put_statement,$1); } 
    | if_statement    { $$ = new mm_statement(if_statement,$1); } 
    | loop_statement    { $$ = new mm_statement(loop_statement,$1); } 
    | save_statement    { $$ = new mm_statement(save_statement,$1); } 
    ; 

//--------------------------- 
// The Main Loop 
//--------------------------- 

statement_list 
    : statement    { $$ = new mm_statement_list(); $$->addStatement(*$1); } 
    | statement_list statement  { $1->addStatement(*$2); $$ = $1; } 
    ; 

block 
    : '{' statement_list '}'   { $$ = $2; } 
    ; 

main 
    : statement_list    { Base->Statements = $1; } 
    ; 

%% 

Sidenote: दुर्भाग्य से, मैं रूबी-विशिष्ट के साथ आपकी मदद नहीं कर सकता (क्योंकि मैं केवल एक पूर्ण नौसिखिया नहीं हूं, बल्कि वास्तव में - किसी अज्ञात कारण के लिए - मुझे इससे नफरत है); लेकिन, सी में भी, मुझे उम्मीद है कि यह आपको एक अजीब विचार देगा ... :-)

+0

मुझे लगता है आप पूरी तरह से यहां बिंदु याद करते हैं। प्रश्न के अंत में ओपी ने यही कहा: 'कृपया ध्यान दें, मैं अपने लिए काम करने के लिए yacc या antlr जैसे पार्सर जेनरेटर का उपयोग नहीं करना चाहता, मैं स्क्रैच से सबकुछ करना चाहता हूं।' –

संबंधित मुद्दे