Skip to content

Commit 735d319

Browse files
authored
Merge pull request #1184 from SteveL-MSFT/dynamic-index
Enable expressions to use index accessor for object properties
2 parents 24976b5 + e5226f1 commit 735d319

File tree

6 files changed

+85
-15
lines changed

6 files changed

+85
-15
lines changed

dsc/tests/dsc_expressions.tests.ps1

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ Describe 'Expressions tests' {
1212
@{ text = "[parameters('test').objectArray[1].value[1].name]"; expected = 'three' }
1313
@{ text = "[parameters('test').index]"; expected = '1' }
1414
@{ text = "[parameters('test').objectArray[parameters('test').index].name]"; expected = 'two' }
15+
@{ text = "[parameters('test')['hello']"; expected = '@{world=there}' }
16+
@{ text = "[parameters('test')['hello']['world']]"; expected = 'there' }
17+
@{ text = "[parameters('test')['array'][1][0]]"; expected = 'two' }
18+
@{ text = "[parameters('test')['objectArray'][0]['name']]"; expected = 'one' }
19+
@{ text = "[parameters('test')['objectArray'][1]['value'][1]['name']]"; expected = 'three' }
20+
@{ text = "[parameters('test')[parameters('propertyName')]]"; expected = '@{world=there}' }
1521
) {
1622
param($text, $expected)
1723
$yaml = @"
@@ -35,6 +41,9 @@ Describe 'Expressions tests' {
3541
- nestedObject:
3642
name: three
3743
value: 3
44+
propertyName:
45+
type: string
46+
defaultValue: hello
3847
resources:
3948
- name: echo
4049
type: Microsoft.DSC.Debug/Echo
@@ -185,7 +194,7 @@ resources:
185194
if ($out.results[0].result.actualState.output.$key -is [psobject]) {
186195
$out.results[0].result.actualState.output.$key.psobject.properties.value | Should -Be $expected.$key.values -Because ($out | ConvertTo-Json -Depth 10 | Out-String)
187196
} else {
188-
$out.results[0].result.actualState.output.$key | Should -Be $expected.$key -Because ($out | ConvertTo-Json -Depth 10 | Out-String)
197+
$out.results[0].result.actualState.output.$key | Should -Be $expected.$key -Because ($out | ConvertTo-Json -Depth 10 | Out-String)
189198
}
190199
}
191200
}
@@ -240,7 +249,7 @@ resources:
240249
$LASTEXITCODE | Should -Be 2
241250
$log = Get-Content -Path $TestDrive/error.log -Raw
242251
$log | Should -BeLike "*ERROR* Arguments must be of the same type*"
243-
252+
244253
}
245254

246255
Context 'Resource name expression evaluation' {

grammars/tree-sitter-dscexpression/grammar.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ module.exports = grammar({
4242
memberAccess: $ => seq('.', field('name', $.memberName)),
4343
memberName: $ => /[a-zA-Z0-9_-]+/,
4444

45-
index: $ => seq('[', field('indexValue', choice($.expression, $.number)), ']'),
45+
propertyName: $ => seq('\'', field('string', $.string), '\''),
46+
index: $ => seq('[', field('indexValue', choice($.expression, $.number, $.propertyName)), ']'),
4647
}
4748

4849
});

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,3 +206,12 @@ String with un-escaped single-quote
206206
(arguments
207207
(string))
208208
(ERROR))))
209+
210+
=====
211+
Object index without quotes
212+
=====
213+
[myObject[foo]]
214+
---
215+
216+
(ERROR
217+
(functionName))

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,24 @@ Array index
160160
(index
161161
(number)))))
162162

163+
=====
164+
Object index
165+
=====
166+
[createObject('a',1)['a']]
167+
---
168+
169+
(statement
170+
(expression
171+
(function
172+
(functionName)
173+
(arguments
174+
(string)
175+
(number)))
176+
(accessor
177+
(index
178+
(propertyName
179+
(string))))))
180+
163181
=====
164182
Multiple array indexes
165183
=====

lib/dsc-lib/locales/en-us.toml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -557,14 +557,17 @@ unavailableInUserFunction = "The 'variables()' function is not available in user
557557

558558
[parser.expression]
559559
functionNodeNotFound = "Function node not found"
560-
parsingFunction = "Parsing function '%{name}'"
561-
parsingAccessor = "Parsing accessor '%{name}'"
560+
parsingFunction = "Parsing function: %{name}"
561+
parsingAccessor = "Parsing accessor: %{name}"
562562
accessorParsingError = "Error parsing accessor"
563-
parsingMemberAccessor = "Parsing member accessor '%{name}'"
563+
parsingMemberAccessor = "Parsing member accessor: %{name}"
564564
memberNotFound = "Member name not found"
565-
parsingIndexAccessor = "Parsing index accessor '%{index}'"
565+
parsingIndexAccessor = "Parsing index accessor: %{index}"
566566
indexNotFound = "Index value not found"
567-
invalidAccessorKind = "Invalid accessor kind: '%{kind}'"
567+
indexValue = "Index value: %{value} with kind %{kind}"
568+
propertyNameValue = "Property name value: %{value}"
569+
invalidIndexValueKind = "Invalid index value kind: %{kind}"
570+
invalidAccessorKind = "Invalid accessor kind: %{kind}"
568571
functionResult = "Function results: %{results}"
569572
functionResultSecure = "Function result is secure"
570573
evalAccessors = "Evaluating accessors"
@@ -575,6 +578,7 @@ indexNotValid = "Index is not a valid number"
575578
indexOutOfBounds = "Index is out of bounds"
576579
indexOnNonArray = "Index access on non-array value"
577580
invalidIndexType = "Invalid index type"
581+
propertyNameNotString = "Property name is not a string"
578582

579583
[parser.functions]
580584
foundErrorNode = "Found error node parsing function"

lib/dsc-lib/src/parser/expressions.rs

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ pub struct Expression {
2525
accessors: Vec<Accessor>,
2626
}
2727

28+
fn node_to_string(node: &Node, statement_bytes: &[u8]) -> Result<String, DscError> {
29+
let text = node.utf8_text(statement_bytes)?;
30+
Ok(text.to_string())
31+
}
32+
2833
impl Expression {
2934
/// Create a new `Expression` instance.
3035
///
@@ -41,11 +46,11 @@ impl Expression {
4146
let Some(function) = expression.child_by_field_name("function") else {
4247
return Err(DscError::Parser(t!("parser.expression.functionNodeNotFound").to_string()));
4348
};
44-
debug!("{}", t!("parser.expression.parsingFunction", name = function : {:?}));
49+
debug!("{}", t!("parser.expression.parsingFunction", name = node_to_string(&function, statement_bytes)? : {:?}));
4550
let function = Function::new(statement_bytes, &function)?;
4651
let mut accessors = Vec::<Accessor>::new();
4752
if let Some(accessor) = expression.child_by_field_name("accessor") {
48-
debug!("{}", t!("parser.expression.parsingAccessor", name = accessor : {:?}));
53+
debug!("{}", t!("parser.expression.parsingAccessor", name = node_to_string(&accessor, statement_bytes)? : {:?}));
4954
if accessor.is_error() {
5055
return Err(DscError::Parser(t!("parser.expression.accessorParsingError").to_string()));
5156
}
@@ -57,30 +62,39 @@ impl Expression {
5762
let accessor_kind = accessor.kind();
5863
let value = match accessor_kind {
5964
"memberAccess" => {
60-
debug!("{}", t!("parser.expression.parsingMemberAccessor", name = accessor : {:?}));
65+
debug!("{}", t!("parser.expression.parsingMemberAccessor", name = node_to_string(&accessor, statement_bytes)? : {:?}));
6166
let Some(member_name) = accessor.child_by_field_name("name") else {
6267
return Err(DscError::Parser(t!("parser.expression.memberNotFound").to_string()));
6368
};
6469
let member = member_name.utf8_text(statement_bytes)?;
6570
Accessor::Member(member.to_string())
6671
},
6772
"index" => {
68-
debug!("{}", t!("parser.expression.parsingIndexAccessor", index = accessor : {:?}));
73+
debug!("{}", t!("parser.expression.parsingIndexAccessor", index = node_to_string(&accessor, statement_bytes)? : {:?}));
6974
let Some(index_value) = accessor.child_by_field_name("indexValue") else {
7075
return Err(DscError::Parser(t!("parser.expression.indexNotFound").to_string()));
7176
};
77+
debug!("{}", t!("parser.expression.indexValue", value = node_to_string(&index_value, statement_bytes)? : {:?}, kind = index_value.kind()));
7278
match index_value.kind() {
7379
"number" => {
7480
let value = index_value.utf8_text(statement_bytes)?;
75-
let value = serde_json::from_str(value)?;
76-
Accessor::Index(value)
81+
let number: i64 = value.parse().map_err(|_| DscError::Parser(t!("parser.expression.indexNotValid").to_string()))?;
82+
Accessor::Index(Value::Number(number.into()))
83+
},
84+
"propertyName" => {
85+
let Some(string_node) = index_value.child_by_field_name("string") else {
86+
return Err(DscError::Parser(t!("parser.expression.propertyNameNotString").to_string()));
87+
};
88+
let value = string_node.utf8_text(statement_bytes)?;
89+
debug!("{}", t!("parser.expression.propertyNameValue", value = value : {:?}));
90+
Accessor::Index(Value::String(value.to_string()))
7791
},
7892
"expression" => {
7993
let expression = Expression::new(statement_bytes, &index_value)?;
8094
Accessor::IndexExpression(expression)
8195
},
8296
_ => {
83-
return Err(DscError::Parser(t!("parser.expression.invalidAccessorKind", kind = accessor_kind).to_string()));
97+
return Err(DscError::Parser(t!("parser.expression.invalidIndexValueKind", kind = index_value.kind()).to_string()));
8498
},
8599
}
86100
},
@@ -186,6 +200,21 @@ impl Expression {
186200
return Err(DscError::Parser(t!("parser.expression.indexOnNonArray").to_string()));
187201
}
188202
}
203+
else if index.is_string() {
204+
let index = index.as_str().ok_or_else(|| DscError::Parser(t!("parser.expression.indexNotValid").to_string()))?;
205+
if let Some(object) = value.as_object() {
206+
if !object.contains_key(index) {
207+
return Err(DscError::Parser(t!("parser.expression.memberNameNotFound", member = index).to_string()));
208+
}
209+
if is_secure {
210+
value = convert_to_secure(&object[index]);
211+
} else {
212+
value = object[index].clone();
213+
}
214+
} else {
215+
return Err(DscError::Parser(t!("parser.expression.accessOnNonObject").to_string()));
216+
}
217+
}
189218
else if !index.is_null() {
190219
return Err(DscError::Parser(t!("parser.expression.invalidIndexType").to_string()));
191220
}

0 commit comments

Comments
 (0)