Lexer+Reader+Printer: Store strings with quotes, improve error handling
This commit is contained in:
+94
-13
@@ -31,30 +31,66 @@ Reader::~Reader()
|
||||
|
||||
void Reader::read()
|
||||
{
|
||||
if (m_node != nullptr) {
|
||||
if (m_node) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_node = readImpl();
|
||||
VERIFY(m_index > m_tokens.size() - 1, "more than one sexp in input");
|
||||
|
||||
// Error checking
|
||||
|
||||
if (m_invalid_syntax) {
|
||||
m_node = new Error("Invalid read syntax: '" + std::string(1, m_error_character) + "'");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_is_unbalanced) {
|
||||
m_node = new Error("Expected '" + std::string(1, m_error_character) + "', got EOF");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isEOF()) {
|
||||
Token::Type type = peek().type;
|
||||
switch (type) {
|
||||
case Token::Type::ParenOpen: // (
|
||||
case Token::Type::ParenClose: // )
|
||||
case Token::Type::String:
|
||||
case Token::Type::Value:
|
||||
m_node = new Error("More than one sexp in input");
|
||||
break;
|
||||
default:
|
||||
m_node = new Error("Unknown error");
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
ASTNode* Reader::readImpl()
|
||||
{
|
||||
if (m_tokens.size() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
switch (peek().type) {
|
||||
case Token::Type::ParenOpen:
|
||||
case Token::Type::ParenOpen: // (
|
||||
return readList();
|
||||
break;
|
||||
case Token::Type::ParenClose: // )
|
||||
m_invalid_syntax = true;
|
||||
m_error_character = ')';
|
||||
return nullptr;
|
||||
break;
|
||||
case Token::Type::String:
|
||||
return readString();
|
||||
break;
|
||||
case Token::Type::Value:
|
||||
return readValue();
|
||||
break;
|
||||
default:
|
||||
// Unimplemented token
|
||||
VERIFY_NOT_REACHED();
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ASTNode* Reader::readList()
|
||||
@@ -62,29 +98,64 @@ ASTNode* Reader::readList()
|
||||
ignore(); // (
|
||||
|
||||
List* list = new List();
|
||||
while (m_index < m_tokens.size() && peek().type != Token::Type::ParenClose) {
|
||||
while (!isEOF() && peek().type != Token::Type::ParenClose) {
|
||||
list->addNode(readImpl());
|
||||
}
|
||||
|
||||
VERIFY(m_index != m_tokens.size(), "missing closing ')'");
|
||||
|
||||
ignore(); // )
|
||||
if (!consumeSpecific(Token { .type = Token::Type::ParenClose })) { // )
|
||||
m_error_character = ')';
|
||||
m_is_unbalanced = true;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static bool isValidString(const std::string& str)
|
||||
{
|
||||
if (str.size() < 2 || str.front() != '"' || str.back() != '"') {
|
||||
return false;
|
||||
}
|
||||
if (str.size() == 2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool escaped = false;
|
||||
for (auto it = str.begin() + 1; it != str.end() - 1; ++it) {
|
||||
if (*it == '\\' && !escaped) {
|
||||
escaped = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// The last character needs to be an escaped '\' or not a '\'
|
||||
if (it == str.end() - 2 && (escaped || *it != '\\')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
escaped = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ASTNode* Reader::readString()
|
||||
{
|
||||
Token token = consume();
|
||||
return new String(token.symbol);
|
||||
std::string symbol = consume().symbol;
|
||||
|
||||
// Unbalanced string
|
||||
if (!isValidString(symbol)) {
|
||||
m_error_character = '"';
|
||||
m_is_unbalanced = true;
|
||||
}
|
||||
|
||||
return new String(symbol);
|
||||
}
|
||||
|
||||
ASTNode* Reader::readValue()
|
||||
{
|
||||
Token token = consume();
|
||||
char* endPtr = nullptr;
|
||||
int64_t result = std::strtoll(token.symbol.c_str(), &endPtr, 10);
|
||||
if (endPtr == token.symbol.c_str() + token.symbol.size()) {
|
||||
char* end_ptr = nullptr;
|
||||
int64_t result = std::strtoll(token.symbol.c_str(), &end_ptr, 10);
|
||||
if (end_ptr == token.symbol.c_str() + token.symbol.size()) {
|
||||
return new Number(result);
|
||||
}
|
||||
|
||||
@@ -110,6 +181,16 @@ Token Reader::consume()
|
||||
return m_tokens[m_index++];
|
||||
}
|
||||
|
||||
bool Reader::consumeSpecific(Token token)
|
||||
{
|
||||
if (isEOF() || peek().type != token.type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ignore();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Reader::ignore()
|
||||
{
|
||||
m_index++;
|
||||
|
||||
Reference in New Issue
Block a user