using KumiScript.Interpreter; namespace KumiScript.Reader { public class Lexer { Stack _tokenStack; readonly StreamReader _streamReader; public Lexer(Stream stream) { _tokenStack = new Stack(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 chars = new List(); 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 chars = new List(); chars.Add((char) c); //NOTE: The language server is WRONG! List((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(); } } }