From a9593971b9d127760c31bc8d91b6b55e207562b0 Mon Sep 17 00:00:00 2001 From: gluaxspeed Date: Tue, 20 Jul 2021 14:52:35 -0700 Subject: [PATCH 1/6] format string and grammar changes --- asg/src/checks/return_path.rs | 2 +- asg/src/const_value.rs | 26 ++++- asg/src/reducer/monoidal_director.rs | 2 +- asg/src/reducer/monoidal_reducer.rs | 2 +- asg/src/reducer/reconstructing_director.rs | 2 +- asg/src/reducer/reconstructing_reducer.rs | 10 +- asg/src/reducer/visitor.rs | 2 +- asg/src/reducer/visitor_director.rs | 2 +- asg/src/statement/console.rs | 75 ++++++------- .../fail/console/log_parameter_fail_empty.leo | 3 - .../fail/console/log_parameter_fail_none.leo | 3 - asg/tests/fail/console/mod.rs | 12 -- ast/src/errors/reducer.rs | 7 ++ ast/src/reducer/canonicalization.rs | 20 ++-- ast/src/reducer/reconstructing_director.rs | 12 +- ast/src/statements/console/console_args.rs | 96 ++++++++++++++++ .../statements/console/console_function.rs | 8 +- .../statements/console/formatted_string.rs | 103 ------------------ ast/src/statements/console/mod.rs | 9 +- compiler/src/console/format.rs | 85 ++++++++++----- compiler/src/errors/console.rs | 12 ++ compiler/src/phases/reducing_director.rs | 26 +++-- grammar/README.md | Bin 54154 -> 111950 bytes grammar/abnf-grammar.txt | 2 +- parser/README.md | 5 + parser/src/parser/statement.rs | 16 +-- parser/src/tokenizer/lexer.rs | 2 +- .../console/log_parameter_fail_empty.leo.out | 2 +- .../console/log_parameter_fail_none.leo.out | 2 +- .../parser/expression/literal/string.leo.out | 1 + .../expression/literal/string_fail.leo.out | 1 - .../parser/parser/statement/console.leo.out | 60 +++++----- tests/parser/expression/literal/string.leo | 2 + .../parser/expression/literal/string_fail.leo | 2 - 34 files changed, 328 insertions(+), 286 deletions(-) delete mode 100644 asg/tests/fail/console/log_parameter_fail_empty.leo delete mode 100644 asg/tests/fail/console/log_parameter_fail_none.leo create mode 100644 ast/src/statements/console/console_args.rs delete mode 100644 ast/src/statements/console/formatted_string.rs create mode 100644 parser/README.md diff --git a/asg/src/checks/return_path.rs b/asg/src/checks/return_path.rs index e0f73d3c0d..ed8874deb2 100644 --- a/asg/src/checks/return_path.rs +++ b/asg/src/checks/return_path.rs @@ -92,7 +92,7 @@ impl<'a> MonoidalReducerStatement<'a, BoolAnd> for ReturnPathReducer { if_true.append(if_false.unwrap_or(BoolAnd(false))) } - fn reduce_formatted_string(&mut self, input: &FormatString, parameters: Vec) -> BoolAnd { + fn reduce_formatted_string(&mut self, input: &ConsoleArgs, parameters: Vec) -> BoolAnd { BoolAnd(false) } diff --git a/asg/src/const_value.rs b/asg/src/const_value.rs index 5a1c57d2a7..d430f0bbc4 100644 --- a/asg/src/const_value.rs +++ b/asg/src/const_value.rs @@ -109,13 +109,29 @@ pub enum CharValue { 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 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 for CharValue { fn from(other: leo_ast::CharValue) -> Self { - use leo_ast::Char::*; - match other.character { - Scalar(value) => CharValue::Scalar(value), - NonScalar(value) => CharValue::NonScalar(value), - } + Self::from(&other.character) } } diff --git a/asg/src/reducer/monoidal_director.rs b/asg/src/reducer/monoidal_director.rs index b0f97fec7b..ce34755a11 100644 --- a/asg/src/reducer/monoidal_director.rs +++ b/asg/src/reducer/monoidal_director.rs @@ -225,7 +225,7 @@ impl<'a, T: Monoid, R: MonoidalReducerStatement<'a, T>> MonoidalDirector<'a, T, .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 .parameters .iter() diff --git a/asg/src/reducer/monoidal_reducer.rs b/asg/src/reducer/monoidal_reducer.rs index 2a10604e1e..9510417241 100644 --- a/asg/src/reducer/monoidal_reducer.rs +++ b/asg/src/reducer/monoidal_reducer.rs @@ -118,7 +118,7 @@ pub trait MonoidalReducerStatement<'a, T: Monoid>: MonoidalReducerExpression<'a, condition.append(if_true).append_option(if_false) } - fn reduce_formatted_string(&mut self, input: &FormatString<'a>, parameters: Vec) -> T { + fn reduce_formatted_string(&mut self, input: &ConsoleArgs<'a>, parameters: Vec) -> T { T::default().append_all(parameters.into_iter()) } diff --git a/asg/src/reducer/reconstructing_director.rs b/asg/src/reducer/reconstructing_director.rs index 080eb1c5db..75127d477a 100644 --- a/asg/src/reducer/reconstructing_director.rs +++ b/asg/src/reducer/reconstructing_director.rs @@ -243,7 +243,7 @@ impl<'a, R: ReconstructingReducerStatement<'a>> ReconstructingDirector<'a, R> { .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 .parameters .iter() diff --git a/asg/src/reducer/reconstructing_reducer.rs b/asg/src/reducer/reconstructing_reducer.rs index f42a707bc8..cf4e617ec4 100644 --- a/asg/src/reducer/reconstructing_reducer.rs +++ b/asg/src/reducer/reconstructing_reducer.rs @@ -274,12 +274,12 @@ pub trait ReconstructingReducerStatement<'a>: ReconstructingReducerExpression<'a fn reduce_formatted_string( &mut self, - input: FormatString<'a>, + input: ConsoleArgs<'a>, parameters: Vec<&'a Expression<'a>>, - ) -> FormatString<'a> { - FormatString { + ) -> ConsoleArgs<'a> { + ConsoleArgs { span: input.span, - parts: input.parts, + string: input.string, 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(_))); Statement::Console(ConsoleStatement { parent: input.parent, diff --git a/asg/src/reducer/visitor.rs b/asg/src/reducer/visitor.rs index 0bd16527bd..1e0b46fd9e 100644 --- a/asg/src/reducer/visitor.rs +++ b/asg/src/reducer/visitor.rs @@ -120,7 +120,7 @@ pub trait StatementVisitor<'a>: ExpressionVisitor<'a> { Default::default() } - fn visit_formatted_string(&mut self, input: &FormatString<'a>) -> VisitResult { + fn visit_formatted_string(&mut self, input: &ConsoleArgs<'a>) -> VisitResult { Default::default() } diff --git a/asg/src/reducer/visitor_director.rs b/asg/src/reducer/visitor_director.rs index ee117dffb8..dc6dae6929 100644 --- a/asg/src/reducer/visitor_director.rs +++ b/asg/src/reducer/visitor_director.rs @@ -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) { VisitResult::VisitChildren => { for parameter in input.parameters.iter() { diff --git a/asg/src/statement/console.rs b/asg/src/statement/console.rs index 5e47cb0012..59992f85a2 100644 --- a/asg/src/statement/console.rs +++ b/asg/src/statement/console.rs @@ -14,15 +14,15 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use crate::{AsgConvertError, Expression, FromAst, Node, PartialType, Scope, Span, Statement, Type}; -use leo_ast::{ConsoleFunction as AstConsoleFunction, FormatStringPart}; +use crate::{AsgConvertError, CharValue, Expression, FromAst, Node, PartialType, Scope, Span, Statement, Type}; +use leo_ast::ConsoleFunction as AstConsoleFunction; use std::cell::Cell; // TODO (protryon): Refactor to not require/depend on span #[derive(Clone)] -pub struct FormatString<'a> { - pub parts: Vec, +pub struct ConsoleArgs<'a> { + pub string: Vec, pub parameters: Vec>>, pub span: Span, } @@ -30,9 +30,9 @@ pub struct FormatString<'a> { #[derive(Clone)] pub enum ConsoleFunction<'a> { Assert(Cell<&'a Expression<'a>>), - Debug(FormatString<'a>), - Error(FormatString<'a>), - Log(FormatString<'a>), + Debug(ConsoleArgs<'a>), + Error(ConsoleArgs<'a>), + Log(ConsoleArgs<'a>), } #[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( scope: &'a Scope<'a>, - value: &leo_ast::FormatString, + value: &leo_ast::ConsoleArgs, _expected_type: Option>, ) -> Result { - let expected_param_len = value - .parts - .iter() - .filter(|x| matches!(x, FormatStringPart::Container)) - .count(); - if value.parameters.len() != expected_param_len { - // + 1 for formatting string as to not confuse user - return Err(AsgConvertError::unexpected_call_argument_count( - expected_param_len + 1, - value.parameters.len() + 1, - &value.span, - )); - } + // let expected_param_len = value + // .parts + // .iter() + // .filter(|x| matches!(x, FormatStringPart::Container)) + // .count(); + // if value.parameters.len() != expected_param_len { + // + 1 for formatting string as to not confuse user + // return Err(AsgConvertError::unexpected_call_argument_count( + // expected_param_len + 1, + // value.parameters.len() + 1, + // &value.span, + // )); + // } + let mut parameters = vec![]; for parameter in value.parameters.iter() { parameters.push(Cell::new(<&Expression<'a>>::from_ast(scope, parameter, None)?)); } - Ok(FormatString { - parts: value.parts.clone(), + Ok(ConsoleArgs { + string: value.string.iter().map(CharValue::from).collect::>(), parameters, span: value.span.clone(), }) } } -impl<'a> Into for &FormatString<'a> { - fn into(self) -> leo_ast::FormatString { - leo_ast::FormatString { - parts: self.parts.clone(), +impl<'a> Into for &ConsoleArgs<'a> { + fn into(self) -> leo_ast::ConsoleArgs { + leo_ast::ConsoleArgs { + string: self.string.iter().map(|c| c.into()).collect::>(), parameters: self.parameters.iter().map(|e| e.get().into()).collect(), 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( <&Expression<'a>>::from_ast(scope, expression, Some(Type::Boolean.into()))?, )), - AstConsoleFunction::Debug(formatted_string) => { - ConsoleFunction::Debug(FormatString::from_ast(scope, formatted_string, 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)?) - } + AstConsoleFunction::Debug(args) => ConsoleFunction::Debug(ConsoleArgs::from_ast(scope, args, None)?), + AstConsoleFunction::Error(args) => ConsoleFunction::Error(ConsoleArgs::from_ast(scope, args, None)?), + AstConsoleFunction::Log(args) => ConsoleFunction::Log(ConsoleArgs::from_ast(scope, args, None)?), }, }) } @@ -122,9 +117,9 @@ impl<'a> Into for &ConsoleStatement<'a> { leo_ast::ConsoleStatement { function: match &self.function { Assert(e) => AstConsoleFunction::Assert(e.get().into()), - Debug(formatted_string) => AstConsoleFunction::Debug(formatted_string.into()), - Error(formatted_string) => AstConsoleFunction::Error(formatted_string.into()), - Log(formatted_string) => AstConsoleFunction::Log(formatted_string.into()), + Debug(args) => AstConsoleFunction::Debug(args.into()), + Error(args) => AstConsoleFunction::Error(args.into()), + Log(args) => AstConsoleFunction::Log(args.into()), }, span: self.span.clone().unwrap_or_default(), } diff --git a/asg/tests/fail/console/log_parameter_fail_empty.leo b/asg/tests/fail/console/log_parameter_fail_empty.leo deleted file mode 100644 index 81b42c0919..0000000000 --- a/asg/tests/fail/console/log_parameter_fail_empty.leo +++ /dev/null @@ -1,3 +0,0 @@ -function main() { - console.log("{}"); -} \ No newline at end of file diff --git a/asg/tests/fail/console/log_parameter_fail_none.leo b/asg/tests/fail/console/log_parameter_fail_none.leo deleted file mode 100644 index c92fdfbb2d..0000000000 --- a/asg/tests/fail/console/log_parameter_fail_none.leo +++ /dev/null @@ -1,3 +0,0 @@ -function main() { - console.log("", 1u32); -} \ No newline at end of file diff --git a/asg/tests/fail/console/mod.rs b/asg/tests/fail/console/mod.rs index 9f967460f3..d825a4db05 100644 --- a/asg/tests/fail/console/mod.rs +++ b/asg/tests/fail/console/mod.rs @@ -27,15 +27,3 @@ fn test_log_parameter_fail_unknown() { let program_string = include_str!("log_parameter_fail_unknown.leo"); 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(); -} diff --git a/ast/src/errors/reducer.rs b/ast/src/errors/reducer.rs index e8b582af85..9655c47bde 100644 --- a/ast/src/errors/reducer.rs +++ b/ast/src/errors/reducer.rs @@ -35,6 +35,13 @@ impl ReducerError { 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 { let message = "Console::Assert cannot be matched here, its handled in another case.".to_string(); diff --git a/ast/src/reducer/canonicalization.rs b/ast/src/reducer/canonicalization.rs index c6355fc7b2..9ca30d2aa2 100644 --- a/ast/src/reducer/canonicalization.rs +++ b/ast/src/reducer/canonicalization.rs @@ -391,23 +391,23 @@ impl Canonicalizer { ConsoleFunction::Assert(expression) => { ConsoleFunction::Assert(self.canonicalize_expression(expression)) } - ConsoleFunction::Debug(format) | ConsoleFunction::Error(format) | ConsoleFunction::Log(format) => { - let parameters = format + ConsoleFunction::Debug(args) | ConsoleFunction::Error(args) | ConsoleFunction::Log(args) => { + let parameters = args .parameters .iter() .map(|parameter| self.canonicalize_expression(parameter)) .collect(); - let formatted = FormatString { - parts: format.parts.clone(), + let console_args = ConsoleArgs { + string: args.string.clone(), parameters, - span: format.span.clone(), + span: args.span.clone(), }; match &console_function_call.function { - ConsoleFunction::Debug(_) => ConsoleFunction::Debug(formatted), - ConsoleFunction::Error(_) => ConsoleFunction::Error(formatted), - ConsoleFunction::Log(_) => ConsoleFunction::Log(formatted), + ConsoleFunction::Debug(_) => ConsoleFunction::Debug(console_args), + ConsoleFunction::Error(_) => ConsoleFunction::Error(console_args), + ConsoleFunction::Log(_) => ConsoleFunction::Log(console_args), _ => unimplemented!(), // impossible } } @@ -493,6 +493,10 @@ impl ReconstructingReducer for Canonicalizer { } fn reduce_string(&mut self, string: &[Char], span: &Span) -> Result { + if string.is_empty() { + return Err(ReducerError::empty_string(span)); + } + let mut elements = Vec::new(); let mut col_adder = 0; for (index, character) in string.iter().enumerate() { diff --git a/ast/src/reducer/reconstructing_director.rs b/ast/src/reducer/reconstructing_director.rs index 74fbf76a94..80a61e0476 100644 --- a/ast/src/reducer/reconstructing_director.rs +++ b/ast/src/reducer/reconstructing_director.rs @@ -390,23 +390,23 @@ impl ReconstructingDirector { ) -> Result { let function = match &console_function_call.function { 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![]; - for parameter in format.parameters.iter() { + for parameter in args.parameters.iter() { parameters.push(self.reduce_expression(parameter)?); } - let formatted = FormatString { - parts: format.parts.clone(), + let formatted = ConsoleArgs { + string: args.string.clone(), parameters, - span: format.span.clone(), + span: args.span.clone(), }; match &console_function_call.function { ConsoleFunction::Debug(_) => ConsoleFunction::Debug(formatted), ConsoleFunction::Error(_) => ConsoleFunction::Error(formatted), ConsoleFunction::Log(_) => ConsoleFunction::Log(formatted), - _ => return Err(ReducerError::impossible_console_assert_call(&format.span)), + _ => return Err(ReducerError::impossible_console_assert_call(&args.span)), } } }; diff --git a/ast/src/statements/console/console_args.rs b/ast/src/statements/console/console_args.rs new file mode 100644 index 0000000000..f5d8977dc3 --- /dev/null +++ b/ast/src/statements/console/console_args.rs @@ -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 . + +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) -> Vec { +// 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, + pub parameters: Vec, + 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::>().join(""), + self.parameters + .iter() + .map(|p| p.to_string()) + .collect::>() + .join(",") + ) + } +} + +impl Node for ConsoleArgs { + fn span(&self) -> &Span { + &self.span + } + + fn set_span(&mut self, span: Span) { + self.span = span; + } +} diff --git a/ast/src/statements/console/console_function.rs b/ast/src/statements/console/console_function.rs index 17a19931cb..5919980af6 100644 --- a/ast/src/statements/console/console_function.rs +++ b/ast/src/statements/console/console_function.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use crate::{Expression, FormatString, Node, Span}; +use crate::{ConsoleArgs, Expression, Node, Span}; use serde::{Deserialize, Serialize}; use std::fmt; @@ -22,9 +22,9 @@ use std::fmt; #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] pub enum ConsoleFunction { Assert(Expression), - Debug(FormatString), - Error(FormatString), - Log(FormatString), + Debug(ConsoleArgs), + Error(ConsoleArgs), + Log(ConsoleArgs), } impl fmt::Display for ConsoleFunction { diff --git a/ast/src/statements/console/formatted_string.rs b/ast/src/statements/console/formatted_string.rs deleted file mode 100644 index 641362b35a..0000000000 --- a/ast/src/statements/console/formatted_string.rs +++ /dev/null @@ -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 . - -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) -> Vec { - 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, - pub parameters: Vec, - 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::>() - .join(""), - self.parameters - .iter() - .map(|p| p.to_string()) - .collect::>() - .join(",") - ) - } -} - -impl Node for FormatString { - fn span(&self) -> &Span { - &self.span - } - - fn set_span(&mut self, span: Span) { - self.span = span; - } -} diff --git a/ast/src/statements/console/mod.rs b/ast/src/statements/console/mod.rs index da4f604aed..16c2d98a40 100644 --- a/ast/src/statements/console/mod.rs +++ b/ast/src/statements/console/mod.rs @@ -17,11 +17,8 @@ pub mod console_function; pub use console_function::*; +pub mod console_args; +pub use console_args::*; + pub mod console_statement; pub use console_statement::*; - -pub mod formatted_container; -pub use formatted_container::*; - -pub mod formatted_string; -pub use formatted_string::*; diff --git a/compiler/src/console/format.rs b/compiler/src/console/format.rs index 7372fa69d2..dd936223fc 100644 --- a/compiler/src/console/format.rs +++ b/compiler/src/console/format.rs @@ -17,8 +17,7 @@ //! Evaluates a formatted string in a compiled Leo program. use crate::{errors::ConsoleError, program::ConstrainedProgram, GroupType}; -use leo_asg::FormatString; -use leo_ast::FormatStringPart; +use leo_asg::{CharValue, ConsoleArgs}; use snarkvm_fields::PrimeField; use snarkvm_r1cs::ConstraintSystem; @@ -26,35 +25,65 @@ impl<'a, F: PrimeField, G: GroupType> ConstrainedProgram<'a, F, G> { pub fn format>( &mut self, cs: &mut CS, - formatted: &FormatString<'a>, + args: &ConsoleArgs<'a>, ) -> Result { - // Check that containers and parameters match - let container_count = formatted - .parts - .iter() - .filter(|x| matches!(x, FormatStringPart::Container)) - .count(); - if container_count != formatted.parameters.len() { - return Err(ConsoleError::length( - container_count, - formatted.parameters.len(), - &formatted.span, - )); - } - - let mut executed_containers = Vec::with_capacity(formatted.parameters.len()); - for parameter in formatted.parameters.iter() { - executed_containers.push(self.enforce_expression(cs, parameter.get())?.to_string()); - } - - let mut out = vec![]; - let mut parameters = executed_containers.iter(); - for part in formatted.parts.iter() { - match part { - FormatStringPart::Const(c) => out.push(c.to_string()), - FormatStringPart::Container => out.push(parameters.next().unwrap().to_string()), + let mut out = Vec::new(); + let mut in_container = false; + let mut substring = String::new(); + let mut arg_index = 0; + let mut escape_right_bracket = false; + for (index, character) in args.string.iter().enumerate() { + match character { + _ if escape_right_bracket => { + escape_right_bracket = false; + continue; + } + CharValue::Scalar(scalar) => match scalar { + '{' if !in_container => { + out.push(substring.clone()); + substring.clear(); + in_container = true; + } + '{' if in_container => { + substring.push('{'); + in_container = false; + } + '}' if in_container => { + in_container = false; + let parameter = match args.parameters.get(arg_index) { + 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("")) } diff --git a/compiler/src/errors/console.rs b/compiler/src/errors/console.rs index 62ee02446a..76d4538f1c 100644 --- a/compiler/src/errors/console.rs +++ b/compiler/src/errors/console.rs @@ -33,6 +33,18 @@ impl ConsoleError { 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 { let message = format!( "Formatter given {} containers and found {} parameters", diff --git a/compiler/src/phases/reducing_director.rs b/compiler/src/phases/reducing_director.rs index 5063774993..ff432e3f27 100644 --- a/compiler/src/phases/reducing_director.rs +++ b/compiler/src/phases/reducing_director.rs @@ -76,12 +76,12 @@ use leo_ast::{ CircuitStaticFunctionAccessExpression, CombinerError, ConditionalStatement as AstConditionalStatement, + ConsoleArgs as AstConsoleArgs, ConsoleFunction as AstConsoleFunction, ConsoleStatement as AstConsoleStatement, DefinitionStatement as AstDefinitionStatement, Expression as AstExpression, ExpressionStatement as AstExpressionStatement, - FormatString, Function as AstFunction, GroupTuple, GroupValue as AstGroupValue, @@ -597,25 +597,27 @@ impl CombineAstAsgDirector { (AstConsoleFunction::Assert(ast_expression), AsgConsoleFunction::Assert(asg_expression)) => { AstConsoleFunction::Assert(self.reduce_expression(&ast_expression, asg_expression.get())?) } - (AstConsoleFunction::Debug(ast_format), AsgConsoleFunction::Debug(asg_format)) - | (AstConsoleFunction::Error(ast_format), AsgConsoleFunction::Error(asg_format)) - | (AstConsoleFunction::Log(ast_format), AsgConsoleFunction::Log(asg_format)) => { + (AstConsoleFunction::Debug(ast_console_args), AsgConsoleFunction::Debug(asg_format)) + | (AstConsoleFunction::Error(ast_console_args), AsgConsoleFunction::Error(asg_format)) + | (AstConsoleFunction::Log(ast_console_args), AsgConsoleFunction::Log(asg_format)) => { 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())?); } - let formatted = FormatString { - parts: ast_format.parts.clone(), + let args = AstConsoleArgs { + string: ast_console_args.string.clone(), parameters, - span: ast_format.span.clone(), + span: ast_console_args.span.clone(), }; match &ast.function { - AstConsoleFunction::Debug(_) => AstConsoleFunction::Debug(formatted), - AstConsoleFunction::Error(_) => AstConsoleFunction::Error(formatted), - AstConsoleFunction::Log(_) => AstConsoleFunction::Log(formatted), - _ => return Err(ReducerError::impossible_console_assert_call(&ast_format.span)), + AstConsoleFunction::Debug(_) => AstConsoleFunction::Debug(args), + AstConsoleFunction::Error(_) => AstConsoleFunction::Error(args), + AstConsoleFunction::Log(_) => AstConsoleFunction::Log(args), + _ => return Err(ReducerError::impossible_console_assert_call(&ast_console_args.span)), } } _ => ast.function.clone(), diff --git a/grammar/README.md b/grammar/README.md index 0086f21350eee12c1de36659187a45de949035e4..6491e949ff2279c879705eef6f63c35cd056f658 100644 GIT binary patch literal 111950 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�r3gp1 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 PtMtv1VYag>?>YE?(oy0} literal 54154 zcmchAX>%M$lI8dM71>zR2%t~^51l+T1G32$$GDV?giPB@$yAA|1dz=-(3MpLq3-_g z`<{E8o{=CZ^=#XWh|0J;+&%mXkBBF$i>q~ecD|{0pX^n4Z{PW5eE0UkrlId4za_4?{yglgFrRz6)fP1UVV zH<$IgxnEtaw$-#=R_kWgcAIs3vfZH1O;s;vhpTn9Sk2ngs}VkIm$POqLu{Hgz;NUJ z<>imnUz%mJuIJSc+mm@atzNX#X4y4W4aoSVJ8x#y$<>J9J>%%F-RRY`6?&~V?P|GS zH7&~5)w^ch;dgb<58!&+uU6~PZoMJE>uPnuMtgwrs+!jT$hRxcIZka>wM&`A`Rby< zALtNsyKLw4>ZGZ*U30pf?~j0l>Zj+&-+lk%adq_auhmaSuU;L!JpStg)ZCn}P};m} zw4ZiyF>f(HU~^qBH&+0A^uM3Jdh#9WA3c8l;`#Ak0eSW8`SHuAuU}WszJFC6RX-fP zI)47-#}`MhsvmxQ^~3kCpB_}Nff!(5bPYmJg&^yunl+ocop+e*UqQYO5Y1=RdHt>d z8K+JA4)D~~6wKt2INa%|p0Ac?GB3eP%0Jcf(`vce>{lINdw9OtTy*yj4=*n-56+g` zgVp-%5acGQyTfk*%V_NWhd-Y$H|y1GJ0+RIcNUW;J?l-J|W< zqFHVL|Koc4cH50#)}Z#Y)q1g4ovrJ|q6Y0x$>awk);L>}gDUWgwC7q_uimvYbf9b@ z)OOZ_`r zcPq!5vud-d&Sb4&jiRrP3$p1pn85~1dV!CtcDcbke*!Sz6KTBdILwAvcQYSxWjdKr z17ffp{?kq|eX!V~p0~frqVyO@LEIl*p10HU>QYCTVIj;{7aYMYtfMC{?vj&M>t-R; zXx1YlCN#U4WBQnhlMNR3=SzXDoo?s#dY|J6V`FKOsTIEY71w)v%7y3f0Im=ga8v9= zR(O^@!>Zk$9022o^>lt$|FeN8I$YEcs_Vm()#`0`_!hWaH}k{#WO@1k3j>%Z=VLM$ zkwb2wd{&D+)CyKb{-?tFc4F+1HGajqm*MgyP=-~EI@ z_=|PZf%SWc$0C`o8xUOiP;iNs??5GpPA9_%qG#(4U35TAS52EW*B2zgvV(93UtN(R zn8^mkX2tfGQ?_n(X^%#Joq>i}>wPbPOf4 zS*=BMw9C`gA-=JyOp5Kk`uz46H*CkMvP!_$!W=+KchO9tu7qN7+5Y$I?_cg$-yI+S zurEs#D;Ko%Kobu#sd_d8%2rEA_LfVKa)Ij^RIa+YXl2ocSSJ(@9qDSNRJ98+wud$1 z)WjLq;~AFk)e2ov+%@kvW6{8#Pci!QC1_Jaik((y{VB&in>So0%VhYA#ZQQbSZdvN#L$^K~Dsa)+K&ASa2NwT z_wWmUbCAg=?+^IlXaX5Tjl?mPr(H)PnEf=WH+S}{d;8U$+rK;jI5&U?hRRta`6uu1 z-5%fjX5t1S`(0Jk8DJM^K@WU3=IG6@%U^#{tKsvntA~#&{N_Jjd{ymIo4crrJRg5%|%!^UHTaUoR$*Y4A^HC z*d3N|gVuL$eg56k|MTbPe~DyVEy2GlC~p{Z6ITeDPj1~qwdcpN;`V~|Hs|ybCR(Oq zirs$yl4 z98nh{b|;4u0^BuF@Knb290UWtA%OwQs{*COm;qeV2K+X~3}DDqf7`y_)E6s?ZU}5| zVil`mM06}xuTu~->sCY@?9MZ;UT6|@x`q`jLZgP>XlPZVZ`w<7?DcjY#lWrIpI-k^ z9ac|Xy?9pP&%H<$zZ%r(^KxgjI-zxeE>Et=$PK2wJln5+Yu2kkm`w|10(7f$M`2l4 z6ZAe|4G~uO%^@e?4)V8l=PDhHUjKbd77kMtqb!>*eKjg+&UN7&L&#u9;Wsn>kbLP^rAnmm4fTs^9GZts9tJ9l;_qyD@?l^m3!`2@{AT0NB)~qNNSF!Aj6E%b==17qx(n43~K$aDP;vVo3=2 zZY*M{gTn!aoC-%tr=&7XLb@MzR4>5bIEb5+LeN#ves@)IhM2(sFnf%fggFcM6MUID zmIcie1OcdXRC7=cV?jB#1Z?zuXo+9cSh>6Mk1n|L)w=zS-t4@BbnjpR&apD8W;v5J zxjRL4pj8JCCI(EFl-Z@~Y17Quj@W^pLeA+hM6-Z4fQK%EIPDX2$sF$2n>D=c?YfCp zyK}W=dfqv32BEIx%N0i0o{aarF{HSb9$28hrnEO-ROh$uvlHWCyAAI|

#5Xd;< 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 diff --git a/grammar/abnf-grammar.txt b/grammar/abnf-grammar.txt index 29e925c70a..df8c9c8338 100644 --- a/grammar/abnf-grammar.txt +++ b/grammar/abnf-grammar.txt @@ -559,7 +559,7 @@ unicode-character-escape = %s"\u{" 1*6hexadecimal-digit "}" ; because string literals denote character arrays, ; 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 / simple-character-escape diff --git a/parser/README.md b/parser/README.md new file mode 100644 index 0000000000..c0ff343922 --- /dev/null +++ b/parser/README.md @@ -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) diff --git a/parser/src/parser/statement.rs b/parser/src/parser/statement.rs index eff1b31e31..f4ec7ea566 100644 --- a/parser/src/parser/statement.rs +++ b/parser/src/parser/statement.rs @@ -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 { + pub fn parse_console_args(&mut self) -> SyntaxResult { let start_span; let string = match self.expect_any()? { SpannedToken { @@ -247,7 +247,7 @@ impl ParserContext { 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(); while self.eat(Token::Comma).is_some() { @@ -255,8 +255,8 @@ impl ParserContext { parameters.push(param); } - Ok(FormatString { - parts, + Ok(ConsoleArgs { + string, span: &start_span + parameters.last().map(|x| x.span()).unwrap_or(&start_span), parameters, }) @@ -275,9 +275,9 @@ impl ParserContext { let expr = self.parse_expression()?; ConsoleFunction::Assert(expr) } - "debug" => ConsoleFunction::Debug(self.parse_formatted_string()?), - "error" => ConsoleFunction::Error(self.parse_formatted_string()?), - "log" => ConsoleFunction::Log(self.parse_formatted_string()?), + "debug" => ConsoleFunction::Debug(self.parse_console_args()?), + "error" => ConsoleFunction::Error(self.parse_console_args()?), + "log" => ConsoleFunction::Log(self.parse_console_args()?), x => { return Err(SyntaxError::unexpected_ident( &x, diff --git a/parser/src/tokenizer/lexer.rs b/parser/src/tokenizer/lexer.rs index 9c3b9f2be0..cf9b452e9b 100644 --- a/parser/src/tokenizer/lexer.rs +++ b/parser/src/tokenizer/lexer.rs @@ -280,7 +280,7 @@ impl Token { } } - if i == input.len() || i == 1 || !end { + if i == input.len() || !end { return (0, None); } diff --git a/tests/expectations/compiler/compiler/console/log_parameter_fail_empty.leo.out b/tests/expectations/compiler/compiler/console/log_parameter_fail_empty.leo.out index b3e4e8bbfe..6ebd98b417 100644 --- a/tests/expectations/compiler/compiler/console/log_parameter_fail_empty.leo.out +++ b/tests/expectations/compiler/compiler/console/log_parameter_fail_empty.leo.out @@ -2,4 +2,4 @@ namespace: Compile expectation: Fail 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" diff --git a/tests/expectations/compiler/compiler/console/log_parameter_fail_none.leo.out b/tests/expectations/compiler/compiler/console/log_parameter_fail_none.leo.out index 56c7a9e3b8..f7007ccf5f 100644 --- a/tests/expectations/compiler/compiler/console/log_parameter_fail_none.leo.out +++ b/tests/expectations/compiler/compiler/console/log_parameter_fail_none.leo.out @@ -2,4 +2,4 @@ namespace: Compile expectation: Fail 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" diff --git a/tests/expectations/parser/parser/expression/literal/string.leo.out b/tests/expectations/parser/parser/expression/literal/string.leo.out index 2eab41078d..c6b41bc0d5 100644 --- a/tests/expectations/parser/parser/expression/literal/string.leo.out +++ b/tests/expectations/parser/parser/expression/literal/string.leo.out @@ -2,6 +2,7 @@ namespace: Token expectation: Pass outputs: + - "'\"\"' @ 1:1-3" - "'\"string\"' @ 1:1-9" - "'\"another { } string\"' @ 1:1-21" - "'\"{ ] [ ; a\"' @ 1:1-12" diff --git a/tests/expectations/parser/parser/expression/literal/string_fail.leo.out b/tests/expectations/parser/parser/expression/literal/string_fail.leo.out index d97d2cda74..891ec74b3c 100644 --- a/tests/expectations/parser/parser/expression/literal/string_fail.leo.out +++ b/tests/expectations/parser/parser/expression/literal/string_fail.leo.out @@ -2,7 +2,6 @@ namespace: Token expectation: Fail 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 | \"\\\"\n | ^\n |\n = unexpected token: '\"'" - " --> test:1:1\n |\n 1 | \"\\l\"\n | ^\n |\n = unexpected token: '\"'" diff --git a/tests/expectations/parser/parser/statement/console.leo.out b/tests/expectations/parser/parser/statement/console.leo.out index a70bf45485..2ed38532c9 100644 --- a/tests/expectations/parser/parser/statement/console.leo.out +++ b/tests/expectations/parser/parser/statement/console.leo.out @@ -16,9 +16,9 @@ outputs: - Console: function: Error: - parts: - - Const: "" - - Container + string: + - Scalar: 123 + - Scalar: 125 parameters: - Identifier: "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":21,\\\"col_stop\\\":22,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.error(\\\\\\\"{}\\\\\\\", x);\\\"}\"}" span: @@ -38,11 +38,11 @@ outputs: - Console: function: Error: - parts: - - Const: "" - - Container - - Const: "" - - Container + string: + - Scalar: 123 + - Scalar: 125 + - Scalar: 123 + - Scalar: 125 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\":\"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: function: Error: - parts: - - Const: x + string: + - Scalar: 120 parameters: [] span: line_start: 1 @@ -83,9 +83,9 @@ outputs: - Console: function: Debug: - parts: - - Const: "" - - Container + string: + - Scalar: 123 + - Scalar: 125 parameters: - Identifier: "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":21,\\\"col_stop\\\":22,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.debug(\\\\\\\"{}\\\\\\\", x);\\\"}\"}" span: @@ -105,11 +105,11 @@ outputs: - Console: function: Debug: - parts: - - Const: "" - - Container - - Const: "" - - Container + string: + - Scalar: 123 + - Scalar: 125 + - Scalar: 123 + - Scalar: 125 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\":\"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: function: Debug: - parts: - - Const: x + string: + - Scalar: 120 parameters: [] span: line_start: 1 @@ -150,9 +150,9 @@ outputs: - Console: function: Log: - parts: - - Const: "" - - Container + string: + - Scalar: 123 + - Scalar: 125 parameters: - Identifier: "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":19,\\\"col_stop\\\":20,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.log(\\\\\\\"{}\\\\\\\", x);\\\"}\"}" span: @@ -172,11 +172,11 @@ outputs: - Console: function: Log: - parts: - - Const: "" - - Container - - Const: "" - - Container + string: + - Scalar: 123 + - Scalar: 125 + - Scalar: 123 + - Scalar: 125 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\":\"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: function: Log: - parts: - - Const: x + string: + - Scalar: 120 parameters: [] span: line_start: 1 diff --git a/tests/parser/expression/literal/string.leo b/tests/parser/expression/literal/string.leo index 1b397dfa83..644ad63f3c 100644 --- a/tests/parser/expression/literal/string.leo +++ b/tests/parser/expression/literal/string.leo @@ -3,6 +3,8 @@ namespace: Token expectation: Pass */ +"" + "string" "another { } string" diff --git a/tests/parser/expression/literal/string_fail.leo b/tests/parser/expression/literal/string_fail.leo index 3015bf53c3..de90e88599 100644 --- a/tests/parser/expression/literal/string_fail.leo +++ b/tests/parser/expression/literal/string_fail.leo @@ -3,8 +3,6 @@ namespace: Token expectation: Fail */ -"" - "Hello world! "\" From 0ddfddfdc5a1818e458c1bd57d67af861d1b4ed6 Mon Sep 17 00:00:00 2001 From: gluaxspeed Date: Wed, 21 Jul 2021 01:31:20 -0700 Subject: [PATCH 2/6] parser readme, started ast readme --- ast/README.md | 193 +++++++++++++++++++++++++++++++++- parser/README.md | 123 +++++++++++++++++++++- parser/src/tokenizer/token.rs | 3 +- 3 files changed, 314 insertions(+), 5 deletions(-) diff --git a/ast/README.md b/ast/README.md index 92305bee12..b0740646d2 100644 --- a/ast/README.md +++ b/ast/README.md @@ -1,5 +1,196 @@ # leo-ast - [![Crates.io](https://img.shields.io/crates/v/leo-ast.svg?color=neon)](https://crates.io/crates/leo-ast) [![Authors](https://img.shields.io/badge/authors-Aleo-orange.svg)](../AUTHORS) [![License](https://img.shields.io/badge/License-GPLv3-blue.svg)](./LICENSE.md) + +This directory contains the code for the AST of a Leo Program. + +## Node Types + +There are several types of nodes in the AST that then have further breakdowns. + +All nodes store a Span, which is useful for tracking the lines and +columns of where the node was taken from in the Leo Program. + +### [Program/File](./src/program.rs) + +The top level nodes in a Leo Program. + +#### [Imports](./src/imports/import.rs) + +Represents an import statement in a Leo Program. +A list of these are stored on the Program. +It stores the path to an import and what is being imported. + +**NOTE**: The import does not contain the source code of the imported Leo Program. + +#### [Circuits](./src/circuits/circuit.rs) + +A circuit node represents a defined Circuit in a Leo Program. +A order preserving map of these are stored on the Program. +Contains the Circuit's name, as well as it's members. +The members are a function, or a variable. +For both of them the Circuit preserves their names. + +#### [Decorators](./src/annotation.rs) + +An annotation node is a decorator that can be applied to a function. +Stored on the function themselves despite being a top-level node. +The node stores the name of the annotation, as well as any args passed to it. + +#### [Functions](./src/functions/function.rs) + +A function node represents a defined function in a Leo Program. +A order preserving map of these are stored on the Program. +A function node stores the following information: + +- The annotations applied to the function. +- An identifier the name of the function. +- The inputs to the function, both their names and types. +- The output of the function as a type if it exists. +- The function body stored as a block statement. + +#### [Global Consts](./src/program.rs) + +A global const is a bit special and has no special node for itself, but rather is a definition statement. +A order preserving map of these are stored on the Program. + +### [Types](./src/types/type_.rs) + +The different types in a Leo Program. +Types themselves are not a node, but rather just information to be stored on a node. + +#### Address + +The address type follows the [BIP_0173](https://en.bitcoin.it/wiki/BIP_0173) format starting with `aleo1`. + +#### Boolean + +The boolean type consists of two values **true** and **false**. + +#### Char + +The char type resents a character from the inclusive range [0, 10FFFF]. + +#### Field + +The field type an unsigned number up to the modulus length of the field. + +#### Group + +The group type a set of affine points on the elliptic curve passed. + +#### IntegerType + +The integer type represents a range of integer types. + +##### U8 + +A integer in the inclusive range [0, 255]. + +##### U16 + +A integer in the inclusive range [0, 65535]. + +##### U32 + +A integer in the inclusive range [0, 4294967295]. + +##### U64 + +A integer in the inclusive range [0, 18446744073709551615]. + +##### U128 + +A integer in the inclusive range [0, 340282366920938463463374607431768211455]. + +##### I8 + +A integer in the inclusive range [-128, 127]. + +##### I16 + +A integer in the inclusive range [-32768, 32767]. + +##### I32 + +A integer in the inclusive range [-2147483648, 2147483647]. + +##### I64 + +A integer in the inclusive range [-9223372036854775808, 9223372036854775807]. + +##### I128 + +A integer in the inclusive range [-170141183460469231731687303715884105728, 170141183460469231731687303715884105727]. + +#### Array + +The array type contains another type, then the number of elements of that type greater than 0. + +#### Tuple + +The tuple type contains n types, where n is greater than or equal to 0. + +#### Circuit + +The circuit type, every circuit represents a different type. + +#### SelfType + +The self type represented by `Self` and only usable inside a circuit. + +### Statements + +The statement level nodes in a Leo Program. + +#### Assignment Statements + +#### Block Statements + +#### Conditional Statements + +#### Console Statements + +#### Definition Statements + +#### Expression Statements + +#### Iteration Statements + +#### Return Statements + +### Expressions + +The expression nodes in a Leo Program. + +#### ArrayAccess Expressions + +#### ArrayInit Expressions + +#### ArrayInline Expressions + +#### ArrayRangeAccess Expressions + +#### Binary Expressions + +#### Call Expressions + +#### CircuitInit Expressions + +#### CircuitMemberAccess Expressions + +#### CircuitStaticFunctionAccess Expressions + +#### Identifier Expressions + +#### Ternary Expressions + +#### TupleAccess Expressions + +#### TupleInit Expressions + +#### Unary Expressions + +#### Value Expressions + diff --git a/parser/README.md b/parser/README.md index c0ff343922..c09aff1736 100644 --- a/parser/README.md +++ b/parser/README.md @@ -1,5 +1,124 @@ -# leo-parser - +# 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) + +This directory contains the code to tokenize, lex and parse Leo files to the [Leo AST](./../ast/README.md). + +## Tokenizer + +The tokenizer contains all tokens in Leo. +It also decides which tokens are keywords. +Meaning that keywords are a subset of tokens. +The lexer goes through character by character as bytes, and converts the bytes into the tokens. + +### Tokens + +Bolded ones are also keywords. + +#### Literals +- CommentLine +- CommentBlock +- StringLit +- Ident +- Int +- **True** +- **False** +- AddressLit +- CharLit + +#### Symbols +- At +- Not +- And +- Or +- Eq +- NotEq +- Lt +- LtEq +- Gt +- GtEq +- Add +- Minus +- Mul +- Div +- Exp +- Assign +- AddEq +- MinusEq +- MulEq +- DivEq +- ExpEq +- LeftParen +- RightParen +- LeftSquare +- RightSquare +- LeftCurly +- RightCurly +- Comma +- Dot +- DotDot +- DotDotDot +- Semicolon +- Colon +- DoubleColon +- Question +- Arrow +- Underscore + +#### Types +- **U8** +- **U16** +- **U32** +- **U64** +- **U128** +- **I8** +- **I16** +- **I32** +- **I64** +- **I128** +- **Field** +- **Group** +- **Bool** +- **Address** +- **Char** +- **BigSelf** + +#### Words +- **Input** +- **LittleSelf** +- **Import** +- **As** +- **Circuit** +- **Console** +- **Const** +- **Else** +- **For** +- **Function** +- **If** +- **In** +- **Let** +- **Mut** +- **Return** +- **Static** +- **String** + +#### Meta +- Eof + +## Parser + +The parser converts the tokens to the [Leo AST](./../ast/README.md). + +The parser is broken down to different files that correspond to different aspects of the AST: + +- [File](./src/parser/file.rs) - Parses the top level nodes in Leo. +- [Types](./src/parser/type_.rs) - Parses the type declarations in Leo. +- [Statements](./src/parser/statement.rs) - Parses the different kinds of statements. +- [Expressions](./src/parser/expression.rs) - Parses the different kinds of expressions. + + For more information on those please read the Leo AST README, linked above. + +## Grammar Relation + +All function and token names are as close as possible to the [Leo Grammar](./../grammar/README.md) diff --git a/parser/src/tokenizer/token.rs b/parser/src/tokenizer/token.rs index 146393588f..700da12e9e 100644 --- a/parser/src/tokenizer/token.rs +++ b/parser/src/tokenizer/token.rs @@ -58,9 +58,8 @@ pub enum Token { AddressLit(#[serde(with = "leo_ast::common::tendril_json")] StrTendril), CharLit(Char), - At, - // Symbols + At, Not, And, Or, From aa1819c3686a217fcaee4af2e742cb3c674e1503 Mon Sep 17 00:00:00 2001 From: gluaxspeed Date: Wed, 21 Jul 2021 02:47:54 -0700 Subject: [PATCH 3/6] finish up ast readme, clean up old files and comments --- asg/src/statement/console.rs | 14 -- ast/README.md | 197 +++++++++++++++--- ast/src/statements/console/console_args.rs | 44 ---- .../statements/console/formatted_container.rs | 41 ---- 4 files changed, 172 insertions(+), 124 deletions(-) delete mode 100644 ast/src/statements/console/formatted_container.rs diff --git a/asg/src/statement/console.rs b/asg/src/statement/console.rs index 59992f85a2..37359f90b3 100644 --- a/asg/src/statement/console.rs +++ b/asg/src/statement/console.rs @@ -54,20 +54,6 @@ impl<'a> FromAst<'a, leo_ast::ConsoleArgs> for ConsoleArgs<'a> { value: &leo_ast::ConsoleArgs, _expected_type: Option>, ) -> Result { - // let expected_param_len = value - // .parts - // .iter() - // .filter(|x| matches!(x, FormatStringPart::Container)) - // .count(); - // if value.parameters.len() != expected_param_len { - // + 1 for formatting string as to not confuse user - // return Err(AsgConvertError::unexpected_call_argument_count( - // expected_param_len + 1, - // value.parameters.len() + 1, - // &value.span, - // )); - // } - let mut parameters = vec![]; for parameter in value.parameters.iter() { parameters.push(Cell::new(<&Expression<'a>>::from_ast(scope, parameter, None)?)); diff --git a/ast/README.md b/ast/README.md index b0740646d2..1ec612648f 100644 --- a/ast/README.md +++ b/ast/README.md @@ -80,7 +80,7 @@ The field type an unsigned number up to the modulus length of the field. The group type a set of affine points on the elliptic curve passed. -#### IntegerType +#### [IntegerType](./src/types/integer_type.rs) The integer type represents a range of integer types. @@ -140,57 +140,204 @@ The circuit type, every circuit represents a different type. The self type represented by `Self` and only usable inside a circuit. -### Statements +### [Statements](./src/statements/statement.rs) The statement level nodes in a Leo Program. -#### Assignment Statements +#### [Assignment Statements](./src/statements/assign/) -#### Block Statements +An assignment statement node stores the following: -#### Conditional Statements +- The operation. + - **=** + - **+=** + - **-=** + - **=** + - **/=** + - **=** + - **&&=** + - **||=** +- The assignee which is a variable that has context of any access expressions on it. +- The value which is an expression. -#### Console Statements +#### [Block Statements](./src/statements/block.rs) -#### Definition Statements +A block statement node stores the following: -#### Expression Statements +- The list of statements inside the block. -#### Iteration Statements +#### [Conditional Statements](./src/statements/conditional.rs) -#### Return Statements +A conditional statement node stores the following: + +- The condition which is an expression. +- The block statement. +- The next block of the conditional if it exists. + +#### [Console Statements](./src/statements/) + +A console statement node stores the following: + +- The console function being called which stores the type of console function it is and its arguments. + +#### [Definition Statements](./src/statements/definition/mod.rs) + +A definition statement node stores the following: + +- The declaration type: + - `let` for mutable definitions. + - `const` for cosntant definitions. +- The names of the varaibles defined. +- The optional type. +- The values to be assigned to the varaibles. + +#### [Expression Statements](./src/statements/expression.rs) + +An expression statement node stores the following: + +- The expression. + +#### [Iteration Statements](./src/statements/iteration.rs) + +A iteration statement node stores the following: + +- The loop iterator variable name. +- The expression to define the starting loop value. +- The expression to define the stoping loop value. +- The block to run for the loop. + +#### [Return Statements](./src/statements/return_statement.rs) + +A return statement node stores the following: + +- The expression that is being returned. ### Expressions The expression nodes in a Leo Program. -#### ArrayAccess Expressions +#### [ArrayAccess Expressions](./src/expression/array_acces.rs) -#### ArrayInit Expressions +An array access expression node stores the following: -#### ArrayInline Expressions +- The array expression. +- The index represented by an expression. -#### ArrayRangeAccess Expressions +#### [ArrayInit Expressions](./src/expression/array_init.rs) -#### Binary Expressions +An array init expression node stores the following: -#### Call Expressions +- The element expression to fill the array with. +- The dimensions of the array to build. -#### CircuitInit Expressions +#### [ArrayInline Expressions](./src/expression/array_inline.rs) -#### CircuitMemberAccess Expressions +An array inline expression node stores the following: -#### CircuitStaticFunctionAccess Expressions +- The elments of an array which is either an spread or an expression. -#### Identifier Expressions +#### [ArrayRangeAccess Expressions](./src/expression/array_range_access.rs) -#### Ternary Expressions +An array range access expression node stores the following: -#### TupleAccess Expressions +- The array expression. +- The optional left side of the range of the array bounds to access. +- The optional right side of the range of the array bounds to access. -#### TupleInit Expressions +#### [Binary Expressions](./src/expression/binary.rs) -#### Unary Expressions +A binary expression node stores the following: -#### Value Expressions +- The left side of the expression. +- The right side of the expression. +- The binary operation of the expression: + - **+** + - **-** + - **\*** + - **/** + - **\*\*** + - **||** + - **&&** + - **==** + - **!=** + - **>=** + - **>** + - **<=** + - **<** +#### [Call Expressions](./src/expression/call.rs) + +A call expression node stores the following: + +- The function expression being called. +- The aruments a list of expressions. + +#### [CircuitInit Expressions](./src/expression/circuit_init.rs) + +A circuit init expression node stores the following: + +- The name of the circuit expression being initialized. +- The aruments a list of expressions. + +#### [CircuitMemberAccess Expressions](./src/expression/circuit_member_access.rs) + +A circuit member access expression node stores the following: + +- The circut expression being accessed. +- The name of the expression being accessed from the circuit. + +#### [CircuitStaticFunctionAccess Expressions](./src/expression/circuit_static_function_access.rs) + +A circuit static function access expression node stores the following: + +- The circut expression being accessed. +- The name of the expression being statically accessed from the circuit. + +#### [Identifier Expressions](./src/common/identifier.rs) + +An identifer expression node stores the following: + +- An identifier stores the string name. + +#### [Ternary Expressions](./src/expression/ternary.rs) + +A ternary expression node stores the following: + +- The condition of the ternary stored as an expression. +- The expression returned if the condition is true. +- The expression returned if the condition is false. + +#### [TupleAccess Expressions](./src/expression/tuple_access.rs) + +A tuple access expression node stores the following: + +- The tuple expression being accessed. +- The index a positive number greater than or equal to 0. + +#### [TupleInit Expressions](./src/expression/tuple_init.rs) + +A tuple init expression node stores the following: + +- The element expressions to fill the tuple with. + +#### [Unary Expressions](./src/expression/unary.rs) + +An unary expression node stores the following: + +- The inner expression. +- The unary operator: + - **!** + - **-** + +#### [Value Expressions](./src/expression/value.rs) + +A value expression node stores one of the following: + +- Address and its value and span. +- Boolean and its value and span. +- Char and its value and span. +- Field and its value and span. +- Group and its value and span. +- Implicit and its value and span. +- Integer and its value and span. +- String and its value and span. diff --git a/ast/src/statements/console/console_args.rs b/ast/src/statements/console/console_args.rs index f5d8977dc3..ebe6e12725 100644 --- a/ast/src/statements/console/console_args.rs +++ b/ast/src/statements/console/console_args.rs @@ -18,50 +18,6 @@ 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) -> Vec { -// 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 { diff --git a/ast/src/statements/console/formatted_container.rs b/ast/src/statements/console/formatted_container.rs deleted file mode 100644 index cca1a17220..0000000000 --- a/ast/src/statements/console/formatted_container.rs +++ /dev/null @@ -1,41 +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 . - -use crate::{Node, Span}; - -use serde::{Deserialize, Serialize}; -use std::fmt; - -#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] -pub struct FormattedContainer { - pub span: Span, -} - -impl fmt::Display for FormattedContainer { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{{}}") - } -} - -impl Node for FormattedContainer { - fn span(&self) -> &Span { - &self.span - } - - fn set_span(&mut self, span: Span) { - self.span = span; - } -} From dd67004af76b7b889613541fa687386649a95877 Mon Sep 17 00:00:00 2001 From: gluaxspeed Date: Wed, 21 Jul 2021 14:42:25 -0700 Subject: [PATCH 4/6] remove old abnf comments, format abnf --- FORMAT_ABNF_GRAMMER.md | 0 grammar/.gitattributes | 3 +- grammar/FORMAT_ABNF_GRAMMER.md | Bin 0 -> 17082 bytes grammar/abnf-grammar.txt | 3 - grammar/format-abnf-grammar.txt | 179 ++++++++++++++++++++++++++++++++ grammar/src/main.rs | 14 ++- 6 files changed, 191 insertions(+), 8 deletions(-) create mode 100644 FORMAT_ABNF_GRAMMER.md create mode 100644 grammar/FORMAT_ABNF_GRAMMER.md create mode 100644 grammar/format-abnf-grammar.txt diff --git a/FORMAT_ABNF_GRAMMER.md b/FORMAT_ABNF_GRAMMER.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/grammar/.gitattributes b/grammar/.gitattributes index 0d7e2876d4..1e3409b350 100644 --- a/grammar/.gitattributes +++ b/grammar/.gitattributes @@ -1 +1,2 @@ -abnf-grammar.txt text eol=crlf \ No newline at end of file +abnf-grammar.txt text eol=crlf +format-abnf-grammar.txt text eol=crlf \ No newline at end of file diff --git a/grammar/FORMAT_ABNF_GRAMMER.md b/grammar/FORMAT_ABNF_GRAMMER.md new file mode 100644 index 0000000000000000000000000000000000000000..3b95e051779bd90ef0bbcd7c3864daadafe6610e GIT binary patch literal 17082 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 literal 0 HcmV?d00001 diff --git a/grammar/abnf-grammar.txt b/grammar/abnf-grammar.txt index df8c9c8338..650a035744 100644 --- a/grammar/abnf-grammar.txt +++ b/grammar/abnf-grammar.txt @@ -555,9 +555,6 @@ unicode-character-escape = %s"\u{" 1*6hexadecimal-digit "}" ; A string literal consists of one or more elements surrounded by double quotes. ; Each element is any character other than double quote or backslash, ; or an escape among the same ones used for elements of character literals. -; There must be at least one element -; because string literals denote character arrays, -; and arrays must not be empty. string-literal = double-quote *string-literal-element double-quote diff --git a/grammar/format-abnf-grammar.txt b/grammar/format-abnf-grammar.txt new file mode 100644 index 0000000000..0fa049a228 --- /dev/null +++ b/grammar/format-abnf-grammar.txt @@ -0,0 +1,179 @@ +; 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 . + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Introduction +; ------------ + +; This file contains an ABNF (Augmented Backus-Naur Form) grammar of Leo string formatting. +; Background on ABNF is provided later in this file. + +; This grammar provides an official definition of how format strings +; are parsed for printed by the Leo compiler. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Background on ABNF +; ------------------ + +; ABNF is an Internet standard: +; see RFC 5234 at https://www.rfc-editor.org/info/rfc5234 +; and RFC 7405 at https://www.rfc-editor.org/info/rfc7405. +; It is used to specify the syntax of JSON, HTTP, and other standards. + +; ABNF adds conveniences and makes slight modifications +; to Backus-Naur Form (BNF), +; without going beyond context-free grammars. + +; Instead of BNF's angle-bracket notation for nonterminals, +; ABNF uses case-insensitive names consisting of letters, digits, and dashes, +; e.g. `HTTP-message` and `IPv6address`. +; ABNF includes an angle-bracket notation for prose descriptions, +; e.g. ``, +; usable as last resort in the definiens of a nonterminal. + +; While BNF allows arbitrary terminals, +; ABNF uses only natural numbers as terminals, +; and denotes them via: +; (i) binary, decimal, or hexadecimal sequences, +; e.g. `%b1.11.1010`, `%d1.3.10`, and `%x.1.3.A` +; all denote the sequence of terminals [1, 3, 10]; +; (ii) binary, decimal, or hexadecimal ranges, +; e.g. `%x30-39` denotes any singleton sequence of terminals +; [_n_] with 48 <= _n_ <= 57 (an ASCII digit); +; (iii) case-sensitive ASCII strings, +; e.g. `%s"Ab"` denotes the sequence of terminals [65, 98]; +; and (iv) case-insensitive ASCII strings, +; e.g. `%i"ab"`, or just `"ab"`, denotes +; any sequence of terminals among +; [65, 66], +; [65, 98], +; [97, 66], and +; [97, 98]. +; ABNF terminals in suitable sets represent ASCII or Unicode characters. + +; ABNF allows repetition prefixes `n*m`, +; where `n` and `m` are natural numbers in decimal notation; +; if absent, +; `n` defaults to 0, and +; `m` defaults to infinity. +; For example, +; `1*4HEXDIG` denotes one to four `HEXDIG`s, +; `*3DIGIT` denotes up to three `DIGIT`s, and +; `1*OCTET` denotes one or more `OCTET`s. +; A single `n` prefix +; abbreviates `n*n`, +; e.g. `3DIGIT` denotes three `DIGIT`s. + +; Instead of BNF's `|`, ABNF uses `/` to separate alternatives. +; Repetition prefixes have precedence over juxtapositions, +; which have precedence over `/`. +; Round brackets group things and override the aforementioned precedence rules, +; e.g. `*(WSP / CRLF WSP)` denotes sequences of terminals +; obtained by repeating, zero or more times, +; either (i) a `WSP` or (ii) a `CRLF` followed by a `WSP`. +; Square brackets also group things but make them optional, +; e.g. `[":" port]` is equivalent to `0*1(":" port)`. + +; Instead of BNF's `::=`, ABNF uses `=` to define nonterminals, +; and `=/` to incrementally add alternatives +; to previously defined nonterminals. +; For example, the rule `BIT = "0" / "1"` +; is equivalent to `BIT = "0"` followed by `BIT =/ "1"`. + +; The syntax of ABNF itself is formally specified in ABNF +; (in Section 4 of the aforementioned RFC 5234, +; after the syntax and semantics of ABNF +; are informally specified in natural language +; (in Sections 1, 2, and 3 of the aforementioned RFC 5234). +; The syntax rules of ABNF prescribe the ASCII codes allowed for +; white space (spaces and horizontal tabs), +; line endings (carriage returns followed by line feeds), +; and comments (semicolons to line endings). + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Structure +; --------- + +; This ABNF grammar consists of one grammar: +; that describes how a Leo string-literal is parsed +; for formatting. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Format String +; ------------------- + +not-double-quote-or-backslash-or-brace = %x0-21 + / %x23-5B + / %x5D-7A + / %x7C + / %x7E-10FFFF + ; anything but " or \ or { or } + +double-quote = %x22 ; " + +single-quote = %x27 ; ' + +single-quote-escape = "\" single-quote ; \' + +double-quote-escape = "\" double-quote ; \" + +backslash-escape = "\\" + +line-feed-escape = %s"\n" + +carriage-return-escape = %s"\r" + +horizontal-tab-escape = %s"\t" + +null-character-escape = "\0" + +simple-character-escape = single-quote-escape + / double-quote-escape + / backslash-escape + / line-feed-escape + / carriage-return-escape + / horizontal-tab-escape + / null-character-escape + +octal-digit = %x30-37 ; 0-7 + +hexadecimal-digit = digit / "a" / "b" / "c" / "d" / "e" / "f" + +ascii-character-escape = %s"\x" octal-digit hexadecimal-digit + +unicode-character-escape = %s"\u{" 1*6hexadecimal-digit "}" + +format-string-element-not-brace = not-double-quote-or-backslash-or-brace + / simple-character-escape + / ascii-character-escape + / unicode-character-escape + +format-string-container = "{}" + +format-string-open-brace = "{{" + +format-string-close-brace = "}}" + +format-string-element = format-string-element-not-brace + / format-string-container + / format-string-open-brace + / format-string-close-brace + +format-string = double-quote *format-string-element double-quote diff --git a/grammar/src/main.rs b/grammar/src/main.rs index 8ef25c11a5..28b9f6da74 100644 --- a/grammar/src/main.rs +++ b/grammar/src/main.rs @@ -40,7 +40,7 @@ // use abnf::types::{Node, Rule}; -use anyhow::Result; +use anyhow::{anyhow, Result}; use std::collections::{HashMap, HashSet}; /// Processor's scope. Used when code block or definition starts or ends. @@ -197,18 +197,24 @@ fn parse_abnf_node(node: &Node, sum: &mut Vec) { fn main() -> Result<()> { // Take Leo ABNF grammar file. - let grammar = include_str!("../abnf-grammar.txt"); + let args: Vec = std::env::args().collect(); + let abnf_path = if let Some(path) = args.get(1) { + std::path::Path::new(path) + } else { + return Err(anyhow!("Usage Error: expects one argument to abnf file to convert.")); + }; + let grammar = std::fs::read_to_string(abnf_path)?; // Parse ABNF to get list of all definitions. // Rust ABNF does not provide support for `%s` (case sensitive strings, part of // the standard); so we need to remove all occurrences before parsing. - let parsed = abnf::rulelist(&str::replace(grammar, "%s", "")).map_err(|e| { + let parsed = abnf::rulelist(&str::replace(&grammar, "%s", "")).map_err(|e| { eprintln!("{}", &e); anyhow::anyhow!(e) })?; // Init parser and run it. That's it. - let mut parser = Processor::new(grammar, parsed); + let mut parser = Processor::new(&grammar, parsed); parser.process(); // Print result of conversion to STDOUT. From 2544a680f16c545f8df3bfd1ba2c423942a1ba5e Mon Sep 17 00:00:00 2001 From: gluaxspeed Date: Wed, 21 Jul 2021 14:57:40 -0700 Subject: [PATCH 5/6] forgot to regen tests after merging --- .../expectations/compiler/compiler/char/nonprinting.leo.out | 6 +++--- tests/expectations/compiler/compiler/char/out.leo.out | 6 +++--- tests/expectations/compiler/compiler/console/debug.leo.out | 6 +++--- tests/expectations/compiler/compiler/console/error.leo.out | 6 +++--- tests/expectations/compiler/compiler/console/log.leo.out | 6 +++--- .../compiler/compiler/console/log_conditional.leo.out | 6 +++--- .../compiler/compiler/console/log_input.leo.out | 6 +++--- .../compiler/compiler/console/log_parameter.leo.out | 6 +++--- .../compiler/compiler/console/log_parameter_many.leo.out | 6 +++--- .../compiler/input_files/program_input/main_group.leo.out | 6 +++--- .../program_input/main_multi_dimension_array.leo.out | 6 +++--- .../input_files/program_input_constants/main_array.leo.out | 6 +++--- .../input_files/program_input_constants/main_group.leo.out | 6 +++--- 13 files changed, 39 insertions(+), 39 deletions(-) diff --git a/tests/expectations/compiler/compiler/char/nonprinting.leo.out b/tests/expectations/compiler/compiler/char/nonprinting.leo.out index 599e6b7150..2acf2401e1 100644 --- a/tests/expectations/compiler/compiler/char/nonprinting.leo.out +++ b/tests/expectations/compiler/compiler/char/nonprinting.leo.out @@ -19,6 +19,6 @@ outputs: r1: type: bool value: "true" - initial_ast: 737b6db278359fac5760302a43f0f27b06e38d9dd6affce2b61946545cb475ac - canonicalized_ast: 737b6db278359fac5760302a43f0f27b06e38d9dd6affce2b61946545cb475ac - type_inferenced_ast: 07d7f6418670dca3233f14b5a7dbde73787168b2cb210f6a66860a0d9a0e9ea7 + initial_ast: 3484f8dedfe79e122e5b4a7e7d00fa185fa08884b67e6824b29580694c5983f6 + canonicalized_ast: 3484f8dedfe79e122e5b4a7e7d00fa185fa08884b67e6824b29580694c5983f6 + type_inferenced_ast: 47dc1acd415b62312ed330c9ef12620f187858ea2b0572b3404936cfb902cdd4 diff --git a/tests/expectations/compiler/compiler/char/out.leo.out b/tests/expectations/compiler/compiler/char/out.leo.out index 7985c76dd8..ab57323b5e 100644 --- a/tests/expectations/compiler/compiler/char/out.leo.out +++ b/tests/expectations/compiler/compiler/char/out.leo.out @@ -100,6 +100,6 @@ outputs: r: type: char value: "'\\u{1f62d}'" - initial_ast: af450dbf8c804894b41a738de1832af3092cae4d2823b5839da8b7dd6a8679de - canonicalized_ast: af450dbf8c804894b41a738de1832af3092cae4d2823b5839da8b7dd6a8679de - type_inferenced_ast: ec94a20aaab8811399eb3cbd6b30345083f956510e3b5cf6ffb55d3087e83cc8 + initial_ast: bfd9bc4584922f1010085bd8f2a44594eb07c5c3ffbca6efa5eecc08c5134c0b + canonicalized_ast: bfd9bc4584922f1010085bd8f2a44594eb07c5c3ffbca6efa5eecc08c5134c0b + type_inferenced_ast: 5986610003c35e0ad024bf0b3a002fe6228548ccd7d99fc2a18f140609851337 diff --git a/tests/expectations/compiler/compiler/console/debug.leo.out b/tests/expectations/compiler/compiler/console/debug.leo.out index 1c5670fc99..9eecd263fd 100644 --- a/tests/expectations/compiler/compiler/console/debug.leo.out +++ b/tests/expectations/compiler/compiler/console/debug.leo.out @@ -16,6 +16,6 @@ outputs: r0: type: bool value: "true" - initial_ast: 40014c8e30c3aa703f4f0362a16df094a45d5f0df5a94dc26e96d87170784df7 - canonicalized_ast: 40014c8e30c3aa703f4f0362a16df094a45d5f0df5a94dc26e96d87170784df7 - type_inferenced_ast: fb64ba6e32355ee6df3bd9942ca209f35714ce61511a9b8b867a1587518647a8 + initial_ast: 80d3dbfdeb9d6fb6e9ba17ba028bd2bf71014b4353f34db9d69a66ee1d0914cf + canonicalized_ast: 80d3dbfdeb9d6fb6e9ba17ba028bd2bf71014b4353f34db9d69a66ee1d0914cf + type_inferenced_ast: bbad9d7593fb1aa2bcc25f2f2625ace659e18f293af35082ad39b1e3cd71e8b5 diff --git a/tests/expectations/compiler/compiler/console/error.leo.out b/tests/expectations/compiler/compiler/console/error.leo.out index 5e70e0e4a8..a8be560200 100644 --- a/tests/expectations/compiler/compiler/console/error.leo.out +++ b/tests/expectations/compiler/compiler/console/error.leo.out @@ -16,6 +16,6 @@ outputs: r0: type: bool value: "true" - initial_ast: 2c41e0c55e5a64fd04160a56adf627d90cdbf665ebf1a0fec4a2b9049234652a - canonicalized_ast: 2c41e0c55e5a64fd04160a56adf627d90cdbf665ebf1a0fec4a2b9049234652a - type_inferenced_ast: fa6ccd4112e58ba2e97f3e600dd5d4858c45bb39a858bdd1b1867f494758cbca + initial_ast: 637b1f2fc3317138b7352122cab1754c30833e87208d443d9657aa178d7e5a31 + canonicalized_ast: 637b1f2fc3317138b7352122cab1754c30833e87208d443d9657aa178d7e5a31 + type_inferenced_ast: b003fc86b328eb76ffb5f13e4cc8b1d19da3c64c9b34ee9f116c46b998121cd3 diff --git a/tests/expectations/compiler/compiler/console/log.leo.out b/tests/expectations/compiler/compiler/console/log.leo.out index a498e594a1..de703bffbf 100644 --- a/tests/expectations/compiler/compiler/console/log.leo.out +++ b/tests/expectations/compiler/compiler/console/log.leo.out @@ -16,6 +16,6 @@ outputs: r0: type: bool value: "true" - initial_ast: e239286adc6d00ad3d4328d5911a60d5cf05612610f994faf05208420aa1ca6a - canonicalized_ast: e239286adc6d00ad3d4328d5911a60d5cf05612610f994faf05208420aa1ca6a - type_inferenced_ast: 8d10cefa3e8eb8f7d5668cb153ebc4574ca591905ab22f9ff7c81cb1be0fb110 + initial_ast: 7f5540a6b26be4fbf33c59d72ed8ba4697bbfffde5a674bacc5846ea6254194d + canonicalized_ast: 7f5540a6b26be4fbf33c59d72ed8ba4697bbfffde5a674bacc5846ea6254194d + type_inferenced_ast: 0a7837344c544abc21e4be2946292baac4553ad6b29c77e8a08a4032b592b206 diff --git a/tests/expectations/compiler/compiler/console/log_conditional.leo.out b/tests/expectations/compiler/compiler/console/log_conditional.leo.out index b43f2e88e0..37cfe37ac7 100644 --- a/tests/expectations/compiler/compiler/console/log_conditional.leo.out +++ b/tests/expectations/compiler/compiler/console/log_conditional.leo.out @@ -22,6 +22,6 @@ outputs: r0: type: bool value: "true" - initial_ast: d81597e32ce19fd62ae7043b42bbd3fd71980c74b913081cb7ed65a249efea67 - canonicalized_ast: d81597e32ce19fd62ae7043b42bbd3fd71980c74b913081cb7ed65a249efea67 - type_inferenced_ast: 5e163645d00884a3abf845af25115c2479d13517733d42f2a11c092d040d83e8 + initial_ast: 686cf650236e7d5578b163101eb4104f0dbdb0fe9ed72ebf109f518b2ab18568 + canonicalized_ast: 686cf650236e7d5578b163101eb4104f0dbdb0fe9ed72ebf109f518b2ab18568 + type_inferenced_ast: dddd466784e0095ccd9004c9b3fe011290e36cc0b0a0ceb20ed4052f99aa9b35 diff --git a/tests/expectations/compiler/compiler/console/log_input.leo.out b/tests/expectations/compiler/compiler/console/log_input.leo.out index 073361471a..ef23f2153c 100644 --- a/tests/expectations/compiler/compiler/console/log_input.leo.out +++ b/tests/expectations/compiler/compiler/console/log_input.leo.out @@ -16,6 +16,6 @@ outputs: r0: type: bool value: "true" - initial_ast: f2a4b944ccecd2be35bab0a4922fb96595b75007db6c35f4be3a1aac08426f9e - canonicalized_ast: f2a4b944ccecd2be35bab0a4922fb96595b75007db6c35f4be3a1aac08426f9e - type_inferenced_ast: bb0b9193a4d3b6d9a91a56820f902c17f0b64705cd6d85cb942479952ec95fab + initial_ast: 0e62d5ba7775423a274c6871ed7aa23143051a1030fa2375b3ac52b4624bed29 + canonicalized_ast: 0e62d5ba7775423a274c6871ed7aa23143051a1030fa2375b3ac52b4624bed29 + type_inferenced_ast: 816f0e99603f81c6fd2e771095de519200e954ce238a76a535cd3f2b5c403c23 diff --git a/tests/expectations/compiler/compiler/console/log_parameter.leo.out b/tests/expectations/compiler/compiler/console/log_parameter.leo.out index a9e5a0c53a..11a643fae8 100644 --- a/tests/expectations/compiler/compiler/console/log_parameter.leo.out +++ b/tests/expectations/compiler/compiler/console/log_parameter.leo.out @@ -16,6 +16,6 @@ outputs: r0: type: bool value: "true" - initial_ast: 057e2a8b0841e05d95c13c39d259a26fc12091362ef60f7291329a1a82f0af5e - canonicalized_ast: 057e2a8b0841e05d95c13c39d259a26fc12091362ef60f7291329a1a82f0af5e - type_inferenced_ast: 054b00815c79679a877ab34a89a5dbd27771f5896984c684bcc43a74c5792b32 + initial_ast: 4c513c484e711bef8b4da2bd54e71f3e6bbea22d002155b598c7573ba5b1c747 + canonicalized_ast: 4c513c484e711bef8b4da2bd54e71f3e6bbea22d002155b598c7573ba5b1c747 + type_inferenced_ast: a33edf5d144205f5f5f820c8a95476b677dfcae5a4997c28cd707dcd637cf12a diff --git a/tests/expectations/compiler/compiler/console/log_parameter_many.leo.out b/tests/expectations/compiler/compiler/console/log_parameter_many.leo.out index 010687e3e1..78b90b9a8c 100644 --- a/tests/expectations/compiler/compiler/console/log_parameter_many.leo.out +++ b/tests/expectations/compiler/compiler/console/log_parameter_many.leo.out @@ -16,6 +16,6 @@ outputs: r0: type: bool value: "true" - initial_ast: ca762bf3526c963a715e0b3ba8f439fde8cfc3466c92e8a76fd3fd78f3dcdcdb - canonicalized_ast: ca762bf3526c963a715e0b3ba8f439fde8cfc3466c92e8a76fd3fd78f3dcdcdb - type_inferenced_ast: 9d00a752fd798ce8b44834df8ed766eb9acd737fade0ed6af938f668c3560a25 + initial_ast: 11e5803a91f359a4d9e41f70173072984d8a430ea2e19169c239f9fdd0b81726 + canonicalized_ast: 11e5803a91f359a4d9e41f70173072984d8a430ea2e19169c239f9fdd0b81726 + type_inferenced_ast: 6833ca94c2ee3e7ceecec151709c145ac12b3fc258b6f720bc203d89056852cf diff --git a/tests/expectations/compiler/compiler/input_files/program_input/main_group.leo.out b/tests/expectations/compiler/compiler/input_files/program_input/main_group.leo.out index e2dbc00e2f..e1afb82eb9 100644 --- a/tests/expectations/compiler/compiler/input_files/program_input/main_group.leo.out +++ b/tests/expectations/compiler/compiler/input_files/program_input/main_group.leo.out @@ -13,6 +13,6 @@ outputs: - input_file: input/main_group.in output: registers: {} - initial_ast: b44ed4078cfd47b305648f8b661e63a925d296608b6e05e68922153dc384e5a6 - canonicalized_ast: f63a180228feec0dd67f648abb07cbf595d43820ea1a98f3fcb746bbd7a6abe9 - type_inferenced_ast: b3179dda593e9c7cd3607416273206a5ae8d171f2c46e6a724f6877e7f3e94b1 + initial_ast: 80d005d0d277205d4e42029b1257aea9f67301ac8193dd8e595051ebb12ee50e + canonicalized_ast: 89eca89eb1633f9346fed9f868d11326c6d8721b846f49b23cf5c54bc426273a + type_inferenced_ast: 4e592b434fbceb1a1b942a65d8f6e1a2ca21754fd8215584512c0ecf89c03fa7 diff --git a/tests/expectations/compiler/compiler/input_files/program_input/main_multi_dimension_array.leo.out b/tests/expectations/compiler/compiler/input_files/program_input/main_multi_dimension_array.leo.out index bd6d8663ca..26317ab6f0 100644 --- a/tests/expectations/compiler/compiler/input_files/program_input/main_multi_dimension_array.leo.out +++ b/tests/expectations/compiler/compiler/input_files/program_input/main_multi_dimension_array.leo.out @@ -16,6 +16,6 @@ outputs: r0: type: bool value: "true" - initial_ast: 0b7d9217b441385cf29f20d5e73748627dcea1a608ccfb42ef3f1dc5e68caaac - canonicalized_ast: 8168a20e4754f86746678eaf7dd23d741b7d81d7ddba1822e0a90fd8c3149651 - type_inferenced_ast: 43dcf7397576914601b03d1b7fd92f531992aca52f0879646316df0840d9ff6a + initial_ast: 5f2fe4c4fae5c53b40a0011389aaba92ed6f61487c4be3938f450d658cae4639 + canonicalized_ast: bada9b141c8740dc1af11c395ac97152ed9ef8796e92ceade5fdcb43354a997d + type_inferenced_ast: 905b29996916de37e498809f39b26520b618d4d6a952156d8406513dda9d4e8a diff --git a/tests/expectations/compiler/compiler/input_files/program_input_constants/main_array.leo.out b/tests/expectations/compiler/compiler/input_files/program_input_constants/main_array.leo.out index d74fb40d3e..8fc0faca83 100644 --- a/tests/expectations/compiler/compiler/input_files/program_input_constants/main_array.leo.out +++ b/tests/expectations/compiler/compiler/input_files/program_input_constants/main_array.leo.out @@ -16,6 +16,6 @@ outputs: r0: type: bool value: "true" - initial_ast: fc0d2296fdb76961806268d592892a064f4de5c55f2bdb705c46c6f564f2a0af - canonicalized_ast: fc0d2296fdb76961806268d592892a064f4de5c55f2bdb705c46c6f564f2a0af - type_inferenced_ast: 40cf529694add5a9193fe0d1784247ae1246f067076c96bc9fdc114e633699af + initial_ast: 5cf9f340177e4a476a81c0cbb645ab443dd93551b5e247332e193273b5a42288 + canonicalized_ast: 5cf9f340177e4a476a81c0cbb645ab443dd93551b5e247332e193273b5a42288 + type_inferenced_ast: 9b10865266ad6fa0f46d35a94fe2c052ff0fb46ffca1dbdc2b2294ca9e52217e diff --git a/tests/expectations/compiler/compiler/input_files/program_input_constants/main_group.leo.out b/tests/expectations/compiler/compiler/input_files/program_input_constants/main_group.leo.out index c3eed3a8a4..78e7a115fd 100644 --- a/tests/expectations/compiler/compiler/input_files/program_input_constants/main_group.leo.out +++ b/tests/expectations/compiler/compiler/input_files/program_input_constants/main_group.leo.out @@ -16,6 +16,6 @@ outputs: r0: type: bool value: "true" - initial_ast: 3d5a53e68eb786c6a74618b539714d3fffbcc890444a5d57409a88cc214cfb7e - canonicalized_ast: 3d5a53e68eb786c6a74618b539714d3fffbcc890444a5d57409a88cc214cfb7e - type_inferenced_ast: 2baac7174583b7f6562bd0715d58da99c335c8ccfd56a2286e18ae57ea078694 + initial_ast: 854be098a724cec3bb03249c8fc03aba5d53c2628bad6d45188872d2f67a7f15 + canonicalized_ast: 854be098a724cec3bb03249c8fc03aba5d53c2628bad6d45188872d2f67a7f15 + type_inferenced_ast: 37a8d6e71f2f6e2ffc61ffd29beb07ff04189647a367e21e7d70245bb3d0b280 From 3a6e4cb9946a3e6ce5dbacb748f0bd0f1ec571ea Mon Sep 17 00:00:00 2001 From: gluaxspeed Date: Wed, 21 Jul 2021 20:00:44 -0700 Subject: [PATCH 6/6] be more specific of where this grammar applies, clean up --- grammar/FORMAT_ABNF_GRAMMER.md | Bin 17082 -> 3660 bytes grammar/format-abnf-grammar.txt | 157 +++----------------------------- 2 files changed, 14 insertions(+), 143 deletions(-) diff --git a/grammar/FORMAT_ABNF_GRAMMER.md b/grammar/FORMAT_ABNF_GRAMMER.md index 3b95e051779bd90ef0bbcd7c3864daadafe6610e..cc88f9fda16e7bedcda279922d09d5f7c877fcbe 100644 GIT binary patch delta 532 zcmZ8dJx>Bb6da92Bx;NpqfvArXYeB;g@Ho21V1`yY>mZvcM6Kf1yH1sSojBgjir?} ziJgVv2T)mC=r6Fh^6ledqS@TOeY3Ok=H}&Ypf}%n4N)R|-#$SVH7Hz2b@^0=2aPKD zsKAAVI`0ZLN?dK6^W7yHj5GupLj;F7z#a3<3CZGPvm9T(TUYeN=WuRu|JE!wU#!;%TQ zR^(*y)E48u+~^;4gyl(Xa<6@9)nuSpV&;|L+NDBMyw*g;!ZCOKP+X47X0Fd%J-Iq6 z`MNA)BSoLa9Cb6Bq_Q)!OKdldb!0g?#d#@cB#RwN8Zmk>6!nZWz2_F_(VTdG+BzGg zoViWi#1MU$in@hK>X_-EcYg8x{B%@jFv3fx7cBb9EDkH=xnHGQGe(`vcv$oF_XC)3 BZ`c3; literal 17082 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 diff --git a/grammar/format-abnf-grammar.txt b/grammar/format-abnf-grammar.txt index 0fa049a228..87d810d881 100644 --- a/grammar/format-abnf-grammar.txt +++ b/grammar/format-abnf-grammar.txt @@ -16,154 +16,25 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; Introduction -; ------------ - -; This file contains an ABNF (Augmented Backus-Naur Form) grammar of Leo string formatting. -; Background on ABNF is provided later in this file. - -; This grammar provides an official definition of how format strings -; are parsed for printed by the Leo compiler. - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - ; Background on ABNF ; ------------------ ; ABNF is an Internet standard: -; see RFC 5234 at https://www.rfc-editor.org/info/rfc5234 -; and RFC 7405 at https://www.rfc-editor.org/info/rfc7405. -; It is used to specify the syntax of JSON, HTTP, and other standards. - -; ABNF adds conveniences and makes slight modifications -; to Backus-Naur Form (BNF), -; without going beyond context-free grammars. - -; Instead of BNF's angle-bracket notation for nonterminals, -; ABNF uses case-insensitive names consisting of letters, digits, and dashes, -; e.g. `HTTP-message` and `IPv6address`. -; ABNF includes an angle-bracket notation for prose descriptions, -; e.g. ``, -; usable as last resort in the definiens of a nonterminal. - -; While BNF allows arbitrary terminals, -; ABNF uses only natural numbers as terminals, -; and denotes them via: -; (i) binary, decimal, or hexadecimal sequences, -; e.g. `%b1.11.1010`, `%d1.3.10`, and `%x.1.3.A` -; all denote the sequence of terminals [1, 3, 10]; -; (ii) binary, decimal, or hexadecimal ranges, -; e.g. `%x30-39` denotes any singleton sequence of terminals -; [_n_] with 48 <= _n_ <= 57 (an ASCII digit); -; (iii) case-sensitive ASCII strings, -; e.g. `%s"Ab"` denotes the sequence of terminals [65, 98]; -; and (iv) case-insensitive ASCII strings, -; e.g. `%i"ab"`, or just `"ab"`, denotes -; any sequence of terminals among -; [65, 66], -; [65, 98], -; [97, 66], and -; [97, 98]. -; ABNF terminals in suitable sets represent ASCII or Unicode characters. - -; ABNF allows repetition prefixes `n*m`, -; where `n` and `m` are natural numbers in decimal notation; -; if absent, -; `n` defaults to 0, and -; `m` defaults to infinity. -; For example, -; `1*4HEXDIG` denotes one to four `HEXDIG`s, -; `*3DIGIT` denotes up to three `DIGIT`s, and -; `1*OCTET` denotes one or more `OCTET`s. -; A single `n` prefix -; abbreviates `n*n`, -; e.g. `3DIGIT` denotes three `DIGIT`s. - -; Instead of BNF's `|`, ABNF uses `/` to separate alternatives. -; Repetition prefixes have precedence over juxtapositions, -; which have precedence over `/`. -; Round brackets group things and override the aforementioned precedence rules, -; e.g. `*(WSP / CRLF WSP)` denotes sequences of terminals -; obtained by repeating, zero or more times, -; either (i) a `WSP` or (ii) a `CRLF` followed by a `WSP`. -; Square brackets also group things but make them optional, -; e.g. `[":" port]` is equivalent to `0*1(":" port)`. - -; Instead of BNF's `::=`, ABNF uses `=` to define nonterminals, -; and `=/` to incrementally add alternatives -; to previously defined nonterminals. -; For example, the rule `BIT = "0" / "1"` -; is equivalent to `BIT = "0"` followed by `BIT =/ "1"`. - -; The syntax of ABNF itself is formally specified in ABNF -; (in Section 4 of the aforementioned RFC 5234, -; after the syntax and semantics of ABNF -; are informally specified in natural language -; (in Sections 1, 2, and 3 of the aforementioned RFC 5234). -; The syntax rules of ABNF prescribe the ASCII codes allowed for -; white space (spaces and horizontal tabs), -; line endings (carriage returns followed by line feeds), -; and comments (semicolons to line endings). - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -; Structure -; --------- - -; This ABNF grammar consists of one grammar: -; that describes how a Leo string-literal is parsed -; for formatting. +; for more info please checkout the +; [README](./README.md) in this directory. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Format String ; ------------------- -not-double-quote-or-backslash-or-brace = %x0-21 - / %x23-5B - / %x5D-7A - / %x7C - / %x7E-10FFFF - ; anything but " or \ or { or } +; This ABNF grammar consists of one grammar: +; that describes how a Leo string-literal is parsed +; for formatting. Meaning in this context +; all characters are already parsed and we don't +; have to worry about escapes or etc. -double-quote = %x22 ; " - -single-quote = %x27 ; ' - -single-quote-escape = "\" single-quote ; \' - -double-quote-escape = "\" double-quote ; \" - -backslash-escape = "\\" - -line-feed-escape = %s"\n" - -carriage-return-escape = %s"\r" - -horizontal-tab-escape = %s"\t" - -null-character-escape = "\0" - -simple-character-escape = single-quote-escape - / double-quote-escape - / backslash-escape - / line-feed-escape - / carriage-return-escape - / horizontal-tab-escape - / null-character-escape - -octal-digit = %x30-37 ; 0-7 - -hexadecimal-digit = digit / "a" / "b" / "c" / "d" / "e" / "f" - -ascii-character-escape = %s"\x" octal-digit hexadecimal-digit - -unicode-character-escape = %s"\u{" 1*6hexadecimal-digit "}" - -format-string-element-not-brace = not-double-quote-or-backslash-or-brace - / simple-character-escape - / ascii-character-escape - / unicode-character-escape +not-brace = %x0-7A / %x7C / %x7E-10FFFF ; anything but { or } format-string-container = "{}" @@ -171,9 +42,9 @@ format-string-open-brace = "{{" format-string-close-brace = "}}" -format-string-element = format-string-element-not-brace - / format-string-container - / format-string-open-brace - / format-string-close-brace - -format-string = double-quote *format-string-element double-quote +format-string-element = not-brace + / format-string-container + / format-string-open-brace + / format-string-close-brace + +format-string = *format-string-element