CMSC 435: Assignment 4

Overview

Write a PREDICTIVE (no backtracking), recursive descent parser for the grammar below. EBNF is used, so be careful to note whether a symbol is a metasymbol or not. The six valid metasymbols are the following: {, }, [, ], (, and ). A symbol is NOT a metasymbol if it is in single quotes (e.g., ‘[’ and ‘}’). All the tokens are single characters, so the scanner is trivial. NAME your scanner function getToken (see Required Functions below).

In a comment above EVERY parsing method, show the rewritten GRAMMAR RULE that it parses. Use the STANDARD techniques we studied to remove left recursion and perform left factoring.

M  →   { S } '$'

S  →   I | W | A | P | C | G

I  →   '[' E ? { S } : { S } ']' | '[' E ? { S } ']'

W  →   '{' E ? { S } '}'

A  →   id = E ;

P  →   < E ;

C  →   < ( 'B' | 'T' | 'N' ) ;

G  →   > id ;

E  →   E + T | E - T | T

T  →   T * U | T / U | T % U | U

U  →   F ^ U | F

F  →   '(' E ')' | id | num

id   →   a | b | ... | z
num  →   0 | 1 | ... | 9

Grammar Key

Non-terminal Meaning
M Main Function
S Statement
I If-Then-[Else]
W While
A Assignment
P Print Integer
C Print Character
G Get Integer
E Expression
T Term
U Up-raised Term
F Factor

Terminals B, T, and N, stand for blank, tab, and newline, respectively. An identifier (id) must be a SINGLE LOWERCASE letter. A number (num) must be a SINGLE DIGIT. A valid program must end in $.

Input Specification

Input a string from the user (one character at a time). Use whitespace (space, tab, carriage return, or newline) as a delimiter.

Output Specification

Indicate whether the sentence is valid or not. Detect an error and output an error message indicating ALL of the following:

- function in which the error occurred,
- the line number,
- expected token, and
- current token.

Use the following error format:

Error in "<function>" on line <lineNum>:
  expected '<expectedToken>' but got '<currentToken>'. 

If the string is valid output “Valid!”.

Sample Session 1

[ a ?
    b  = 5 ;
]  $

Valid!

Sample Session 2

> x;
> y;

< (x * y)
    + 3
    % (y ^ 2);

{ x &
    x = x - 1;
}
$
  
Error in "W" on line 8:
  expected '?' but got '&'. 

Required Functions

// Read and return a character token.
// Skip whitespace and keep track of line number. 
char
getToken ();
  
// Check if the current token matches `expectedToken`.
// If so, update the current token.
// If not, call the `error` function. 
// `function` is the function which called `match`.
void
match (std::string_view function, char expectedToken);

// Output an error message using the appropriate format.
// `function` is the function in which the error occurred. 
void
error (std::string_view function, std::string_view expected);

Submission

Submit your single parser file Parser.cc.

Hints

The part of the grammar involving “{ S }” means “zero or more statements”. In handling such a construct, you need to know when to stop calling function S, the one that handles a statement. There are two methods to do this: call S as long as the next token is one that can start a statement. The other (equivalent) method is to keep calling S until the next token is one that may end a sequence of statements.

See the RecursiveDescent directory for examples and some test cases.

Extra Credit

Use the code below to generate tokens.

// Token representation holding the character and its one-based source line
struct Token
{
  char value;
  int line;
};

// Use a C++23 std::generator and coroutine
std::generator<Token>
tokenize ()
{
  char ch;
  int line = 1;
  while (std::cin >> std::noskipws >> ch)
    if (ch == '\n')
      ++line;
    else if (not std::isspace (static_cast<unsigned char> (ch)))
      co_yield Token { ch, line };
  co_yield Token { '$', line };
}

Gary M. Zoppetti, Ph.D.