%{
/*
** $PROJECT: c.datatype
**
** $VER: cdt.y 39.4 (24.03.95)
**
** by
**
** Stefan Ruppert , Windthorststraße 5 , 65439 Flörsheim , GERMANY
**
** (C) Copyright 1995
** All Rights Reserved !
**
** $HISTORY:
**
** 24.03.95 : 039.004 : missing else keyword added
** 23.03.95 : 039.003 : now full tabs handling
** 12.03.95 : 039.002 : added C++ keywords
** 07.03.95 : 039.001 : initial
*/

#include "classbase.h"

void add_line(struct ClassBase *cb,struct CParse *cparse,ULONG index,UWORD flags)
{
	struct Style *style = &cparse->Data->cd_CStyle[index];
	struct Line *line;

	/* ignore segments , which are marked as text and no tabs are found ! */
	if(!(style->Style == ~0 && index != C_STANDARD && cparse->XOffsetAdd == 0))
	{
		if((line = AllocPooled(cparse->Data->cd_Pool,sizeof(struct Line))))
		{
			STRPTR ptr = cparse->SegPtr;
			ULONG x = cparse->XOffsetAdd;

			while(*ptr == ' ' || *ptr == '\t')
			{
				if(*ptr == ' ')
					x += cparse->TabWidth;
				ptr++;
			}

			line->ln_Text    = ptr;
			line->ln_TextLen = cparse->ActPtr - ptr;

			if((flags & LNF_LF) && line->ln_TextLen > 0)
				line->ln_TextLen--;

			line->ln_XOffset = cparse->XOffset + x;
			line->ln_YOffset = cparse->YOffset;
			line->ln_Width   = TextLength(cparse->RPort,line->ln_Text,line->ln_TextLen) + x;
			line->ln_Height  = cparse->RPort->TxHeight;
			line->ln_Flags   = flags;
			line->ln_FgPen   = style->FgPen;
			line->ln_BgPen   = style->BgPen;
			line->ln_Style   = style->Style;

			if(flags & LNF_LF)
			{
				if(cparse->XOffset + line->ln_Width > cparse->MaxWidth)
					cparse->MaxWidth = cparse->XOffset + line->ln_Width;

				cparse->YOffset += line->ln_Height;
				cparse->XOffset  = 0;
			} else
			{
				cparse->XOffset += line->ln_Width;

				D({
					STRPTR ptr = &line->ln_Text[line->ln_TextLen];
					UBYTE help = *ptr;

					*ptr = '\0';
					bug("text : %s\n",line->ln_Text);
					*ptr = help;
				  });
			}

			cparse->XOffsetAdd = 0;
			cparse->SegPtr  = cparse->ActPtr;

			AddTail(cparse->LineList,(struct Node *) line);
		}
	}
}

void add_cexpr(struct ClassBase *cb,struct CParse *cparse,ULONG index,BOOL lf)
{
	if(cparse->TxtPtr > cparse->SegPtr)
	{
		STRPTR actptr = cparse->ActPtr;
		ULONG index = (cparse->Mode < C_MAX) ? cparse->Mode : C_STANDARD;
		ULONG x = cparse->XOffsetAdd;

		cparse->XOffsetAdd = 0;
		cparse->ActPtr = cparse->TxtPtr;

		add_line(cb,cparse,index,0);

		cparse->XOffsetAdd = x;
		cparse->SegPtr = cparse->ActPtr;
		cparse->ActPtr = actptr;
	}


	add_line(cb,cparse,index,lf);

	cparse->TxtPtr = cparse->ActPtr;
}

/* don't use stdio.h, because I use the AmigaOS calls */
#define _STDIO_H

%}

%pure_parser

/* cpp keywords */
%token T_INCLUDE T_DEFINE T_UNDEF T_PRAGMA T_LINE T_ERROR
%token T_IF T_IFDEF T_IFNDEF T_ELIF T_ENDIF

/* storage class */
%token T_AUTO T_EXTERN T_REGISTER T_STATIC

/* type specifiers */
%token T_CHAR T_SHORT T_INT T_LONG
%token T_SIGNED T_UNSIGNED T_FLOAT T_DOUBLE T_VOID

/* other c keywords */
%token T_SWITCH T_CASE T_BREAK T_DEFAULT
%token T_IF T_ELSE T_FOR T_DO T_WHILE T_CONTINUE
%token T_CONST T_VOLATILE
%token T_GOTO
%token T_RETURN

%token T_STRUCT T_UNION T_ENUM T_TYPEDEF
%token T_SIZEOF

/* C++ keywords */
%token T_CLASS T_PUBLIC T_PRIVATE T_PROTECTED
%token T_VIRTUAL T_FRIEND
%token T_NEW T_DELETE T_OPERATOR
%token T_TRY T_CATCH T_THROW
%token T_INLINE

/* identifier */
%token T_ID
/* one char */
%token T_CHR
/* any number constant */
%token T_NUMBER

%%

input:  statement
		| input statement
;

statement:
		  '#'                { cparse->Mode = C_CPP; }
		  cpp                { cparse->Mode = C_STANDARD; }
		| '\n'               { add_cexpr(cb,cparse,C_STANDARD,LNF_LF); }
		| simple_type_name   { add_cexpr(cb,cparse,C_TYPES,0);   }
		| storage_class      { add_cexpr(cb,cparse,C_STORAGE,0); }
		| keywords
		| textlist
;

cpp:
		  cppline     '\n'         { add_cexpr(cb,cparse,C_STANDARD,LNF_LF); }
;

cppline:
		  cppkeyword               { add_cexpr(cb,cparse,C_CPP,0); cparse->Mode = C_CPPTEXTLIST; }
		  cpptextlist
		| define
;

cppkeyword:
		  T_INCLUDE
		| T_UNDEF
		| T_PRAGMA
		| T_LINE
		| T_ERROR
		| T_IF
		| T_IFDEF
		| T_IFNDEF
		| T_ELIF
		| T_ENDIF
		| T_ELSE
;

cpptextlist:
		| textlist
;

define:
		  T_DEFINE                 { add_cexpr(cb,cparse,C_CPP,0); cparse->Mode = C_CPPTEXTLIST; }
		  T_ID                     { cparse->TxtPtr = cparse->ActPtr;  }
		  definelist
;

definelist:
		| definetextlist
;

definetextlist:
		  definetext                { if(cparse->XOffsetAdd) add_cexpr(cb,cparse,C_STANDARD,0);
												cparse->TxtPtr = cparse->ActPtr;   }
		| definetextlist definetext { if(cparse->XOffsetAdd) add_cexpr(cb,cparse,C_STANDARD,0);
												cparse->TxtPtr = cparse->ActPtr;   }
;

definetext:
		  T_ID
		| T_CHR
		| T_NUMBER                 { add_cexpr(cb,cparse,C_NUMBER,0);   }
		| '"' string '"'           { add_cexpr(cb,cparse,C_STRING,0);   }
		| '\'' char '\''           { add_cexpr(cb,cparse,C_STRING,0);   }
		| '#'                      { cparse->TxtPtr = cparse->ActPtr;   }
		| '\\' '\n'                { add_cexpr(cb,cparse,C_STANDARD,LNF_LF); }
		  definelist
;



keywords:
		  keywordsincomplete       { add_cexpr(cb,cparse,C_KEYWORD,0); }
		| keywordscomplete

keywordsincomplete:
		  T_CONST
		| T_VOLATILE
		| T_SWITCH
		| T_CASE
		| T_BREAK
		| T_IF
		| T_ELSE
		| T_FOR
		| T_DO
		| T_WHILE
		| T_CONTINUE
		| T_RETURN
		| T_DEFAULT
		| T_GOTO
		| T_TYPEDEF
		| T_SIZEOF
		| T_PUBLIC
		| T_PRIVATE
		| T_PROTECTED
		| T_VIRTUAL
		| T_FRIEND
		| T_NEW
		| T_DELETE
		| T_OPERATOR
		| T_TRY
		| T_CATCH
		| T_THROW
		| T_INLINE
;

keywordscomplete:
		  elaborated_type_specifier
;

elaborated_type_specifier:
		  class_key                { add_cexpr(cb,cparse,C_KEYWORD,0); }
		  elaborated_type_specifier_name
		| T_ENUM                   { add_cexpr(cb,cparse,C_KEYWORD,0); }
		  elaborated_type_specifier_name
;

class_key:
		  T_STRUCT
		| T_UNION
		| T_CLASS
;

elaborated_type_specifier_name:
		| T_ID                     { add_cexpr(cb,cparse,C_TYPENAME,0); }
;

simple_type_name:
		  T_CHAR
		| T_SHORT
		| T_INT
		| T_LONG
		| T_SIGNED
		| T_UNSIGNED
		| T_FLOAT
		| T_DOUBLE
		| T_VOID
;

storage_class:
		  T_AUTO
		| T_EXTERN
		| T_STATIC
		| T_REGISTER
;

textlist:
		  text                     { if(cparse->XOffsetAdd) add_cexpr(cb,cparse,C_STANDARD,0);
											  cparse->TxtPtr = cparse->ActPtr; }
		| textlist text            { if(cparse->XOffsetAdd) add_cexpr(cb,cparse,C_STANDARD,0);
											  cparse->TxtPtr = cparse->ActPtr; }
;

text:
		  T_ID                     {  }
		| T_CHR
		| T_NUMBER                 { add_cexpr(cb,cparse,C_NUMBER,0); }
		| '\''
		  char
		  '\''                     { add_cexpr(cb,cparse,C_STRING,0); }
		| '"'                      { cparse->ActPtr--;add_cexpr(cb,cparse,C_STANDARD,0);
											  cparse->ActPtr++;cparse->Mode = C_STRING;         }
		  string
		  '"'                      { add_cexpr(cb,cparse,C_STRING,0);
											  cparse->Mode = C_STANDARD; }
;

char:
		| T_ID
		| T_CHR
		| T_NUMBER
		| '"'
		| '#'
		| '\\' stringescaped
;

string:
		  stringtoken
		| string stringtoken
;

stringtoken:
		| T_ID
		| T_CHR
		| T_NUMBER
		| '\''
		| '#'
		| '\\' stringescaped
;

stringescaped:
		  T_ID
		| T_CHR
		| T_NUMBER
		| '"'
		| '\\'
		| '\''
;

%%

struct Keyword
{
	STRPTR k_Word;
	int k_ID;
	int k_Len;
};

struct Keyword keywords[] =
{
	{"auto"     ,T_AUTO      ,4},
	{"break"    ,T_BREAK     ,5},
	{"case"     ,T_CASE      ,4},
	{"catch"    ,T_CATCH     ,5},
	{"char"     ,T_CHAR      ,4},
	{"class"    ,T_CLASS     ,5},
	{"const"    ,T_CONST     ,5},
	{"continue" ,T_CONTINUE  ,8},
	{"default"  ,T_DEFAULT   ,7},
	{"delete"   ,T_DELETE    ,6},
	{"do"       ,T_DO        ,2},
	{"double"   ,T_DOUBLE    ,6},
	{"enum"     ,T_ENUM      ,4},
	{"else"     ,T_ELSE      ,4},
	{"extern"   ,T_EXTERN    ,6},
	{"float"    ,T_FLOAT     ,5},
	{"for"      ,T_FOR       ,3},
	{"friend"   ,T_FRIEND    ,6},
	{"goto"     ,T_GOTO      ,4},
	{"if"       ,T_IF        ,2},
	{"inline"   ,T_INLINE    ,6},
	{"int"      ,T_INT       ,3},
	{"long"     ,T_LONG      ,4},
	{"new"      ,T_NEW       ,3},
	{"operator" ,T_OPERATOR  ,8},
	{"private"  ,T_PRIVATE   ,7},
	{"protected",T_PROTECTED ,9},
	{"public"   ,T_PUBLIC    ,6},
	{"register" ,T_REGISTER  ,8},
	{"return"   ,T_RETURN    ,6},
	{"short"    ,T_SHORT     ,5},
	{"signed"   ,T_SIGNED    ,6},
	{"sizeof"   ,T_SIZEOF    ,6},
	{"static"   ,T_STATIC    ,6},
	{"struct"   ,T_STRUCT    ,6},
	{"switch"   ,T_SWITCH    ,6},
	{"throw"    ,T_THROW     ,5},
	{"try"      ,T_TRY       ,3},
	{"typedef"  ,T_TYPEDEF   ,7},
	{"union"    ,T_UNION     ,5},
	{"unsigned" ,T_UNSIGNED  ,8},
	{"virtual"  ,T_VIRTUAL   ,7},
	{"void"     ,T_VOID      ,4},
	{"volatile" ,T_VOLATILE  ,8},
	{"while"    ,T_WHILE     ,5},
	{NULL       ,0}
};

struct Keyword cppkeywords[] =
{
	{"define"   ,T_DEFINE    ,6},
	{"elif"     ,T_ELIF      ,4},
	{"else"     ,T_ELSE      ,4},
	{"endif"    ,T_ENDIF     ,5},
	{"error"    ,T_ERROR     ,5},
	{"if"       ,T_IF        ,2},
	{"ifdef"    ,T_IFDEF     ,5},
	{"ifndef"   ,T_IFNDEF    ,6},
	{"include"  ,T_INCLUDE   ,7},
	{"line"     ,T_LINE      ,4},
	{"pragma"   ,T_PRAGMA    ,6},
	{"undef"    ,T_UNDEF     ,5},
	{NULL       ,0}
};

int cdtparse_lex(int *lvalp,struct ClassBase *cb,struct CParse *cparse)
{
	STRPTR ptr = cparse->ActPtr;
	STRPTR end = cparse->EndPtr;
	STRPTR name;
	ULONG tablen = cparse->Data->cd_TabLength;
	ULONG addx,chrs;
	struct Keyword *kw = NULL;
	int retval = 0;
	int len;

	if(ptr < end)
	{
		do
		{
			chrs = cparse->Chars;
			while(ptr < end && (*ptr == ' ' || *ptr == '\t'))
			{
				if(*ptr == '\t')
				{
					addx = (tablen - (cparse->Chars % tablen));

					if(chrs)
						cparse->XOffsetAdd += addx * cparse->TabWidth;
					else
						cparse->XOffset    += addx * cparse->TabWidth;
					cparse->Chars      += addx;
				} else
					cparse->Chars++;

				ptr++;
			}

			/* handle comments */
			if(*ptr == '/' && ptr < end - 1 && (ptr[1] == '*' || ptr[1] == '/') && cparse->Mode != C_STRING)
			{
				ptr += 2;
				if(ptr[-1] == '*')
				{
					int nested = 1;

					while(ptr < end && nested)
					{
						switch(*ptr)
						{
						case '*':
							if(ptr[1] == '/')
							{
								nested--;
								ptr++;
							}
							break;
						case '/':
							if(ptr[1] == '*')
							{
								nested++;
								ptr++;
							}
							break;
						case '\n':
							cparse->Chars = 0;
							cparse->ActPtr = ptr + 1;
							add_cexpr(cb,cparse,C_COMMENT,LNF_LF);
							break;
						case '\t':
							cparse->ActPtr = ptr;
							add_cexpr(cb,cparse,C_COMMENT,0);
							cparse->Chars--;
							chrs = cparse->Chars;
							while(*ptr == '\t')
							{
								addx = (tablen - (cparse->Chars % tablen));

								if(chrs)
									cparse->XOffsetAdd += addx * cparse->TabWidth;
								else
									cparse->XOffset    += addx * cparse->TabWidth;
								cparse->Chars      += addx;
								ptr++;
							}
							ptr--;
							cparse->Chars++;
							break;
						}
						cparse->Chars++;
						ptr++;
					}
					cparse->ActPtr = ptr;
					add_cexpr(cb,cparse,C_COMMENT,0);
				} else
				{
					while(ptr < end && *ptr != '\n')
					{
						if(*ptr == '\t')
						{
							cparse->ActPtr = ptr;
							add_cexpr(cb,cparse,C_COMMENT,0);
							chrs = cparse->Chars;
							while(*ptr == '\t')
							{
								addx = (tablen - (cparse->Chars % tablen));

								if(chrs)
									cparse->XOffsetAdd += addx * cparse->TabWidth;
								else
									cparse->XOffset    += addx * cparse->TabWidth;
								cparse->Chars      += addx;
								ptr++;
							}
						} else
							cparse->Chars++;
						ptr++;
					}
					ptr++;
					cparse->ActPtr = ptr;

					add_cexpr(cb,cparse,C_COMMENT,LNF_LF);
				}
			} else
				break;

		} while(*ptr == ' ' || *ptr == '\t' || *ptr == '/');

		if(ptr < end)
		{
			if(isalpha(*ptr) || *ptr == '_')
			{
				name = ptr;

				while(ptr < end && (isalpha(*ptr) || isdigit(*ptr) || *ptr == '_'))
					ptr++;

				len  = (ptr - name);

				ptr--;

				retval = T_ID;

				/* only search for keyword if it is normal text or a cppline !!! */
				if(cparse->Mode == C_CPP)
					kw = cppkeywords;
				else if(cparse->Mode == C_STANDARD)
					kw = keywords;

				if(len > 1 && kw)
				{
					int cmp;

					while(kw->k_Word)
					{
						if(!(cmp = strncmp(kw->k_Word,name,len)))
						{
							DB(("len = %ld , strlen(kw) = %ld\n",len,kw->k_Len));
							if(len == kw->k_Len)
							{
								retval = kw->k_ID;
								break;
							}
						} else if(cmp > 0)
							break;
						kw++;
					}
				}
				cparse->Chars += len;
				cparse->ActPtr = ptr + 1;
			} else if(isdigit(*ptr))
			{
				name = ptr;
				ptr++;

				if(*ptr == 'x')
				{
					ptr++;
					while(ptr < end && isxdigit(*ptr))
						ptr++;
				} else
				{
					while(ptr < end && isdigit(*ptr))
						ptr++;
				}

				ptr--;
				cparse->Chars += (ptr - name);
				cparse->ActPtr = ptr + 1;

				retval = T_NUMBER;
			} else
			{
				retval = T_CHR;

				cparse->Chars++;
				switch(*ptr)
				{
				case '\n':
					cparse->Chars = 0;
				case '"':
				case '#':
				case '\\':
				case '\'':
					retval = *ptr;
					break;
				}
				cparse->ActPtr = ptr + 1;
			}
		}
	}

	DB(("token : %ld\n",retval));
	cparse->Token = retval;

	return(retval);
}

void *cdtparse_alloc(struct ClassBase *cb,struct CParse *cparse,int size)
{
	struct cdtMem *mem;

	if((mem = AllocPooled(cparse->Data->cd_Pool,size + sizeof(struct cdtMem))))
	{
		mem->Next = cparse->Memory;
		mem->Size = size + sizeof(struct cdtMem);

		mem++;
	}
	return(mem);
}

void cdtparse_free(struct ClassBase *cb,struct CParse *cparse)
{
	struct cdtMem *mem = cparse->Memory;
	struct cdtMem *next;

	while(mem)
	{
		next = mem->Next;
		mem--;
		FreePooled(cparse->Data->cd_Pool,mem,mem->Size);
		mem = next;
	}
}

int cdtparse_error(char *str)
{
	DB(("error : %s\n",str));
	return(0);
}

void cdtparse_parseerror(struct ClassBase *cb,struct CParse *cparse)
{
	char *buf;

	if((buf = AllocPooled(cparse->Data->cd_Pool,50)))
	{
		STRPTR seg = cparse->SegPtr;
		STRPTR act = cparse->ActPtr;

		sprintf(buf,"Parse Error : last Token %4ld for line :",cparse->Token);
		cparse->SegPtr = buf;
		cparse->ActPtr = buf + strlen(buf);
		add_line(cb,cparse,C_STANDARD,LNF_LF);

		while(seg > cparse->BegPtr && *seg != '\n')
			seg--;

		if(*seg == '\n')
			seg++;

		while(act < cparse->EndPtr && *act != '\n')
			act++;
		cparse->SegPtr = seg;
		cparse->ActPtr = act;
		add_line(cb,cparse,C_STANDARD,LNF_LF);
	}
}

