format string and grammar changes

This commit is contained in:
gluaxspeed 2021-07-20 14:52:35 -07:00
parent 9facd3e984
commit a9593971b9
34 changed files with 328 additions and 286 deletions

View File

@ -92,7 +92,7 @@ impl<'a> MonoidalReducerStatement<'a, BoolAnd> for ReturnPathReducer {
if_true.append(if_false.unwrap_or(BoolAnd(false))) if_true.append(if_false.unwrap_or(BoolAnd(false)))
} }
fn reduce_formatted_string(&mut self, input: &FormatString, parameters: Vec<BoolAnd>) -> BoolAnd { fn reduce_formatted_string(&mut self, input: &ConsoleArgs, parameters: Vec<BoolAnd>) -> BoolAnd {
BoolAnd(false) BoolAnd(false)
} }

View File

@ -109,13 +109,29 @@ pub enum CharValue {
NonScalar(u32), NonScalar(u32),
} }
impl From<&leo_ast::Char> for CharValue {
fn from(other: &leo_ast::Char) -> Self {
use leo_ast::Char::*;
match other {
Scalar(value) => CharValue::Scalar(*value),
NonScalar(value) => CharValue::NonScalar(*value),
}
}
}
impl Into<leo_ast::Char> for &CharValue {
fn into(self) -> leo_ast::Char {
use leo_ast::Char::*;
match self {
CharValue::Scalar(value) => Scalar(*value),
CharValue::NonScalar(value) => NonScalar(*value),
}
}
}
impl From<leo_ast::CharValue> for CharValue { impl From<leo_ast::CharValue> for CharValue {
fn from(other: leo_ast::CharValue) -> Self { fn from(other: leo_ast::CharValue) -> Self {
use leo_ast::Char::*; Self::from(&other.character)
match other.character {
Scalar(value) => CharValue::Scalar(value),
NonScalar(value) => CharValue::NonScalar(value),
}
} }
} }

View File

@ -225,7 +225,7 @@ impl<'a, T: Monoid, R: MonoidalReducerStatement<'a, T>> MonoidalDirector<'a, T,
.reduce_conditional_statement(input, condition, if_true, if_false) .reduce_conditional_statement(input, condition, if_true, if_false)
} }
pub fn reduce_formatted_string(&mut self, input: &FormatString<'a>) -> T { pub fn reduce_formatted_string(&mut self, input: &ConsoleArgs<'a>) -> T {
let parameters = input let parameters = input
.parameters .parameters
.iter() .iter()

View File

@ -118,7 +118,7 @@ pub trait MonoidalReducerStatement<'a, T: Monoid>: MonoidalReducerExpression<'a,
condition.append(if_true).append_option(if_false) condition.append(if_true).append_option(if_false)
} }
fn reduce_formatted_string(&mut self, input: &FormatString<'a>, parameters: Vec<T>) -> T { fn reduce_formatted_string(&mut self, input: &ConsoleArgs<'a>, parameters: Vec<T>) -> T {
T::default().append_all(parameters.into_iter()) T::default().append_all(parameters.into_iter())
} }

View File

@ -243,7 +243,7 @@ impl<'a, R: ReconstructingReducerStatement<'a>> ReconstructingDirector<'a, R> {
.reduce_conditional_statement(input, condition, if_true, if_false) .reduce_conditional_statement(input, condition, if_true, if_false)
} }
pub fn reduce_formatted_string(&mut self, input: FormatString<'a>) -> FormatString<'a> { pub fn reduce_formatted_string(&mut self, input: ConsoleArgs<'a>) -> ConsoleArgs<'a> {
let parameters = input let parameters = input
.parameters .parameters
.iter() .iter()

View File

@ -274,12 +274,12 @@ pub trait ReconstructingReducerStatement<'a>: ReconstructingReducerExpression<'a
fn reduce_formatted_string( fn reduce_formatted_string(
&mut self, &mut self,
input: FormatString<'a>, input: ConsoleArgs<'a>,
parameters: Vec<&'a Expression<'a>>, parameters: Vec<&'a Expression<'a>>,
) -> FormatString<'a> { ) -> ConsoleArgs<'a> {
FormatString { ConsoleArgs {
span: input.span, span: input.span,
parts: input.parts, string: input.string,
parameters: parameters.into_iter().map(Cell::new).collect(), parameters: parameters.into_iter().map(Cell::new).collect(),
} }
} }
@ -293,7 +293,7 @@ pub trait ReconstructingReducerStatement<'a>: ReconstructingReducerExpression<'a
}) })
} }
fn reduce_console_log(&mut self, input: ConsoleStatement<'a>, argument: FormatString<'a>) -> Statement<'a> { fn reduce_console_log(&mut self, input: ConsoleStatement<'a>, argument: ConsoleArgs<'a>) -> Statement<'a> {
assert!(!matches!(input.function, ConsoleFunction::Assert(_))); assert!(!matches!(input.function, ConsoleFunction::Assert(_)));
Statement::Console(ConsoleStatement { Statement::Console(ConsoleStatement {
parent: input.parent, parent: input.parent,

View File

@ -120,7 +120,7 @@ pub trait StatementVisitor<'a>: ExpressionVisitor<'a> {
Default::default() Default::default()
} }
fn visit_formatted_string(&mut self, input: &FormatString<'a>) -> VisitResult { fn visit_formatted_string(&mut self, input: &ConsoleArgs<'a>) -> VisitResult {
Default::default() Default::default()
} }

View File

@ -319,7 +319,7 @@ impl<'a, R: StatementVisitor<'a>> VisitorDirector<'a, R> {
} }
} }
pub fn visit_formatted_string(&mut self, input: &FormatString<'a>) -> ConcreteVisitResult { pub fn visit_formatted_string(&mut self, input: &ConsoleArgs<'a>) -> ConcreteVisitResult {
match self.visitor.visit_formatted_string(input) { match self.visitor.visit_formatted_string(input) {
VisitResult::VisitChildren => { VisitResult::VisitChildren => {
for parameter in input.parameters.iter() { for parameter in input.parameters.iter() {

View File

@ -14,15 +14,15 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>. // along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{AsgConvertError, Expression, FromAst, Node, PartialType, Scope, Span, Statement, Type}; use crate::{AsgConvertError, CharValue, Expression, FromAst, Node, PartialType, Scope, Span, Statement, Type};
use leo_ast::{ConsoleFunction as AstConsoleFunction, FormatStringPart}; use leo_ast::ConsoleFunction as AstConsoleFunction;
use std::cell::Cell; use std::cell::Cell;
// TODO (protryon): Refactor to not require/depend on span // TODO (protryon): Refactor to not require/depend on span
#[derive(Clone)] #[derive(Clone)]
pub struct FormatString<'a> { pub struct ConsoleArgs<'a> {
pub parts: Vec<FormatStringPart>, pub string: Vec<CharValue>,
pub parameters: Vec<Cell<&'a Expression<'a>>>, pub parameters: Vec<Cell<&'a Expression<'a>>>,
pub span: Span, pub span: Span,
} }
@ -30,9 +30,9 @@ pub struct FormatString<'a> {
#[derive(Clone)] #[derive(Clone)]
pub enum ConsoleFunction<'a> { pub enum ConsoleFunction<'a> {
Assert(Cell<&'a Expression<'a>>), Assert(Cell<&'a Expression<'a>>),
Debug(FormatString<'a>), Debug(ConsoleArgs<'a>),
Error(FormatString<'a>), Error(ConsoleArgs<'a>),
Log(FormatString<'a>), Log(ConsoleArgs<'a>),
} }
#[derive(Clone)] #[derive(Clone)]
@ -48,41 +48,42 @@ impl<'a> Node for ConsoleStatement<'a> {
} }
} }
impl<'a> FromAst<'a, leo_ast::FormatString> for FormatString<'a> { impl<'a> FromAst<'a, leo_ast::ConsoleArgs> for ConsoleArgs<'a> {
fn from_ast( fn from_ast(
scope: &'a Scope<'a>, scope: &'a Scope<'a>,
value: &leo_ast::FormatString, value: &leo_ast::ConsoleArgs,
_expected_type: Option<PartialType<'a>>, _expected_type: Option<PartialType<'a>>,
) -> Result<Self, AsgConvertError> { ) -> Result<Self, AsgConvertError> {
let expected_param_len = value // let expected_param_len = value
.parts // .parts
.iter() // .iter()
.filter(|x| matches!(x, FormatStringPart::Container)) // .filter(|x| matches!(x, FormatStringPart::Container))
.count(); // .count();
if value.parameters.len() != expected_param_len { // if value.parameters.len() != expected_param_len {
// + 1 for formatting string as to not confuse user // + 1 for formatting string as to not confuse user
return Err(AsgConvertError::unexpected_call_argument_count( // return Err(AsgConvertError::unexpected_call_argument_count(
expected_param_len + 1, // expected_param_len + 1,
value.parameters.len() + 1, // value.parameters.len() + 1,
&value.span, // &value.span,
)); // ));
} // }
let mut parameters = vec![]; let mut parameters = vec![];
for parameter in value.parameters.iter() { for parameter in value.parameters.iter() {
parameters.push(Cell::new(<&Expression<'a>>::from_ast(scope, parameter, None)?)); parameters.push(Cell::new(<&Expression<'a>>::from_ast(scope, parameter, None)?));
} }
Ok(FormatString { Ok(ConsoleArgs {
parts: value.parts.clone(), string: value.string.iter().map(CharValue::from).collect::<Vec<_>>(),
parameters, parameters,
span: value.span.clone(), span: value.span.clone(),
}) })
} }
} }
impl<'a> Into<leo_ast::FormatString> for &FormatString<'a> { impl<'a> Into<leo_ast::ConsoleArgs> for &ConsoleArgs<'a> {
fn into(self) -> leo_ast::FormatString { fn into(self) -> leo_ast::ConsoleArgs {
leo_ast::FormatString { leo_ast::ConsoleArgs {
parts: self.parts.clone(), string: self.string.iter().map(|c| c.into()).collect::<Vec<_>>(),
parameters: self.parameters.iter().map(|e| e.get().into()).collect(), parameters: self.parameters.iter().map(|e| e.get().into()).collect(),
span: self.span.clone(), span: self.span.clone(),
} }
@ -102,15 +103,9 @@ impl<'a> FromAst<'a, leo_ast::ConsoleStatement> for ConsoleStatement<'a> {
AstConsoleFunction::Assert(expression) => ConsoleFunction::Assert(Cell::new( AstConsoleFunction::Assert(expression) => ConsoleFunction::Assert(Cell::new(
<&Expression<'a>>::from_ast(scope, expression, Some(Type::Boolean.into()))?, <&Expression<'a>>::from_ast(scope, expression, Some(Type::Boolean.into()))?,
)), )),
AstConsoleFunction::Debug(formatted_string) => { AstConsoleFunction::Debug(args) => ConsoleFunction::Debug(ConsoleArgs::from_ast(scope, args, None)?),
ConsoleFunction::Debug(FormatString::from_ast(scope, formatted_string, None)?) AstConsoleFunction::Error(args) => ConsoleFunction::Error(ConsoleArgs::from_ast(scope, args, None)?),
} AstConsoleFunction::Log(args) => ConsoleFunction::Log(ConsoleArgs::from_ast(scope, args, None)?),
AstConsoleFunction::Error(formatted_string) => {
ConsoleFunction::Error(FormatString::from_ast(scope, formatted_string, None)?)
}
AstConsoleFunction::Log(formatted_string) => {
ConsoleFunction::Log(FormatString::from_ast(scope, formatted_string, None)?)
}
}, },
}) })
} }
@ -122,9 +117,9 @@ impl<'a> Into<leo_ast::ConsoleStatement> for &ConsoleStatement<'a> {
leo_ast::ConsoleStatement { leo_ast::ConsoleStatement {
function: match &self.function { function: match &self.function {
Assert(e) => AstConsoleFunction::Assert(e.get().into()), Assert(e) => AstConsoleFunction::Assert(e.get().into()),
Debug(formatted_string) => AstConsoleFunction::Debug(formatted_string.into()), Debug(args) => AstConsoleFunction::Debug(args.into()),
Error(formatted_string) => AstConsoleFunction::Error(formatted_string.into()), Error(args) => AstConsoleFunction::Error(args.into()),
Log(formatted_string) => AstConsoleFunction::Log(formatted_string.into()), Log(args) => AstConsoleFunction::Log(args.into()),
}, },
span: self.span.clone().unwrap_or_default(), span: self.span.clone().unwrap_or_default(),
} }

View File

@ -1,3 +0,0 @@
function main() {
console.log("{}");
}

View File

@ -1,3 +0,0 @@
function main() {
console.log("", 1u32);
}

View File

@ -27,15 +27,3 @@ fn test_log_parameter_fail_unknown() {
let program_string = include_str!("log_parameter_fail_unknown.leo"); let program_string = include_str!("log_parameter_fail_unknown.leo");
load_asg(program_string).err().unwrap(); load_asg(program_string).err().unwrap();
} }
#[test]
fn test_log_parameter_fail_empty() {
let program_string = include_str!("log_parameter_fail_empty.leo");
load_asg(program_string).err().unwrap();
}
#[test]
fn test_log_parameter_fail_none() {
let program_string = include_str!("log_parameter_fail_empty.leo");
load_asg(program_string).err().unwrap();
}

View File

@ -35,6 +35,13 @@ impl ReducerError {
ReducerError::Error(FormattedError::new_from_span(message, span)) ReducerError::Error(FormattedError::new_from_span(message, span))
} }
pub fn empty_string(span: &Span) -> Self {
let message =
"Cannot constrcut an empty string: it has the type of [char; 0] which is not possible.".to_string();
Self::new_from_span(message, span)
}
pub fn impossible_console_assert_call(span: &Span) -> Self { pub fn impossible_console_assert_call(span: &Span) -> Self {
let message = "Console::Assert cannot be matched here, its handled in another case.".to_string(); let message = "Console::Assert cannot be matched here, its handled in another case.".to_string();

View File

@ -391,23 +391,23 @@ impl Canonicalizer {
ConsoleFunction::Assert(expression) => { ConsoleFunction::Assert(expression) => {
ConsoleFunction::Assert(self.canonicalize_expression(expression)) ConsoleFunction::Assert(self.canonicalize_expression(expression))
} }
ConsoleFunction::Debug(format) | ConsoleFunction::Error(format) | ConsoleFunction::Log(format) => { ConsoleFunction::Debug(args) | ConsoleFunction::Error(args) | ConsoleFunction::Log(args) => {
let parameters = format let parameters = args
.parameters .parameters
.iter() .iter()
.map(|parameter| self.canonicalize_expression(parameter)) .map(|parameter| self.canonicalize_expression(parameter))
.collect(); .collect();
let formatted = FormatString { let console_args = ConsoleArgs {
parts: format.parts.clone(), string: args.string.clone(),
parameters, parameters,
span: format.span.clone(), span: args.span.clone(),
}; };
match &console_function_call.function { match &console_function_call.function {
ConsoleFunction::Debug(_) => ConsoleFunction::Debug(formatted), ConsoleFunction::Debug(_) => ConsoleFunction::Debug(console_args),
ConsoleFunction::Error(_) => ConsoleFunction::Error(formatted), ConsoleFunction::Error(_) => ConsoleFunction::Error(console_args),
ConsoleFunction::Log(_) => ConsoleFunction::Log(formatted), ConsoleFunction::Log(_) => ConsoleFunction::Log(console_args),
_ => unimplemented!(), // impossible _ => unimplemented!(), // impossible
} }
} }
@ -493,6 +493,10 @@ impl ReconstructingReducer for Canonicalizer {
} }
fn reduce_string(&mut self, string: &[Char], span: &Span) -> Result<Expression, ReducerError> { fn reduce_string(&mut self, string: &[Char], span: &Span) -> Result<Expression, ReducerError> {
if string.is_empty() {
return Err(ReducerError::empty_string(span));
}
let mut elements = Vec::new(); let mut elements = Vec::new();
let mut col_adder = 0; let mut col_adder = 0;
for (index, character) in string.iter().enumerate() { for (index, character) in string.iter().enumerate() {

View File

@ -390,23 +390,23 @@ impl<R: ReconstructingReducer> ReconstructingDirector<R> {
) -> Result<ConsoleStatement, ReducerError> { ) -> Result<ConsoleStatement, ReducerError> {
let function = match &console_function_call.function { let function = match &console_function_call.function {
ConsoleFunction::Assert(expression) => ConsoleFunction::Assert(self.reduce_expression(expression)?), ConsoleFunction::Assert(expression) => ConsoleFunction::Assert(self.reduce_expression(expression)?),
ConsoleFunction::Debug(format) | ConsoleFunction::Error(format) | ConsoleFunction::Log(format) => { ConsoleFunction::Debug(args) | ConsoleFunction::Error(args) | ConsoleFunction::Log(args) => {
let mut parameters = vec![]; let mut parameters = vec![];
for parameter in format.parameters.iter() { for parameter in args.parameters.iter() {
parameters.push(self.reduce_expression(parameter)?); parameters.push(self.reduce_expression(parameter)?);
} }
let formatted = FormatString { let formatted = ConsoleArgs {
parts: format.parts.clone(), string: args.string.clone(),
parameters, parameters,
span: format.span.clone(), span: args.span.clone(),
}; };
match &console_function_call.function { match &console_function_call.function {
ConsoleFunction::Debug(_) => ConsoleFunction::Debug(formatted), ConsoleFunction::Debug(_) => ConsoleFunction::Debug(formatted),
ConsoleFunction::Error(_) => ConsoleFunction::Error(formatted), ConsoleFunction::Error(_) => ConsoleFunction::Error(formatted),
ConsoleFunction::Log(_) => ConsoleFunction::Log(formatted), ConsoleFunction::Log(_) => ConsoleFunction::Log(formatted),
_ => return Err(ReducerError::impossible_console_assert_call(&format.span)), _ => return Err(ReducerError::impossible_console_assert_call(&args.span)),
} }
} }
}; };

View File

@ -0,0 +1,96 @@
// Copyright (C) 2019-2021 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{Char, Expression, Node, Span};
use serde::{Deserialize, Serialize};
use std::fmt;
// use tendril::StrTendril;
// #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
// pub enum FormatStringPart {
// Const(#[serde(with = "crate::common::tendril_json")] StrTendril),
// Container,
// }
// impl FormatStringPart {
// pub fn from_string(string: Vec<Char>) -> Vec<Self> {
// let mut parts = Vec::new();
// let mut in_container = false;
// let mut substring = String::new();
// for (_, character) in string.iter().enumerate() {
// match character {
// Char::Scalar(scalar) => match scalar {
// '{' if !in_container => {
// parts.push(FormatStringPart::Const(substring.clone().into()));
// substring.clear();
// in_container = true;
// }
// '}' if in_container => {
// in_container = false;
// parts.push(FormatStringPart::Container);
// }
// _ if in_container => {
// in_container = false;
// }
// _ => substring.push(*scalar),
// },
// Char::NonScalar(non_scalar) => {
// substring.push_str(format!("\\u{{{:x}}}", non_scalar).as_str());
// in_container = false;
// }
// }
// }
// if !substring.is_empty() {
// parts.push(FormatStringPart::Const(substring.into()));
// }
// parts
// }
// }
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct ConsoleArgs {
pub string: Vec<Char>,
pub parameters: Vec<Expression>,
pub span: Span,
}
impl fmt::Display for ConsoleArgs {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"\"{}\", {}",
self.string.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(""),
self.parameters
.iter()
.map(|p| p.to_string())
.collect::<Vec<_>>()
.join(",")
)
}
}
impl Node for ConsoleArgs {
fn span(&self) -> &Span {
&self.span
}
fn set_span(&mut self, span: Span) {
self.span = span;
}
}

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>. // along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{Expression, FormatString, Node, Span}; use crate::{ConsoleArgs, Expression, Node, Span};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
@ -22,9 +22,9 @@ use std::fmt;
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub enum ConsoleFunction { pub enum ConsoleFunction {
Assert(Expression), Assert(Expression),
Debug(FormatString), Debug(ConsoleArgs),
Error(FormatString), Error(ConsoleArgs),
Log(FormatString), Log(ConsoleArgs),
} }
impl fmt::Display for ConsoleFunction { impl fmt::Display for ConsoleFunction {

View File

@ -1,103 +0,0 @@
// Copyright (C) 2019-2021 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{Char, Expression, Node, Span};
use serde::{Deserialize, Serialize};
use std::fmt;
use tendril::StrTendril;
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub enum FormatStringPart {
Const(#[serde(with = "crate::common::tendril_json")] StrTendril),
Container,
}
impl FormatStringPart {
pub fn from_string(string: Vec<Char>) -> Vec<Self> {
let mut parts = Vec::new();
let mut in_container = false;
let mut substring = String::new();
for (_, character) in string.iter().enumerate() {
match character {
Char::Scalar(scalar) => match scalar {
'{' if !in_container => {
parts.push(FormatStringPart::Const(substring.clone().into()));
substring.clear();
in_container = true;
}
'}' if in_container => {
in_container = false;
parts.push(FormatStringPart::Container);
}
_ if in_container => {
in_container = false;
}
_ => substring.push(*scalar),
},
Char::NonScalar(non_scalar) => {
substring.push_str(format!("\\u{{{:x}}}", non_scalar).as_str());
in_container = false;
}
}
}
if !substring.is_empty() {
parts.push(FormatStringPart::Const(substring.into()));
}
parts
}
}
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct FormatString {
pub parts: Vec<FormatStringPart>,
pub parameters: Vec<Expression>,
pub span: Span,
}
impl fmt::Display for FormatString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"\"{}\", {}",
self.parts
.iter()
.map(|x| match x {
FormatStringPart::Const(x) => x.to_string(),
FormatStringPart::Container => "{}".to_string(),
})
.collect::<Vec<_>>()
.join(""),
self.parameters
.iter()
.map(|p| p.to_string())
.collect::<Vec<_>>()
.join(",")
)
}
}
impl Node for FormatString {
fn span(&self) -> &Span {
&self.span
}
fn set_span(&mut self, span: Span) {
self.span = span;
}
}

View File

@ -17,11 +17,8 @@
pub mod console_function; pub mod console_function;
pub use console_function::*; pub use console_function::*;
pub mod console_args;
pub use console_args::*;
pub mod console_statement; pub mod console_statement;
pub use console_statement::*; pub use console_statement::*;
pub mod formatted_container;
pub use formatted_container::*;
pub mod formatted_string;
pub use formatted_string::*;

View File

@ -17,8 +17,7 @@
//! Evaluates a formatted string in a compiled Leo program. //! Evaluates a formatted string in a compiled Leo program.
use crate::{errors::ConsoleError, program::ConstrainedProgram, GroupType}; use crate::{errors::ConsoleError, program::ConstrainedProgram, GroupType};
use leo_asg::FormatString; use leo_asg::{CharValue, ConsoleArgs};
use leo_ast::FormatStringPart;
use snarkvm_fields::PrimeField; use snarkvm_fields::PrimeField;
use snarkvm_r1cs::ConstraintSystem; use snarkvm_r1cs::ConstraintSystem;
@ -26,35 +25,65 @@ impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
pub fn format<CS: ConstraintSystem<F>>( pub fn format<CS: ConstraintSystem<F>>(
&mut self, &mut self,
cs: &mut CS, cs: &mut CS,
formatted: &FormatString<'a>, args: &ConsoleArgs<'a>,
) -> Result<String, ConsoleError> { ) -> Result<String, ConsoleError> {
// Check that containers and parameters match let mut out = Vec::new();
let container_count = formatted let mut in_container = false;
.parts let mut substring = String::new();
.iter() let mut arg_index = 0;
.filter(|x| matches!(x, FormatStringPart::Container)) let mut escape_right_bracket = false;
.count(); for (index, character) in args.string.iter().enumerate() {
if container_count != formatted.parameters.len() { match character {
return Err(ConsoleError::length( _ if escape_right_bracket => {
container_count, escape_right_bracket = false;
formatted.parameters.len(), continue;
&formatted.span, }
)); CharValue::Scalar(scalar) => match scalar {
} '{' if !in_container => {
out.push(substring.clone());
let mut executed_containers = Vec::with_capacity(formatted.parameters.len()); substring.clear();
for parameter in formatted.parameters.iter() { in_container = true;
executed_containers.push(self.enforce_expression(cs, parameter.get())?.to_string()); }
} '{' if in_container => {
substring.push('{');
let mut out = vec![]; in_container = false;
let mut parameters = executed_containers.iter(); }
for part in formatted.parts.iter() { '}' if in_container => {
match part { in_container = false;
FormatStringPart::Const(c) => out.push(c.to_string()), let parameter = match args.parameters.get(arg_index) {
FormatStringPart::Container => out.push(parameters.next().unwrap().to_string()), Some(index) => index,
None => return Err(ConsoleError::length(arg_index + 1, args.parameters.len(), &args.span)),
};
out.push(self.enforce_expression(cs, parameter.get())?.to_string());
arg_index += 1;
}
'}' if !in_container => {
if let Some(CharValue::Scalar(next)) = args.string.get(index + 1) {
if *next == '}' {
substring.push('}');
escape_right_bracket = true;
} else {
return Err(ConsoleError::expected_escaped_right_brace(&args.span));
}
}
}
_ if in_container => {
return Err(ConsoleError::expected_left_or_right_brace(&args.span));
}
_ => substring.push(*scalar),
},
CharValue::NonScalar(non_scalar) => {
substring.push_str(format!("\\u{{{:x}}}", non_scalar).as_str());
in_container = false;
}
} }
} }
out.push(substring);
// Check that containers and parameters match
if arg_index != args.parameters.len() {
return Err(ConsoleError::length(arg_index, args.parameters.len(), &args.span));
}
Ok(out.join("")) Ok(out.join(""))
} }

View File

@ -33,6 +33,18 @@ impl ConsoleError {
ConsoleError::Error(FormattedError::new_from_span(message, span)) ConsoleError::Error(FormattedError::new_from_span(message, span))
} }
pub fn expected_left_or_right_brace(span: &Span) -> Self {
let message = "Formatter given a {. Expected a { or } after".to_string();
Self::new_from_span(message, span)
}
pub fn expected_escaped_right_brace(span: &Span) -> Self {
let message = "Formatter given a }. Expected a container {} or }}".to_string();
Self::new_from_span(message, span)
}
pub fn length(containers: usize, parameters: usize, span: &Span) -> Self { pub fn length(containers: usize, parameters: usize, span: &Span) -> Self {
let message = format!( let message = format!(
"Formatter given {} containers and found {} parameters", "Formatter given {} containers and found {} parameters",

View File

@ -76,12 +76,12 @@ use leo_ast::{
CircuitStaticFunctionAccessExpression, CircuitStaticFunctionAccessExpression,
CombinerError, CombinerError,
ConditionalStatement as AstConditionalStatement, ConditionalStatement as AstConditionalStatement,
ConsoleArgs as AstConsoleArgs,
ConsoleFunction as AstConsoleFunction, ConsoleFunction as AstConsoleFunction,
ConsoleStatement as AstConsoleStatement, ConsoleStatement as AstConsoleStatement,
DefinitionStatement as AstDefinitionStatement, DefinitionStatement as AstDefinitionStatement,
Expression as AstExpression, Expression as AstExpression,
ExpressionStatement as AstExpressionStatement, ExpressionStatement as AstExpressionStatement,
FormatString,
Function as AstFunction, Function as AstFunction,
GroupTuple, GroupTuple,
GroupValue as AstGroupValue, GroupValue as AstGroupValue,
@ -597,25 +597,27 @@ impl<R: ReconstructingReducer, O: CombinerOptions> CombineAstAsgDirector<R, O> {
(AstConsoleFunction::Assert(ast_expression), AsgConsoleFunction::Assert(asg_expression)) => { (AstConsoleFunction::Assert(ast_expression), AsgConsoleFunction::Assert(asg_expression)) => {
AstConsoleFunction::Assert(self.reduce_expression(&ast_expression, asg_expression.get())?) AstConsoleFunction::Assert(self.reduce_expression(&ast_expression, asg_expression.get())?)
} }
(AstConsoleFunction::Debug(ast_format), AsgConsoleFunction::Debug(asg_format)) (AstConsoleFunction::Debug(ast_console_args), AsgConsoleFunction::Debug(asg_format))
| (AstConsoleFunction::Error(ast_format), AsgConsoleFunction::Error(asg_format)) | (AstConsoleFunction::Error(ast_console_args), AsgConsoleFunction::Error(asg_format))
| (AstConsoleFunction::Log(ast_format), AsgConsoleFunction::Log(asg_format)) => { | (AstConsoleFunction::Log(ast_console_args), AsgConsoleFunction::Log(asg_format)) => {
let mut parameters = vec![]; let mut parameters = vec![];
for (ast_parameter, asg_parameter) in ast_format.parameters.iter().zip(asg_format.parameters.iter()) { for (ast_parameter, asg_parameter) in
ast_console_args.parameters.iter().zip(asg_format.parameters.iter())
{
parameters.push(self.reduce_expression(&ast_parameter, asg_parameter.get())?); parameters.push(self.reduce_expression(&ast_parameter, asg_parameter.get())?);
} }
let formatted = FormatString { let args = AstConsoleArgs {
parts: ast_format.parts.clone(), string: ast_console_args.string.clone(),
parameters, parameters,
span: ast_format.span.clone(), span: ast_console_args.span.clone(),
}; };
match &ast.function { match &ast.function {
AstConsoleFunction::Debug(_) => AstConsoleFunction::Debug(formatted), AstConsoleFunction::Debug(_) => AstConsoleFunction::Debug(args),
AstConsoleFunction::Error(_) => AstConsoleFunction::Error(formatted), AstConsoleFunction::Error(_) => AstConsoleFunction::Error(args),
AstConsoleFunction::Log(_) => AstConsoleFunction::Log(formatted), AstConsoleFunction::Log(_) => AstConsoleFunction::Log(args),
_ => return Err(ReducerError::impossible_console_assert_call(&ast_format.span)), _ => return Err(ReducerError::impossible_console_assert_call(&ast_console_args.span)),
} }
} }
_ => ast.function.clone(), _ => ast.function.clone(),

Binary file not shown.

View File

@ -559,7 +559,7 @@ unicode-character-escape = %s"\u{" 1*6hexadecimal-digit "}"
; because string literals denote character arrays, ; because string literals denote character arrays,
; and arrays must not be empty. ; and arrays must not be empty.
string-literal = double-quote 1*string-literal-element double-quote string-literal = double-quote *string-literal-element double-quote
string-literal-element = not-double-quote-or-backslash string-literal-element = not-double-quote-or-backslash
/ simple-character-escape / simple-character-escape

5
parser/README.md Normal file
View File

@ -0,0 +1,5 @@
# leo-parser
[![Crates.io](https://img.shields.io/crates/v/leo-parser.svg?color=neon)](https://crates.io/crates/leo-parser)
[![Authors](https://img.shields.io/badge/authors-Aleo-orange.svg)](../AUTHORS)
[![License](https://img.shields.io/badge/License-GPLv3-blue.svg)](./LICENSE.md)

View File

@ -232,9 +232,9 @@ impl ParserContext {
} }
/// ///
/// Returns a [`FormatString`] AST node if the next tokens represent a formatted string. /// Returns a [`ConsoleArgs`] AST node if the next tokens represent a formatted string.
/// ///
pub fn parse_formatted_string(&mut self) -> SyntaxResult<FormatString> { pub fn parse_console_args(&mut self) -> SyntaxResult<ConsoleArgs> {
let start_span; let start_span;
let string = match self.expect_any()? { let string = match self.expect_any()? {
SpannedToken { SpannedToken {
@ -247,7 +247,7 @@ impl ParserContext {
SpannedToken { token, span } => return Err(SyntaxError::unexpected_str(&token, "formatted string", &span)), SpannedToken { token, span } => return Err(SyntaxError::unexpected_str(&token, "formatted string", &span)),
}; };
let parts = FormatStringPart::from_string(string); // let parts = FormatStringPart::from_string(string);
let mut parameters = Vec::new(); let mut parameters = Vec::new();
while self.eat(Token::Comma).is_some() { while self.eat(Token::Comma).is_some() {
@ -255,8 +255,8 @@ impl ParserContext {
parameters.push(param); parameters.push(param);
} }
Ok(FormatString { Ok(ConsoleArgs {
parts, string,
span: &start_span + parameters.last().map(|x| x.span()).unwrap_or(&start_span), span: &start_span + parameters.last().map(|x| x.span()).unwrap_or(&start_span),
parameters, parameters,
}) })
@ -275,9 +275,9 @@ impl ParserContext {
let expr = self.parse_expression()?; let expr = self.parse_expression()?;
ConsoleFunction::Assert(expr) ConsoleFunction::Assert(expr)
} }
"debug" => ConsoleFunction::Debug(self.parse_formatted_string()?), "debug" => ConsoleFunction::Debug(self.parse_console_args()?),
"error" => ConsoleFunction::Error(self.parse_formatted_string()?), "error" => ConsoleFunction::Error(self.parse_console_args()?),
"log" => ConsoleFunction::Log(self.parse_formatted_string()?), "log" => ConsoleFunction::Log(self.parse_console_args()?),
x => { x => {
return Err(SyntaxError::unexpected_ident( return Err(SyntaxError::unexpected_ident(
&x, &x,

View File

@ -280,7 +280,7 @@ impl Token {
} }
} }
if i == input.len() || i == 1 || !end { if i == input.len() || !end {
return (0, None); return (0, None);
} }

View File

@ -2,4 +2,4 @@
namespace: Compile namespace: Compile
expectation: Fail expectation: Fail
outputs: outputs:
- " --> compiler-test:4:17\n |\n 4 | console.log(\"{}\");\n | ^^^^\n |\n = function call expected 2 arguments, got 1" - " --> compiler-test:4:17\n |\n 4 | console.log(\"{}\");\n | ^^^^\n |\n = Formatter given 1 containers and found 0 parameters"

View File

@ -2,4 +2,4 @@
namespace: Compile namespace: Compile
expectation: Fail expectation: Fail
outputs: outputs:
- " --> compiler-test:4:17\n |\n 4 | console.log(\"\", 1u32);\n | ^\n |\n = unexpected token: '\"'" - " --> compiler-test:4:17\n |\n 4 | console.log(\"\", 1u32);\n | ^^^^^^^^\n |\n = Formatter given 0 containers and found 1 parameters"

View File

@ -2,6 +2,7 @@
namespace: Token namespace: Token
expectation: Pass expectation: Pass
outputs: outputs:
- "'\"\"' @ 1:1-3"
- "'\"string\"' @ 1:1-9" - "'\"string\"' @ 1:1-9"
- "'\"another { } string\"' @ 1:1-21" - "'\"another { } string\"' @ 1:1-21"
- "'\"{ ] [ ; a\"' @ 1:1-12" - "'\"{ ] [ ; a\"' @ 1:1-12"

View File

@ -2,7 +2,6 @@
namespace: Token namespace: Token
expectation: Fail expectation: Fail
outputs: outputs:
- " --> test:1:1\n |\n 1 | \"\"\n | ^\n |\n = unexpected token: '\"'"
- " --> test:1:1\n |\n 1 | \"Hello world!\n | ^\n |\n = unexpected token: '\"'" - " --> test:1:1\n |\n 1 | \"Hello world!\n | ^\n |\n = unexpected token: '\"'"
- " --> test:1:1\n |\n 1 | \"\\\"\n | ^\n |\n = unexpected token: '\"'" - " --> test:1:1\n |\n 1 | \"\\\"\n | ^\n |\n = unexpected token: '\"'"
- " --> test:1:1\n |\n 1 | \"\\l\"\n | ^\n |\n = unexpected token: '\"'" - " --> test:1:1\n |\n 1 | \"\\l\"\n | ^\n |\n = unexpected token: '\"'"

View File

@ -16,9 +16,9 @@ outputs:
- Console: - Console:
function: function:
Error: Error:
parts: string:
- Const: "" - Scalar: 123
- Container - Scalar: 125
parameters: parameters:
- Identifier: "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":21,\\\"col_stop\\\":22,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.error(\\\\\\\"{}\\\\\\\", x);\\\"}\"}" - Identifier: "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":21,\\\"col_stop\\\":22,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.error(\\\\\\\"{}\\\\\\\", x);\\\"}\"}"
span: span:
@ -38,11 +38,11 @@ outputs:
- Console: - Console:
function: function:
Error: Error:
parts: string:
- Const: "" - Scalar: 123
- Container - Scalar: 125
- Const: "" - Scalar: 123
- Container - Scalar: 125
parameters: parameters:
- Identifier: "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":23,\\\"col_stop\\\":24,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.error(\\\\\\\"{}{}\\\\\\\", x, y);\\\"}\"}" - Identifier: "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":23,\\\"col_stop\\\":24,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.error(\\\\\\\"{}{}\\\\\\\", x, y);\\\"}\"}"
- Identifier: "{\"name\":\"y\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":26,\\\"col_stop\\\":27,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.error(\\\\\\\"{}{}\\\\\\\", x, y);\\\"}\"}" - Identifier: "{\"name\":\"y\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":26,\\\"col_stop\\\":27,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.error(\\\\\\\"{}{}\\\\\\\", x, y);\\\"}\"}"
@ -63,8 +63,8 @@ outputs:
- Console: - Console:
function: function:
Error: Error:
parts: string:
- Const: x - Scalar: 120
parameters: [] parameters: []
span: span:
line_start: 1 line_start: 1
@ -83,9 +83,9 @@ outputs:
- Console: - Console:
function: function:
Debug: Debug:
parts: string:
- Const: "" - Scalar: 123
- Container - Scalar: 125
parameters: parameters:
- Identifier: "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":21,\\\"col_stop\\\":22,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.debug(\\\\\\\"{}\\\\\\\", x);\\\"}\"}" - Identifier: "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":21,\\\"col_stop\\\":22,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.debug(\\\\\\\"{}\\\\\\\", x);\\\"}\"}"
span: span:
@ -105,11 +105,11 @@ outputs:
- Console: - Console:
function: function:
Debug: Debug:
parts: string:
- Const: "" - Scalar: 123
- Container - Scalar: 125
- Const: "" - Scalar: 123
- Container - Scalar: 125
parameters: parameters:
- Identifier: "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":23,\\\"col_stop\\\":24,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.debug(\\\\\\\"{}{}\\\\\\\", x, y);\\\"}\"}" - Identifier: "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":23,\\\"col_stop\\\":24,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.debug(\\\\\\\"{}{}\\\\\\\", x, y);\\\"}\"}"
- Identifier: "{\"name\":\"y\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":26,\\\"col_stop\\\":27,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.debug(\\\\\\\"{}{}\\\\\\\", x, y);\\\"}\"}" - Identifier: "{\"name\":\"y\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":26,\\\"col_stop\\\":27,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.debug(\\\\\\\"{}{}\\\\\\\", x, y);\\\"}\"}"
@ -130,8 +130,8 @@ outputs:
- Console: - Console:
function: function:
Debug: Debug:
parts: string:
- Const: x - Scalar: 120
parameters: [] parameters: []
span: span:
line_start: 1 line_start: 1
@ -150,9 +150,9 @@ outputs:
- Console: - Console:
function: function:
Log: Log:
parts: string:
- Const: "" - Scalar: 123
- Container - Scalar: 125
parameters: parameters:
- Identifier: "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":19,\\\"col_stop\\\":20,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.log(\\\\\\\"{}\\\\\\\", x);\\\"}\"}" - Identifier: "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":19,\\\"col_stop\\\":20,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.log(\\\\\\\"{}\\\\\\\", x);\\\"}\"}"
span: span:
@ -172,11 +172,11 @@ outputs:
- Console: - Console:
function: function:
Log: Log:
parts: string:
- Const: "" - Scalar: 123
- Container - Scalar: 125
- Const: "" - Scalar: 123
- Container - Scalar: 125
parameters: parameters:
- Identifier: "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":21,\\\"col_stop\\\":22,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.log(\\\\\\\"{}{}\\\\\\\", x, y);\\\"}\"}" - Identifier: "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":21,\\\"col_stop\\\":22,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.log(\\\\\\\"{}{}\\\\\\\", x, y);\\\"}\"}"
- Identifier: "{\"name\":\"y\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":24,\\\"col_stop\\\":25,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.log(\\\\\\\"{}{}\\\\\\\", x, y);\\\"}\"}" - Identifier: "{\"name\":\"y\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":24,\\\"col_stop\\\":25,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.log(\\\\\\\"{}{}\\\\\\\", x, y);\\\"}\"}"
@ -197,8 +197,8 @@ outputs:
- Console: - Console:
function: function:
Log: Log:
parts: string:
- Const: x - Scalar: 120
parameters: [] parameters: []
span: span:
line_start: 1 line_start: 1

View File

@ -3,6 +3,8 @@ namespace: Token
expectation: Pass expectation: Pass
*/ */
""
"string" "string"
"another { } string" "another { } string"

View File

@ -3,8 +3,6 @@ namespace: Token
expectation: Fail expectation: Fail
*/ */
""
"Hello world! "Hello world!
"\" "\"