diff --git a/book/src/customexample.md b/book/src/customexample.md index 1ce50b5..fc1c8c9 100644 --- a/book/src/customexample.md +++ b/book/src/customexample.md @@ -3,24 +3,24 @@ ``` eth_dos_distance(src_or, src_key, dst_or, dst_key, distance_or, distance_key) = OR( - eth_dos_distance_ind_0(?src_or, ?src_key, ?dst_or, ?dst_key, ?distance_or, ?distance_key), - eth_dos_distance_base(?src_or, ?src_key, ?dst_or, ?dst_key, ?distance_or, ?distance_key) + eth_dos_distance_ind_0(src_or, src_key, dst_or, dst_key, distance_or, distance_key), + eth_dos_distance_base(src_or, src_key, dst_or, dst_key, distance_or, distance_key) ) eth_dos_distance_base(src_or, src_key, dst_or, dst_key, distance_or, distance_key) = AND( - Equal(?src_or[?src_key], ?dst_or[?dst_key]), - ValueOf(?distance_or[?distance_key], 0) + Equal(src_or[src_key], dst_or[dst_key]), + ValueOf(distance_or[distance_key], 0) ) eth_dos_distance_ind_0(src_or, src_key, dst_or, dst_key, distance_or, distance_key, private: intermed_or, intermed_key, shorter_distance_or, shorter_distance_key, one_or, one_key) = AND( - eth_dos_distance(?src_or, ?src_key, ?intermed_or, ?intermed_key, ?shorter_distance_or, ?shorter_distance_key) + eth_dos_distance(src_or, src_key, intermed_or, intermed_key, shorter_distance_or, shorter_distance_key) // distance == shorter_distance + 1 - ValueOf(?one_or[?one_key], 1) - SumOf(?distance_or[?distance_key], ?shorter_distance_or[?shorter_distance_key], ?one_or[?one_key]) + ValueOf(one_or[one_key], 1) + SumOf(distance_or[distance_key], shorter_distance_or[shorter_distance_key], one_or[one_key]) // intermed is a friend of dst - eth_friend(?intermed_or, ?intermed_key, ?dst_or, ?dst_key) + eth_friend(intermed_or, intermed_key, dst_or, dst_key) ) ``` diff --git a/book/src/custompred.md b/book/src/custompred.md index 6459f97..3c1b461 100644 --- a/book/src/custompred.md +++ b/book/src/custompred.md @@ -11,17 +11,17 @@ The syntax of a custom operation is best explained with an example. Original example with anchored keys, origins, and keys. | Args | Condition | Output | |------------|-----------------------------------------|----| -| signed_dict: Dict,
signer: PublicKey,
good_boy_issuers: AnchoredKey::MerkleRoot,
receiver: AnchoredKey | SignedBy(?signed_dict, ?signer),
Contains(?good_boy_issuers, ?signer),
Equals(?signed_dict["friend"], ?receiver) | GoodBoy(?receiver, ?good_boy_issuers) | +| signed_dict: Dict,
signer: PublicKey,
good_boy_issuers: AnchoredKey::MerkleRoot,
receiver: AnchoredKey | SignedBy(signed_dict, signer),
Contains(good_boy_issuers, signer),
Equals(signed_dict["friend"], receiver) | GoodBoy(receiver, good_boy_issuers) | Compiled example with only origins and keys. | Args | Condition | Output | |------------|-----------------------------------------|----| -| signed_dict: Dict,
signer: PublicKey,
good_boy_issuers_origin: Origin,
good_boy_issuers_key: Key::MerkleRoot,
receiver_origin: Origin,
receiver_key: Key | SignedBy(?signed_dict, ?signer),
Contains(?good_boy_issuers_origin[?good_boy_issuers_key], ?signer),
Equals(?signed_dict["friend"], ?receiver_origin[?receiver_key]) | GoodBoy(?receiver_origin[?receiver_key]), ?good_boy_issuers_origin[?good_boy_issuers_key]) | +| signed_dict: Dict,
signer: PublicKey,
good_boy_issuers_origin: Origin,
good_boy_issuers_key: Key::MerkleRoot,
receiver_origin: Origin,
receiver_key: Key | SignedBy(signed_dict, signer),
Contains(good_boy_issuers_origin[good_boy_issuers_key], signer),
Equals(signed_dict["friend"], receiver_origin[receiver_key]) | GoodBoy(receiver_origin[receiver_key]), good_boy_issuers_origin[good_boy_issuers_key]) | A custom operation accepts as input a number of statements (the `Condition`); each statement has a number of arguments, which may be constants or anchored keys; and an [anchored key](./anchoredkeys.md) in turn can optionally be decomposed as a pair of a Dict and a Key. -In the "original example" above, the anchored keys `good_boy_issuers` and `receiver` are not broken down, but `?signed_dict["friend"]` is. The purpose of breaking it down, in this case, is to use an entry of a dictionary that has been signed. +In the "original example" above, the anchored keys `good_boy_issuers` and `receiver` are not broken down, but `signed_dict["friend"]` is. The purpose of breaking it down, in this case, is to use an entry of a dictionary that has been signed. In the "compiled example", all the anchored keys have been broken down into dictionaries and keys. diff --git a/book/src/examples.md b/book/src/examples.md index d89cd88..5b34936 100644 --- a/book/src/examples.md +++ b/book/src/examples.md @@ -13,21 +13,21 @@ rules, the inductive case and the base case. ``` // src, dst: PubKey, attetation_pod: Pod eth_dos_friend(src, dst, private: attestation_pod) = AND( - ValueOf(?attestation_pod[KEY_TYPE], SIGNATURE) - Equal(?attestation_pod[KEY_SIGNER], src) - Equal(?attestation_pod["attestation"], dst) + ValueOf(attestation_pod[KEY_TYPE], SIGNATURE) + Equal(attestation_pod[KEY_SIGNER], src) + Equal(attestation_pod["attestation"], dst) ) // src, intermed, dst: PubKey, distance, shorter_distance: Int eth_dos_distance(src, dst, distance, private: shorter_distance, intermed) = OR( AND( - eth_dos_distance(?src, ?intermed, ?shorter) - SumOf(?distance, ?shorter_distance, 1) - eth_friend(?intermed, ?dst) + eth_dos_distance(src, intermed, shorter) + SumOf(distance, shorter_distance, 1) + eth_friend(intermed, dst) ) AND( - Equal(?src, ?dst) - Equal(?distance, 0) + Equal(src, dst) + Equal(distance, 0) ) ) ``` @@ -52,29 +52,29 @@ A ZuKYC Pod exposes a single custom statement with one custom deduction rule. ``` // receiver: PubKey, gov_id, paystub, sk_pok: Pod, nullifier, sk: Raw loan_check(receiver, private: gov_id, paystub, nullifier, sk, sk_pok) = AND( - Equal(?gov_id["pk"], receiver) + Equal(gov_id["pk"], receiver) // Not in the sanction list SetNotContains(SANCTION_LIST, receiver) // Valid government-issued ID - Equal(?gov_id[KEY_SIGNER], ZOO_GOV) - Equal(?gov_id[KEY_TYPE], SIGNATURE) + Equal(gov_id[KEY_SIGNER], ZOO_GOV) + Equal(gov_id[KEY_TYPE], SIGNATURE) // At least 18 years old - Lt(?gov_id["date_of_birth"], NOW_MINUS_18Y) # date_of_birdth is more than 18y old - Equal(?paystub[KEY_SIGNER], ZOO_DEEL) - Equal(?paystub[KEY_TYPE], SIGNATURE) - Equal(?paystub[ssn], ?gov_id["ssn"]) + Lt(gov_id["date_of_birth"], NOW_MINUS_18Y) # date_of_birdth is more than 18y old + Equal(paystub[KEY_SIGNER], ZOO_DEEL) + Equal(paystub[KEY_TYPE], SIGNATURE) + Equal(paystub[ssn], gov_id["ssn"]) // At least one year of consistent employment with your current employer - Lt(?paystub["start_date"], NOW_MINUS_1Y) # start_date is more than 1y old - Gt(?paystub["issue_date"], NOW_MINUS_7D) # issue_date is less than 7d old + Lt(paystub["start_date"], NOW_MINUS_1Y) # start_date is more than 1y old + Gt(paystub["issue_date"], NOW_MINUS_7D) # issue_date is less than 7d old // Annual salary is at least $20,000 - Gt(?paystub["annual_salary"], 20000) + Gt(paystub["annual_salary"], 20000) // Private key knowledge - Equal(?sk_pok[KEY_SIGNER], receiver) - Equal(?sk_pok[KEY_TYPE], SIGNATURE) - Equal(?sk_pok["auth"], "ZUKYC_V1_AUTH") - HashOf(, 0, ?sk) + Equal(sk_pok[KEY_SIGNER], receiver) + Equal(sk_pok[KEY_TYPE], SIGNATURE) + Equal(sk_pok["auth"], "ZUKYC_V1_AUTH") + HashOf(, 0, sk) // Nullifier - HashOf(nullifier, "ZUKYC_V1_NULLIFIER", ?sk) + HashOf(nullifier, "ZUKYC_V1_NULLIFIER", sk) ) ``` @@ -95,19 +95,19 @@ A ZuKYC Pod exposes a single custom statement with one custom deduction rule. ``` // receiver: String, gov_pk, paystub_pk: PubKey, gov_id, paystub: Pod loan_check(receiver, gov_pk, paystub_pk, private: gov_id, paystub) = AND( - Equal(?gov_id["id_number"], ?receiver) + Equal(gov_id["id_number"], receiver) // Not in the sanction list - SetNotContains(SANCTION_LIST, ?gov_id["id_number"]) + SetNotContains(SANCTION_LIST, gov_id["id_number"]) // Valid government-issued ID - ValueOf(?gov_id[KEY_SIGNER], ?gov_pk) - Equal(?gov_id[KEY_TYPE], SIGNATURE) + ValueOf(gov_id[KEY_SIGNER], gov_pk) + Equal(gov_id[KEY_TYPE], SIGNATURE) // At least 18 years old - Lt(?gov_id["date_of_birth"], NOW_MINUS_18Y) # date_of_birdth is more than 18y old - ValueOf(?paystub[KEY_SIGNER], ?paystub_pk) - Equal(?paystub[KEY_TYPE], SIGNATURE) - Equal(?paystub["ssn"], ?gov_id["ssn"]) + Lt(gov_id["date_of_birth"], NOW_MINUS_18Y) # date_of_birdth is more than 18y old + ValueOf(paystub[KEY_SIGNER], paystub_pk) + Equal(paystub[KEY_TYPE], SIGNATURE) + Equal(paystub["ssn"], gov_id["ssn"]) // At least one year of consistent employment with your current employer - Lt(?paystub["start_date"], NOW_MINUS_1Y) # start_date is more than 1y old + Lt(paystub["start_date"], NOW_MINUS_1Y) # start_date is more than 1y old ) ``` @@ -118,11 +118,11 @@ A Good Boy Pod exposes one custom statement with one custom deduction rule. ``` // user: PubKey, good_boy_issuers: Set, pod: Pod, age: Int is_good_boy(user, good_boy_issuers, private: pod, age) = AND( - Equal(?pod[KEY_TYPE], SIGNATURE) - SetContains(?good_boy_issuers, ?pod[KEY_SIGNER]) + Equal(pod[KEY_TYPE], SIGNATURE) + SetContains(good_boy_issuers, pod[KEY_SIGNER]) // A good boy issuer says this user is a good boy - Equal(?pod["user"], ?user) - Equal(?pod["age"], ?age) + Equal(pod["user"], user) + Equal(pod["age"], age) ) ``` @@ -131,12 +131,12 @@ A Friend Pod exposes one custom statement with one custom deduction rule. ``` // good_boy, friend: PubKey, good_boy_issuers: Set, friend_pod: Pod is_friend(good_boy, friend, good_boy_issuers, friend_pod) = AND( - Equal(?pod[KEY_TYPE], SIGNATURE) + Equal(pod[KEY_TYPE], SIGNATURE) // The issuer is a good boy - is_good_boy(?good_boy, ?good_boy_issuers) + is_good_boy(good_boy, good_boy_issuers) // A good boy says this is their friend - Equal(?pod[KEY_SIGNER], ?good_boy) - Equal(?pod["friend"], ?friend) + Equal(pod[KEY_SIGNER], good_boy) + Equal(pod["friend"], friend) ) ``` @@ -147,10 +147,10 @@ with one custom deduction rule. great_boy: PubKey, good_boy_issuers: Set, friend_pod_0, friend_pod_1: Pod is_great_boy(great_boy, good_boy_issuers, private: friend_pod_0, friend_pod_1) = AND // Two good boys consider this user their friend - is_friend(?friend_pod_0[KEY_SIGNER], ?great_boy) - is_friend(?friend_pod_1[KEY_SIGNER], ?great_boy) + is_friend(friend_pod_0[KEY_SIGNER], great_boy) + is_friend(friend_pod_1[KEY_SIGNER], great_boy) // good boy 0 != good boy 1 - NotEqual(?friend_pod_0[KEY_SIGNER], ?friend_pod_1[KEY_SIGNER]) + NotEqual(friend_pod_0[KEY_SIGNER], friend_pod_1[KEY_SIGNER]) ``` ## Attested GreatBoy @@ -161,13 +161,13 @@ An Attested Great Boy Pod is like a Great Boy Pod, but the names of the signers // great_boy: PubKey, friend0, friend1: String, good_boy_issuers: Set, friend_pod_0, friend_pod_1: Pod is_great_boy(great_boy, friend0, friend1, good_boy_issuers, private: friend_pod_0, friend_pod_1) = AND // Two good boys consider this user their friend - is_friend(?friend_pod_0[KEY_SIGNER], ?great_boy) - is_friend(?friend_pod_1[KEY_SIGNER], ?great_boy) + is_friend(friend_pod_0[KEY_SIGNER], great_boy) + is_friend(friend_pod_1[KEY_SIGNER], great_boy) // good boy 0 != good boy 1 - NotEqual(?friend_pod_0[KEY_SIGNER], ?friend_pod_1[KEY_SIGNER]) + NotEqual(friend_pod_0[KEY_SIGNER], friend_pod_1[KEY_SIGNER]) // publicize signer names - ValueOf(?friend_pod_0["name"], ?friend0) - ValueOf(?friend_pod_1["name"], ?friend1) + ValueOf(friend_pod_0["name"], friend0) + ValueOf(friend_pod_1["name"], friend1) ``` To produce a Great Boy Pod, you need two Friend Pods, `friend_pod0` and `friend_pod1`, each of which reveals its `signer`. @@ -197,9 +197,9 @@ A post is popular if it has at least two comments from different signers. ``` // post, comment1, comment2: Pod statement is_popular(post, private: comment1, comment2) = AND( - IsEqual(?comment1["referenced_post"], ?post) - IsEqual(?comment2["referenced_post"], ?post) - NotEqual(?comment1[KEY_SIGNER], ?comment2[KEY_SIGNER]) + IsEqual(comment1["referenced_post"], post) + IsEqual(comment2["referenced_post"], post) + NotEqual(comment1[KEY_SIGNER], comment2[KEY_SIGNER]) ) ``` @@ -209,7 +209,7 @@ Suppose I want to prove that two different people are over 18, and a third perso ``` // age: Int over_18(age) = AND( - GtEq(?age, 18) + GtEq(age, 18) ) ``` @@ -229,10 +229,10 @@ over_18(?1) = AND( Maybe I have two input pods `gov_id1` and `gov_id2`, and I want to prove that these pods refer to two different people, both of whom are over 18; and a third pods `gov_id3` refers to someone under 18. So in my public output statements, I want to have: ``` -NotEqual(?gov_id1["name"], ?gov_id2["name"]) -over_18(?gov_id1["age"]) -over_18(?gov_id2["age"]) -under_18(?gov_id3["age"]). +NotEqual(gov_id1["name"], gov_id2["name"]) +over_18(gov_id1["age"]) +over_18(gov_id2["age"]) +under_18(gov_id3["age"]). ``` I would prove this with the following sequence of deductions: diff --git a/book/src/simpleexample.md b/book/src/simpleexample.md index d6a127a..97d16f9 100644 --- a/book/src/simpleexample.md +++ b/book/src/simpleexample.md @@ -118,10 +118,10 @@ The wildcard system handles this very naturally, since the dict of the anchored ``` eth_friend(src_or, src_key, dst_or, dst_key) = and< // the attestation dict is signed by (src_or, src_key) - SignedBy(?attestation_dict, ?src_or[?src_key]) + SignedBy(attestation_dict, src_or[src_key]) // that same attestation pod has an "attestation" - Equal(?attestation_dict["attestation"], ?dst_or[?dst_key]) + Equal(attestation_dict["attestation"], dst_or[dst_key]) > ``` diff --git a/examples/main_pod_points.rs b/examples/main_pod_points.rs index fb049be..dc9bbe7 100644 --- a/examples/main_pod_points.rs +++ b/examples/main_pod_points.rs @@ -72,17 +72,17 @@ fn main() -> Result<(), Box> { let input = format!( r#" points(player, level, points, private: points_dict) = AND( - SignedBy(?points_dict, PublicKey({game_pk})) - Contains(?points_dict, "player", ?player) - Contains(?points_dict, "level", ?level) - Contains(?points_dict, "points", ?points) + SignedBy(points_dict, PublicKey({game_pk})) + Contains(points_dict, "player", player) + Contains(points_dict, "level", level) + Contains(points_dict, "points", points) ) over_9000(player, private: points_lvl_1, points_lvl_2, points_total) = AND( - points(?player, 1, ?points_lvl_1) - points(?player, 2, ?points_lvl_2) - SumOf(?points_total, ?points_lvl_1, ?points_lvl_2) - Gt(?points_total, 9000) + points(player, 1, points_lvl_1) + points(player, 2, points_lvl_2) + SumOf(points_total, points_lvl_1, points_lvl_2) + Gt(points_total, 9000) ) "#, game_pk = game_pk, diff --git a/src/examples/custom.rs b/src/examples/custom.rs index 6d2f6ae..e9c5422 100644 --- a/src/examples/custom.rs +++ b/src/examples/custom.rs @@ -12,24 +12,24 @@ use crate::{ pub fn eth_dos_batch(params: &Params) -> Result> { let input = r#" eth_friend(src, dst, private: attestation) = AND( - SignedBy(?attestation, ?src) - Contains(?attestation, "attestation", ?dst) + SignedBy(attestation, src) + Contains(attestation, "attestation", dst) ) eth_dos_base(src, dst, distance) = AND( - Equal(?src, ?dst) - Equal(?distance, 0) + Equal(src, dst) + Equal(distance, 0) ) eth_dos_ind(src, dst, distance, private: shorter_distance, intermed) = AND( - eth_dos(?src, ?intermed, ?shorter_distance) - SumOf(?distance, ?shorter_distance, 1) - eth_friend(?intermed, ?dst) + eth_dos(src, intermed, shorter_distance) + SumOf(distance, shorter_distance, 1) + eth_friend(intermed, dst) ) eth_dos(src, dst, distance) = OR( - eth_dos_base(?src, ?dst, ?distance) - eth_dos_ind(?src, ?dst, ?distance) + eth_dos_base(src, dst, distance) + eth_dos_ind(src, dst, distance) ) "#; let batch = parse(input, params, &[]).expect("lang parse").custom_batch; @@ -47,7 +47,7 @@ pub fn eth_dos_request() -> Result { r#" use _, _, _, eth_dos from 0x{batch_id} REQUEST( - eth_dos(?src, ?dst, ?distance) + eth_dos(src, dst, distance) ) "#, ); diff --git a/src/examples/mod.rs b/src/examples/mod.rs index 868f783..7c225cf 100644 --- a/src/examples/mod.rs +++ b/src/examples/mod.rs @@ -82,12 +82,12 @@ pub fn zu_kyc_pod_request(gov_signer: &Value, pay_signer: &Value) -> Result for BuilderArg { diff --git a/src/frontend/pod_request.rs b/src/frontend/pod_request.rs index c057c2b..2de199c 100644 --- a/src/frontend/pod_request.rs +++ b/src/frontend/pod_request.rs @@ -240,8 +240,8 @@ mod tests { let request = parse( r#" REQUEST( - SumOf(?a, ?b, ?c) - Equal(?a, 10) + SumOf(a, b, c) + Equal(a, 10) ) "#, ¶ms, diff --git a/src/lang/grammar.pest b/src/lang/grammar.pest index 252c8ed..89a0e3f 100644 --- a/src/lang/grammar.pest +++ b/src/lang/grammar.pest @@ -11,15 +11,14 @@ WHITESPACE = _{ (" " | "\t" | NEWLINE)+ } // COMMENT matches a line comment (//...\n) or block comment (/*...*/). COMMENT = _{ ("//" ~ (!NEWLINE ~ ANY)* | "/*" ~ (!"*/" ~ ANY)* ~ "*/" ) } -// Define rules for identifiers (predicate names, variable names without '?') +reserved_identifier = { "private" | "true" | "false" } + +// Define rules for identifiers (predicate names, wildcard names) // Must start with alpha or _, followed by alpha, numeric, or _ -identifier = @{ !("private") ~ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* } +identifier = @{ !reserved_identifier ~ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* } private_kw = { "private:" } -// Define wildcard names (start with '?') -wildcard = @{ "?" ~ identifier } - arg_section = { public_arg_list ~ ("," ~ private_kw ~ private_arg_list)? } @@ -49,15 +48,15 @@ custom_predicate_def = { statement_list = { statement+ } -statement_arg = { anchored_key | wildcard | literal_value } +statement_arg = { literal_value | anchored_key | identifier } statement_arg_list = { statement_arg ~ ("," ~ statement_arg)* } statement = { identifier ~ "(" ~ statement_arg_list? ~ ")" } -// Anchored Key: ?Var["key_literal"] or ?Var.key_identifier +// Anchored Key: Var["key_literal"] or Var.key_identifier anchored_key = { - (wildcard ~ "[" ~ literal_string ~ "]") - | (wildcard ~ "." ~ identifier) + (identifier ~ "[" ~ literal_string ~ "]") + | (identifier ~ "." ~ identifier) } // Literal Values (ordered to avoid ambiguity, e.g., string before int) @@ -110,10 +109,10 @@ dict_pair = { literal_string ~ ":" ~ literal_value } // --- Rules for testing full input matching --- test_identifier = { SOI ~ identifier ~ EOI } -test_wildcard = { SOI ~ wildcard ~ EOI } test_literal_int = { SOI ~ literal_int ~ EOI } test_hash_hex = { SOI ~ hash_hex ~ EOI } test_literal_raw = { SOI ~ literal_raw ~ EOI } test_literal_value = { SOI ~ literal_value ~ EOI } test_statement = { SOI ~ statement ~ EOI } +test_statement_arg = { SOI ~ statement_arg ~ EOI } test_custom_predicate_def = { SOI ~ custom_predicate_def ~ EOI } diff --git a/src/lang/mod.rs b/src/lang/mod.rs index b97646a..cc34e6d 100644 --- a/src/lang/mod.rs +++ b/src/lang/mod.rs @@ -62,7 +62,7 @@ mod tests { fn test_e2e_simple_predicate() -> Result<(), LangError> { let input = r#" is_equal(PodA, PodB) = AND( - Equal(?PodA["the_key"], ?PodB["the_key"]) + Equal(PodA["the_key"], PodB["the_key"]) ) "#; @@ -80,8 +80,8 @@ mod tests { let expected_statements = vec![StatementTmpl { pred: Predicate::Native(NativePredicate::Equal), args: vec![ - sta_ak(("PodA", 0), "the_key"), // ?PodA["the_key"] -> Wildcard(0), Key("the_key") - sta_ak(("PodB", 1), "the_key"), // ?PodB["the_key"] -> Wildcard(1), Key("the_key") + sta_ak(("PodA", 0), "the_key"), // PodA["the_key"] -> Wildcard(0), Key("the_key") + sta_ak(("PodB", 1), "the_key"), // PodB["the_key"] -> Wildcard(1), Key("the_key") ], }]; let expected_predicate = CustomPredicate::and( @@ -106,8 +106,8 @@ mod tests { fn test_e2e_simple_request() -> Result<(), LangError> { let input = r#" REQUEST( - Equal(?ConstPod["my_val"], Raw(0x0000000000000000000000000000000000000000000000000000000000000001)) - Lt(?GovPod["dob"], ?ConstPod["my_val"]) + Equal(ConstPod["my_val"], Raw(0x0000000000000000000000000000000000000000000000000000000000000001)) + Lt(GovPod["dob"], ConstPod["my_val"]) ) "#; @@ -124,15 +124,15 @@ mod tests { StatementTmpl { pred: Predicate::Native(NativePredicate::Equal), args: vec![ - sta_ak(("ConstPod", 0), "my_val"), // ?ConstPod["my_val"] -> Wildcard(0), Key("my_val") + sta_ak(("ConstPod", 0), "my_val"), // ConstPod["my_val"] -> Wildcard(0), Key("my_val") sta_lit(RawValue::from(1)), ], }, StatementTmpl { pred: Predicate::Native(NativePredicate::Lt), args: vec![ - sta_ak(("GovPod", 1), "dob"), // ?GovPod["dob"] -> Wildcard(1), Key("dob") - sta_ak(("ConstPod", 0), "my_val"), // ?ConstPod["my_val"] -> Wildcard(0), Key("my_val") + sta_ak(("GovPod", 1), "dob"), // GovPod["dob"] -> Wildcard(1), Key("dob") + sta_ak(("ConstPod", 0), "my_val"), // ConstPod["my_val"] -> Wildcard(0), Key("my_val") ], }, ]; @@ -146,8 +146,8 @@ mod tests { fn test_e2e_predicate_with_private_var() -> Result<(), LangError> { let input = r#" uses_private(A, private: Temp) = AND( - Equal(?A["input_key"], ?Temp["const_key"]) - Equal(?Temp["const_key"], "some_value") + Equal(A["input_key"], Temp["const_key"]) + Equal(Temp["const_key"], "some_value") ) "#; @@ -166,14 +166,14 @@ mod tests { StatementTmpl { pred: Predicate::Native(NativePredicate::Equal), args: vec![ - sta_ak(("A", 0), "input_key"), // ?A["input_key"] -> Wildcard(0), Key("input_key") - sta_ak(("Temp", 1), "const_key"), // ?Temp["const_key"] -> Wildcard(1), Key("const_key") + sta_ak(("A", 0), "input_key"), // A["input_key"] -> Wildcard(0), Key("input_key") + sta_ak(("Temp", 1), "const_key"), // Temp["const_key"] -> Wildcard(1), Key("const_key") ], }, StatementTmpl { pred: Predicate::Native(NativePredicate::Equal), args: vec![ - sta_ak(("Temp", 1), "const_key"), // ?Temp["const_key"] -> Wildcard(1), Key("const_key") + sta_ak(("Temp", 1), "const_key"), // Temp["const_key"] -> Wildcard(1), Key("const_key") sta_lit("some_value"), // Literal("some_value") ], }, @@ -200,11 +200,11 @@ mod tests { fn test_e2e_request_with_custom_call() -> Result<(), LangError> { let input = r#" my_pred(X, Y) = AND( - Equal(?X["val"], ?Y["val"]) + Equal(X["val"], Y["val"]) ) REQUEST( - my_pred(?Pod1, ?Pod2) + my_pred(Pod1, Pod2) ) "#; @@ -222,8 +222,8 @@ mod tests { let expected_pred_statements = vec![StatementTmpl { pred: Predicate::Native(NativePredicate::Equal), args: vec![ - sta_ak(("X", 0), "val"), // ?X["val"] -> Wildcard(0), Key("val") - sta_ak(("Y", 1), "val"), // ?Y["val"] -> Wildcard(1), Key("val") + sta_ak(("X", 0), "val"), // X["val"] -> Wildcard(0), Key("val") + sta_ak(("Y", 1), "val"), // Y["val"] -> Wildcard(1), Key("val") ], }]; let expected_predicate = CustomPredicate::and( @@ -259,15 +259,15 @@ mod tests { #[test] fn test_e2e_request_with_various_args() -> Result<(), LangError> { let input = r#" - some_pred(A, B, C) = AND( Equal(?A["foo"], ?B["bar"]) ) + some_pred(A, B, C) = AND( Equal(A["foo"], B["bar"]) ) REQUEST( some_pred( - ?Var1, // Wildcard + Var1, // Wildcard 12345, // Int Literal "hello_string" // String Literal (Removed invalid AK args) ) - Equal(?AnotherPod["another_key"], ?Var1["some_field"]) + Equal(AnotherPod["another_key"], Var1["some_field"]) ) "#; @@ -280,15 +280,15 @@ mod tests { assert!(!request_templates.is_empty()); // Expected Wildcard Indices in Request Scope: - // ?Var1 -> 0 - // ?AnotherPod -> 1 + // Var1 -> 0 + // AnotherPod -> 1 // Expected structure let expected_templates = vec![ StatementTmpl { pred: Predicate::Custom(CustomPredicateRef::new(batch_result, 0)), // Refers to some_pred args: vec![ - StatementTmplArg::Wildcard(wc("Var1", 0)), // ?Var1 + StatementTmplArg::Wildcard(wc("Var1", 0)), // Var1 StatementTmplArg::Literal(Value::from(12345i64)), // 12345 StatementTmplArg::Literal(Value::from("hello_string")), // "hello_string" ], @@ -296,9 +296,9 @@ mod tests { StatementTmpl { pred: Predicate::Native(NativePredicate::Equal), args: vec![ - // ?AnotherPod["another_key"] -> Wildcard(1), Key("another_key") + // AnotherPod["another_key"] -> Wildcard(1), Key("another_key") sta_ak(("AnotherPod", 1), "another_key"), - // ?Var1["some_field"] -> Wildcard(0), Key("some_field") + // Var1["some_field"] -> Wildcard(0), Key("some_field") sta_ak(("Var1", 0), "some_field"), ], }, @@ -313,11 +313,11 @@ mod tests { fn test_e2e_syntactic_sugar_predicates() -> Result<(), LangError> { let input = r#" REQUEST( - GtEq(?A["foo"], ?B["bar"]) - Gt(?C["baz"], ?D["qux"]) - DictContains(?A["foo"], ?B["bar"], ?C["baz"]) - DictNotContains(?A["foo"], ?B["bar"]) - ArrayContains(?A["foo"], ?B["bar"], ?C["baz"]) + GtEq(A["foo"], B["bar"]) + Gt(C["baz"], D["qux"]) + DictContains(A["foo"], B["bar"], C["baz"]) + DictNotContains(A["foo"], B["bar"]) + ArrayContains(A["foo"], B["bar"], C["baz"]) ) "#; @@ -370,12 +370,12 @@ mod tests { let input = r#" REQUEST( // Order matters for comparison with the hardcoded templates - SetNotContains(?sanctions["sanctionList"], ?gov["idNumber"]) - Lt(?gov["dateOfBirth"], ?SELF_HOLDER_18Y["const_18y"]) - Equal(?pay["startDate"], ?SELF_HOLDER_1Y["const_1y"]) - Equal(?gov["socialSecurityNumber"], ?pay["socialSecurityNumber"]) - Equal(?SELF_HOLDER_18Y["const_18y"], 1169909388) - Equal(?SELF_HOLDER_1Y["const_1y"], 1706367566) + SetNotContains(sanctions["sanctionList"], gov["idNumber"]) + Lt(gov["dateOfBirth"], SELF_HOLDER_18Y["const_18y"]) + Equal(pay["startDate"], SELF_HOLDER_1Y["const_1y"]) + Equal(gov["socialSecurityNumber"], pay["socialSecurityNumber"]) + Equal(SELF_HOLDER_18Y["const_18y"], 1169909388) + Equal(SELF_HOLDER_1Y["const_1y"], 1706367566) ) "#; @@ -406,7 +406,7 @@ mod tests { // Define the request templates using wildcards for constants let expected_templates = vec![ - // 1. NotContains(?sanctions["sanctionList"], ?gov["idNumber"]) + // 1. NotContains(sanctions["sanctionList"], gov["idNumber"]) StatementTmpl { pred: Predicate::Native(NativePredicate::NotContains), args: vec![ @@ -417,7 +417,7 @@ mod tests { sta_ak((wc_gov.name.as_str(), wc_gov.index), id_num_key), ], }, - // 2. Lt(?gov["dateOfBirth"], ?SELF_HOLDER_18Y["const_18y"]) + // 2. Lt(gov["dateOfBirth"], SELF_HOLDER_18Y["const_18y"]) StatementTmpl { pred: Predicate::Native(NativePredicate::Lt), args: vec![ @@ -428,7 +428,7 @@ mod tests { ), ], }, - // 3. Equal(?pay["startDate"], ?SELF_HOLDER_1Y["const_1y"]) + // 3. Equal(pay["startDate"], SELF_HOLDER_1Y["const_1y"]) StatementTmpl { pred: Predicate::Native(NativePredicate::Equal), args: vec![ @@ -436,7 +436,7 @@ mod tests { sta_ak((wc_self_1y.name.as_str(), wc_self_1y.index), const_1y_key), ], }, - // 4. Equal(?gov["socialSecurityNumber"], ?pay["socialSecurityNumber"]) + // 4. Equal(gov["socialSecurityNumber"], pay["socialSecurityNumber"]) StatementTmpl { pred: Predicate::Native(NativePredicate::Equal), args: vec![ @@ -444,7 +444,7 @@ mod tests { sta_ak((wc_pay.name.as_str(), wc_pay.index), ssn_key), ], }, - // 5. Equal(?SELF_HOLDER_18Y["const_18y"], 1169909388) + // 5. Equal(SELF_HOLDER_18Y["const_18y"], 1169909388) StatementTmpl { pred: Predicate::Native(NativePredicate::Equal), args: vec![ @@ -455,7 +455,7 @@ mod tests { sta_lit(now_minus_18y_val.clone()), ], }, - // 6. Equal(?SELF_HOLDER_1Y["const_1y"], 1706367566) + // 6. Equal(SELF_HOLDER_1Y["const_1y"], 1706367566) StatementTmpl { pred: Predicate::Native(NativePredicate::Equal), args: vec![ @@ -494,24 +494,24 @@ mod tests { let input = r#" eth_friend(src, dst, private: attestation_dict) = AND( - SignedBy(?attestation_dict, ?src) - Equal(?attestation_dict["attestation"], ?dst) + SignedBy(attestation_dict, src) + Equal(attestation_dict["attestation"], dst) ) eth_dos_distance_base(src, dst, distance) = AND( - Equal(?src, ?dst) - Equal(?distance, 0) + Equal(src, dst) + Equal(distance, 0) ) eth_dos_distance_ind(src, dst, distance, private: shorter_distance, intermed) = AND( - eth_dos_distance(?src, ?dst, ?distance) - SumOf(?distance, ?shorter_distance, 1) - eth_friend(?intermed, ?dst) + eth_dos_distance(src, dst, distance) + SumOf(distance, shorter_distance, 1) + eth_friend(intermed, dst) ) eth_dos_distance(src, dst, distance) = OR( - eth_dos_distance_base(?src, ?dst, ?distance) - eth_dos_distance_ind(?src, ?dst, ?distance) + eth_dos_distance_base(src, dst, distance) + eth_dos_distance_ind(src, dst, distance) ) "#; @@ -668,8 +668,8 @@ mod tests { let imported_pred_stmts = vec![StatementTmpl { pred: Predicate::Native(NativePredicate::Equal), args: vec![ - sta_ak(("A", 0), "foo"), // ?A["foo"] - sta_ak(("B", 1), "bar"), // ?B["bar"] + sta_ak(("A", 0), "foo"), // A["foo"] + sta_ak(("B", 1), "bar"), // B["bar"] ], }]; let imported_predicate = CustomPredicate::and( @@ -690,7 +690,7 @@ mod tests { use imported_pred from 0x{} REQUEST( - imported_pred(?Pod1, ?Pod2) + imported_pred(Pod1, Pod2) ) "#, batch_id_str @@ -741,8 +741,8 @@ mod tests { use pred_one, _, pred_three from 0x{} REQUEST( - pred_one(?Pod1) - pred_three(?Pod2) + pred_one(Pod1) + pred_three(Pod2) ) "#, batch_id_str @@ -799,7 +799,7 @@ mod tests { use imported_eq from 0x{} wrapper_pred(X, Y) = AND( - imported_eq(?X, ?Y) + imported_eq(X, Y) ) "#, batch_id_str @@ -848,12 +848,12 @@ mod tests { let input = format!( r#" REQUEST( - Equal(?A["pk"], {}) - Equal(?B["raw"], {}) - Equal(?C["string"], {}) - Equal(?D["int"], {}) - Equal(?E["bool"], {}) - Equal(?F["sk"], {}) + Equal(A["pk"], {}) + Equal(B["raw"], {}) + Equal(C["string"], {}) + Equal(D["int"], {}) + Equal(E["bool"], {}) + Equal(F["sk"], {}) ) "#, Value::from(pk).to_podlang_string(), @@ -865,13 +865,13 @@ mod tests { ); /* REQUEST( - Equal(?A["pk"], PublicKey(3t9fNuU194n7mSJPRdeaJRMqw6ZQCUddzvECWNe1k2b1rdBezXpJxF)) - Equal(?C["raw"], Raw(0x0000000000000000000000000000000000000000000000000000000000000001)) - Equal(?D["string"], "hello") - Equal(?E["int"], 123) - Equal(?F["bool"], true) - Equal(?G["sk"], SecretKey(random_secret_key_base_64)) - Equal(?H["self"], SELF) + Equal(A["pk"], PublicKey(3t9fNuU194n7mSJPRdeaJRMqw6ZQCUddzvECWNe1k2b1rdBezXpJxF)) + Equal(C["raw"], Raw(0x0000000000000000000000000000000000000000000000000000000000000001)) + Equal(D["string"], "hello") + Equal(E["int"], 123) + Equal(F["bool"], true) + Equal(G["sk"], SecretKey(random_secret_key_base_64)) + Equal(H["self"], SELF) ) */ @@ -947,8 +947,8 @@ mod tests { let input = r#" identity_verified(username, private: identity_dict) = AND( - Equal(?identity_dict["username"], ?username) - Equal(?identity_dict["user_public_key"], ?user_public_key) + Equal(identity_dict["username"], username) + Equal(identity_dict["user_public_key"], user_public_key) ) "# .to_string(); diff --git a/src/lang/parser.rs b/src/lang/parser.rs index 12bd22e..69eecc4 100644 --- a/src/lang/parser.rs +++ b/src/lang/parser.rs @@ -32,9 +32,9 @@ pub fn parse_podlang(input: &str) -> Result, ParseError> { mod tests { use super::*; - fn assert_parses(rule: Rule, input: &str) { + fn assert_parses(rule: Rule, input: &str) -> Pairs<'_, Rule> { match PodlangParser::parse(rule, input) { - Ok(_) => (), // Successfully parsed + Ok(pairs) => pairs, // Successfully parsed Err(e) => panic!("Failed to parse input:\n{}\nError: {}", input, e), } } @@ -79,26 +79,52 @@ mod tests { #[test] fn test_parse_wildcard() { - assert_parses(Rule::wildcard, "?Var"); - assert_parses(Rule::wildcard, "?_Internal"); - assert_parses(Rule::wildcard, "?X1"); - assert_fails(Rule::test_wildcard, "NotAVar"); // Use test rule - assert_fails(Rule::test_wildcard, "?"); // Use test rule - assert_fails(Rule::test_wildcard, "?invalid-char"); // Use test rule + assert_parses(Rule::identifier, "Var"); + assert_parses(Rule::identifier, "_Internal"); + assert_parses(Rule::identifier, "X1"); + assert_fails(Rule::test_identifier, ""); // Use test rule + assert_fails(Rule::test_identifier, "invalid-char"); // Use test rule + assert_fails(Rule::test_identifier, "?noMoreQuestionMarks"); // Use test rule + assert_fails(Rule::test_identifier, "123noStartingDigits"); // Use test rule + assert_fails(Rule::test_identifier, "true"); // Use test rule + assert_fails(Rule::test_identifier, "false"); // Use test rule } #[test] fn test_parse_anchored_key() { - assert_parses(Rule::anchored_key, "?PodVar[\"literal key\"]"); - assert_parses(Rule::anchored_key, "?PodVar.literal_key"); - assert_fails(Rule::anchored_key, "PodVar[\"key\"]"); // Needs wildcard for pod - assert_fails(Rule::anchored_key, "PodVar.key"); // Needs wildcard for pod - assert_fails(Rule::anchored_key, "?PodVar[invalid_key]"); // Key must be literal string - assert_fails(Rule::anchored_key, "?PodVar.123"); // Key must be valid identifier - assert_fails(Rule::anchored_key, "?PodVar[]"); // Key cannot be empty - assert_fails(Rule::anchored_key, "?PodVar."); // Key cannot be empty - assert_fails(Rule::anchored_key, "?PodVar[?key]"); // Key cannot be wildcard - assert_fails(Rule::anchored_key, "?PodVar.?key"); // Key cannot be wildcard + assert_parses(Rule::anchored_key, "PodVar[\"literal_key\"]"); + assert_parses(Rule::anchored_key, "PodVar.literal_key"); + assert_fails(Rule::anchored_key, "PodVar[invalid_key]"); // Key must be literal string + assert_fails(Rule::anchored_key, "PodVar.123"); // Key must be valid identifier + assert_fails(Rule::anchored_key, "PodVar[]"); // Key cannot be empty + assert_fails(Rule::anchored_key, "PodVar."); // Key cannot be empty + assert_fails(Rule::anchored_key, "?PodVar[\"key\"]"); // No more question marks on wildcards + assert_fails(Rule::anchored_key, "?PodVar.key"); // No more question marks on wildcards + } + + #[test] + fn test_parse_arg_ambiguity() { + fn assert_inner(rule: &Rule, input: &str) { + assert_eq!( + assert_parses(Rule::test_statement_arg, input) + .next() + .unwrap() + .into_inner() + .next() + .unwrap() + .into_inner() + .next() + .unwrap() + .as_rule(), + *rule + ); + } + + // Ensure different types of args parse in the right priority order. + assert_inner(&Rule::identifier, "someVar"); + assert_inner(&Rule::anchored_key, "someVar[\"key\"]"); + assert_inner(&Rule::literal_value, "true"); + assert_inner(&Rule::literal_value, "PublicKey(abc)"); } #[test] @@ -179,10 +205,10 @@ mod tests { // Trimmed leading/trailing whitespace r#"REQUEST( // Check equality - Equal(?gov["socialSecurityNumber"], ?pay["socialSecurityNumber"]) + Equal(gov["socialSecurityNumber"], pay["socialSecurityNumber"]) // Check age > 18 - ValueOf(?const_holder["const_18y"], 1169909388) - Lt(?gov["dateOfBirth"], ?const_holder["const_18y"]) + ValueOf(const_holder["const_18y"], 1169909388) + Lt(gov["dateOfBirth"], const_holder["const_18y"]) )"#, ); } @@ -193,14 +219,14 @@ mod tests { Rule::test_custom_predicate_def, // Trimmed leading/trailing whitespace r#"my_pred(A, B) = AND( - Equal(?A["foo"], ?B["bar"]) + Equal(A["foo"], B["bar"]) )"#, ); assert_parses( Rule::test_custom_predicate_def, // Trimmed leading/trailing whitespace r#"pred_with_private(X, private: TempKey) = OR( - Equal(?X["key"], 1234) + Equal(X["key"], 1234) )"#, ); assert_fails( @@ -216,15 +242,15 @@ mod tests { r#"// File defining one predicate and one request is_valid_user(UserPod, private: ConstVal) = AND( // User age must be > 18 (using a constant value) - ValueOf(?ConstVal["min_age"], 18) - Gt(?UserPod["age"], ?ConstVal["min_age"]) + ValueOf(ConstVal["min_age"], 18) + Gt(UserPod["age"], ConstVal["min_age"]) // User must not be banned - NotContains(?_BANNED_USERS.list, ?UserPod.userId) + NotContains(_BANNED_USERS.list, UserPod.userId) ) REQUEST( - is_valid_user(?SomeUser) - Equal(?SomeUser["country"], ?Other["country"]) + is_valid_user(SomeUser) + Equal(SomeUser["country"], Other["country"]) )"#, ); } diff --git a/src/lang/pretty_print.rs b/src/lang/pretty_print.rs index 6a3a800..5ba3470 100644 --- a/src/lang/pretty_print.rs +++ b/src/lang/pretty_print.rs @@ -227,7 +227,7 @@ mod tests { fn test_simple_predicate_pretty_print() { let params = Params::default(); - // Create a simple predicate: is_equal(PodA, PodB) = AND(Equal(?PodA["key"], ?PodB["key"])) + // Create a simple predicate: is_equal(PodA, PodB) = AND(Equal(PodA["key"], PodB["key"])) let statements = vec![StatementTmpl { pred: Predicate::Native(NativePredicate::Equal), args: vec![ @@ -254,7 +254,7 @@ mod tests { let pretty_printed = predicate.to_podlang_string(); let expected = r#"is_equal(PodA, PodB) = AND( - Equal(?PodA["key"], ?PodB["key"]) + Equal(PodA["key"], PodB["key"]) )"#; assert_eq!(pretty_printed, expected); } @@ -263,7 +263,7 @@ mod tests { fn test_predicate_with_private_args() { let params = Params::default(); - // Create: uses_private(A, private: Temp) = AND(Equal(?A["input"], ?Temp["const"])) + // Create: uses_private(A, private: Temp) = AND(Equal(A["input"], Temp["const"])) let statements = vec![StatementTmpl { pred: Predicate::Native(NativePredicate::Equal), args: vec![ @@ -290,7 +290,7 @@ mod tests { let pretty_printed = predicate.to_podlang_string(); let expected = r#"uses_private(A, private: Temp) = AND( - Equal(?A["input"], ?Temp["const"]) + Equal(A["input"], Temp["const"]) )"#; assert_eq!(pretty_printed, expected); } @@ -299,7 +299,7 @@ mod tests { fn test_statement_with_literal_args() { let params = Params::default(); - // Create: check_value(Pod) = AND(Equal(?Pod["field"], 42)) + // Create: check_value(Pod) = AND(Equal(Pod["field"], 42)) let statements = vec![StatementTmpl { pred: Predicate::Native(NativePredicate::Equal), args: vec![ @@ -323,7 +323,7 @@ mod tests { let pretty_printed = predicate.to_podlang_string(); let expected = r#"check_value(Pod) = AND( - Equal(?Pod["field"], 42) + Equal(Pod["field"], 42) )"#; assert_eq!(pretty_printed, expected); } @@ -332,7 +332,7 @@ mod tests { fn test_or_predicate() { let params = Params::default(); - // Create: either_or(A, B) = OR(Equal(?A["x"], 1), Equal(?B["y"], 2)) + // Create: either_or(A, B) = OR(Equal(A["x"], 1), Equal(B["y"], 2)) let statements = vec![ StatementTmpl { pred: Predicate::Native(NativePredicate::Equal), @@ -368,8 +368,8 @@ mod tests { let pretty_printed = predicate.to_podlang_string(); let expected = r#"either_or(A, B) = OR( - Equal(?A["x"], 1) - Equal(?B["y"], 2) + Equal(A["x"], 1) + Equal(B["y"], 2) )"#; assert_eq!(pretty_printed, expected); } @@ -402,7 +402,7 @@ mod tests { fn test_round_trip_simple_predicate() { let input = r#" simple_equal(PodA, PodB) = AND( - Equal(?PodA["key"], ?PodB["key"]) + Equal(PodA["key"], PodB["key"]) ) "#; assert_round_trip(input); @@ -412,8 +412,8 @@ mod tests { fn test_round_trip_predicate_with_private_args() { let input = r#" uses_private(A, private: Temp) = AND( - Equal(?A["input_key"], ?Temp["const_key"]) - Equal(?Temp["const_key"], "some_value") + Equal(A["input_key"], Temp["const_key"]) + Equal(Temp["const_key"], "some_value") ) "#; assert_round_trip(input); @@ -423,8 +423,8 @@ mod tests { fn test_round_trip_or_predicate() { let input = r#" either_condition(X, Y) = OR( - Equal(?X["status"], "active") - Equal(?Y["type"], 1) + Equal(X["status"], "active") + Equal(Y["type"], 1) ) "#; assert_round_trip(input); @@ -434,12 +434,12 @@ mod tests { fn test_round_trip_multiple_predicates() { let input = r#" pred_one(A) = AND( - Equal(?A["field"], 42) + Equal(A["field"], 42) ) pred_two(B, C) = AND( - Equal(?B["value"], ?C["value"]) - NotEqual(?B["id"], ?C["id"]) + Equal(B["value"], C["value"]) + NotEqual(B["id"], C["id"]) ) "#; assert_round_trip(input); @@ -449,10 +449,10 @@ mod tests { fn test_round_trip_various_literals() { let input = r#" literal_test(Pod) = AND( - Equal(?Pod["int_field"], 123) - Equal(?Pod["string_field"], "hello world") - Equal(?Pod["bool_field"], true) - NotEqual(?Pod["other_bool"], false) + Equal(Pod["int_field"], 123) + Equal(Pod["string_field"], "hello world") + Equal(Pod["bool_field"], true) + NotEqual(Pod["other_bool"], false) ) "#; assert_round_trip(input); @@ -462,11 +462,11 @@ mod tests { fn test_round_trip_complex_predicate() { let input = r#" complex_predicate(User, Document, private: Verifier, Timestamp) = AND( - Equal(?User["active"], true) - Equal(?Document["owner"], ?User["id"]) - Equal(?Verifier["type"], 1) - Lt(?Timestamp["created"], ?Timestamp["expires"]) - NotContains(?Document["blocked_users"], ?User["id"]) + Equal(User["active"], true) + Equal(Document["owner"], User["id"]) + Equal(Verifier["type"], 1) + Lt(Timestamp["created"], Timestamp["expires"]) + NotContains(Document["blocked_users"], User["id"]) ) "#; assert_round_trip(input); @@ -476,10 +476,10 @@ mod tests { fn test_round_trip_with_sum_and_hash_operations() { let input = r#" math_operations(A, B, C) = AND( - SumOf(?A["value"], ?B["value"], ?C["total"]) - ProductOf(?A["factor"], ?B["factor"], ?C["product"]) - MaxOf(?A["score"], ?B["score"], ?C["max_score"]) - HashOf(?A["data"], ?B["salt"], ?C["hash"]) + SumOf(A["value"], B["value"], C["total"]) + ProductOf(A["factor"], B["factor"], C["product"]) + MaxOf(A["score"], B["score"], C["max_score"]) + HashOf(A["data"], B["salt"], C["hash"]) ) "#; assert_round_trip(input); @@ -489,13 +489,13 @@ mod tests { fn test_round_trip_nested_custom_calls() { let input = r#" base_check(Pod) = AND( - Equal(?Pod["status"], "valid") + Equal(Pod["status"], "valid") ) derived_check(PodA, PodB) = AND( - base_check(?PodA) - base_check(?PodB) - NotEqual(?PodA["id"], ?PodB["id"]) + base_check(PodA) + base_check(PodB) + NotEqual(PodA["id"], PodB["id"]) ) "#; assert_round_trip(input); @@ -505,8 +505,8 @@ mod tests { fn test_round_trip_container_operations() { let input = r#" container_checks(List, Item, Dict, Key, Value) = AND( - Contains(?List, ?Item, ?Value) - NotContains(?Dict, ?Key) + Contains(List, Item, Value) + NotContains(Dict, Key) ) "#; assert_round_trip(input); @@ -518,7 +518,7 @@ mod tests { let input = format!( r#" secret_key_test(Pod) = AND( - Equal(?Pod["sk"], {}) + Equal(Pod["sk"], {}) ) "#, Value::from(sk.clone()).to_podlang_string() @@ -530,13 +530,13 @@ mod tests { fn test_pretty_print_demonstration() { let input = r#" base_check(Pod) = AND( - Equal(?Pod["status"], "valid") + Equal(Pod["status"], "valid") ) derived_check(PodA, PodB) = AND( - base_check(?PodA) - base_check(?PodB) - NotEqual(?PodA["id"], ?PodB["id"]) + base_check(PodA) + base_check(PodB) + NotEqual(PodA["id"], PodB["id"]) ) "#; @@ -603,7 +603,7 @@ mod tests { let input = format!( r#" test_pred(Pod) = AND( - Equal(?Pod["field"], "{}") + Equal(Pod["field"], "{}") ) "#, // Manually escape for the input - this simulates what would be in actual Podlang source diff --git a/src/lang/processor.rs b/src/lang/processor.rs index 607c65e..37b4588 100644 --- a/src/lang/processor.rs +++ b/src/lang/processor.rs @@ -319,8 +319,8 @@ fn pest_pair_to_builder_arg( let value = process_literal_value(params, arg_content_pair)?; Ok(BuilderArg::Literal(value)) } - Rule::wildcard => { - let wc_str = arg_content_pair.as_str().strip_prefix("?").unwrap(); + Rule::identifier => { + let wc_str = arg_content_pair.as_str(); if let StatementContext::CustomPredicate { argument_names, pred_name, @@ -339,7 +339,7 @@ fn pest_pair_to_builder_arg( Rule::anchored_key => { let mut inner_ak_pairs = arg_content_pair.clone().into_inner(); let root_pair = inner_ak_pairs.next().unwrap(); - let root_wc_str = root_pair.as_str().strip_prefix("?").unwrap(); + let root_wc_str = root_pair.as_str(); if let StatementContext::CustomPredicate { argument_names, @@ -1072,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); @@ -1089,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); @@ -1111,8 +1111,8 @@ mod processor_tests { #[test] fn test_fp_multiple_predicates() -> Result<(), ProcessorError> { let input = r#" - pred1(X) = AND( Equal(?X["k"],?X.k) ) - pred2(Y, Z) = OR( Equal(?Y["v"], 123) ) + pred1(X) = AND( Equal(X["k"],X.k) ) + pred2(Y, Z) = OR( Equal(Y["v"], 123) ) "#; let pairs = get_document_content_pairs(input)?; let params = Params::default(); @@ -1203,7 +1203,7 @@ mod processor_tests { fn test_fp_mixed_content() -> Result<(), ProcessorError> { let input = r#" pred_one(X) = AND(None()) - REQUEST( pred_one(?A) ) + REQUEST( pred_one(A) ) pred_two(Y, Z) = OR(None()) "#; let pairs = get_document_content_pairs(input)?; @@ -1231,7 +1231,7 @@ mod processor_tests { .as_ref() .unwrap() .as_str() - .contains("pred_one(?A)")); + .contains("pred_one(A)")); Ok(()) } @@ -1241,7 +1241,7 @@ mod processor_tests { // Undefined predicates will be flagged as an error on the second pass let input = r#" REQUEST( - pred_one(?A) + pred_one(A) ) "#; let pairs = get_document_content_pairs(input)?; @@ -1260,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)?; diff --git a/src/middleware/custom.rs b/src/middleware/custom.rs index 9b2efc1..306e464 100644 --- a/src/middleware/custom.rs +++ b/src/middleware/custom.rs @@ -24,9 +24,9 @@ impl Wildcard { impl fmt::Display for Wildcard { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if f.alternate() { - write!(f, "?{}:{}", self.index, self.name) + write!(f, "{}:{}", self.index, self.name) } else { - write!(f, "?{}", self.name) + write!(f, "{}", self.name) } } }