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

Misc #466

Merged
merged 3 commits into from
Sep 13, 2023
Merged

Misc #466

Show file tree
Hide file tree
Changes from all commits
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
6 changes: 5 additions & 1 deletion rust/candid/src/parser/grammar.lalrpop
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::types::value::{IDLField, IDLValue, IDLArgs, VariantValue};
use crate::types::{TypeEnv, FuncMode};
use crate::utils::check_unique;
use super::types::{IDLType, PrimType, TypeField, FuncType, Binding, Dec, IDLProg, IDLTypes};
use super::types::{IDLType, PrimType, TypeField, FuncType, Binding, Dec, IDLProg, IDLTypes, IDLInitArgs};
use super::test::{Assert, Input, Test};
use super::token::{Token, error2, LexicalError, Span};
use crate::{Principal, types::Label};
Expand Down Expand Up @@ -272,6 +272,10 @@ pub IDLProg: IDLProg = {
<decs:SepBy<Def, ";">> <actor:MainActor?> => IDLProg { decs, actor }
}

pub IDLInitArgs: IDLInitArgs = {
<decs:SepBy<Def, ";">> <args:TupTyp> => IDLInitArgs { decs, args }
}

// Test file

Input: Input = {
Expand Down
13 changes: 13 additions & 0 deletions rust/candid/src/parser/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,26 @@ pub struct IDLProg {
pub actor: Option<IDLType>,
}

#[derive(Debug)]
pub struct IDLInitArgs {
pub decs: Vec<Dec>,
pub args: Vec<IDLType>,
}

impl std::str::FromStr for IDLProg {
type Err = crate::Error;
fn from_str(str: &str) -> Result<Self> {
let lexer = super::token::Tokenizer::new(str);
Ok(super::grammar::IDLProgParser::new().parse(lexer)?)
}
}
impl std::str::FromStr for IDLInitArgs {
type Err = crate::Error;
fn from_str(str: &str) -> Result<Self> {
let lexer = super::token::Tokenizer::new(str);
Ok(super::grammar::IDLInitArgsParser::new().parse(lexer)?)
}
}

impl std::str::FromStr for IDLType {
type Err = crate::Error;
Expand Down
18 changes: 17 additions & 1 deletion rust/candid/src/parser/typing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,12 +241,28 @@ fn load_imports(
}

/// Type check IDLProg and adds bindings to type environment. Returns
/// a hash map for the serivce method signatures. This function ignores the imports.
/// the main actor if present. This function ignores the imports.
pub fn check_prog(te: &mut TypeEnv, prog: &IDLProg) -> Result<Option<Type>> {
let mut env = Env { te, pre: false };
check_decs(&mut env, &prog.decs)?;
check_actor(&env, &prog.actor)
}
/// Type check init args extracted from canister metadata candid:args.
/// Need to provide `main_env`, because init args may refer to variables from the main did file.
pub fn check_init_args(
te: &mut TypeEnv,
main_env: &TypeEnv,
prog: &IDLInitArgs,
) -> Result<Vec<Type>> {
let mut env = Env { te, pre: false };
check_decs(&mut env, &prog.decs)?;
env.te.merge(main_env)?;
let mut args = Vec::new();
for arg in prog.args.iter() {
args.push(check_type(&env, arg)?);
}
Ok(args)
}

fn check_file_(file: &Path, is_pretty: bool) -> Result<(TypeEnv, Option<Type>)> {
let base = if file.is_absolute() {
Expand Down
32 changes: 28 additions & 4 deletions rust/candid/src/types/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,17 +356,41 @@ impl fmt::Display for Field {
/// Construct a field type, which can be used in `TypeInner::Record` and `TypeInner::Variant`.
///
/// `field!{ a: TypeInner::Nat.into() }` expands to `Field { id: Label::Named("a"), ty: ... }`
/// `field!{ 0: TypeInner::Nat.into() }` expands to `Field { id: Label::Id(0), ty: ... }`
/// `field!{ 0: Nat::ty() }` expands to `Field { id: Label::Id(0), ty: ... }`
macro_rules! field {
{ $id:tt : $ty:expr } => {
{ $id:tt : $ty:expr } => {{
$crate::types::internal::Field {
id: match stringify!($id).parse::<u32>() {
Ok(id) => $crate::types::Label::Id(id),
Err(_) => $crate::types::Label::Named(stringify!($id).to_string()),
}.into(),
ty: $ty
}
};
}}
}
#[macro_export]
/// Construct a record type, e.g., `record!{ label: Nat::ty(); 42: String::ty() }`.
macro_rules! record {
{ $($id:tt : $ty:expr);* $(;)? } => {{
let mut fs: Vec<$crate::types::internal::Field> = vec![ $($crate::field!{$id : $ty}),* ];
fs.sort_unstable_by_key(|f| f.id.get_id());
if let Err(e) = $crate::utils::check_unique(fs.iter().map(|f| &f.id)) {
panic!("{e}");
}
Into::<$crate::types::Type>::into($crate::types::TypeInner::Record(fs))
}}
}
#[macro_export]
/// Construct a variant type, e.g., `variant!{ tag: <()>::ty() }`.
macro_rules! variant {
{ $($id:tt : $ty:expr);* $(;)? } => {{
let mut fs: Vec<$crate::types::internal::Field> = vec![ $($crate::field!{$id : $ty}),* ];
fs.sort_unstable_by_key(|f| f.id.get_id());
if let Err(e) = $crate::utils::check_unique(fs.iter().map(|f| &f.id)) {
panic!("{e}");
}
Into::<$crate::types::Type>::into($crate::types::TypeInner::Variant(fs))
}}
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -431,7 +455,7 @@ macro_rules! func {
/// `service!{ "f": func!((HttpRequest) -> ()) }` expands to `Type(Rc::new(TypeInner::Service(...)))`
macro_rules! service {
{ $($meth:tt : $ty:expr);* $(;)? } => {{
let mut ms = vec![ $(($meth.to_string(), $ty)),* ];
let mut ms: Vec<(String, $crate::types::Type)> = vec![ $(($meth.to_string(), $ty)),* ];
ms.sort_unstable_by(|a, b| a.0.as_str().partial_cmp(b.0.as_str()).unwrap());
if let Err(e) = $crate::utils::check_unique(ms.iter().map(|m| &m.0)) {
panic!("{e}");
Expand Down
24 changes: 23 additions & 1 deletion rust/candid/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ pub fn service_equal(left: CandidSource, right: CandidSource) -> Result<()> {
#[cfg_attr(docsrs, doc(cfg(feature = "parser")))]
#[cfg(feature = "parser")]
/// Take a did file and outputs the init args and the service type (without init args).
/// If the original did file contains imports, the output flatens the type definitions.
/// If the original did file contains imports, the output flattens the type definitions.
/// For now, the comments from the original did file is omitted.
pub fn instantiate_candid(candid: CandidSource) -> Result<(Vec<Type>, (TypeEnv, Type))> {
use crate::types::TypeInner;
Expand All @@ -95,6 +95,28 @@ pub fn instantiate_candid(candid: CandidSource) -> Result<(Vec<Type>, (TypeEnv,
})
}

/// Merge canister metadata candid:args and candid:service into a service constructor.
/// If candid:service already contains init args, returns the original did file.
#[cfg_attr(docsrs, doc(cfg(feature = "parser")))]
#[cfg(feature = "parser")]
pub fn merge_init_args(candid: &str, init: &str) -> Result<(TypeEnv, Type)> {
use crate::parser::{types::IDLInitArgs, typing::check_init_args};
use crate::types::TypeInner;
let candid = CandidSource::Text(candid);
let (env, serv) = candid.load()?;
let serv = serv.ok_or_else(|| Error::msg("the Candid interface has no main service type"))?;
match serv.as_ref() {
TypeInner::Class(_, _) => Ok((env, serv)),
TypeInner::Service(_) => {
let prog = init.parse::<IDLInitArgs>()?;
let mut env2 = TypeEnv::new();
let args = check_init_args(&mut env2, &env, &prog)?;
Ok((env2, TypeInner::Class(args, serv).into()))
}
_ => unreachable!(),
}
}

/// Encode sequence of Rust values into Candid message of type `candid::Result<Vec<u8>>`.
#[macro_export]
macro_rules! Encode {
Expand Down
11 changes: 11 additions & 0 deletions rust/candid/tests/parse_value.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use candid::types::value::{IDLArgs, IDLField, IDLValue, VariantValue};
use candid::types::{Label, Type, TypeEnv, TypeInner};
use candid::{record, variant, CandidType, Nat};

fn parse_args(input: &str) -> IDLArgs {
input.parse().unwrap()
Expand Down Expand Up @@ -136,6 +137,12 @@ fn parse_optional_record() {
let mut args =
parse_args("(opt record {}, record { 1=42;44=\"test\"; 2=false }, variant { 5=null })");
let typ = parse_type("record { 1: nat; 44: text; 2: bool }");
assert_eq!(
typ,
record! { 1: Nat::ty(); 44: String::ty(); 2: bool::ty() }
);
assert_eq!(args.args[0].value_ty(), TypeInner::Opt(record! {}).into());
assert_eq!(args.args[2].value_ty(), variant! { 5: <()>::ty() });
args.args[1] = args.args[1]
.annotate_type(true, &TypeEnv::new(), &typ)
.unwrap();
Expand Down Expand Up @@ -180,6 +187,10 @@ fn parse_nested_record() {
let typ = parse_type(
"record {label: nat; 0x2b:record { test:text; \"opt\":text }; long_label: opt null }",
);
assert_eq!(
typ,
record! {label: Nat::ty(); 43: record!{ test: String::ty(); opt: String::ty() }; long_label: Option::<()>::ty(); }
);
args.args[0] = args.args[0]
.annotate_type(true, &TypeEnv::new(), &typ)
.unwrap();
Expand Down
60 changes: 33 additions & 27 deletions tools/ui/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion tools/ui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,8 @@ members = [

[profile.release]
lto = true
opt-level = 'z'
opt-level = 2

[patch.crates-io.candid]
path = "../../rust/candid"

1 change: 1 addition & 0 deletions tools/ui/src/didjs/didjs.did
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ service : {
did_to_js : (text) -> (opt text) query;
binding : (text, text) -> (opt text) query;
subtype : (new: text, old: text) -> (variant { Ok: null; Err: text }) query;
merge_init_args : (text, text) -> (opt text) query
}
Loading
Loading