1
0
Fork 0
mirror of synced 2025-09-23 12:18:44 +00:00

add debug flag to enable logs, add expression logging without abi and interpolation

This commit is contained in:
schaeff 2022-06-22 11:09:53 +02:00
parent 5ea93bb677
commit 57f42b5d5f
26 changed files with 284 additions and 34 deletions

View file

@ -58,7 +58,11 @@ pub fn subcommand() -> App<'static, 'static> {
.long("isolate-branches") .long("isolate-branches")
.help("Isolate the execution of branches: a panic in a branch only makes the program panic if this branch is being logically executed") .help("Isolate the execution of branches: a panic in a branch only makes the program panic if this branch is being logically executed")
.required(false) .required(false)
) ).arg(Arg::with_name("debug")
.long("debug")
.help("Include logs")
.required(false)
)
} }
pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> { pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> {
@ -106,8 +110,9 @@ fn cli_compile<T: Field>(sub_matches: &ArgMatches) -> Result<(), String> {
)), )),
}?; }?;
let config = let config = CompileConfig::default()
CompileConfig::default().isolate_branches(sub_matches.is_present("isolate-branches")); .isolate_branches(sub_matches.is_present("isolate-branches"))
.debug(sub_matches.is_present("debug"));
let resolver = FileSystemResolver::with_stdlib_root(stdlib_path); let resolver = FileSystemResolver::with_stdlib_root(stdlib_path);

View file

@ -356,7 +356,13 @@ impl<'ast> From<pest::LogStatement<'ast>> for absy::StatementNode<'ast> {
fn from(statement: pest::LogStatement<'ast>) -> absy::StatementNode<'ast> { fn from(statement: pest::LogStatement<'ast>) -> absy::StatementNode<'ast> {
use crate::absy::NodeValue; use crate::absy::NodeValue;
absy::Statement::Log(statement.content.inner).span(statement.span) let expressions = statement
.expressions
.into_iter()
.map(|e| absy::ExpressionNode::from(e))
.collect();
absy::Statement::Log(statement.content.inner, expressions).span(statement.span)
} }
} }

View file

@ -395,7 +395,7 @@ pub enum Statement<'ast> {
Vec<StatementNode<'ast>>, Vec<StatementNode<'ast>>,
), ),
MultipleDefinition(Vec<AssigneeNode<'ast>>, ExpressionNode<'ast>), MultipleDefinition(Vec<AssigneeNode<'ast>>, ExpressionNode<'ast>),
Log(String), Log(String, Vec<ExpressionNode<'ast>>),
} }
pub type StatementNode<'ast> = Node<Statement<'ast>>; pub type StatementNode<'ast> = Node<Statement<'ast>>;
@ -429,7 +429,16 @@ impl<'ast> fmt::Display for Statement<'ast> {
} }
write!(f, " = {}", rhs) write!(f, " = {}", rhs)
} }
Statement::Log(ref l) => write!(f, "log!({})", l), Statement::Log(ref l, ref expressions) => write!(
f,
"log!({}, {})",
l,
expressions
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(", ")
),
} }
} }
} }

View file

@ -176,6 +176,8 @@ impl fmt::Display for CompileErrorInner {
pub struct CompileConfig { pub struct CompileConfig {
#[serde(default)] #[serde(default)]
pub isolate_branches: bool, pub isolate_branches: bool,
#[serde(default)]
pub debug: bool,
} }
impl CompileConfig { impl CompileConfig {
@ -183,6 +185,11 @@ impl CompileConfig {
self.isolate_branches = flag; self.isolate_branches = flag;
self self
} }
pub fn debug(mut self, debug: bool) -> Self {
self.debug = debug;
self
}
} }
type FilePath = PathBuf; type FilePath = PathBuf;
@ -198,6 +205,8 @@ pub fn compile<'ast, T: Field, E: Into<imports::Error>>(
let (typed_ast, abi): (crate::zir::ZirProgram<'_, T>, _) = let (typed_ast, abi): (crate::zir::ZirProgram<'_, T>, _) =
check_with_arena(source, location, resolver, &config, arena)?; check_with_arena(source, location, resolver, &config, arena)?;
println!("{}", typed_ast);
// flatten input program // flatten input program
log::debug!("Flatten"); log::debug!("Flatten");
let program_flattened = FlattenerIterator::from_function_and_config(typed_ast.main, config); let program_flattened = FlattenerIterator::from_function_and_config(typed_ast.main, config);

View file

@ -14,6 +14,7 @@ pub use self::flat_variable::FlatVariable;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::solvers::Solver; use crate::solvers::Solver;
use crate::typed_absy::ConcreteType;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt;
use zokrates_field::Field; use zokrates_field::Field;
@ -159,7 +160,7 @@ pub enum FlatStatement<T> {
Condition(FlatExpression<T>, FlatExpression<T>, RuntimeError), Condition(FlatExpression<T>, FlatExpression<T>, RuntimeError),
Definition(FlatVariable, FlatExpression<T>), Definition(FlatVariable, FlatExpression<T>),
Directive(FlatDirective<T>), Directive(FlatDirective<T>),
Log(String), Log(String, Vec<(ConcreteType, Vec<FlatExpression<T>>)>),
} }
impl<T: Field> fmt::Display for FlatStatement<T> { impl<T: Field> fmt::Display for FlatStatement<T> {
@ -170,7 +171,22 @@ impl<T: Field> fmt::Display for FlatStatement<T> {
write!(f, "{} == {} // {}", lhs, rhs, message) write!(f, "{} == {} // {}", lhs, rhs, message)
} }
FlatStatement::Directive(ref d) => write!(f, "{}", d), FlatStatement::Directive(ref d) => write!(f, "{}", d),
FlatStatement::Log(ref l) => write!(f, "log!({})", l), FlatStatement::Log(ref l, ref expressions) => write!(
f,
"log!(\"{}\"), {})",
l,
expressions
.iter()
.map(|(_, e)| format!(
"[{}]",
e.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join(", ")
))
.collect::<Vec<_>>()
.join(", ")
),
} }
} }
} }
@ -208,7 +224,19 @@ impl<T: Field> FlatStatement<T> {
..d ..d
}) })
} }
FlatStatement::Log(l) => FlatStatement::Log(l), FlatStatement::Log(l, e) => FlatStatement::Log(
l,
e.into_iter()
.map(|(t, e)| {
(
t,
e.into_iter()
.map(|e| e.apply_substitution(substitution))
.collect(),
)
})
.collect(),
),
} }
} }
} }

View file

@ -1234,7 +1234,20 @@ impl<'ast, T: Field> Flattener<'ast, T> {
inputs: new_inputs, inputs: new_inputs,
}) })
} }
FlatStatement::Log(l) => FlatStatement::Log(l), FlatStatement::Log(l, expressions) => FlatStatement::Log(
l,
expressions
.into_iter()
.map(|(t, e)| {
(
t,
e.into_iter()
.map(|e| e.apply_substitution(&replacement_map))
.collect(),
)
})
.collect(),
),
}); });
statements_flattened.extend(statements); statements_flattened.extend(statements);
@ -2573,8 +2586,23 @@ impl<'ast, T: Field> Flattener<'ast, T> {
} }
} }
} }
ZirStatement::Log(l) => { ZirStatement::Log(l, expressions) => {
statements_flattened.push_back(FlatStatement::Log(l)); let expressions = expressions
.into_iter()
.map(|(t, e)| {
(
t,
e.into_iter()
.map(|e| {
self.flatten_expression(statements_flattened, e)
.get_field_unchecked()
})
.collect(),
)
})
.collect();
statements_flattened.push_back(FlatStatement::Log(l, expressions));
} }
} }
} }

View file

@ -58,7 +58,19 @@ pub fn fold_statement<T: Field, F: Folder<T>>(f: &mut F, s: Statement<T>) -> Vec
message, message,
)], )],
Statement::Directive(dir) => vec![Statement::Directive(f.fold_directive(dir))], Statement::Directive(dir) => vec![Statement::Directive(f.fold_directive(dir))],
Statement::Log(l) => vec![Statement::Log(l)], Statement::Log(l, e) => vec![Statement::Log(
l,
e.into_iter()
.map(|(t, e)| {
(
t,
e.into_iter()
.map(|e| f.fold_linear_combination(e))
.collect(),
)
})
.collect(),
)],
} }
} }

View file

@ -74,7 +74,13 @@ impl<T: Field> From<FlatStatement<T>> for Statement<T> {
e => Statement::Constraint(LinComb::from(e).into(), var.into(), None), e => Statement::Constraint(LinComb::from(e).into(), var.into(), None),
}, },
FlatStatement::Directive(ds) => Statement::Directive(ds.into()), FlatStatement::Directive(ds) => Statement::Directive(ds.into()),
FlatStatement::Log(l) => Statement::Log(l), FlatStatement::Log(l, expressions) => Statement::Log(
l,
expressions
.into_iter()
.map(|(t, e)| (t, e.into_iter().map(|e| LinComb::from(e)).collect()))
.collect(),
),
} }
} }
} }

View file

@ -76,8 +76,24 @@ impl Interpreter {
witness.insert(*o, res[i].clone()); witness.insert(*o, res[i].clone());
} }
} }
Statement::Log(l) => { Statement::Log(l, expressions) => {
writeln!(log_stream, "{}", l).map_err(|_| Error::LogStream)?; write!(log_stream, "{}", l).map_err(|_| Error::LogStream)?;
write!(log_stream, " - ").map_err(|_| Error::LogStream)?;
let len = expressions.len();
for (index, (t, e)) in expressions.into_iter().enumerate() {
let values: Vec<_> =
e.iter().map(|e| e.evaluate(&witness).unwrap()).collect();
write!(log_stream, "{:?}", values).map_err(|_| Error::LogStream)?;
if index < len - 1 {
write!(log_stream, ", ").map_err(|_| Error::LogStream)?;
}
}
writeln!(log_stream).map_err(|_| Error::LogStream)?;
} }
} }
} }

View file

@ -1,6 +1,7 @@
use crate::flat_absy::flat_parameter::FlatParameter; use crate::flat_absy::flat_parameter::FlatParameter;
use crate::flat_absy::{FlatVariable, RuntimeError}; use crate::flat_absy::{FlatVariable, RuntimeError};
use crate::solvers::Solver; use crate::solvers::Solver;
use crate::typed_absy::ConcreteType;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
use std::hash::Hash; use std::hash::Hash;
@ -26,7 +27,7 @@ pub use self::witness::Witness;
pub enum Statement<T> { pub enum Statement<T> {
Constraint(QuadComb<T>, LinComb<T>, Option<RuntimeError>), Constraint(QuadComb<T>, LinComb<T>, Option<RuntimeError>),
Directive(Directive<T>), Directive(Directive<T>),
Log(String), Log(String, Vec<(ConcreteType, Vec<LinComb<T>>)>),
} }
impl<T: Field> Statement<T> { impl<T: Field> Statement<T> {
@ -71,7 +72,22 @@ impl<T: Field> fmt::Display for Statement<T> {
match *self { match *self {
Statement::Constraint(ref quad, ref lin, _) => write!(f, "{} == {}", quad, lin), Statement::Constraint(ref quad, ref lin, _) => write!(f, "{} == {}", quad, lin),
Statement::Directive(ref s) => write!(f, "{}", s), Statement::Directive(ref s) => write!(f, "{}", s),
Statement::Log(ref s) => write!(f, "{}", s), Statement::Log(ref s, ref expressions) => write!(
f,
"log!(\"{}\", {})",
s,
expressions
.iter()
.map(|(_, l)| format!(
"[{}]",
l.iter()
.map(|l| l.to_string())
.collect::<Vec<_>>()
.join(", ")
))
.collect::<Vec<_>>()
.join(", ")
),
} }
} }
} }

View file

@ -86,7 +86,7 @@ impl<T: Field> SMTLib2 for Statement<T> {
write!(f, " |~prime|))") write!(f, " |~prime|))")
} }
Statement::Directive(ref s) => s.to_smtlib2(f), Statement::Directive(ref s) => s.to_smtlib2(f),
Statement::Log(_) => write!(f, ""), Statement::Log(..) => write!(f, ""),
} }
} }
} }

View file

@ -61,7 +61,13 @@ pub fn visit_statement<T: Field, F: Visitor<T>>(f: &mut F, s: &Statement<T>) {
} }
} }
Statement::Directive(dir) => f.visit_directive(dir), Statement::Directive(dir) => f.visit_directive(dir),
Statement::Log(_) => {} Statement::Log(_, expressions) => {
for (_, e) in expressions {
for e in e {
f.visit_linear_combination(e);
}
}
}
} }
} }

View file

@ -36,10 +36,10 @@
// - `q == k * v if v isn't in i`: insert `v` into `i` and return `c_0` // - `q == k * v if v isn't in i`: insert `v` into `i` and return `c_0`
// - otherwise return `c_0` // - otherwise return `c_0`
use crate::flat_absy::flat_variable::FlatVariable;
use crate::ir::folder::Folder; use crate::ir::folder::Folder;
use crate::ir::LinComb; use crate::ir::LinComb;
use crate::ir::*; use crate::ir::*;
use crate::{flat_absy::flat_variable::FlatVariable, ir::folder::fold_statement};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use zokrates_field::Field; use zokrates_field::Field;
@ -168,7 +168,7 @@ impl<T: Field> Folder<T> for RedefinitionOptimizer<T> {
} }
} }
} }
Statement::Log(l) => vec![Statement::Log(l)], s => fold_statement(self, s),
} }
} }

View file

@ -2024,7 +2024,15 @@ impl<'ast, T: Field> Checker<'ast, T> {
}), }),
}.map_err(|e| vec![e]) }.map_err(|e| vec![e])
} }
Statement::Log(l) => Ok(TypedStatement::Log(l)), Statement::Log(l, expressions) => {
let expressions = expressions
.into_iter()
.map(|e| self.check_expression(e, module_id, types))
.collect::<Result<Vec<_>, _>>()
.map_err(|e| vec![e])?;
Ok(TypedStatement::Log(l, expressions))
}
} }
} }

View file

@ -75,7 +75,13 @@ impl<T: Field> FlatStatement<T> {
.collect(), .collect(),
..d ..d
})), })),
FlatStatement::Log(l) => Some(FlatStatement::Log(l)), FlatStatement::Log(l, expressions) => Some(FlatStatement::Log(
l,
expressions
.into_iter()
.map(|(t, e)| (t, e.into_iter().map(|e| e.propagate(constants)).collect()))
.collect(),
)),
} }
} }
} }

View file

@ -1,5 +1,5 @@
use crate::typed_absy::types::UBitwidth; use crate::typed_absy::types::UBitwidth;
use crate::typed_absy::{self, Expr}; use crate::typed_absy::{self, Expr, Typed};
use crate::zir; use crate::zir;
use std::marker::PhantomData; use std::marker::PhantomData;
use zokrates_field::Field; use zokrates_field::Field;
@ -474,7 +474,17 @@ fn fold_statement<'ast, T: Field>(
f.fold_expression_list(statements_buffer, elist), f.fold_expression_list(statements_buffer, elist),
)] )]
} }
typed_absy::TypedStatement::Log(l) => vec![zir::ZirStatement::Log(l)], typed_absy::TypedStatement::Log(l, e) => vec![zir::ZirStatement::Log(
l,
e.into_iter()
.map(|e| {
(
e.get_type().try_into().unwrap(),
f.fold_expression(statements_buffer, e),
)
})
.collect(),
)],
typed_absy::TypedStatement::PushCallLog(..) => vec![], typed_absy::TypedStatement::PushCallLog(..) => vec![],
typed_absy::TypedStatement::PopCallLog => vec![], typed_absy::TypedStatement::PopCallLog => vec![],
}; };

View file

@ -0,0 +1,20 @@
use crate::typed_absy::{folder::*, TypedProgram, TypedStatement};
use zokrates_field::Field;
#[derive(Default)]
pub struct LogIgnorer;
impl LogIgnorer {
pub fn ignore<'ast, T: Field>(p: TypedProgram<'ast, T>) -> TypedProgram<'ast, T> {
Self::default().fold_program(p)
}
}
impl<'ast, T: Field> Folder<'ast, T> for LogIgnorer {
fn fold_statement(&mut self, s: TypedStatement<'ast, T>) -> Vec<TypedStatement<'ast, T>> {
match s {
TypedStatement::Log(..) => vec![],
s => fold_statement(self, s),
}
}
}

View file

@ -10,6 +10,7 @@ mod constant_argument_checker;
mod constant_resolver; mod constant_resolver;
mod flat_propagation; mod flat_propagation;
mod flatten_complex_types; mod flatten_complex_types;
mod log_ignorer;
mod out_of_bounds; mod out_of_bounds;
mod propagation; mod propagation;
mod reducer; mod reducer;
@ -23,6 +24,7 @@ use self::branch_isolator::Isolator;
use self::condition_redefiner::ConditionRedefiner; use self::condition_redefiner::ConditionRedefiner;
use self::constant_argument_checker::ConstantArgumentChecker; use self::constant_argument_checker::ConstantArgumentChecker;
use self::flatten_complex_types::Flattener; use self::flatten_complex_types::Flattener;
use self::log_ignorer::LogIgnorer;
use self::out_of_bounds::OutOfBoundsChecker; use self::out_of_bounds::OutOfBoundsChecker;
use self::propagation::Propagator; use self::propagation::Propagator;
use self::reducer::reduce_program; use self::reducer::reduce_program;
@ -107,6 +109,17 @@ impl<'ast, T: Field> TypedProgram<'ast, T> {
r r
}; };
// include logs
let r = if config.debug {
log::debug!("Static analyser: Include logs");
r
} else {
log::debug!("Static analyser: Ignore logs");
let r = LogIgnorer::ignore(r);
log::trace!("\n{}", r);
r
};
// reduce the program to a single function // reduce the program to a single function
log::debug!("Static analyser: Reduce program"); log::debug!("Static analyser: Reduce program");
let r = reduce_program(r).map_err(Error::from)?; let r = reduce_program(r).map_err(Error::from)?;

View file

@ -540,6 +540,9 @@ pub fn fold_statement<'ast, T: Field, F: Folder<'ast, T>>(
assignees.into_iter().map(|a| f.fold_assignee(a)).collect(), assignees.into_iter().map(|a| f.fold_assignee(a)).collect(),
f.fold_expression_list(elist), f.fold_expression_list(elist),
), ),
TypedStatement::Log(s, e) => {
TypedStatement::Log(s, e.into_iter().map(|e| f.fold_expression(e)).collect())
}
s => s, s => s,
}; };
vec![res] vec![res]

View file

@ -641,7 +641,7 @@ pub enum TypedStatement<'ast, T> {
Vec<TypedStatement<'ast, T>>, Vec<TypedStatement<'ast, T>>,
), ),
MultipleDefinition(Vec<TypedAssignee<'ast, T>>, TypedExpressionList<'ast, T>), MultipleDefinition(Vec<TypedAssignee<'ast, T>>, TypedExpressionList<'ast, T>),
Log(String), Log(String, Vec<TypedExpression<'ast, T>>),
// Aux // Aux
PushCallLog( PushCallLog(
DeclarationFunctionKey<'ast, T>, DeclarationFunctionKey<'ast, T>,
@ -708,7 +708,16 @@ impl<'ast, T: fmt::Display> fmt::Display for TypedStatement<'ast, T> {
} }
write!(f, " = {}", rhs) write!(f, " = {}", rhs)
} }
TypedStatement::Log(ref l) => write!(f, "log!({})", l), TypedStatement::Log(ref l, ref expressions) => write!(
f,
"log!({}, {})",
l,
expressions
.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join(", ")
),
TypedStatement::PushCallLog(ref key, ref generics) => write!( TypedStatement::PushCallLog(ref key, ref generics) => write!(
f, f,
"// PUSH CALL TO {}/{}::<{}>", "// PUSH CALL TO {}/{}::<{}>",

View file

@ -555,6 +555,12 @@ pub fn fold_statement<'ast, T: Field, F: ResultFolder<'ast, T>>(
.collect::<Result<_, _>>()?, .collect::<Result<_, _>>()?,
f.fold_expression_list(elist)?, f.fold_expression_list(elist)?,
), ),
TypedStatement::Log(s, e) => TypedStatement::Log(
s,
e.into_iter()
.map(|e| f.fold_expression(e))
.collect::<Result<Vec<_>, _>>()?,
),
s => s, s => s,
}; };
Ok(vec![res]) Ok(vec![res])

View file

@ -122,7 +122,12 @@ pub fn fold_statement<'ast, T: Field, F: Folder<'ast, T>>(
variables.into_iter().map(|v| f.fold_variable(v)).collect(), variables.into_iter().map(|v| f.fold_variable(v)).collect(),
f.fold_expression_list(elist), f.fold_expression_list(elist),
), ),
ZirStatement::Log(l) => ZirStatement::Log(l), ZirStatement::Log(l, e) => ZirStatement::Log(
l,
e.into_iter()
.map(|(t, e)| (t, e.into_iter().map(|e| f.fold_expression(e)).collect()))
.collect(),
),
}; };
vec![res] vec![res]
} }

View file

@ -10,6 +10,7 @@ mod variable;
pub use self::parameter::Parameter; pub use self::parameter::Parameter;
pub use self::types::Type; pub use self::types::Type;
pub use self::variable::Variable; pub use self::variable::Variable;
use crate::typed_absy::ConcreteType;
pub use crate::zir::uint::{ShouldReduce, UExpression, UExpressionInner, UMetadata}; pub use crate::zir::uint::{ShouldReduce, UExpression, UExpressionInner, UMetadata};
use crate::embed::FlatEmbed; use crate::embed::FlatEmbed;
@ -112,7 +113,7 @@ pub enum ZirStatement<'ast, T> {
), ),
Assertion(BooleanExpression<'ast, T>, RuntimeError), Assertion(BooleanExpression<'ast, T>, RuntimeError),
MultipleDefinition(Vec<ZirAssignee<'ast>>, ZirExpressionList<'ast, T>), MultipleDefinition(Vec<ZirAssignee<'ast>>, ZirExpressionList<'ast, T>),
Log(String), Log(String, Vec<(ConcreteType, Vec<ZirExpression<'ast, T>>)>),
} }
impl<'ast, T: fmt::Display> fmt::Display for ZirStatement<'ast, T> { impl<'ast, T: fmt::Display> fmt::Display for ZirStatement<'ast, T> {
@ -162,7 +163,22 @@ impl<'ast, T: fmt::Display> fmt::Display for ZirStatement<'ast, T> {
} }
write!(f, " = {}", rhs) write!(f, " = {}", rhs)
} }
ZirStatement::Log(ref l) => write!(f, "log!({})", l), ZirStatement::Log(ref l, ref expressions) => write!(
f,
"log!(\"{}\"), {})",
l,
expressions
.iter()
.map(|(_, e)| format!(
"[{}]",
e.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join(", ")
))
.collect::<Vec<_>>()
.join(", ")
),
} }
} }
} }

View file

@ -147,7 +147,19 @@ pub fn fold_statement<'ast, T: Field, F: ResultFolder<'ast, T>>(
.collect::<Result<_, _>>()?, .collect::<Result<_, _>>()?,
f.fold_expression_list(elist)?, f.fold_expression_list(elist)?,
), ),
ZirStatement::Log(l) => ZirStatement::Log(l), ZirStatement::Log(l, e) => {
let e = e
.into_iter()
.map(|(t, e)| {
e.into_iter()
.map(|e| f.fold_expression(e))
.collect::<Result<Vec<_>, _>>()
.map(|e| (t, e))
})
.collect::<Result<Vec<_>, _>>()?;
ZirStatement::Log(l, e)
}
}; };
Ok(vec![res]) Ok(vec![res])
} }

View file

@ -60,8 +60,8 @@ statement = { (return_statement // does not require subsequent newline
) ~ NEWLINE ) ~ NEWLINE
) ~ NEWLINE* } ) ~ NEWLINE* }
log_statement = { "log!(\"" ~ log_statement_content ~ "\")"} log_statement = { "log!(\"" ~ log_statement_content ~ "\"" ~ "," ~ expression_list ~ ")"}
log_statement_content = { ASCII_ALPHANUMERIC* } log_statement_content = { (ASCII_ALPHANUMERIC | "{" | "}")* }
iteration_statement = { "for" ~ ty ~ identifier ~ "in" ~ expression ~ ".." ~ expression ~ "do" ~ NEWLINE* ~ statement* ~ "endfor"} iteration_statement = { "for" ~ ty ~ identifier ~ "in" ~ expression ~ ".." ~ expression ~ "do" ~ NEWLINE* ~ statement* ~ "endfor"}
return_statement = { "return" ~ expression_list} return_statement = { "return" ~ expression_list}
definition_statement = { typed_identifier_or_assignee_list ~ "=" ~ expression } // declare and assign, so only identifiers are allowed, unlike `assignment_statement` definition_statement = { typed_identifier_or_assignee_list ~ "=" ~ expression } // declare and assign, so only identifiers are allowed, unlike `assignment_statement`

View file

@ -373,6 +373,7 @@ mod ast {
#[pest_ast(rule(Rule::log_statement))] #[pest_ast(rule(Rule::log_statement))]
pub struct LogStatement<'ast> { pub struct LogStatement<'ast> {
pub content: LogStatementContent<'ast>, pub content: LogStatementContent<'ast>,
pub expressions: Vec<Expression<'ast>>,
#[pest_ast(outer())] #[pest_ast(outer())]
pub span: Span<'ast>, pub span: Span<'ast>,
} }