/*parser.c - recursive-descent parser for arithmetic expressions*/ /*There is, in general, one parsing function for each nonterminal symbol in the grammar. Refer to the grammar (or syntax chart) to understand how these routines interact with each other. Some regions of the code are commented very lightly, because their structure and function follow from the grammar. -There are two important and related invariant conditions over all these parsing functions. On entry, the first token in the phrase about to be parsed is in 'token'. On exit, the token immediately following the last token in the expression just parsed is in 'token' (except in the case of parse_program, where, if all is well, there is nothing following the '.'). To maintain these invariants, it is necessary to keep track of tokens that delimit expressions and to eat them where appropriate. -In other words, each parsing function assumes that the input stream of tokens has been prepared for it, and it in turn prepares the input stream for whatever parsing function may be called next.*/ #include "scanner.h" #include "parser.h" /*WHICH STANDARD HEADER FILES DO YOU NEED TO INCLUDE HERE?*/ /*Build an internal tree node, which contains an operator and points to two subtrees.*/ static Tree *NewTree(op, left, right) Token op; Tree *left, *right; { /*USE MALLOC TO ALLOCATE A NEW TREE NODE, AND FILL ITS FIELDS WITH THE GIVEN OPERATOR AND SUBTREE POINTERS. (USE THE MACROS DEFINED IN parser.h.)*/ } /*Build a new leaf, which contains an operator of NumberToken, and a number.*/ static Tree *NewLeaf(value) int value; { /*USE MALLOC TO ALLOCATE A NEW TREE NODE, GIVE IT AN OPERATOR OF NumberToken, AND FILL ITS VALUE FIELD WITH THE GIVEN VALUE. (USE THE MACROS DEFINED IN parser.h.)*/ } static Tree *parse_leaf() { Tree *ptr; ptr = NewLeaf(number); next_token(); return(ptr); } static Tree *parse_arith_expr(); /*forward declaration*/ static Tree *parse_factor() { /*WE'LL GIVE YOU THIS ONE :-)*/ Tree *factor; if(token == LeftParenToken) { next_token(); /*eat the '('*/ factor = parse_arith_expr(); if(token != RightParenToken) { printf("expected \")\" in factor, found "); print_token(token); printf("\n"); exit(1); } next_token(); /*eat the ')'*/ return(factor); } else if(token == NumberToken) return(parse_leaf()); else { printf("unexpected factor "); print_token(token); printf("\n"); exit(1); } } static Tree *parse_term() { Tree *left_tree, *tree; left_tree = parse_factor(); /*invariant: 'left_tree' points to the root of a parse tree that represents all the tokens in the expression that are to the left of 'token'.*/ /*bound: the number of expressions that remain to be parsed*/ while((token == TimesToken) || (token == DivideToken)) { /*CREATE A NEW TREE NODE CONTAINING THE CURRENT TOKEN AT ITS ROOT, left_tree ON ITS LEFT, AND THE RESULT OF parse_factor() ON ITS RIGHT. MAKE THIS NEW NODE THE NEW VALUE OF left_tree.*/ } return(left_tree); } static Tree *parse_arith_expr() { /*THIS IS JUST LIKE parse_term() ABOVE, EXCEPT WITH TERMS INSTEAD OF FACTORS, AND PlusToken AND MinusToken INSTEAD OF TimesToken AND DivideToken. IN FACT, WITH A LITTLE WORK YOU CAN WRITE A SINGLE, PARAMETRISED FUNCTION THAT DOES THE WORK OF parse_term() AND parse_arith_expr(). THE PROTOTYPE FOR THAT FUNCTION WOULD BE SOMETHING LIKE THIS: static Tree *parse_expressions(Tree *(*parsing_fn)(), Token op0, Token op1); (FOR NOW, WRITE IT THE MORE OBVIOUS WAY. YOU CAN COME BACK AND DO IT THE COOL WAY AFTER WE TALK ABOUT POINTERS TO FUNCTIONS.)*/ } /*This is the top-level parsing function.*/ Tree *parse_expr() { Tree *expr; init_scanner(); next_token(); expr = parse_arith_expr(); if(token != PeriodToken) { printf("unexpected token at end of input: "); print_token(token); printf("\n"); exit(1); } return(expr); } /*print an indented subtree - used by print_tree() below*/ static void print_subtree(t, indent) Tree *t; int indent; { register int spaces; /*invariant: all nodes to the left of 't' have been printed*/ while(TreeOper(t) != NumberToken) { for(spaces = 0; spaces != indent; spaces++) putchar(' '); print_token(TreeOper(t)); putchar('\n'); print_subtree(TreeLeft(t), 2+indent); indent += 2; t = TreeRight(t); } for(spaces = 0; spaces != indent; spaces++) putchar(' '); printf("%d\n", TreeValue(t)); } /*print the contents of a tree - useful for debugging*/ void print_tree(t) Tree *t; { print_subtree(t, 0); } /*ONCE YOU'RE DONE WITH THIS PROGRAM, GET parser.h AND testparse.c, AND COMPILE THEM AND LINK THEM ALL TOGETHER WITH YOUR SCANNER LIKE THIS: cc -g -o testparse testparse.c scanner.c parser.c */