Skip to content

Commit d6d4b26

Browse files
authored
Merge pull request #524 from SteveL-MSFT/multiline
Enable multiline string support in tree-sitter gammar
2 parents df131f9 + da4eb79 commit d6d4b26

File tree

9 files changed

+124
-7
lines changed

9 files changed

+124
-7
lines changed

dsc/examples/multiline.dsc.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
2+
resources:
3+
- name: test multi-line
4+
type: Test/Echo
5+
properties:
6+
output: |
7+
This is a
8+
'multi-line'
9+
string.
10+
- name: test single-quote escaping
11+
type: Test/Echo
12+
properties:
13+
output: "[concat('This is a single-quote: ', '''')]"

dsc/examples/winps_script.dsc.yaml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
2+
metadata:
3+
Microsoft.DSC:
4+
securityContext: Elevated
5+
resources:
6+
- type: Microsoft.Windows/WindowsPowerShell
7+
name: Run WinPS script
8+
properties:
9+
resources:
10+
- name: Run script
11+
type: PSDesiredStateConfiguration/Script
12+
properties:
13+
GetScript: |
14+
$text = @"
15+
get
16+
"@
17+
# Returning result must be this type of hashtable
18+
@{Result=$text}
19+
TestScript: |
20+
# TestScript must return a boolean
21+
$true
22+
SetScript: |
23+
$text = @"
24+
set
25+
"@

dsc/tests/dsc_expressions.tests.ps1

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,17 @@ resources:
6363
$LASTEXITCODE | Should -Be 2
6464
$out | Should -BeLike "*ERROR*"
6565
}
66+
67+
It 'Multi-line string literals work' {
68+
$yamlPath = "$PSScriptRoot/../examples/multiline.dsc.yaml"
69+
$out = dsc config get -p $yamlPath | ConvertFrom-Json
70+
$LASTEXITCODE | Should -Be 0
71+
$out.results[0].result.actualState.output | Should -BeExactly @"
72+
This is a
73+
'multi-line'
74+
string.
75+
76+
"@.Replace("`r", "")
77+
$out.results[1].result.actualState.output | Should -BeExactly "This is a single-quote: '"
78+
}
6679
}

dsc_lib/src/parser/functions.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ fn convert_args_node(statement_bytes: &[u8], args: &Option<Node>) -> Result<Opti
9797
match arg.kind() {
9898
"string" => {
9999
let value = arg.utf8_text(statement_bytes)?;
100-
result.push(FunctionArg::Value(Value::String(value.to_string())));
100+
// Resolve escaped single quotes
101+
result.push(FunctionArg::Value(Value::String(value.to_string().replace("''", "'"))));
101102
},
102103
"number" => {
103104
let value = arg.utf8_text(statement_bytes)?;

tree-sitter-dscexpression/build.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ if ($UpdatePackages) {
4848
npm cache clean --force
4949
npm logout
5050
vsts-npm-auth -config .npmrc -F -V
51-
npm install --force --verbose
51+
npm install --force --verbose --registry https://pkgs.dev.azure.com/mseng/_packaging/OneESD-DevOps/npm/registry/
5252
}
5353

5454
Invoke-NativeCommand 'npx tree-sitter generate --build'

tree-sitter-dscexpression/grammar.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ const PREC = {
77
module.exports = grammar({
88
name: 'dscexpression',
99

10+
extras: $ => ['\n', ' '],
11+
1012
rules: {
1113
statement: $ => choice(
1214
$.escapedStringLiteral,
@@ -16,16 +18,16 @@ module.exports = grammar({
1618
escapedStringLiteral: $ => token(prec(PREC.ESCAPEDSTRING, seq('[[', /.*?/))),
1719
_expressionString: $ => prec(PREC.EXPRESSIONSTRING, seq('[', $.expression, ']')),
1820
expression: $ => seq(field('function', $.function), optional(field('accessor',$.accessor))),
19-
stringLiteral: $ => token(prec(PREC.STRINGLITERAL, /[^\[].*?/)),
21+
stringLiteral: $ => token(prec(PREC.STRINGLITERAL, /[^\[](.|\n)*?/)),
2022

2123
function: $ => seq(field('name', $.functionName), '(', field('args', optional($.arguments)), ')'),
2224
functionName: $ => /[a-z][a-zA-Z0-9]*/,
2325
arguments: $ => seq($._argument, repeat(seq(',', $._argument))),
2426
_argument: $ => choice($.expression, $._quotedString, $.number, $.boolean),
2527

2628
_quotedString: $ => seq('\'', $.string, '\''),
27-
// ARM strings are allowed to contain single-quote characters
28-
string: $ => /[^']*/,
29+
// ARM strings are not allowed to contain single-quote characters unless escaped
30+
string: $ => /([^']|''|\n)*/,
2931
number: $ => /-?\d+/,
3032
boolean: $ => choice('true', 'false'),
3133

tree-sitter-dscexpression/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tree-sitter-dscexpression/test/corpus/invalid_expressions.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,3 +198,19 @@ Expression with index accessor outside
198198
(number))))
199199
(ERROR
200200
(number)))
201+
202+
=====
203+
String with un-escaped single-quote
204+
=====
205+
[myFunction('argString'test'value')]
206+
---
207+
208+
(statement
209+
(expression
210+
(function
211+
(functionName)
212+
(arguments
213+
(string))
214+
(ERROR
215+
(functionName)
216+
(functionName)))))

tree-sitter-dscexpression/test/corpus/valid_expressions.txt

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,3 +274,50 @@ Array index with function and nested dot-notation with index
274274
(memberName)))))
275275
(index
276276
(number)))))
277+
278+
=====
279+
Multi-line expression
280+
=====
281+
[myFunction(
282+
'argString',
283+
1,
284+
-1,
285+
true
286+
)]
287+
---
288+
289+
(statement
290+
(expression
291+
(function
292+
(functionName)
293+
(arguments
294+
(string)
295+
(number)
296+
(number)
297+
(boolean)))))
298+
299+
=====
300+
Multi-line literal string
301+
=====
302+
This
303+
is
304+
a
305+
multiline
306+
string
307+
---
308+
309+
(statement
310+
(stringLiteral))
311+
312+
=====
313+
Escaped single-quotes
314+
=====
315+
[myFunction('this ''is'' a string')]
316+
---
317+
318+
(statement
319+
(expression
320+
(function
321+
(functionName)
322+
(arguments
323+
(string)))))

0 commit comments

Comments
 (0)