Česky
Kamil Dudka

IFJ 05 (C language)

File detail

Name:Downloadparser.c [Download]
Location: ifj05 > src
Size:10.8 KB
Last modification:2007-08-29 02:18

Source code

/*
 * Projekt: IFJ05
 * Resitele: xbarin02, xdudka00, xfilak01, xhefka00, xhradi08
 *
 * Soubor: parser.c - Syntakticky analyzator a generator kodu
 * Autor: Kamil Dudka, xdudka00
 */
 
#define INIT_TAPE_SIZE		256	// Pocatecni velikost pasky (pocet pseudoinstrukci)
 
#include "ifj05.h"
#include "scanner.h"
#include "symbols.h"
#include "variables.h"
#include "parser.h"
#include "expr.h"
#include "tape.h"
#include "runtime.h"
 
#include <ctype.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
 
static char szErrString[] =		"Invalid string";
static char szErrEscape[] =		"Invalid escape sequence";
static char szErrChar[] =		"Invalid char in input";
static char szErrExcl[] =		"'=' expected after '!'";
static char szErrColon[] =		"':' or '=' expected after ':'";
static char szErrFloat[] =		"Invalid format of float";
static char szErrInt[] =		"Invalid format of integer";
static char szErrLabelRedefinition[] =	"Redefinition of label \"%s\"";
static char szErrSyntax[] =		"Syntax error";
static char szErrStatement[] =		"Unknown statement";
static char szErrSemicolon[] =		"Semicolon expected";
static char szErrNoId[] =		"Identifier expected";
static char szErrNoLabel[] =		"Label expected";
static char szErrEOF[] =		"Unexpected end of file";
 
/*
 * Generuje navesti (zapis do tabulky symbolu)
 */
static inline void GenLabel (PParserData pPData, void *id) {
	dprintf ("@%s::\n", SymbolsGetName (id));
	if (0== SymbolsSetLabel (id, TapeGetNextIndex (pPData->pTape)))
		// Ok..
		return;
 
	// Label redefinition (semantic error)
	bool bSkipErr = pPData -> bSkipErr;
	ParseError (pPData, szErrLabelRedefinition, SymbolsGetName (id));
 
	// Semantic error is not parser error
	pPData -> bSkipErr = bSkipErr;
}
 
/*
 * Konecny automat parsujici zdrojovy soubor
 */
static int MainFA (PParserData);
 
/*
 * Obsluha syntakticke chyby
 * 1. parametr - ukazatel na lokalni data parseru
 * 2. parametr - chybova hlaska bez "\n"
 */
void ParseError (PParserData pPData, ...) {
	// Zacatek funkce s promennym poctem parametru
	va_list pArgs;
	va_start (pArgs, pPData);
 
	// Nebude se generovat kod
	pPData-> bErrCompile = true;
 
	if (pPData-> bSkipErr)
		// Preskakuju chyby nez narazim na ';' (rizeno hlavnim FA)
		return;
	pPData-> bSkipErr  = true;
 
	// Vypis chybove hlasky
	GlobalErr ("%s:%d: error: ", pPData->srcFileName, pPData->token.line +1);
 
	// Vypis variabilni chybove hlasky
	const char *fmt = va_arg (pArgs, const char *);
	GlobalErrV (fmt, pArgs);
 
	// Odradkuje
	GlobalErr ("\n");
 
	// Konec funkce s promennym poctem parametru
	va_end (pArgs);
}
 
static inline void TokenCharErr (PParserData pPData) {
	// Nebude se generovat kod
	pPData-> bErrCompile = true;
 
	if (pPData-> bSkipErr)
		// Preskakuju chyby nez narazim na ';' (rizeno hlavnim FA)
		return;
	pPData-> bSkipErr  = true;
 
	// Vypis chybove hlasky
	GlobalErr ("%s:%d: error: ", pPData->srcFileName, pPData->token.line +1);
 
	// Vypis znaku ktery chybu zpusobil
	const int iChar = *(int *)(pPData->token.pData);
	if (EOF== iChar)
		GlobalErr ("EOF - ");
 
	else if (isalpha (iChar) || isdigit (iChar))
		GlobalErr ("'%c' - ", iChar);
 
	else
		GlobalErr ("0x%X - ", iChar);
 
	// Vypis vlastni chybove hlasky
	switch (pPData->token.tType) {
		case tInvalidString:
			GlobalErr (szErrString);
			break;
 
		case tInvalidEscape:
			GlobalErr (szErrEscape);			
			break;
 
		case tInvalidChar:
			GlobalErr (szErrChar);
			break;
 
		default:
			break;
	}
 
	// Odradkuje
	GlobalErr ("\n");
}
 
 
/*
 * Nacte dalsi token
 *   - vola funkci z modulu "scanner"
 *   - sam obslouzi lexikalni chybu
 * 1. parametr - ukazatel na lokalni data parseru
 */
int ParserGetNextToken (PParserData pPData) {
	ScannerGetNextToken (pPData->pScan, &(pPData->token));
	switch (pPData->token.tType) {	
		case tInvalidString:
		case tInvalidEscape:
		case tInvalidChar:
			TokenCharErr (pPData);
			return 1;
 
		case tInvalidExcl:
			ParseError (pPData, szErrExcl);
			return 1;
 
		case tInvalidColon:
			ParseError (pPData, szErrColon);
			return 1;
 
		case tInvalidSign:
		case tInvalidExp:
		case tInvalidFloat:
			ParseError (pPData, szErrFloat);
			return 1;
 
		case tInvalidInt:
			ParseError (pPData, szErrInt);
			return 1;
 
		default:
			return 0;
	}
}
 
/*
 * Vstupni bod Parseru - odstartuje syntaxi rizeny preklad a interpretaci kodu
 * 1. parametr - ukazatel na otevreny zdrojovy soubor (moznost presmerovani)
 * 2. parametr - jmeno zdrojoveho souboru - muze byt obsazeno v chybovych hlaskach
 * Vraci kod chyby (nula znamena vsechno OK)
 */
int ParserInvoke (FILE *hFile, const char *srcFileName) {
	// Lokalni data Parseru
	TParserData parserData;
		parserData.srcFileName= srcFileName,
		parserData.bErrCompile= false;
		parserData.bSkipErr = false;
 
	// Inicializace Scanneru
	if (NULL== (parserData.pScan = ScannerInit (hFile) ))
		GlobalErrAlloc ();
 
	// Inicializace Pasky (pro ukladani pseudokodu)
	if (NULL== (parserData.pTape = TapeCreate (INIT_TAPE_SIZE)))
		GlobalErrAlloc ();
 
	// Odstartuji syntaktickou analyzu
	dprintf ("Analysing source code:\n");
	int iErr = MainFA (&parserData);
 
	// Go!
	if (0==iErr) {
		dprintf ("\nSource ok, starting execution..\n");
		iErr = RuntimeProcess (parserData.pTape, srcFileName);
	}
 
#ifndef NDEBUG
	else
		dprintf ("One or more parse errors detected..\n");
 
	if (0==iErr)
		dprintf ("Normal program exit..\n");
#endif
 
	dprintf ("Destruction of tape..\n");
	TapeDestroy (parserData.pTape);
 
	dprintf ("Destruction of scanner..\n");
	ScannerDestroy (parserData.pScan);
 
	dprintf ("Thank you for using IFJ05..\n");
	return iErr;
}
 
/*
 * Zapise pseudoinstrukci na vystupni pasku
 * 1. parametr - odkaz na lokalni data parseru
 * 2. parametr - kod instrukce, ktera se ma vlozit
 * 3. parametr - ukazatel predany jako operand psudoinstrukce
 */
void Generate (PParserData pPData, TInstruction eInst, void *id) {
#ifndef NDEBUG
	switch (eInst) {
		case inPUSHV:
			dprintf ("  push from     %s\n", SymbolsGetName (id) );
			break;
		case inPUSHC:
			dprintf ("  push          "); ConstantPrint (id); dprintf ("\n");
			break;
		case inPOPV:
			dprintf ("  pop to        %s\n", SymbolsGetName (id) );
			break;
		case inREADV:
			dprintf ("  read          %s\n", SymbolsGetName (id) );
			break;
		case inPRINTV:
			dprintf ("  print         %s\n", SymbolsGetName (id) );
			break;
		case inJMPV:
			dprintf ("  jmp           @%s\n", SymbolsGetName (id) );
			break;
		case inJNZV:
			dprintf ("  jnz           @%s\n", SymbolsGetName (id) );
			break;
		case inCMPE:
			dprintf ("  cmp_e\n");
			break;
		case inCMPNE:
			dprintf ("  cmp_ne\n");
			break;
		case inCMPL:
			dprintf ("  cmp_l\n");
			break;
		case inCMPG:
			dprintf ("  cmp_g\n");
			break;
		case inCMPLE:
			dprintf ("  cmp_le\n");
			break;
		case inCMPGE:
			dprintf ("  cmp_ge\n");
			break;
		case inADD:
			dprintf ("  add\n");
			break;
		case inSUB:
			dprintf ("  sub\n");
			break;
		case inMUL:
			dprintf ("  mul\n");
			break;
		case inDIV:
			dprintf ("  div\n");
			break;
		default:
			GlobalErr ("Internal error: Invalid instruction\n");
 
	} // switch (eInst)
#endif
	TInstBlock inst;
 
	inst.Inst = eInst;
	inst.Params.pointer = id;
	inst.line_no = pPData->token.line;
 
	TapeAddInst (pPData->pTape, &inst);
}
 
/*
 * Hlavni FA syntaktickeho analyzatoru
 * Provadi syntaxi rizeny preklad
 *
 * 1. parametr - ukazatel na otevreny zdrojovy soubor (moznost presmerovani)
 * 2. parametr - jmeno zdrojoveho souboru - muze byt obsazeno v chybovych hlaskach
 * vraci: kod chyby (nula znamena vsechno OK)
 */
static int MainFA (PParserData pPData) {
	// Mnozina stavu FA, pocatecni stav je init
	enum TState {
		stInit,
		stIdReaded,
		stAssignReaded,
		stReadReaded,
		stPrintReaded,
		stGotoReaded,
		stOnReaded,
		stOnGotoReaded,
		stStatementReaded,
		stReadNext,
	} state = stInit;
 
	// Vlastni cyklus FA
	void *id = NULL;
	while (1) {
		ParserGetNextToken (pPData);
		switch (state) {
			case stReadNext:
				if (tEOF == pPData->token.tType)
					// Konec souboru - vsechno ok?
					return pPData->bErrCompile;
 
			case stInit:
				switch (pPData->token.tType) {
					case tIdentifier:
						id = pPData->token.pData;
						state = stIdReaded;
						break;
 
					case tKeyRead:
						state = stReadReaded;
						break;
 
					case tKeyPrint:
						state = stPrintReaded;
						break;
 
					case tKeyGoTo:
						state = stGotoReaded;
						break;
 
					case tKeyOn:
						state = stOnReaded;
						break;
 
					default:
						state = stStatementReaded;
						ParseError (pPData, szErrStatement);
				}
				break; // (stInit)
 
			case stIdReaded:
				switch (pPData->token.tType) {
					case tAssignment:
						state = stAssignReaded;
						break;
 
					case tLabel:
						state = stReadNext;
						GenLabel (pPData, id);
						break;
 
					default:
						state = stStatementReaded;
						ParseError (pPData, szErrSyntax);
				}
				break; // (stIdReaded)
 
			case stReadReaded:
				state = stStatementReaded;
				if (tIdentifier == pPData->token.tType)
					Generate (pPData, inREADV, pPData->token.pData);
				else
					// Za Read neni identifikator
					ParseError (pPData, szErrNoId);
 
				break;	// (stReadReaded)
 
			case stPrintReaded:
				state = stStatementReaded;
 
				if (tIdentifier == pPData->token.tType)
					Generate (pPData, inPRINTV, pPData->token.pData);
				else
					// Za Print neni identifikator
					ParseError (pPData, szErrNoId);
 
				break;	// (stPrintReaded)
 
			case stGotoReaded:
				state = stStatementReaded;
 
				if (tIdentifier == pPData->token.tType)
					Generate (pPData, inJMPV, pPData->token.pData);
				else
					// Za go_to neni identifikator
					ParseError (pPData, szErrNoLabel);
 
				break;	// (stGotoReaded)
 
			case stOnReaded:
				if (!ExprParse (pPData) && tKeyGoTo==pPData->token.tType)
					// Preskoci i klicove slovo go_to, ktere bylo nacteno behem PA
					state = stOnGotoReaded;
				else
					// Chyba ve vyrazu
					state = stStatementReaded;
 
				break;	// (stOnReaded)
 
			case stOnGotoReaded:
				state = stStatementReaded;
 
				if (tIdentifier == pPData->token.tType)
					Generate (pPData, inJNZV, pPData->token.pData);
				else
					// Za go_to neni identifikator
					ParseError (pPData, szErrNoLabel);
 
				break;	// (stOnGotoReaded)
 
			case stAssignReaded:
				// Zpracovani hodnoty na prave strane
				if (!ExprParse (pPData) && tSemicolon==pPData->token.tType) {
					// Preskoci i strednik, ktery byl nacten behem PA
					state = stReadNext;
					Generate (pPData, inPOPV, id);
 
					break; // (stAssignReaded)
				}
 
				// Chyba ve vyrazu
				state = stStatementReaded;
 
			case stStatementReaded:
				switch (pPData->token.tType) {
					case tSemicolon:
						state = stReadNext;
 
						// Zastavit preskakovani spatnych prikazu
						pPData-> bSkipErr = false;
						break;
 
					case tEOF:
						ParseError (pPData, szErrEOF);
						return 1;
 
					default:
						ParseError (pPData, szErrSemicolon);
				}
 
				break;	// (stStatementReaded)
 
		} // switch (state)
 
	} // while (1)
	return 0;
}