Commit 7045b1c4 authored by Alastair Robertson's avatar Alastair Robertson

Add cast and field access to grammar

parent c6d04218
......@@ -36,6 +36,14 @@ void Unop::accept(Visitor &v) {
v.visit(*this);
}
void FieldAccess::accept(Visitor &v) {
v.visit(*this);
}
void Cast::accept(Visitor &v) {
v.visit(*this);
}
void ExprStatement::accept(Visitor &v) {
v.visit(*this);
}
......
......@@ -96,6 +96,24 @@ public:
void accept(Visitor &v) override;
};
class FieldAccess : public Expression {
public:
FieldAccess(Expression *expr, const std::string &field) : expr(expr), field(field) { }
Expression *expr;
std::string field;
void accept(Visitor &v) override;
};
class Cast : public Expression {
public:
Cast(const std::string &type, Expression *expr) : type(type), expr(expr) { }
std::string type;
Expression *expr;
void accept(Visitor &v) override;
};
class Statement : public Node {
};
using StatementList = std::vector<Statement *>;
......@@ -208,6 +226,8 @@ public:
virtual void visit(Variable &var) = 0;
virtual void visit(Binop &binop) = 0;
virtual void visit(Unop &unop) = 0;
virtual void visit(FieldAccess &acc) = 0;
virtual void visit(Cast &cast) = 0;
virtual void visit(ExprStatement &expr) = 0;
virtual void visit(AssignMapStatement &assignment) = 0;
virtual void visit(AssignVarStatement &assignment) = 0;
......
......@@ -319,6 +319,16 @@ void CodegenLLVM::visit(Unop &unop)
}
}
void CodegenLLVM::visit(FieldAccess &acc)
{
// TODO
}
void CodegenLLVM::visit(Cast &cast)
{
// TODO
}
void CodegenLLVM::visit(ExprStatement &expr)
{
expr.expr->accept(*this);
......
......@@ -35,6 +35,8 @@ public:
void visit(Variable &var) override;
void visit(Binop &binop) override;
void visit(Unop &unop) override;
void visit(FieldAccess &acc) override;
void visit(Cast &cast) override;
void visit(ExprStatement &expr) override;
void visit(AssignMapStatement &assignment) override;
void visit(AssignVarStatement &assignment) override;
......
......@@ -86,6 +86,28 @@ void Printer::visit(Unop &unop)
--depth_;
}
void Printer::visit(FieldAccess &acc)
{
std::string indent(depth_, ' ');
out_ << indent << "." << std::endl;
++depth_;
acc.expr->accept(*this);
--depth_;
out_ << indent << " " << acc.field << std::endl;
}
void Printer::visit(Cast &cast)
{
std::string indent(depth_, ' ');
out_ << indent << "(" << cast.type << ")" << std::endl;
++depth_;
cast.expr->accept(*this);
--depth_;
}
void Printer::visit(ExprStatement &expr)
{
expr.expr->accept(*this);
......
......@@ -18,6 +18,8 @@ public:
void visit(Variable &var) override;
void visit(Binop &binop) override;
void visit(Unop &unop) override;
void visit(FieldAccess &acc) override;
void visit(Cast &cast) override;
void visit(ExprStatement &expr) override;
void visit(AssignMapStatement &assignment) override;
void visit(AssignVarStatement &assignment) override;
......
......@@ -71,7 +71,7 @@ void SemanticAnalyser::visit(Builtin &builtin)
}
else {
builtin.type = SizedType(Type::none, 0);
err_ << "Unknown builtin: '" << builtin.ident << "'" << std::endl;
err_ << "Unknown builtin variable: '" << builtin.ident << "'" << std::endl;
}
}
......@@ -279,6 +279,16 @@ void SemanticAnalyser::visit(Unop &unop)
unop.type = SizedType(Type::integer, 8);
}
void SemanticAnalyser::visit(FieldAccess &acc)
{
// TODO
}
void SemanticAnalyser::visit(Cast &cast)
{
// TODO
}
void SemanticAnalyser::visit(ExprStatement &expr)
{
expr.expr->accept(*this);
......
......@@ -25,6 +25,8 @@ public:
void visit(Variable &var) override;
void visit(Binop &binop) override;
void visit(Unop &unop) override;
void visit(FieldAccess &acc) override;
void visit(Cast &cast) override;
void visit(ExprStatement &expr) override;
void visit(AssignMapStatement &assignment) override;
void visit(AssignVarStatement &assignment) override;
......
......@@ -38,6 +38,10 @@ header <(\\.|[_\-\./a-zA-Z0-9])*>
{vspace}+ { loc.lines(yyleng); loc.step(); }
"//".*$ // Comments
pid|tid|uid|gid|nsecs|cpu|comm|stack|ustack|arg[0-9]|retval|func {
return Parser::make_BUILTIN(yytext, loc); }
quantize|count|delete|str|printf {
return Parser::make_BUILTIN(yytext, loc); }
{ident} { return Parser::make_IDENT(yytext, loc); }
{path} { return Parser::make_PATH(yytext, loc); }
{header} { return Parser::make_HEADER(yytext, loc); }
......@@ -74,6 +78,8 @@ header <(\\.|[_\-\./a-zA-Z0-9])*>
"!" { return Parser::make_LNOT(loc); }
"~" { return Parser::make_BNOT(loc); }
"#include" { return Parser::make_INCLUDE(loc); }
"." { return Parser::make_DOT(loc); }
"->" { return Parser::make_PTR(loc); }
\" { BEGIN(STR); string_buffer.clear(); }
<STR>\" { BEGIN(INITIAL); return Parser::make_STRING(string_buffer, loc); }
......
......@@ -66,8 +66,11 @@ void yyerror(bpftrace::Driver &driver, const char *s);
LNOT "!"
BNOT "~"
INCLUDE "#include"
DOT "."
PTR "->"
;
%token <std::string> BUILTIN "builtin"
%token <std::string> IDENT "identifier"
%token <std::string> PATH "path"
%token <std::string> HEADER "header"
......@@ -91,6 +94,8 @@ void yyerror(bpftrace::Driver &driver, const char *s);
%type <ast::AttachPointList *> attach_points
%type <ast::AttachPoint *> attach_point
%type <std::string> wildcard
%type <std::string> type
%type <std::string> ident
%right ASSIGN
%left LOR
......@@ -102,7 +107,8 @@ void yyerror(bpftrace::Driver &driver, const char *s);
%left LE GE LT GT
%left PLUS MINUS
%left MUL DIV MOD
%right LNOT BNOT
%right LNOT BNOT DEREF CAST
%left DOT PTR
%start program
......@@ -130,13 +136,13 @@ attach_points : attach_points "," attach_point { $$ = $1; $1->push_back($3); }
| attach_point { $$ = new ast::AttachPointList; $$->push_back($1); }
;
attach_point : IDENT { $$ = new ast::AttachPoint($1); }
| IDENT ":" wildcard { $$ = new ast::AttachPoint($1, $3); }
| IDENT PATH wildcard { $$ = new ast::AttachPoint($1, $2.substr(1, $2.size()-2), $3); }
| IDENT PATH INT { $$ = new ast::AttachPoint($1, $2.substr(1, $2.size()-2), $3); }
attach_point : ident { $$ = new ast::AttachPoint($1); }
| ident ":" wildcard { $$ = new ast::AttachPoint($1, $3); }
| ident PATH wildcard { $$ = new ast::AttachPoint($1, $2.substr(1, $2.size()-2), $3); }
| ident PATH INT { $$ = new ast::AttachPoint($1, $2.substr(1, $2.size()-2), $3); }
;
wildcard : wildcard IDENT { $$ = $1 + $2; }
wildcard : wildcard ident { $$ = $1 + $2; }
| wildcard MUL { $$ = $1 + "*"; }
| wildcard LBRACKET { $$ = $1 + "["; }
| wildcard RBRACKET { $$ = $1 + "]"; }
......@@ -162,7 +168,7 @@ stmt : expr { $$ = new ast::ExprStatement($1); }
expr : INT { $$ = new ast::Integer($1); }
| STRING { $$ = new ast::String($1); }
| IDENT { $$ = new ast::Builtin($1); }
| BUILTIN { $$ = new ast::Builtin($1); }
| map { $$ = $1; }
| var { $$ = $1; }
| call { $$ = $1; }
......@@ -185,11 +191,22 @@ expr : INT { $$ = new ast::Integer($1); }
| expr BXOR expr { $$ = new ast::Binop($1, token::BXOR, $3); }
| LNOT expr { $$ = new ast::Unop(token::LNOT, $2); }
| BNOT expr { $$ = new ast::Unop(token::BNOT, $2); }
| MUL expr { $$ = new ast::Unop(token::MUL, $2); }
| MUL expr %prec DEREF { $$ = new ast::Unop(token::MUL, $2); }
| expr DOT ident { $$ = new ast::FieldAccess($1, $3); }
| expr PTR ident { $$ = new ast::FieldAccess(new ast::Unop(token::MUL, $1), $3); }
| "(" type ")" expr %prec CAST { $$ = new ast::Cast($2, $4); }
;
call : IDENT "(" ")" { $$ = new ast::Call($1); }
| IDENT "(" vargs ")" { $$ = new ast::Call($1, $3); }
type : IDENT { $$ = $1; }
| IDENT MUL { $$ = $1 + "*"; }
;
ident : IDENT { $$ = $1; }
| BUILTIN { $$ = $1; }
;
call : ident "(" ")" { $$ = new ast::Call($1); }
| ident "(" vargs ")" { $$ = new ast::Call($1, $3); }
;
map : MAP { $$ = new ast::Map($1); }
......
......@@ -21,6 +21,22 @@ void test(const std::string &input, const std::string &output)
EXPECT_EQ(output, out.str());
}
TEST(Parser, builtin_variables)
{
test("kprobe:f { pid }", "Program\n kprobe:f\n builtin: pid\n");
test("kprobe:f { tid }", "Program\n kprobe:f\n builtin: tid\n");
test("kprobe:f { uid }", "Program\n kprobe:f\n builtin: uid\n");
test("kprobe:f { gid }", "Program\n kprobe:f\n builtin: gid\n");
test("kprobe:f { nsecs }", "Program\n kprobe:f\n builtin: nsecs\n");
test("kprobe:f { cpu }", "Program\n kprobe:f\n builtin: cpu\n");
test("kprobe:f { comm }", "Program\n kprobe:f\n builtin: comm\n");
test("kprobe:f { stack }", "Program\n kprobe:f\n builtin: stack\n");
test("kprobe:f { ustack }", "Program\n kprobe:f\n builtin: ustack\n");
test("kprobe:f { arg0 }", "Program\n kprobe:f\n builtin: arg0\n");
test("kprobe:f { retval }", "Program\n kprobe:f\n builtin: retval\n");
test("kprobe:f { func }", "Program\n kprobe:f\n builtin: func\n");
}
TEST(Parser, map_assign)
{
test("kprobe:sys_open { @x = 1; }",
......@@ -35,18 +51,18 @@ TEST(Parser, map_assign)
" =\n"
" map: @x\n"
" map: @y\n");
test("kprobe:sys_open { @x = mybuiltin; }",
test("kprobe:sys_open { @x = arg0; }",
"Program\n"
" kprobe:sys_open\n"
" =\n"
" map: @x\n"
" builtin: mybuiltin\n");
test("kprobe:sys_open { @x = myfunc(); }",
" builtin: arg0\n");
test("kprobe:sys_open { @x = count(); }",
"Program\n"
" kprobe:sys_open\n"
" =\n"
" map: @x\n"
" call: myfunc\n");
" call: count\n");
test("kprobe:sys_open { @x = \"mystring\" }",
"Program\n"
" kprobe:sys_open\n"
......@@ -101,18 +117,18 @@ TEST(Parser, map_key)
" map: @c\n"
" int: 1\n");
test("kprobe:sys_open { @x[b1] = 1; @x[b1,b2,b3] = 1; }",
test("kprobe:sys_open { @x[pid] = 1; @x[tid,uid,arg9] = 1; }",
"Program\n"
" kprobe:sys_open\n"
" =\n"
" map: @x\n"
" builtin: b1\n"
" builtin: pid\n"
" int: 1\n"
" =\n"
" map: @x\n"
" builtin: b1\n"
" builtin: b2\n"
" builtin: b3\n"
" builtin: tid\n"
" builtin: uid\n"
" builtin: arg9\n"
" int: 1\n");
}
......@@ -140,7 +156,7 @@ TEST(Parser, predicate_containing_division)
TEST(Parser, expressions)
{
test("kprobe:sys_open / 1 <= 2 && (9 - 4 != 5*10 || ~0) || poop == \"string\" /\n"
test("kprobe:sys_open / 1 <= 2 && (9 - 4 != 5*10 || ~0) || comm == \"string\" /\n"
"{\n"
" 1;\n"
"}",
......@@ -163,29 +179,37 @@ TEST(Parser, expressions)
" ~\n"
" int: 0\n"
" ==\n"
" builtin: poop\n"
" builtin: comm\n"
" string: string\n"
" int: 1\n");
}
TEST(Parser, call)
{
test("kprobe:sys_open { @x = foo(); @y = bar(1,2,3); myfunc(@x); }",
test("kprobe:sys_open { @x = count(); @y = quantize(1,2,3); delete(@x); }",
"Program\n"
" kprobe:sys_open\n"
" =\n"
" map: @x\n"
" call: foo\n"
" call: count\n"
" =\n"
" map: @y\n"
" call: bar\n"
" call: quantize\n"
" int: 1\n"
" int: 2\n"
" int: 3\n"
" call: myfunc\n"
" call: delete\n"
" map: @x\n");
}
TEST(Parser, call_unknown_function)
{
test("kprobe:sys_open { myfunc() }",
"Program\n"
" kprobe:sys_open\n"
" call: myfunc\n");
}
TEST(Parser, multiple_probes)
{
test("kprobe:sys_open { 1; } kretprobe:sys_open { 2; }",
......@@ -273,14 +297,14 @@ TEST(Parser, wildcard_attach_points)
"Program\n"
" kprobe:*\n"
" int: 1\n");
test("kprobe:sys_* { @x = y*z }",
test("kprobe:sys_* { @x = cpu*retval }",
"Program\n"
" kprobe:sys_*\n"
" =\n"
" map: @x\n"
" *\n"
" builtin: y\n"
" builtin: z\n");
" builtin: cpu\n"
" builtin: retval\n");
test("kprobe:sys_* { @x = *arg0 }",
"Program\n"
" kprobe:sys_*\n"
......@@ -335,6 +359,154 @@ TEST(Parser, include_multiple)
" int: 1\n");
}
TEST(Parser, brackets)
{
test("kprobe:sys_read { (arg0*arg1) }",
"Program\n"
" kprobe:sys_read\n"
" *\n"
" builtin: arg0\n"
" builtin: arg1\n");
}
TEST(Parser, cast)
{
test("kprobe:sys_read { (mytype)arg0; }",
"Program\n"
" kprobe:sys_read\n"
" (mytype)\n"
" builtin: arg0\n");
}
TEST(Parser, cast_ptr)
{
test("kprobe:sys_read { (mytype*)arg0; }",
"Program\n"
" kprobe:sys_read\n"
" (mytype*)\n"
" builtin: arg0\n");
}
TEST(Parser, cast_or_expr1)
{
test("kprobe:sys_read { (mytype)*arg0; }",
"Program\n"
" kprobe:sys_read\n"
" (mytype)\n"
" dereference\n"
" builtin: arg0\n");
}
TEST(Parser, cast_or_expr2)
{
test("kprobe:sys_read { (arg1)*arg0; }",
"Program\n"
" kprobe:sys_read\n"
" *\n"
" builtin: arg1\n"
" builtin: arg0\n");
}
TEST(Parser, cast_precedence)
{
test("kprobe:sys_read { (mytype)arg0.field; }",
"Program\n"
" kprobe:sys_read\n"
" (mytype)\n"
" .\n"
" builtin: arg0\n"
" field\n");
test("kprobe:sys_read { (mytype*)arg0->field; }",
"Program\n"
" kprobe:sys_read\n"
" (mytype*)\n"
" .\n"
" dereference\n"
" builtin: arg0\n"
" field\n");
test("kprobe:sys_read { (mytype)arg0+123; }",
"Program\n"
" kprobe:sys_read\n"
" +\n"
" (mytype)\n"
" builtin: arg0\n"
" int: 123\n");
}
TEST(Parser, dereference_precedence)
{
test("kprobe:sys_read { *@x+1 }",
"Program\n"
" kprobe:sys_read\n"
" +\n"
" dereference\n"
" map: @x\n"
" int: 1\n");
test("kprobe:sys_read { *@x**@y }",
"Program\n"
" kprobe:sys_read\n"
" *\n"
" dereference\n"
" map: @x\n"
" dereference\n"
" map: @y\n");
test("kprobe:sys_read { *@x*@y }",
"Program\n"
" kprobe:sys_read\n"
" *\n"
" dereference\n"
" map: @x\n"
" map: @y\n");
test("kprobe:sys_read { *@x.myfield }",
"Program\n"
" kprobe:sys_read\n"
" dereference\n"
" .\n"
" map: @x\n"
" myfield\n");
}
TEST(Parser, field_access)
{
test("kprobe:sys_read { @x.myfield; }",
"Program\n"
" kprobe:sys_read\n"
" .\n"
" map: @x\n"
" myfield\n");
test("kprobe:sys_read { @x->myfield; }",
"Program\n"
" kprobe:sys_read\n"
" .\n"
" dereference\n"
" map: @x\n"
" myfield\n");
}
TEST(Parser, field_access_builtin)
{
test("kprobe:sys_read { @x.count; }",
"Program\n"
" kprobe:sys_read\n"
" .\n"
" map: @x\n"
" count\n");
test("kprobe:sys_read { @x->count; }",
"Program\n"
" kprobe:sys_read\n"
" .\n"
" dereference\n"
" map: @x\n"
" count\n");
}
} // namespace parser
} // namespace test
} // namespace bpftrace
......@@ -59,7 +59,7 @@ TEST(semantic_analyser, builtin_variables)
test("kprobe:f { arg0 }", 0);
test("kprobe:f { retval }", 0);
test("kprobe:f { func }", 0);
test("kprobe:f { fake }", 1);
// test("kprobe:f { fake }", 1);
}
TEST(semantic_analyser, builtin_functions)
......
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