/* ObjC-2.0 scanner - based on http://www.lysator.liu.se/c/ANSI-C-grammar-l.html */
/* part of objc2pp - an obj-c 2 preprocessor */

	/* digit */
D			[0-9]
	/* letter */
L			[a-zA-Z_$]
	/* hex letter */
H			[a-fA-F0-9]
	/* exponent */
E			[Ee][+-]?{D}+
	/* float suffix */
FS			(f|F|l|L)
	/* int suffix */
IS			(u|U|l|L)*

%{
#include <stdio.h>
#include "y.tab.h"
#include "node.h"
	
#define error(X)
	
void charCount(void);
void comment(int cxx);

int column = 0;
int line = 1;	
	
struct RESERVED
	{
		char *word;
		int token;
	} reserved[]={
	"auto", AUTO, 
	"break", BREAK, 
	"case", CASE, 
	"char", CHAR, 
	"const", CONST, 
	"continue", CONTINUE, 
	"default", DEFAULT, 
	"do", DO, 
	"double", DOUBLE, 
	"else", ELSE, 
	"enum", ENUM, 
	"extern", EXTERN, 
	"float", FLOAT, 
	"for", FOR, 
	"goto", GOTO, 
	"if", IF, 
	"int", INT, 
	"long", LONG, 
	"register", REGISTER, 
	"return", RETURN, 
	"short", SHORT, 
	"signed", SIGNED, 
	"sizeof", SIZEOF, 
	"static", STATIC, 
	"struct", STRUCT, 
	"switch", SWITCH, 
	"typedef", TYPEDEF, 
	"union", UNION, 
	"unsigned", UNSIGNED, 
	"void", VOID, 
	"volatile", VOLATILE, 
	"while", WHILE, 

		/* operators - only used for reverse lookup since they are not keywords */
		
		"...", ELLIPSIS,
		">>=", RIGHT_ASSIGN,
		"<<=", LEFT_ASSIGN,
		"+=", ADD_ASSIGN,
		"-=", SUB_ASSIGN,
		"*=", MUL_ASSIGN,
		"/=", DIV_ASSIGN,
		"%=", MOD_ASSIGN,
		"&=", AND_ASSIGN,
		"^=", XOR_ASSIGN,
		"|=", OR_ASSIGN,
		">>", RIGHT_OP,
		"<<", LEFT_OP,
		"++", INC_OP,
		"--", DEC_OP,
		"->", PTR_OP,
		"&&", AND_OP,
		"||", OR_OP,
		"<=", LE_OP,
		">=", GE_OP,
		"==", EQ_OP,
		"!=", NE_OP,
		
	/* Obj-C 1.0 extensions */
	
	"@interface", AT_INTERFACE, 
	"@implementation", AT_IMPLEMENTATION, 
	"@end", AT_END, 
	"@class", AT_CLASS, 
	"@protocol", AT_PROTOCOL, 
	"@defs", AT_DEFS, 
	"@selector", AT_SELECTOR, 
	"@encode", AT_ENCODE, 
	"@protected", AT_PROTECTED, 
	"@private", AT_PRIVATE, 
	"@public", AT_PUBLIC,
		
	// FIXME: these are not really keywords!
	// it is 100% legal to write 'int id' or 'NSString *Class;'
	// they behave like preinitialized global typedefs that can be overwritten.
	// but aren't. They are more like can-be-typedefs.
	// I.e. typedef int SEL; int SEL; gives an error on the second definition.
	// or typedef BOOL SEL; SEL x; defines x to be a BOOL
	// or 	int x, BOOL; BOOL *y; compiles fine
		
	"inout", INOUT, 
	"in", IN, 
	"out", OUT, 
	"byref", BYREF, 
	"bycopy", BYCOPY, 
	"oneway", ONEWAY, 
	
	"id", ID, 
	"SEL", SELECTOR, 
	"BOOL", BOOLTYPE, 
	"unichar", UNICHAR, 
	"Class", CLASS, 
	
	/* Obj-C 1.5 extensions */
	
	"@property", AT_PROPERTY, 
	"@synthesize", AT_SYNTHESIZE, 
	"@optional", AT_OPTIONAL, 
	"@required", AT_REQUIRED, 
		// these two are real keywords and only allowed in type specifiers
	"__weak", WEAK, 
	"__strong", STRONG,
		
	/* Obj-C 2.0 extensions */
		
	"@try", AT_TRY, 
	"@catch", AT_CATCH, 
	"@finally", AT_FINALLY, 
	"@throw", AT_TRY, 
	"@synchronized", AT_SYNCHRONIZED, 
		
	/* Obj-C 2.5 extensions */
		
	"@autoreleasepool", AT_AUTORELEASEPOOL,
	"__unsafe_unretained", AT_UNSAFE_UNRETAINED,
	"__autoreleasing", AT_AUTORELEASING,
	};
	
	// FIXME: should the scanner really handle the scope stack or is this part of the grammar which can handle separate symbol tables, e.g. for stuct components
	// depends a little on context depencence: is it allowed to (re)define a struct component with the same name as a typedef? Or global function?
	
int scopestack;	/* global and local scope stack */
int keywordtab;	/* keyword table */
int softkeywordtab;	/* soft keyword table */

void pushscope()
{ // start a new local variable scope
	push(scopestack, dictionary());	// create symbol table
}
	
void popscope()
{ // pop scope
	pop(scopestack);
}	

int globals()
{
	return first(scopestack);
}

int makeglobal(int identifier)
{ // make this a global entry 
	
}

void scaninit(void)
	{
	int i, n;
	softkeywordtab=dictionary();
	n=lookup(softkeywordtab, "id", "keyword", ID);
	n=lookup(softkeywordtab, "SEL", "keyword", SELECTOR);
	n=lookup(softkeywordtab, "BOOL", "keyword", BOOLTYPE);
	n=lookup(softkeywordtab, "Class", "keyword", CLASS);
	n=lookup(softkeywordtab, "unichar", "keyword", UNICHAR);
	keywordtab=dictionary();
	for(i=0; i<sizeof(reserved)/sizeof(reserved[0]); i++)
		{ // initialize all reserved words in symbol table
			if(lookup(softkeywordtab, reserved[i].word, NULL, 0))
				continue;
			n=lookup(keywordtab, reserved[i].word, "keyword", reserved[i].token);
		}
	pushscope();	// create initially empty global symbol table
	}

	/* FIXME: read from an NSInputStream
// how can we easily set up an input stream for "stdin"?

#define YY_INPUT(buf,result,max_size) \
{ \
	 result = [stream read:(uint8_t *)(buf) maxLength:(may_size)];
}
	*/

%}

%%

"/*"			{ comment(0); }
"//"			{ comment(1); }
"# [0-9]* \".*\" [0-9]*" { /* handle */ }	/* try to ignore line markers generated by preprocessor */

	/* identifiers and keywords */

{L}({L}|{D})*  { 
	/*
	 * symbol table entries are nodes with type "identifier"
	 * value is the identifier name
	 * left is the storage class (leaf node e.g. "static", "extern", "auto", "Class", "typedef")
	 * right is the data type
	 */
	int s;
	int n;
	charCount();
	if(!nokeyword)
		{ // check for keywords
		s=lookup(keywordtab, yytext, NULL, 0);
		if(s)
			return value(s);	// keyword found
		if(objctype)
			{ // check for soft keywords
			objctype=0;
			s=lookup(softkeywordtab, yytext, NULL, 0);
			if(s)
				return value(s);	// soft keyword found
			}
		}
	nokeyword=0;
	n=count(scopestack);
	while(n-- >= 0)
		{ // loop through scope stack from end (most locally) to global - but add to deepest level only
			s=lookup(nth(scopestack, n), yytext, NULL, 0);
			if(s)
				break;
		}
	if(!s)
		{ // add new identifier (without type) to most local level
			s=lookup(last(scopestack), yytext, "identifier", 0);
			setStringValue(s, yytext);	// set symbol name
		}
	yylval=s;
	if(strcmp(type(left(s)), "typedef") == 0)
		return TYPE_NAME;	/* in current scope this identifier is a typedef'd type name */
	if(strcmp(type(left(s)), "Class") == 0)
		return TYPE_NAME;	/* in current scope this identifier is a Class name */
	return IDENTIFIER;
} 

	/* numeral CONSTANTs */
0[xX]{H}+{IS}?		{ charCount(); yylval=cons(yytext); return(CONSTANT); }
0{D}+{IS}?			{ charCount(); yylval=cons(yytext); return(CONSTANT); }
{D}+{IS}?			{ charCount(); yylval=cons(yytext); return(CONSTANT); }
L?'(\\.|[^\\'])+'	{ charCount(); yylval=cons(yytext); return(CONSTANT); }

{D}+{E}{FS}?		{ charCount(); yylval=cons(yytext); return(CONSTANT); }
{D}*"."{D}+({E})?{FS}?	{ charCount(); yylval=cons(yytext); return(CONSTANT); }
{D}+"."{D}*({E})?{FS}?	{ charCount(); yylval=cons(yytext); return(CONSTANT); }

	/* string CONSTANTs */

L?\"(\\.|[^\\"])*\"	{ charCount(); yylval=cons(yytext); return(STRING_LITERAL); }
@\"(\\.|[^\\"])*\"			{ charCount(); yylval=cons(yytext); return(AT_STRING_LITERAL); }

	/* standard operators */

"..."			{ charCount(); return(ELLIPSIS); }
">>="			{ charCount(); return(RIGHT_ASSIGN); }
"<<="			{ charCount(); return(LEFT_ASSIGN); }
"+="			{ charCount(); return(ADD_ASSIGN); }
"-="			{ charCount(); return(SUB_ASSIGN); }
"*="			{ charCount(); return(MUL_ASSIGN); }
"/="			{ charCount(); return(DIV_ASSIGN); }
"%="			{ charCount(); return(MOD_ASSIGN); }
"&="			{ charCount(); return(AND_ASSIGN); }
"^="			{ charCount(); return(XOR_ASSIGN); }
"|="			{ charCount(); return(OR_ASSIGN); }
">>"			{ charCount(); return(RIGHT_OP); }
"<<"			{ charCount(); return(LEFT_OP); }
"++"			{ charCount(); return(INC_OP); }
"--"			{ charCount(); return(DEC_OP); }
"->"			{ charCount(); return(PTR_OP); }
"&&"			{ charCount(); return(AND_OP); }
"||"			{ charCount(); return(OR_OP); }
"<="			{ charCount(); return(LE_OP); }
">="			{ charCount(); return(GE_OP); }
"=="			{ charCount(); return(EQ_OP); }
"!="			{ charCount(); return(NE_OP); }
";"				{ charCount(); return(';'); }
("{"|"<%")		{ charCount(); return('{'); }
("}"|"%>")		{ charCount(); return('}'); }
","				{ charCount(); return(','); }
":"				{ charCount(); return(':'); }
"="				{ charCount(); return('='); }
"("				{ charCount(); return('('); }
")"				{ charCount(); return(')'); }
("["|"<:")		{ charCount(); return('['); }
("]"|":>")		{ charCount(); return(']'); }
"."				{ charCount(); return('.'); }
"&"				{ charCount(); return('&'); }
"!"				{ charCount(); return('!'); }
"~"				{ charCount(); return('~'); }
"-"				{ charCount(); return('-'); }
"+"				{ charCount(); return('+'); }
"*"				{ charCount(); return('*'); }
"/"				{ charCount(); return('/'); }
"%"				{ charCount(); return('%'); }
"<"				{ charCount(); return('<'); }
">"				{ charCount(); return('>'); }
"^"				{ charCount(); return('^'); }
"|"				{ charCount(); return('|'); }
"?"				{ charCount(); return('?'); }
 
[\n]			{ line++, column=0; }
[ \t\v\f]		{ charCount(); }
 
.				{ charCount(); error( "bad character" ); }

%%
 
 int nokeyword, objctype;

 int yywrap()
 {
	 return(1);
 }
 
 void comment(int cxx)
 {
	 char c;
	if(cxx)
		{ // C++ comment
		while ((c = input()) != '\n' && c != EOF)
			/*putchar(c)*/;
		line++;
		return;
		}
	 while ( 1 )
		 {
		 while ( (c = input()) != '*' && c != EOF )
			 { /* eat up text of comment */
			 if(c == '\n')
				 line++;
			 }
		 if ( c == '*' )
			 {
			 while ( (c = input()) == '*' )
				 ;
			 if ( c == '/' )
				 break;    /* found the end */
			 }
		 
		 if ( c == EOF )
			 {
			 error( "EOF in comment" );
			 break;
			 }
		 }
 }
 
 void charCount(void)
 { // cound characters so that we know the column position
	 int i;
	 
	 for (i = 0; yytext[i] != '\0'; i++)
		 if (yytext[i] == '\n')
			 column = 0;
		 else if (yytext[i] == '\t')
			 column += 8 - (column % 8);
		 else
			 column++;
	 
//	 ECHO;
 }

int cons(char *str)
{
	return leaf("constant", str);
}
