singrdk/base/Libraries/ProtoLisp/Lexer.cs

190 lines
5.6 KiB
C#
Raw Normal View History

2008-11-17 18:29:00 -05:00
// ----------------------------------------------------------------------------
2008-03-05 09:52:00 -05:00
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
2008-11-17 18:29:00 -05:00
// ----------------------------------------------------------------------------
2008-03-05 09:52:00 -05:00
using System;
using System.Text;
using System.IO;
using System.Diagnostics;
//
// The Lexer class tokenizes an input stream of text
// and can also read off complete Proto-Lisp expressions
// for evaluation.
//
// The lexer has a one-character lookahead buffer.
//
namespace ProtoLisp
{
class Lexer
{
// Constructor
public Lexer(Stream stream)
{
inputStream = stream;
haveLookAhead = false;
}
// Returns the next character in the input stream.
// The stream is not (conceptually) advanced.
// Calling PeekNextChar or GetNextChar will
// return the same character.
//
// Returns null if the end of the stream has been
// reached.
private bool PeekNextChar(out char val)
{
2008-11-17 18:29:00 -05:00
if (haveLookAhead) {
2008-03-05 09:52:00 -05:00
val = lookAheadVal;
return true;
}
2008-11-17 18:29:00 -05:00
else {
2008-03-05 09:52:00 -05:00
int nextVal = inputStream.ReadByte();
2008-11-17 18:29:00 -05:00
if (nextVal == -1) {
2008-03-05 09:52:00 -05:00
// Nothing more to read
val = '\0';
return false;
}
2008-11-17 18:29:00 -05:00
else {
2008-03-05 09:52:00 -05:00
haveLookAhead = true;
// Hacky cast: just assume everything is 1-byte ASCII
lookAheadVal = (char)nextVal;
val = lookAheadVal;
return true;
}
}
}
// Retrieves the next character, and advances the stream.
// Returns null if the end has already been reached.
private bool GetNextChar(out char val)
{
char dummy;
2008-11-17 18:29:00 -05:00
if (!PeekNextChar(out dummy)) {
2008-03-05 09:52:00 -05:00
val = '\0';
return false;
}
Debug.Assert(haveLookAhead);
val = lookAheadVal;
haveLookAhead = false;
return true;
}
// Advances the stream without returning the current
// character.
private void DiscardNextChar()
{
char dummyChar;
GetNextChar(out dummyChar);
}
// Get the next token.
private string GetToken()
{
char peekChar;
// Skip whitespace
2008-11-17 18:29:00 -05:00
if (!PeekNextChar(out peekChar)) {
2008-03-05 09:52:00 -05:00
return null;
}
2008-11-17 18:29:00 -05:00
while (Char.IsWhiteSpace(peekChar)) {
2008-03-05 09:52:00 -05:00
// Throw away the whitespace char
DiscardNextChar();
2008-11-17 18:29:00 -05:00
if (!PeekNextChar(out peekChar)) {
2008-03-05 09:52:00 -05:00
return null;
}
}
// Special one-character tokens
2008-11-17 18:29:00 -05:00
if ((peekChar == '(') || (peekChar == ')') || (peekChar == '\'')) {
2008-03-05 09:52:00 -05:00
// Consume and return this character
DiscardNextChar();
return new String(peekChar, 1);
}
String token = "";
do
{
token += peekChar;
DiscardNextChar();
2008-11-17 18:29:00 -05:00
if (!PeekNextChar(out peekChar)) {
2008-03-05 09:52:00 -05:00
// Ran into the end of the stream
return token;
}
} while ((!Char.IsWhiteSpace(peekChar)) && (peekChar != '(') && (peekChar != ')'));
return token;
}
public PLObject GetExpression()
{
string token = GetToken();
2008-11-17 18:29:00 -05:00
if (token == null) {
2008-03-05 09:52:00 -05:00
return null;
}
2008-11-17 18:29:00 -05:00
if (token.Equals("'")) {
2008-03-05 09:52:00 -05:00
// The "'" character means "quote", and preserve
// the next expression.
PLList retval = new PLList();
retval.Add(new PLStringAtom("quote"));
PLObject nextExpr = GetExpression();
2008-11-17 18:29:00 -05:00
if (nextExpr == null) {
2008-03-05 09:52:00 -05:00
throw new Exception("'quote' was not followed by a complete expression");
}
retval.Add(nextExpr);
return retval;
}
2008-11-17 18:29:00 -05:00
if (!token.Equals("(")) {
2008-03-05 09:52:00 -05:00
// Not the beginning of a list; the expression is
// simply this token. See if it's a number or a
// plain string.
2008-11-17 18:29:00 -05:00
try {
2008-03-05 09:52:00 -05:00
return new PLNumberAtom(token);
}
2008-11-17 18:29:00 -05:00
catch (Exception) {
2008-03-05 09:52:00 -05:00
// Just treat it as a regular string
return new PLStringAtom(token);
}
}
2008-11-17 18:29:00 -05:00
else {
2008-03-05 09:52:00 -05:00
// We have the beginning of a list, which can contain
// any number of expressions. Keep building our list
// until we encounter the closing paren.
PLList retval = new PLList();
PLObject nextExpr = GetExpression();
2008-11-17 18:29:00 -05:00
while (! nextExpr.Equals(")")) {
2008-03-05 09:52:00 -05:00
retval.Add(nextExpr);
nextExpr = GetExpression();
2008-11-17 18:29:00 -05:00
if (nextExpr == null) {
2008-03-05 09:52:00 -05:00
throw new Exception("Incomplete expression (unbalanced parens?");
}
}
return retval;
}
}
private Stream inputStream;
private bool haveLookAhead;
private char lookAheadVal;
}
}