-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTaggedString.qll
More file actions
91 lines (84 loc) · 2.82 KB
/
TaggedString.qll
File metadata and controls
91 lines (84 loc) · 2.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
private import qtil.parameterization.SignatureTypes
private import qtil.inheritance.UnderlyingString
private import qtil.parameterization.Finalize
/**
* A module for creating strings with an enum-like tag value.
*
* To use this module, create a type which is uniquely identified by its `toString()` member
* predicate. Then you may refer to a tagged string by type `Tagged<YourTag>::String`.
*
* See `Tagged::String` for more details.
*/
module Tagged<FiniteStringableType Tag> {
/**
* A class for representing strings with an enum-like tag value.
*
* To use this class, define a type which is uniquely identified by its `toString()` member
* predicate. Then refer to this class as `Tagged<YourTag>::String`.
*
* REQUIREMENTS: The toString() method of a tag must return non-overlapping results; no toString()
* may begin with or equal the toString() result from another instance (for instance, `"foo"` and
* `"foobar"` would be considered overlapping). The Tag type must be finite (not have
* `bindingset[this]`) and the toString() member predicate must be reversable (not have
* `bindingset[this]`).
*
* Tagged strings may be "constructed" by calling `s.make(tag, str)`, or with
* `s.make(str).getTag() = ...`.
*
* Example usage:
* ```ql
* newtype TMyEnum = TOptionA() or TOptionB();
* class MyEnum extends TMyEnum {
* string toString() {
* this = TOptionA() and result = "option_a"
* or
* this = TOptionB() and result = "option_b"
* }
* }
*
* Tagged<MyEnum>::String getTaggedString() {
* result.make(TOptionA(), "string value")
* }
* ```
*/
bindingset[this]
class String extends Final<UnderlyingString>::Type {
Tag tag;
string strVal;
String() { this = tag.toString() + strVal }
/**
* Holds if this tagged string has the given tag and string, which uniquely identifies and
* therefore may "make" this instance.
*/
bindingset[mstrVal]
bindingset[this]
predicate make(Tag mtag, string mstrVal) { this = mtag.toString() + mstrVal }
/**
* Holds if the tagged string has the given string contents, and any tag.
*
* To also set the tag of this string, either use `make(tag, str)`, or constrain the tag of
* the result of this predicate with `make(str).getTag() = ...`.
*/
bindingset[mstrVal]
bindingset[result]
String make(string mstrVal) {
make(_, mstrVal) and
result = this
}
/**
* Get the tag of this tagged string.
*/
bindingset[this]
Tag getTag() { result = tag }
/**
* Holds if this tagged string has the given tag.
*/
bindingset[this]
predicate isTagged(Tag mtag) { tag = mtag }
/**
* Get the string contents of this tagged string.
*/
bindingset[this]
string getStr() { result = strVal }
}
}