WhaleSpunk

Untitled

Mar 30th, 2017
67
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 18.87 KB | None | 0 0
  1. %{
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <stdlib.h>
  5.  
  6. #define DEBUG 0
  7.  
  8. typedef struct AST_Tree{
  9. char type[60];
  10. struct AST_Tree* child;
  11. struct AST_Tree* brother;
  12. char *value;
  13. }AST_Tree_node;
  14.  
  15.  
  16. AST_Tree_node *head = NULL;
  17.  
  18. AST_Tree_node* insertNode(char* node);
  19. AST_Tree_node* insert_value_node(char* node, char* value);
  20. void insert_child(AST_Tree_node* father, AST_Tree_node* child);
  21. void insert_brother(AST_Tree_node* brother, AST_Tree_node* node);
  22. void printTree(AST_Tree_node* aux, int level);
  23. void printTerminal(AST_Tree_node *node);
  24. void printPoints(int n);
  25. int flag=0;
  26. int aux2 = 0;
  27. int imprimir=0;
  28. int parse=0;
  29. int yylex(void);
  30. void yyerror(const char *s);
  31.  
  32. extern int n_linha;
  33. extern int n_coluna;
  34. extern char * yytext;
  35. extern int yyleng;
  36. AST_Tree_node *nodeAux;
  37. AST_Tree_node *nodeAux2;
  38. AST_Tree_node *nodeAux3;
  39.  
  40.  
  41.  
  42. %}
  43.  
  44. %union{
  45. char *str;
  46. struct AST_Tree *node;
  47. }
  48.  
  49. %token <str> ID STRLIT BOOLLIT DECLIT REALLIT
  50.  
  51. %token PUBLIC BOOL INT DOUBLE SEMI COMMA PARSEINT OCURV OSQUARE CSQUARE CCURV ASSIGN AND OR EQ GEQ GT LEQ LT NEQ PLUS MINUS STAR DIV MOD NOT DOTLENGTH STATIC STRING VOID CLASS OBRACE CBRACE IF ELSE WHILE DO PRINT RETURN RESERVED
  52.  
  53. %type <node> Program FieldDecl_MethodDecl_SEMI FieldDecl CommaID MethodDecl MethodHeader FormalParams CommaTypeID MethodBody VarDecl_Statement VarDecl Type Statement Statement_aux Expr ExprNew Assignment MethodInvocation CommaExpr ParseArgs
  54.  
  55.  
  56.  
  57. %left COMMA
  58. %right ASSIGN
  59. %left OR
  60. %left AND
  61. %left EQ NEQ
  62. %left LT GT LEQ GEQ
  63. %left MINUS PLUS
  64. %left STAR DIV MOD
  65. %right NOT
  66. %left OCURV OBRACE OSQUARE CCURV CBRACE CSQUARE
  67.  
  68. %nonassoc ELSE
  69.  
  70. %%
  71.  
  72.  
  73.  
  74. Program: CLASS ID OBRACE FieldDecl_MethodDecl_SEMI CBRACE {if(flag == 0){$$ = head = insertNode("Program");nodeAux = insert_value_node("Id", $2); insert_child($$, nodeAux); insert_child($$, $4);}}
  75. | CLASS ID OBRACE CBRACE {if(flag==0){$$ = head = insertNode("Program");nodeAux = insert_value_node("Id", $2);insert_child($$, nodeAux);}}
  76. ;
  77.  
  78. FieldDecl_MethodDecl_SEMI: FieldDecl_MethodDecl_SEMI FieldDecl {if(flag ==0){insert_child(head, $2);}}
  79. | FieldDecl_MethodDecl_SEMI MethodDecl {if(flag == 0){insert_child(head, $2);}}
  80. | FieldDecl_MethodDecl_SEMI SEMI {;}
  81. | FieldDecl {if(flag ==0 ){$$ = $1;}}
  82. | MethodDecl {if(flag == 0){$$ = $1 ;}}
  83. | SEMI {;}
  84. ;
  85.  
  86.  
  87. FieldDecl: PUBLIC STATIC Type ID CommaID SEMI {if(flag ==0){$$ = insertNode("FieldDecl"); insert_child($$, $3); nodeAux = insert_value_node("Id", $4);insert_child($$, nodeAux); insert_child($$, $5); }}
  88. | PUBLIC STATIC Type ID SEMI {if(flag == 0){$$ = insertNode("FieldDecl"); insert_child($$, $3); nodeAux = insert_value_node("Id", $4); insert_child($$, nodeAux);}}
  89. | error SEMI {if(flag == 0){$$ = NULL;}}
  90. ;
  91.  
  92. CommaID: CommaID COMMA ID {if(flag == 0){$$ = $1 ; nodeAux = insert_value_node("Id", $3); insert_brother($$, nodeAux);}}
  93. | COMMA ID {if(flag == 0){$$ = insert_value_node("Id", $2);}}
  94. ;
  95.  
  96. MethodDecl: PUBLIC STATIC MethodHeader MethodBody {if(flag ==0 ){$$ = insertNode("MethodDecl"); insert_child($$, $3); insert_child($$, $4);}}
  97. ;
  98.  
  99. MethodHeader: Type ID OCURV FormalParams CCURV {if(flag == 0){$$ = insertNode("MethodHeader"); insert_child($$, $1); nodeAux = insert_value_node("Id", $2); insert_child($$, nodeAux); insert_child($$, $4);}}
  100. | Type ID OCURV CCURV {if(flag == 0){$$ = insertNode("MethodHeader"); insert_child($$, $1); nodeAux = insert_value_node("Id", $2); insert_child($$, nodeAux);}}
  101. | VOID ID OCURV FormalParams CCURV {if(flag == 0){$$ = insertNode("MethodHeader"); nodeAux = insertNode("Void"); insert_child($$, nodeAux); nodeAux2 = insert_value_node("Id", $2); insert_child($$, nodeAux2); insert_child($$, $4);}}
  102. | VOID ID OCURV CCURV {if(flag == 0){$$ = insertNode("MethodHeader"); nodeAux = insertNode("Void"); insert_child($$, nodeAux); nodeAux2 = insert_value_node("Id", $2); insert_child($$, nodeAux2);}}
  103. ;
  104.  
  105. FormalParams: Type ID CommaTypeID {if(flag == 0){$$ = insertNode("MethodParams"); nodeAux2 = insertNode("ParamDecl"); insert_child($$, nodeAux2); insert_child(nodeAux2, $1); nodeAux = insert_value_node("Id", $2); insert_child(nodeAux2, nodeAux); insert_child(nodeAux2, $3);}}
  106. | Type ID {if(flag == 0){$$ = insertNode("MethodParams"); nodeAux2 = insertNode("ParamDecl"); insert_child($$, nodeAux2); insert_child(nodeAux2, $1); nodeAux = insert_value_node("Id", $2); insert_child(nodeAux2, nodeAux);}}
  107. | STRING OSQUARE CSQUARE ID {if(flag == 0){$$ = insertNode("MethodParams"); nodeAux2 = insertNode("ParamDecl"); insert_child($$, nodeAux2); nodeAux = insertNode("StringArray"); insert_child(nodeAux2, nodeAux); nodeAux3 = insert_value_node("Id", $4); insert_child(nodeAux2, nodeAux3);}}
  108. ;
  109.  
  110.  
  111. CommaTypeID: COMMA Type ID {if(flag == 0){$$ = $2; nodeAux = insert_value_node("Id", $3); insert_brother($$, nodeAux);}}
  112. | CommaTypeID COMMA Type ID {if(flag ==0){$$ = $1; insert_brother($$, $3); nodeAux2 = insert_value_node("Id", $4); insert_brother($$, nodeAux2);}}
  113. ;
  114.  
  115.  
  116. MethodBody: OBRACE VarDecl_Statement CBRACE {if(flag == 0){$$ = insertNode("MethodBody"); insert_child($$, $2);}}
  117. | OBRACE CBRACE {if(flag == 0){$$ = insertNode("MethodBody");}}
  118. ;
  119.  
  120.  
  121. VarDecl_Statement: VarDecl {if(flag == 0){$$ = $1;}}
  122. | Statement {if(flag == 0){$$ = $1;}}
  123. | VarDecl_Statement VarDecl {if(flag == 0){$$ = $1; insert_brother($$, $2);}}
  124. | VarDecl_Statement Statement {if(flag == 0){$$ = $1; insert_brother($$, $2);}}
  125. ;
  126.  
  127.  
  128.  
  129. VarDecl: Type ID CommaID SEMI {if(flag == 0){
  130. $$ = insertNode("VarDecl");
  131. insert_child($$,$1);
  132. nodeAux = insert_value_node("Id", $2);
  133. insert_child($$, nodeAux);
  134. nodeAux2 = $3;
  135. AST_Tree_node *node = nodeAux2;
  136. AST_Tree_node *copy;
  137. while(node != NULL){
  138. copy = insertNode($1->type);
  139. node->brother = NULL;
  140. nodeAux3 = insertNode("VarDecl");
  141. insert_brother($$, nodeAux3);
  142. insert_child(nodeAux3, copy);
  143. insert_child(nodeAux3, node);
  144. node = nodeAux2->brother;
  145. nodeAux2 = nodeAux2->brother;
  146. }}}
  147.  
  148. | Type ID SEMI {if(flag == 0){$$ = insertNode("VarDecl"); insert_child($$, $1); nodeAux2 = insert_value_node("Id", $2);insert_child($$, nodeAux2);}}
  149. ;
  150.  
  151. Type: BOOL {if(flag == 0){$$ = insertNode("Bool");}}
  152. | INT {if(flag == 0){$$ = insertNode("Int");}}
  153. | DOUBLE {if(flag == 0){$$ =insertNode("Double");}}
  154. ;
  155.  
  156. Statement: OBRACE Statement_aux CBRACE {if(flag ==0){$$= $2;}}
  157. | OBRACE CBRACE {if(flag==0){$$ = NULL;}}
  158. | IF OCURV Expr CCURV Statement {
  159. if(flag==0){
  160. $$=insertNode("If");
  161. insert_child($$,$3);
  162. nodeAux = $5;
  163. int num=0;
  164. if(nodeAux != NULL){
  165. AST_Tree_node *node = nodeAux;
  166. while(node!= NULL){
  167. node = node->brother;
  168. num++;
  169. }
  170. if(num==1){
  171. insert_child($$,nodeAux);
  172. nodeAux2 = insertNode("Block");
  173. insert_child($$, nodeAux2);
  174. }
  175. if(num>1){
  176. nodeAux2 = insertNode("Block");
  177. nodeAux3 = insertNode("Block");
  178. insert_child(nodeAux2, nodeAux);
  179. insert_child($$,nodeAux2);
  180. insert_brother(nodeAux2,nodeAux3);
  181. }
  182. }
  183. if(nodeAux==NULL)
  184. {
  185. nodeAux2 = insertNode("Block");
  186. nodeAux3 = insertNode("Block");
  187. insert_child($$, nodeAux2);
  188. insert_child($$, nodeAux3);
  189. }
  190. }
  191. }
  192.  
  193. | IF OCURV Expr CCURV Statement ELSE Statement {
  194. if(flag==0){
  195. $$=insertNode("If");
  196. insert_child($$,$3);
  197. nodeAux = $5;
  198. int num=0, num2=0;
  199.  
  200. if(nodeAux != NULL){
  201. AST_Tree_node *node = nodeAux;
  202. while(node!= NULL){
  203. node = node->brother;
  204. num++;
  205. }
  206. if(num==1){
  207. insert_child($$,nodeAux);
  208.  
  209. }
  210. if(num>1){
  211. nodeAux2 = insertNode("Block");
  212. insert_child(nodeAux2, nodeAux);
  213. insert_child($$,nodeAux2);
  214.  
  215. }
  216. }
  217. else if(nodeAux==NULL)
  218. {
  219. nodeAux2 = insertNode("Block");
  220. insert_child($$, nodeAux2);
  221.  
  222. }
  223.  
  224. if($7!=NULL)
  225. {
  226. AST_Tree_node *node2 = $7;
  227. while(node2!= NULL){
  228. node2 = node2->brother;
  229. num2++;
  230. }
  231. if(num2==1){
  232. insert_child($$,$7);
  233.  
  234. }
  235. if(num2>1){
  236. nodeAux2 = insertNode("Block");
  237. insert_child(nodeAux2, $7);
  238. insert_child($$,nodeAux2);
  239.  
  240. }
  241. }
  242.  
  243. else if($7==NULL)
  244. {
  245. nodeAux2 = insertNode("Block");
  246. insert_child($$, nodeAux2);
  247. }
  248. }
  249.  
  250.  
  251. }
  252. | WHILE OCURV Expr CCURV Statement {
  253. if(flag==0){
  254. $$=insertNode("While");
  255. insert_child($$,$3);
  256. nodeAux = $5;
  257. int num=0;
  258. if(nodeAux != NULL){
  259. AST_Tree_node *node = nodeAux;
  260. while(node!= NULL){
  261. node = node->brother;
  262. num++;
  263. }
  264. if(num==1){
  265. insert_child($$,nodeAux);
  266.  
  267. }
  268. if(num>1){
  269. nodeAux2 = insertNode("Block");
  270. insert_child(nodeAux2, nodeAux);
  271. insert_child($$,nodeAux2);
  272.  
  273. }
  274. }
  275. if(nodeAux==NULL)
  276. {
  277. nodeAux2 = insertNode("Block");
  278. insert_child($$, nodeAux2);
  279.  
  280. }
  281. }
  282. }
  283. | DO Statement WHILE OCURV Expr CCURV SEMI {
  284. if(flag==0){
  285. $$=insertNode("DoWhile");
  286. insert_child($$,$5);
  287. nodeAux = $2;
  288. int num=0;
  289. if(nodeAux != NULL){
  290. AST_Tree_node *node = nodeAux;
  291. while(node!= NULL){
  292. node = node->brother;
  293. num++;
  294. }
  295. if(num==1){
  296. insert_child($$,nodeAux);
  297.  
  298. }
  299. if(num>1){
  300. nodeAux2 = insertNode("Block");
  301. insert_child(nodeAux2, nodeAux);
  302. insert_child($$,nodeAux2);
  303.  
  304. }
  305. }
  306. if(nodeAux==NULL)
  307. {
  308. nodeAux2 = insertNode("Block");
  309. insert_child($$, nodeAux2);
  310.  
  311. }
  312. }
  313. }
  314. | PRINT OCURV Expr CCURV SEMI {
  315. if(flag==0){
  316.  
  317. $$=insertNode("Print");
  318. insert_child($$,$3);
  319. }
  320. }
  321. | PRINT OCURV STRLIT CCURV SEMI {
  322. if(flag==0){
  323.  
  324.  
  325. $$=insertNode("Print");
  326. nodeAux = insert_value_node("StrLit", $3);
  327. insert_child($$, nodeAux);
  328. }
  329. }
  330. | SEMI {
  331. if(flag==0){
  332. $$=NULL;
  333. }
  334. }
  335. | Assignment SEMI {
  336. if(flag==0){
  337. $$=$1;
  338. }
  339. }
  340. | MethodInvocation SEMI {
  341. if(flag==0){
  342. $$=$1;
  343. }
  344. }
  345. | ParseArgs SEMI {
  346. if(flag==0){
  347. $$=$1;
  348. }
  349. }
  350. | RETURN SEMI {
  351. if(flag==0){
  352. $$ = insertNode("Return");
  353. }
  354. }
  355. | RETURN Expr SEMI {
  356. if(flag==0){
  357. $$ = insertNode("Return");
  358. insert_child($$,$2);
  359.  
  360. }
  361. }
  362. | error SEMI {if(flag==0){$$=NULL;}}
  363. ;
  364.  
  365.  
  366.  
  367. Statement_aux: Statement {if(flag==0){$$=$1;}}
  368. | Statement_aux Statement {if(flag==0){$$ = $1 ; insert_brother($$,$2);}}
  369. ;
  370.  
  371.  
  372.  
  373. Expr: ExprNew {if(flag == 0){$$ = $1;}}
  374. | Assignment {if(flag == 0){$$ = $1;}}
  375. ;
  376.  
  377. ExprNew: MethodInvocation {if(flag == 0){$$ = $1;}}
  378. | ParseArgs {if(flag == 0){$$ = $1;}}
  379. | ExprNew AND ExprNew {if(flag == 0){$$ = insertNode("And"); insert_child($$, $1); insert_child($$, $3);}}
  380. | ExprNew OR ExprNew {if(flag == 0){$$ = insertNode("Or"); insert_child($$, $1); insert_child($$, $3);}}
  381. | ExprNew EQ ExprNew {if(flag == 0){$$ = insertNode("Eq"); insert_child($$, $1); insert_child($$, $3);}}
  382. | ExprNew GEQ ExprNew {if(flag == 0){$$ = insertNode("Geq"); insert_child($$, $1); insert_child($$, $3);}}
  383. | ExprNew GT ExprNew {if(flag == 0){$$ = insertNode("Gt"); insert_child($$, $1); insert_child($$, $3);}}
  384. | ExprNew LEQ ExprNew {if(flag == 0){$$ = insertNode("Leq"); insert_child($$, $1); insert_child($$, $3);}}
  385. | ExprNew LT ExprNew {if(flag == 0){$$ = insertNode("Lt"); insert_child($$, $1); insert_child($$, $3);}}
  386. | ExprNew NEQ ExprNew {if(flag == 0){$$ = insertNode("Neq"); insert_child($$, $1); insert_child($$, $3);}}
  387. | ExprNew PLUS ExprNew {if(flag == 0){$$ = insertNode("Add"); insert_child($$, $1); insert_child($$, $3);}}
  388. | ExprNew MINUS ExprNew {if(flag == 0){$$ = insertNode("Sub"); insert_child($$, $1); insert_child($$, $3);}}
  389. | ExprNew STAR ExprNew {if(flag == 0){$$ = insertNode("Mul"); insert_child($$, $1); insert_child($$, $3);}}
  390. | ExprNew DIV ExprNew {if(flag == 0){$$ = insertNode("Div"); insert_child($$, $1); insert_child($$, $3);}}
  391. | ExprNew MOD ExprNew {if(flag == 0){$$ = insertNode("Mod"); insert_child($$, $1); insert_child($$, $3);}}
  392. | PLUS ExprNew %prec NOT {if(flag == 0){$$ = insertNode("Plus"); insert_child($$, $2);}}
  393. | MINUS ExprNew %prec NOT {if(flag == 0){$$ = insertNode("Minus"); insert_child($$, $2);}}
  394. | NOT ExprNew {if(flag == 0){$$ = insertNode("Not"); insert_child($$, $2);}}
  395. | ID DOTLENGTH {if(flag == 0){$$ = insertNode("Length"); nodeAux = insert_value_node("Id", $1); insert_child($$, nodeAux);}}
  396. | ID {if(flag == 0){$$ = insert_value_node("Id", $1);}}
  397. | OCURV Expr CCURV {if(flag == 0){$$ = $2;}}
  398. | BOOLLIT {if(flag == 0){$$ = insert_value_node("BoolLit", $1);}}
  399. | DECLIT {if(flag == 0){$$ = insert_value_node("DecLit", $1);}}
  400. | REALLIT {if(flag == 0){$$ = insert_value_node("RealLit", $1);}}
  401. | OCURV error CCURV {if(flag == 0){$$ = NULL;}}
  402. ;
  403.  
  404. Assignment: ID ASSIGN Expr {if(flag == 0){$$ = insertNode("Assign"); nodeAux = insert_value_node("Id", $1); insert_child($$, nodeAux); insert_child($$, $3);}}
  405. ;
  406.  
  407.  
  408. MethodInvocation: ID OCURV CCURV {if(flag == 0){$$ = insertNode("Call"); nodeAux = insert_value_node("Id", $1); insert_child($$, nodeAux);}}
  409. | ID OCURV Expr CCURV {if(flag == 0){$$ = insertNode("Call"); nodeAux = insert_value_node("Id", $1); insert_child($$, nodeAux); insert_child($$, $3);}}
  410. | ID OCURV Expr CommaExpr CCURV {if(flag == 0){$$ = insertNode("Call"); nodeAux = insert_value_node("Id", $1); insert_child($$, nodeAux);insert_child($$, $3); insert_child($$, $4);}}
  411. | ID OCURV error CCURV {if(flag == 0){$$ = NULL;}}
  412. ;
  413.  
  414. CommaExpr: COMMA Expr {if(flag == 0){$$ = $2;}}
  415. | CommaExpr COMMA Expr {if(flag == 0){$$ = $1; insert_brother($$, $3);}}
  416. ;
  417.  
  418. ParseArgs: PARSEINT OCURV ID OSQUARE Expr CSQUARE CCURV {if(flag == 0){$$ = insertNode("ParseArgs"); nodeAux = insert_value_node("Id", $3); insert_child($$, nodeAux); insert_child($$, $5);}}
  419. | PARSEINT OCURV error CCURV {if(flag ==0){$$ = NULL;}}
  420. ;
  421.  
  422.  
  423.  
  424. %%
  425.  
  426. void yyerror(const char* s){
  427. if(flag ==0){
  428. flag = 1;
  429. }
  430. printf("Line %d, col %d: %s: %s\n",n_linha,(int)(n_coluna - strlen(yytext)),s,yytext);
  431. }
  432.  
  433.  
  434.  
  435. void insert_brother(AST_Tree_node* brother, AST_Tree_node* node)
  436. {
  437.  
  438.  
  439. AST_Tree_node* aux = brother;
  440.  
  441. if(aux!=NULL && node!=NULL)
  442. {
  443. while(aux->brother != NULL)
  444. {
  445. aux = aux->brother;
  446. }
  447. aux->brother = node;
  448. }
  449. }
  450.  
  451.  
  452. void insert_child(AST_Tree_node* father, AST_Tree_node* child)
  453. {
  454.  
  455. AST_Tree_node* temp = father->child;
  456.  
  457. if(temp==NULL)
  458. {
  459. father->child = child;
  460. }
  461. else
  462. {
  463.  
  464. insert_brother(father->child, child);
  465. }
  466.  
  467. }
  468.  
  469.  
  470. AST_Tree_node* insert_value_node(char* node, char* value){
  471.  
  472. AST_Tree_node* new_node = insertNode(node);
  473. new_node->value = value;
  474. return new_node;
  475.  
  476. }
  477.  
  478. AST_Tree_node* insertNode(char* node){
  479. AST_Tree_node* new_node;
  480. new_node = (AST_Tree_node *)(malloc(sizeof(AST_Tree_node)));
  481. if(new_node != NULL){
  482. new_node->child = NULL;
  483. new_node->brother = NULL;
  484. new_node->value = NULL;
  485. strcpy(new_node->type, node);
  486. }
  487. return new_node;
  488. }
  489.  
  490.  
  491. void printTree(AST_Tree_node *node, int level){
  492. printPoints(level);
  493.  
  494. if(strcmp(node->type, "Id") == 0 || strcmp(node->type, "BoolLit") == 0 || strcmp(node->type, "DecLit") == 0 || strcmp(node->type, "RealLit") == 0 || strcmp(node->type, "StrLit") == 0 ){
  495. printTerminal(node);
  496. }
  497. else{
  498.  
  499. printf("%s\n", node->type);
  500.  
  501.  
  502. }
  503.  
  504. AST_Tree_node *child = node->child;
  505.  
  506. if(child != NULL){
  507. printTree(child, level+1);
  508.  
  509. while(child->brother != NULL){
  510. child = child->brother;
  511. printTree(child, level+1);
  512. }
  513.  
  514. }
  515.  
  516. }
  517.  
  518.  
  519. void printTerminal(AST_Tree_node *node){
  520.  
  521. printf("%s(%s)\n", node->type, node->value);
  522. }
  523. void printPoints(int n){
  524. while(n > 0){
  525. printf("..");
  526. n--;
  527. }
  528. }
  529.  
  530. void deleteTree(AST_Tree_node *head){
  531.  
  532. AST_Tree_node *child = head->child;
  533. AST_Tree_node *aux;
  534. while(child != NULL){
  535. while(child->brother !=NULL){
  536. if(child->child != NULL){
  537. child = child->child;
  538. }
  539. aux = child->brother;
  540. free(child);
  541. child = aux;
  542. }
  543.  
  544. }
  545.  
  546.  
  547. }
  548. int main(int argc, char *argv[])
  549. {
  550. if(argc>1){
  551. if(strcmp(argv[1],"-l")==0){
  552. imprimir=1;
  553. yylex();
  554. }
  555. else if(strcmp(argv[1],"-1")==0){
  556. yylex();
  557. }
  558. }
  559. else{
  560. parse=1;
  561. yyparse();
  562. }
  563. if(flag == 0){
  564. printTree(head,0);
  565. deleteTree(head);
  566.  
  567. }
  568.  
  569. return 0;
  570. }
Add Comment
Please, Sign In to add comment