Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • tddd55/tddd55-lab
  • yunli455/tddd55-lab
  • dimpi105/tddd55-lab
  • aleen361/tddd55-lab
4 results
Show changes
#!/usr/bin/python3
'''
Written by Martin Olivesten in 2023 for TDDD55.
Full permission to use in anyway with no restrictions. :)
Run: `python3 test.py -h` for usage.
'''
from subprocess import Popen, PIPE, STDOUT
from re import search
from argparse import ArgumentParser as ArgParse
from dataclasses import dataclass
from random import randrange
from io import TextIOWrapper
from typing import List, Tuple
GREEN = '\x1b[32m'
YELLOW = '\x1b[33m'
RED = '\x1b[31m'
RESET = '\x1b[39m'
PASS = f'{GREEN}PASS{RESET}'
FAIL = f'{RED}FAIL{RESET}'
MAYBE = f'{YELLOW}MAYBE{RESET}'
REAL_REGEX = r'-?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][+-]?[0-9]+)?'
dump = ''
@dataclass
class Result():
expr: str
expected: str
got: str
outcome: str
def parse_tests(file: TextIOWrapper) -> List[Tuple[str, str]]:
tests = file.readlines()
tests = filter(lambda t: t[0] != '#' and t.strip(), tests)
tests = map(lambda t: t.split('='), tests)
tests = list(tests)
for t in tests:
if len(t) != 2:
print(f'ERR: Invalid test format: `{", ".join(t).strip()}`.')
exit(1)
tests = map(lambda p: list(map(str.strip, p)), tests)
return list(tests)
def run_tests(tests: List[Tuple[str, str]], prog: str) -> (List[Result], int):
global dump
res = []
n_err = 0
for i, test in enumerate(tests):
expr, expected = test
p = Popen([prog], stdin=PIPE, stdout=PIPE, stderr=STDOUT)
p_out = p.communicate(f'{expr}\n'.encode())[0].decode()
m = search(fr'Result:\s*{REAL_REGEX}', p_out)
if m is None:
res.append(Result(expr, expected, 'ERR', FAIL))
n_err += 1
dump += 'ERR: Could not match for result in output.\n'
dump += f'INPUT: {expr}\n'
dump += f'OUTPUT:\n{p_out}\n\n'
continue
got = search(REAL_REGEX, m[0])[0]
outcome = ''
s_ans = got if len(got) <= len(expected) else expected
l_ans = got if len(got) > len(expected) else expected
if got == expected:
outcome = PASS
elif (len(s_ans) > 1
and s_ans.find('.') != -1
and (search(s_ans, l_ans)
or search(s_ans[:-1], l_ans[:-1]))):
outcome = MAYBE
else:
outcome = FAIL
res.append(Result(expr, expected, got, outcome))
return (res, n_err)
def main(file: TextIOWrapper, prog: str) -> None:
global dump
tests = parse_tests(open(file, 'r'))
if len(tests) == 0:
print("ERR: No tests to run")
exit(1)
res, n_err = run_tests(tests, prog)
expr_len = max(map(lambda r: len(r.expr), res))
expect_len = max(map(lambda r: len(r.expected), res))
got_len = max(map(lambda r: len(r.got), res))
nr_len = len(str(len(res)))
bar_len = 35 + expr_len + expect_len + got_len + nr_len
print('='*bar_len)
for i, r in enumerate(res):
print(f'| {i:>{nr_len}}: {r.expr:>{expr_len}} | Expected: {r.expected:>{expect_len}} | Got: {r.got:>{got_len}} | {r.outcome:>{5 + len(RED) + len(RESET)}} |') # noqa: E501
print('='*bar_len)
if n_err:
rand_str = ''.join([str(randrange(0, 10)) for _ in range(8)])
dump_file = f'./test-{rand_str}-dump.txt'
print(f'Writing error outputs to dump file: `{dump_file}`')
with open(dump_file, 'w') as f:
f.write(dump)
if __name__ == '__main__':
ap = ArgParse()
ap.add_argument(
'file',
help='Test file to use.'
)
ap.add_argument(
'-p', '--program',
default='./lab2',
help='Executable to run tests on. Defaults to `./lab2`.'
)
args = ap.parse_args()
main(args.file, args.program)
# Format:
# expression = expected answer
# '#' and empty lines ignored
# Precedence and associativity
1+2*3^-4 = 1.02469
1+2/3 = 1.66667
(1+2)/3 = 1
2*3+5 = 11
2*(3+5) = 16
2^2^3 = 256
2*2^2*2 = 16
(2*2)^(2*2) = 256
2^2*10 = 40
1--1 = 2
1*2^2+3/4^-1 = 16
(-4*-4+4^2) = 32
((-4*-4+4^2)-32+1) = 1
-(-1--(-(-10--100))) = 91
# Symbolic constants
e = 2.71828
pi = 3.14159
e^pi = 23.1407
# Symbolic functions
log10(10) = 1
sin(pi)+cos(pi) = -1
log(e^e) = 2.71828
sin(pi/2)+cos(0)+tan(pi/4) = 3
......@@ -15,6 +15,8 @@ OUTFILE = compiler
FLEX = flex
BISON = bison
TESTFILES=expression_test.c factorial_test.c fibonacci_test.c sort_test.c simple_array_test.c
TESTEXECS=expression_test factorial_test fibonacci_test sort_test simple_array_test
DPFILE = Makefile.dependencies
......@@ -39,10 +41,9 @@ parser.o : parser.cc
$(CC) $(CFLAGS) -c $<
clean :
rm -f $(OBJECTS) core *~ scanner.cc parser.cc parser.hh $(DPFILE) $(OUTFILE) parser.cc.output
rm -f $(OBJECTS) core *~ scanner.cc parser.cc parser.hh $(DPFILE) $(OUTFILE) parser.cc.output $(TESTFILES) $(TESTEXECS)
touch $(DPFILE)
$(DPFILE) depend : $(BASESRC) $(HEADERS)
$(CC) $(DPFLAGS) $(CFLAGS) $(BASESRC) > $(DPFILE)
......
symtab.o: symtab.cc symtab.hh string.hh ast.hh codegen.hh
ast.o: ast.cc ast.hh symtab.hh string.hh codegen.hh
codegen.o: codegen.cc ast.hh symtab.hh string.hh codegen.hh
main.o: main.cc ast.hh symtab.hh string.hh codegen.hh parser.hh
string.o: string.cc string.hh
# Code Skeleton for Lab 3 and Lab 4
This folder contains the code skeleton that you will be working on during Lab 3 and Lab 4.
## Lab 3
For Lab 3 two files are of interest, `parser.y` and `scanner.l`.
To start, begin by integrating what you did in lab 1 to the `scanner.l` file.
Here it is important that you are careful, and that you do not simply copy paste.
Look at the structure of `scanner.l` and enter your rules s.t you do not remove any includes present in that file.
Following this a good starting point for Lab 3 is to start by implementing support for the different expressions:
```C
/* --- Your code here ---
*
* Insert the expression grammar here
* The start symbol of the expression grammar is
* expression. This is important since it's used
* in a number of other places.
*
* Make sure that your code creates itor nodes in the
* AST wherever necessary and that it only created
* trees for expressions with compatible types!
*/
expression :
{
cerr << "Expression here" << endl;
}
;
```
Followed by the different rules for the conditions of the language:
```C
/* --- Your code here ---
*
* Insert the condition grammar here
* The start symbol is condition. It's used
* elsewhere, so make sure you get it right.
* The boolean constants false and true are represented using the BooleanConstant AST-node.
* The other logical relations are subclasses of BinaryRelation.
* See the file ast.hh for the declarations.
*/
condition :
{
cerr << "Condition here" << endl;
}
;
/* --- End your code --- */
```
### Testing Lab 3.
There is a number of test files in the `test` directory that you may use to check if your parser works as expected.
We also provide traces that you can use to compare the output of your solution to some expected output.
To do this you can exceute the following sequence of commands.
To check if you get about the same result for expressions you can run:
```bash
./compiler ./test/expression_test.prog > my_output.txt
diff -y my_output.txt ../traces/trace_expression_test.txt
```
The same procedure can be done for the more advanced test, that is `traces/trace-lab3.txt`
```
./compiler ./test/test > my_output.txt
diff -y my_output.txt ../traces/trace-lab3.txt
```
## Lab 4
In Lab 4 you will work in file `codegen.cc` search for `/* --- Your code here --- */`'
to see where you need to introduce your changes.
A good place to start is the function `BinaryGenerateCode`
```C++
/*
*
* This function is used to generate code for all kinds of binary
* operators and relations. The arguments to the function are the
* following:
*
* q The QuadsList onto which the generated code is placed.
* realop The quad to generate for the operator if the arguments
* are of type real.
* intop The quad to generate for the operator if the arguments
* are of type integer.
* left The AST for the left-hand side of the operator. The
* value type of this AST must be the same as that of the
* AST for the right-hand side.
* right The AST for the right-hand side of the operator. The
* value type of this AST must be the same as that of the
* AST for the left-hand side.
* node The operator node itself. You probably won't need this
* unless you want to print an error message.
* type If not NULL, this is the type of the result. This is
* used for relations, where the type of the result is
* always integer, even if the operands are real. If this
* parameter is NULL, then the type of the result is the
* same as the type of the operands (the parser needs to
* make sure that the operands have the same type.)
*
* See the GenerateCode methods for the binary operators for
* examples of how this function is used.
*
*/
static VariableInformation *BinaryGenerateCode(QuadsList& q,
tQuadType realop,
tQuadType intop,
ASTNode *left,
ASTNode *right,
ASTNode *node,
TypeInformation *type = NULL)
{
/* --- Your code here --- */
return NULL;
/* --- End your code --- */
}
```
### Testing Lab 4
In order to test Lab 4, there exists a test script.
For more details see the README in the test folder.
## Other notes
For more exhaustive documentation see [The Lab Skeleton Documentation](https://www.ida.liu.se/~TDDD55/laboratories/instructions/_static/skeleton.pdf)
......@@ -45,7 +45,7 @@ void ASTNode::print(ostream& o)
o << "ASTNode";
}
void ASTNode::xprint(ostream& o, char *cls)
void ASTNode::xprint(ostream& o, const char *cls)
{
o << "ASTNode (" << cls << ")";
}
......@@ -228,7 +228,7 @@ void BinaryOperation::print(ostream& o)
xprint(o, "BinaryOperation");
}
void BinaryOperation::xprint(ostream& o, char *cls)
void BinaryOperation::xprint(ostream& o, const char *cls)
{
o << cls << " (left, right) ["
<< ShortSymbols << valueType << LongSymbols
......@@ -241,7 +241,7 @@ void BinaryOperation::xprint(ostream& o, char *cls)
endChild(o);
}
void Plus::print(ostream& o) { xprint(o, "Plus"); } //
void Plus::print(ostream& o) { xprint(o, "Plus"); } //
void Minus::print(ostream& o) { xprint(o, "Minus"); }
void Times::print(ostream& o) { xprint(o, "Times"); }
void Divide::print(ostream& o) {xprint(o, "Divide"); }
......@@ -293,7 +293,7 @@ void BinaryRelation::print(ostream& o)
xprint(o, "BinaryRelation");
}
void BinaryRelation::xprint(ostream& o, char *cls)
void BinaryRelation::xprint(ostream& o, const char *cls)
{
o << cls << " (left, right)\n";
beginChild(o);
......@@ -316,7 +316,7 @@ void BinaryCondition::print(ostream& o)
xprint(o, "BinaryCondition");
}
void BinaryCondition::xprint(ostream& o, char *cls)
void BinaryCondition::xprint(ostream& o, const char *cls)
{
o << cls << " (left, right)\n";
beginChild(o);
......@@ -328,7 +328,7 @@ void BinaryCondition::xprint(ostream& o, char *cls)
}
void And::print(ostream& o) { xprint(o, "And"); }
void Or::print(ostream& o) { xprint(o, "Or"); } //
void Or::print(ostream& o) { xprint(o, "Or"); } //
void Not::print(ostream& o)
{
......
......@@ -59,7 +59,7 @@ protected:
void endChild(ostream& o);
void lastChild(ostream& o);
virtual void print(ostream& o);
virtual void xprint(ostream& o, char* cls);
virtual void xprint(ostream& o, const char* cls);
public:
virtual VariableInformation *GenerateCode(QuadsList &q) = 0;
......@@ -318,7 +318,7 @@ class BinaryOperation : public Expression
{
protected:
virtual void print(ostream& o);
virtual void xprint(ostream& o, char *);
virtual void xprint(ostream& o, const char *);
public:
Expression *left, *right;
......@@ -480,7 +480,7 @@ class BinaryRelation : public Condition
{
protected:
virtual void print(ostream& o);
virtual void xprint(ostream& o, char *cls);
virtual void xprint(ostream& o, const char *cls);
public:
Expression *left;
......@@ -563,7 +563,7 @@ class BinaryCondition : public Condition
{
protected:
virtual void print(ostream& o);
virtual void xprint(ostream&o, char *cls);
virtual void xprint(ostream&o, const char *cls);
public:
Condition *left, *right;
......
#!bin/bash
# Author: John Tinnerholm(johti17) johti17@liu.se
# This file tests if the last lab (Lab-4 generates correct code)
# Simple passing this test might not be enough to complete the laboration since there might be other issues..
# To the curious reader, yes this could have been solved by a function..
echo "****Testing Lab 4****"
echo "Testing simple expressions"
./compiler -c ./test/expression_test.prog > expression_test.c
if [ $? -eq 0 ]; then
echo "Compilation-OK"
else
echo "Compilation-Failed.. exiting"
exit 1
fi
echo "Frontend OK"
echo "Translating to C-Code..."
gcc expression_test.c -o expression_test -lm -Wno-int-conversion
if [ $? -eq 0 ]; then
echo "C-Code-Compilation-OK"
else
echo "C-Code-Compilation-Failed.. exiting"
exit 1
fi
echo "Testing the expression_test"
./expression_test > expression_output
if [ $? -eq 0 ]; then
echo "Program ran sucessfully"
else
echo "Program-Failed.. exiting"
exit 1
fi
echo "Checking that we get the correct output.."
if cmp -s "./expression_output" "test/expression_output_ref"; then
echo "The program generated the correct output"
else
echo "The output does not match..."
diff -y expression_output ./test/expression_output_ref
echo "Exiting.."
exit 1
fi
echo "Removing the testfile: rm expression_output"
#Remove the testfile
rm expression_output
echo "***********************"
echo "expression_test passed!"
echo "***********************"
#= Factorial =#
echo "Testing factorial_test"
./compiler -c ./test/factorial_test.prog > factorial_test.c
if [ $? -eq 0 ]; then
echo "Compilation-OK"
else
echo "Compilation-Failed.. exiting"
exit 1
fi
echo "Frontend OK"
echo "Translating to C-Code..."
gcc factorial_test.c -o factorial_test -lm -Wno-int-conversion
if [ $? -eq 0 ]; then
echo "C-Code-Compilation-OK"
else
echo "C-Code-Compilation-Failed.. exiting"
exit 1
fi
echo "Testing the factorial_test"
./factorial_test > factorial_output
if [ $? -eq 0 ]; then
echo "Program ran sucessfully"
else
echo "Program-Failed.. exiting"
exit 1
fi
echo "Checking that we get the correct output.."
if cmp -s "./factorial_output" "test/factorial_output_ref"; then
echo "The program generated the correct output"
else
echo "The output does not match..."
diff -y factorial_output ./test/factorial_output_ref
echo "Exiting.."
exit 1
fi
echo "Removing the testfile: rm factorial_output"
#Remove the testfile
rm factorial_output
echo "***********************"
echo "factorial_test passed!"
echo "***********************"
#= Fibonacci =#
echo "Testing fibonacci_test"
./compiler -c ./test/fibonacci_test.prog > fibonacci_test.c
if [ $? -eq 0 ]; then
echo "Compilation-OK"
else
echo "Compilation-Failed.. exiting"
exit 1
fi
echo "Frontend OK"
echo "Translating to C-Code..."
gcc fibonacci_test.c -o fibonacci_test -lm -Wno-int-conversion
if [ $? -eq 0 ]; then
echo "C-Code-Compilation-OK"
else
echo "C-Code-Compilation-Failed.. exiting"
exit 1
fi
echo "Testing the fibonacci_test"
./fibonacci_test > fibonacci_output
if [ $? -eq 0 ]; then
echo "Program ran sucessfully"
else
echo "Program-Failed.. exiting"
exit 1
fi
echo "Checking that we get the correct output.."
if cmp -s "./fibonacci_output" "test/fibonacci_output_ref"; then
echo "The program generated the correct output"
else
echo "The output does not match..."
diff -y fibonacci_output ./test/fibonacci_output_ref
echo "Exiting.."
exit 1
fi
echo "Removing the testfile: rm fibonacci_output"
#Remove the testfile
rm fibonacci_output
echo "***********************"
echo "fibonacci_test passed!"
echo "***********************"
#= Simple array =#
echo "Testing simple_array_test"
./compiler -c ./test/simple_array_test.prog > simple_array_test.c
if [ $? -eq 0 ]; then
echo "Compilation-OK"
else
echo "Compilation-Failed.. exiting"
exit 1
fi
echo "Frontend OK"
echo "Translating to C-Code..."
gcc simple_array_test.c -o simple_array_test -lm -Wno-int-conversion
if [ $? -eq 0 ]; then
echo "C-Code-Compilation-OK"
else
echo "C-Code-Compilation-Failed.. exiting"
exit 1
fi
echo "Testing the simple_array_test"
./simple_array_test > simple_array_test_output
if [ $? -eq 0 ]; then
echo "Program ran sucessfully"
else
echo "Program-Failed.. exiting"
exit 1
fi
echo "Checking that we get the correct output.."
if cmp -s "./simple_array_test_output" "test/simple_array_test_output_ref"; then
echo "The program generated the correct output"
else
echo "The output does not match..."
diff -y simple_array_test_output ./test/simple_array_test_output_ref
echo "Exiting.."
exit 1
fi
echo "Removing the testfile: rm simple_array_output"
#Remove the testfile
rm simple_array_test_output
echo "***********************"
echo "simple_array_test passed!"
echo "***********************"
#= Simple array =#
echo "Testing sort_test"
./compiler -c ./test/sort_test.prog > sort_test.c
if [ $? -eq 0 ]; then
echo "Compilation-OK"
else
echo "Compilation-Failed.. exiting"
exit 1
fi
echo "Frontend OK"
echo "Translating to C-Code..."
gcc sort_test.c -o sort_test -lm -Wno-int-conversion
if [ $? -eq 0 ]; then
echo "C-Code-Compilation-OK"
else
echo "C-Code-Compilation-Failed.. exiting"
exit 1
fi
echo "Testing the sort_test"
./sort_test > sort_test_output
if [ $? -eq 0 ]; then
echo "Program ran sucessfully"
else
echo "Program-Failed.. exiting"
exit 1
fi
echo "Checking that we get the correct output.."
if cmp -s "./sort_test_output" "test/sort_test_output_ref"; then
echo "The program generated the correct output"
else
echo "The output does not match..."
diff -y sort_test_output ./test/sort_test_output_ref
echo "Exiting.."
exit 1
fi
echo "Removing the testfile: rm simple_array_output"
#Remove the testfile
rm sort_test_output
echo "***********************"
echo "sort_test passed!"
echo "***********************"
This diff is collapsed.
......@@ -87,9 +87,9 @@ class Quad
{
private:
ostream& print(ostream&);
public:
tQuadType opcode;
void print_c(ostream& o);
//
// Arguments. Make sure you initialize the right ones!
......@@ -162,8 +162,8 @@ class QuadsList
static long labelCounter;
ostream& print(ostream&);
public:
void print_c(ostream&);
QuadsList() :
head(NULL),
tail(NULL) {};
......
#ifndef __KOMP_FUNCTION__
#define __KOMP_FUNCTION__
/*
* SymbolInformationType is used to tag object subclassed from
* SymbolInformation. The value of SymbolInformation's type field
* specified which subclass the object belongs to.
*/
typedef enum
{
kFunctionInformation,
kVariableInformation,
kTypeInformation,
} SymbolInformationType;
/*
* SymbolInformation is the base class for all information about
* symbols. It is never used directly; use the subclasses instead
*/
class SymbolInformation
{
public:
SymbolInformationType type;
string id;
SymbolTable *table;
SymbolInformation(SymbolInformationType t) : type(t) {};
};
/*
* FunctionInformation represents information stored about a function
* in the symbol table. It contains the return type of the function, a
* pointer to the functions's first parameter and a pointer to the
* symbol table for the function.
*/
class FunctionInformation : SymbolInformation
{
public:
TypeInformation *returnType;
VariableInformation *firstParam;
SymbolTable *symbolTable;
FunctionInformation() : SymbolInformationType(kFunctionInformation) {};
void SetParent(FunctionInformation *);
void SetReturnType(TypeInformation *);
void SetName(string&);
char AddParameter(string&, TypeInformation *);
char AddVariable(string&, TypeInformation *);
char AddArrayType(TypeInformation *, int);
};
/*
* VariableInformation represents information stored about a variable
* in the symbol table. It contains a type field which specifies
* the type of the variable and a next field which is used to link
* together parameters and local variables in a symbol table.
*/
class VariableInformation : SymbolInformation
{
public:
TypeInformation *type;
VariableInformation *next;
VariableInformation() : SymbolInformation(kVariableInformation) {};
};
class TypeInformation : SymbolInformation
{
public:
TypeInformation *elementType;
int arrayDimensions;
TypeInformation() : SymbolInformation(kTypeInformation) {};
}
/*
* SymbolTable is a symbol table. You'll never really use this
* directly. Instead, use the methods in the FunctionInformation
* class for adding and looking up variables in the symbol table
*/
class SymbolTableElement
{
public:
SymbolInformation *info;
SymbolTableElement *next;
};
class SymbolTable
{
public:
SymbolTableElement **table;
static int nextTemporary;
void AddSymbol(SymbolInformation *);
SymbolInformation *LookupSymbol(string&);
VariableInformation *GenTemp(TypeInformation *);
};
......@@ -6,24 +6,23 @@
#include "symtab.hh"
#include <unistd.h>
using namespace std;
extern int yyparse(void);
extern int yydebug;
extern int errorCount;
extern int warningCount;
static char *optionString = "dh";
static const char *OPTION_STRING = "dhc";
void Usage(char *program)
{
cerr << "Usage:\n"
<< program << " [-d] [filename]\n"
<< program << " [-d|-c] [filename]\n"
<< program << " -h\n"
<< "\n"
<< "Options:\n"
<< " -h Shows this message.\n"
<< " -d Turn on parser debugging.\n";
<< " -d Turn on parser debugging.\n"
<< " -c Generates a C-representation of the program\n";
exit(1);
}
......@@ -32,16 +31,19 @@ int main(int argc, char **argv)
{
int option;
extern FILE *yyin;
//
// Set up the symbol table
//
currentFunction = new FunctionInformation("main.");
/*
Set up the symbol table.
Adds builtin functions used by the language.
Builtin functions include routines to print reals integers etc.
*/
currentFunction = new FunctionInformation("main");
kIntegerType = new TypeInformation("integer", sizeof(long));
kRealType = new TypeInformation("real", sizeof(double));
kFPrintFunction = new FunctionInformation("putreal");
FunctionInformation *kPLReadFunction = new FunctionInformation("putline");
kIPrintFunction = new FunctionInformation("putint");
kFReadFunction = new FunctionInformation("getreal");
kIReadFunction = new FunctionInformation("getint");
......@@ -52,13 +54,15 @@ int main(int argc, char **argv)
kFPrintFunction->AddParameter("x", kRealType);
kIReadFunction->SetReturnType(kIntegerType);
kFReadFunction->SetReturnType(kRealType);
kIReadFunction->SetReturnType(kIntegerType);
currentFunction->AddSymbol(kIntegerType);
currentFunction->AddSymbol(kRealType);
currentFunction->AddSymbol(kIPrintFunction);
currentFunction->AddSymbol(kFPrintFunction);
currentFunction->AddSymbol(kIReadFunction);
currentFunction->AddSymbol(kFReadFunction);
currentFunction->AddSymbol(kPLReadFunction);
//
// Check command-line arguments
......@@ -66,7 +70,7 @@ int main(int argc, char **argv)
opterr = 0;
optopt = '?';
while ((option = getopt(argc, argv, optionString)) != EOF)
while ((option = getopt(argc, argv, OPTION_STRING)) != EOF)
{
switch (option)
{
......@@ -79,6 +83,12 @@ int main(int argc, char **argv)
case '?':
Usage(argv[0]);
break;
case 'c':
std::cout << "/*Using standard-library*/\n";
std::cout << "#include \"stl.h\"\n";
SymbolInformation::outputFormat = SymbolInformation::kCFormat;
currentFunction->outputFormat = SymbolInformation::kCFormat;
break;
}
}
......@@ -92,7 +102,7 @@ int main(int argc, char **argv)
}
optind += 1;
}
if (optind < argc)
Usage(argv[0]);
......@@ -100,8 +110,6 @@ int main(int argc, char **argv)
// Compile the input
//
yyparse();
return 0;
int retcode = yyparse();
return retcode;
}
......@@ -7,10 +7,10 @@
extern char *yytext;
extern int yylineno, errorCount, warningCount;
extern FunctionInformation *currentFunction;
extern FunctionInformation *currentFunction;
extern int yylex(void);
extern void yyerror(char *);
extern void yyerror(const char *);
extern char CheckCompatibleTypes(Expression **, Expression **);
extern char CheckAssignmentTypes(LeftValue **, Expression **);
extern char CheckFunctionParameters(FunctionInformation *,
......@@ -45,12 +45,12 @@ extern ostream& warning(void);
FunctionCall *call;
LeftValue *lvalue;
ElseIfList *elseIfList;
VariableInformation *variable;
TypeInformation *type;
FunctionInformation *function;
::string *id;
::string *id;
int integer;
double real;
void *null;
......@@ -81,7 +81,7 @@ extern ostream& warning(void);
*/
%token FUNCTION ID DECLARE ARRAY INTEGER OF REAL XBEGIN XEND IF THEN
%token ELSE WHILE DO ASSIGN RETURN GE LE EQ NE ARRAY TRUE FALSE PROGRAM
%token ELSE WHILE DO ASSIGN RETURN GE LE EQ NE TRUE FALSE PROGRAM
%token ELSEIF
......@@ -174,20 +174,15 @@ functions : functions function
* Write the function production. Take care to enter and exit
* scope correctly. You'll need to understand how shift-reduce
* parsing works and when actions are run to do this.
*
* Solutions that rely on shift-time actions will not be
* acceptable. You should be able to solve the problem
* using actions at reduce time only.
*
* In lab 4 you also need to generate code for functions after parsing
* them. Just calling GeneratCode in the function should do the trick.
*/
*/
function :
{
cerr << "Function here" << endl;
}
;
{
cerr << "Function here" << endl;
}
;
/* --- End your code --- */
......@@ -249,7 +244,7 @@ type : id
else
{
typeInfo = info->SymbolAsType();
if (typeInfo == NULL)
{
error() << *($1) << " is not a type" << "\n" <<flush;
......@@ -299,7 +294,7 @@ statements : statements statement
if ($2 == NULL)
$$ = NULL;
else
$$ = new StatementList($1, $2);
$$ = new StatementList($1, $2);
}
| /* Empty */
{
......@@ -403,7 +398,7 @@ returnstmt : RETURN expression
error() << " attempt to return "
<< ShortSymbols << expr->valueType << '\n';
error() << " in function declared to return "
<< ShortSymbols
<< ShortSymbols
<< currentFunction->GetReturnType()
<< LongSymbols << '\n';
$$ = NULL;
......@@ -454,13 +449,13 @@ variable : id
<< *($1)
<< "\n"
<< flush;
$$ = NULL;
}
else
{
varInfo = info->SymbolAsVariable();
if (varInfo == NULL)
{
error()
......@@ -545,7 +540,7 @@ call : funcname '(' expressions ')'
id : ID
{
$$ = new ::string(yytext);
$$ = new ::string(yytext);
}
;
......@@ -576,13 +571,13 @@ real : REAL
*/
expression :
{
cerr << "Expression here" << endl;
}
;
{
cerr << "Expression here" << endl;
}
;
/* --- End your code --- */
expressions : expressionz
{
......@@ -617,13 +612,15 @@ expressionz : expressionz ',' expression
* Insert the condition grammar here
* The start symbol is condition. It's used
* elsewhere, so make sure you get it right.
*/
* The boolean constants false and true are represented using the BooleanConstant AST-node.
* The other logical relations are subclasses of BinaryRelation.
* See the file ast.hh for the declarations.
*/
condition :
{
cerr << "Condition here" << endl;
}
;
{
cerr << "Condition here" << endl;
}
;
/* --- End your code --- */
......@@ -632,7 +629,7 @@ condition :
int errorCount = 0;
int warningCount = 0;
/* --- Your code here ---
*
......@@ -642,14 +639,14 @@ int warningCount = 0;
/* It is reasonable to believe that you will need a function
* that checks that two expressions are of compatible types,
* and if possible makes a type conversion.
* For your convenience a skeleton for such a function is
* For your convenience a skeleton for such a function is
* provided below. It will be very similar to CheckAssignmentTypes.
*/
/*
* CheckCompatibleTypes checks that the expressions indirectly pointed
* to by left and right are compatible. If type conversion is
* necessary, the pointers left and right point to will be modified to
* necessary, the pointers left and right point to will be modified to
* point to the node representing type conversion. That's why you have
* to pass a pointer to pointer to Expression in these arguments.
*/
......@@ -702,7 +699,7 @@ char CheckAssignmentTypes(LeftValue **left, Expression **right)
/*
* CheckFunctionParameters is used to check parameters passed to a
* function. func is the function we're passing parameters to, formals
* function. func is the function we're passing parameters to, formals
* is a pointer to the last formal parameter we're checking against
* and params is a pointer to the ExpressionList we're checking. If
* type conversion is necessary, the Expressions pointed to by the
......@@ -769,7 +766,7 @@ char CheckFunctionParameters(FunctionInformation *func,
<< params->expression->valueType
<< '\n'
<< LongSymbols << flush;
return 0;
return 0;
}
}
}
......@@ -800,7 +797,7 @@ char CheckReturnType(Expression **expr, TypeInformation *info)
}
void yyerror(char *message)
void yyerror(const char *message)
{
error() << message << '\n' << flush;
}
......
......@@ -55,7 +55,8 @@ not return NOT;
"<>" return NE;
array return ARRAY;
of return OF;
true return TRUE;
false return FALSE;
<<EOF>> yyterminate();
......
/*
The quite limited standard library/runtime for your language
--
John Tinnerholm
*/
#include <math.h>
#include <stdio.h>
//Array preprocessing
#define array_real(X) real([X])
#define array_integer(X) real([X])
#define real double
#define integer long
#define conditional bool
#define bool integer
integer putint(long x) {
printf("%ld", x);
return 0;
}
integer putreal(double x) {
printf("%lf", x);
return 0;
}
double getreal() {
double r;
scanf("%lf", &r);
return r;
}
double getint() {
long l;
scanf("%ld", &l);
return l;
}
integer putline()
{
printf("\n");
return 0;
}
......@@ -73,6 +73,16 @@ string::string(char *s)
chunk_size = 10;
}
string::string(const char *s)
{
text = strdup(s);
if (text == NULL)
abort();
size = strlen(s) + 1;
position = strlen(s);
chunk_size = 10;
}
string::string(char c, int sz)
{
text = (char *)malloc(sz);
......
......@@ -20,7 +20,7 @@ using std::ostream;
// really necessary. If strings were considered read only (no
// destructive update), then it would be possible to keep a pool of
// strings with reference counts and just pass along references to
// that pool.
// that pool.
//
class string
......@@ -34,10 +34,14 @@ private:
void ensure_size(int);
public:
//To kill warnings and make this custom string play nice...
class error {}; // Exception thrown when out of memory
string(); // Default constructor creates empty string
string(char *); // Create string from character pointer
string(const char *); // Create string from a constant character pointer
string(char, int); // Create string filles with characters
string(const string &); // Copy constructor
string(int); // Convert an integer
......@@ -50,7 +54,7 @@ public:
string& operator=(const string&); // Assignment operator
string& operator=(const char *); // Assignment operator
string& operator=(const char); // Assignment operator
string& operator+=(const string&); // Append operator
string& operator+=(const char); // Append operator
string& operator+=(const char *); // Append operator
......
......@@ -42,6 +42,9 @@ ostream& SymbolInformation::print(ostream& o)
case kShortFormat:
o << id;
break;
case kCFormat:
o << id;
break;
default:
o << "Bad output format\n";
abort();
......@@ -64,7 +67,6 @@ ostream& TypeInformation::print(ostream& o)
o << '\n';
o << " Dimensions: " << arrayDimensions << '\n';
o << " Size: " << size << '\n';
case kSummaryFormat:
o << (void*)this << ' ';
if (elementType != NULL)
......@@ -78,10 +80,10 @@ ostream& TypeInformation::print(ostream& o)
{
o << id;
}
o << " [" << size << "]";
break;
case kShortFormat:
if (elementType != NULL)
{
......@@ -94,7 +96,16 @@ ostream& TypeInformation::print(ostream& o)
o << id;
}
break;
case kCFormat:
if (elementType != NULL)
{
o << "[" << arrayDimensions << "]";
}
else
{
o << id;
}
break;
default:
o << "Bad output format\n";
......@@ -133,6 +144,10 @@ ostream& VariableInformation::print(ostream& o)
o << id;
break;
case kCFormat:
o << id;
break;
default:
o << "Bad output format\n";
abort();
......@@ -185,12 +200,13 @@ ostream& FunctionInformation::print(ostream& o)
tmp = tmp->prev;
}
o << LongSymbols;
o << '\n';
}
else
{
o << " Locals: none\n";
}
o << " Body: " << (void*)body << '\n';
if (body) o << body;
o << '\n';
......@@ -198,7 +214,7 @@ ostream& FunctionInformation::print(ostream& o)
o << " Quads: " << (void*)quads << '\n';
if (quads) o << quads;
o << '\n';
o << symbolTable;
break;
......@@ -222,7 +238,61 @@ ostream& FunctionInformation::print(ostream& o)
case kShortFormat:
o << id;
break;
case kCFormat:
/*C-Code preamble*/
if (returnType == NULL) {
o << "void\n";
} else {
o << returnType << "\n";
}
o << "" << id << "(";
//Parameters
if (lastParam != NULL)
{
tmp = lastParam;
while (tmp != NULL)
{
if (tmp->type->elementType != NULL){ //Array...
o << tmp->type->elementType << "*" << "\t";
} else {
// o << tmp->type->elementType;
o << tmp->type << "\t";
}
if (tmp->prev == NULL) {
o << tmp->id;
} else {
o << tmp->id << ",";
}
tmp = tmp->prev;
}
}
o << ")\n";
/* Generate C-Code for the body! */
o << "{\n";
if (lastLocal)
{
o << "//Locals:\n";
tmp = lastLocal;
while (tmp != NULL)
{
if (tmp->type->elementType != NULL){ //Array...
o << tmp->type->elementType;
o << "\t";
o << tmp;
o << tmp->type << ";\n";
}
else {
o << tmp->type << "\t" << tmp << ";\n";
}
tmp = tmp->prev;
}
}
quads->print_c(o);
o << "}\n";
break;
default:
o << "Bad output format.\n";
abort();
......@@ -327,7 +397,7 @@ TypeInformation *FunctionInformation::AddArrayType(TypeInformation *elemType,
else
{
name = elemType->id + "<" + dimensions + ">.";
name = elemType->id + (char*)"<" + dimensions + (char*)">.";
xinfo = symbolTable.LookupSymbol(name);
if (xinfo == NULL)
{
......@@ -360,14 +430,14 @@ FunctionInformation *FunctionInformation::AddFunction(const ::string& name,
if (xinfo != NULL && xinfo->tag == kTypeInformation)
{
cerr << "Bug: you tried to create a function that's also a type\n";
abort();
abort();
}
xinfo = symbolTable.LookupSymbol(name);
if (xinfo != NULL)
{
cerr << "Bug: you tried to create a function with a name "
<< "that's already in use\n";
<< "that's already in use\n";
}
fn->id = name;
......@@ -382,13 +452,13 @@ VariableInformation *FunctionInformation::TemporaryVariable(TypeInformation *typ
temporaryCount += 1;
info = new VariableInformation(::string("T:") + (int)temporaryCount, type);
info = new VariableInformation(::string("T_") + (int)temporaryCount, type);
info->prev = NULL;
AddSymbol(info);
return info;
}
char FunctionInformation::OkToAddSymbol(const ::string& name)
{
......@@ -554,6 +624,12 @@ ostream& LongSymbols(ostream& o)
return o;
}
ostream& CFormat(ostream& o)
{
SymbolInformation::outputFormat = SymbolInformation::kCFormat;
return o;
}
ostream& SummarySymbols(ostream& o)
{
SymbolInformation::outputFormat = SymbolInformation::kSummaryFormat;
......
......@@ -22,6 +22,7 @@ extern FunctionInformation *kFPrintFunction;
extern FunctionInformation *kIPrintFunction;
extern FunctionInformation *kFReadFunction;
extern FunctionInformation *kIReadFunction;
extern FunctionInformation *kIReadFunction;
extern TypeInformation *kRealType;
extern TypeInformation *kIntegerType;
......@@ -91,19 +92,16 @@ protected:
friend ostream& LongSymbols(ostream&);
friend ostream& SummarySymbols(ostream&);
friend ostream& ShortSymbols(ostream&);
typedef enum { kFullFormat, kSummaryFormat, kShortFormat } tFormatType;
static tFormatType outputFormat;
public:
typedef enum { kFullFormat, kSummaryFormat, kShortFormat, kCFormat } tFormatType;
static tFormatType outputFormat;
SymbolInformationType tag;
::string id;
SymbolTable *table;
SymbolInformation(SymbolInformationType t, const ::string &i) :
tag(t),
id(i) {};
tag(t),
id(i) {};
virtual ~SymbolInformation() {}
virtual FunctionInformation *SymbolAsFunction(void) { return NULL; };
......@@ -143,13 +141,13 @@ private:
public:
FunctionInformation(const ::string& i) :
SymbolInformation(kFunctionInformation, i),
parent(NULL),
returnType(NULL),
lastParam(NULL),
lastLocal(NULL),
body(NULL),
quads(NULL) {};
SymbolInformation(kFunctionInformation, i),
parent(NULL),
returnType(NULL),
lastParam(NULL),
lastLocal(NULL),
body(NULL),
quads(NULL) {};
virtual FunctionInformation *SymbolAsFunction(void) { return this; };
......@@ -166,7 +164,7 @@ public:
FunctionInformation *AddFunction(const ::string&, FunctionInformation *);
VariableInformation *AddParameter(const ::string&, TypeInformation *);
VariableInformation *AddVariable(const ::string&, TypeInformation *);
VariableInformation *AddVariable(const ::string&, TypeInformation *);
SymbolInformation *AddSymbol(SymbolInformation *);
TypeInformation *AddArrayType(TypeInformation *, int);
......@@ -199,10 +197,10 @@ public:
virtual VariableInformation *SymbolAsVariable(void) { return this; };
VariableInformation(const ::string& i) :
SymbolInformation(kVariableInformation, i) {};
SymbolInformation(kVariableInformation, i) {};
VariableInformation(const ::string& i, TypeInformation *t) :
SymbolInformation(kVariableInformation, i),
type(t) {};
SymbolInformation(kVariableInformation, i),
type(t) {};
};
......@@ -222,8 +220,8 @@ public:
virtual TypeInformation *SymbolAsType(void) { return this; };
TypeInformation(const ::string& i, unsigned long s) :
SymbolInformation(kTypeInformation, i),
size(s) {};
SymbolInformation(kTypeInformation, i),
size(s) {};
};
......@@ -233,6 +231,7 @@ public:
ostream& ShortSymbols(ostream& o);
ostream& LongSymbols(ostream& o);
ostream& CFormat(ostream& o);
#endif
# This file contains various files for testing
## expression_test.prog
This program simple executes and print simple expressions
## factorial_test.prog
This program checks if functions and if/else statements are handled correctly.
Checks the factorial function
## fibonacci_test.prog
Similar to the factorial test, also tests recursions
## sort_test.prog
This program simple executes selection sort on an unsorted array
## *output_ref
These are references files specifing the expected output from the programs above.
It is used by codegen-test.sh
## How to test?
To test a program simply execute codegen-test.sh in the parent directory.
```
bash codegen-test.sh
```
## How do I compile my own program?
Invoke the compiler.
Pipe the output to a file with the suffix.c
```
compiler -c > <name>.c
```
Do not use the -d flag when doing this.
Then compile this file with
```
gcc -lm <name.c>
```