Report on caught syntax errors

master
Clyne 2 years ago
parent 5c14bb190f
commit fe904e47f2

1
.gitignore vendored

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

@ -80,8 +80,9 @@ AST::Value AST::ProcedureCall::codegen(CompilerState& state)
std::vector<llvm::Type *> argtypes;
for (auto& a : operands) {
args.push_back(a->codegen(state));
argtypes.push_back(args.back()->getType());
auto gen = a->codegen(state);
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/TargetOptions.h"
#include <cstdio>
#include <iostream>
#include <string>
#include "ast.hpp"
@ -80,14 +80,26 @@ int main(int argc, const char *argv[])
auto block = llvm::BasicBlock::Create(state.context, "entry", func);
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;
state.builder.CreateRet(state.builder.getInt32(0));
node->codegen(state);
}
puts("");
state.module.print(llvm::errs(), nullptr); // prints everything
state.builder.CreateRet(state.builder.getInt32(0));
compileToObjectFile(state);
std::cout << std::endl;
state.module.print(llvm::errs(), nullptr); // prints everything
compileToObjectFile(state);
} else {
std::cout << "Nothing to do." << std::endl;
}
return 0;
}

@ -24,24 +24,25 @@
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <iostream>
#include <vector>
void Parser::addString(const std::string& str)
{
std::copy(str.cbegin(), str.cend(), std::back_inserter(text));
if (!str.empty())
std::copy(str.cbegin(), str.cend(), std::back_inserter(text));
}
void Parser::consumeWhitespace()
void Parser::consumeWhitespace() noexcept
{
while (isspace(text.front()))
text.pop_front();
}
std::optional<std::string> Parser::consumeIdentifier()
std::optional<std::string> Parser::consumeIdentifier() noexcept
{
std::string ret;
// TODO Accept all valid identifiers according to R7RS-small.
if (isalpha(text.front())) {
do {
ret += text.front();
@ -66,32 +67,35 @@ std::optional<std::variant<int, double>> Parser::consumeLiteralNumber()
if (ret.empty())
return {};
else if (ret.find('.') == std::string::npos)
return (int)strtol(ret.c_str(), nullptr, 0);
else
return strtod(ret.c_str(), nullptr);
if (ret.find('.') == std::string::npos) {
int r = strtol(ret.c_str(), nullptr, 0);
// 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()) {
consumeWhitespace();
// At the top-level, there will only be procedure calls.
ret = parseProcedureCall();
if (lastError != None)
return nullptr;
std::cout << (ret ? ret->desc() : "nullptr") << std::endl;
if (ret)
ret->codegen(state);
auto node = parseProcedureCall();
if (errors.empty() && node) {
ret.push_back(node);
} else {
return {};
}
}
lastError = None;
return ret;
}
@ -110,6 +114,7 @@ AST::Node *Parser::parseExpression()
return lit;
}
errors.push_back(InvalidExpression);
return nullptr;
}
@ -117,7 +122,7 @@ AST::Node *Parser::parseProcedureCall()
{
// Consume the opening parenthesis.
if (text.front() != '(') {
lastError = ExpectedProcedure;
errors.push_back(ExpectedProcedureCallOpen);
return nullptr;
} else {
text.pop_front();
@ -125,11 +130,10 @@ AST::Node *Parser::parseProcedureCall()
// Consume the identifier string.
auto ident = parseExpression();
if (ident == nullptr) {
lastError = InvalidOperator;
if (ident == nullptr)
return nullptr;
}
// Check for special procedure calls.
if (auto id = dynamic_cast<AST::Identifier *>(ident); id) {
if (id->name == "lambda")
return parseLambdaExpression();
@ -141,13 +145,16 @@ AST::Node *Parser::parseProcedureCall()
return parseAssignment();
}
// This is a regular procedure call.
// Build the argument list:
std::vector<AST::Node *> args;
consumeWhitespace();
while (text.front() != ')') {
auto node = parseExpression();
if (node == nullptr) {
lastError = InvalidOperand;
errors.push_back(InvalidOperand);
return nullptr;
}
@ -164,7 +171,7 @@ AST::Node *Parser::parseProcedureCall()
return pc;
} else {
lastError = ExpectedProcedureCallClose;
errors.push_back(ExpectedProcedureCallClose);
return nullptr;
}
}
@ -173,18 +180,24 @@ AST::Node *Parser::parseConditional()
{
consumeWhitespace();
auto cond = parseExpression();
if (cond == nullptr)
if (cond == nullptr) {
errors.push_back(InvalidCondition);
return nullptr;
}
consumeWhitespace();
auto ift = parseExpression();
if (ift == nullptr)
if (ift == nullptr) {
errors.push_back(InvalidThenBranch);
return nullptr;
}
consumeWhitespace();
auto iff = parseExpression();
if (iff == nullptr)
if (iff == nullptr) {
errors.push_back(InvalidThenBranch);
return nullptr;
}
consumeWhitespace();
if (text.front() == ')') {
@ -195,9 +208,10 @@ AST::Node *Parser::parseConditional()
node->iftrue = ift;
node->iffalse = iff;
return node;
} else {
errors.push_back(ExpectedProcedureCallClose);
return nullptr;
}
return nullptr;
}
AST::Node *Parser::parseDefinition()
@ -221,8 +235,14 @@ AST::Node *Parser::parseDefinition()
def->ident = id;
def->value = val;
return def;
} else {
errors.push_back(ExpectedProcedureCallClose);
}
} else {
errors.push_back(InvalidInitializer);
}
} else {
errors.push_back(ExpectedIdentifier);
}
return nullptr;
@ -249,8 +269,14 @@ AST::Node *Parser::parseAssignment()
def->ident = id;
def->value = val;
return def;
} else {
errors.push_back(ExpectedProcedureCallClose);
}
} else {
errors.push_back(InvalidAssignValue);
}
} else {
errors.push_back(ExpectedIdentifier);
}
return nullptr;
@ -258,15 +284,17 @@ AST::Node *Parser::parseAssignment()
AST::Node *Parser::parseLambdaExpression()
{
// First get argument list
// Consume beginning of argument list.
consumeWhitespace();
if (text.front() != '(') {
lastError = ExpectedArgumentList;
errors.push_back(ExpectedArgumentList);
return nullptr;
} else {
text.pop_front();
}
// Consume argument list:
std::vector<AST::Identifier *> args;
while (text.front() != ')') {
@ -276,22 +304,28 @@ AST::Node *Parser::parseLambdaExpression()
ident->name = *arg;
args.push_back(ident);
} else {
lastError = InvalidOperand;
errors.push_back(InvalidArgumentName);
return nullptr;
}
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;
// Next, consume function body.
consumeWhitespace();
while (text.front() != ')') {
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);
consumeWhitespace();
@ -306,8 +340,64 @@ AST::Node *Parser::parseLambdaExpression()
return le;
} else {
lastError = ExpectedProcedureCallClose;
errors.push_back(ExpectedProcedureCallClose);
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
#include "ast.hpp"
#include "state.hpp"
#include <deque>
#include <optional>
@ -33,27 +32,62 @@
class Parser {
public:
enum Error {
None,
ExpectedProcedure,
ExpectedProcedureCallOpen,
ExpectedIdentifier,
ExpectedProcedureCallClose,
ExpectedArgumentList,
UnknownIdentifier,
InvalidOperator,
InvalidExpression,
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();
std::optional<std::variant<int, double>> consumeLiteralNumber();
/**
* Attempts to parse all text in the text queue, stopping if there is an
* 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:
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 *parseProcedureCall();

Loading…
Cancel
Save