context: /*
  ** $Id: llex.c,v 2.20.1.1 2007/12/27 13:02:25 roberto Exp $
  ** Lexical Analyzer
  ** See Copyright Notice in lua.h
  */
  
  using System;
  using System.Collections.Generic;
  using System.Text;
  using System.Runtime.InteropServices;
  using System.Diagnostics;
  
  namespace KopiLua
  {
  	using TValue = Lua.LuaTypeValue;
  	using LuaNumberType = System.Double;
  	using ZIO = Lua.Zio;
  
  	public partial class Lua
  	{
  		public const int FIRSTRESERVED	= 257;
  
  		/* maximum length of a reserved word */
  		public const int TOKENLEN	= 9; // "function"
  
  
  		/*
  		* WARNING: if you change the order of this enumeration,
  		* grep "ORDER RESERVED"
  		*/
  		public enum RESERVED {
  		  /* terminal symbols denoted by reserved words */
  		  TK_AND = FIRSTRESERVED, TK_BREAK,
  		  TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
  		  TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
  		  TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
  		  /* other terminal symbols */
  		  TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER,
  		  TK_NAME, TK_STRING, TK_EOS
  		};
  
  		/* number of reserved words */
  		public const int NUMRESERVED = (int)RESERVED.TK_WHILE - FIRSTRESERVED + 1;
  
  		public class SemInfo {
  			public SemInfo() { }
  			public SemInfo(SemInfo copy)
  			{
  				this.r = copy.r;
  				this.ts = copy.ts;
  			}
  			public LuaNumberType r;
  			public TString ts;
  		} ;  /* semantics information */
  
  		public class Token {
  			public Token() { }
  			public Token(Token copy)
  			{
  				this.token = copy.token;
  				this.seminfo = new SemInfo(copy.seminfo);
  			}
  			public int token;
  			public SemInfo seminfo = new SemInfo();
  		};
  
  
  		public class LexState {
  			public int current;  /* current character (charint) */
  			public int linenumber;  /* input line counter */
  			public int lastline;  /* line of last token `consumed' */
  			public Token t = new Token();  /* current token */
  			public Token lookahead = new Token();  /* look ahead token */
  			public FuncState fs;  /* `FuncState' is private to the parser */
  			public LuaState L;
  			public ZIO z;  /* input stream */
  			public Mbuffer buff;  /* buffer for tokens */
  			public TString source;  /* current source name */
  			public char decpoint;  /* locale decimal point */
  		};
  
  
  		public static void Next(LexState ls) { ls.current = zgetc(ls.z); }
  
  
  		public static bool CurrIsNewline(LexState ls) { return (ls.current == '\n' || ls.current == '\r'); }
  
  
  		/* ORDER RESERVED */
  		public static readonly string[] LuaXTokens = {
  			"and", "break", "do", "else", "elseif",
  			"end", "false", "for", "function", "if",
  			"in", "local", "nil", "not", "or", "repeat",
  			"return", "then", "true", "until", "while",
  			"..", "...", "==", ">=", "<=", "~=",
  			"<number>", "<name>", "<string>", "<eof>"
  		};
  
  
  		public static void SaveAndNext(LexState ls) {Save(ls, ls.current); Next(ls);}
  
  		private static void Save (LexState ls, int c) {
  		  Mbuffer b = ls.buff;
  		  if (b.n + 1 > b.buffsize) {
  			uint newsize;
  			if (b.buffsize >= MAXSIZET/2)
  			  LuaXLexError(ls, "lexical element too long", 0);
  			newsize = b.buffsize * 2;
  			luaZ_resizebuffer(ls.L, b, (int)newsize);
  		  }
  		  b.buffer[b.n++] = (char)c;
  		}
  
  		
  		public static void LuaXInit (LuaState L) {
  		  int i;
  		  for (i=0; i<NUMRESERVED; i++) {
  			TString ts = luaS_new(L, LuaXTokens[i]);
  			luaS_fix(ts);  /* reserved words are never collected */
  			LuaAssert(LuaXTokens[i].Length+1 <= TOKENLEN);
  			ts.tsv.reserved = CastByte(i+1);  /* reserved word */
  		  }
  		}
  
  
  		public const int MAXSRC          = 80;
  
  
  		public static CharPtr LuaXTokenToString (LexState ls, int token) {
  		  if (token < FIRSTRESERVED) {
  			LuaAssert(token == (byte)token);
  			return (iscntrl(token)) ? LuaOPushFString(ls.L, "char(%d)", token) :
  									  LuaOPushFString(ls.L, "%c", token);
  		  }
  		  else
  			return LuaXTokens[(int)token-FIRSTRESERVED];
  		}
  
  
  		public static CharPtr TextToken (LexState ls, int token) {
  		  switch (token) {
  			case (int)RESERVED.TK_NAME:
  			case (int)RESERVED.TK_STRING:
  			case (int)RESERVED.TK_NUMBER:
  			  Save(ls, '\0');
  			  return luaZ_buffer(ls.buff);
  			default:
  			  return LuaXTokenToString(ls, token);
  		  }
  		}
  
  		public static void LuaXLexError (LexState ls, CharPtr msg, int token) {
  		  CharPtr buff = new char[MAXSRC];
  		  LuaOChunkID(buff, GetStr(ls.source), MAXSRC);
  		  msg = LuaOPushFString(ls.L, "%s:%d: %s", buff, ls.linenumber, msg);
  		  if (token != 0)
  			LuaOPushFString(ls.L, "%s near " + LUA_QS, msg, TextToken(ls, token));
  		  LuaDThrow(ls.L, LUA_ERRSYNTAX);
  		}
  
  		public static void LuaXSyntaxError (LexState ls, CharPtr msg) {
  		  LuaXLexError(ls, msg, ls.t.token);
  		}
  
  		[CLSCompliantAttribute(false)]
  		public static TString LuaXNewString(LexState ls, CharPtr str, uint l)
  		{
  		  LuaState L = ls.L;
  		  TString ts = luaS_newlstr(L, str, l);
  		  TValue o = luaH_setstr(L, ls.fs.h, ts);  /* entry for `str' */
  		  if (TTIsNil (o)) {
  				SetBValue (o, 1);  /* make sure `str' will not be collected */
  				LuaCCheckGC(L);
  		  }
  		  return ts;
  		}
  
  
  		private static void IncLineNumber (LexState ls) {
  		  int old = ls.current;
  		  LuaAssert(CurrIsNewline(ls));
  		  Next(ls);  /* skip `\n' or `\r' */
  		  if (CurrIsNewline(ls) && ls.current != old)
  			Next(ls);  /* skip `\n\r' or `\r\n' */
  		  if (++ls.linenumber >= MAXINT)
  			LuaXSyntaxError(ls, "chunk has too many lines");
  		}
  
  
  		public static void LuaXSetInput (LuaState L, LexState ls, ZIO z, TString source) {
  		  ls.decpoint = '.';
  		  ls.L = L;
  		  ls.lookahead.token = (int)RESERVED.TK_EOS;  /* no look-ahead token */
  		  ls.z = z;
  		  ls.fs = null;
  		  ls.linenumber = 1;
  		  ls.lastline = 1;
  		  ls.source = source;
  		  luaZ_resizebuffer(ls.L, ls.buff, LUAMINBUFFER);  /* initialize buffer */
  		  Next(ls);  /* read first char */
  		}
  
  
  
  		/*
  		** =======================================================
  		** LEXICAL ANALYZER
  		** =======================================================
  		*/
  
  
  		private static int CheckNext (LexState ls, CharPtr set) {
  		  if (strchr(set, (char)ls.current) == null)
  			return 0;
  		  SaveAndNext(ls);
  		  return 1;
  		}
  
  
  		private static void BufferReplace (LexState ls, char from, char to) {
  		  uint n = luaZ_bufflen(ls.buff);
  		  CharPtr p = luaZ_buffer(ls.buff);
  		  while ((n--) != 0)
  			  if (p[n] == from) p[n] = to;
  		}
  
  
  		private static void TryDecPoint (LexState ls, SemInfo seminfo) {
  		  /* format error: try to update decimal point separator */
  			// todo: add proper support for localeconv - mjf
  			//lconv cv = localeconv();
  			char old = ls.decpoint;
  			ls.decpoint = '.'; // (cv ? cv.decimal_point[0] : '.');
  			BufferReplace(ls, old, ls.decpoint);  /* try updated decimal separator */
  			if (LuaOStr2d(luaZ_buffer(ls.buff), out seminfo.r) == 0)
  			{
  				/* format error with correct decimal point: no more options */
  				BufferReplace(ls, ls.decpoint, '.');  /* undo change (for error message) */
  				LuaXLexError(ls, "malformed number", (int)RESERVED.TK_NUMBER);
  			}
  		}
  
  
  		/* LUA_NUMBER */
  		private static void ReadNumeral (LexState ls, SemInfo seminfo) {
  		  LuaAssert(isdigit(ls.current));
  		  do {
  			SaveAndNext(ls);
  		  } while (isdigit(ls.current) || ls.current == '.');
  		  if (CheckNext(ls, "Ee") != 0)  /* `E'? */
  			CheckNext(ls, "+-");  /* optional exponent sign */
  		  while (isalnum(ls.current) || ls.current == '_')
  			SaveAndNext(ls);
  		  Save(ls, '\0');
  		  BufferReplace(ls, '.', ls.decpoint);  /* follow locale for decimal point */
  		  if (LuaOStr2d(luaZ_buffer(ls.buff), out seminfo.r) == 0)  /* format error? */
  			TryDecPoint(ls, seminfo); /* try to update decimal point separator */
  		}
  
  
  		private static int SkipSep (LexState ls) {
  		  int count = 0;
  		  int s = ls.current;
  		  LuaAssert(s == '[' || s == ']');
  		  SaveAndNext(ls);
  		  while (ls.current == '=') {
  			SaveAndNext(ls);
  			count++;
  		  }
  		  return (ls.current == s) ? count : (-count) - 1;
  		}
  
  
  		private static void ReadLongString (LexState ls, SemInfo seminfo, int sep) {
  		  //int cont = 0;
  		  //(void)(cont);  /* avoid warnings when `cont' is not used */
  		  SaveAndNext(ls);  /* skip 2nd `[' */
  		  if (CurrIsNewline(ls))  /* string starts with a newline? */
  			IncLineNumber(ls);  /* skip it */
  		  for (;;) {
  			switch (ls.current) {
  			  case EOZ:
  				LuaXLexError(ls, (seminfo != null) ? "unfinished long string" :
  										   "unfinished long comment", (int)RESERVED.TK_EOS);
  				break;  /* to avoid warnings */
  		#if LUA_COMPAT_LSTR
  			  case '[': {
  				if (skip_sep(ls) == sep) {
  				  save_and_next(ls);  /* skip 2nd `[' */
  				  cont++;
  		#if LUA_COMPAT_LSTR
  				  if (sep == 0)
  					luaX_lexerror(ls, "nesting of [[...]] is deprecated", '[');
  		#endif
  				}
  				break;
  			  }
  		#endif
  			  case ']':
  				if (SkipSep(ls) == sep)
  				{
  				  SaveAndNext(ls);  /* skip 2nd `]' */
  		//#if defined(LUA_COMPAT_LSTR) && LUA_COMPAT_LSTR == 2
  		//          cont--;
  		//          if (sep == 0 && cont >= 0) break;
  		//#endif
  				  goto endloop;
  				}
  			  break;
  			  case '\n':
  			  case '\r':
  				Save(ls, '\n');
  				IncLineNumber(ls);
  				if (seminfo == null) luaZ_resetbuffer(ls.buff);  /* avoid wasting space */
  				break;
  			  default: {
  				if (seminfo != null) SaveAndNext(ls);
  				else Next(ls);
  			  }
  			  break;
  			}
  		  } endloop:
  		  if (seminfo != null)
  		  {
  			  seminfo.ts = LuaXNewString(ls, luaZ_buffer(ls.buff) + (2 + sep), 
  											(uint)(luaZ_bufflen(ls.buff) - 2*(2 + sep)));
  		  }
  		}
  
  
  		static void ReadString (LexState ls, int del, SemInfo seminfo) {
  		  SaveAndNext(ls);
  		  while (ls.current != del) {
  			switch (ls.current) {
  			  case EOZ:
  				LuaXLexError(ls, "unfinished string", (int)RESERVED.TK_EOS);
  				continue;  /* to avoid warnings */
  			  case '\n':
  			  case '\r':
  				LuaXLexError(ls, "unfinished string", (int)RESERVED.TK_STRING);
  				continue;  /* to avoid warnings */
  			  case '\\': {
  				int c;
  				Next(ls);  /* do not save the `\' */
  				switch (ls.current) {
  				  case 'a': c = '\a'; break;
  				  case 'b': c = '\b'; break;
  				  case 'f': c = '\f'; break;
  				  case 'n': c = '\n'; break;
  				  case 'r': c = '\r'; break;
  				  case 't': c = '\t'; break;
  				  case 'v': c = '\v'; break;
  				  case '\n':  /* go through */
  				  case '\r': Save(ls, '\n'); IncLineNumber(ls); continue;
  				  case EOZ: continue;  /* will raise an error next loop */
  				  default: {
  					if (!isdigit(ls.current))
  					  SaveAndNext(ls);  /* handles \\, \", \', and \? */
  					else {  /* \xxx */
  					  int i = 0;
  					  c = 0;
  					  do {
  
gt: 						c = 10*c + (ls.current-'0');