Skip to content
Snippets Groups Projects
Verified Commit 9414bcfb authored by Martin Sjölund's avatar Martin Sjölund
Browse files

Swapped positions of lab1 and lab2

parent 6bd3dd3c
No related branches found
No related tags found
No related merge requests found
......@@ -8,7 +8,7 @@ PROJECT_NAME = TDDD55
PROJECT_NUMBER =
PROJECT_BRIEF = "TDDD55 Labs"
PROJECT_LOGO =
OUTPUT_DIRECTORY = doxygen-lab1
OUTPUT_DIRECTORY = doxygen-lab2
CREATE_SUBDIRS = NO
ALLOW_UNICODE_NAMES = NO
OUTPUT_LANGUAGE = English
......@@ -111,7 +111,7 @@ WARN_LOGFILE =
#---------------------------------------------------------------------------
# Configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = ../lab1
INPUT = ../lab2
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.c \
*.cc \
......
......@@ -22,7 +22,7 @@ html: Makefile doxygen-lab1/xml/index.xml doxygen-lab3/xml/index.xml
clean:
rm -rf "$(BUILDDIR)" doxygen-lab*
doxygen-lab1/xml/index.xml: Doxyfile.lab1
doxygen-lab2/xml/index.xml: Doxyfile.lab2
doxygen $<
doxygen-lab3/xml/index.xml: Doxyfile.lab3
doxygen $<
.. _lab1:
Lab 2: Scanner Specification
============================
Lab 1: Attribute Grammars and Top-Down Parsing
==============================================
The scanner is the part of the compiler responsible for grouping characters in the input stream into tokens which are used by the parser.
A typical scanner for a programming language will recognize tokens such as reserved words, identifiers, numeric constants, strings and operators.
.. default-domain:: cpp
One of the simplest ways of implementing a scanner is to use a scanner generator.
There are numerous such tools available. For this exercise you will use ``flex``, which stands for "Fast lex".
Although not as flexible as bottom-up parsers, top-down parsers can easily be implemented by hand, and as such they may be more convenient than a bottom-up parsers.
In this exercise you will specify a language of mathematical expressions using an attribute grammar, and then write a top-down parser to calculate the value of expressions in the language.
Using flex
----------
The language consists of numbers, symbolic constants, single-argument functions, one unary and five binary operators.
A grammar for the language is given below, but this grammar is not suitable for implementation using a top-down technique since it is ambiguous and contains left recursion.
Full documentation for flex is available as a man page.
Just type ``man flex`` at a Unix prompt to see the documentation.
There are a few differences between ``lex`` and ``flex``, but not too many.
.. code-block :: bnf
The Generated Scanner
---------------------
<S> ::= <E> <end of line> <S> // Single Expression
| <end of line> // No more input
<E> ::= <E> "+" <E> // Addition
| <E> "-" <E> // Subtraction
| <E> "*" <E> // Multiplication
| <E> "/" <E> // Division
| <E> "^" <E> // Exponentiation
| "-" <E> // Unary minus
| "(" <E> ")" // Grouping
| id "(" <E> ")" // Function call
| id // Symbolic constant
| num // Numeric value
When you compile a scanner specification using flex, a function named ``yylex`` is generated.
The default definition of this function takes no arguments and returns an integer, which represents the scanned token.
Tokens are usually numbered from 257 and up, since that allows the scanner to return any single character as a token.
In ``scanner.l`` the final rule uses this feature; any unmatched characters are returned as tokens.
The generated scanner also includes a number of important global variables and utility functions.
The ones that you will encounter are the variables ``yylineno``, ``yyin``, ``yytext`` and the function ``yyterminate``.
yylineno
This variable holds the number of the current line of input.
It is useful for error reporting, but slows the scanner down somewhat, so in order to use it, it has to be explicitly enabled using command-line options or the ``yylineno`` declaration in the scanner specification.
yyin
This variable holds the file pointer from which ``yylex`` reads its input.
yytext
This is a character array containing the characters that were recognized as a token.
yyterminate
This function terminates the scanning process and causes ``yylex`` to return 0.
It can be called in any action, and is the default action at the end of file.
The Tokens
----------
Your scanner shall skip comments, both single-line C++ comments and multi line C-style comments.
If the scanner sees ``/*`` within a C comment it has to print a warning message.
If the end of file is encountered within a C style comment, your scanner must print an error message and then terminate.
Floating-point numbers
Consist of an integer part followed by a period, a decimal part and an exponent.
The integer and decimal parts are simply sequences of digits.
The exponent part consists of the character ``E`` followed by an optional sign and a sequence of digits.
Either the integer or the decimal part (or both) must be given.
The exponent is optional.
If the integer part and exponent are both given, the decimal point and decimal part are optional.
These are some valid floating-point numbers: ``1.1``, ``.1``, ``1.``, ``1E2``, ``2E-3``, ``.1E-4``.
When your scanner recognizes a floating-point number it should return ``REAL``.
Integers
Are simply sequences of digits that are not part of identifiers or floating-point numbers.
When your scanner recognizes an integer it should return ``INTEGER``.
Identifiers
Must start with a letter, followed by any number of digits, letters or underscore characters.
When your scanner recognizes an identifier it should return ``ID``.
Requirements
------------
Rewrite the grammar in the previous section so that the precedence and associativity of all operators becomes obvious.
Your grammar may contain left recursion.
The operator precedence is unary negation before exponentiation before multiplication and division, before addition and subtraction.
Addition, subtraction, multiplication and division are left associative.
Exponentiation is right-associative.
Eliminate left recursion from your grammar and revise it so it is suitable for implementation in a predictive top-down parser.
Implement your attribute grammar in a C++ class named :class:`Parser`.
The :class:`Parser` class should contain a method named :func:`Parser::Parse` that returns the value of a single statement in the language.
Your interpreter should understand the following symbolic constants and functions:
.. list-table ::
* - pi
- 3.14159265
* - e
- 2.71828183
* - `log() <https://linux.die.net/man/3/log>`_
- Natural logarithm
* - `log10() <https://linux.die.net/man/3/log10>`_
- Base 10 logarithm
* - `exp() <https://linux.die.net/man/3/exp>`_
- Powers of e
* - `sin() <https://linux.die.net/man/3/sin>`_
- Sine
* - `cos() <https://linux.die.net/man/3/cos>`_
- Cosine
* - `tan() <https://linux.die.net/man/3/tan>`_
- Tangent
* - `arcsin() <https://linux.die.net/man/3/arcsin>`_
- Arc sine
* - `arccos() <https://linux.die.net/man/3/arccos>`_
- Arc cosine
* - `arctan() <https://linux.die.net/man/3/arctan>`_
- Arc tangent
All the functions are available in the standard math library.
See the Unix manual pages for details.
Implement error recovery in your parser.
The simplest form of error recovery is to scan tokens to the end of a line and then resume parsing.
Feel free to implement a smarter error recovery strategy.
You are to finish the scanner specification in ``scanner.l`` by adding rules for C and C++ style comments, identifiers, integers and reals.
Compile your scanner using the command ``make scanner``.
This generates a program named scanner, which you can use to test your scanner.
Run your scanner on the files in lab1/test and check that it generates the correct output.
Hand in the following
* Printouts of all the files you modified or created.
* Answers to the questions in the next section.
* Test data that show that the program works as specified.
Be sure to test error recovery, both from parser and scanner errors.
Be sure to check that error recovery does not interfere with the next input line.
Check that precedence and associativity rules are followed.
* The scanner specification, with your changes clearly marked.
* Test data that show that the scanner works as specified.
Demonstrate your solution to your lab assistant during a laboratory session.
Send an e-mail (one e-mail per group) with your modified code and answers to the questions to the same assistant, put TDDD55, assignment number and your LiU logins in the e-mail subject line.
Questions
---------
#. Define a regular expression for numeric constants.
It should allow integers, numbers with a fractional part and numbers with an exponent.
A number containing a decimal point must have at least one digit before or after the decimal point (or both).
The exponent may have a sign, plus or minus, and is always an integer.
.. list-table ::
:header-rows: 1
* - Allowed
- Not allowed
* - ``1234``
- ``A123``
* - ``3.14``
- ``.``
* - ``.112``
- ``112.a``
* - ``112.``
- ``1E2.3``
* - ``12.34``
- ``2.3e3.``
* - ``34E-23``
- ``23E 54``
* - ``34.E+3``
-
* - ``2.2e5``
-
#. Construct a DFA that accepts the same language as the regular expression you defined in the previous question.
Suggest how to implement a scanner based on your DFA.
Supporting Programs
-------------------
The files ``lab1.cc`` and ``lab1.hh`` contain a skeleton for the parser class and a class called :class:`Trace` that can be used to trace invocation of functions.
See the :func:`Parser::Parse` method for an example of how to use it.
Objects of the class print an entry message when created and an exit message when destroyed.
The files ``lex.cc`` and ``lex.hh`` contain a scanner class.
To use it create an object of type :class:`Scanner` and call its :func:`Scanner::Scan` method to get a token.
Tokens returned are of type :class:`Token`.
See the comments in ``lex.hh`` for a description of how they work.
The file ``main.cc`` contains a sample main program.
You may have to modify it depending on how you choose to report errors from your parser.
If the scanner encounters an error it will throw an object of type :class`ScannerError`.
Your main program should catch this exception (the sample main program does), print an error message (you can print a :class:`ScannerError` object using stream operators) and then perform error recovery.
Send an e-mail (one e-mail per group) with your modified code to the same assistant, put TDDD55, assignment number and your LiU logins in the e-mail subject line.
Lab 2: Scanner Specification
============================
.. _lab2:
The scanner is the part of the compiler responsible for grouping characters in the input stream into tokens which are used by the parser.
A typical scanner for a programming language will recognize tokens such as reserved words, identifiers, numeric constants, strings and operators.
Lab 2: Attribute Grammars and Top-Down Parsing
==============================================
One of the simplest ways of implementing a scanner is to use a scanner generator.
There are numerous such tools available. For this exercise you will use ``flex``, which stands for "Fast lex".
.. default-domain:: cpp
Using flex
----------
Although not as flexible as bottom-up parsers, top-down parsers can easily be implemented by hand, and as such they may be more convenient than a bottom-up parsers.
In this exercise you will specify a language of mathematical expressions using an attribute grammar, and then write a top-down parser to calculate the value of expressions in the language.
Full documentation for flex is available as a man page.
Just type ``man flex`` at a Unix prompt to see the documentation.
There are a few differences between ``lex`` and ``flex``, but not too many.
The language consists of numbers, symbolic constants, single-argument functions, one unary and five binary operators.
A grammar for the language is given below, but this grammar is not suitable for implementation using a top-down technique since it is ambiguous and contains left recursion.
The Generated Scanner
---------------------
.. code-block :: bnf
When you compile a scanner specification using flex, a function named ``yylex`` is generated.
The default definition of this function takes no arguments and returns an integer, which represents the scanned token.
Tokens are usually numbered from 257 and up, since that allows the scanner to return any single character as a token.
In ``scanner.l`` the final rule uses this feature; any unmatched characters are returned as tokens.
The generated scanner also includes a number of important global variables and utility functions.
The ones that you will encounter are the variables ``yylineno``, ``yyin``, ``yytext`` and the function ``yyterminate``.
yylineno
This variable holds the number of the current line of input.
It is useful for error reporting, but slows the scanner down somewhat, so in order to use it, it has to be explicitly enabled using command-line options or the ``yylineno`` declaration in the scanner specification.
yyin
This variable holds the file pointer from which ``yylex`` reads its input.
yytext
This is a character array containing the characters that were recognized as a token.
yyterminate
This function terminates the scanning process and causes ``yylex`` to return 0.
It can be called in any action, and is the default action at the end of file.
The Tokens
----------
Your scanner shall skip comments, both single-line C++ comments and multi line C-style comments.
If the scanner sees ``/*`` within a C comment it has to print a warning message.
If the end of file is encountered within a C style comment, your scanner must print an error message and then terminate.
Floating-point numbers
Consist of an integer part followed by a period, a decimal part and an exponent.
The integer and decimal parts are simply sequences of digits.
The exponent part consists of the character ``E`` followed by an optional sign and a sequence of digits.
Either the integer or the decimal part (or both) must be given.
The exponent is optional.
If the integer part and exponent are both given, the decimal point and decimal part are optional.
These are some valid floating-point numbers: ``1.1``, ``.1``, ``1.``, ``1E2``, ``2E-3``, ``.1E-4``.
When your scanner recognizes a floating-point number it should return ``REAL``.
Integers
Are simply sequences of digits that are not part of identifiers or floating-point numbers.
When your scanner recognizes an integer it should return ``INTEGER``.
Identifiers
Must start with a letter, followed by any number of digits, letters or underscore characters.
When your scanner recognizes an identifier it should return ``ID``.
<S> ::= <E> <end of line> <S> // Single Expression
| <end of line> // No more input
<E> ::= <E> "+" <E> // Addition
| <E> "-" <E> // Subtraction
| <E> "*" <E> // Multiplication
| <E> "/" <E> // Division
| <E> "^" <E> // Exponentiation
| "-" <E> // Unary minus
| "(" <E> ")" // Grouping
| id "(" <E> ")" // Function call
| id // Symbolic constant
| num // Numeric value
Requirements
------------
You are to finish the scanner specification in ``scanner.l`` by adding rules for C and C++ style comments, identifiers, integers and reals.
Compile your scanner using the command ``make scanner``.
This generates a program named scanner, which you can use to test your scanner.
Run your scanner on the files in lab2/test and check that it generates the correct output.
Rewrite the grammar in the previous section so that the precedence and associativity of all operators becomes obvious.
Your grammar may contain left recursion.
The operator precedence is unary negation before exponentiation before multiplication and division, before addition and subtraction.
Addition, subtraction, multiplication and division are left associative.
Exponentiation is right-associative.
Eliminate left recursion from your grammar and revise it so it is suitable for implementation in a predictive top-down parser.
Implement your attribute grammar in a C++ class named :class:`Parser`.
The :class:`Parser` class should contain a method named :func:`Parser::Parse` that returns the value of a single statement in the language.
Your interpreter should understand the following symbolic constants and functions:
.. list-table ::
* - pi
- 3.14159265
* - e
- 2.71828183
* - `log() <https://linux.die.net/man/3/log>`_
- Natural logarithm
* - `log10() <https://linux.die.net/man/3/log10>`_
- Base 10 logarithm
* - `exp() <https://linux.die.net/man/3/exp>`_
- Powers of e
* - `sin() <https://linux.die.net/man/3/sin>`_
- Sine
* - `cos() <https://linux.die.net/man/3/cos>`_
- Cosine
* - `tan() <https://linux.die.net/man/3/tan>`_
- Tangent
* - `arcsin() <https://linux.die.net/man/3/arcsin>`_
- Arc sine
* - `arccos() <https://linux.die.net/man/3/arccos>`_
- Arc cosine
* - `arctan() <https://linux.die.net/man/3/arctan>`_
- Arc tangent
All the functions are available in the standard math library.
See the Unix manual pages for details.
Implement error recovery in your parser.
The simplest form of error recovery is to scan tokens to the end of a line and then resume parsing.
Feel free to implement a smarter error recovery strategy.
Hand in the following
* The scanner specification, with your changes clearly marked.
* Test data that show that the scanner works as specified.
* Printouts of all the files you modified or created.
* Answers to the questions in the next section.
* Test data that show that the program works as specified.
Be sure to test error recovery, both from parser and scanner errors.
Be sure to check that error recovery does not interfere with the next input line.
Check that precedence and associativity rules are followed.
Demonstrate your solution to your lab assistant during a laboratory session.
Send an e-mail (one e-mail per group) with your modified code to the same assistant, put TDDD55, assignment number and your LiU logins in the e-mail subject line.
Send an e-mail (one e-mail per group) with your modified code and answers to the questions to the same assistant, put TDDD55, assignment number and your LiU logins in the e-mail subject line.
Questions
---------
#. Define a regular expression for numeric constants.
It should allow integers, numbers with a fractional part and numbers with an exponent.
A number containing a decimal point must have at least one digit before or after the decimal point (or both).
The exponent may have a sign, plus or minus, and is always an integer.
.. list-table ::
:header-rows: 1
* - Allowed
- Not allowed
* - ``1234``
- ``A123``
* - ``3.14``
- ``.``
* - ``.112``
- ``112.a``
* - ``112.``
- ``1E2.3``
* - ``12.34``
- ``2.3e3.``
* - ``34E-23``
- ``23E 54``
* - ``34.E+3``
-
* - ``2.2e5``
-
#. Construct a DFA that accepts the same language as the regular expression you defined in the previous question.
Suggest how to implement a scanner based on your DFA.
Supporting Programs
-------------------
The files ``lab2.cc`` and ``lab2.hh`` contain a skeleton for the parser class and a class called :class:`Trace` that can be used to trace invocation of functions.
See the :func:`Parser::Parse` method for an example of how to use it.
Objects of the class print an entry message when created and an exit message when destroyed.
The files ``lex.cc`` and ``lex.hh`` contain a scanner class.
To use it create an object of type :class:`Scanner` and call its :func:`Scanner::Scan` method to get a token.
Tokens returned are of type :class:`Token`.
See the comments in ``lex.hh`` for a description of how they work.
The file ``main.cc`` contains a sample main program.
You may have to modify it depending on how you choose to report errors from your parser.
If the scanner encounters an error it will throw an object of type :class`ScannerError`.
Your main program should catch this exception (the sample main program does), print an error message (you can print a :class:`ScannerError` object using stream operators) and then perform error recovery.
CC = g++
CCFLAGS = -g
LDFLAGS =
lab1 : lex.o main.o lab1.o
$(CC) -o lab1 $(CCFLAGS) $(LDFLAGS) lex.o main.o lab1.o
lex.o: lex.hh lex.cc
$(CC) $(CFLAGS) -c lex.cc
lab1.o: lab1.hh lex.hh lab1.cc
$(CC) $(CFLAGS) -c lab1.cc
main.o: lab1.hh lex.hh main.cc
$(CC) $(CFLAGS) -c main.cc
clean:
rm *.o lab1
CC = g++
CFLAGS = -ggdb3 -Wall -Woverloaded-virtual
#CC = CC
#CFLAGS = -g +p +w
GCFLAGS = -g
LDFLAGS =
DPFLAGS = -MM
SOURCES = scanner.cc main.cc
HEADERS = scanner.h
OBJECTS = $(SOURCES:%.cc=%.o)
OUTFILE = scanner
DPFILE = Makefile.dependencies
FLEX = flex
all : $(OUTFILE)
$(OUTFILE) : $(OBJECTS)
$(CC) -o $(OUTFILE) $(OBJECTS) $(LDFLAGS)
scanner.cc : scanner.l
$(FLEX) scanner.l
scanner.o : scanner.cc
$(CC) $(GCFLAGS) -c $<
.cc.o: $(DPFILE)
$(CC) $(CFLAGS) -c $<
clean :
rm -f $(OBJECTS) $(OUTFILE) core *~ scanner.cc $(DPFILE)
touch $(DPFILE)
$(DPFILE) depend : $(SOURCES) $(HEADERS)
$(CC) $(DPFLAGS) $(CFLAGS) $(SOURCES) > $(DPFILE)
include $(DPFILE)
File moved
#include <iostream>
#include <stdlib.h>
#include "lab1.hh"
#include "lex.hh"
int main(void)
{
Parser parser;
double val;
while (1)
{
try
{
cout << "Expression: " << flush;
val = parser.Parse();
cout << "Result: " << val << '\n' << flush;
}
catch (ScannerError& e)
{
cerr << e << '\n' << flush;
parser.Recover();
}
catch (ParserError)
{
parser.Recover();
}
catch (ParserEndOfFile)
{
cerr << "End of file\n" << flush;
exit(0);
}
}
}
#include <iostream>
#include <iomanip>
#include <stdio.h>
#include <stdlib.h>
#include "scanner.h"
using namespace std;
typedef struct
{
int token;
char *name;
} tTokenName;
tTokenName tokens[] = {{ FUNCTION, "FUNCTION" },
{ ID, "ID" },
{ DECLARE, "DECLARE" },
{ ARRAY, "ARRAY" },
{ INTEGER, "INTEGER" },
{ OF, "OF" },
{ REAL, "REAL" },
{ XBEGIN, "XBEGIN" },
{ XEND, "XEND" },
{ IF, "IF" },
{ THEN, "THEN" },
{ ELSE, "ELSE" },
{ WHILE, "WHILE" },
{ DO, "DO" },
{ ASSIGN, "ASSIGN" },
{ RETURN, "RETURN" },
{ GE, "GE" },
{ LE, "LE" },
{ EQ, "EQ" },
{ NE, "NE" },
{ TRUE, "TRUE" },
{ FALSE, "FALSE" },
{ PROGRAM, "PROGRAM" },
{ ELSEIF, "ELSEIF" },
{ NOT, "NOT" },
{ AND, "AND" },
{ OR, "OR" },
{ UMINUS, "UMINUS" }};
int numTokens = sizeof(tokens)/sizeof(*tokens);
ostream& PrintToken(ostream& o, int token)
{
int i;
extern char *yytext;
for (i = 0; i < numTokens; i++)
{
if (token == tokens[i].token)
{
o << tokens[i].name << " \'" << yytext << "\'";
return o;
}
}
o << '\'' << (unsigned char)token << '\'';
return o;
}
// omanip<int> Token(int t)
// {
// return omanip<int>(&PrintToken, t);
// }
int main(int argc, char **argv)
{
int token;
extern FILE *yyin;
extern int yylex();
/*
* Open the input file, if any
*/
switch(argc)
{
case 1:
yyin = stdin;
break;
case 2:
yyin = fopen(argv[1], "r");
if (yyin == NULL)
{
perror(argv[1]);
exit(1);
}
break;
default:
cerr << "Usage: " << argv[0] << " [ filename ]\n";
exit(1);
}
/*
* Loop for as long as there are tokens
*/
while ((token = yylex()) != 0)
{
cout << "Scanned "; PrintToken(cout, token); cout << '\n' << flush;
}
cout << "End of file\n";
exit(0);
}
void yyerror(char *msg)
{
extern int yylineno;
cerr << "Error at line " << yylineno << ": " << msg << '\n' << flush;
}
File moved
File moved
File moved
File moved
File moved
CC = g++
CFLAGS = -ggdb3 -Wall -Woverloaded-virtual
#CC = CC
#CFLAGS = -g +p +w
GCFLAGS = -g
LDFLAGS =
DPFLAGS = -MM
SOURCES = scanner.cc main.cc
HEADERS = scanner.h
OBJECTS = $(SOURCES:%.cc=%.o)
OUTFILE = scanner
DPFILE = Makefile.dependencies
FLEX = flex
all : $(OUTFILE)
$(OUTFILE) : $(OBJECTS)
$(CC) -o $(OUTFILE) $(OBJECTS) $(LDFLAGS)
scanner.cc : scanner.l
$(FLEX) scanner.l
scanner.o : scanner.cc
$(CC) $(GCFLAGS) -c $<
.cc.o: $(DPFILE)
$(CC) $(CFLAGS) -c $<
clean :
rm -f $(OBJECTS) $(OUTFILE) core *~ scanner.cc $(DPFILE)
touch $(DPFILE)
$(DPFILE) depend : $(SOURCES) $(HEADERS)
$(CC) $(DPFLAGS) $(CFLAGS) $(SOURCES) > $(DPFILE)
include $(DPFILE)
CC = g++
CCFLAGS = -g
LDFLAGS =
lab2 : lex.o main.o lab2.o
$(CC) -o lab2 $(CCFLAGS) $(LDFLAGS) lex.o main.o lab2.o
lex.o: lex.hh lex.cc
$(CC) $(CFLAGS) -c lex.cc
lab2.o: lab2.hh lex.hh lab2.cc
$(CC) $(CFLAGS) -c lab2.cc
main.o: lab2.hh lex.hh main.cc
$(CC) $(CFLAGS) -c main.cc
clean:
rm *.o lab2
#include "lab1.hh"
#include "lab2.hh"
//
......
File moved
File moved
File moved
#include <iostream>
#include <iomanip>
#include <stdio.h>
#include <stdlib.h>
#include "scanner.h"
using namespace std;
typedef struct
{
int token;
char *name;
} tTokenName;
tTokenName tokens[] = {{ FUNCTION, "FUNCTION" },
{ ID, "ID" },
{ DECLARE, "DECLARE" },
{ ARRAY, "ARRAY" },
{ INTEGER, "INTEGER" },
{ OF, "OF" },
{ REAL, "REAL" },
{ XBEGIN, "XBEGIN" },
{ XEND, "XEND" },
{ IF, "IF" },
{ THEN, "THEN" },
{ ELSE, "ELSE" },
{ WHILE, "WHILE" },
{ DO, "DO" },
{ ASSIGN, "ASSIGN" },
{ RETURN, "RETURN" },
{ GE, "GE" },
{ LE, "LE" },
{ EQ, "EQ" },
{ NE, "NE" },
{ TRUE, "TRUE" },
{ FALSE, "FALSE" },
{ PROGRAM, "PROGRAM" },
{ ELSEIF, "ELSEIF" },
{ NOT, "NOT" },
{ AND, "AND" },
{ OR, "OR" },
{ UMINUS, "UMINUS" }};
int numTokens = sizeof(tokens)/sizeof(*tokens);
ostream& PrintToken(ostream& o, int token)
{
int i;
extern char *yytext;
for (i = 0; i < numTokens; i++)
{
if (token == tokens[i].token)
{
o << tokens[i].name << " \'" << yytext << "\'";
return o;
}
}
o << '\'' << (unsigned char)token << '\'';
return o;
}
// omanip<int> Token(int t)
// {
// return omanip<int>(&PrintToken, t);
// }
int main(int argc, char **argv)
{
int token;
extern FILE *yyin;
extern int yylex();
/*
* Open the input file, if any
*/
switch(argc)
{
case 1:
yyin = stdin;
break;
case 2:
yyin = fopen(argv[1], "r");
if (yyin == NULL)
{
perror(argv[1]);
exit(1);
}
break;
default:
cerr << "Usage: " << argv[0] << " [ filename ]\n";
exit(1);
}
/*
* Loop for as long as there are tokens
*/
while ((token = yylex()) != 0)
{
cout << "Scanned "; PrintToken(cout, token); cout << '\n' << flush;
}
cout << "End of file\n";
exit(0);
}
void yyerror(char *msg)
{
extern int yylineno;
cerr << "Error at line " << yylineno << ": " << msg << '\n' << flush;
}
#include <iostream>
#include <stdlib.h>
#include "lab2.hh"
#include "lex.hh"
int main(void)
{
Parser parser;
double val;
while (1)
{
try
{
cout << "Expression: " << flush;
val = parser.Parse();
cout << "Result: " << val << '\n' << flush;
}
catch (ScannerError& e)
{
cerr << e << '\n' << flush;
parser.Recover();
}
catch (ParserError)
{
parser.Recover();
}
catch (ParserEndOfFile)
{
cerr << "End of file\n" << flush;
exit(0);
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment