From 7e04eb51ff34f7947f396ef30b8e53edc1dc3f2c Mon Sep 17 00:00:00 2001 From: Andrew Twyman Date: Thu, 11 Sep 2025 12:23:46 -0700 Subject: [PATCH] Optional dot syntax for anchored keys (#423) --- book/src/anchoredkeys.md | 9 ++++++--- book/src/simpleexample.md | 2 +- src/examples/mod.rs | 8 ++++---- src/lang/grammar.pest | 7 +++++-- src/lang/parser.rs | 11 ++++++++--- src/lang/processor.rs | 17 ++++++++++++----- 6 files changed, 36 insertions(+), 18 deletions(-) diff --git a/book/src/anchoredkeys.md b/book/src/anchoredkeys.md index d2fd38d..33d0142 100644 --- a/book/src/anchoredkeys.md +++ b/book/src/anchoredkeys.md @@ -12,10 +12,13 @@ some of its arguments is proved, it means that a valid Merkle proof of the value behind it exists and was used at some point to construct a `Contains` statement that introduced that anchored key. +In PODLang, anchored key indexing can use subscript syntax `foo["bar"]` which +allows any string key, or dot syntax `foo.bar` if the key is a valid identifier. + For example: ``` 0: None -1: Contains(foo, bar, 42) <- ContainsFromEntries 0 0 0 mt_proof -2: Lt(foo[bar], 100) <- LtFromEntries 1 0 -3: NotEqual(foo[bar], 100) <- LtToNotEqual 2 +1: Contains(foo, "bar", 42) <- ContainsFromEntries 0 0 0 mt_proof +2: Lt(foo["bar"], 100) <- LtFromEntries 1 0 +3: NotEqual(foo.bar, 100) <- LtToNotEqual 2 ``` diff --git a/book/src/simpleexample.md b/book/src/simpleexample.md index 840823d..d6a127a 100644 --- a/book/src/simpleexample.md +++ b/book/src/simpleexample.md @@ -59,7 +59,7 @@ Equals(a, c) First, we need to decompose all the anchored keys as (dict, key) pairs. This is the frontend description of the deduction rule. ``` IF -Equals(a_or[a_key], b_or[b_key) +Equals(a_or[a_key], b_or[b_key]) AND Equals(b_or[b_key], c_or[c_key]) THEN diff --git a/src/examples/mod.rs b/src/examples/mod.rs index 8ab2d55..868f783 100644 --- a/src/examples/mod.rs +++ b/src/examples/mod.rs @@ -82,10 +82,10 @@ pub fn zu_kyc_pod_request(gov_signer: &Value, pay_signer: &Value) -> Result parse_pest_string_literal(&key_part_pair)?, + Rule::identifier => key_part_pair.as_str().to_string(), + _ => unreachable!( + "unknown key type in anchored key: {:?}", + key_part_pair.as_rule() + ), + }; Ok(BuilderArg::Key(root_wc_str.to_string(), key_str)) } _ => unreachable!("Unexpected rule: {:?}", arg_content_pair.as_rule()), @@ -1065,7 +1072,7 @@ mod processor_tests { #[test] fn test_fp_only_request() -> Result<(), ProcessorError> { - let input = "REQUEST( Equal(?A[\"k\"],?B[\"k\"]) )"; // Escaped quotes + let input = "REQUEST( Equal(?A[\"k\"],?B.k) )"; // Escaped quotes let pairs = get_document_content_pairs(input)?; let params = Params::default(); let mut ctx = ProcessingContext::new(¶ms); @@ -1082,7 +1089,7 @@ mod processor_tests { #[test] fn test_fp_simple_predicate() -> Result<(), ProcessorError> { - let input = "my_pred(A, B) = AND( Equal(?A[\"k\"],?B[\"k\"]) )"; // Escaped quotes + let input = "my_pred(A, B) = AND( Equal(?A[\"k\"],?B.k) )"; // Escaped quotes let pairs = get_document_content_pairs(input)?; let params = Params::default(); let mut ctx = ProcessingContext::new(¶ms); @@ -1104,7 +1111,7 @@ mod processor_tests { #[test] fn test_fp_multiple_predicates() -> Result<(), ProcessorError> { let input = r#" - pred1(X) = AND( Equal(?X["k"],?X["k"]) ) + pred1(X) = AND( Equal(?X["k"],?X.k) ) pred2(Y, Z) = OR( Equal(?Y["v"], 123) ) "#; let pairs = get_document_content_pairs(input)?; @@ -1253,7 +1260,7 @@ mod processor_tests { // Native predicate names are case-sensitive let input = r#" REQUEST( - EQUAL(?A["b"], ?C["d"]) + EQUAL(?A["b"], ?C.d) ) "#; let pairs = get_document_content_pairs(input)?;