diff --git a/compiler/src/errors/macro_.rs b/compiler/src/errors/macro_.rs new file mode 100644 index 0000000000..5a95104fb1 --- /dev/null +++ b/compiler/src/errors/macro_.rs @@ -0,0 +1,35 @@ +use crate::errors::ExpressionError; +use leo_types::{Error as FormattedError, Span}; + +use std::path::PathBuf; + +#[derive(Debug, Error)] +pub enum MacroError { + #[error("{}", _0)] + Error(#[from] FormattedError), + + #[error("{}", _0)] + Expression(#[from] ExpressionError), +} + +impl MacroError { + pub fn set_path(&mut self, path: PathBuf) { + match self { + MacroError::Expression(error) => error.set_path(path), + MacroError::Error(error) => error.set_path(path), + } + } + + fn new_from_span(message: String, span: Span) -> Self { + MacroError::Error(FormattedError::new_from_span(message, span)) + } + + pub fn length(containers: usize, parameters: usize, span: Span) -> Self { + let message = format!( + "Formatter given {} containers and found {} parameters", + containers, parameters + ); + + Self::new_from_span(message, span) + } +} diff --git a/compiler/src/errors/mod.rs b/compiler/src/errors/mod.rs index 2f97fbe06f..3599d55a2c 100644 --- a/compiler/src/errors/mod.rs +++ b/compiler/src/errors/mod.rs @@ -10,6 +10,9 @@ pub use self::function::*; pub mod import; pub use self::import::*; +pub mod macro_; +pub use self::macro_::*; + pub mod statement; pub use self::statement::*; diff --git a/compiler/src/errors/statement.rs b/compiler/src/errors/statement.rs index f460dc7ca3..c1f345272e 100644 --- a/compiler/src/errors/statement.rs +++ b/compiler/src/errors/statement.rs @@ -1,4 +1,4 @@ -use crate::errors::{AddressError, BooleanError, ExpressionError, IntegerError, ValueError}; +use crate::errors::{AddressError, BooleanError, ExpressionError, IntegerError, MacroError, ValueError}; use leo_types::{Error as FormattedError, Span, Type}; use std::path::PathBuf; @@ -20,6 +20,9 @@ pub enum StatementError { #[error("{}", _0)] IntegerError(#[from] IntegerError), + #[error("{}", _0)] + MacroError(#[from] MacroError), + #[error("{}", _0)] ValueError(#[from] ValueError), } @@ -32,6 +35,7 @@ impl StatementError { StatementError::Error(error) => error.set_path(path), StatementError::ExpressionError(error) => error.set_path(path), StatementError::IntegerError(error) => error.set_path(path), + StatementError::MacroError(error) => error.set_path(path), StatementError::ValueError(error) => error.set_path(path), } } diff --git a/compiler/src/lib.rs b/compiler/src/lib.rs index b50b86814c..f163c5438a 100644 --- a/compiler/src/lib.rs +++ b/compiler/src/lib.rs @@ -21,6 +21,9 @@ pub use self::function::*; pub mod import; pub use self::import::*; +pub mod macro_; +pub use self::macro_::*; + pub mod program; pub use self::program::*; diff --git a/compiler/src/macro_/format.rs b/compiler/src/macro_/format.rs new file mode 100644 index 0000000000..f5d8292ffd --- /dev/null +++ b/compiler/src/macro_/format.rs @@ -0,0 +1,53 @@ +//! Evaluates a macro in a compiled Leo program. + +use crate::{errors::MacroError, program::ConstrainedProgram, GroupType}; +use leo_types::FormattedString; + +use snarkos_models::{ + curves::{Field, PrimeField}, + gadgets::r1cs::ConstraintSystem, +}; + +impl> ConstrainedProgram { + pub fn format>( + &mut self, + cs: &mut CS, + file_scope: String, + function_scope: String, + formatted: FormattedString, + ) -> Result { + // Check that containers and parameters match + if formatted.containers.len() != formatted.parameters.len() { + return Err(MacroError::length( + formatted.containers.len(), + formatted.parameters.len(), + formatted.span.clone(), + )); + } + + // Trim starting double quote `"` + let mut string = formatted.string.as_str(); + string = string.trim_start_matches("\""); + + // Trim everything after the ending double quote `"` + let parts: Vec<&str> = string.split("\"").collect(); + string = parts[0]; + + // Insert the parameter for each container `{}` + let mut result = string.to_string(); + + for parameter in formatted.parameters.into_iter() { + let parameter_value = self.enforce_expression( + cs, + file_scope.clone(), + function_scope.clone(), + &vec![], + parameter.expression, + )?; + + result = result.replacen("{}", ¶meter_value.to_string(), 1); + } + + Ok(result) + } +} diff --git a/compiler/src/macro_/macro_.rs b/compiler/src/macro_/macro_.rs new file mode 100644 index 0000000000..aac921881f --- /dev/null +++ b/compiler/src/macro_/macro_.rs @@ -0,0 +1,32 @@ +//! Evaluates a macro in a compiled Leo program. + +use crate::{errors::MacroError, program::ConstrainedProgram, GroupType}; +use leo_types::{FormattedMacro, MacroName}; + +use snarkos_models::{ + curves::{Field, PrimeField}, + gadgets::r1cs::ConstraintSystem, +}; + +impl> ConstrainedProgram { + pub fn evaluate_macro>( + &mut self, + cs: &mut CS, + file_scope: String, + function_scope: String, + macro_: FormattedMacro, + ) -> Result<(), MacroError> { + let string = macro_ + .string + .map(|string| self.format(cs, file_scope, function_scope, string)) + .unwrap_or(Ok("".to_string()))?; + + match macro_.name { + MacroName::Debug(_) => log::debug!("{}\n", string), + MacroName::Error(_) => log::error!("{}\n", string), + MacroName::PrintLine(_) => println!("{}\n", string), + } + + Ok(()) + } +} diff --git a/compiler/src/macro_/mod.rs b/compiler/src/macro_/mod.rs new file mode 100644 index 0000000000..5b3801059c --- /dev/null +++ b/compiler/src/macro_/mod.rs @@ -0,0 +1,5 @@ +pub mod format; +pub use self::format::*; + +pub mod macro_; +pub use self::macro_::*; diff --git a/compiler/src/statement/statement.rs b/compiler/src/statement/statement.rs index 6a4851042c..c8cb1fc3ab 100644 --- a/compiler/src/statement/statement.rs +++ b/compiler/src/statement/statement.rs @@ -79,7 +79,7 @@ impl> ConstrainedProgram { self.enforce_assert_eq_statement(cs, indicator, &resolved_left, &resolved_right, span)?; } Statement::Macro(macro_) => { - println!("{}", macro_); + self.evaluate_macro(cs, file_scope, function_scope, macro_)?; } Statement::Expression(expression, span) => { let expression_string = expression.to_string(); diff --git a/types/src/common/range_or_expression.rs b/types/src/common/range_or_expression.rs index b4d45f6326..2eccd3f841 100644 --- a/types/src/common/range_or_expression.rs +++ b/types/src/common/range_or_expression.rs @@ -1,4 +1,4 @@ -use crate::{Error as FormattedError, Expression, Span}; +use crate::{Error, Expression, Span}; use leo_ast::{common::RangeOrExpression as AstRangeOrExpression, values::NumberValue}; use serde::{Deserialize, Serialize}; @@ -14,7 +14,7 @@ pub enum RangeOrExpression { pub fn unwrap_bound(bound: Option) -> Option { bound.map(|number| { let message = format!("Range bounds should be integers"); - let error = FormattedError::new_from_span(message, Span::from(number.span.clone())); + let error = Error::new_from_span(message, Span::from(number.span.clone())); number.value.parse::().expect(&error.to_string()) }) diff --git a/types/src/macros/formatted_error.rs b/types/src/macros/error_macro.rs similarity index 72% rename from types/src/macros/formatted_error.rs rename to types/src/macros/error_macro.rs index e2fb0a242c..54d92666ce 100644 --- a/types/src/macros/formatted_error.rs +++ b/types/src/macros/error_macro.rs @@ -4,15 +4,15 @@ use serde::{Deserialize, Serialize}; use std::fmt; #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct FormattedError {} +pub struct ErrorMacro {} -impl<'ast> From> for FormattedError { +impl<'ast> From> for ErrorMacro { fn from(_error: AstError<'ast>) -> Self { Self {} } } -impl fmt::Display for FormattedError { +impl fmt::Display for ErrorMacro { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "error") } diff --git a/types/src/macros/macro_name.rs b/types/src/macros/macro_name.rs index 771e05c4fe..47f34f0dbb 100644 --- a/types/src/macros/macro_name.rs +++ b/types/src/macros/macro_name.rs @@ -1,4 +1,4 @@ -use crate::{Debug, FormattedError, PrintLine}; +use crate::{Debug, ErrorMacro, PrintLine}; use leo_ast::macros::MacroName as AstMacroName; use serde::{Deserialize, Serialize}; @@ -7,7 +7,7 @@ use std::fmt; #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum MacroName { Debug(Debug), - Error(FormattedError), + Error(ErrorMacro), PrintLine(PrintLine), } @@ -15,7 +15,7 @@ impl<'ast> From> for MacroName { fn from(name: AstMacroName<'ast>) -> Self { match name { AstMacroName::Debug(debug) => MacroName::Debug(Debug::from(debug)), - AstMacroName::Error(error) => MacroName::Error(FormattedError::from(error)), + AstMacroName::Error(error) => MacroName::Error(ErrorMacro::from(error)), AstMacroName::PrintLine(print_line) => MacroName::PrintLine(PrintLine::from(print_line)), } } diff --git a/types/src/macros/mod.rs b/types/src/macros/mod.rs index 288bf4b031..18f32b46c1 100644 --- a/types/src/macros/mod.rs +++ b/types/src/macros/mod.rs @@ -4,8 +4,8 @@ pub use debug::*; pub mod formatted_container; pub use formatted_container::*; -pub mod formatted_error; -pub use formatted_error::*; +pub mod error_macro; +pub use error_macro::*; pub mod formatted_macro; pub use formatted_macro::*;