Report on caught syntax errors
This commit is contained in:
parent
5c14bb190f
commit
fe904e47f2
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
.*
|
||||
*.o
|
||||
main
|
||||
|
5
ast.cpp
5
ast.cpp
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
24
main.cpp
24
main.cpp
@ -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;
|
||||
}
|
||||
|
170
parser.cpp
170
parser.cpp
@ -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;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
} else {
|
||||
errors.push_back(ExpectedProcedureCallClose);
|
||||
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;
|
||||
}
|
||||
|
||||
|
54
parser.hpp
54
parser.hpp
@ -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();
|
||||
|
||||
AST::Node *parse(CompilerState&);
|
||||
std::string describeErrors() noexcept;
|
||||
|
||||
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…
x
Reference in New Issue
Block a user