Report on caught syntax errors

master
Clyne 2 years ago
parent 5c14bb190f
commit fe904e47f2

1
.gitignore vendored

@ -1,2 +1,3 @@
.*
*.o *.o
main main

@ -80,8 +80,9 @@ AST::Value AST::ProcedureCall::codegen(CompilerState& state)
std::vector<llvm::Type *> argtypes; std::vector<llvm::Type *> argtypes;
for (auto& a : operands) { for (auto& a : operands) {
args.push_back(a->codegen(state)); auto gen = a->codegen(state);
argtypes.push_back(args.back()->getType()); args.push_back(gen);
argtypes.push_back(gen ? gen->getType() : llvm::Type::getVoidTy(state.context));
} }

@ -59,7 +59,7 @@ TODO:
#include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h" #include "llvm/Target/TargetOptions.h"
#include <cstdio> #include <iostream>
#include <string> #include <string>
#include "ast.hpp" #include "ast.hpp"
@ -80,14 +80,26 @@ int main(int argc, const char *argv[])
auto block = llvm::BasicBlock::Create(state.context, "entry", func); auto block = llvm::BasicBlock::Create(state.context, "entry", func);
state.builder.SetInsertPoint(block); state.builder.SetInsertPoint(block);
parser.parse(state); auto astTree = parser.parse();
if (parser.hasErrors()) {
auto err = parser.describeErrors();
std::cout << "Error: " << std::endl << err << std::endl;
} else if (!astTree.empty()) {
for (auto& node : astTree) {
std::cout << node->desc() << std::endl;
node->codegen(state);
}
state.builder.CreateRet(state.builder.getInt32(0)); state.builder.CreateRet(state.builder.getInt32(0));
puts(""); std::cout << std::endl;
state.module.print(llvm::errs(), nullptr); // prints everything state.module.print(llvm::errs(), nullptr); // prints everything
compileToObjectFile(state); compileToObjectFile(state);
} else {
std::cout << "Nothing to do." << std::endl;
}
return 0; return 0;
} }

@ -24,24 +24,25 @@
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
#include <cstdlib> #include <cstdlib>
#include <iostream>
#include <vector> #include <vector>
void Parser::addString(const std::string& str) void Parser::addString(const std::string& str)
{ {
if (!str.empty())
std::copy(str.cbegin(), str.cend(), std::back_inserter(text)); std::copy(str.cbegin(), str.cend(), std::back_inserter(text));
} }
void Parser::consumeWhitespace() void Parser::consumeWhitespace() noexcept
{ {
while (isspace(text.front())) while (isspace(text.front()))
text.pop_front(); text.pop_front();
} }
std::optional<std::string> Parser::consumeIdentifier() std::optional<std::string> Parser::consumeIdentifier() noexcept
{ {
std::string ret; std::string ret;
// TODO Accept all valid identifiers according to R7RS-small.
if (isalpha(text.front())) { if (isalpha(text.front())) {
do { do {
ret += text.front(); ret += text.front();
@ -66,32 +67,35 @@ std::optional<std::variant<int, double>> Parser::consumeLiteralNumber()
if (ret.empty()) if (ret.empty())
return {}; return {};
else if (ret.find('.') == std::string::npos)
return (int)strtol(ret.c_str(), nullptr, 0); if (ret.find('.') == std::string::npos) {
else int r = strtol(ret.c_str(), nullptr, 0);
return strtod(ret.c_str(), nullptr); // TODO Error check
return r;
} else {
auto r = strtod(ret.c_str(), nullptr);
// TODO Error check
return r;
}
} }
AST::Node *Parser::parse(CompilerState& state) std::deque<AST::Node *> Parser::parse()
{ {
AST::Node *ret = nullptr; std::deque<AST::Node *> ret;
errors.clear();
while (!text.empty()) { while (!text.empty()) {
consumeWhitespace(); consumeWhitespace();
// At the top-level, there will only be procedure calls. // At the top-level, there will only be procedure calls.
ret = parseProcedureCall(); auto node = parseProcedureCall();
if (lastError != None) if (errors.empty() && node) {
return nullptr; ret.push_back(node);
} else {
return {};
std::cout << (ret ? ret->desc() : "nullptr") << std::endl; }
if (ret)
ret->codegen(state);
} }
lastError = None;
return ret; return ret;
} }
@ -110,6 +114,7 @@ AST::Node *Parser::parseExpression()
return lit; return lit;
} }
errors.push_back(InvalidExpression);
return nullptr; return nullptr;
} }
@ -117,7 +122,7 @@ AST::Node *Parser::parseProcedureCall()
{ {
// Consume the opening parenthesis. // Consume the opening parenthesis.
if (text.front() != '(') { if (text.front() != '(') {
lastError = ExpectedProcedure; errors.push_back(ExpectedProcedureCallOpen);
return nullptr; return nullptr;
} else { } else {
text.pop_front(); text.pop_front();
@ -125,11 +130,10 @@ AST::Node *Parser::parseProcedureCall()
// Consume the identifier string. // Consume the identifier string.
auto ident = parseExpression(); auto ident = parseExpression();
if (ident == nullptr) { if (ident == nullptr)
lastError = InvalidOperator;
return nullptr; return nullptr;
}
// Check for special procedure calls.
if (auto id = dynamic_cast<AST::Identifier *>(ident); id) { if (auto id = dynamic_cast<AST::Identifier *>(ident); id) {
if (id->name == "lambda") if (id->name == "lambda")
return parseLambdaExpression(); return parseLambdaExpression();
@ -141,13 +145,16 @@ AST::Node *Parser::parseProcedureCall()
return parseAssignment(); return parseAssignment();
} }
// This is a regular procedure call.
// Build the argument list:
std::vector<AST::Node *> args; std::vector<AST::Node *> args;
consumeWhitespace(); consumeWhitespace();
while (text.front() != ')') { while (text.front() != ')') {
auto node = parseExpression(); auto node = parseExpression();
if (node == nullptr) { if (node == nullptr) {
lastError = InvalidOperand; errors.push_back(InvalidOperand);
return nullptr; return nullptr;
} }
@ -164,7 +171,7 @@ AST::Node *Parser::parseProcedureCall()
return pc; return pc;
} else { } else {
lastError = ExpectedProcedureCallClose; errors.push_back(ExpectedProcedureCallClose);
return nullptr; return nullptr;
} }
} }
@ -173,18 +180,24 @@ AST::Node *Parser::parseConditional()
{ {
consumeWhitespace(); consumeWhitespace();
auto cond = parseExpression(); auto cond = parseExpression();
if (cond == nullptr) if (cond == nullptr) {
errors.push_back(InvalidCondition);
return nullptr; return nullptr;
}
consumeWhitespace(); consumeWhitespace();
auto ift = parseExpression(); auto ift = parseExpression();
if (ift == nullptr) if (ift == nullptr) {
errors.push_back(InvalidThenBranch);
return nullptr; return nullptr;
}
consumeWhitespace(); consumeWhitespace();
auto iff = parseExpression(); auto iff = parseExpression();
if (iff == nullptr) if (iff == nullptr) {
errors.push_back(InvalidThenBranch);
return nullptr; return nullptr;
}
consumeWhitespace(); consumeWhitespace();
if (text.front() == ')') { if (text.front() == ')') {
@ -195,10 +208,11 @@ AST::Node *Parser::parseConditional()
node->iftrue = ift; node->iftrue = ift;
node->iffalse = iff; node->iffalse = iff;
return node; return node;
} } else {
errors.push_back(ExpectedProcedureCallClose);
return nullptr; return nullptr;
} }
}
AST::Node *Parser::parseDefinition() AST::Node *Parser::parseDefinition()
{ {
@ -221,8 +235,14 @@ AST::Node *Parser::parseDefinition()
def->ident = id; def->ident = id;
def->value = val; def->value = val;
return def; return def;
} else {
errors.push_back(ExpectedProcedureCallClose);
} }
} else {
errors.push_back(InvalidInitializer);
} }
} else {
errors.push_back(ExpectedIdentifier);
} }
return nullptr; return nullptr;
@ -249,8 +269,14 @@ AST::Node *Parser::parseAssignment()
def->ident = id; def->ident = id;
def->value = val; def->value = val;
return def; return def;
} else {
errors.push_back(ExpectedProcedureCallClose);
} }
} else {
errors.push_back(InvalidAssignValue);
} }
} else {
errors.push_back(ExpectedIdentifier);
} }
return nullptr; return nullptr;
@ -258,15 +284,17 @@ AST::Node *Parser::parseAssignment()
AST::Node *Parser::parseLambdaExpression() AST::Node *Parser::parseLambdaExpression()
{ {
// First get argument list // Consume beginning of argument list.
consumeWhitespace(); consumeWhitespace();
if (text.front() != '(') { if (text.front() != '(') {
lastError = ExpectedArgumentList; errors.push_back(ExpectedArgumentList);
return nullptr; return nullptr;
} else { } else {
text.pop_front(); text.pop_front();
} }
// Consume argument list:
std::vector<AST::Identifier *> args; std::vector<AST::Identifier *> args;
while (text.front() != ')') { while (text.front() != ')') {
@ -276,22 +304,28 @@ AST::Node *Parser::parseLambdaExpression()
ident->name = *arg; ident->name = *arg;
args.push_back(ident); args.push_back(ident);
} else { } else {
lastError = InvalidOperand; errors.push_back(InvalidArgumentName);
return nullptr; return nullptr;
} }
consumeWhitespace(); consumeWhitespace();
} }
text.pop_front(); // consume arg list ')'
// Consume arg list closing ')' that must be there.
text.pop_front();
// Consume lambda body:
std::vector<AST::Node *> body; std::vector<AST::Node *> body;
// Next, consume function body.
consumeWhitespace(); consumeWhitespace();
while (text.front() != ')') { while (text.front() != ')') {
auto exp = parseExpression(); auto exp = parseExpression();
//if (exp == nullptr) // TODO commands/definitions are okay
// return nullptr; if (!errors.empty()) {
errors.push_back(InvalidLambdaBody);
return nullptr;
}
body.push_back(exp); body.push_back(exp);
consumeWhitespace(); consumeWhitespace();
@ -306,8 +340,64 @@ AST::Node *Parser::parseLambdaExpression()
return le; return le;
} else { } else {
lastError = ExpectedProcedureCallClose; errors.push_back(ExpectedProcedureCallClose);
return nullptr; return nullptr;
} }
} }
std::string Parser::describeErrors() noexcept
{
std::string ret;
for (auto& err : errors) {
switch (err) {
case ExpectedProcedureCallOpen:
ret += "Expected opening \'(\' for procedure call.\n";
break;
case ExpectedIdentifier:
ret += "Expected a valid identifier.\n";
break;
case ExpectedProcedureCallClose:
ret += "Expected closing \')\' for procedure call.\n";
break;
case ExpectedArgumentList:
ret += "Expected beginning of argument list.\n";
break;
case UnknownIdentifier:
ret += "Given identifier is not valid.\n";
break;
case InvalidExpression:
ret += "Expected a valid expression.\n";
break;
case InvalidOperand:
ret += "Given invalid argument or operand.\n";
break;
case InvalidCondition:
ret += "Given invalid condition for conditional statement.\n";
break;
case InvalidThenBranch:
ret += "Given invalid \"then\" branch for conditional statement.\n";
break;
case InvalidElseBranch:
ret += "Given invalid \"else\" branch for conditional statement.\n";
break;
case InvalidInitializer:
ret += "Given invalid initializer for a declaration.\n";
break;
case InvalidAssignValue:
ret += "Given invalid value for an assignment.\n";
break;
case InvalidArgumentName:
ret += "Given invalid name for an argument.\n";
break;
case InvalidLambdaBody:
ret += "Lambda body is invalid.\n";
break;
default:
break;
}
}
return ret;
}

@ -23,7 +23,6 @@
#define PARSER_HPP #define PARSER_HPP
#include "ast.hpp" #include "ast.hpp"
#include "state.hpp"
#include <deque> #include <deque>
#include <optional> #include <optional>
@ -33,27 +32,62 @@
class Parser { class Parser {
public: public:
enum Error { enum Error {
None, ExpectedProcedureCallOpen,
ExpectedProcedure,
ExpectedIdentifier, ExpectedIdentifier,
ExpectedProcedureCallClose, ExpectedProcedureCallClose,
ExpectedArgumentList, ExpectedArgumentList,
UnknownIdentifier, UnknownIdentifier,
InvalidOperator, InvalidExpression,
InvalidOperand, InvalidOperand,
InvalidCondition,
InvalidThenBranch,
InvalidElseBranch,
InvalidInitializer,
InvalidAssignValue,
InvalidArgumentName,
InvalidLambdaBody
}; };
void addString(const std::string& str); /**
* Appends the given string to the end of the text queue.
* Call parse() after adding text to produce the AST.
*/
void addString(const std::string&);
void consumeWhitespace(); /**
std::optional<std::string> consumeIdentifier(); * Attempts to parse all text in the text queue, stopping if there is an
std::optional<std::variant<int, double>> consumeLiteralNumber(); * error.
* @return An iterable collection of produced AST nodes.
*/
std::deque<AST::Node *> parse();
std::string describeErrors() noexcept;
AST::Node *parse(CompilerState&); bool hasErrors() const noexcept {
return !errors.empty();
}
private: private:
std::deque<char> text; std::deque<char> text;
Error lastError = None; std::deque<Error> errors;
/**
* Advances through the text queue until a non-whitespace character is
* found.
*/
void consumeWhitespace() noexcept;
/**
* Attempts to consume an identifier from the text queue.
* @return A string containing the identifier if one is found.
*/
std::optional<std::string> consumeIdentifier() noexcept;
/**
* Attempts to consume a literal number from the text queue.
* @return The number if one is found: an integer if there is no decimal
* point; otherwise, a double.
* TODO Ensure and add noexcept.
*/
std::optional<std::variant<int, double>> consumeLiteralNumber();
AST::Node *parseExpression(); AST::Node *parseExpression();
AST::Node *parseProcedureCall(); AST::Node *parseProcedureCall();

Loading…
Cancel
Save