/********************************************************************** Java parser and pretty-printer. Uses a recursive-descent parser that accepts a superset of the language. At the moment it's an unreasonably large superset: basically any string of tokens with balanced curvy braces is accepted inside a class definition. You have to fix that by fleshing out the routines that parse a compound_stmt. You also have to - find all declared identifiers, so you can print them red - indent things appropriately - fix inter-token spaces **********************************************************************/ #include #include #include "reader.h" #include "scanner.h" #include "html.h" #include "parser.h" static token_t tok; static location_t loc; static int indent_level = 0; #define INDENT_WIDTH 4 /* forward declarations: */ static void parse_compilation_unit(); static void parse_package_opt(); static void parse_imports(); static void parse_type_definitions(); static void parse_modifiers(); static void parse_brackets_opt(); static void parse_qualified_name(); static void parse_qualified_name_tail(); static void parse_qualified_name_list(); static void parse_more_qualified_names(); static void parse_class_definition(); static void parse_extends_clause(); static void parse_implements_clause(); static void parse_throws_clause(); static void parse_fields(); static void parse_field(); static void parse_field_tail(); static void parse_qn_field_tail(); static void parse_type_field_tail(); static void parse_name_field_tail(); static void parse_routine_tail(); static void parse_routine_body_opt(); static void parse_params_opt(); static void parse_more_params(); static void parse_param(); static void parse_var_decs(); static void parse_var_decs_tail(); static void parse_eq_initializer_opt(); static void parse_more_var_decs(); static void parse_initializer(); static void parse_initializer_list_opt(); static void parse_more_initializers(); static void parse_type(); static void parse_compound_stmt(); static void parse_balanced_stuff(); /* temporary */ static void parse_stuff();; /* temporary */ static void parse_expr_list(); static void parse_more_exprs(); static void parse_expr_list_opt(); static void parse_expr_opt(); static void parse_expr(); static void parse_atom(); static int at_bol = 1; /* We're about to start a new line of output. */ static int comment_nl = 0; /* We've just printed a newline because of a comment. Don't print *another* newline if the parser asks for one. */ /******** Print a newline. Remember we did so, so put_token can indent appropriately. Skip newline if we've already done it in the process of printing a comment. ********/ static void newline() { if (comment_nl) { comment_nl = 0; } else { putchar('\n'); at_bol = 1; } } /******** Print current token. Precede with appropriate space. ********/ static void put_token() { if (at_bol) { int i = (indent_level) * INDENT_WIDTH; while (i--) { putchar(' '); } } else { if (tok.tc == T_NEW_COMMENT || tok.tc == T_OLD_COMMENT) { int i = INDENT_WIDTH; while (i--) { putchar(' '); } } else { putchar(' '); /* YOU NEED TO FIX THIS; insert space between tokens only when appropriate */ } } print_token(&tok); at_bol = 0; comment_nl = 0; /* no longer care if comment printed a newline */ } /******** Get next token from the scanner. Filter out white space, comments, and newlines. Print comments appropriately: - If comment is preceded by a newline in input, precede it with newline in output. If an old-style comment is followed by a newline, add a newline to the output. These are the ONLY places where the formatter pays attention to white space in the input. - if the comment is new style (// to eoln), follow with newline (scanner does not include newline in comment). - with both of the previous rules, if the parser also decides to generate a newline (e.g. for a semicolon), combine them so we don't generate an *extra* one. ********/ static void get_token() { token_class prev_class = T_SPACE; do { scan(&loc, &tok); if (tok.tc == T_NL_SPACE && prev_class == T_OLD_COMMENT) { /* comment was followed by a newline */ newline(); comment_nl = 1; /* remember this newline */ } if (tok.tc == T_OLD_COMMENT || tok.tc == T_NEW_COMMENT) { if (prev_class == T_NL_SPACE) { /* comment was preceded by a newline */ newline(); comment_nl = 1; /* remember this newline */ } put_token(); /* print comment itself */ if (tok.tc == T_NEW_COMMENT) { /* newline is not part of comment */ comment_nl = 0; /* we really do want this one */ newline(); comment_nl = 1; /* remember this newline */ } } prev_class = tok.tc; } while (tok.tc == T_SPACE || tok.tc == T_NL_SPACE || tok.tc == T_OLD_COMMENT || tok.tc == T_NEW_COMMENT); } /******** A parse error has occurred. Print error message and halt. ********/ static void parse_error() { fprintf(stderr, "Syntax error"); fprintf(stderr, " at line %d, column %d\n", tok.location.line->line_num, tok.location.column); exit(1); } /******** A token of class tc is expected from the scanner. Verify and print. ********/ static void match(token_class tc) { if (tc == T_ID_DEC && tok.tc == T_IDENTIFIER) { tok.tc = T_ID_DEC; } if (tc != tok.tc) { parse_error(); } put_token(); get_token(); } /******** Scan source, identify structure, and print appropriately. ********/ void parse() { set_to_beginning(&loc); get_token(); parse_compilation_unit(); } static void parse_compilation_unit() { parse_package_opt(); parse_imports(); parse_type_definitions(); match(T_EOF); } static void parse_package_opt() { switch (tok.tc) { case T_PACKAGE: match(T_PACKAGE); parse_qualified_name(); match(T_SEMIC); newline(); break; default: break; /* just return */ } } static void parse_imports() { switch (tok.tc) { case T_IMPORT: match(T_IMPORT); parse_expr(); match(T_SEMIC); newline(); parse_imports(); break; default: break; /* just return */ } } static void parse_type_definitions() { switch (tok.tc) { case T_SEMIC: match(T_SEMIC); newline(); parse_type_definitions(); break; case T_MODIFIER: case T_CLASSWORD: case T_SYNCHRONIZED: parse_modifiers(); parse_class_definition(); parse_type_definitions(); break; default: break; /* just return */ } } static void parse_modifiers() { switch (tok.tc) { case T_MODIFIER: match(T_MODIFIER); parse_modifiers(); break; case T_SYNCHRONIZED: match(T_SYNCHRONIZED); parse_modifiers(); break; default: break; /* just return */ } } static void parse_brackets_opt() { switch (tok.tc) { case T_LBRAC: match(T_LBRAC); match(T_RBRAC); parse_brackets_opt(); break; default: break; /* just return */ } } static void parse_qualified_name() { match(T_IDENTIFIER); parse_qualified_name_tail(); } static void parse_qualified_name_tail() { switch (tok.tc) { case T_DOT: match(T_DOT); match(T_IDENTIFIER); parse_qualified_name_tail(); break; default: break; /* just return */ } } static void parse_qualified_name_list() { parse_qualified_name(); parse_more_qualified_names(); } static void parse_more_qualified_names() { switch (tok.tc) { case T_COMMA: match(T_COMMA); parse_qualified_name_list(); break; default: break; /* just return */ } } static void parse_class_definition() { match(T_CLASSWORD); match(T_ID_DEC); /* NOTE THIS!! Passing T_ID_DEC instead of T_IDENTIFIER */ parse_extends_clause(); parse_implements_clause(); match(T_LBRACE); newline(); parse_fields(); match(T_RBRACE); newline(); } static void parse_extends_clause() { switch (tok.tc) { case T_EXTENDS: match(T_EXTENDS); parse_qualified_name_list(); break; default: break; /* just return */ } } static void parse_implements_clause() { switch (tok.tc) { case T_IMPLEMENTS: match(T_IMPLEMENTS); parse_qualified_name_list(); break; default: break; /* just return */ } } static void parse_throws_clause() { switch (tok.tc) { case T_THROWS: match(T_THROWS); parse_qualified_name_list(); break; default: break; /* just return */ } } static void parse_fields() { switch (tok.tc) { case T_IDENTIFIER: case T_LBRACE: case T_SEMIC: case T_MODIFIER: case T_TYPE: case T_SYNCHRONIZED: parse_field(); parse_fields(); break; default: break; /* just return */ } } static void parse_field() { switch (tok.tc) { case T_IDENTIFIER: case T_LBRACE: case T_MODIFIER: case T_TYPE: case T_SYNCHRONIZED: parse_modifiers(); parse_field_tail(); break; case T_SEMIC: match(T_SEMIC); newline(); break; default: parse_error(); break; } } static void parse_field_tail() { switch (tok.tc) { case T_IDENTIFIER: parse_qualified_name(); /* PROBLEM!: this name may be either a type name or an identifier declaration, and we won't know until we see what comes next. How do we format it? */ parse_qn_field_tail(); break; case T_LBRACE: parse_compound_stmt(); /* static initializer */ break; case T_TYPE: match(T_TYPE); parse_type_field_tail(); break; default: parse_error(); break; } } static void parse_qn_field_tail() { switch (tok.tc) { case T_IDENTIFIER: case T_LBRAC: parse_type_field_tail(); break; case T_LPAREN: parse_routine_tail(); /* constructor */ break; default: parse_error(); break; } } static void parse_type_field_tail() { parse_brackets_opt(); match(T_IDENTIFIER); parse_name_field_tail(); } static void parse_name_field_tail() { switch (tok.tc) { case T_LBRAC: case T_EQUALS: case T_SEMIC: case T_COMMA: parse_var_decs_tail(); match(T_SEMIC); newline(); break; case T_LPAREN: parse_routine_tail(); /* non-constructor method */ break; default: parse_error(); break; } } static void parse_routine_tail() { match(T_LPAREN); parse_params_opt(); match(T_RPAREN); parse_brackets_opt(); parse_throws_clause(); parse_routine_body_opt(); } static void parse_routine_body_opt() { switch (tok.tc) { case T_SEMIC: match(T_SEMIC); newline(); break; case T_LBRACE: parse_compound_stmt(); break; default: parse_error(); break; } } static void parse_params_opt() { switch (tok.tc) { case T_IDENTIFIER: case T_TYPE: parse_param(); parse_more_params(); break; default: break; /* just return */ } } static void parse_more_params() { switch (tok.tc) { case T_COMMA: match(T_COMMA); parse_param(); parse_more_params(); break; default: break; /* just return */ } } static void parse_param() { parse_type(); parse_brackets_opt(); match(T_IDENTIFIER); parse_brackets_opt(); } static void parse_var_decs() { match(T_IDENTIFIER); parse_var_decs_tail(); } static void parse_var_decs_tail() { parse_brackets_opt(); parse_eq_initializer_opt(); parse_more_var_decs(); } static void parse_eq_initializer_opt() { switch (tok.tc) { case T_EQUALS: match(T_EQUALS); parse_initializer(); break; default: break; /* just return */ } } static void parse_more_var_decs() { switch (tok.tc) { case T_COMMA: match(T_COMMA); parse_var_decs(); break; default: break; /* just return */ } } static void parse_initializer() { switch (tok.tc) { case T_IDENTIFIER: case T_OPERATOR: case T_KWOPERATOR: case T_LBRAC: case T_LPAREN: case T_EQUALS: case T_QMARK: case T_DOT: case T_STAR: case T_LITERAL: case T_TYPE: case T_ATOMWORD: parse_expr(); break; case T_LBRACE: match(T_LBRACE); parse_initializer_list_opt(); match(T_RBRACE); break; default: parse_error(); break; } } static void parse_initializer_list_opt() { switch (tok.tc) { case T_IDENTIFIER: case T_OPERATOR: case T_KWOPERATOR: case T_LBRACE: case T_LBRAC: case T_LPAREN: case T_EQUALS: case T_QMARK: case T_DOT: case T_STAR: case T_LITERAL: case T_TYPE: case T_ATOMWORD: parse_initializer(); parse_more_initializers(); break; default: break; /* just return */ } } static void parse_more_initializers() { switch (tok.tc) { case T_COMMA: match(T_COMMA); parse_initializer(); parse_more_initializers(); break; default: break; /* just return */ } } static void parse_type() { switch (tok.tc) { case T_IDENTIFIER: parse_qualified_name(); break; case T_TYPE: match(T_TYPE); break; default: parse_error(); break; } } /* NB: you will need to modify routine parse_compound_stmt() */ static void parse_compound_stmt() { indent_level++; match(T_LBRACE); newline(); parse_balanced_stuff(); indent_level--; match(T_RBRACE); newline(); } static void parse_expr_list() { parse_expr(); parse_more_exprs(); } static void parse_more_exprs() { switch (tok.tc) { case T_COMMA: match(T_COMMA); parse_expr_list(); break; default: break; /* just return */ } } static void parse_expr_list_opt() { switch (tok.tc) { case T_IDENTIFIER: case T_OPERATOR: case T_KWOPERATOR: case T_LBRAC: case T_LPAREN : case T_EQUALS: case T_QMARK: case T_DOT: case T_STAR: case T_LITERAL: case T_TYPE: case T_ATOMWORD: parse_expr_list(); break; default: break; /* just return */ } } static void parse_expr_opt() { switch (tok.tc) { case T_IDENTIFIER: case T_OPERATOR: case T_KWOPERATOR: case T_LBRAC: case T_LPAREN : case T_EQUALS: case T_QMARK: case T_DOT: case T_STAR: case T_LITERAL: case T_TYPE: case T_ATOMWORD: parse_expr(); break; default: break; /* just return */ } } static void parse_expr() { parse_atom(); parse_expr_opt(); } /* NB: you will need to modify routine parse_atom() */ static void parse_atom() { switch (tok.tc) { case T_IDENTIFIER: case T_DOT: case T_TYPE: case T_LITERAL: case T_OPERATOR: case T_KWOPERATOR: case T_EQUALS: case T_STAR: case T_ATOMWORD: match(tok.tc); break; case T_LBRAC: match(T_LBRAC); parse_expr_opt(); match(T_RBRAC); break; case T_QMARK: match(T_QMARK); parse_expr(); match(T_COLON); break; case T_LPAREN: match(T_LPAREN); parse_expr_list_opt(); match(T_RPAREN); break; default: parse_error(); break; /* just return */ } } /* NB: the following two routines accept random junk inside a class definition. You will need to replace them. */ static void parse_balanced_stuff() { switch (tok.tc) { case T_LBRACE: match(T_LBRACE); newline(); parse_balanced_stuff(); match(T_RBRACE); newline(); parse_balanced_stuff(); break; case T_RBRACE: break; /* just return */ default: parse_stuff(); parse_balanced_stuff(); break; } } static void parse_stuff() { switch (tok.tc) { case T_IDENTIFIER: case T_OPERATOR: case T_KWOPERATOR: case T_LBRAC: case T_RBRAC: case T_LPAREN: case T_RPAREN: case T_EQUALS: case T_COLON: case T_QMARK: case T_COMMA: case T_DOT: case T_STAR: case T_MODIFIER: case T_LITERAL: case T_TYPE: case T_ATOMWORD: case T_CLASSWORD: case T_BREAK: case T_CASE: case T_CATCH: case T_CONTINUE: case T_DEFAULT: case T_DO: case T_ELSE: case T_FINALLY: case T_FOR: case T_GOTO: case T_IF: case T_RETURN: case T_SWITCH: case T_SYNCHRONIZED: case T_THROW: case T_TRY: case T_WHILE: match(tok.tc); break; case T_SEMIC: match(T_SEMIC); newline(); break; default: parse_error(); break; } }