Skip to content

Selecting literals

Literals represent the group of expressions that have an inline, literal, value represented verbatim in the source code, such as the string "x", or the number 3, or the boolean true. Literal selectors in SYNG can match against either the type of the literal, such as String for both "a" and "b", or against both type and value of the literal, such as exactly the String "a".

The first form is useful when you don't necessarily care about the value of the literal, just its type; say you want to know that a function call is made using a string argument, regardless of what the string value is. Other times, you may be looking for a particular value -- both are acceptable outcomes of a literal selector.

Querying for literal values on their own is not likely to be very useful, but they are the building blocks of more powerful queries once combined with other selectors, so learning about them first will make things easier later.

This chapter describes the selectors available for each literal.

Selecting String literals

(str) selects any String literal value:

clojure
(str)
(str)

Which will match "a" and x("b") and var x = "c".

Or it could select a particular String value:

clojure
(str "foo")
(str "foo")

Which will match "foo" but not "bar". To select values that match a specific pattern, you could use a Regular Expression:

clojure
(str /foo.+/) ; "foo_bar"
              ; "foo BAR"
(str /foo.+/) ; "foo_bar"
              ; "foo BAR"

This form is explained in more detail later in the guide to Pattern Matching, but it's enough to know that it exists for now.

Selecting Number literals

(num) selects any numeric value and has the following signature:

clojure
(num [value] [upper_bound])
(num [value] [upper_bound])

Using (num) on its own with no arguments will match any kind of number literal, like 1 and x(2) and var x = 3.0.

Or, given the first argument, it selects a particular numeric value, like 1 but not 2:

clojure
(num 1)
(num 1)

Finally, it selects a value within a particular range given both arguments, the first being the lower bound and the second the upper:

clojure
(num 1 5)
(num 1 5)

Which will match 1 through 5 but not 0 nor 6.

(num) accepts floats in both forms, but when you're specifying a range, the type must match both bounds -- don't mix integers and floats in a single range.

Selecting Boolean literals

(bool) selects any boolean value:

clojure
(bool)
(bool)

Which will match true and x(false).

Or it could select a particular boolean value, like true but not false:

clojure
(bool true)
(bool true)

No other arguments are allowed for this selector other than true and false.

Selecting RegExp literals

(regex) selects any literal Regular Expression:

clojure
(regex [expression?] [flags])
(regex [expression?] [flags])

The bare form will select /foo/ and x(/bar/g):

clojure
(regex)
(regex)

Or it could select a particular expression, such as /foo/ but not /bar/:

clojure
(regex "foo")
(regex "foo")

Or it could select a particular expression with a specific set of flags, such as /foo/gi and /foo/ig but not /foo/:

clojure
(regex "foo" "gi")
(regex "foo" "gi")

When you don't care about the expression and only the flags, you may pass _ as the first argument -- the placeholder atom:

clojure
(regex _ "gi")
(regex _ "gi")

This will match /foo/gi and /bar/gi.

As was the case with String literals, you could select an expression - or flags - that match a specific pattern through a Regular Expression:

clojure
(regex /^foo.+/ /gi/) ; /foobarbaz/i
                      ; /foo_X/g
(regex /^foo.+/ /gi/) ; /foobarbaz/i
                      ; /foo_X/g

Regex all the way down!

RegExp literals vs. instances

Note that (regex) will not select instances of the RegExp class like new RegExp('foo'), for that we can use the (of) selector described in a later chapter.

Selecting Computed literals

Computed literals appear in specific expressions, commonly found in object literals, like the [a] in { [a]: 1 }. Contrary to what their name implies, a computed literal may have a value of any arbitrary expression, including other literals. This is unlike most literals we've encountered so far.

(comp) selects such computed literals:

clojure
(comp [type?])
(comp [type?])

Where [type] may be any selector to match the type of the literal. In our example of the object literal { [a]: 1 }, the type of the key is an identifier, so we can select it as such:

clojure
(comp (id))
(comp (id))

When we don't care about the type of computed literal, omit the argument:

clojure
(comp)
(comp)

Which will select the a in { [a]: 1 } and class MyClass { ["a"]() {} }.

This selector may only appear as an argument to other selectors that match expressions where computed literals are legal, namely (obj) and (mem), which we'll encounter later.

Selecting the null literal

(null) selects the literal null value as its name implies. Since null represents no other value than null, the selector receives no arguments.

clojure
(null)
(null)

This will match nulls anywhere: in null and x(null) and x = null.

Copyright © 2022-present Semantic Works, Inc.