Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement multiline string literals in metadata. #121

Merged
merged 4 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/src/compiler/tests/testdata/errors/69.out
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ error: syntax error
--> line:1:23
|
1 | rule test { meta: a = condition: true }
| ^ expected `false`, `true`, number, or string literal
| ^ expected `false`, `true`, multiline string literal, number, or string literal
|
4 changes: 2 additions & 2 deletions parser/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ impl<'src> Display for MetaValue<'src> {
Self::Bool(v) => write!(f, "{}", v),
Self::Integer(v) => write!(f, "{}", v),
Self::Float(v) => write!(f, "{:.1}", v),
Self::String(v) => write!(f, "{}", v),
Self::Bytes(v) => write!(f, "{}", v),
Self::String(v) => write!(f, "\"{}\"", v),
Self::Bytes(v) => write!(f, "\"{}\"", v),
}
}
}
Expand Down
28 changes: 20 additions & 8 deletions parser/src/parser/cst2ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -867,7 +867,7 @@ fn meta_from_cst<'src>(
GrammarRule::float_lit => {
MetaValue::Float(float_lit_from_cst(ctx, value_node)?)
}
GrammarRule::string_lit => {
GrammarRule::string_lit | GrammarRule::multiline_string_lit => {
match string_lit_from_cst(ctx, value_node, true)? {
// If the result is a string borrowed directly from the
// source code, we can be sure that it's a valid UTF-8
Expand Down Expand Up @@ -1868,19 +1868,31 @@ fn string_lit_from_cst<'src>(
string_lit: CSTNode<'src>,
allow_escape_char: bool,
) -> Result<Cow<'src, BStr>, Error> {
expect!(string_lit, GrammarRule::string_lit);
let num_quotes = match string_lit.as_rule() {
GrammarRule::string_lit => {
// The string literal must be enclosed in double quotes.
debug_assert!(string_lit.as_str().starts_with('\"'));
debug_assert!(string_lit.as_str().ends_with('\"'));
1
}
GrammarRule::multiline_string_lit => {
// The string literal must be enclosed in 3 double quotes.
debug_assert!(string_lit.as_str().starts_with("\"\"\""));
debug_assert!(string_lit.as_str().ends_with("\"\"\""));
3
}
_ => {
panic!("expecting string literal or multiline string literal but found {:?}", string_lit.as_rule());
}
};

let literal = string_lit.as_str();

// The string literal must be enclosed in double quotes.
debug_assert!(literal.starts_with('\"'));
debug_assert!(literal.ends_with('\"'));

// The span doesn't include the quotes.
let string_span = ctx.span(&string_lit).subspan(1, literal.len() - 1);
let string_span = ctx.span(&string_lit).subspan(num_quotes, literal.len() - num_quotes);

// From now on ignore the quotes.
let literal = &literal[1..literal.len() - 1];
let literal = &literal[num_quotes..literal.len() - num_quotes];

// Check if the string contains some backslash.
let backslash_pos = if let Some(backslash_pos) = literal.find('\\') {
Expand Down
1 change: 1 addition & 0 deletions parser/src/parser/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ impl ErrorInfo {
Rule::rule_decl => "rule declaration",
Rule::source_file => "YARA rules",
Rule::string_lit => "string literal",
Rule::multiline_string_lit => "multiline string literal",
wxsBSD marked this conversation as resolved.
Show resolved Hide resolved
Rule::regexp => "regular expression",
Rule::pattern_mods => "pattern modifiers",

Expand Down
20 changes: 20 additions & 0 deletions parser/src/parser/grammar.pest
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,25 @@ pattern_length = @{
"!" ~ ident_chars*
}

// Multiline string literal. i.e:
//
// """
// I'm a multiline string literal!
//
// Hooray!
// """
multiline_string_lit = @{
DOUBLE_QUOTES{3} ~ (
// The escape sequence \\ has precedence, if not, \\" would be interpreted
// as a backslash \, followed by the escape sequence \"
"\\\\" |
// Allow \" inside the double quotes.
"\\\"" |
// Allow any characters except triple quotes.
!DOUBLE_QUOTES{3} ~ ANY
)* ~ DOUBLE_QUOTES{3}
}

// String literal (i.e: "", "foo", "bar").
string_lit = @{
DOUBLE_QUOTES ~ (
Expand Down Expand Up @@ -306,6 +325,7 @@ meta_def = {
k_FALSE |
float_lit |
integer_lit |
multiline_string_lit |
string_lit
)
}
Expand Down
20 changes: 19 additions & 1 deletion parser/src/parser/tests/testdata/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@
some_bool = true
some_bool = false
some_string = "foo"
some_multiline_string = """
I'm a multiline string literal!

\"test\"

I also handle escapes, \x41\x42\x43!

... and emojis 🤖!
"""
condition:
true
}
Expand All @@ -20,7 +29,16 @@
│ ├─ some_float = 2.0
│ ├─ some_bool = true
│ ├─ some_bool = false
│ └─ some_string = foo
│ ├─ some_string = "foo"
│ └─ some_multiline_string = "
I'm a multiline string literal!

"test"

I also handle escapes, ABC!

... and emojis 🤖!
"
└─ condition
└─ true

Expand Down
Loading