Commit f9369c71 authored by Alastair Robertson's avatar Alastair Robertson

Perform two pass semantic analysis to check types of expressions

parent ce98e39c
#include "ast.h" #include "ast.h"
#include "parser.tab.hh"
namespace ebpf { namespace ebpf {
namespace bpftrace { namespace bpftrace {
...@@ -52,6 +53,38 @@ void Program::accept(Visitor &v) { ...@@ -52,6 +53,38 @@ void Program::accept(Visitor &v) {
v.visit(*this); v.visit(*this);
} }
std::string opstr(Binop &binop)
{
switch (binop.op) {
case ebpf::bpftrace::Parser::token::EQ: return "==";
case ebpf::bpftrace::Parser::token::NE: return "!=";
case ebpf::bpftrace::Parser::token::LE: return "<=";
case ebpf::bpftrace::Parser::token::GE: return ">=";
case ebpf::bpftrace::Parser::token::LT: return "<";
case ebpf::bpftrace::Parser::token::GT: return ">";
case ebpf::bpftrace::Parser::token::LAND: return "&&";
case ebpf::bpftrace::Parser::token::LOR: return "||";
case ebpf::bpftrace::Parser::token::PLUS: return "+";
case ebpf::bpftrace::Parser::token::MINUS: return "-";
case ebpf::bpftrace::Parser::token::MUL: return "*";
case ebpf::bpftrace::Parser::token::DIV: return "/";
case ebpf::bpftrace::Parser::token::MOD: return "%";
case ebpf::bpftrace::Parser::token::BAND: return "&";
case ebpf::bpftrace::Parser::token::BOR: return "|";
case ebpf::bpftrace::Parser::token::BXOR: return "^";
default: abort();
}
}
std::string opstr(Unop &unop)
{
switch (unop.op) {
case ebpf::bpftrace::Parser::token::LNOT: return "!";
case ebpf::bpftrace::Parser::token::BNOT: return "~";
default: abort();
}
}
} // namespace ast } // namespace ast
} // namespace bpftrace } // namespace bpftrace
} // namespace ebpf } // namespace ebpf
...@@ -153,6 +153,9 @@ public: ...@@ -153,6 +153,9 @@ public:
virtual void visit(Program &program) = 0; virtual void visit(Program &program) = 0;
}; };
std::string opstr(Binop &binop);
std::string opstr(Unop &unop);
} // namespace ast } // namespace ast
} // namespace bpftrace } // namespace bpftrace
} // namespace ebpf } // namespace ebpf
#include "printer.h" #include "printer.h"
#include "ast.h" #include "ast.h"
#include "parser.tab.hh"
namespace ebpf { namespace ebpf {
namespace bpftrace { namespace bpftrace {
...@@ -49,28 +48,7 @@ void Printer::visit(Map &map) ...@@ -49,28 +48,7 @@ void Printer::visit(Map &map)
void Printer::visit(Binop &binop) void Printer::visit(Binop &binop)
{ {
std::string indent(depth_, ' '); std::string indent(depth_, ' ');
std::string opstr; out_ << indent << opstr(binop) << std::endl;
switch (binop.op) {
case ebpf::bpftrace::Parser::token::EQ: opstr = "=="; break;
case ebpf::bpftrace::Parser::token::NE: opstr = "!="; break;
case ebpf::bpftrace::Parser::token::LE: opstr = "<="; break;
case ebpf::bpftrace::Parser::token::GE: opstr = ">="; break;
case ebpf::bpftrace::Parser::token::LT: opstr = "<"; break;
case ebpf::bpftrace::Parser::token::GT: opstr = ">"; break;
case ebpf::bpftrace::Parser::token::LAND: opstr = "&&"; break;
case ebpf::bpftrace::Parser::token::LOR: opstr = "||"; break;
case ebpf::bpftrace::Parser::token::PLUS: opstr = "+"; break;
case ebpf::bpftrace::Parser::token::MINUS: opstr = "-"; break;
case ebpf::bpftrace::Parser::token::MUL: opstr = "*"; break;
case ebpf::bpftrace::Parser::token::DIV: opstr = "/"; break;
case ebpf::bpftrace::Parser::token::MOD: opstr = "%"; break;
case ebpf::bpftrace::Parser::token::BAND: opstr = "&"; break;
case ebpf::bpftrace::Parser::token::BOR: opstr = "|"; break;
case ebpf::bpftrace::Parser::token::BXOR: opstr = "^"; break;
default: abort();
}
out_ << indent << opstr << std::endl;
++depth_; ++depth_;
binop.left->accept(*this); binop.left->accept(*this);
...@@ -81,14 +59,7 @@ void Printer::visit(Binop &binop) ...@@ -81,14 +59,7 @@ void Printer::visit(Binop &binop)
void Printer::visit(Unop &unop) void Printer::visit(Unop &unop)
{ {
std::string indent(depth_, ' '); std::string indent(depth_, ' ');
std::string opstr; out_ << indent << opstr(unop) << std::endl;
switch (unop.op) {
case ebpf::bpftrace::Parser::token::LNOT: opstr = "!"; break;
case ebpf::bpftrace::Parser::token::BNOT: opstr = "~"; break;
default: abort();
}
out_ << indent << opstr << std::endl;
++depth_; ++depth_;
unop.expr->accept(*this); unop.expr->accept(*this);
......
...@@ -28,16 +28,28 @@ void SemanticAnalyser::visit(Builtin &builtin) ...@@ -28,16 +28,28 @@ void SemanticAnalyser::visit(Builtin &builtin)
void SemanticAnalyser::visit(Call &call) void SemanticAnalyser::visit(Call &call)
{ {
int nargs = 0;
if (call.vargs) { if (call.vargs) {
nargs = call.vargs->size();
for (Expression *expr : *call.vargs) { for (Expression *expr : *call.vargs) {
expr->accept(*this); expr->accept(*this);
} }
} }
if (call.func == "quantize") if (call.func == "quantize") {
type_ = Type::quantize; type_ = Type::quantize;
else if (call.func == "count") if (nargs != 1) {
err_ << "quantize() should take 1 argument (";
err_ << nargs << " provided)" << std::endl;
}
}
else if (call.func == "count") {
type_ = Type::count; type_ = Type::count;
if (nargs != 0) {
err_ << "count() should take 0 arguments (";
err_ << nargs << " provided)" << std::endl;
}
}
else { else {
type_ = Type::none; type_ = Type::none;
err_ << "Unknown function: '" << call.func << "'" << std::endl; err_ << "Unknown function: '" << call.func << "'" << std::endl;
...@@ -60,50 +72,39 @@ void SemanticAnalyser::visit(Map &map) ...@@ -60,50 +72,39 @@ void SemanticAnalyser::visit(Map &map)
err_ << "Argument mismatch for " << map.ident << ": "; err_ << "Argument mismatch for " << map.ident << ": ";
err_ << "trying to access with arguments: [ "; err_ << "trying to access with arguments: [ ";
for (Type t : args) { err_ << typestr(t) << " "; } for (Type t : args) { err_ << typestr(t) << " "; }
err_ << "]" << std::endl; err_ << "]\n\twhen map already uses the arguments: [ ";
err_ << "when map already uses the arguments: [ ";
for (Type t : search->second) { err_ << typestr(t) << " "; } for (Type t : search->second) { err_ << typestr(t) << " "; }
err_ << "]" << std::endl; err_ << "]\n" << std::endl;
} }
} }
else { else {
map_args_.insert({map.ident, args}); map_args_.insert({map.ident, args});
} }
type_ = map_val_.find(map.ident)->second;
} }
void SemanticAnalyser::visit(Binop &binop) void SemanticAnalyser::visit(Binop &binop)
{ {
Type lhs, rhs;
binop.left->accept(*this); binop.left->accept(*this);
switch (binop.op) { lhs = type_;
case ebpf::bpftrace::Parser::token::EQ: break;
case ebpf::bpftrace::Parser::token::NE: break;
case ebpf::bpftrace::Parser::token::LE: break;
case ebpf::bpftrace::Parser::token::GE: break;
case ebpf::bpftrace::Parser::token::LT: break;
case ebpf::bpftrace::Parser::token::GT: break;
case ebpf::bpftrace::Parser::token::LAND: break;
case ebpf::bpftrace::Parser::token::LOR: break;
case ebpf::bpftrace::Parser::token::PLUS: break;
case ebpf::bpftrace::Parser::token::MINUS: break;
case ebpf::bpftrace::Parser::token::MUL: break;
case ebpf::bpftrace::Parser::token::DIV: break;
case ebpf::bpftrace::Parser::token::MOD: break;
case ebpf::bpftrace::Parser::token::BAND: break;
case ebpf::bpftrace::Parser::token::BOR: break;
case ebpf::bpftrace::Parser::token::BXOR: break;
default: abort();
}
binop.right->accept(*this); binop.right->accept(*this);
rhs = type_;
if (pass_ == 2 && lhs != rhs) {
err_ << "Type mismatch for '" << opstr(binop) << "': ";
err_ << "comparing '" << typestr(lhs) << "' ";
err_ << "with '" << typestr(rhs) << "'" << std::endl;
}
type_ = Type::integer;
} }
void SemanticAnalyser::visit(Unop &unop) void SemanticAnalyser::visit(Unop &unop)
{ {
switch (unop.op) {
case ebpf::bpftrace::Parser::token::LNOT: break;
case ebpf::bpftrace::Parser::token::BNOT: break;
default: abort();
}
unop.expr->accept(*this); unop.expr->accept(*this);
type_ = Type::integer;
} }
void SemanticAnalyser::visit(ExprStatement &expr) void SemanticAnalyser::visit(ExprStatement &expr)
...@@ -122,8 +123,8 @@ void SemanticAnalyser::visit(AssignMapStatement &assignment) ...@@ -122,8 +123,8 @@ void SemanticAnalyser::visit(AssignMapStatement &assignment)
if (search->second != type_) { if (search->second != type_) {
err_ << "Type mismatch for " << map_ident << ": "; err_ << "Type mismatch for " << map_ident << ": ";
err_ << "trying to assign variable of type '" << typestr(type_); err_ << "trying to assign variable of type '" << typestr(type_);
err_ << "' when map already contains a '"; err_ << "'\n\twhen map already contains a value of type '";
err_ << typestr(search->second) << "'" << std::endl; err_ << typestr(search->second) << "'\n" << std::endl;
} }
} }
else { else {
...@@ -143,9 +144,8 @@ void SemanticAnalyser::visit(AssignMapCallStatement &assignment) ...@@ -143,9 +144,8 @@ void SemanticAnalyser::visit(AssignMapCallStatement &assignment)
if (search->second != type_) { if (search->second != type_) {
err_ << "Type mismatch for " << map_ident << ": "; err_ << "Type mismatch for " << map_ident << ": ";
err_ << "trying to assign result of '" << assignment.call->func; err_ << "trying to assign result of '" << assignment.call->func;
err_ << "'" << typestr(type_); err_ << "()'\n\twhen map already contains a value of type '";
err_ << "' when map already contains a '"; err_ << typestr(search->second) << "'\n" << std::endl;
err_ << typestr(search->second) << "'" << std::endl;
} }
} }
else { else {
...@@ -178,16 +178,21 @@ void SemanticAnalyser::visit(Program &program) ...@@ -178,16 +178,21 @@ void SemanticAnalyser::visit(Program &program)
int SemanticAnalyser::analyse() int SemanticAnalyser::analyse()
{ {
root_->accept(*this); // Two pass analysis, to handle variables being used before they are defined:
// - First pass checks assignments
std::string errors = err_.str(); // - Second pass checks expressions
if (errors.empty()) { std::string errors;
return 0;
} for (pass_ = 1; pass_ <= 2; pass_++) {
else { root_->accept(*this);
std::cerr << errors; errors = err_.str();
return 1; if (!errors.empty()) {
std::cerr << errors;
return pass_;
}
} }
return 0;
} }
std::string SemanticAnalyser::typestr(Type t) std::string SemanticAnalyser::typestr(Type t)
......
...@@ -32,6 +32,7 @@ public: ...@@ -32,6 +32,7 @@ public:
private: private:
Node *root_; Node *root_;
std::ostringstream err_; std::ostringstream err_;
int pass_;
enum class Type enum class Type
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment