151 lines
3.6 KiB
C#
151 lines
3.6 KiB
C#
|
using KumiScript.Interpreter;
|
||
|
|
||
|
namespace KumiScript.Reader
|
||
|
{
|
||
|
public class Lexer
|
||
|
{
|
||
|
Stack<Token> _tokenStack;
|
||
|
readonly StreamReader _streamReader;
|
||
|
|
||
|
public Lexer(Stream stream)
|
||
|
{
|
||
|
_tokenStack = new Stack<Token>(16);
|
||
|
_streamReader = new StreamReader(stream);
|
||
|
}
|
||
|
|
||
|
public Token NextToken()
|
||
|
{
|
||
|
if (_tokenStack.IsEmpty())
|
||
|
return LexFromStream();
|
||
|
|
||
|
return _tokenStack.Pop();
|
||
|
}
|
||
|
|
||
|
public Token PeekToken()
|
||
|
{
|
||
|
Token t = NextToken();
|
||
|
PushToken(t);
|
||
|
return t;
|
||
|
}
|
||
|
|
||
|
public void PushToken(Token token)
|
||
|
{
|
||
|
_tokenStack.Push(token);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
private Token LexFromStream()
|
||
|
{
|
||
|
int c = GetChar();
|
||
|
|
||
|
while (char.IsWhiteSpace((char) c))
|
||
|
c = GetChar();
|
||
|
|
||
|
if (c == ';')
|
||
|
{
|
||
|
while ((c = GetChar()) != '\n');
|
||
|
c = GetChar();
|
||
|
}
|
||
|
|
||
|
if (c == -1)
|
||
|
return new EndOfFileToken();
|
||
|
if (c == '(')
|
||
|
return new ParenthesisToken(true);
|
||
|
if (c == ')')
|
||
|
return new ParenthesisToken(false);
|
||
|
if (c == '"')
|
||
|
return LexString();
|
||
|
if (isspec(c))
|
||
|
return new SpecialToken((char) c);
|
||
|
|
||
|
return LexGeneric(c);
|
||
|
}
|
||
|
|
||
|
private Token LexString()
|
||
|
{
|
||
|
List<char> chars = new List<char>();
|
||
|
int c = GetChar();
|
||
|
|
||
|
while (c != '"')
|
||
|
{
|
||
|
if (c == '\\')
|
||
|
c = GetChar();
|
||
|
|
||
|
chars.Add((char) c);
|
||
|
c = GetChar();
|
||
|
if (c == -1)
|
||
|
throw new Exception("Unexpected EOF!");
|
||
|
}
|
||
|
|
||
|
return new StringToken(new string(chars.ToArray()));
|
||
|
}
|
||
|
|
||
|
private Token LexGeneric(int c)
|
||
|
{
|
||
|
List<char> chars = new List<char>();
|
||
|
chars.Add((char) c); //NOTE: The language server is WRONG! List<char>((char) c) BREAKS! **** you m$
|
||
|
|
||
|
int p = PeekChar();
|
||
|
while (!char.IsWhiteSpace((char) p) && !isspecparen(p) && p != -1)
|
||
|
{
|
||
|
c = GetChar();
|
||
|
chars.Add((char) c);
|
||
|
p = PeekChar();
|
||
|
}
|
||
|
|
||
|
return new AtomToken(new string(chars.ToArray()));
|
||
|
}
|
||
|
|
||
|
private static bool isspec(int c)
|
||
|
{
|
||
|
switch (c)
|
||
|
{
|
||
|
case '#':
|
||
|
return true;
|
||
|
case '\'':
|
||
|
return true;
|
||
|
case '\\':
|
||
|
return true;
|
||
|
case '`':
|
||
|
return true;
|
||
|
case ',':
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
private static bool isspecparen(int c)
|
||
|
{
|
||
|
switch (c)
|
||
|
{
|
||
|
case '(':
|
||
|
return true;
|
||
|
case ')':
|
||
|
return true;
|
||
|
case '#':
|
||
|
return true;
|
||
|
case '\'':
|
||
|
return true;
|
||
|
case '\\':
|
||
|
return true;
|
||
|
case '`':
|
||
|
return true;
|
||
|
case ',':
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
private int GetChar()
|
||
|
{
|
||
|
return _streamReader.Read();
|
||
|
}
|
||
|
|
||
|
private int PeekChar()
|
||
|
{
|
||
|
return _streamReader.Peek();
|
||
|
}
|
||
|
}
|
||
|
}
|