Skip to content
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
32bd282
1st draft for string parsing and ast type
damirka May 18, 2021
39b61a0
added character parsing
damirka May 19, 2021
2f9fde8
removes commented blocks
damirka May 19, 2021
7379850
fix tests
damirka May 19, 2021
ed03a38
fix unicode chars
damirka May 19, 2021
f96c4ad
Merge branch 'feature/char-type' into feature/string-parsing
damirka May 19, 2021
f404c42
fix comments
damirka May 19, 2021
86fc239
more comment fixes
damirka May 19, 2021
a5f994a
Merge remote-tracking branch 'origin/feature/char-type' into feature/…
damirka May 20, 2021
4fe91f0
string canonicalization to char array
gluax May 20, 2021
947f13b
Merge branch 'feature/string-parsing' of github.com:AleoHQ/leo into f…
gluax May 20, 2021
c4a1bd6
merge and print strings like strings, rather than arrays
gluax May 20, 2021
ca59ff3
tests for now should be changed to use input strings when they are in
gluax May 20, 2021
c8e63a2
change pest according to suggestion fixes it
gluax May 21, 2021
c63a549
Merge pull request #979 from AleoHQ/feature/string-canonicalization
acoglio May 21, 2021
ddbdd48
Revert "change pest according to suggestion fixes it"
gluax May 22, 2021
329b330
merge remote
gluax May 22, 2021
efc8320
string parsing leverages eat_char
gluax May 22, 2021
8cb1dc6
remove debug stmts
gluax May 22, 2021
1c57eb4
input string syntax
gluax May 22, 2021
bce10cc
quick test fix
gluax May 22, 2021
7145a75
console.log refactor, concat test... has asg or TIPhase reducer bug
gluax May 23, 2021
91f27bc
explicit type resolves issue, will make bug for implict
gluax May 23, 2021
497b039
added few more test cases for strings
damirka May 24, 2021
a3416c1
added max value for \x7F escape
damirka May 24, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions asg/src/expression/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@ impl<'a> FromAst<'a, leo_ast::ValueExpression> for Constant<'a> {
value: ConstValue::Int(ConstInt::parse(int_type, value, span)?),
}
}
String(_str_type, _value) => {
unimplemented!("strings do not exist on ASG level")
}
})
}
}
Expand Down
2 changes: 1 addition & 1 deletion ast/src/annotation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{Identifier, Span};
use serde::{Deserialize, Serialize};
use tendril::StrTendril;

#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct Annotation {
pub span: Span,
pub name: Identifier,
Expand Down
6 changes: 6 additions & 0 deletions ast/src/errors/reducer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ impl ReducerError {
ReducerError::Error(FormattedError::new_from_span(message, span))
}

pub fn failed_to_convert_tendril_to_char(tendril: String, span: &Span) -> Self {
let message = format!("Failed to convert tendril `{}` to char", tendril);

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();

Expand Down
13 changes: 11 additions & 2 deletions ast/src/expression/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub enum ValueExpression {
#[serde(with = "crate::common::tendril_json")] StrTendril,
Span,
),
String(Vec<char>, Span),
}

impl fmt::Display for ValueExpression {
Expand All @@ -46,6 +47,12 @@ impl fmt::Display for ValueExpression {
Implicit(implicit, _) => write!(f, "{}", implicit),
Integer(value, type_, _) => write!(f, "{}{}", value, type_),
Group(group) => write!(f, "{}", group),
String(char_vec, _) => {
for character in char_vec {
write!(f, "{}", character)?
}
Ok(())
}
}
}
}
Expand All @@ -59,7 +66,8 @@ impl Node for ValueExpression {
| Char(_, span)
| Field(_, span)
| Implicit(_, span)
| Integer(_, _, span) => span,
| Integer(_, _, span)
| String(_, span) => span,
Group(group) => match &**group {
GroupValue::Single(_, span) | GroupValue::Tuple(GroupTuple { span, .. }) => span,
},
Expand All @@ -74,7 +82,8 @@ impl Node for ValueExpression {
| Char(_, span)
| Field(_, span)
| Implicit(_, span)
| Integer(_, _, span) => *span = new_span,
| Integer(_, _, span)
| String(_, span) => *span = new_span,
Group(group) => match &mut **group {
GroupValue::Single(_, span) | GroupValue::Tuple(GroupTuple { span, .. }) => *span = new_span,
},
Expand Down
50 changes: 49 additions & 1 deletion ast/src/input/input_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
use crate::{ArrayDimensions, GroupValue};
use leo_input::{
errors::InputParserError,
expressions::{ArrayInitializerExpression, ArrayInlineExpression, Expression, TupleExpression},
expressions::{ArrayInitializerExpression, ArrayInlineExpression, Expression, StringExpression, TupleExpression},
types::{ArrayType, DataType, IntegerType, TupleType, Type},
values::{
Address,
Expand Down Expand Up @@ -115,11 +115,59 @@ impl InputValue {
(Type::Array(array_type), Expression::ArrayInitializer(initializer)) => {
InputValue::from_array_initializer(array_type, initializer)
}
(Type::Array(array_type), Expression::StringExpression(string)) => {
InputValue::from_string(array_type, string)
}
(Type::Tuple(tuple_type), Expression::Tuple(tuple)) => InputValue::from_tuple(tuple_type, tuple),
(type_, expression) => Err(InputParserError::expression_type_mismatch(type_, expression)),
}
}

///
/// Returns a new `InputValue` from the given `ArrayType` and `StringExpression`.
///
pub(crate) fn from_string(mut array_type: ArrayType, string: StringExpression) -> Result<Self, InputParserError> {
// Create a new `ArrayDimensions` type from the input array_type dimensions.
let array_dimensions_type = ArrayDimensions::from(array_type.dimensions.clone());

// Convert the array dimensions to usize.
let array_dimensions = parse_array_dimensions(array_dimensions_type, &array_type.span)?;

// Return an error if the outer array dimension does not equal the number of array elements.
if array_dimensions[0] != string.chars.len() {
return Err(InputParserError::invalid_string_length(
array_dimensions[0],
string.chars.len(),
&string.span,
));
}

array_type.dimensions = array_type.dimensions.next_dimension();

let inner_array_type = if array_dimensions.len() == 1 {
// This is a single array
*array_type.type_
} else {
// This is a multi-dimensional array
return Err(InputParserError::invalid_string_dimensions(&array_type.span));
};

let mut elements = Vec::with_capacity(string.chars.len());
for character in string.chars.into_iter() {
let element = InputValue::from_expression(
inner_array_type.clone(),
Expression::Value(Value::Char(CharValue {
value: character.clone(),
span: character.span().clone(),
})),
)?;

elements.push(element)
}

Ok(InputValue::Array(elements))
}

///
/// Returns a new `InputValue` from the given `ArrayType` and `ArrayInlineExpression`.
///
Expand Down
14 changes: 14 additions & 0 deletions ast/src/reducer/canonicalization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,20 @@ impl ReconstructingReducer for Canonicalizer {
}
}

fn reduce_string(&mut self, string: &[char], span: &Span) -> Result<Expression, ReducerError> {
let mut elements = Vec::new();
for character in string {
elements.push(SpreadOrExpression::Expression(Expression::Value(
ValueExpression::Char(*character, span.clone()),
)));
}

Ok(Expression::ArrayInline(ArrayInlineExpression {
elements,
span: span.clone(),
}))
}

fn reduce_array_init(
&mut self,
array_init: &ArrayInitExpression,
Expand Down
13 changes: 9 additions & 4 deletions ast/src/reducer/reconstructing_director.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ impl<R: ReconstructingReducer> ReconstructingDirector<R> {
pub fn reduce_expression(&mut self, expression: &Expression) -> Result<Expression, ReducerError> {
let new = match expression {
Expression::Identifier(identifier) => Expression::Identifier(self.reduce_identifier(&identifier)?),
Expression::Value(value) => Expression::Value(self.reduce_value(&value)?),
Expression::Value(value) => self.reduce_value(&value)?,
Expression::Binary(binary) => Expression::Binary(self.reduce_binary(&binary)?),
Expression::Unary(unary) => Expression::Unary(self.reduce_unary(&unary)?),
Expression::Ternary(ternary) => Expression::Ternary(self.reduce_ternary(&ternary)?),
Expand Down Expand Up @@ -100,12 +100,17 @@ impl<R: ReconstructingReducer> ReconstructingDirector<R> {
self.reducer.reduce_group_value(group_value, new)
}

pub fn reduce_value(&mut self, value: &ValueExpression) -> Result<ValueExpression, ReducerError> {
pub fn reduce_string(&mut self, string: &[char], span: &Span) -> Result<Expression, ReducerError> {
self.reducer.reduce_string(string, span)
}

pub fn reduce_value(&mut self, value: &ValueExpression) -> Result<Expression, ReducerError> {
let new = match value {
ValueExpression::Group(group_value) => {
ValueExpression::Group(Box::new(self.reduce_group_value(&group_value)?))
Expression::Value(ValueExpression::Group(Box::new(self.reduce_group_value(&group_value)?)))
}
_ => value.clone(),
ValueExpression::String(string, span) => self.reduce_string(&string, &span)?,
_ => Expression::Value(value.clone()),
};

self.reducer.reduce_value(value, new)
Expand Down
13 changes: 8 additions & 5 deletions ast/src/reducer/reconstructing_reducer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,14 @@ pub trait ReconstructingReducer {
Ok(new)
}

fn reduce_value(
&mut self,
_value: &ValueExpression,
new: ValueExpression,
) -> Result<ValueExpression, ReducerError> {
fn reduce_string(&mut self, string: &[char], span: &Span) -> Result<Expression, ReducerError> {
Ok(Expression::Value(ValueExpression::String(
string.to_vec(),
span.clone(),
)))
}

fn reduce_value(&mut self, _value: &ValueExpression, new: Expression) -> Result<Expression, ReducerError> {
Ok(new)
}

Expand Down
37 changes: 33 additions & 4 deletions ast/src/statements/console/formatted_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,43 @@ use crate::{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),
Const(char),
Container,
}

impl FormatStringPart {
pub fn from_string(string: Vec<char>) -> Vec<Self> {
let mut parts = Vec::new();
let mut in_container = false;
let mut i = 0;

while i < string.len() {
let character = string[i];

match character {
'{' if !in_container => in_container = true,
'}' if in_container => {
in_container = false;
parts.push(FormatStringPart::Container);
}
_ if in_container => {
in_container = false;
parts.push(FormatStringPart::Const('{'));
continue;
}
_ => parts.push(FormatStringPart::Const(character)),
}

i += 1;
}

parts
}
}

#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct FormatString {
pub parts: Vec<FormatStringPart>,
Expand All @@ -41,8 +70,8 @@ impl fmt::Display for FormatString {
self.parts
.iter()
.map(|x| match x {
FormatStringPart::Const(x) => x,
FormatStringPart::Container => "{}",
FormatStringPart::Const(x) => x.to_string(),
FormatStringPart::Container => "{}".to_string(),
})
.collect::<Vec<_>>()
.join("")
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/console/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
let mut parameters = executed_containers.iter();
for part in formatted.parts.iter() {
match part {
FormatStringPart::Const(c) => out.push(&**c),
FormatStringPart::Container => out.push(&**parameters.next().unwrap()),
FormatStringPart::Const(c) => out.push(c.to_string()),
FormatStringPart::Container => out.push(parameters.next().unwrap().to_string()),
}
}

Expand Down
14 changes: 8 additions & 6 deletions compiler/src/phases/reducing_director.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,7 @@ impl<R: ReconstructingReducer, O: CombinerOptions> CombineAstAsgDirector<R, O> {
asg: &AsgExpression,
) -> Result<AstExpression, ReducerError> {
let new = match (ast, asg) {
(AstExpression::Value(value), AsgExpression::Constant(const_)) => {
AstExpression::Value(self.reduce_value(&value, &const_)?)
}
(AstExpression::Value(value), AsgExpression::Constant(const_)) => self.reduce_value(&value, &const_)?,
(AstExpression::Binary(ast), AsgExpression::Binary(asg)) => {
AstExpression::Binary(self.reduce_binary(&ast, &asg)?)
}
Expand Down Expand Up @@ -404,7 +402,7 @@ impl<R: ReconstructingReducer, O: CombinerOptions> CombineAstAsgDirector<R, O> {
self.ast_reducer.reduce_unary(ast, inner, ast.op.clone())
}

pub fn reduce_value(&mut self, ast: &ValueExpression, asg: &AsgConstant) -> Result<ValueExpression, ReducerError> {
pub fn reduce_value(&mut self, ast: &ValueExpression, asg: &AsgConstant) -> Result<AstExpression, ReducerError> {
let mut new = ast.clone();

if self.options.type_inference_enabled() {
Expand Down Expand Up @@ -436,15 +434,19 @@ impl<R: ReconstructingReducer, O: CombinerOptions> CombineAstAsgDirector<R, O> {
ConstValue::Char(_) => {
if let Some(c) = tendril.chars().next() {
new = ValueExpression::Char(c, span.clone());
} else {
return Err(ReducerError::failed_to_convert_tendril_to_char(
tendril.to_string(),
span,
));
}
// TODO RETURN ERR
}
_ => unimplemented!(), // impossible?
}
}
}

self.ast_reducer.reduce_value(ast, new)
self.ast_reducer.reduce_value(ast, AstExpression::Value(new))
}

pub fn reduce_variable_ref(
Expand Down
20 changes: 14 additions & 6 deletions compiler/src/value/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,22 @@ impl<'a, F: PrimeField, G: GroupType<F>> fmt::Display for ConstrainedValue<'a, F

// Data type wrappers
ConstrainedValue::Array(ref array) => {
write!(f, "[")?;
for (i, e) in array.iter().enumerate() {
write!(f, "{}", e)?;
if i < array.len() - 1 {
write!(f, ", ")?;
if matches!(array[0], ConstrainedValue::Char(_)) {
for character in array {
write!(f, "{}", character)?;
}

Ok(())
} else {
write!(f, "[")?;
for (i, e) in array.iter().enumerate() {
write!(f, "{}", e)?;
if i < array.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, "]")
}
write!(f, "]")
}
ConstrainedValue::Tuple(ref tuple) => {
let values = tuple.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(", ");
Expand Down
13 changes: 13 additions & 0 deletions compiler/tests/canonicalization/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,16 @@ fn test_illegal_array_range_fail() {
let program = parse_program(program_string);
assert!(program.is_err());
}

#[test]
fn test_string_transformation() {
let program_string = include_str!("string_transformation.leo");
let program = parse_program(program_string).unwrap();
assert_satisfied(program);

let ast = parse_program_ast(program_string);
let expected_json = include_str!("string_transformation.json");
let expected_ast: Ast = Ast::from_json_string(expected_json).expect("Unable to parse json.");

assert_eq!(expected_ast, ast);
}
Loading