diff --git a/CHANGELOG.md b/CHANGELOG.md index c52086976..884c2565e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,42 @@ ### Candid * Breaking changes: - + `pp_args` and `pp_init_args` noew require a `&[ArgType]` parameter. The `pp_rets` function has been added, with the signature of the old `pp_args`. + + `pp_args` and `pp_init_args` now require a `&[ArgType]` parameter. The `pp_rets` function has been added, with the signature of the old `pp_args`. + +* Non-breaking changes: + + The following structs have been moved from the `candid_parser` crate to the `candid::types::syntax` module: + - `IDLType` + - `IDLTypes` + - `PrimType` + - `FuncType` + - `IDLArgType` + - `TypeField` + - `Dec` + - `Binding` + - `IDLProg` + - `IDLInitArgs` ### candid_parser * Breaking changes: + + The following structs have been moved to the `candid` crate: + - `IDLType` + - `IDLTypes` + - `PrimType` + - `FuncType` + - `IDLArgType` + - `TypeField` + - `Dec` + - `Binding` + - `IDLProg` + - `IDLInitArgs` + As a consequence, the `FromStr` trait is no longer implemented for the following types: + - `IDLProg` + - `IDLInitArgs` + - `IDLType` + - `IDLTypes` + You must now use the `parse_idl_prog`, `parse_idl_init_args`, `parse_idl_type` and `parse_idl_types` functions to parse these types, respectively. + + `pretty_parse` doesn't work anymore with the `IDLProg` and `IDLTypes` types. Use `pretty_parse_idl_prog` and `pretty_parse_idl_types` instead. + The `args` field in both `FuncType` and `IDLInitArgs` now have type `Vec`. * Non-breaking changes: diff --git a/rust/candid/src/types/mod.rs b/rust/candid/src/types/mod.rs index 72b385c90..93852a7e7 100644 --- a/rust/candid/src/types/mod.rs +++ b/rust/candid/src/types/mod.rs @@ -9,6 +9,7 @@ use serde::ser::Error; mod impls; pub mod internal; pub mod subtype; +pub mod syntax; pub mod type_env; #[cfg_attr(docsrs, doc(cfg(feature = "value")))] #[cfg(feature = "value")] diff --git a/rust/candid_parser/src/types.rs b/rust/candid/src/types/syntax.rs similarity index 65% rename from rust/candid_parser/src/types.rs rename to rust/candid/src/types/syntax.rs index 91ed6efdb..1f943ea2d 100644 --- a/rust/candid_parser/src/types.rs +++ b/rust/candid/src/types/syntax.rs @@ -1,7 +1,6 @@ -use crate::Result; -use candid::types::{FuncMode, Label}; +use crate::types::{FuncMode, Label}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum IDLType { PrimT(PrimType), VarT(String), @@ -60,14 +59,14 @@ pub enum PrimType { Empty, }} -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct FuncType { pub modes: Vec, pub args: Vec, pub rets: Vec, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct IDLArgType { pub typ: IDLType, pub name: Option, @@ -91,7 +90,7 @@ impl IDLArgType { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct TypeField { pub label: Label, pub typ: IDLType, @@ -104,13 +103,13 @@ pub enum Dec { ImportServ(String), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Binding { pub id: String, pub typ: IDLType, } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct IDLProg { pub decs: Vec, pub actor: Option, @@ -121,34 +120,3 @@ pub struct IDLInitArgs { pub decs: Vec, pub args: Vec, } - -impl std::str::FromStr for IDLProg { - type Err = crate::Error; - fn from_str(str: &str) -> Result { - 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 { - let lexer = super::token::Tokenizer::new(str); - Ok(super::grammar::IDLInitArgsParser::new().parse(lexer)?) - } -} - -impl std::str::FromStr for IDLType { - type Err = crate::Error; - fn from_str(str: &str) -> Result { - let lexer = super::token::Tokenizer::new(str); - Ok(super::grammar::TypParser::new().parse(lexer)?) - } -} - -impl std::str::FromStr for IDLTypes { - type Err = crate::Error; - fn from_str(str: &str) -> Result { - let lexer = super::token::Tokenizer::new(str); - Ok(super::grammar::TypsParser::new().parse(lexer)?) - } -} diff --git a/rust/candid_parser/src/bindings/motoko.rs b/rust/candid_parser/src/bindings/motoko.rs index f7ceb949a..29dac5dba 100644 --- a/rust/candid_parser/src/bindings/motoko.rs +++ b/rust/candid_parser/src/bindings/motoko.rs @@ -3,8 +3,8 @@ use candid::pretty::candid::is_valid_as_id; use candid::pretty::utils::*; -use candid::types::{ArgType, FuncMode}; -use candid::types::{Field, Function, Label, SharedLabel, Type, TypeEnv, TypeInner}; +use candid::types::{ArgType, Field, FuncMode, Function, Label, SharedLabel, Type, TypeInner}; +use candid::TypeEnv; use pretty::RcDoc; // The definition of tuple is language specific. diff --git a/rust/candid_parser/src/error.rs b/rust/candid_parser/src/error.rs index 75b46e3e8..8ebd53cf3 100644 --- a/rust/candid_parser/src/error.rs +++ b/rust/candid_parser/src/error.rs @@ -1,10 +1,11 @@ //! When serializing or deserializing Candid goes wrong. +use candid::types::syntax::{IDLProg, IDLTypes}; use codespan_reporting::diagnostic::Label; use std::io; use thiserror::Error; -use crate::token; +use crate::{parse_idl_prog, parse_idl_types, token}; use codespan_reporting::{ diagnostic::Diagnostic, files::{Error as ReportError, SimpleFile}, @@ -105,14 +106,20 @@ impl From for Error { } } +/// Does not work for parsing [IDLProg] and [IDLTypes], use [pretty_parse_idl_prog] and [pretty_parse_idl_types] instead. pub fn pretty_parse(name: &str, str: &str) -> Result where T: std::str::FromStr, { - str.parse::().or_else(|e| { - pretty_diagnose(name, str, &e)?; - Err(e) - }) + str.parse::().or_else(|e| pretty_print_err(name, str, e)) +} + +pub fn pretty_parse_idl_prog(name: &str, str: &str) -> Result { + parse_idl_prog(str).or_else(|e| pretty_print_err(name, str, e)) +} + +pub fn pretty_parse_idl_types(name: &str, str: &str) -> Result { + parse_idl_types(str).or_else(|e| pretty_print_err(name, str, e)) } /// Wrap the parser error and pretty print the error message. @@ -122,10 +129,12 @@ where /// # Ok::<(), candid_parser::Error>(()) /// ``` pub fn pretty_wrap(name: &str, str: &str, f: impl FnOnce(&str) -> Result) -> Result { - f(str).or_else(|e| { - pretty_diagnose(name, str, &e)?; - Err(e) - }) + f(str).or_else(|e| pretty_print_err(name, str, e)) +} + +fn pretty_print_err(name: &str, source: &str, e: Error) -> Result { + pretty_diagnose(name, source, &e)?; + Err(e) } pub fn pretty_diagnose(file_name: &str, source: &str, e: &Error) -> Result<()> { diff --git a/rust/candid_parser/src/grammar.lalrpop b/rust/candid_parser/src/grammar.lalrpop index 28ec9df65..dd22238eb 100644 --- a/rust/candid_parser/src/grammar.lalrpop +++ b/rust/candid_parser/src/grammar.lalrpop @@ -1,7 +1,7 @@ -use super::types::{IDLType, PrimType, TypeField, FuncType, Binding, Dec, IDLProg, IDLTypes, IDLInitArgs, IDLArgType}; use super::test::{Assert, Input, Test}; use super::token::{Token, error, error2, LexicalError, Span}; use candid::{Principal, types::Label}; +use candid::types::syntax::{IDLType, PrimType, TypeField, FuncType, Binding, Dec, IDLProg, IDLTypes, IDLInitArgs, IDLArgType}; use candid::types::value::{IDLField, IDLValue, IDLArgs, VariantValue}; use candid::types::{TypeEnv, FuncMode}; use candid::utils::check_unique; diff --git a/rust/candid_parser/src/lib.rs b/rust/candid_parser/src/lib.rs index ca850bcdf..4086ac3c8 100644 --- a/rust/candid_parser/src/lib.rs +++ b/rust/candid_parser/src/lib.rs @@ -47,7 +47,7 @@ //! ``` //! # fn f() -> anyhow::Result<()> { //! use candid::{TypeEnv, types::{Type, TypeInner}}; -//! use candid_parser::{IDLProg, check_prog}; +//! use candid_parser::{check_prog, parse_idl_prog}; //! let did_file = r#" //! type List = opt record { head: int; tail: List }; //! type byte = nat8; @@ -58,7 +58,7 @@ //! "#; //! //! // Parse did file into an AST -//! let ast: IDLProg = did_file.parse()?; +//! let ast = parse_idl_prog(did_file)?; //! //! // Type checking a given .did file //! // let (env, opt_actor) = check_file("a.did")?; @@ -86,7 +86,7 @@ //! use candid::{IDLArgs, types::value::IDLValue}; //! use candid_parser::parse_idl_args; //! # use candid::TypeEnv; -//! # use candid_parser::{IDLProg, check_prog}; +//! # use candid_parser::{check_prog, parse_idl_prog}; //! # let did_file = r#" //! # type List = opt record { head: int; tail: List }; //! # type byte = nat8; @@ -95,7 +95,7 @@ //! # g : (List) -> (int) query; //! # } //! # "#; -//! # let ast = did_file.parse::()?; +//! # let ast = parse_idl_prog(did_file)?; //! # let mut env = TypeEnv::new(); //! # let actor = check_prog(&mut env, &ast)?.unwrap(); //! // Get method type f : (byte, int, nat, int8) -> (List) @@ -119,15 +119,15 @@ #![cfg_attr(docsrs, feature(doc_cfg))] pub mod error; -pub use error::{pretty_parse, pretty_wrap, Error, Result}; +pub use error::{ + pretty_parse, pretty_parse_idl_prog, pretty_parse_idl_types, pretty_wrap, Error, Result, +}; pub mod bindings; pub mod grammar; pub mod token; -pub mod types; -pub mod utils; -pub use types::IDLProg; pub mod typing; +pub mod utils; pub use typing::{check_file, check_prog, pretty_check_file}; pub use candid; @@ -142,6 +142,26 @@ pub mod configs; pub mod random; pub mod test; +pub fn parse_idl_prog(str: &str) -> Result { + let lexer = token::Tokenizer::new(str); + Ok(grammar::IDLProgParser::new().parse(lexer)?) +} + +pub fn parse_idl_init_args(str: &str) -> Result { + let lexer = token::Tokenizer::new(str); + Ok(grammar::IDLInitArgsParser::new().parse(lexer)?) +} + +pub fn parse_idl_type(str: &str) -> Result { + let lexer = token::Tokenizer::new(str); + Ok(grammar::TypParser::new().parse(lexer)?) +} + +pub fn parse_idl_types(str: &str) -> Result { + let lexer = token::Tokenizer::new(str); + Ok(grammar::TypsParser::new().parse(lexer)?) +} + pub fn parse_idl_args(s: &str) -> crate::Result { let lexer = token::Tokenizer::new(s); Ok(grammar::ArgsParser::new().parse(lexer)?) diff --git a/rust/candid_parser/src/test.rs b/rust/candid_parser/src/test.rs index 563b756a7..9f3dbd970 100644 --- a/rust/candid_parser/src/test.rs +++ b/rust/candid_parser/src/test.rs @@ -1,6 +1,6 @@ -use super::types::{Dec, IDLProg, IDLType}; use super::typing::check_prog; use crate::{Error, Result}; +use candid::types::syntax::{Dec, IDLProg, IDLType}; use candid::types::value::IDLArgs; use candid::types::{Type, TypeEnv}; use candid::DecoderConfig; diff --git a/rust/candid_parser/src/typing.rs b/rust/candid_parser/src/typing.rs index 3cc20367f..a4a7d7daf 100644 --- a/rust/candid_parser/src/typing.rs +++ b/rust/candid_parser/src/typing.rs @@ -1,6 +1,8 @@ -use super::types::*; -use crate::{pretty_parse, Error, Result}; -use candid::types::{ArgType, Field, Function, Type, TypeEnv, TypeInner}; +use crate::{parse_idl_prog, pretty_parse_idl_prog, Error, Result}; +use candid::types::{ + syntax::{Binding, Dec, IDLArgType, IDLInitArgs, IDLProg, IDLType, PrimType, TypeField}, + ArgType, Field, Function, Type, TypeEnv, TypeInner, +}; use candid::utils::check_unique; use std::collections::{BTreeMap, BTreeSet}; use std::path::{Path, PathBuf}; @@ -11,7 +13,7 @@ pub struct Env<'a> { } /// Convert candid AST to internal Type -pub fn ast_to_type(env: &TypeEnv, ast: &super::types::IDLType) -> Result { +pub fn ast_to_type(env: &TypeEnv, ast: &IDLType) -> Result { let env = Env { te: &mut env.clone(), pre: false, @@ -237,9 +239,9 @@ fn load_imports( let code = std::fs::read_to_string(&path) .map_err(|_| Error::msg(format!("Cannot import {file:?}")))?; let code = if is_pretty { - pretty_parse::(path.to_str().unwrap(), &code)? + pretty_parse_idl_prog(path.to_str().unwrap(), &code)? } else { - code.parse::()? + parse_idl_prog(&code)? }; let base = path.parent().unwrap(); load_imports(is_pretty, base, visited, &code, list)?; @@ -324,9 +326,9 @@ fn check_file_(file: &Path, is_pretty: bool) -> Result<(TypeEnv, Option)> let prog = std::fs::read_to_string(file).map_err(|_| Error::msg(format!("Cannot open {file:?}")))?; let prog = if is_pretty { - pretty_parse::(file.to_str().unwrap(), &prog)? + pretty_parse_idl_prog(file.to_str().unwrap(), &prog)? } else { - prog.parse::()? + parse_idl_prog(&prog)? }; let mut visited = BTreeMap::new(); let mut imports = Vec::new(); @@ -346,7 +348,7 @@ fn check_file_(file: &Path, is_pretty: bool) -> Result<(TypeEnv, Option)> let mut actor: Option = None; for (include_serv, path, name) in imports.iter() { let code = std::fs::read_to_string(path)?; - let code = code.parse::()?; + let code = parse_idl_prog(&code)?; check_decs(&mut env, &code.decs)?; if *include_serv { let t = check_actor(&env, &code.actor)?; diff --git a/rust/candid_parser/src/utils.rs b/rust/candid_parser/src/utils.rs index 1552d67e2..5074a87ee 100644 --- a/rust/candid_parser/src/utils.rs +++ b/rust/candid_parser/src/utils.rs @@ -1,6 +1,8 @@ -use crate::{check_prog, pretty_check_file, pretty_parse, Error, Result}; -use candid::types::TypeInner; -use candid::{types::Type, TypeEnv}; +use crate::{check_prog, pretty_check_file, pretty_parse_idl_prog, Error, Result}; +use candid::{ + types::{Type, TypeInner}, + TypeEnv, +}; use std::path::Path; pub enum CandidSource<'a> { @@ -13,7 +15,7 @@ impl CandidSource<'_> { Ok(match self { CandidSource::File(path) => pretty_check_file(path)?, CandidSource::Text(str) => { - let ast = pretty_parse("", str)?; + let ast = pretty_parse_idl_prog("", str)?; let mut env = TypeEnv::new(); let actor = check_prog(&mut env, &ast)?; (env, actor) @@ -83,7 +85,7 @@ pub fn get_metadata(env: &TypeEnv, serv: &Option) -> Option { /// Merge canister metadata candid:args and candid:service into a service constructor. /// If candid:service already contains init args, returns the original did file. pub fn merge_init_args(candid: &str, init: &str) -> Result<(TypeEnv, Type)> { - use crate::{types::IDLInitArgs, typing::check_init_args}; + use crate::{parse_idl_init_args, typing::check_init_args}; use candid::types::TypeInner; let candid = CandidSource::Text(candid); let (env, serv) = candid.load()?; @@ -92,7 +94,7 @@ pub fn merge_init_args(candid: &str, init: &str) -> Result<(TypeEnv, Type)> { match serv.as_ref() { TypeInner::Class(_, _) => Ok((env, serv)), TypeInner::Service(_) => { - let prog = init.parse::()?; + let prog = parse_idl_init_args(init)?; let mut env2 = TypeEnv::new(); let args = check_init_args(&mut env2, &env, &prog)?; Ok((env2, TypeInner::Class(args, serv).into())) @@ -104,9 +106,9 @@ pub fn merge_init_args(candid: &str, init: &str) -> Result<(TypeEnv, Type)> { /// Note that this only checks structural equality, not equivalence. For recursive types, it may reject /// an unrolled type. pub fn check_rust_type(candid_args: &str) -> Result<()> { - use crate::{types::IDLInitArgs, typing::check_init_args}; + use crate::{parse_idl_init_args, typing::check_init_args}; use candid::types::{internal::TypeContainer, subtype::equal, TypeEnv}; - let parsed = candid_args.parse::()?; + let parsed = parse_idl_init_args(candid_args)?; let mut env = TypeEnv::new(); let args = check_init_args(&mut env, &TypeEnv::new(), &parsed)?; let mut rust_env = TypeContainer::new(); diff --git a/rust/candid_parser/tests/parse_type.rs b/rust/candid_parser/tests/parse_type.rs index ad9569404..3129c4afd 100644 --- a/rust/candid_parser/tests/parse_type.rs +++ b/rust/candid_parser/tests/parse_type.rs @@ -2,14 +2,14 @@ use candid::pretty::candid::compile; use candid::types::TypeEnv; use candid_parser::bindings::{javascript, motoko, rust, typescript}; use candid_parser::configs::Configs; -use candid_parser::types::IDLProg; +use candid_parser::parse_idl_prog; use candid_parser::typing::{check_file, check_prog}; use goldenfile::Mint; use std::io::Write; use std::path::Path; #[test] -fn parse_idl_prog() { +fn test_parse_idl_prog() { let prog = r#" import "test.did"; type my_type = principal; @@ -28,7 +28,7 @@ service server : { i : f; } "#; - prog.parse::().unwrap(); + parse_idl_prog(prog).unwrap(); } #[test_generator::test_resources("rust/candid_parser/tests/assets/*.did")] @@ -45,7 +45,7 @@ fn compiler_test(resource: &str) { let mut output = mint.new_goldenfile(filename.with_extension("did")).unwrap(); let content = compile(&env, &actor); // Type check output - let ast = content.parse::().unwrap(); + let ast = parse_idl_prog(&content).unwrap(); check_prog(&mut TypeEnv::new(), &ast).unwrap(); writeln!(output, "{content}").unwrap(); } diff --git a/rust/candid_parser/tests/parse_value.rs b/rust/candid_parser/tests/parse_value.rs index 65c23769b..c93f5d587 100644 --- a/rust/candid_parser/tests/parse_value.rs +++ b/rust/candid_parser/tests/parse_value.rs @@ -1,7 +1,7 @@ use candid::types::value::{IDLArgs, IDLField, IDLValue, VariantValue}; use candid::types::{Label, Type, TypeEnv, TypeInner}; use candid::{record, variant, CandidType, Nat}; -use candid_parser::parse_idl_args; +use candid_parser::{parse_idl_args, parse_idl_type}; fn parse_args(input: &str) -> IDLArgs { parse_idl_args(input).unwrap() @@ -12,9 +12,8 @@ fn parse_args_err(input: &str) -> candid_parser::Result { } fn parse_type(input: &str) -> Type { - use candid_parser::types::IDLType; let env = TypeEnv::new(); - let ast = input.parse::().unwrap(); + let ast = parse_idl_type(input).unwrap(); candid_parser::typing::ast_to_type(&env, &ast).unwrap() } diff --git a/rust/candid_parser/tests/value.rs b/rust/candid_parser/tests/value.rs index e23a579a4..aa6e0a281 100644 --- a/rust/candid_parser/tests/value.rs +++ b/rust/candid_parser/tests/value.rs @@ -1,7 +1,7 @@ use candid::types::value::{IDLArgs, IDLField, IDLValue, VariantValue}; use candid::types::{Label, TypeEnv}; use candid::{decode_args, decode_one, Decode}; -use candid_parser::{parse_idl_args, types::IDLProg, typing::check_prog}; +use candid_parser::{parse_idl_args, parse_idl_prog, typing::check_prog}; #[test] fn test_parser() { @@ -31,7 +31,7 @@ service : { f : f; } "#; - let ast = candid.parse::().unwrap(); + let ast = parse_idl_prog(candid).unwrap(); let mut env = TypeEnv::new(); let actor = check_prog(&mut env, &ast).unwrap().unwrap(); let method = env.get_method(&actor, "f").unwrap(); diff --git a/tools/didc/src/main.rs b/tools/didc/src/main.rs index f44f90efe..89959ddd9 100644 --- a/tools/didc/src/main.rs +++ b/tools/didc/src/main.rs @@ -1,11 +1,12 @@ use anyhow::{bail, Result}; -use candid_parser::candid::types::{subtype, Type}; +use candid_parser::candid::types::{ + subtype, + syntax::{IDLType, IDLTypes}, + Type, +}; use candid_parser::{ - configs::Configs, - parse_idl_args, parse_idl_value, pretty_check_file, pretty_parse, pretty_wrap, - types::{IDLType, IDLTypes}, - typing::ast_to_type, - Error, IDLArgs, IDLValue, TypeEnv, + configs::Configs, parse_idl_args, parse_idl_type, parse_idl_value, pretty_check_file, + pretty_parse_idl_types, pretty_wrap, typing::ast_to_type, Error, IDLArgs, IDLValue, TypeEnv, }; use clap::Parser; use console::style; @@ -90,7 +91,9 @@ enum Command { Subtype { #[clap(short, long)] defs: Option, + #[clap(value_parser = parse_idl_type)] ty1: IDLType, + #[clap(value_parser = parse_idl_type)] ty2: IDLType, }, } @@ -152,7 +155,7 @@ fn parse_args(str: &str) -> Result { pretty_wrap("candid arguments", str, parse_idl_args) } fn parse_types(str: &str) -> Result { - pretty_parse("type annotations", str) + pretty_parse_idl_types("type annotations", str) } fn load_config(input: &Option) -> Result { match input {