0% found this document useful (0 votes)
171 views31 pages

Principles of Compiler Construction Lab File NSUT

The document describes three experiments related to developing language processors. Experiment 1 implements tokenization of source code by separating tokens. Experiment 2 implements the lexical analysis phase of a compiler by classifying tokens as keywords, identifiers, integers, etc. Experiment 3 develops simple language processors like a desk calculator and assembler, with the desk calculator implementation shown using stacks to evaluate arithmetic expressions.

Uploaded by

Vishu Aasliya
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
171 views31 pages

Principles of Compiler Construction Lab File NSUT

The document describes three experiments related to developing language processors. Experiment 1 implements tokenization of source code by separating tokens. Experiment 2 implements the lexical analysis phase of a compiler by classifying tokens as keywords, identifiers, integers, etc. Experiment 3 develops simple language processors like a desk calculator and assembler, with the desk calculator implementation shown using stacks to evaluate arithmetic expressions.

Uploaded by

Vishu Aasliya
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 31

Experiment 1

AIM: Write a program to implement the separation of


tokens from a source program.
Implementation:
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

bool isDelimiter(char ch)


{
if (ch == ' ' || ch == '+' || ch == '-' || ch == '*' ||
ch == '/' || ch == ',' || ch == ';' || ch == '>' ||
ch == '<' || ch == '=' || ch == '(' || ch == ')' ||
ch == '[' || ch == ']' || ch == '{' || ch == '}')
return (true);
return (false);
}

bool isOperator(char ch)


{
if (ch == '+' || ch == '-' || ch == '*' ||
ch == '/' || ch == ',' || ch == ';' || ch == '>' ||
ch == '<' || ch == '=' || ch == '(' || ch == ')' ||
ch == '[' || ch == ']' || ch == '{' || ch == '}')
return (true);
return (false);
}

char* subString(char* str, int left, int right)


{
int i;
char* subStr = (char*)malloc(sizeof(char) * (right - left + 2));

for (i = left; i <= right; i++)


subStr[i - left] = str[i];
subStr[right - left + 1] = '\0';
return (subStr);
}

void parse(char* str)


{
int left = 0, right = 0;
int len = strlen(str);

while (right <= len && left <= right) {


if (isDelimiter(str[right]) == false)
right++;

if (isDelimiter(str[right]) == true && left == right) {


if (isOperator(str[right]) == true)
printf("%c\n", str[right]);

right++;
left = right;
} else if (isDelimiter(str[right]) == true && left != right
|| (right == len && left != right)) {
char* subStr = subString(str, left, right - 1);
printf("%s\n", subStr);

left = right;
}
}
return;
}

int main()
{
char str[100] = "while(1){int a = 4};";
parse(str);
return (0);
}

Output:
Experiment 2
AIM: Write a program to implement lexical analysis phase
of a compiler.
Implementation:
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

bool isDelimiter(char ch)


{
if (ch == ' ' || ch == '+' || ch == '-' || ch == '*' ||
ch == '/' || ch == ',' || ch == ';' || ch == '>' ||
ch == '<' || ch == '=' || ch == '(' || ch == ')' ||
ch == '[' || ch == ']' || ch == '{' || ch == '}')
return (true);
return (false);
}

bool isOperator(char ch)


{
if (ch == '+' || ch == '-' || ch == '*' ||
ch == '/' || ch == '>' || ch == '<' ||
ch == '=')
return (true);
return (false);
}

bool validIdentifier(char* str)


{
if (str[0] == '0' || str[0] == '1' || str[0] == '2' ||
str[0] == '3' || str[0] == '4' || str[0] == '5' ||
str[0] == '6' || str[0] == '7' || str[0] == '8' ||
str[0] == '9' || isDelimiter(str[0]) == true)
return (false);
return (true);
}

bool isKeyword(char* str)


{
if (!strcmp(str, "if") || !strcmp(str, "else") ||
!strcmp(str, "while") || !strcmp(str, "do") ||
!strcmp(str, "break") ||
!strcmp(str, "continue") || !strcmp(str, "int")
|| !strcmp(str, "double") || !strcmp(str, "float")
|| !strcmp(str, "return") || !strcmp(str, "char")
|| !strcmp(str, "case") || !strcmp(str, "char")
|| !strcmp(str, "sizeof") || !strcmp(str, "long")
|| !strcmp(str, "short") || !strcmp(str, "typedef")
|| !strcmp(str, "switch") || !strcmp(str, "unsigned")
|| !strcmp(str, "void") || !strcmp(str, "static")
|| !strcmp(str, "struct") || !strcmp(str, "goto")|| !strcmp(str, "string"))
return (true);
return (false);
}

bool isInteger(char* str)


{
int i, len = strlen(str);

if (len == 0)
return (false);
for (i = 0; i < len; i++) {
if (str[i] != '0' && str[i] != '1' && str[i] != '2'
&& str[i] != '3' && str[i] != '4' && str[i] != '5'
&& str[i] != '6' && str[i] != '7' && str[i] != '8'
&& str[i] != '9' || (str[i] == '-' && i > 0))
return (false);
}
return (true);
}

bool isRealNumber(char* str)


{
int i, len = strlen(str);
bool hasDecimal = false;

if (len == 0)
return (false);
for (i = 0; i < len; i++) {
if (str[i] != '0' && str[i] != '1' && str[i] != '2'
&& str[i] != '3' && str[i] != '4' && str[i] != '5'
&& str[i] != '6' && str[i] != '7' && str[i] != '8'
&& str[i] != '9' && str[i] != '.' ||
(str[i] == '-' && i > 0))
return (false);
if (str[i] == '.')
hasDecimal = true;
}
return (hasDecimal);
}

char* subString(char* str, int left, int right)


{
int i;
char* subStr = (char*)malloc(sizeof(char) * (right - left + 2));

for (i = left; i <= right; i++)


subStr[i - left] = str[i];
subStr[right - left + 1] = '\0';
return (subStr);
}

void parse(char* str)


{
int left = 0, right = 0;
int len = strlen(str);

while (right <= len && left <= right) {


if (isDelimiter(str[right]) == false)
right++;

if (isDelimiter(str[right]) == true && left == right) {


if (isOperator(str[right]) == true)
printf("'%c' IS AN OPERATOR\n", str[right]);
else
printf("'%c' IS A DELIMITER\n", str[right]);

right++;
left = right;

} else if (isDelimiter(str[right]) == true && left != right


|| (right == len && left != right)) {
char* subStr = subString(str, left, right - 1);

if (isKeyword(subStr) == true)
printf("'%s' IS A KEYWORD\n", subStr);

else if (isInteger(subStr) == true)


printf("'%s' IS AN INTEGER\n", subStr);

else if (isRealNumber(subStr) == true)


printf("'%s' IS A REAL NUMBER\n", subStr);

else if (validIdentifier(subStr) == true


&& isDelimiter(str[right - 1]) == false)
printf("'%s' IS A VALID IDENTIFIER\n", subStr);
else if (validIdentifier(subStr) == false
&& isDelimiter(str[right - 1]) == false)
printf("'%s' IS NOT A VALID IDENTIFIER\n", subStr);
else
printf("'%s' IS A DELIMITER\n", subStr);

left = right;
}
}
return;
}

int main()
{
char str[100] = "while(1){int a = 4.6};";
parse(str);
return (0);
}

Output:
Experiment 3

AIM: Develop simple language processors like desk calculator


and assembler.
Implementation

Desk calculator
#include <iostream>
#include <sstream>
#include <stack>
#include <string>
#include <vector>

// Define a function to evaluate a given expression


double evaluateExpression(const std::string& expression) {
std::istringstream iss(expression);
std::stack<double> operandStack;
std::stack<char> operatorStack;

while (!iss.eof()) {
std::string token;
iss >> token;

if (token == "+" || token == "-" || token == "*" || token == "/") {


while (!operatorStack.empty() && operatorStack.top() != '(' && ((token == "*" || token == "/") ?
(operatorStack.top() == '*' || operatorStack.top() == '/') : true)) {
double b = operandStack.top();
operandStack.pop();
double a = operandStack.top();
operandStack.pop();
char op = operatorStack.top();
operatorStack.pop();

if (op == '+') operandStack.push(a + b);


else if (op == '-') operandStack.push(a - b);
else if (op == '*') operandStack.push(a * b);
else if (op == '/') operandStack.push(a / b);
}
operatorStack.push(token[0]);
} else if (token == "(") {
operatorStack.push('(');
} else if (token == ")") {
while (!operatorStack.empty() && operatorStack.top() != '(') {
double b = operandStack.top();
operandStack.pop();
double a = operandStack.top();
operandStack.pop();
char op = operatorStack.top();
operatorStack.pop();
2
if (op == '+') operandStack.push(a + b);
else if (op == '-') operandStack.push(a - b);
else if (op == '*') operandStack.push(a * b);
else if (op == '/') operandStack.push(a / b);
}
operatorStack.pop(); // Pop the remaining '('
} else {
double operand = stod(token);
operandStack.push(operand);
}
}

while (!operatorStack.empty()) {
double b = operandStack.top();
operandStack.pop();
double a = operandStack.top();
operandStack.pop();
char op = operatorStack.top();
operatorStack.pop();

if (op == '+') operandStack.push(a + b);


else if (op == '-') operandStack.push(a - b);
else if (op == '*') operandStack.push(a * b);
else if (op == '/') operandStack.push(a / b);
}

return operandStack.top();
}

int main() {
std::string expression;
std::cout << "Enter an arithmetic expression: ";
std::getline(std::cin, expression);

double result = evaluateExpression(expression);


std::cout << "Result: " << result << std::endl;

return 0;
}

2. ASSEMBLER
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_LINE_LENGTH 100


#define MAX_SYMBOLS 100

// Data structures to store symbols and their addresses


typedef struct {
char label[10];
int address;
} SymbolTableEntry;

3
SymbolTableEntry symbolTable[MAX_SYMBOLS];
int symbolCount = 0;

// Functions
int searchSymbolTable(char label[10]) {
for (int i = 0; i < symbolCount; i++) {
if (strcmp(symbolTable[i].label, label) == 0) {
return symbolTable[i].address;
}
}
return -1;
}

void addToSymbolTable(char label[10], int address) {


strcpy(symbolTable[symbolCount].label, label);
symbolTable[symbolCount].address = address;
symbolCount++;
}

int main() {
char line[MAX_LINE_LENGTH];
char mnemonic[10];
char label[10];
char operand[10];
int address = 0;
int machineCode = 0;

FILE *inputFile = fopen("assembly_code.txt", "r");


FILE *outputFile = fopen("machine_code.txt", "w");

if (!inputFile) {
printf("Error: Unable to open input file.\n");
return 1;
}

if (!outputFile) {
printf("Error: Unable to open output file.\n");
return 1;
}

while (fgets(line, MAX_LINE_LENGTH, inputFile)) {


sscanf(line, "%s %s %s", label, mnemonic, operand);

// First pass: Build the symbol table


if (label[0] != '\0') {
addToSymbolTable(label, address);
}

// Second pass: Translate instructions to machine code


if (strcmp(mnemonic, "ADD") == 0) {
machineCode = 0x1000;
} else if (strcmp(mnemonic, "SUB") == 0) {
machineCode = 0x2000;
} else if (strcmp(mnemonic, "LD") == 0) {
machineCode = 0x3000;
} else if (strcmp(mnemonic, "ST") == 0) {
machineCode = 0x4000;
} else if (strcmp(mnemonic, "HLT") == 0) {

4
machineCode = 0xF000;
} else {
printf("Error: Invalid mnemonic '%s'.\n", mnemonic);
return 1;
}

if (operand[0] != '\0') {
int operandValue = searchSymbolTable(operand);
if (operandValue == -1) {
printf("Error: Undefined label '%s'.\n", operand);
return 1;
}
machineCode += operandValue;
}

fprintf(outputFile, "%04X\n", machineCode);


address++;
}

fclose(inputFile);
fclose(outputFile);
printf("Assembly completed successfully.\n");

return 0;
}

RESULTS

Output:
a. Desk Calculator

5
ASSEMBLER O/P:

6
Experiment 4

AIM: Design a small high-level language.


Implementation:

Name of the Language: "MiniLang"

Features and Components:

1. Data Types:
• Integer (int): Represents whole numbers.
• Float (float): Represents numbers with decimal points.
• String (str): Represents text.

2. Variables:
• Variables are defined with the var keyword.
• Variables are dynamically typed (no need to specify data types explicitly).

3. Operators:
• Arithmetic operators: +, -, *, /, % (modulo)
• Comparison operators: ==, !=, <, >, <=, >=
• Logical operators: and, or, not

4. Control Flow:
• Conditional statements: if, else if, else
• Looping: for and while loops

5. Functions:
• Functions are defined with the func keyword.
• Support for parameters and return values.

6. Comments:
• Single-line comments using #

7
Source Code:

#include <iostream>
#include <string>
#include <stack>
#include <cctype>
#include <cstdlib>

using namespace std;

// Function to evaluate arithmetic expressions


int evaluateExpression(const string& expression) {
stack<int> values;
stack<char> operators;

for (size_t i = 0; i < expression.length(); ++i) {


if (isspace(expression[i])) {
continue; // Skip whitespace
} else if (isdigit(expression[i])) {
int value = 0;
while (i < expression.length() && isdigit(expression[i])) {
value = value * 10 + (expression[i] - '0');
++i;
}
values.push(value);
--i; // Backtrack one character
} else if (expression[i] == '+' || expression[i] == '-' || expression[i] == '*' || expression[i] == '/') {
while (!operators.empty() && (expression[i] != '+' && expression[i] != '-') && (operators.top() == '*' ||
operators.top() == '/')) {
char op = operators.top();
operators.pop();
int right = values.top();
values.pop();
int left = values.top();
values.pop();
if (op == '+') {
values.push(left + right);
} else if (op == '-') {
values.push(left - right);
} else if (op == '*') {
values.push(left * right);
} else if (op == '/') {
values.push(left / right);
}
}
operators.push(expression[i]);
} else {
cerr << "Error: Invalid character in the expression." << endl;
exit(1);
}
}

while (!operators.empty()) {
char op = operators.top();
operators.pop();

8
int right = values.top();
values.pop();
int left = values.top();
values.pop();
if (op == '+') {
values.push(left + right);
} else if (op == '-') {
values.push(left - right);
} else if (op == '*') {
values.push(left * right);
} else if (op == '/') {
values.push(left / right);
}
}

return values.top();
}

int main() {
string input;
while (true) {
cout << "Enter an arithmetic expression (or 'quit' to exit): ";
getline(cin, input);
if (input == "quit") {
break;
}

int result = evaluateExpression(input);


cout << "Result: " << result << endl;
}

return 0;
}

EXAMPLE

9
Output:

10
Experiment 5

AIM
Develop a lexical analyzer and a syntax analyzer for the same using the LEX and
YACC tools. Also implement the bookkeeper module.

Implementation
1. Lexical Analyzer(lex):

11
2 Syntax Analyzer(Yacc/bison)
%{

#include <stdio.h>

int yylex(void);

%}

%token NUMBER

%left '+' '-'

%left '*' '/'

%%

calc: expr '\n' { printf("Result: %d\n", $1);

expr: NUMBER { $$ = $1; }

| expr '+' expr { $$ = $1 + $3; }

| expr '-' expr { $$ = $1 - $3; }

| expr '*' expr { $$ = $1 * $3; }

| expr '/' expr {

if ($3 != 0) {

$$ = $1 / $3;

} else {

fprintf(stderr, "Error: Division by zero\n");

$$ = 0;

%%

int main() {

yyparse();

return 0;

}
12
Bookkeeper Module

You can create a file named bookkeeper.c with the following content:

#include <stdio.h>

void bookkeeper_module() {

printf("Bookkeeper module is working!\n");


}

Results
Compile

Input

Output

13
Experiment 6
AIM
Develop a simple calculator using LEX and YACC tools.

Implementation

Lexical Analyser for Calculator


%{
#include <stdio.h>
#include "y.tab.h"
extern int yylval;
%}
%% [ ] ;
“end” {return 0;}
[a-z] {
c = yytext[0]; yylval = c - 'a'; return(LETTER);
}
[0-9] {
c = yytext[0]; yylval = c - '0'; return(DIGIT);
}
[^a-z0-9\b] { c = yytext[0]; return(c);
}
%% 7
Parser for Calculator
%{
#include<stdio.h> int regs[26];
int base;
%}
%start list
/* %union { int a; } */
%token DIGIT LETTER
%left '|'
%left '&'
%left '+' '-'
%left '*' '/' '%'
%left UMINUS /*supplies precedence for unary minus */
%% /* beginning of rules section */ list: /*empty */
|
list stat '\n'
|
list error '\n'
{
yyerrok;
}
;
14
stat: expr
{
printf("Value of expression is %d\n",$1);
}
|
LETTER '=' expr
{
regs[$1] = $3;
}
;
expr: '(' expr ')' {
$$ = $2; }
|expr '*' expr { $$ = $1 * $3; }
|expr '/' expr { $$ = $1 / $3; }
|expr '%' expr { $$ = $1 % $3; }
|expr '+' expr { $$ = $1 + $3; }
|expr '-' expr { $$ = $1 - $3; }
|expr '&' expr { $$ = $1 & $3; }
|expr '|' expr { $$ = $1 | $3; }
|'-' expr %prec UMINUS { $$ = -$2; }
|LETTER { $$ = regs[$1]; }
|number ; number: DIGIT { $$ = $1; base = ($1==0) ? 8 : 10; }
| number DIGIT { $$ = base * $1 + $2; };
%%
main() {
printf("Enter arithmetic expressions for evaluation\nEnter end to stop.\n"); return(yyparse()); }
yyerror(s)
char *s; { fprintf(stderr, "%s\n",s); }
yywrap() {
return(1);
}

RESULTS

15
Experiment 7

AIM

Implement a program for symbol table using hashing.

Implementation
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 11
char l[10];
struct symb
{
int add;
char label[10];
}sy[11];
void search();
void main()
{
int a[MAX],num,key,i,ch;
char ans;
int create(int);
void lprob(int [],int,int);
void display(int []);
system("cls");
for(i=0;i<MAX;i++)
a[i]=0;
do
{
printf("\nEnter your choice:");
printf("\n1 - Create entry in the symbol table\n2 - Search in the symbol table\n");
scanf("%d",&ch);
switch(ch)
{
case 1:
do
{
printf("\nEnter the address:");
scanf("%d",&num);
key=create(num);
printf("Enter the label:");
scanf("%s",l);
lprob(a,key,num);
16
printf("\nContinue?\n0 for no\n1 for yes\n");
scanf("%s",ans);
}
while(ans==1);
display(a);
break;
case 2:
search();
break;
}
}while(ch<=2);
getch();
}

int create(int num)


{
int key;
key=num%11;
return key;
}

void lprob(int a[MAX],int key,int num)


{
int flag,i,count=0;
void display(int a[]);
flag=0;
if(a[key]==0)
{
a[key]=num;
sy[key].add=num;
strcpy(sy[key].label,l);
}
else
{
i=0;
while(i<MAX)
{
if(a[i]!=0)
count++;
i++;
}
if(count==MAX)
{
printf("\nHash table is full");
display(a);
getch();
exit(1);
}
for(i=key+1;i<MAX;i++)
if(a[i]==0)
{
17
a[i]=num;
flag=1;
sy[key].add=num;
strcpy(sy[key].label,l);
break;
}
for(i=0;i<key && flag==0;i++)
if(a[i]==0)
{
a[i]=num;
flag=1;
sy[key].add=num;
strcpy(sy[key].label,l);
break;
}
}
}
void display(int a[MAX])
{
FILE *fp;
int i;
fp=fopen("symbol.txt","w");
printf("\nSymbol Table:");
printf("\nHash values Address Label");
for(i=0;i<MAX;i++)
{
printf("\n%d\t %d\t %s",i,sy[i].add,sy[i].label);
fprintf(fp,"\n%d %d %s",i,sy[i].add,sy[i].label);
}
fclose(fp);
}

void search()
{
FILE *fp1;
char la[10];
int set=0,s;
int j,i;
printf("Enter the label: ");
scanf("%s",la);
fp1=fopen("symbol.txt","r");
for(i=0;i<MAX;i++)
{
fscanf(fp1,"%d%d",&j,&sy[i].add);
if(sy[i].add!=0)
fscanf(fp1,"%s",sy[i].label);
}
for(i=0;i<MAX;i++)
{
if(sy[i].add!=0)
{
18
if(strcmp(sy[i].label,la)==0)
{
set=1;
s=sy[i].add;
}
}
}
if(set==1)
printf("\nThe label '%s' is present in the symbol table at address:%d\n",la,s);
else
printf("\nThe label is not present in the symbol table\n");
}

RESULTS

19
20
21
EXPERIMENT-8

AIM
Implement a bottom-up parser using YACC tool.

Implementation

LEX file:

%{
#include<stdio.h>
#include "y.tab.h"
%}

%%
[0-9]+ {yylval.dval=atof(yytext);
return DIGIT;
}
\n|. return yytext[0];
%%

YACC file:
%{
/*This YACC specification file generates the LALR parser for the program
considered in experiment 4.*/
#include<stdio.h>
%}
%union
{
double dval;
}
%token <dval> DIGIT
%type <dval> expr
%type <dval> term
%type <dval> factor
%%
line: expr '\n' {
printf("%g\n",$1);
}
;
expr: expr '+' term {$$=$1 + $3 ;}
| term
;
term: term '*' factor {$$=$1 * $3 ;}
| factor
22
;
factor: '(' expr ')' {$$=$2 ;}
| DIGIT
;
%%
int main()
{
yyparse();
}
yyerror(char *s)
{
printf("%s",s);
}

RESULT:
$lex parser.l
$yacc –d parser.y
$cc lex.yy.c y.tab.c –ll –lm
$./a.out

9+4

15.0000

23
EXPERIMENT-9
AIM
Represent ‘C’ language using Context Free Grammar

IMPLEMENTATION
program -> declaration-list

declaration-list -> declaration


| declaration-list declaration
declaration -> var-declaration
| fun-declaration

var-declaration -> type-specifier ID ;


type-specifier -> int
| float

fun-declaration -> type-specifier ID ( params ) compound-stmt

params -> param-list


| VOID
param-list -> param
| param-list , param

param -> type-specifier ID

compound-stmt -> { local-declarations statement-list }


local-declarations -> local-declaration
| local-declarations local-declaration
local-declaration -> type-specifier ID ;
statement-list -> statement
| statement-list statement
statement -> expression-stmt
| compound-stmt
| selection-stmt
| iteration-stmt
| return-stmt

expression-stmt -> expression ;


expression -> var = expression
| simple-expression

var -> ID
simple-expression -> additive-expression relop additive-expression
| additive-expression

relop -> < | <= | > | >= | == | !=

additive-expression -> additive-expression addop term


| term

addop -> + | -

24
term -> term mulop factor
| factor

mulop -> * | /
factor -> ( expression )
| var
| call
| NUM

call -> ID ( args )


args -> arg-list

arg-list -> expression
| arg-list , expression

selection-stmt -> if ( expression ) statement


| if ( expression ) statement else statement

iteration-stmt -> while ( expression ) statement

return-stmt -> return ;


| return expression

25
EXPERIMENT-10
AIM
Add assignment statement, If then else statement and while loop to the calculator and generate the three address code for
the same

IMPLEMENTATION
import ply.yacc as yacc
# Tokens
tokens = (
'NUMBER',
'ID',
'ASSIGN',
'IF',
'THEN',
'ELSE',
'WHILE',
'PLUS',
'MINUS',
'TIMES',
'DIVIDE',
'LPAREN',
'RPAREN',
'SEMICOLON',
)
# Grammar
def p_program(p):
'''
program : statements
'''
p[0] = p[1]
def p_statements_empty(p):
'''
statements :
'''
p[0] = []
def p_statements_statement(p):
'''
statements : statements statement SEMICOLON
'''
p[0] = p[1] + [p[2]]
def p_statement_assign(p):
'''
statement : ID ASSIGN expression
'''
p[0] = ("=", p[1], p[3])
def p_statement_if(p):
'''
statement : IF expression THEN statements ELSE statements
'''
p[0] = ("if-else", p[2], p[4], p[6])
def p_statement_while(p):
'''
statement : WHILE expression statements
'''
p[0] = ("while", p[2], p[3])
26

You might also like