{*****************************************************}
{ * PROJECT:       BBB interpreter}
{ * ORGANIZATION:  Microfluffy Corp.}
{ * LANGUAGE:      Turbo Pascal}
{ * FILE:          parser.pas}
{ * DESCRIPTION:   This file implements parser and the}
{ *                line execution engie}
{ * VERSION:       1.0}
{ *****************************************************}

unit Parser;

interface

	uses
		Scan, SymTab;

{ The ParseLine function takes a line of code, parses it and}
{  executes it.  st is the line of code, and LineNum is the line}
{  number that is currently being executed.  The function returns}
{  which line number to execute next, or if it returns NEXT_LINE}
{  then the program should execute the next line. }
	function ParseLine (var st: LineStr; LineNum: integer): integer;

implementation

	type

  { The result record to be passed thru the parser }
		Result = record

				case Tag : TokenType of
					T_NUM: (
							IntResult: integer
					);
					T_STRING: (
							StrResult: string
					);

			end;



	function ParseLine (var st: LineStr; LineNum: integer): integer;



  { there are a bunch of helper procedures here, each of which}
{}
{    parses a different nonterminal in the BNF.  See the project}
{}
{    documentation for more information. }



  { a few forwards }

		procedure Factor (var r: result);
		forward;

		procedure PrimaryExp (var r: result);
		forward;

		procedure Term (var r: result);
		forward;



  {}
{}
{    <expression> ::= <term>}
{}
{                     <term> + <expression>}
{}
{                     <term> - <expression>}
{}
{  }

		procedure Expression (var r: result);

			var

				rr: result;

				op: TokenType;

		begin {Expression}

			Term(r);

			while ((Token = T_PLUS) or (Token = T_MINUS)) do
				begin

					op := Token;

					NextToken;

					Term(rr);

					if (r.tag <> rr.tag) then

						PrintError(e_type, LineNum);

					case op of

						T_PLUS: 
							if (r.tag = T_NUM) then

								r.intResult := r.intResult + rr.intResult

							else
								r.strResult := concat(r.strResult, rr.strResult);

						T_MINUS: 
							if (r.tag = T_NUM) then

								r.intResult := r.intResult - rr.intResult

							else
								PrintError(e_type, LineNum);

					end;  {case}

				end;  {while}

		end; {Expression}



  {}
{}
{    <term> ::= <factor>}
{}
{               <factor> * <term>}
{}
{               <factor> / <term>}
{}
{               <factor> mod <term>}
{}
{  }

		procedure Term (var r: result);

			var

				rr: result;

				op: TokenType;

		begin {Term}

			Factor(r);

			while ((Token = T_STAR) or (Token = T_SLASH) or (Token = T_MOD)) do
				begin

					op := Token;

					NextToken;

					Factor(rr);

					if (r.tag = T_STRING) or (rr.tag = T_STRING) then

						PrintError(e_type, LineNum);

					case op of

						T_STAR: 
							r.intResult := r.intResult * rr.intResult;

						T_MOD: 
							r.intResult := r.intResult mod rr.intResult;

						T_SLASH: 

							begin

								if (rr.intResult = 0) then

									PrintError(e_divzero, LineNum);

								r.intResult := r.intResult div rr.intResult;

							end;

					end; {case}

				end; {while}

		end; {Term}



  {}
{}
{    <factor> ::= <primary_exp>}
{}
{                  - <primary_exp>}
{}
{  }

		procedure Factor (var r: result);

		begin {Factor}

			if (Token = T_MINUS) then
				begin

					NextToken;

					PrimaryExp(r);

					if (r.tag = T_STRING) then

						PrintError(e_type, LineNum);

					r.intResult := -r.intResult;

				end

			else

				PrimaryExp(r);

		end; {Factor}



  {}
{}
{    <primary_exp> ::= <var>}
{}
{                      <number>}
{}
{                      <string>}
{}
{                      ( <expresion> )}
{}
{  }

		procedure PrimaryExp (var r: result);

		begin

			case (Token) of

				T_VAR:   { <var> }

					begin

						if (IsStrVar(StrValue)) then
							begin

								r.tag := T_STRING;

								GetStrVal(StrValue, r.strResult);

							end

						else
							begin

								r.tag := T_NUM;

								GetIntVal(StrValue, r.intResult);

							end;

						NextToken;

					end;



				T_NUM:    { <number> }

					begin

						r.tag := T_NUM;

						r.intResult := IntValue;

						NextToken;

					end;



				T_STRING: { <string> }

					begin

						r.tag := T_STRING;

						r.strResult := StrValue;

						NextToken;

					end;



				T_LPAREN:  { ( <expression> ) }

					begin

						NextToken;

						Expression(r);

						if (Token <> T_RPAREN) then
							begin

								PrintError(e_syntax, LineNum);

							end;

						NextToken;

					end;

				otherwise

					begin

						PrintError(e_syntax, LineNum);

					end;

			end; {case}

		end; {PrimaryExp}



  {}
{}
{    <exp_list> ::=  <expression>}
{}
{                    <expression> , <exp_list>}
{}
{    <statement> ::= print <exp_list>}
{}
{                    print <exp_list> ;}
{}
{  }

		procedure ParsePrint;

			var

				r: Result;

		begin {ParsePrint}

			repeat

				NextToken;

				Expression(r);

				case r.tag of

					T_NUM: 
						write(r.IntResult : 0);

					T_STRING: 
						write(r.StrResult);

				end;

			until (Token <> T_COMMA);



			if (Token = T_SEMI) then

				NextToken

			else
				writeln;



			if (Token <> T_EOL) then

				PrintError(e_syntax, LineNum);

		end;  {ParsePrint}



  {}
{}
{    <statement> ::= end}
{}
{  }

		procedure ParseEnd;

		begin {ParseEnd}

			NextToken;

			if (Token <> T_EOL) then

				PrintError(e_syntax, LineNum)

			else
				begin
					writeln('Press any key to exit.');
					readln;
					Halt;
				end
		end;  {ParseEnd}



  {}
{}
{   <statement> ::= goto <number>}
{}
{  }

		procedure ParseGoto;

			var
				r: Result;

		begin {ParseGoto}

			NextToken;

			if (Token <> T_NUM) then

				PrintError(e_syntax, LineNum)

			else
				ParseLine := IntValue;

			NextToken;

			if (Token <> T_EOL) then

				PrintError(e_syntax, LineNum);

		end;  {ParseGoto}



	begin {ParseLine}

		ParseLine := NEXT_LINE;  {by default we will just go to the next line}

		StartLine(st);           {start the scanner on this line}



		case Token of

			T_END: 
				ParseEnd;

			T_GOTO: 
				ParseGoto;

			T_PRINT: 
				ParsePrint;

			T_EOL: 
				; { comment handling }

			otherwise

				begin

					PrintError(e_syntax, LineNum);

				end;

		end; {case}

	end;  {ParseLine}



end.
