"type checking"
This commit is contained in:
parent
49b0f214a6
commit
2d4f4c5bd5
|
@ -136,7 +136,7 @@ std::any EmitVisitor::visitFactor(xlangParser::FactorContext *ctx) {
|
||||||
if (auto expr_list = ctx->exprList()) {
|
if (auto expr_list = ctx->exprList()) {
|
||||||
for (auto expr : expr_list->expr()) {
|
for (auto expr : expr_list->expr()) {
|
||||||
visitExpr(expr);
|
visitExpr(expr);
|
||||||
args.push_back(last);
|
args.emplace_back(last);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
output << " " << tmp() << " = w call $"
|
output << " " << tmp() << " = w call $"
|
||||||
|
|
|
@ -15,8 +15,15 @@ void ErrorListener::syntaxError([[maybe_unused]] antlr4::Recognizer *recognizer,
|
||||||
size_t line, size_t charPositionInLine,
|
size_t line, size_t charPositionInLine,
|
||||||
const std::string &msg,
|
const std::string &msg,
|
||||||
[[maybe_unused]] std::exception_ptr e) {
|
[[maybe_unused]] std::exception_ptr e) {
|
||||||
std::cerr << file << ":" << line << ":" << charPositionInLine << ": " << msg
|
std::cerr << file << ":" << line << ":" << charPositionInLine + 1 << ": "
|
||||||
<< std::endl;
|
<< msg << std::endl;
|
||||||
|
has_error = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ErrorListener::typeError(size_t line, size_t charPositionInLine,
|
||||||
|
std::string_view msg) {
|
||||||
|
std::cerr << file << ":" << line << ":" << charPositionInLine + 1 << ": "
|
||||||
|
<< msg << std::endl;
|
||||||
has_error = true;
|
has_error = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,11 +11,13 @@ class ErrorListener : public antlr4::BaseErrorListener {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ErrorListener(std::string_view inputfile);
|
ErrorListener(std::string_view inputfile);
|
||||||
|
|
||||||
bool hasError();
|
bool hasError();
|
||||||
void syntaxError(antlr4::Recognizer *recognizer,
|
void syntaxError(antlr4::Recognizer *recognizer,
|
||||||
antlr4::Token *offendingSymbol, size_t line,
|
antlr4::Token *offendingSymbol, size_t line,
|
||||||
size_t charPositionInLine, const std::string &msg,
|
size_t charPositionInLine, const std::string &msg,
|
||||||
std::exception_ptr e) override;
|
std::exception_ptr e) override;
|
||||||
|
void typeError(size_t line, size_t charPositionInLine, std::string_view msg);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace xlang
|
} // namespace xlang
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <error.hh>
|
#include <error.hh>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <typecheck.hh>
|
||||||
#include <xlangLexer.h>
|
#include <xlangLexer.h>
|
||||||
#include <xlangParser.h>
|
#include <xlangParser.h>
|
||||||
|
|
||||||
|
@ -51,6 +52,12 @@ int main(int argc, char **argv) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xlang::TypeCheckVisitor typecheck{errorlistener};
|
||||||
|
typecheck.visitFile(tree);
|
||||||
|
if (errorlistener.hasError()) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
xlang::EmitVisitor emit{outputfile};
|
xlang::EmitVisitor emit{outputfile};
|
||||||
emit.visitFile(tree);
|
emit.visitFile(tree);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -3,6 +3,7 @@ xc_exe = executable('xc',
|
||||||
'main.cc',
|
'main.cc',
|
||||||
'emit.cc',
|
'emit.cc',
|
||||||
'error.cc',
|
'error.cc',
|
||||||
|
'typecheck.cc',
|
||||||
antlr4.process('xlang.g4'),
|
antlr4.process('xlang.g4'),
|
||||||
],
|
],
|
||||||
dependencies : [
|
dependencies : [
|
||||||
|
|
109
bootstrap/typecheck.cc
Normal file
109
bootstrap/typecheck.cc
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
#include <typecheck.hh>
|
||||||
|
|
||||||
|
namespace xlang {
|
||||||
|
|
||||||
|
bool TypeCheckVisitor::inScope(const std::string &name) {
|
||||||
|
for (auto it = scope.end() - 1;; it--) {
|
||||||
|
if (it->find(name) != it->end()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (it == scope.begin()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeCheckVisitor::TypeCheckVisitor(ErrorListener &errorlistener)
|
||||||
|
: errorlistener{errorlistener} {}
|
||||||
|
|
||||||
|
std::any TypeCheckVisitor::visitFile(xlangParser::FileContext *ctx) {
|
||||||
|
for (auto function : ctx->function()) {
|
||||||
|
auto token = function->Identifier()->getSymbol();
|
||||||
|
auto name = token->getText();
|
||||||
|
if (function_arity.find(name) != function_arity.end()) {
|
||||||
|
errorlistener.typeError(token->getLine(), token->getCharPositionInLine(),
|
||||||
|
"duplicate function '" + name + "'");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (auto arg_list = function->argumentList()) {
|
||||||
|
function_arity[name] = arg_list->Identifier().size();
|
||||||
|
} else {
|
||||||
|
function_arity[name] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visitChildren(ctx);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any TypeCheckVisitor::visitFunction(xlangParser::FunctionContext *ctx) {
|
||||||
|
scope.emplace_back();
|
||||||
|
if (auto arg_list = ctx->argumentList()) {
|
||||||
|
for (const auto arg : arg_list->Identifier()) {
|
||||||
|
scope.back().emplace(arg->getSymbol()->getText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visitBlock(ctx->block());
|
||||||
|
scope.pop_back();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any TypeCheckVisitor::visitBlock(xlangParser::BlockContext *ctx) {
|
||||||
|
scope.emplace_back();
|
||||||
|
visitChildren(ctx);
|
||||||
|
scope.pop_back();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any TypeCheckVisitor::visitStatement(xlangParser::StatementContext *ctx) {
|
||||||
|
if (auto identifier = ctx->Identifier()) {
|
||||||
|
visitExpr(ctx->expr());
|
||||||
|
scope.back().emplace(identifier->getSymbol()->getText());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
visitChildren(ctx);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any TypeCheckVisitor::visitFactor(xlangParser::FactorContext *ctx) {
|
||||||
|
if (auto identifier = ctx->Identifier()) {
|
||||||
|
auto token = identifier->getSymbol();
|
||||||
|
auto name = token->getText();
|
||||||
|
if (ctx->LeftParen()) {
|
||||||
|
if (function_arity.find(name) == function_arity.end()) {
|
||||||
|
errorlistener.typeError(token->getLine(),
|
||||||
|
token->getCharPositionInLine(),
|
||||||
|
"unknown function '" + name + "'");
|
||||||
|
}
|
||||||
|
if (auto expr_list = ctx->exprList()) {
|
||||||
|
auto arity = function_arity[name];
|
||||||
|
auto arg_num = expr_list->expr().size();
|
||||||
|
if (arity != arg_num) {
|
||||||
|
errorlistener.typeError(
|
||||||
|
token->getLine(), token->getCharPositionInLine(),
|
||||||
|
"function '" + name + "' expects " + std::to_string(arity) +
|
||||||
|
" arguments, but got " + std::to_string(arg_num));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (auto arity = function_arity[name]) {
|
||||||
|
errorlistener.typeError(
|
||||||
|
token->getLine(), token->getCharPositionInLine(),
|
||||||
|
"function '" + name + "' expects " + std::to_string(arity) +
|
||||||
|
" arguments, but got none");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visitChildren(ctx);
|
||||||
|
} else {
|
||||||
|
if (!inScope(name)) {
|
||||||
|
errorlistener.typeError(token->getLine(),
|
||||||
|
token->getCharPositionInLine(),
|
||||||
|
"variable '" + name + "' is not in scope");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
visitChildren(ctx);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xlang
|
29
bootstrap/typecheck.hh
Normal file
29
bootstrap/typecheck.hh
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <error.hh>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <vector>
|
||||||
|
#include <xlangBaseVisitor.h>
|
||||||
|
|
||||||
|
namespace xlang {
|
||||||
|
|
||||||
|
class TypeCheckVisitor : public xlangBaseVisitor {
|
||||||
|
ErrorListener &errorlistener;
|
||||||
|
std::unordered_map<std::string, unsigned int> function_arity;
|
||||||
|
std::vector<std::unordered_set<std::string>> scope;
|
||||||
|
|
||||||
|
bool inScope(const std::string &name);
|
||||||
|
|
||||||
|
public:
|
||||||
|
TypeCheckVisitor(ErrorListener &errorlistener);
|
||||||
|
|
||||||
|
std::any visitFile(xlangParser::FileContext *ctx) override;
|
||||||
|
std::any visitFunction(xlangParser::FunctionContext *ctx) override;
|
||||||
|
std::any visitBlock(xlangParser::BlockContext *ctx) override;
|
||||||
|
std::any visitStatement(xlangParser::StatementContext *ctx) override;
|
||||||
|
std::any visitFactor(xlangParser::FactorContext *ctx) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xlang
|
Loading…
Reference in a new issue