Skip to content

Commit 24935c9

Browse files
authored
Merge pull request github#12285 from github/calumgrant/ql-ra-parser
RA parser first draft
2 parents 7dc5e7e + 0fdcf03 commit 24935c9

File tree

3 files changed

+170
-0
lines changed

3 files changed

+170
-0
lines changed

ql/ql/src/experimental/RA.qll

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/**
2+
* Parses RA expressions.
3+
*/
4+
5+
/**
6+
* A predicate that contains RA.
7+
*/
8+
signature class RApredicate {
9+
string getLineOfRA(int n);
10+
}
11+
12+
/**
13+
* Parses strings of RA provided by an RA predicate, and represented the
14+
*/
15+
module RAParser<RApredicate Predicate> {
16+
private string parseRaExpr(Predicate p, int line, int arity, int lhs) {
17+
exists(string str | str = p.getLineOfRA(line).trim() |
18+
arity = str.regexpCapture("\\{([0-9]+)\\} r([0-9]+) = (.+)", 1).toInt() and
19+
lhs = str.regexpCapture("\\{([0-9]+)\\} r([0-9]+) = (.+)", 2).toInt() and
20+
result = str.regexpCapture("\\{([0-9]+)\\} r([0-9]+) = (.+)", 3)
21+
)
22+
}
23+
24+
bindingset[str]
25+
private int parseReturn(string str) {
26+
result = str.trim().regexpCapture("return r([0-9]+)", 1).toInt()
27+
}
28+
29+
private newtype TRA =
30+
TReturn(Predicate p, int line, int v) { v = parseReturn(p.getLineOfRA(line)) } or
31+
TUnknown(Predicate p, int line, int lhs, int arity, string rhs) {
32+
rhs = parseRaExpr(p, line, arity, lhs)
33+
}
34+
35+
/** An RA Expression. */
36+
abstract class RAExpr extends TRA {
37+
/** Gets the predicate this RA expression belongs to. */
38+
abstract Predicate getPredicate();
39+
40+
/** Gets the line index of this RA expression. */
41+
abstract int getLine();
42+
43+
/** Gets the LHS of the expression of the form `rNN = ...` */
44+
abstract int getLhs();
45+
46+
/** Gets a variable of the form `rNN` in the RHS of the RA expression. */
47+
abstract int getARhsVariable();
48+
49+
/** Gets the given arity of the RA expression. */
50+
abstract int getArity();
51+
52+
/** Gets a predicate name referenced in the RHS of an RA expression. */
53+
abstract string getARhsPredicate();
54+
55+
final string toString() { result = this.getPredicate().getLineOfRA(this.getLine()) }
56+
57+
/** Gets a child of this RA expression - not by index yet. */
58+
RAExpr getAChild() {
59+
result.getPredicate() = this.getPredicate() and result.getLhs() = this.getARhsVariable()
60+
}
61+
}
62+
63+
/**
64+
* A generic RA expression - where we haven't precisely parsed the RA expression type.
65+
* For Hackathon purposes, we probably don't need more than this.
66+
*/
67+
class RAUnknownExpr extends RAExpr, TUnknown {
68+
Predicate p;
69+
int line;
70+
string rhs;
71+
int arity;
72+
int lhs;
73+
74+
RAUnknownExpr() { this = TUnknown(p, line, lhs, arity, rhs) }
75+
76+
override int getLine() { result = line }
77+
78+
override Predicate getPredicate() { result = p }
79+
80+
override int getLhs() { result = lhs }
81+
82+
override int getARhsVariable() {
83+
result = rhs.splitAt(" ").regexpCapture("r([0-9]+)", 1).toInt()
84+
}
85+
86+
// This is a dumb regex to find a predicate name - they always contain a `#` (TODO...)
87+
override string getARhsPredicate() { result = rhs.splitAt(" ") and result.indexOf("#") > 0 }
88+
89+
override int getArity() { result = arity }
90+
}
91+
92+
class RAReturnExpr extends RAExpr, TReturn {
93+
RAReturnExpr() { this = TReturn(p, line, res) }
94+
95+
Predicate p;
96+
int line;
97+
int res;
98+
99+
override Predicate getPredicate() { result = p }
100+
101+
override int getLine() { result = line }
102+
103+
override int getLhs() { none() }
104+
105+
override int getARhsVariable() { result = res }
106+
107+
override int getArity() { none() }
108+
109+
override string getARhsPredicate() { none() }
110+
}
111+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
children
2+
| return r7 | {4} r7 = r3 UNION r6 |
3+
| {4} r2 = JOIN r1 WITH raparser#d14bb8db::TestPredicate#b ON FIRST 1 OUTPUT "p1", toString("p1"), 123, " r1 = SCAN fubar\\n r1" | {1} r1 = CONSTANT(unique string)["p1"] |
4+
| {4} r3 = STREAM DEDUP r2 | {4} r2 = JOIN r1 WITH raparser#d14bb8db::TestPredicate#b ON FIRST 1 OUTPUT "p1", toString("p1"), 123, " r1 = SCAN fubar\\n r1" |
5+
| {4} r4 = JOIN r1 WITH raparser#d14bb8db::TestPredicate#b ON FIRST 1 OUTPUT "p1", "(no string representation)", 123, " r1 = SCAN fubar\\n r1" | {1} r1 = CONSTANT(unique string)["p1"] |
6+
| {4} r5 = STREAM DEDUP r4 | {4} r4 = JOIN r1 WITH raparser#d14bb8db::TestPredicate#b ON FIRST 1 OUTPUT "p1", "(no string representation)", 123, " r1 = SCAN fubar\\n r1" |
7+
| {4} r6 = r5 AND NOT project##select#query#ffff#nullary({}) | {4} r5 = STREAM DEDUP r4 |
8+
| {4} r7 = r3 UNION r6 | {4} r3 = STREAM DEDUP r2 |
9+
| {4} r7 = r3 UNION r6 | {4} r6 = r5 AND NOT project##select#query#ffff#nullary({}) |
10+
#select
11+
| p1 | 1 | {1} r1 = CONSTANT(unique string)["p1"] | 1 | 1 | 0 | 0 |
12+
| p1 | 3 | {4} r2 = JOIN r1 WITH raparser#d14bb8db::TestPredicate#b ON FIRST 1 OUTPUT "p1", toString("p1"), 123, " r1 = SCAN fubar\\n r1" | 2 | 4 | 1 | 1 |
13+
| p1 | 5 | {4} r3 = STREAM DEDUP r2 | 3 | 4 | 0 | 1 |
14+
| p1 | 6 | {4} r4 = JOIN r1 WITH raparser#d14bb8db::TestPredicate#b ON FIRST 1 OUTPUT "p1", "(no string representation)", 123, " r1 = SCAN fubar\\n r1" | 4 | 4 | 1 | 1 |
15+
| p1 | 7 | {4} r5 = STREAM DEDUP r4 | 5 | 4 | 0 | 1 |
16+
| p1 | 8 | {4} r6 = r5 AND NOT project##select#query#ffff#nullary({}) | 6 | 4 | 1 | 1 |
17+
| p1 | 9 | {4} r7 = r3 UNION r6 | 7 | 4 | 0 | 2 |

ql/ql/test/experimental/raparser.ql

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import experimental.RA
2+
3+
class TestPredicate extends string {
4+
TestPredicate() { this = "p1" }
5+
6+
string getLineOfRA(int line) {
7+
line = 1 and
8+
result = " {1} r1 = CONSTANT(unique string)[\"p1\"]"
9+
or
10+
line = 2 and result = ""
11+
or
12+
line = 3 and
13+
result =
14+
" {4} r2 = JOIN r1 WITH raparser#d14bb8db::TestPredicate#b ON FIRST 1 OUTPUT \"p1\", toString(\"p1\"), 123, \" r1 = SCAN fubar\\n r1\""
15+
or
16+
line = 4 and result = ""
17+
or
18+
line = 5 and result = " {4} r3 = STREAM DEDUP r2"
19+
or
20+
line = 6 and
21+
result =
22+
" {4} r4 = JOIN r1 WITH raparser#d14bb8db::TestPredicate#b ON FIRST 1 OUTPUT \"p1\", \"(no string representation)\", 123, \" r1 = SCAN fubar\\n r1\""
23+
or
24+
line = 7 and result = " {4} r5 = STREAM DEDUP r4"
25+
or
26+
line = 8 and result = " {4} r6 = r5 AND NOT project##select#query#ffff#nullary({})"
27+
or
28+
line = 9 and result = " {4} r7 = r3 UNION r6"
29+
or
30+
line = 10 and result = " return r7"
31+
}
32+
}
33+
34+
query predicate children(
35+
RAParser<TestPredicate>::RAExpr parent, RAParser<TestPredicate>::RAExpr child
36+
) {
37+
child = parent.getAChild()
38+
}
39+
40+
from RAParser<TestPredicate>::RAExpr expr
41+
select expr.getPredicate(), expr.getLine(), expr, expr.getLhs(), expr.getArity(),
42+
count(expr.getARhsPredicate()), count(expr.getARhsVariable())

0 commit comments

Comments
 (0)