mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-11-27 12:17:35 +03:00
Merge pull request #957 from AleoHQ/feature/char-type
Feature/char type
This commit is contained in:
commit
c56208fa4b
@ -109,6 +109,7 @@ pub enum ConstValue {
|
|||||||
Field(BigInt),
|
Field(BigInt),
|
||||||
Address(StrTendril),
|
Address(StrTendril),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
|
Char(char),
|
||||||
|
|
||||||
// compounds
|
// compounds
|
||||||
Tuple(Vec<ConstValue>),
|
Tuple(Vec<ConstValue>),
|
||||||
@ -302,6 +303,7 @@ impl ConstValue {
|
|||||||
ConstValue::Field(_) => Type::Field,
|
ConstValue::Field(_) => Type::Field,
|
||||||
ConstValue::Address(_) => Type::Address,
|
ConstValue::Address(_) => Type::Address,
|
||||||
ConstValue::Boolean(_) => Type::Boolean,
|
ConstValue::Boolean(_) => Type::Boolean,
|
||||||
|
ConstValue::Char(_) => Type::Char,
|
||||||
ConstValue::Tuple(sub_consts) => {
|
ConstValue::Tuple(sub_consts) => {
|
||||||
Type::Tuple(sub_consts.iter().map(|x| x.get_type()).collect::<Option<Vec<Type>>>()?)
|
Type::Tuple(sub_consts.iter().map(|x| x.get_type()).collect::<Option<Vec<Type>>>()?)
|
||||||
}
|
}
|
||||||
|
@ -247,6 +247,10 @@ impl AsgConvertError {
|
|||||||
Self::new_from_span(format!("failed to parse boolean value '{}'", value), span)
|
Self::new_from_span(format!("failed to parse boolean value '{}'", value), span)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn invalid_char(value: &str, span: &Span) -> Self {
|
||||||
|
Self::new_from_span(format!("failed to parse char value '{}'", value), span)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn invalid_int(value: &str, span: &Span) -> Self {
|
pub fn invalid_int(value: &str, span: &Span) -> Self {
|
||||||
Self::new_from_span(format!("failed to parse int value '{}'", value), span)
|
Self::new_from_span(format!("failed to parse int value '{}'", value), span)
|
||||||
}
|
}
|
||||||
|
@ -118,6 +118,24 @@ impl<'a> FromAst<'a, leo_ast::ValueExpression> for Constant<'a> {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Char(value, span) => {
|
||||||
|
match expected_type.map(PartialType::full).flatten() {
|
||||||
|
Some(Type::Char) | None => (),
|
||||||
|
Some(x) => {
|
||||||
|
return Err(AsgConvertError::unexpected_type(
|
||||||
|
&x.to_string(),
|
||||||
|
Some(&*Type::Char.to_string()),
|
||||||
|
span,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Constant {
|
||||||
|
parent: Cell::new(None),
|
||||||
|
span: Some(span.clone()),
|
||||||
|
value: ConstValue::Char(*value),
|
||||||
|
}
|
||||||
|
}
|
||||||
Field(value, span) => {
|
Field(value, span) => {
|
||||||
match expected_type.map(PartialType::full).flatten() {
|
match expected_type.map(PartialType::full).flatten() {
|
||||||
Some(Type::Field) | None => (),
|
Some(Type::Field) | None => (),
|
||||||
@ -202,6 +220,9 @@ impl<'a> FromAst<'a, leo_ast::ValueExpression> for Constant<'a> {
|
|||||||
value: ConstValue::Int(ConstInt::parse(int_type, value, span)?),
|
value: ConstValue::Int(ConstInt::parse(int_type, value, span)?),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
String(_str_type, _value) => {
|
||||||
|
unimplemented!("strings do not exist on ASG level")
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,6 +236,7 @@ impl<'a> Into<leo_ast::ValueExpression> for &Constant<'a> {
|
|||||||
ConstValue::Boolean(value) => {
|
ConstValue::Boolean(value) => {
|
||||||
leo_ast::ValueExpression::Boolean(value.to_string().into(), self.span.clone().unwrap_or_default())
|
leo_ast::ValueExpression::Boolean(value.to_string().into(), self.span.clone().unwrap_or_default())
|
||||||
}
|
}
|
||||||
|
ConstValue::Char(value) => leo_ast::ValueExpression::Char(*value, self.span.clone().unwrap_or_default()),
|
||||||
ConstValue::Field(value) => {
|
ConstValue::Field(value) => {
|
||||||
leo_ast::ValueExpression::Field(value.to_string().into(), self.span.clone().unwrap_or_default())
|
leo_ast::ValueExpression::Field(value.to_string().into(), self.span.clone().unwrap_or_default())
|
||||||
}
|
}
|
||||||
|
@ -178,6 +178,7 @@ impl<'a> Scope<'a> {
|
|||||||
Ok(match type_ {
|
Ok(match type_ {
|
||||||
Address => Type::Address,
|
Address => Type::Address,
|
||||||
Boolean => Type::Boolean,
|
Boolean => Type::Boolean,
|
||||||
|
Char => Type::Char,
|
||||||
Field => Type::Field,
|
Field => Type::Field,
|
||||||
Group => Type::Group,
|
Group => Type::Group,
|
||||||
IntegerType(int_type) => Type::Integer(int_type.clone()),
|
IntegerType(int_type) => Type::Integer(int_type.clone()),
|
||||||
|
@ -25,6 +25,7 @@ pub enum Type<'a> {
|
|||||||
// Data types
|
// Data types
|
||||||
Address,
|
Address,
|
||||||
Boolean,
|
Boolean,
|
||||||
|
Char,
|
||||||
Field,
|
Field,
|
||||||
Group,
|
Group,
|
||||||
Integer(IntegerType),
|
Integer(IntegerType),
|
||||||
@ -134,6 +135,7 @@ impl<'a> fmt::Display for Type<'a> {
|
|||||||
match self {
|
match self {
|
||||||
Type::Address => write!(f, "address"),
|
Type::Address => write!(f, "address"),
|
||||||
Type::Boolean => write!(f, "bool"),
|
Type::Boolean => write!(f, "bool"),
|
||||||
|
Type::Char => write!(f, "char"),
|
||||||
Type::Field => write!(f, "field"),
|
Type::Field => write!(f, "field"),
|
||||||
Type::Group => write!(f, "group"),
|
Type::Group => write!(f, "group"),
|
||||||
Type::Integer(sub_type) => sub_type.fmt(f),
|
Type::Integer(sub_type) => sub_type.fmt(f),
|
||||||
@ -199,6 +201,7 @@ impl<'a> Into<leo_ast::Type> for &Type<'a> {
|
|||||||
match self {
|
match self {
|
||||||
Address => leo_ast::Type::Address,
|
Address => leo_ast::Type::Address,
|
||||||
Boolean => leo_ast::Type::Boolean,
|
Boolean => leo_ast::Type::Boolean,
|
||||||
|
Char => leo_ast::Type::Char,
|
||||||
Field => leo_ast::Type::Field,
|
Field => leo_ast::Type::Field,
|
||||||
Group => leo_ast::Type::Group,
|
Group => leo_ast::Type::Group,
|
||||||
Integer(int_type) => leo_ast::Type::IntegerType(int_type.clone()),
|
Integer(int_type) => leo_ast::Type::IntegerType(int_type.clone()),
|
||||||
|
@ -19,7 +19,7 @@ use crate::{Identifier, Span};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tendril::StrTendril;
|
use tendril::StrTendril;
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct Annotation {
|
pub struct Annotation {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub name: Identifier,
|
pub name: Identifier,
|
||||||
|
@ -35,6 +35,12 @@ impl ReducerError {
|
|||||||
ReducerError::Error(FormattedError::new_from_span(message, span))
|
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 {
|
pub fn impossible_console_assert_call(span: &Span) -> Self {
|
||||||
let message = "Console::Assert cannot be matched here, its handled in another case.".to_string();
|
let message = "Console::Assert cannot be matched here, its handled in another case.".to_string();
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ pub enum ValueExpression {
|
|||||||
// todo: deserialize values here
|
// todo: deserialize values here
|
||||||
Address(#[serde(with = "crate::common::tendril_json")] StrTendril, Span),
|
Address(#[serde(with = "crate::common::tendril_json")] StrTendril, Span),
|
||||||
Boolean(#[serde(with = "crate::common::tendril_json")] StrTendril, Span),
|
Boolean(#[serde(with = "crate::common::tendril_json")] StrTendril, Span),
|
||||||
|
Char(char, Span),
|
||||||
Field(#[serde(with = "crate::common::tendril_json")] StrTendril, Span),
|
Field(#[serde(with = "crate::common::tendril_json")] StrTendril, Span),
|
||||||
Group(Box<GroupValue>),
|
Group(Box<GroupValue>),
|
||||||
Implicit(#[serde(with = "crate::common::tendril_json")] StrTendril, Span),
|
Implicit(#[serde(with = "crate::common::tendril_json")] StrTendril, Span),
|
||||||
@ -32,6 +33,7 @@ pub enum ValueExpression {
|
|||||||
#[serde(with = "crate::common::tendril_json")] StrTendril,
|
#[serde(with = "crate::common::tendril_json")] StrTendril,
|
||||||
Span,
|
Span,
|
||||||
),
|
),
|
||||||
|
String(String, Span),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ValueExpression {
|
impl fmt::Display for ValueExpression {
|
||||||
@ -40,10 +42,12 @@ impl fmt::Display for ValueExpression {
|
|||||||
match &self {
|
match &self {
|
||||||
Address(address, _) => write!(f, "{}", address),
|
Address(address, _) => write!(f, "{}", address),
|
||||||
Boolean(boolean, _) => write!(f, "{}", boolean),
|
Boolean(boolean, _) => write!(f, "{}", boolean),
|
||||||
|
Char(character, _) => write!(f, "{}", character),
|
||||||
Field(field, _) => write!(f, "{}", field),
|
Field(field, _) => write!(f, "{}", field),
|
||||||
Implicit(implicit, _) => write!(f, "{}", implicit),
|
Implicit(implicit, _) => write!(f, "{}", implicit),
|
||||||
Integer(value, type_, _) => write!(f, "{}{}", value, type_),
|
Integer(value, type_, _) => write!(f, "{}{}", value, type_),
|
||||||
Group(group) => write!(f, "{}", group),
|
Group(group) => write!(f, "{}", group),
|
||||||
|
String(string, _) => write!(f, "{}", string),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -52,7 +56,13 @@ impl Node for ValueExpression {
|
|||||||
fn span(&self) -> &Span {
|
fn span(&self) -> &Span {
|
||||||
use ValueExpression::*;
|
use ValueExpression::*;
|
||||||
match &self {
|
match &self {
|
||||||
Address(_, span) | Boolean(_, span) | Field(_, span) | Implicit(_, span) | Integer(_, _, span) => span,
|
Address(_, span)
|
||||||
|
| Boolean(_, span)
|
||||||
|
| Char(_, span)
|
||||||
|
| Field(_, span)
|
||||||
|
| Implicit(_, span)
|
||||||
|
| Integer(_, _, span)
|
||||||
|
| String(_, span) => span,
|
||||||
Group(group) => match &**group {
|
Group(group) => match &**group {
|
||||||
GroupValue::Single(_, span) | GroupValue::Tuple(GroupTuple { span, .. }) => span,
|
GroupValue::Single(_, span) | GroupValue::Tuple(GroupTuple { span, .. }) => span,
|
||||||
},
|
},
|
||||||
@ -62,9 +72,13 @@ impl Node for ValueExpression {
|
|||||||
fn set_span(&mut self, new_span: Span) {
|
fn set_span(&mut self, new_span: Span) {
|
||||||
use ValueExpression::*;
|
use ValueExpression::*;
|
||||||
match self {
|
match self {
|
||||||
Address(_, span) | Boolean(_, span) | Field(_, span) | Implicit(_, span) | Integer(_, _, span) => {
|
Address(_, span)
|
||||||
*span = new_span
|
| Boolean(_, span)
|
||||||
}
|
| Char(_, span)
|
||||||
|
| Field(_, span)
|
||||||
|
| Implicit(_, span)
|
||||||
|
| Integer(_, _, span)
|
||||||
|
| String(_, span) => *span = new_span,
|
||||||
Group(group) => match &mut **group {
|
Group(group) => match &mut **group {
|
||||||
GroupValue::Single(_, span) | GroupValue::Tuple(GroupTuple { span, .. }) => *span = new_span,
|
GroupValue::Single(_, span) | GroupValue::Tuple(GroupTuple { span, .. }) => *span = new_span,
|
||||||
},
|
},
|
||||||
|
@ -17,9 +17,18 @@
|
|||||||
use crate::{ArrayDimensions, GroupValue};
|
use crate::{ArrayDimensions, GroupValue};
|
||||||
use leo_input::{
|
use leo_input::{
|
||||||
errors::InputParserError,
|
errors::InputParserError,
|
||||||
expressions::{ArrayInitializerExpression, ArrayInlineExpression, Expression, TupleExpression},
|
expressions::{ArrayInitializerExpression, ArrayInlineExpression, Expression, StringExpression, TupleExpression},
|
||||||
types::{ArrayType, DataType, IntegerType, TupleType, Type},
|
types::{ArrayType, CharType, DataType, IntegerType, TupleType, Type},
|
||||||
values::{Address, AddressValue, BooleanValue, FieldValue, GroupValue as InputGroupValue, NumberValue, Value},
|
values::{
|
||||||
|
Address,
|
||||||
|
AddressValue,
|
||||||
|
BooleanValue,
|
||||||
|
CharValue,
|
||||||
|
FieldValue,
|
||||||
|
GroupValue as InputGroupValue,
|
||||||
|
NumberValue,
|
||||||
|
Value,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use pest::Span;
|
use pest::Span;
|
||||||
|
|
||||||
@ -29,6 +38,7 @@ use std::fmt;
|
|||||||
pub enum InputValue {
|
pub enum InputValue {
|
||||||
Address(String),
|
Address(String),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
|
Char(char),
|
||||||
Field(String),
|
Field(String),
|
||||||
Group(GroupValue),
|
Group(GroupValue),
|
||||||
Integer(IntegerType, String),
|
Integer(IntegerType, String),
|
||||||
@ -53,6 +63,11 @@ impl InputValue {
|
|||||||
Ok(InputValue::Boolean(boolean))
|
Ok(InputValue::Boolean(boolean))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn from_char(character: CharValue) -> Result<Self, InputParserError> {
|
||||||
|
let character = character.value.inner()?;
|
||||||
|
Ok(InputValue::Char(character))
|
||||||
|
}
|
||||||
|
|
||||||
fn from_number(integer_type: IntegerType, number: String) -> Self {
|
fn from_number(integer_type: IntegerType, number: String) -> Self {
|
||||||
InputValue::Integer(integer_type, number)
|
InputValue::Integer(integer_type, number)
|
||||||
}
|
}
|
||||||
@ -69,6 +84,7 @@ impl InputValue {
|
|||||||
match data_type {
|
match data_type {
|
||||||
DataType::Address(_) => Err(InputParserError::implicit_type(data_type, implicit)),
|
DataType::Address(_) => Err(InputParserError::implicit_type(data_type, implicit)),
|
||||||
DataType::Boolean(_) => Err(InputParserError::implicit_type(data_type, implicit)),
|
DataType::Boolean(_) => Err(InputParserError::implicit_type(data_type, implicit)),
|
||||||
|
DataType::Char(_) => Err(InputParserError::implicit_type(data_type, implicit)),
|
||||||
DataType::Integer(integer_type) => Ok(InputValue::from_number(integer_type, implicit.to_string())),
|
DataType::Integer(integer_type) => Ok(InputValue::from_number(integer_type, implicit.to_string())),
|
||||||
DataType::Group(_) => Err(InputParserError::implicit_group(implicit)),
|
DataType::Group(_) => Err(InputParserError::implicit_group(implicit)),
|
||||||
DataType::Field(_) => Ok(InputValue::Field(implicit.to_string())),
|
DataType::Field(_) => Ok(InputValue::Field(implicit.to_string())),
|
||||||
@ -79,6 +95,7 @@ impl InputValue {
|
|||||||
match (data_type, value) {
|
match (data_type, value) {
|
||||||
(DataType::Address(_), Value::Address(address)) => Ok(InputValue::from_address_value(address)),
|
(DataType::Address(_), Value::Address(address)) => Ok(InputValue::from_address_value(address)),
|
||||||
(DataType::Boolean(_), Value::Boolean(boolean)) => InputValue::from_boolean(boolean),
|
(DataType::Boolean(_), Value::Boolean(boolean)) => InputValue::from_boolean(boolean),
|
||||||
|
(DataType::Char(_), Value::Char(character)) => InputValue::from_char(character),
|
||||||
(DataType::Integer(integer_type), Value::Integer(integer)) => {
|
(DataType::Integer(integer_type), Value::Integer(integer)) => {
|
||||||
Ok(InputValue::from_number(integer_type, integer.to_string()))
|
Ok(InputValue::from_number(integer_type, integer.to_string()))
|
||||||
}
|
}
|
||||||
@ -98,11 +115,60 @@ impl InputValue {
|
|||||||
(Type::Array(array_type), Expression::ArrayInitializer(initializer)) => {
|
(Type::Array(array_type), Expression::ArrayInitializer(initializer)) => {
|
||||||
InputValue::from_array_initializer(array_type, 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::Tuple(tuple_type), Expression::Tuple(tuple)) => InputValue::from_tuple(tuple_type, tuple),
|
||||||
(type_, expression) => Err(InputParserError::expression_type_mismatch(type_, expression)),
|
(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());
|
||||||
|
assert!(matches!(*array_type.type_, Type::Basic(DataType::Char(CharType {}))));
|
||||||
|
|
||||||
|
// 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`.
|
/// Returns a new `InputValue` from the given `ArrayType` and `ArrayInlineExpression`.
|
||||||
///
|
///
|
||||||
@ -303,6 +369,7 @@ impl fmt::Display for InputValue {
|
|||||||
match self {
|
match self {
|
||||||
InputValue::Address(ref address) => write!(f, "{}", address),
|
InputValue::Address(ref address) => write!(f, "{}", address),
|
||||||
InputValue::Boolean(ref boolean) => write!(f, "{}", boolean),
|
InputValue::Boolean(ref boolean) => write!(f, "{}", boolean),
|
||||||
|
InputValue::Char(ref character) => write!(f, "{}", character),
|
||||||
InputValue::Group(ref group) => write!(f, "{}", group),
|
InputValue::Group(ref group) => write!(f, "{}", group),
|
||||||
InputValue::Field(ref field) => write!(f, "{}", field),
|
InputValue::Field(ref field) => write!(f, "{}", field),
|
||||||
InputValue::Integer(ref type_, ref number) => write!(f, "{}{:?}", number, type_),
|
InputValue::Integer(ref type_, ref number) => write!(f, "{}{:?}", number, type_),
|
||||||
|
@ -485,6 +485,20 @@ impl ReconstructingReducer for Canonicalizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reduce_string(&mut self, string: &str, span: &Span) -> Result<Expression, ReducerError> {
|
||||||
|
let mut elements = Vec::new();
|
||||||
|
for character in string.chars() {
|
||||||
|
elements.push(SpreadOrExpression::Expression(Expression::Value(
|
||||||
|
ValueExpression::Char(character, span.clone()),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Expression::ArrayInline(ArrayInlineExpression {
|
||||||
|
elements,
|
||||||
|
span: span.clone(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
fn reduce_array_init(
|
fn reduce_array_init(
|
||||||
&mut self,
|
&mut self,
|
||||||
array_init: &ArrayInitExpression,
|
array_init: &ArrayInitExpression,
|
||||||
|
@ -51,7 +51,7 @@ impl<R: ReconstructingReducer> ReconstructingDirector<R> {
|
|||||||
pub fn reduce_expression(&mut self, expression: &Expression) -> Result<Expression, ReducerError> {
|
pub fn reduce_expression(&mut self, expression: &Expression) -> Result<Expression, ReducerError> {
|
||||||
let new = match expression {
|
let new = match expression {
|
||||||
Expression::Identifier(identifier) => Expression::Identifier(self.reduce_identifier(&identifier)?),
|
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::Binary(binary) => Expression::Binary(self.reduce_binary(&binary)?),
|
||||||
Expression::Unary(unary) => Expression::Unary(self.reduce_unary(&unary)?),
|
Expression::Unary(unary) => Expression::Unary(self.reduce_unary(&unary)?),
|
||||||
Expression::Ternary(ternary) => Expression::Ternary(self.reduce_ternary(&ternary)?),
|
Expression::Ternary(ternary) => Expression::Ternary(self.reduce_ternary(&ternary)?),
|
||||||
@ -100,12 +100,17 @@ impl<R: ReconstructingReducer> ReconstructingDirector<R> {
|
|||||||
self.reducer.reduce_group_value(group_value, new)
|
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: &str, 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 {
|
let new = match value {
|
||||||
ValueExpression::Group(group_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)
|
self.reducer.reduce_value(value, new)
|
||||||
|
@ -51,11 +51,14 @@ pub trait ReconstructingReducer {
|
|||||||
Ok(new)
|
Ok(new)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reduce_value(
|
fn reduce_string(&mut self, string: &str, span: &Span) -> Result<Expression, ReducerError> {
|
||||||
&mut self,
|
Ok(Expression::Value(ValueExpression::String(
|
||||||
_value: &ValueExpression,
|
string.to_string(),
|
||||||
new: ValueExpression,
|
span.clone(),
|
||||||
) -> Result<ValueExpression, ReducerError> {
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reduce_value(&mut self, _value: &ValueExpression, new: Expression) -> Result<Expression, ReducerError> {
|
||||||
Ok(new)
|
Ok(new)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,33 @@ pub enum FormatStringPart {
|
|||||||
Container,
|
Container,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FormatStringPart {
|
||||||
|
pub fn from_string(string: String) -> Vec<Self> {
|
||||||
|
let mut parts = Vec::new();
|
||||||
|
let mut in_container = false;
|
||||||
|
let mut start = 0;
|
||||||
|
for (index, character) in string.chars().enumerate() {
|
||||||
|
match character {
|
||||||
|
'{' if !in_container => {
|
||||||
|
parts.push(FormatStringPart::Const(string[start..index].into()));
|
||||||
|
start = index;
|
||||||
|
in_container = true;
|
||||||
|
}
|
||||||
|
'}' if in_container => {
|
||||||
|
in_container = false;
|
||||||
|
parts.push(FormatStringPart::Container);
|
||||||
|
}
|
||||||
|
_ if in_container => {
|
||||||
|
in_container = false;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
|
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
|
||||||
pub struct FormatString {
|
pub struct FormatString {
|
||||||
pub parts: Vec<FormatStringPart>,
|
pub parts: Vec<FormatStringPart>,
|
||||||
@ -41,8 +68,8 @@ impl fmt::Display for FormatString {
|
|||||||
self.parts
|
self.parts
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| match x {
|
.map(|x| match x {
|
||||||
FormatStringPart::Const(x) => x,
|
FormatStringPart::Const(x) => x.to_string(),
|
||||||
FormatStringPart::Container => "{}",
|
FormatStringPart::Container => "{}".to_string(),
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join("")
|
.join("")
|
||||||
|
@ -31,6 +31,7 @@ pub enum Type {
|
|||||||
// Data types
|
// Data types
|
||||||
Address,
|
Address,
|
||||||
Boolean,
|
Boolean,
|
||||||
|
Char,
|
||||||
Field,
|
Field,
|
||||||
Group,
|
Group,
|
||||||
IntegerType(IntegerType),
|
IntegerType(IntegerType),
|
||||||
@ -66,6 +67,7 @@ impl Type {
|
|||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Type::Address, Type::Address) => true,
|
(Type::Address, Type::Address) => true,
|
||||||
(Type::Boolean, Type::Boolean) => true,
|
(Type::Boolean, Type::Boolean) => true,
|
||||||
|
(Type::Char, Type::Char) => true,
|
||||||
(Type::Field, Type::Field) => true,
|
(Type::Field, Type::Field) => true,
|
||||||
(Type::Group, Type::Group) => true,
|
(Type::Group, Type::Group) => true,
|
||||||
(Type::IntegerType(left), Type::IntegerType(right)) => left.eq(&right),
|
(Type::IntegerType(left), Type::IntegerType(right)) => left.eq(&right),
|
||||||
@ -108,6 +110,7 @@ impl From<InputDataType> for Type {
|
|||||||
match data_type {
|
match data_type {
|
||||||
InputDataType::Address(_type) => Type::Address,
|
InputDataType::Address(_type) => Type::Address,
|
||||||
InputDataType::Boolean(_type) => Type::Boolean,
|
InputDataType::Boolean(_type) => Type::Boolean,
|
||||||
|
InputDataType::Char(_type) => Type::Char,
|
||||||
InputDataType::Field(_type) => Type::Field,
|
InputDataType::Field(_type) => Type::Field,
|
||||||
InputDataType::Group(_type) => Type::Group,
|
InputDataType::Group(_type) => Type::Group,
|
||||||
InputDataType::Integer(type_) => Type::IntegerType(IntegerType::from(type_)),
|
InputDataType::Integer(type_) => Type::IntegerType(IntegerType::from(type_)),
|
||||||
@ -147,6 +150,7 @@ impl fmt::Display for Type {
|
|||||||
match *self {
|
match *self {
|
||||||
Type::Address => write!(f, "address"),
|
Type::Address => write!(f, "address"),
|
||||||
Type::Boolean => write!(f, "bool"),
|
Type::Boolean => write!(f, "bool"),
|
||||||
|
Type::Char => write!(f, "char"),
|
||||||
Type::Field => write!(f, "field"),
|
Type::Field => write!(f, "field"),
|
||||||
Type::Group => write!(f, "group"),
|
Type::Group => write!(f, "group"),
|
||||||
Type::IntegerType(ref integer_type) => write!(f, "{}", integer_type),
|
Type::IntegerType(ref integer_type) => write!(f, "{}", integer_type),
|
||||||
|
@ -51,8 +51,8 @@ impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
|
|||||||
let mut parameters = executed_containers.iter();
|
let mut parameters = executed_containers.iter();
|
||||||
for part in formatted.parts.iter() {
|
for part in formatted.parts.iter() {
|
||||||
match part {
|
match part {
|
||||||
FormatStringPart::Const(c) => out.push(&**c),
|
FormatStringPart::Const(c) => out.push(c.to_string()),
|
||||||
FormatStringPart::Container => out.push(&**parameters.next().unwrap()),
|
FormatStringPart::Container => out.push(parameters.next().unwrap().to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,16 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use crate::errors::{AddressError, BooleanError, FieldError, FunctionError, GroupError, IntegerError, ValueError};
|
use crate::errors::{
|
||||||
|
AddressError,
|
||||||
|
BooleanError,
|
||||||
|
CharError,
|
||||||
|
FieldError,
|
||||||
|
FunctionError,
|
||||||
|
GroupError,
|
||||||
|
IntegerError,
|
||||||
|
ValueError,
|
||||||
|
};
|
||||||
use leo_ast::{FormattedError, Identifier, LeoError, Span};
|
use leo_ast::{FormattedError, Identifier, LeoError, Span};
|
||||||
use snarkvm_r1cs::SynthesisError;
|
use snarkvm_r1cs::SynthesisError;
|
||||||
|
|
||||||
@ -26,6 +35,9 @@ pub enum ExpressionError {
|
|||||||
#[error("{}", _0)]
|
#[error("{}", _0)]
|
||||||
BooleanError(#[from] BooleanError),
|
BooleanError(#[from] BooleanError),
|
||||||
|
|
||||||
|
#[error("{}", _0)]
|
||||||
|
CharError(#[from] CharError),
|
||||||
|
|
||||||
#[error("{}", _0)]
|
#[error("{}", _0)]
|
||||||
Error(#[from] FormattedError),
|
Error(#[from] FormattedError),
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
use crate::errors::{
|
use crate::errors::{
|
||||||
AddressError,
|
AddressError,
|
||||||
BooleanError,
|
BooleanError,
|
||||||
|
CharError,
|
||||||
ExpressionError,
|
ExpressionError,
|
||||||
FieldError,
|
FieldError,
|
||||||
GroupError,
|
GroupError,
|
||||||
@ -36,6 +37,9 @@ pub enum FunctionError {
|
|||||||
#[error("{}", _0)]
|
#[error("{}", _0)]
|
||||||
BooleanError(#[from] BooleanError),
|
BooleanError(#[from] BooleanError),
|
||||||
|
|
||||||
|
#[error("{}", _0)]
|
||||||
|
CharError(#[from] CharError),
|
||||||
|
|
||||||
#[error("{}", _0)]
|
#[error("{}", _0)]
|
||||||
ExpressionError(#[from] ExpressionError),
|
ExpressionError(#[from] ExpressionError),
|
||||||
|
|
||||||
|
41
compiler/src/errors/value/char.rs
Normal file
41
compiler/src/errors/value/char.rs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||||
|
// This file is part of the Leo library.
|
||||||
|
|
||||||
|
// The Leo library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// The Leo library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use crate::errors::FieldError;
|
||||||
|
use leo_ast::{FormattedError, LeoError, Span};
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum CharError {
|
||||||
|
#[error("{}", _0)]
|
||||||
|
Error(#[from] FormattedError),
|
||||||
|
|
||||||
|
#[error("{}", _0)]
|
||||||
|
FieldError(#[from] FieldError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LeoError for CharError {}
|
||||||
|
|
||||||
|
impl CharError {
|
||||||
|
fn new_from_span(message: String, span: &Span) -> Self {
|
||||||
|
CharError::Error(FormattedError::new_from_span(message, span))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invalid_char(actual: String, span: &Span) -> Self {
|
||||||
|
let message = format!("expected char element input type, found `{}`", actual);
|
||||||
|
|
||||||
|
Self::new_from_span(message, span)
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,9 @@ pub use self::address::*;
|
|||||||
pub mod boolean;
|
pub mod boolean;
|
||||||
pub use self::boolean::*;
|
pub use self::boolean::*;
|
||||||
|
|
||||||
|
pub mod char;
|
||||||
|
pub use self::char::*;
|
||||||
|
|
||||||
pub mod field;
|
pub mod field;
|
||||||
pub use self::field::*;
|
pub use self::field::*;
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ use crate::{
|
|||||||
program::ConstrainedProgram,
|
program::ConstrainedProgram,
|
||||||
relational::*,
|
relational::*,
|
||||||
resolve_core_circuit,
|
resolve_core_circuit,
|
||||||
value::{Address, ConstrainedValue, Integer},
|
value::{Address, Char, ConstrainedValue, Integer},
|
||||||
FieldType,
|
FieldType,
|
||||||
GroupType,
|
GroupType,
|
||||||
};
|
};
|
||||||
@ -43,6 +43,9 @@ impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
|
|||||||
Ok(match value {
|
Ok(match value {
|
||||||
ConstValue::Address(value) => ConstrainedValue::Address(Address::constant(value.to_string(), span)?),
|
ConstValue::Address(value) => ConstrainedValue::Address(Address::constant(value.to_string(), span)?),
|
||||||
ConstValue::Boolean(value) => ConstrainedValue::Boolean(Boolean::Constant(*value)),
|
ConstValue::Boolean(value) => ConstrainedValue::Boolean(Boolean::Constant(*value)),
|
||||||
|
ConstValue::Char(value) => {
|
||||||
|
ConstrainedValue::Char(Char::constant(cs, *value, format!("{}", *value as u32), span)?)
|
||||||
|
}
|
||||||
ConstValue::Field(value) => ConstrainedValue::Field(FieldType::constant(cs, value.to_string(), span)?),
|
ConstValue::Field(value) => ConstrainedValue::Field(FieldType::constant(cs, value.to_string(), span)?),
|
||||||
ConstValue::Group(value) => ConstrainedValue::Group(G::constant(value, span)?),
|
ConstValue::Group(value) => ConstrainedValue::Group(G::constant(value, span)?),
|
||||||
ConstValue::Int(value) => ConstrainedValue::Integer(Integer::new(value)),
|
ConstValue::Int(value) => ConstrainedValue::Integer(Integer::new(value)),
|
||||||
|
@ -39,6 +39,10 @@ pub fn evaluate_eq<'a, F: PrimeField, G: GroupType<F>, CS: ConstraintSystem<F>>(
|
|||||||
let unique_namespace = cs.ns(|| namespace_string);
|
let unique_namespace = cs.ns(|| namespace_string);
|
||||||
bool_1.evaluate_equal(unique_namespace, &bool_2)
|
bool_1.evaluate_equal(unique_namespace, &bool_2)
|
||||||
}
|
}
|
||||||
|
(ConstrainedValue::Char(char_1), ConstrainedValue::Char(char_2)) => {
|
||||||
|
let unique_namespace = cs.ns(|| namespace_string);
|
||||||
|
char_1.evaluate_equal(unique_namespace, &char_2)
|
||||||
|
}
|
||||||
(ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => {
|
(ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => {
|
||||||
let unique_namespace = cs.ns(|| namespace_string);
|
let unique_namespace = cs.ns(|| namespace_string);
|
||||||
num_1.evaluate_equal(unique_namespace, &num_2)
|
num_1.evaluate_equal(unique_namespace, &num_2)
|
||||||
|
@ -22,10 +22,12 @@ use crate::{
|
|||||||
program::ConstrainedProgram,
|
program::ConstrainedProgram,
|
||||||
value::{
|
value::{
|
||||||
boolean::input::bool_from_input,
|
boolean::input::bool_from_input,
|
||||||
|
char::char_from_input,
|
||||||
field::input::field_from_input,
|
field::input::field_from_input,
|
||||||
group::input::group_from_input,
|
group::input::group_from_input,
|
||||||
ConstrainedValue,
|
ConstrainedValue,
|
||||||
},
|
},
|
||||||
|
Char,
|
||||||
FieldType,
|
FieldType,
|
||||||
GroupType,
|
GroupType,
|
||||||
Integer,
|
Integer,
|
||||||
@ -49,6 +51,7 @@ impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
|
|||||||
match type_ {
|
match type_ {
|
||||||
Type::Address => Ok(Address::from_input(cs, name, input_option, span)?),
|
Type::Address => Ok(Address::from_input(cs, name, input_option, span)?),
|
||||||
Type::Boolean => Ok(bool_from_input(cs, name, input_option, span)?),
|
Type::Boolean => Ok(bool_from_input(cs, name, input_option, span)?),
|
||||||
|
Type::Char => Ok(char_from_input(cs, name, input_option, span)?),
|
||||||
Type::Field => Ok(field_from_input(cs, name, input_option, span)?),
|
Type::Field => Ok(field_from_input(cs, name, input_option, span)?),
|
||||||
Type::Group => Ok(group_from_input(cs, name, input_option, span)?),
|
Type::Group => Ok(group_from_input(cs, name, input_option, span)?),
|
||||||
Type::Integer(integer_type) => Ok(ConstrainedValue::Integer(Integer::from_input(
|
Type::Integer(integer_type) => Ok(ConstrainedValue::Integer(Integer::from_input(
|
||||||
@ -69,7 +72,7 @@ impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
|
|||||||
impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
|
impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
|
||||||
pub fn constant_main_function_input<CS: ConstraintSystem<F>>(
|
pub fn constant_main_function_input<CS: ConstraintSystem<F>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
_cs: &mut CS,
|
cs: &mut CS,
|
||||||
type_: &Type,
|
type_: &Type,
|
||||||
name: &str,
|
name: &str,
|
||||||
input_option: Option<InputValue>,
|
input_option: Option<InputValue>,
|
||||||
@ -80,8 +83,14 @@ impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
|
|||||||
match (type_, input) {
|
match (type_, input) {
|
||||||
(Type::Address, InputValue::Address(addr)) => Ok(ConstrainedValue::Address(Address::constant(addr, span)?)),
|
(Type::Address, InputValue::Address(addr)) => Ok(ConstrainedValue::Address(Address::constant(addr, span)?)),
|
||||||
(Type::Boolean, InputValue::Boolean(value)) => Ok(ConstrainedValue::Boolean(Boolean::constant(value))),
|
(Type::Boolean, InputValue::Boolean(value)) => Ok(ConstrainedValue::Boolean(Boolean::constant(value))),
|
||||||
|
(Type::Char, InputValue::Char(character)) => Ok(ConstrainedValue::Char(Char::constant(
|
||||||
|
cs,
|
||||||
|
character,
|
||||||
|
format!("{}", character as u32),
|
||||||
|
span,
|
||||||
|
)?)),
|
||||||
(Type::Field, InputValue::Field(value)) => {
|
(Type::Field, InputValue::Field(value)) => {
|
||||||
Ok(ConstrainedValue::Field(FieldType::constant(_cs, value, span)?))
|
Ok(ConstrainedValue::Field(FieldType::constant(cs, value, span)?))
|
||||||
}
|
}
|
||||||
(Type::Group, InputValue::Group(value)) => Ok(ConstrainedValue::Group(G::constant(&value.into(), span)?)),
|
(Type::Group, InputValue::Group(value)) => Ok(ConstrainedValue::Group(G::constant(&value.into(), span)?)),
|
||||||
(Type::Integer(integer_type), InputValue::Integer(_, value)) => Ok(ConstrainedValue::Integer(
|
(Type::Integer(integer_type), InputValue::Integer(_, value)) => Ok(ConstrainedValue::Integer(
|
||||||
@ -99,7 +108,7 @@ impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
|
|||||||
Ok(ConstrainedValue::Array(
|
Ok(ConstrainedValue::Array(
|
||||||
values
|
values
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| self.constant_main_function_input(_cs, type_, name, Some(x.clone()), span))
|
.map(|x| self.constant_main_function_input(cs, type_, name, Some(x.clone()), span))
|
||||||
.collect::<Result<Vec<_>, _>>()?,
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -113,7 +122,7 @@ impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
|
|||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, x)| {
|
.map(|(i, x)| {
|
||||||
self.constant_main_function_input(_cs, types.get(i).unwrap(), name, Some(x.clone()), span)
|
self.constant_main_function_input(cs, types.get(i).unwrap(), name, Some(x.clone()), span)
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, _>>()?,
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
))
|
))
|
||||||
|
@ -47,7 +47,10 @@ impl fmt::Display for Output {
|
|||||||
writeln!(f, "[{}]", REGISTERS_VARIABLE_NAME)?;
|
writeln!(f, "[{}]", REGISTERS_VARIABLE_NAME)?;
|
||||||
// format: "token_id: u64 = 1u64;"
|
// format: "token_id: u64 = 1u64;"
|
||||||
for (name, register) in self.registers.iter() {
|
for (name, register) in self.registers.iter() {
|
||||||
writeln!(f, "{}: {} = {};", name, register.type_, register.value)?;
|
match register.type_.as_str() {
|
||||||
|
"char" => writeln!(f, "{}: {} = '{}';", name, register.type_, register.value)?,
|
||||||
|
_ => writeln!(f, "{}: {} = {};", name, register.type_, register.value)?,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -152,9 +152,7 @@ impl<R: ReconstructingReducer, O: CombinerOptions> CombineAstAsgDirector<R, O> {
|
|||||||
asg: &AsgExpression,
|
asg: &AsgExpression,
|
||||||
) -> Result<AstExpression, ReducerError> {
|
) -> Result<AstExpression, ReducerError> {
|
||||||
let new = match (ast, asg) {
|
let new = match (ast, asg) {
|
||||||
(AstExpression::Value(value), AsgExpression::Constant(const_)) => {
|
(AstExpression::Value(value), AsgExpression::Constant(const_)) => self.reduce_value(&value, &const_)?,
|
||||||
AstExpression::Value(self.reduce_value(&value, &const_)?)
|
|
||||||
}
|
|
||||||
(AstExpression::Binary(ast), AsgExpression::Binary(asg)) => {
|
(AstExpression::Binary(ast), AsgExpression::Binary(asg)) => {
|
||||||
AstExpression::Binary(self.reduce_binary(&ast, &asg)?)
|
AstExpression::Binary(self.reduce_binary(&ast, &asg)?)
|
||||||
}
|
}
|
||||||
@ -404,7 +402,7 @@ impl<R: ReconstructingReducer, O: CombinerOptions> CombineAstAsgDirector<R, O> {
|
|||||||
self.ast_reducer.reduce_unary(ast, inner, ast.op.clone())
|
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();
|
let mut new = ast.clone();
|
||||||
|
|
||||||
if self.options.type_inference_enabled() {
|
if self.options.type_inference_enabled() {
|
||||||
@ -433,12 +431,22 @@ impl<R: ReconstructingReducer, O: CombinerOptions> CombineAstAsgDirector<R, O> {
|
|||||||
ConstValue::Boolean(_) => {
|
ConstValue::Boolean(_) => {
|
||||||
new = ValueExpression::Boolean(tendril.clone(), span.clone());
|
new = ValueExpression::Boolean(tendril.clone(), span.clone());
|
||||||
}
|
}
|
||||||
|
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,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => unimplemented!(), // impossible?
|
_ => unimplemented!(), // impossible?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ast_reducer.reduce_value(ast, new)
|
self.ast_reducer.reduce_value(ast, AstExpression::Value(new))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reduce_variable_ref(
|
pub fn reduce_variable_ref(
|
||||||
|
167
compiler/src/value/char/char.rs
Normal file
167
compiler/src/value/char/char.rs
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||||
|
// This file is part of the Leo library.
|
||||||
|
|
||||||
|
// The Leo library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// The Leo library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
errors::CharError,
|
||||||
|
value::{field::input::allocate_field, ConstrainedValue},
|
||||||
|
FieldType,
|
||||||
|
GroupType,
|
||||||
|
};
|
||||||
|
|
||||||
|
use leo_ast::{InputValue, Span};
|
||||||
|
use snarkvm_fields::PrimeField;
|
||||||
|
use snarkvm_gadgets::utilities::{
|
||||||
|
bits::comparator::{ComparatorGadget, EvaluateLtGadget},
|
||||||
|
boolean::Boolean,
|
||||||
|
eq::{ConditionalEqGadget, EqGadget, EvaluateEqGadget, NEqGadget},
|
||||||
|
select::CondSelectGadget,
|
||||||
|
};
|
||||||
|
use snarkvm_r1cs::{ConstraintSystem, SynthesisError};
|
||||||
|
|
||||||
|
/// A char
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Char<F: PrimeField> {
|
||||||
|
pub character: char,
|
||||||
|
pub field: FieldType<F>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: PrimeField> Char<F> {
|
||||||
|
pub fn constant<CS: ConstraintSystem<F>>(
|
||||||
|
cs: CS,
|
||||||
|
character: char,
|
||||||
|
field: String,
|
||||||
|
span: &Span,
|
||||||
|
) -> Result<Self, CharError> {
|
||||||
|
Ok(Self {
|
||||||
|
character,
|
||||||
|
field: FieldType::constant(cs, field, span)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: PrimeField> PartialEq for Char<F> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.field.eq(&other.field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: PrimeField> Eq for Char<F> {}
|
||||||
|
|
||||||
|
impl<F: PrimeField> PartialOrd for Char<F> {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
self.field.partial_cmp(&other.field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: PrimeField> EvaluateLtGadget<F> for Char<F> {
|
||||||
|
fn less_than<CS: ConstraintSystem<F>>(&self, cs: CS, other: &Self) -> Result<Boolean, SynthesisError> {
|
||||||
|
self.field.less_than(cs, &other.field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: PrimeField> ComparatorGadget<F> for Char<F> {}
|
||||||
|
|
||||||
|
impl<F: PrimeField> EvaluateEqGadget<F> for Char<F> {
|
||||||
|
fn evaluate_equal<CS: ConstraintSystem<F>>(&self, cs: CS, other: &Self) -> Result<Boolean, SynthesisError> {
|
||||||
|
self.field.evaluate_equal(cs, &other.field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: PrimeField> EqGadget<F> for Char<F> {}
|
||||||
|
|
||||||
|
impl<F: PrimeField> ConditionalEqGadget<F> for Char<F> {
|
||||||
|
fn conditional_enforce_equal<CS: ConstraintSystem<F>>(
|
||||||
|
&self,
|
||||||
|
cs: CS,
|
||||||
|
other: &Self,
|
||||||
|
condition: &Boolean,
|
||||||
|
) -> Result<(), SynthesisError> {
|
||||||
|
self.field.conditional_enforce_equal(cs, &other.field, condition)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cost() -> usize {
|
||||||
|
<FieldType<F> as ConditionalEqGadget<F>>::cost()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: PrimeField> NEqGadget<F> for Char<F> {
|
||||||
|
fn enforce_not_equal<CS: ConstraintSystem<F>>(&self, cs: CS, other: &Self) -> Result<(), SynthesisError> {
|
||||||
|
self.field.enforce_not_equal(cs, &other.field)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cost() -> usize {
|
||||||
|
<FieldType<F> as NEqGadget<F>>::cost()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: PrimeField> CondSelectGadget<F> for Char<F> {
|
||||||
|
fn conditionally_select<CS: ConstraintSystem<F>>(
|
||||||
|
cs: CS,
|
||||||
|
cond: &Boolean,
|
||||||
|
first: &Self,
|
||||||
|
second: &Self,
|
||||||
|
) -> Result<Self, SynthesisError> {
|
||||||
|
let field = FieldType::<F>::conditionally_select(cs, cond, &first.field, &second.field)?;
|
||||||
|
|
||||||
|
if field == first.field {
|
||||||
|
return Ok(Char {
|
||||||
|
character: first.character,
|
||||||
|
field,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Char {
|
||||||
|
character: second.character,
|
||||||
|
field,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cost() -> usize {
|
||||||
|
<FieldType<F> as CondSelectGadget<F>>::cost()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn char_from_input<'a, F: PrimeField, G: GroupType<F>, CS: ConstraintSystem<F>>(
|
||||||
|
cs: &mut CS,
|
||||||
|
name: &str,
|
||||||
|
input_value: Option<InputValue>,
|
||||||
|
span: &Span,
|
||||||
|
) -> Result<ConstrainedValue<'a, F, G>, CharError> {
|
||||||
|
// Check that the parameter value is the correct type
|
||||||
|
let option = match input_value {
|
||||||
|
Some(input) => {
|
||||||
|
if let InputValue::Char(character) = input {
|
||||||
|
(character, Some((character as u32).to_string()))
|
||||||
|
} else {
|
||||||
|
return Err(CharError::invalid_char(input.to_string(), span));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => (' ', None),
|
||||||
|
};
|
||||||
|
|
||||||
|
let field = allocate_field(cs, name, option.1, span)?;
|
||||||
|
|
||||||
|
Ok(ConstrainedValue::Char(Char {
|
||||||
|
character: option.0,
|
||||||
|
field,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: PrimeField + std::fmt::Display> std::fmt::Display for Char<F> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.character)
|
||||||
|
}
|
||||||
|
}
|
20
compiler/src/value/char/mod.rs
Normal file
20
compiler/src/value/char/mod.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||||
|
// This file is part of the Leo library.
|
||||||
|
|
||||||
|
// The Leo library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// The Leo library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! An char value in a compiled Leo program.
|
||||||
|
|
||||||
|
pub mod char;
|
||||||
|
pub use self::char::*;
|
@ -26,8 +26,9 @@ use snarkvm_gadgets::{
|
|||||||
fields::FieldGadget,
|
fields::FieldGadget,
|
||||||
utilities::{
|
utilities::{
|
||||||
alloc::AllocGadget,
|
alloc::AllocGadget,
|
||||||
|
bits::comparator::{ComparatorGadget, EvaluateLtGadget},
|
||||||
boolean::Boolean,
|
boolean::Boolean,
|
||||||
eq::{ConditionalEqGadget, EqGadget, EvaluateEqGadget},
|
eq::{ConditionalEqGadget, EqGadget, EvaluateEqGadget, NEqGadget},
|
||||||
select::CondSelectGadget,
|
select::CondSelectGadget,
|
||||||
uint::UInt8,
|
uint::UInt8,
|
||||||
ToBitsBEGadget,
|
ToBitsBEGadget,
|
||||||
@ -36,8 +37,6 @@ use snarkvm_gadgets::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use snarkvm_r1cs::{ConstraintSystem, SynthesisError};
|
use snarkvm_r1cs::{ConstraintSystem, SynthesisError};
|
||||||
|
|
||||||
use snarkvm_gadgets::utilities::eq::NEqGadget;
|
|
||||||
use std::{borrow::Borrow, cmp::Ordering};
|
use std::{borrow::Borrow, cmp::Ordering};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -50,7 +49,7 @@ impl<F: PrimeField> FieldType<F> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a new `FieldType` from the given `String` or returns a `FieldError`.
|
/// Returns a new `FieldType` from the given `String` or returns a `FieldError`.
|
||||||
pub fn constant<CS: ConstraintSystem<F>>(_cs: CS, string: String, span: &Span) -> Result<Self, FieldError> {
|
pub fn constant<CS: ConstraintSystem<F>>(cs: CS, string: String, span: &Span) -> Result<Self, FieldError> {
|
||||||
let number_info = number_string_typing(&string);
|
let number_info = number_string_typing(&string);
|
||||||
|
|
||||||
let value = match number_info {
|
let value = match number_info {
|
||||||
@ -60,7 +59,7 @@ impl<F: PrimeField> FieldType<F> {
|
|||||||
(number, _) => F::from_str(&number).map_err(|_| FieldError::invalid_field(string.clone(), span))?,
|
(number, _) => F::from_str(&number).map_err(|_| FieldError::invalid_field(string.clone(), span))?,
|
||||||
};
|
};
|
||||||
|
|
||||||
let value = FpGadget::alloc_constant(_cs, || Ok(value)).map_err(|_| FieldError::invalid_field(string, span))?;
|
let value = FpGadget::alloc_constant(cs, || Ok(value)).map_err(|_| FieldError::invalid_field(string, span))?;
|
||||||
|
|
||||||
Ok(FieldType(value))
|
Ok(FieldType(value))
|
||||||
}
|
}
|
||||||
@ -171,6 +170,14 @@ impl<F: PrimeField> PartialOrd for FieldType<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<F: PrimeField> EvaluateLtGadget<F> for FieldType<F> {
|
||||||
|
fn less_than<CS: ConstraintSystem<F>>(&self, _cs: CS, _other: &Self) -> Result<Boolean, SynthesisError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: PrimeField> ComparatorGadget<F> for FieldType<F> {}
|
||||||
|
|
||||||
impl<F: PrimeField> EvaluateEqGadget<F> for FieldType<F> {
|
impl<F: PrimeField> EvaluateEqGadget<F> for FieldType<F> {
|
||||||
fn evaluate_equal<CS: ConstraintSystem<F>>(&self, cs: CS, other: &Self) -> Result<Boolean, SynthesisError> {
|
fn evaluate_equal<CS: ConstraintSystem<F>>(&self, cs: CS, other: &Self) -> Result<Boolean, SynthesisError> {
|
||||||
self.0.is_eq(cs, &other.0)
|
self.0.is_eq(cs, &other.0)
|
||||||
|
@ -21,6 +21,9 @@ pub use self::address::*;
|
|||||||
|
|
||||||
pub mod boolean;
|
pub mod boolean;
|
||||||
|
|
||||||
|
pub mod char;
|
||||||
|
pub use self::char::*;
|
||||||
|
|
||||||
pub mod field;
|
pub mod field;
|
||||||
pub use self::field::*;
|
pub use self::field::*;
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
//! The in memory stored value for a defined name in a compiled Leo program.
|
//! The in memory stored value for a defined name in a compiled Leo program.
|
||||||
|
|
||||||
use crate::{errors::ValueError, Address, FieldType, GroupType, Integer};
|
use crate::{errors::ValueError, Address, Char, FieldType, GroupType, Integer};
|
||||||
use leo_asg::{Circuit, Identifier, Span, Type};
|
use leo_asg::{Circuit, Identifier, Span, Type};
|
||||||
|
|
||||||
use snarkvm_fields::PrimeField;
|
use snarkvm_fields::PrimeField;
|
||||||
@ -32,6 +32,7 @@ pub enum ConstrainedValue<'a, F: PrimeField, G: GroupType<F>> {
|
|||||||
// Data types
|
// Data types
|
||||||
Address(Address),
|
Address(Address),
|
||||||
Boolean(Boolean),
|
Boolean(Boolean),
|
||||||
|
Char(Char<F>),
|
||||||
Field(FieldType<F>),
|
Field(FieldType<F>),
|
||||||
Group(G),
|
Group(G),
|
||||||
Integer(Integer),
|
Integer(Integer),
|
||||||
@ -52,6 +53,7 @@ impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedValue<'a, F, G> {
|
|||||||
// Data types
|
// Data types
|
||||||
ConstrainedValue::Address(_address) => Type::Address,
|
ConstrainedValue::Address(_address) => Type::Address,
|
||||||
ConstrainedValue::Boolean(_bool) => Type::Boolean,
|
ConstrainedValue::Boolean(_bool) => Type::Boolean,
|
||||||
|
ConstrainedValue::Char(_char) => Type::Char,
|
||||||
ConstrainedValue::Field(_field) => Type::Field,
|
ConstrainedValue::Field(_field) => Type::Field,
|
||||||
ConstrainedValue::Group(_group) => Type::Group,
|
ConstrainedValue::Group(_group) => Type::Group,
|
||||||
ConstrainedValue::Integer(integer) => Type::Integer(integer.get_type()),
|
ConstrainedValue::Integer(integer) => Type::Integer(integer.get_type()),
|
||||||
@ -90,20 +92,29 @@ impl<'a, F: PrimeField, G: GroupType<F>> fmt::Display for ConstrainedValue<'a, F
|
|||||||
.map(|v| v.to_string())
|
.map(|v| v.to_string())
|
||||||
.unwrap_or_else(|| "[allocated]".to_string())
|
.unwrap_or_else(|| "[allocated]".to_string())
|
||||||
),
|
),
|
||||||
|
ConstrainedValue::Char(ref value) => write!(f, "{}", value),
|
||||||
ConstrainedValue::Field(ref value) => write!(f, "{:?}", value),
|
ConstrainedValue::Field(ref value) => write!(f, "{:?}", value),
|
||||||
ConstrainedValue::Group(ref value) => write!(f, "{:?}", value),
|
ConstrainedValue::Group(ref value) => write!(f, "{:?}", value),
|
||||||
ConstrainedValue::Integer(ref value) => write!(f, "{}", value),
|
ConstrainedValue::Integer(ref value) => write!(f, "{}", value),
|
||||||
|
|
||||||
// Data type wrappers
|
// Data type wrappers
|
||||||
ConstrainedValue::Array(ref array) => {
|
ConstrainedValue::Array(ref array) => {
|
||||||
write!(f, "[")?;
|
if matches!(array[0], ConstrainedValue::Char(_)) {
|
||||||
for (i, e) in array.iter().enumerate() {
|
for character in array {
|
||||||
write!(f, "{}", e)?;
|
write!(f, "{}", character)?;
|
||||||
if i < array.len() - 1 {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) => {
|
ConstrainedValue::Tuple(ref tuple) => {
|
||||||
let values = tuple.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(", ");
|
let values = tuple.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(", ");
|
||||||
@ -144,6 +155,9 @@ impl<'a, F: PrimeField, G: GroupType<F>> ConditionalEqGadget<F> for ConstrainedV
|
|||||||
(ConstrainedValue::Boolean(bool_1), ConstrainedValue::Boolean(bool_2)) => {
|
(ConstrainedValue::Boolean(bool_1), ConstrainedValue::Boolean(bool_2)) => {
|
||||||
bool_1.conditional_enforce_equal(cs, bool_2, condition)
|
bool_1.conditional_enforce_equal(cs, bool_2, condition)
|
||||||
}
|
}
|
||||||
|
(ConstrainedValue::Char(char_1), ConstrainedValue::Char(char_2)) => {
|
||||||
|
char_1.conditional_enforce_equal(cs, char_2, condition)
|
||||||
|
}
|
||||||
(ConstrainedValue::Field(field_1), ConstrainedValue::Field(field_2)) => {
|
(ConstrainedValue::Field(field_1), ConstrainedValue::Field(field_2)) => {
|
||||||
field_1.conditional_enforce_equal(cs, field_2, condition)
|
field_1.conditional_enforce_equal(cs, field_2, condition)
|
||||||
}
|
}
|
||||||
@ -188,6 +202,9 @@ impl<'a, F: PrimeField, G: GroupType<F>> CondSelectGadget<F> for ConstrainedValu
|
|||||||
(ConstrainedValue::Boolean(bool_1), ConstrainedValue::Boolean(bool_2)) => {
|
(ConstrainedValue::Boolean(bool_1), ConstrainedValue::Boolean(bool_2)) => {
|
||||||
ConstrainedValue::Boolean(Boolean::conditionally_select(cs, cond, bool_1, bool_2)?)
|
ConstrainedValue::Boolean(Boolean::conditionally_select(cs, cond, bool_1, bool_2)?)
|
||||||
}
|
}
|
||||||
|
(ConstrainedValue::Char(char_1), ConstrainedValue::Char(char_2)) => {
|
||||||
|
ConstrainedValue::Char(Char::conditionally_select(cs, cond, char_1, char_2)?)
|
||||||
|
}
|
||||||
(ConstrainedValue::Field(field_1), ConstrainedValue::Field(field_2)) => {
|
(ConstrainedValue::Field(field_1), ConstrainedValue::Field(field_2)) => {
|
||||||
ConstrainedValue::Field(FieldType::conditionally_select(cs, cond, field_1, field_2)?)
|
ConstrainedValue::Field(FieldType::conditionally_select(cs, cond, field_1, field_2)?)
|
||||||
}
|
}
|
||||||
|
@ -95,3 +95,16 @@ fn test_illegal_array_range_fail() {
|
|||||||
let program = parse_program(program_string);
|
let program = parse_program(program_string);
|
||||||
assert!(program.is_err());
|
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);
|
||||||
|
}
|
||||||
|
309
compiler/tests/canonicalization/string_transformation.json
Normal file
309
compiler/tests/canonicalization/string_transformation.json
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"expected_input": [],
|
||||||
|
"imports": [],
|
||||||
|
"circuits": {},
|
||||||
|
"global_consts": {},
|
||||||
|
"functions": {
|
||||||
|
"{\"name\":\"main\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":10,\\\"col_stop\\\":14,\\\"path\\\":\\\"\\\",\\\"content\\\":\\\"function main() {\\\"}\"}": {
|
||||||
|
"annotations": [],
|
||||||
|
"identifier": "{\"name\":\"main\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":10,\\\"col_stop\\\":14,\\\"path\\\":\\\"\\\",\\\"content\\\":\\\"function main() {\\\"}\"}",
|
||||||
|
"input": [],
|
||||||
|
"output": {
|
||||||
|
"Tuple": []
|
||||||
|
},
|
||||||
|
"block": {
|
||||||
|
"statements": [
|
||||||
|
{
|
||||||
|
"Definition": {
|
||||||
|
"declaration_type": "Let",
|
||||||
|
"variable_names": [
|
||||||
|
{
|
||||||
|
"mutable": true,
|
||||||
|
"identifier": "{\"name\":\"s\",\"span\":\"{\\\"line_start\\\":2,\\\"line_stop\\\":2,\\\"col_start\\\":9,\\\"col_stop\\\":10,\\\"path\\\":\\\"\\\",\\\"content\\\":\\\" let s = \\\\\\\"Hello, World!\\\\\\\";\\\"}\"}",
|
||||||
|
"span": {
|
||||||
|
"line_start": 2,
|
||||||
|
"line_stop": 2,
|
||||||
|
"col_start": 9,
|
||||||
|
"col_stop": 10,
|
||||||
|
"path": "",
|
||||||
|
"content": " let s = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type_": {
|
||||||
|
"Array": [
|
||||||
|
"Char",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"value": "13"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"ArrayInline": {
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"H",
|
||||||
|
{
|
||||||
|
"line_start": 2,
|
||||||
|
"line_stop": 2,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " let s = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"e",
|
||||||
|
{
|
||||||
|
"line_start": 2,
|
||||||
|
"line_stop": 2,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " let s = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"l",
|
||||||
|
{
|
||||||
|
"line_start": 2,
|
||||||
|
"line_stop": 2,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " let s = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"l",
|
||||||
|
{
|
||||||
|
"line_start": 2,
|
||||||
|
"line_stop": 2,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " let s = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"o",
|
||||||
|
{
|
||||||
|
"line_start": 2,
|
||||||
|
"line_stop": 2,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " let s = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
",",
|
||||||
|
{
|
||||||
|
"line_start": 2,
|
||||||
|
"line_stop": 2,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " let s = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
" ",
|
||||||
|
{
|
||||||
|
"line_start": 2,
|
||||||
|
"line_stop": 2,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " let s = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"W",
|
||||||
|
{
|
||||||
|
"line_start": 2,
|
||||||
|
"line_stop": 2,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " let s = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"o",
|
||||||
|
{
|
||||||
|
"line_start": 2,
|
||||||
|
"line_stop": 2,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " let s = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"r",
|
||||||
|
{
|
||||||
|
"line_start": 2,
|
||||||
|
"line_stop": 2,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " let s = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"l",
|
||||||
|
{
|
||||||
|
"line_start": 2,
|
||||||
|
"line_stop": 2,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " let s = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"d",
|
||||||
|
{
|
||||||
|
"line_start": 2,
|
||||||
|
"line_stop": 2,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " let s = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"!",
|
||||||
|
{
|
||||||
|
"line_start": 2,
|
||||||
|
"line_stop": 2,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " let s = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"span": {
|
||||||
|
"line_start": 2,
|
||||||
|
"line_stop": 2,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " let s = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"span": {
|
||||||
|
"line_start": 2,
|
||||||
|
"line_stop": 2,
|
||||||
|
"col_start": 5,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " let s = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"span": {
|
||||||
|
"line_start": 1,
|
||||||
|
"line_stop": 3,
|
||||||
|
"col_start": 17,
|
||||||
|
"col_stop": 2,
|
||||||
|
"path": "",
|
||||||
|
"content": "function main() {\n...\n}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"span": {
|
||||||
|
"line_start": 1,
|
||||||
|
"line_stop": 3,
|
||||||
|
"col_start": 1,
|
||||||
|
"col_stop": 2,
|
||||||
|
"path": "",
|
||||||
|
"content": "function main() {\n...\n}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
function main() {
|
||||||
|
let s = "Hello, World!";
|
||||||
|
}
|
@ -984,11 +984,326 @@
|
|||||||
"content": " const m = Foo {};"
|
"content": " const m = Foo {};"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Definition": {
|
||||||
|
"declaration_type": "Const",
|
||||||
|
"variable_names": [
|
||||||
|
{
|
||||||
|
"mutable": false,
|
||||||
|
"identifier": "{\"name\":\"n\",\"span\":\"{\\\"line_start\\\":23,\\\"line_stop\\\":23,\\\"col_start\\\":9,\\\"col_stop\\\":10,\\\"path\\\":\\\"\\\",\\\"content\\\":\\\" const n = 'a';\\\"}\"}",
|
||||||
|
"span": {
|
||||||
|
"line_start": 23,
|
||||||
|
"line_stop": 23,
|
||||||
|
"col_start": 9,
|
||||||
|
"col_stop": 10,
|
||||||
|
"path": "",
|
||||||
|
"content": " const n = 'a';"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type_": "Char",
|
||||||
|
"value": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"a",
|
||||||
|
{
|
||||||
|
"line_start": 23,
|
||||||
|
"line_stop": 23,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 16,
|
||||||
|
"path": "",
|
||||||
|
"content": " const n = 'a';"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"span": {
|
||||||
|
"line_start": 23,
|
||||||
|
"line_stop": 23,
|
||||||
|
"col_start": 3,
|
||||||
|
"col_stop": 16,
|
||||||
|
"path": "",
|
||||||
|
"content": " const n = 'a';"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Definition": {
|
||||||
|
"declaration_type": "Const",
|
||||||
|
"variable_names": [
|
||||||
|
{
|
||||||
|
"mutable": false,
|
||||||
|
"identifier": "{\"name\":\"o\",\"span\":\"{\\\"line_start\\\":24,\\\"line_stop\\\":24,\\\"col_start\\\":9,\\\"col_stop\\\":10,\\\"path\\\":\\\"\\\",\\\"content\\\":\\\" const o = \\\\\\\"Hello, World!\\\\\\\";\\\"}\"}",
|
||||||
|
"span": {
|
||||||
|
"line_start": 24,
|
||||||
|
"line_stop": 24,
|
||||||
|
"col_start": 9,
|
||||||
|
"col_stop": 10,
|
||||||
|
"path": "",
|
||||||
|
"content": " const o = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type_": {
|
||||||
|
"Array": [
|
||||||
|
"Char",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"value": "13"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"ArrayInline": {
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"H",
|
||||||
|
{
|
||||||
|
"line_start": 24,
|
||||||
|
"line_stop": 24,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " const o = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"e",
|
||||||
|
{
|
||||||
|
"line_start": 24,
|
||||||
|
"line_stop": 24,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " const o = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"l",
|
||||||
|
{
|
||||||
|
"line_start": 24,
|
||||||
|
"line_stop": 24,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " const o = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"l",
|
||||||
|
{
|
||||||
|
"line_start": 24,
|
||||||
|
"line_stop": 24,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " const o = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"o",
|
||||||
|
{
|
||||||
|
"line_start": 24,
|
||||||
|
"line_stop": 24,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " const o = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
",",
|
||||||
|
{
|
||||||
|
"line_start": 24,
|
||||||
|
"line_stop": 24,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " const o = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
" ",
|
||||||
|
{
|
||||||
|
"line_start": 24,
|
||||||
|
"line_stop": 24,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " const o = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"W",
|
||||||
|
{
|
||||||
|
"line_start": 24,
|
||||||
|
"line_stop": 24,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " const o = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"o",
|
||||||
|
{
|
||||||
|
"line_start": 24,
|
||||||
|
"line_stop": 24,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " const o = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"r",
|
||||||
|
{
|
||||||
|
"line_start": 24,
|
||||||
|
"line_stop": 24,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " const o = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"l",
|
||||||
|
{
|
||||||
|
"line_start": 24,
|
||||||
|
"line_stop": 24,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " const o = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"d",
|
||||||
|
{
|
||||||
|
"line_start": 24,
|
||||||
|
"line_stop": 24,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " const o = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Expression": {
|
||||||
|
"Value": {
|
||||||
|
"Char": [
|
||||||
|
"!",
|
||||||
|
{
|
||||||
|
"line_start": 24,
|
||||||
|
"line_stop": 24,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " const o = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"span": {
|
||||||
|
"line_start": 24,
|
||||||
|
"line_stop": 24,
|
||||||
|
"col_start": 13,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " const o = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"span": {
|
||||||
|
"line_start": 24,
|
||||||
|
"line_stop": 24,
|
||||||
|
"col_start": 3,
|
||||||
|
"col_stop": 28,
|
||||||
|
"path": "",
|
||||||
|
"content": " const o = \"Hello, World!\";"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"span": {
|
"span": {
|
||||||
"line_start": 9,
|
"line_start": 9,
|
||||||
"line_stop": 23,
|
"line_stop": 25,
|
||||||
"col_start": 17,
|
"col_start": 17,
|
||||||
"col_stop": 2,
|
"col_stop": 2,
|
||||||
"path": "",
|
"path": "",
|
||||||
@ -997,11 +1312,11 @@
|
|||||||
},
|
},
|
||||||
"span": {
|
"span": {
|
||||||
"line_start": 9,
|
"line_start": 9,
|
||||||
"line_stop": 23,
|
"line_stop": 25,
|
||||||
"col_start": 1,
|
"col_start": 1,
|
||||||
"col_stop": 2,
|
"col_stop": 2,
|
||||||
"path": "",
|
"path": "",
|
||||||
"content": "function main() {\n...\n}\n\n\n\n\n\n\n\n\n\n\n\n"
|
"content": "function main() {\n...\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,4 +20,6 @@ function main() {
|
|||||||
const k = (1u8, 1u8);
|
const k = (1u8, 1u8);
|
||||||
const l = (1u8, 1u8, true);
|
const l = (1u8, 1u8, true);
|
||||||
const m = Foo {};
|
const m = Foo {};
|
||||||
|
const n = 'a';
|
||||||
|
const o = "Hello, World!";
|
||||||
}
|
}
|
@ -83,6 +83,27 @@ impl InputParserError {
|
|||||||
Self::new_from_span(message, span)
|
Self::new_from_span(message, span)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn invalid_char(character: String, span: &Span) -> Self {
|
||||||
|
let message = format!("Expected valid character found `{}`", character);
|
||||||
|
|
||||||
|
Self::new_from_span(message, span)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invalid_string_dimensions(span: &Span) -> Self {
|
||||||
|
let message = "String type defintion of a char array should not be multi-dimensional".to_string();
|
||||||
|
|
||||||
|
Self::new_from_span(message, span)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invalid_string_length(expected: usize, received: usize, span: &Span) -> Self {
|
||||||
|
let message = format!(
|
||||||
|
"Expected size of char array `{}` to match string size instead received `{}`",
|
||||||
|
expected, received
|
||||||
|
);
|
||||||
|
|
||||||
|
Self::new_from_span(message, span)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn implicit_type(data_type: DataType, implicit: NumberValue) -> Self {
|
pub fn implicit_type(data_type: DataType, implicit: NumberValue) -> Self {
|
||||||
let message = format!("expected `{}`, found `{}`", data_type, implicit);
|
let message = format!("expected `{}`, found `{}`", data_type, implicit);
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ use std::fmt;
|
|||||||
pub enum Expression<'ast> {
|
pub enum Expression<'ast> {
|
||||||
ArrayInitializer(ArrayInitializerExpression<'ast>),
|
ArrayInitializer(ArrayInitializerExpression<'ast>),
|
||||||
ArrayInline(ArrayInlineExpression<'ast>),
|
ArrayInline(ArrayInlineExpression<'ast>),
|
||||||
|
StringExpression(StringExpression<'ast>),
|
||||||
Tuple(TupleExpression<'ast>),
|
Tuple(TupleExpression<'ast>),
|
||||||
Value(Value<'ast>),
|
Value(Value<'ast>),
|
||||||
}
|
}
|
||||||
@ -34,6 +35,7 @@ impl<'ast> Expression<'ast> {
|
|||||||
match self {
|
match self {
|
||||||
Expression::ArrayInitializer(expression) => &expression.span,
|
Expression::ArrayInitializer(expression) => &expression.span,
|
||||||
Expression::ArrayInline(expression) => &expression.span,
|
Expression::ArrayInline(expression) => &expression.span,
|
||||||
|
Expression::StringExpression(string) => &string.span,
|
||||||
Expression::Tuple(tuple) => &tuple.span,
|
Expression::Tuple(tuple) => &tuple.span,
|
||||||
Expression::Value(value) => value.span(),
|
Expression::Value(value) => value.span(),
|
||||||
}
|
}
|
||||||
@ -56,6 +58,7 @@ impl<'ast> fmt::Display for Expression<'ast> {
|
|||||||
|
|
||||||
write!(f, "array [{}]", values)
|
write!(f, "array [{}]", values)
|
||||||
}
|
}
|
||||||
|
Expression::StringExpression(ref string) => write!(f, "{}", string),
|
||||||
Expression::Tuple(ref tuple) => {
|
Expression::Tuple(ref tuple) => {
|
||||||
let values = tuple
|
let values = tuple
|
||||||
.expressions
|
.expressions
|
||||||
|
@ -25,3 +25,6 @@ pub use expression::*;
|
|||||||
|
|
||||||
pub mod tuple_expression;
|
pub mod tuple_expression;
|
||||||
pub use tuple_expression::*;
|
pub use tuple_expression::*;
|
||||||
|
|
||||||
|
pub mod string_expression;
|
||||||
|
pub use string_expression::*;
|
||||||
|
41
input/src/expressions/string_expression.rs
Normal file
41
input/src/expressions/string_expression.rs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||||
|
// This file is part of the Leo library.
|
||||||
|
|
||||||
|
// The Leo library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// The Leo library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use crate::{ast::Rule, values::CharTypes};
|
||||||
|
|
||||||
|
use pest::Span;
|
||||||
|
use pest_ast::FromPest;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, FromPest, PartialEq, Eq)]
|
||||||
|
#[pest_ast(rule(Rule::expression_string))]
|
||||||
|
pub struct StringExpression<'ast> {
|
||||||
|
pub chars: Vec<CharTypes<'ast>>,
|
||||||
|
#[pest_ast(outer())]
|
||||||
|
pub span: Span<'ast>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ast> fmt::Display for StringExpression<'ast> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "\"")?;
|
||||||
|
|
||||||
|
for character in self.chars.iter() {
|
||||||
|
write!(f, "{:?}", character)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "\"")
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ protected_name = {
|
|||||||
| "else"
|
| "else"
|
||||||
| "false"
|
| "false"
|
||||||
| type_field
|
| type_field
|
||||||
|
| type_char
|
||||||
| "for"
|
| "for"
|
||||||
| "function"
|
| "function"
|
||||||
| type_group
|
| type_group
|
||||||
@ -73,6 +74,9 @@ type_integer_signed = {
|
|||||||
| type_i128
|
| type_i128
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Declared in types/char_type.rs
|
||||||
|
type_char = { "char" }
|
||||||
|
|
||||||
// Declared in types/field_type.rs
|
// Declared in types/field_type.rs
|
||||||
type_field = { "field" }
|
type_field = { "field" }
|
||||||
|
|
||||||
@ -86,7 +90,7 @@ type_boolean = { "bool" }
|
|||||||
type_address = { "address" }
|
type_address = { "address" }
|
||||||
|
|
||||||
// Declared in types/data_type.rs
|
// Declared in types/data_type.rs
|
||||||
type_data = { type_field | type_group | type_boolean | type_address | type_integer }
|
type_data = { type_field | type_group | type_boolean | type_address | type_integer | type_char }
|
||||||
|
|
||||||
// Declared in types/array_type.rs
|
// Declared in types/array_type.rs
|
||||||
type_array = { "[" ~ type_ ~ ";" ~ array_dimensions ~ "]" }
|
type_array = { "[" ~ type_ ~ ";" ~ array_dimensions ~ "]" }
|
||||||
@ -115,6 +119,7 @@ value = {
|
|||||||
| value_boolean
|
| value_boolean
|
||||||
| value_field
|
| value_field
|
||||||
| value_group
|
| value_group
|
||||||
|
| value_char
|
||||||
| value_integer
|
| value_integer
|
||||||
| value_number // must be last as a catch all
|
| value_number // must be last as a catch all
|
||||||
}
|
}
|
||||||
@ -128,6 +133,22 @@ number_negative = @{ "-" ~ ASCII_DIGIT+ }
|
|||||||
// Declared in values/number_positive.rs
|
// Declared in values/number_positive.rs
|
||||||
number_positive = @{ ASCII_DIGIT+ }
|
number_positive = @{ ASCII_DIGIT+ }
|
||||||
|
|
||||||
|
// Decalred in values/char_types.rs
|
||||||
|
// ANY is equivalent to '\u{00}'..'\u{10FFFF}'
|
||||||
|
basic_char = { ANY }
|
||||||
|
escaped_char = @{ "\\" ~ ("\"" | "\'" | "\\" | "/" | "b" | "f" | "n" | "r" | "t") }
|
||||||
|
hex_char = @{ "\\" ~ "x" ~ ASCII_HEX_DIGIT{2} }
|
||||||
|
unicode_char = @{ "\\" ~ "u" ~ "{" ~ ASCII_HEX_DIGIT{1, 6} ~ "}" }
|
||||||
|
|
||||||
|
char_types = {
|
||||||
|
escaped_char
|
||||||
|
| unicode_char
|
||||||
|
| hex_char
|
||||||
|
| basic_char
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declared in values/char_value.rs
|
||||||
|
value_char = ${ "\'" ~ char_types ~ "\'" }
|
||||||
|
|
||||||
// Declared in values/integer_value.rs
|
// Declared in values/integer_value.rs
|
||||||
value_integer = { value_integer_signed | value_integer_unsigned}
|
value_integer = { value_integer_signed | value_integer_unsigned}
|
||||||
@ -179,12 +200,16 @@ expression_array_initializer = { "[" ~ expression ~ ";" ~ array_dimensions ~ "]"
|
|||||||
expression_array_inline = { "[" ~ NEWLINE* ~ inline_array_inner ~ NEWLINE* ~ "]"}
|
expression_array_inline = { "[" ~ NEWLINE* ~ inline_array_inner ~ NEWLINE* ~ "]"}
|
||||||
inline_array_inner = _{ (expression ~ ("," ~ NEWLINE* ~ expression)*)? }
|
inline_array_inner = _{ (expression ~ ("," ~ NEWLINE* ~ expression)*)? }
|
||||||
|
|
||||||
|
// Declared in expressions/string_expression.rs
|
||||||
|
expression_string = ${ "\"" ~ (!"\"" ~ char_types)+ ~ "\"" }
|
||||||
|
|
||||||
// Declared in expressions/expression.rs
|
// Declared in expressions/expression.rs
|
||||||
expression = {
|
expression = {
|
||||||
value
|
value
|
||||||
| expression_tuple
|
| expression_tuple
|
||||||
| expression_array_inline
|
| expression_array_inline
|
||||||
| expression_array_initializer
|
| expression_array_initializer
|
||||||
|
| expression_string
|
||||||
}
|
}
|
||||||
expression_tuple = { "(" ~ expression ~ ("," ~ expression)+ ~")" }
|
expression_tuple = { "(" ~ expression ~ ("," ~ expression)+ ~")" }
|
||||||
|
|
||||||
|
23
input/src/types/char_type.rs
Normal file
23
input/src/types/char_type.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||||
|
// This file is part of the Leo library.
|
||||||
|
|
||||||
|
// The Leo library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// The Leo library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use crate::ast::Rule;
|
||||||
|
|
||||||
|
use pest_ast::FromPest;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, FromPest, PartialEq, Eq)]
|
||||||
|
#[pest_ast(rule(Rule::type_char))]
|
||||||
|
pub struct CharType {}
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::Rule,
|
ast::Rule,
|
||||||
types::{BooleanType, FieldType, GroupType, IntegerType},
|
types::{BooleanType, CharType, FieldType, GroupType, IntegerType},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::types::AddressType;
|
use crate::types::AddressType;
|
||||||
@ -27,6 +27,7 @@ use pest_ast::FromPest;
|
|||||||
pub enum DataType {
|
pub enum DataType {
|
||||||
Address(AddressType),
|
Address(AddressType),
|
||||||
Boolean(BooleanType),
|
Boolean(BooleanType),
|
||||||
|
Char(CharType),
|
||||||
Field(FieldType),
|
Field(FieldType),
|
||||||
Group(GroupType),
|
Group(GroupType),
|
||||||
Integer(IntegerType),
|
Integer(IntegerType),
|
||||||
@ -37,6 +38,7 @@ impl std::fmt::Display for DataType {
|
|||||||
match self {
|
match self {
|
||||||
DataType::Address(_) => write!(f, "address"),
|
DataType::Address(_) => write!(f, "address"),
|
||||||
DataType::Boolean(_) => write!(f, "bool"),
|
DataType::Boolean(_) => write!(f, "bool"),
|
||||||
|
DataType::Char(_) => write!(f, "char"),
|
||||||
DataType::Field(_) => write!(f, "field"),
|
DataType::Field(_) => write!(f, "field"),
|
||||||
DataType::Group(_) => write!(f, "group"),
|
DataType::Group(_) => write!(f, "group"),
|
||||||
DataType::Integer(ref integer) => write!(f, "{}", integer),
|
DataType::Integer(ref integer) => write!(f, "{}", integer),
|
||||||
|
@ -26,6 +26,9 @@ pub use array_type::*;
|
|||||||
pub mod boolean_type;
|
pub mod boolean_type;
|
||||||
pub use boolean_type::*;
|
pub use boolean_type::*;
|
||||||
|
|
||||||
|
pub mod char_type;
|
||||||
|
pub use char_type::*;
|
||||||
|
|
||||||
pub mod data_type;
|
pub mod data_type;
|
||||||
pub use data_type::*;
|
pub use data_type::*;
|
||||||
|
|
||||||
|
129
input/src/values/char_types.rs
Normal file
129
input/src/values/char_types.rs
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||||
|
// This file is part of the Leo library.
|
||||||
|
|
||||||
|
// The Leo library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// The Leo library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ast::{span_into_string, Rule},
|
||||||
|
errors::InputParserError,
|
||||||
|
};
|
||||||
|
|
||||||
|
use pest::Span;
|
||||||
|
use pest_ast::FromPest;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, FromPest, PartialEq, Eq)]
|
||||||
|
#[pest_ast(rule(Rule::basic_char))]
|
||||||
|
pub struct BasicChar<'ast> {
|
||||||
|
#[pest_ast(outer(with(span_into_string)))]
|
||||||
|
pub value: String,
|
||||||
|
#[pest_ast(outer())]
|
||||||
|
pub span: Span<'ast>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, FromPest, PartialEq, Eq)]
|
||||||
|
#[pest_ast(rule(Rule::escaped_char))]
|
||||||
|
pub struct EscapedChar<'ast> {
|
||||||
|
#[pest_ast(outer(with(span_into_string)))]
|
||||||
|
pub value: String,
|
||||||
|
#[pest_ast(outer())]
|
||||||
|
pub span: Span<'ast>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, FromPest, PartialEq, Eq)]
|
||||||
|
#[pest_ast(rule(Rule::hex_char))]
|
||||||
|
pub struct HexChar<'ast> {
|
||||||
|
#[pest_ast(outer(with(span_into_string)))]
|
||||||
|
pub value: String,
|
||||||
|
#[pest_ast(outer())]
|
||||||
|
pub span: Span<'ast>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, FromPest, PartialEq, Eq)]
|
||||||
|
#[pest_ast(rule(Rule::unicode_char))]
|
||||||
|
pub struct UnicodeChar<'ast> {
|
||||||
|
#[pest_ast(outer(with(span_into_string)))]
|
||||||
|
pub value: String,
|
||||||
|
#[pest_ast(outer())]
|
||||||
|
pub span: Span<'ast>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, FromPest, PartialEq, Eq)]
|
||||||
|
#[pest_ast(rule(Rule::char_types))]
|
||||||
|
pub enum CharTypes<'ast> {
|
||||||
|
Basic(BasicChar<'ast>),
|
||||||
|
Escaped(EscapedChar<'ast>),
|
||||||
|
Hex(HexChar<'ast>),
|
||||||
|
Unicode(UnicodeChar<'ast>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ast> CharTypes<'ast> {
|
||||||
|
pub fn span(&self) -> &Span<'ast> {
|
||||||
|
match self {
|
||||||
|
CharTypes::Basic(value) => &value.span,
|
||||||
|
CharTypes::Escaped(value) => &value.span,
|
||||||
|
CharTypes::Hex(value) => &value.span,
|
||||||
|
CharTypes::Unicode(value) => &value.span,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ast> CharTypes<'ast> {
|
||||||
|
pub fn inner(self) -> Result<char, InputParserError> {
|
||||||
|
match self {
|
||||||
|
Self::Basic(character) => {
|
||||||
|
if let Some(character) = character.value.chars().next() {
|
||||||
|
return Ok(character);
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(InputParserError::invalid_char(character.value, &character.span))
|
||||||
|
}
|
||||||
|
Self::Escaped(character) => {
|
||||||
|
if let Some(inner) = character.value.chars().nth(1) {
|
||||||
|
return match inner {
|
||||||
|
'0' => Ok(0 as char),
|
||||||
|
't' => Ok(9 as char),
|
||||||
|
'n' => Ok(10 as char),
|
||||||
|
'r' => Ok(13 as char),
|
||||||
|
'\"' => Ok(34 as char),
|
||||||
|
'\'' => Ok(39 as char),
|
||||||
|
'\\' => Ok(92 as char),
|
||||||
|
_ => Err(InputParserError::invalid_char(character.value, &character.span)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(InputParserError::invalid_char(character.value, &character.span))
|
||||||
|
}
|
||||||
|
Self::Hex(character) => {
|
||||||
|
let hex_string_number = character.value[2..character.value.len()].to_string();
|
||||||
|
if let Ok(number) = u8::from_str_radix(&hex_string_number, 16) {
|
||||||
|
if number < 127 {
|
||||||
|
return Ok(number as char);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(InputParserError::invalid_char(character.value, &character.span))
|
||||||
|
}
|
||||||
|
Self::Unicode(character) => {
|
||||||
|
let unicode_string_number = character.value[3..=character.value.len() - 2].to_string();
|
||||||
|
if let Ok(hex) = u32::from_str_radix(&unicode_string_number, 16) {
|
||||||
|
if let Some(unicode) = std::char::from_u32(hex) {
|
||||||
|
return Ok(unicode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(InputParserError::invalid_char(character.value, &character.span))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
input/src/values/char_value.rs
Normal file
35
input/src/values/char_value.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||||
|
// This file is part of the Leo library.
|
||||||
|
|
||||||
|
// The Leo library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// The Leo library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use crate::{ast::Rule, values::CharTypes};
|
||||||
|
|
||||||
|
use pest::Span;
|
||||||
|
use pest_ast::FromPest;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, FromPest, PartialEq, Eq)]
|
||||||
|
#[pest_ast(rule(Rule::value_char))]
|
||||||
|
pub struct CharValue<'ast> {
|
||||||
|
pub value: CharTypes<'ast>,
|
||||||
|
#[pest_ast(outer())]
|
||||||
|
pub span: Span<'ast>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ast> fmt::Display for CharValue<'ast> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{:?}", self.value)
|
||||||
|
}
|
||||||
|
}
|
@ -26,6 +26,12 @@ pub use address_value::*;
|
|||||||
pub mod boolean_value;
|
pub mod boolean_value;
|
||||||
pub use boolean_value::*;
|
pub use boolean_value::*;
|
||||||
|
|
||||||
|
pub mod char_types;
|
||||||
|
pub use char_types::*;
|
||||||
|
|
||||||
|
pub mod char_value;
|
||||||
|
pub use char_value::*;
|
||||||
|
|
||||||
pub mod field_value;
|
pub mod field_value;
|
||||||
pub use field_value::*;
|
pub use field_value::*;
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::Rule,
|
ast::Rule,
|
||||||
values::{BooleanValue, FieldValue, GroupValue, IntegerValue, NumberValue},
|
values::{BooleanValue, CharValue, FieldValue, GroupValue, IntegerValue, NumberValue},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::values::AddressValue;
|
use crate::values::AddressValue;
|
||||||
@ -29,6 +29,7 @@ use std::fmt;
|
|||||||
pub enum Value<'ast> {
|
pub enum Value<'ast> {
|
||||||
Address(AddressValue<'ast>),
|
Address(AddressValue<'ast>),
|
||||||
Boolean(BooleanValue<'ast>),
|
Boolean(BooleanValue<'ast>),
|
||||||
|
Char(CharValue<'ast>),
|
||||||
Field(FieldValue<'ast>),
|
Field(FieldValue<'ast>),
|
||||||
Group(GroupValue<'ast>),
|
Group(GroupValue<'ast>),
|
||||||
Implicit(NumberValue<'ast>),
|
Implicit(NumberValue<'ast>),
|
||||||
@ -40,6 +41,7 @@ impl<'ast> Value<'ast> {
|
|||||||
match self {
|
match self {
|
||||||
Value::Address(value) => &value.span(),
|
Value::Address(value) => &value.span(),
|
||||||
Value::Boolean(value) => &value.span,
|
Value::Boolean(value) => &value.span,
|
||||||
|
Value::Char(value) => &value.span,
|
||||||
Value::Field(value) => &value.span,
|
Value::Field(value) => &value.span,
|
||||||
Value::Group(value) => &value.span,
|
Value::Group(value) => &value.span,
|
||||||
Value::Implicit(value) => &value.span(),
|
Value::Implicit(value) => &value.span(),
|
||||||
@ -53,6 +55,7 @@ impl<'ast> fmt::Display for Value<'ast> {
|
|||||||
match *self {
|
match *self {
|
||||||
Value::Address(ref value) => write!(f, "{}", value),
|
Value::Address(ref value) => write!(f, "{}", value),
|
||||||
Value::Boolean(ref value) => write!(f, "{}", value),
|
Value::Boolean(ref value) => write!(f, "{}", value),
|
||||||
|
Value::Char(ref value) => write!(f, "{}", value),
|
||||||
Value::Field(ref value) => write!(f, "{}", value),
|
Value::Field(ref value) => write!(f, "{}", value),
|
||||||
Value::Group(ref value) => write!(f, "{}", value),
|
Value::Group(ref value) => write!(f, "{}", value),
|
||||||
Value::Implicit(ref value) => write!(f, "{}", value),
|
Value::Implicit(ref value) => write!(f, "{}", value),
|
||||||
|
@ -689,6 +689,8 @@ impl ParserContext {
|
|||||||
Token::True => Expression::Value(ValueExpression::Boolean("true".into(), span)),
|
Token::True => Expression::Value(ValueExpression::Boolean("true".into(), span)),
|
||||||
Token::False => Expression::Value(ValueExpression::Boolean("false".into(), span)),
|
Token::False => Expression::Value(ValueExpression::Boolean("false".into(), span)),
|
||||||
Token::AddressLit(value) => Expression::Value(ValueExpression::Address(value, span)),
|
Token::AddressLit(value) => Expression::Value(ValueExpression::Address(value, span)),
|
||||||
|
Token::CharLit(value) => Expression::Value(ValueExpression::Char(value, span)),
|
||||||
|
Token::StringLit(value) => Expression::Value(ValueExpression::String(value, span)),
|
||||||
Token::LeftParen => self.parse_tuple_expression(&span)?,
|
Token::LeftParen => self.parse_tuple_expression(&span)?,
|
||||||
Token::LeftSquare => self.parse_array_expression(&span)?,
|
Token::LeftSquare => self.parse_array_expression(&span)?,
|
||||||
Token::Ident(name) => {
|
Token::Ident(name) => {
|
||||||
|
@ -226,16 +226,19 @@ impl ParserContext {
|
|||||||
///
|
///
|
||||||
pub fn parse_formatted_string(&mut self) -> SyntaxResult<FormatString> {
|
pub fn parse_formatted_string(&mut self) -> SyntaxResult<FormatString> {
|
||||||
let start_span;
|
let start_span;
|
||||||
let parts = match self.expect_any()? {
|
let string = match self.expect_any()? {
|
||||||
SpannedToken {
|
SpannedToken {
|
||||||
token: Token::FormatString(parts),
|
token: Token::StringLit(chars),
|
||||||
span,
|
span,
|
||||||
} => {
|
} => {
|
||||||
start_span = span;
|
start_span = span;
|
||||||
parts
|
chars
|
||||||
}
|
}
|
||||||
SpannedToken { token, span } => return Err(SyntaxError::unexpected_str(&token, "formatted string", &span)),
|
SpannedToken { token, span } => return Err(SyntaxError::unexpected_str(&token, "formatted string", &span)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let parts = FormatStringPart::from_string(string);
|
||||||
|
|
||||||
let mut parameters = Vec::new();
|
let mut parameters = Vec::new();
|
||||||
while self.eat(Token::Comma).is_some() {
|
while self.eat(Token::Comma).is_some() {
|
||||||
let param = self.parse_expression()?;
|
let param = self.parse_expression()?;
|
||||||
@ -243,13 +246,7 @@ impl ParserContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(FormatString {
|
Ok(FormatString {
|
||||||
parts: parts
|
parts,
|
||||||
.into_iter()
|
|
||||||
.map(|x| match x {
|
|
||||||
crate::FormatStringPart::Const(value) => FormatStringPart::Const(value),
|
|
||||||
crate::FormatStringPart::Container => FormatStringPart::Container,
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
span: &start_span + parameters.last().map(|x| x.span()).unwrap_or(&start_span),
|
span: &start_span + parameters.last().map(|x| x.span()).unwrap_or(&start_span),
|
||||||
parameters,
|
parameters,
|
||||||
})
|
})
|
||||||
|
@ -31,6 +31,7 @@ const TYPE_TOKENS: &[Token] = &[
|
|||||||
Token::Group,
|
Token::Group,
|
||||||
Token::Address,
|
Token::Address,
|
||||||
Token::Bool,
|
Token::Bool,
|
||||||
|
Token::Char,
|
||||||
];
|
];
|
||||||
|
|
||||||
impl ParserContext {
|
impl ParserContext {
|
||||||
@ -117,6 +118,7 @@ impl ParserContext {
|
|||||||
Token::Group => Type::Group,
|
Token::Group => Type::Group,
|
||||||
Token::Address => Type::Address,
|
Token::Address => Type::Address,
|
||||||
Token::Bool => Type::Boolean,
|
Token::Bool => Type::Boolean,
|
||||||
|
Token::Char => Type::Char,
|
||||||
x => Type::IntegerType(Self::token_to_int_type(x).expect("invalid int type")),
|
x => Type::IntegerType(Self::token_to_int_type(x).expect("invalid int type")),
|
||||||
},
|
},
|
||||||
token.span,
|
token.span,
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use crate::tokenizer::{FormatStringPart, Token};
|
use crate::tokenizer::Token;
|
||||||
use leo_ast::Span;
|
use leo_ast::Span;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tendril::StrTendril;
|
use tendril::StrTendril;
|
||||||
@ -45,6 +45,7 @@ fn eat_identifier(input_tendril: &StrTendril) -> Option<StrTendril> {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let input = input_tendril[..].as_bytes();
|
let input = input_tendril[..].as_bytes();
|
||||||
|
|
||||||
if !input[0].is_ascii_alphabetic() {
|
if !input[0].is_ascii_alphabetic() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -60,6 +61,80 @@ fn eat_identifier(input_tendril: &StrTendril) -> Option<StrTendril> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Token {
|
impl Token {
|
||||||
|
///
|
||||||
|
/// Returns a `char` if an character can be eaten, otherwise returns [`None`].
|
||||||
|
///
|
||||||
|
fn eat_char(input_tendril: StrTendril, escaped: bool, hex: bool, unicode: bool) -> Option<char> {
|
||||||
|
if input_tendril.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if escaped {
|
||||||
|
let string = input_tendril.to_string();
|
||||||
|
let escaped = &string[1..string.len()];
|
||||||
|
|
||||||
|
if escaped.len() != 1 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(character) = escaped.chars().next() {
|
||||||
|
return match character {
|
||||||
|
'0' => Some(0 as char),
|
||||||
|
't' => Some(9 as char),
|
||||||
|
'n' => Some(10 as char),
|
||||||
|
'r' => Some(13 as char),
|
||||||
|
'\"' => Some(34 as char),
|
||||||
|
'\'' => Some(39 as char),
|
||||||
|
'\\' => Some(92 as char),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hex {
|
||||||
|
let string = input_tendril.to_string();
|
||||||
|
let hex_string = &string[2..string.len()];
|
||||||
|
|
||||||
|
if hex_string.len() != 2 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(ascii_number) = u8::from_str_radix(&hex_string, 16) {
|
||||||
|
// According to RFC, we allow only values less than 128.
|
||||||
|
if ascii_number > 127 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Some(ascii_number as char);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if unicode {
|
||||||
|
let string = input_tendril.to_string();
|
||||||
|
let unicode_number = &string[3..string.len() - 1];
|
||||||
|
let len = unicode_number.len();
|
||||||
|
if !(1..=6).contains(&len) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(hex) = u32::from_str_radix(&unicode_number, 16) {
|
||||||
|
if let Some(character) = std::char::from_u32(hex) {
|
||||||
|
return Some(character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if input_tendril.to_string().chars().count() != 1 {
|
||||||
|
return None;
|
||||||
|
} else if let Some(character) = input_tendril.to_string().chars().next() {
|
||||||
|
return Some(character);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Returns a tuple: [(integer length, integer token)] if an integer can be eaten, otherwise returns [`None`].
|
/// Returns a tuple: [(integer length, integer token)] if an integer can be eaten, otherwise returns [`None`].
|
||||||
/// An integer can be eaten if its bytes are at the front of the given `input_tendril` string.
|
/// An integer can be eaten if its bytes are at the front of the given `input_tendril` string.
|
||||||
@ -93,6 +168,23 @@ impl Token {
|
|||||||
(i, Some(Token::Int(input_tendril.subtendril(0, i as u32))))
|
(i, Some(Token::Int(input_tendril.subtendril(0, i as u32))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the number of bytes in an emoji via a bit mask.
|
||||||
|
fn utf8_byte_count(byte: u8) -> u8 {
|
||||||
|
let mut mask = 0x80;
|
||||||
|
let mut result = 0;
|
||||||
|
while byte & mask > 0 {
|
||||||
|
result += 1;
|
||||||
|
mask >>= 1;
|
||||||
|
}
|
||||||
|
if result == 0 {
|
||||||
|
1
|
||||||
|
} else if result > 4 {
|
||||||
|
4
|
||||||
|
} else {
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Returns a tuple: [(token length, token)] if the next token can be eaten, otherwise returns [`None`].
|
/// Returns a tuple: [(token length, token)] if the next token can be eaten, otherwise returns [`None`].
|
||||||
/// The next token can be eaten if the bytes at the front of the given `input_tendril` string can be scanned into a token.
|
/// The next token can be eaten if the bytes at the front of the given `input_tendril` string can be scanned into a token.
|
||||||
@ -106,48 +198,126 @@ impl Token {
|
|||||||
x if x.is_ascii_whitespace() => return (1, None),
|
x if x.is_ascii_whitespace() => return (1, None),
|
||||||
b'"' => {
|
b'"' => {
|
||||||
let mut i = 1;
|
let mut i = 1;
|
||||||
|
let mut len: u8 = 1;
|
||||||
|
let mut start = 1;
|
||||||
let mut in_escape = false;
|
let mut in_escape = false;
|
||||||
let mut start = 1usize;
|
let mut escaped = false;
|
||||||
let mut segments = Vec::new();
|
let mut hex = false;
|
||||||
|
let mut unicode = false;
|
||||||
|
let mut end = false;
|
||||||
|
let mut string = String::new();
|
||||||
|
|
||||||
while i < input.len() {
|
while i < input.len() {
|
||||||
|
// If it's an emoji get the length.
|
||||||
|
if input[i] & 0x80 > 0 {
|
||||||
|
len = Self::utf8_byte_count(input[i]);
|
||||||
|
i += (len as usize) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
if !in_escape {
|
if !in_escape {
|
||||||
if input[i] == b'"' {
|
if input[i] == b'"' {
|
||||||
|
end = true;
|
||||||
break;
|
break;
|
||||||
}
|
} else if input[i] == b'\\' {
|
||||||
if input[i] == b'\\' {
|
in_escape = true;
|
||||||
in_escape = !in_escape;
|
start = i;
|
||||||
} else if i < input.len() - 1 && input[i] == b'{' {
|
i += 1;
|
||||||
if i < input.len() - 2 && input[i + 1] == b'{' {
|
|
||||||
i += 2;
|
|
||||||
continue;
|
|
||||||
} else if input[i + 1] != b'}' {
|
|
||||||
i += 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if start < i {
|
|
||||||
segments.push(FormatStringPart::Const(
|
|
||||||
input_tendril.subtendril(start as u32, (i - start) as u32),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
segments.push(FormatStringPart::Container);
|
|
||||||
start = i + 2;
|
|
||||||
i = start;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
in_escape = false;
|
len += 1;
|
||||||
|
|
||||||
|
match input[i] {
|
||||||
|
b'x' => {
|
||||||
|
hex = true;
|
||||||
|
}
|
||||||
|
b'u' => {
|
||||||
|
unicode = true;
|
||||||
|
}
|
||||||
|
b'}' if unicode => {
|
||||||
|
in_escape = false;
|
||||||
|
}
|
||||||
|
_ if !hex && !unicode => {
|
||||||
|
escaped = true;
|
||||||
|
in_escape = false;
|
||||||
|
}
|
||||||
|
_ if hex && len == 4 => {
|
||||||
|
in_escape = false;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !in_escape {
|
||||||
|
match Self::eat_char(
|
||||||
|
input_tendril.subtendril(start as u32, len as u32),
|
||||||
|
escaped,
|
||||||
|
hex,
|
||||||
|
unicode,
|
||||||
|
) {
|
||||||
|
Some(character) => {
|
||||||
|
len = 1;
|
||||||
|
escaped = false;
|
||||||
|
hex = false;
|
||||||
|
unicode = false;
|
||||||
|
string.push(character);
|
||||||
|
}
|
||||||
|
None => return (0, None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
i += 1;
|
i += 1;
|
||||||
|
|
||||||
|
if !escaped && !hex && !unicode {
|
||||||
|
start = i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if i == input.len() {
|
|
||||||
|
if i == input.len() || i == 1 || !end {
|
||||||
return (0, None);
|
return (0, None);
|
||||||
}
|
}
|
||||||
if start < i {
|
|
||||||
segments.push(FormatStringPart::Const(
|
return (i + 1, Some(Token::StringLit(string)));
|
||||||
input_tendril.subtendril(start as u32, (i - start) as u32),
|
}
|
||||||
));
|
b'\'' => {
|
||||||
|
let mut i = 1;
|
||||||
|
let mut in_escape = false;
|
||||||
|
let mut escaped = false;
|
||||||
|
let mut hex = false;
|
||||||
|
let mut unicode = false;
|
||||||
|
let mut end = false;
|
||||||
|
|
||||||
|
while i < input.len() {
|
||||||
|
if !in_escape {
|
||||||
|
if input[i] == b'\'' {
|
||||||
|
end = true;
|
||||||
|
break;
|
||||||
|
} else if input[i] == b'\\' {
|
||||||
|
in_escape = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if input[i] == b'x' {
|
||||||
|
hex = true;
|
||||||
|
} else if input[i] == b'u' {
|
||||||
|
unicode = true;
|
||||||
|
} else {
|
||||||
|
escaped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
in_escape = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
}
|
}
|
||||||
return (i + 1, Some(Token::FormatString(segments)));
|
|
||||||
|
if !end {
|
||||||
|
return (0, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
return match Self::eat_char(input_tendril.subtendril(1, (i - 1) as u32), escaped, hex, unicode) {
|
||||||
|
Some(character) => (i + 1, Some(Token::CharLit(character))),
|
||||||
|
None => (0, None),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
x if x.is_ascii_digit() => {
|
x if x.is_ascii_digit() => {
|
||||||
return Self::eat_integer(&input_tendril);
|
return Self::eat_integer(&input_tendril);
|
||||||
@ -310,6 +480,7 @@ impl Token {
|
|||||||
"address" => Token::Address,
|
"address" => Token::Address,
|
||||||
"as" => Token::As,
|
"as" => Token::As,
|
||||||
"bool" => Token::Bool,
|
"bool" => Token::Bool,
|
||||||
|
"char" => Token::Char,
|
||||||
"circuit" => Token::Circuit,
|
"circuit" => Token::Circuit,
|
||||||
"console" => Token::Console,
|
"console" => Token::Console,
|
||||||
"const" => Token::Const,
|
"const" => Token::Const,
|
||||||
|
@ -259,6 +259,6 @@ mod tests {
|
|||||||
let original = &raw[*start + token.span.col_start - 1..*stop + token.span.col_stop - 1];
|
let original = &raw[*start + token.span.col_start - 1..*stop + token.span.col_stop - 1];
|
||||||
assert_eq!(original, &token_raw);
|
assert_eq!(original, &token_raw);
|
||||||
}
|
}
|
||||||
println!("{}", serde_json::to_string_pretty(&tokens).unwrap());
|
// println!("{}", serde_json::to_string_pretty(&tokens).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,22 +18,6 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use tendril::StrTendril;
|
use tendril::StrTendril;
|
||||||
|
|
||||||
/// Parts of a formatted string for logging to the console.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum FormatStringPart {
|
|
||||||
Const(#[serde(with = "leo_ast::common::tendril_json")] StrTendril),
|
|
||||||
Container,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for FormatStringPart {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
FormatStringPart::Const(c) => write!(f, "{}", c),
|
|
||||||
FormatStringPart::Container => write!(f, "{{}}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents all valid Leo syntax tokens.
|
/// Represents all valid Leo syntax tokens.
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum Token {
|
pub enum Token {
|
||||||
@ -41,12 +25,13 @@ pub enum Token {
|
|||||||
// Literals
|
// Literals
|
||||||
CommentLine(#[serde(with = "leo_ast::common::tendril_json")] StrTendril),
|
CommentLine(#[serde(with = "leo_ast::common::tendril_json")] StrTendril),
|
||||||
CommentBlock(#[serde(with = "leo_ast::common::tendril_json")] StrTendril),
|
CommentBlock(#[serde(with = "leo_ast::common::tendril_json")] StrTendril),
|
||||||
FormatString(Vec<FormatStringPart>),
|
StringLit(String),
|
||||||
Ident(#[serde(with = "leo_ast::common::tendril_json")] StrTendril),
|
Ident(#[serde(with = "leo_ast::common::tendril_json")] StrTendril),
|
||||||
Int(#[serde(with = "leo_ast::common::tendril_json")] StrTendril),
|
Int(#[serde(with = "leo_ast::common::tendril_json")] StrTendril),
|
||||||
True,
|
True,
|
||||||
False,
|
False,
|
||||||
AddressLit(#[serde(with = "leo_ast::common::tendril_json")] StrTendril),
|
AddressLit(#[serde(with = "leo_ast::common::tendril_json")] StrTendril),
|
||||||
|
CharLit(char),
|
||||||
|
|
||||||
At,
|
At,
|
||||||
|
|
||||||
@ -104,6 +89,7 @@ pub enum Token {
|
|||||||
Group,
|
Group,
|
||||||
Bool,
|
Bool,
|
||||||
Address,
|
Address,
|
||||||
|
Char,
|
||||||
BigSelf,
|
BigSelf,
|
||||||
|
|
||||||
// primary expresion
|
// primary expresion
|
||||||
@ -156,6 +142,7 @@ pub const KEYWORD_TOKENS: &[Token] = &[
|
|||||||
Token::Address,
|
Token::Address,
|
||||||
Token::As,
|
Token::As,
|
||||||
Token::Bool,
|
Token::Bool,
|
||||||
|
Token::Char,
|
||||||
Token::Circuit,
|
Token::Circuit,
|
||||||
Token::Console,
|
Token::Console,
|
||||||
Token::Const,
|
Token::Const,
|
||||||
@ -204,19 +191,13 @@ impl fmt::Display for Token {
|
|||||||
match self {
|
match self {
|
||||||
CommentLine(s) => write!(f, "{}", s),
|
CommentLine(s) => write!(f, "{}", s),
|
||||||
CommentBlock(s) => write!(f, "{}", s),
|
CommentBlock(s) => write!(f, "{}", s),
|
||||||
FormatString(parts) => {
|
StringLit(content) => write!(f, "\"{}\"", content),
|
||||||
// todo escapes
|
|
||||||
write!(f, "\"")?;
|
|
||||||
for part in parts.iter() {
|
|
||||||
part.fmt(f)?;
|
|
||||||
}
|
|
||||||
write!(f, "\"")
|
|
||||||
}
|
|
||||||
Ident(s) => write!(f, "{}", s),
|
Ident(s) => write!(f, "{}", s),
|
||||||
Int(s) => write!(f, "{}", s),
|
Int(s) => write!(f, "{}", s),
|
||||||
True => write!(f, "true"),
|
True => write!(f, "true"),
|
||||||
False => write!(f, "false"),
|
False => write!(f, "false"),
|
||||||
AddressLit(s) => write!(f, "{}", s),
|
AddressLit(s) => write!(f, "{}", s),
|
||||||
|
CharLit(s) => write!(f, "{}", s),
|
||||||
|
|
||||||
At => write!(f, "@"),
|
At => write!(f, "@"),
|
||||||
|
|
||||||
@ -271,6 +252,7 @@ impl fmt::Display for Token {
|
|||||||
Group => write!(f, "group"),
|
Group => write!(f, "group"),
|
||||||
Bool => write!(f, "bool"),
|
Bool => write!(f, "bool"),
|
||||||
Address => write!(f, "address"),
|
Address => write!(f, "address"),
|
||||||
|
Char => write!(f, "char"),
|
||||||
BigSelf => write!(f, "Self"),
|
BigSelf => write!(f, "Self"),
|
||||||
|
|
||||||
Input => write!(f, "input"),
|
Input => write!(f, "input"),
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
function main(a: field, b: field, c: field) {
|
|
||||||
console.assert(a + b == c);
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
function main(a: field, b: field) {
|
|
||||||
console.assert(a == b);
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
function main(a: field, b: field, c: field) {
|
|
||||||
console.assert(a / b == c);
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
function main(a: field, b: field, c: bool) {
|
|
||||||
console.assert(a == b == c);
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
function main() {
|
|
||||||
const negOneField: field = -1field;
|
|
||||||
const oneField = 1field;
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
function main(a: field, b: field, c: field) {
|
|
||||||
console.assert(a * b == c);
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
function main(a: field, b: field) {
|
|
||||||
console.assert(-a == b);
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
function main() {
|
|
||||||
const f = 1 field;
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
function main(registers) -> field {
|
|
||||||
return registers.r;
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
function main(a: field, b: field, c: field) {
|
|
||||||
console.assert(a - b == c);
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
function main(s: bool, a: field, b: field, c: field) {
|
|
||||||
const r = s ? a : b;
|
|
||||||
|
|
||||||
console.assert(r == c);
|
|
||||||
}
|
|
21
tests/compiler/char/circuit.leo
Normal file
21
tests/compiler/char/circuit.leo
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
namespace: Compile
|
||||||
|
expectation: Pass
|
||||||
|
input_file:
|
||||||
|
- inputs/ascii.in
|
||||||
|
- inputs/escaped_unicode.in
|
||||||
|
- inputs/escaped.in
|
||||||
|
- inputs/hex.in
|
||||||
|
- inputs/unicode.in
|
||||||
|
*/
|
||||||
|
|
||||||
|
circuit Foo {
|
||||||
|
character: char;
|
||||||
|
}
|
||||||
|
|
||||||
|
function main(character: char) -> char {
|
||||||
|
let f = Foo { character };
|
||||||
|
|
||||||
|
let character = f.character == 'a' ? 'a' : f.character;
|
||||||
|
return character;
|
||||||
|
}
|
5
tests/compiler/char/inputs/ascii.in
Normal file
5
tests/compiler/char/inputs/ascii.in
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[main]
|
||||||
|
character: char = 'a';
|
||||||
|
|
||||||
|
[registers]
|
||||||
|
r: char = 'a';
|
5
tests/compiler/char/inputs/escaped.in
Normal file
5
tests/compiler/char/inputs/escaped.in
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[main]
|
||||||
|
character: char = '\'';
|
||||||
|
|
||||||
|
[registers]
|
||||||
|
r: char = 'a';
|
5
tests/compiler/char/inputs/escaped_unicode.in
Normal file
5
tests/compiler/char/inputs/escaped_unicode.in
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[main]
|
||||||
|
character: char = '\u{2764}';
|
||||||
|
|
||||||
|
[registers]
|
||||||
|
r: char = 'a';
|
5
tests/compiler/char/inputs/hex.in
Normal file
5
tests/compiler/char/inputs/hex.in
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[main]
|
||||||
|
character: char = '\x2A';
|
||||||
|
|
||||||
|
[registers]
|
||||||
|
r: char = 'a';
|
5
tests/compiler/char/inputs/unicode.in
Normal file
5
tests/compiler/char/inputs/unicode.in
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[main]
|
||||||
|
character: char = '❤';
|
||||||
|
|
||||||
|
[registers]
|
||||||
|
r: char = 'a';
|
8
tests/compiler/char/invalid_char.leo
Normal file
8
tests/compiler/char/invalid_char.leo
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
namespace: Compile
|
||||||
|
expectation: Fail
|
||||||
|
*/
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
const not_valid = '';
|
||||||
|
}
|
14
tests/compiler/char/neq.leo
Normal file
14
tests/compiler/char/neq.leo
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
namespace: Compile
|
||||||
|
expectation: Pass
|
||||||
|
input_file:
|
||||||
|
- inputs/ascii.in
|
||||||
|
- inputs/escaped_unicode.in
|
||||||
|
- inputs/escaped.in
|
||||||
|
- inputs/hex.in
|
||||||
|
- inputs/unicode.in
|
||||||
|
*/
|
||||||
|
|
||||||
|
function main(character: char) -> char {
|
||||||
|
return character != 'a' ? 'a' : 'Z';
|
||||||
|
}
|
2
tests/compiler/char/output/output_char.out
Normal file
2
tests/compiler/char/output/output_char.out
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[registers]
|
||||||
|
r: char = 'a';
|
10
tests/compiler/field/add.leo
Normal file
10
tests/compiler/field/add.leo
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
namespace: Compile
|
||||||
|
expectation: Pass
|
||||||
|
input_file:
|
||||||
|
- inputs/fields.in
|
||||||
|
*/
|
||||||
|
|
||||||
|
function main(a: field, b: field, c: field) -> bool {
|
||||||
|
return a + b == c;
|
||||||
|
}
|
10
tests/compiler/field/div.leo
Normal file
10
tests/compiler/field/div.leo
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
namespace: Compile
|
||||||
|
expectation: Pass
|
||||||
|
input_file:
|
||||||
|
- inputs/fields.in
|
||||||
|
*/
|
||||||
|
|
||||||
|
function main(a: field, b: field, c: field) -> bool {
|
||||||
|
return a / b != c;
|
||||||
|
}
|
10
tests/compiler/field/eq.leo
Normal file
10
tests/compiler/field/eq.leo
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
namespace: Compile
|
||||||
|
expectation: Pass
|
||||||
|
input_file:
|
||||||
|
- inputs/fields.in
|
||||||
|
*/
|
||||||
|
|
||||||
|
function main(a: field, b: field) -> bool {
|
||||||
|
return a == b;
|
||||||
|
}
|
11
tests/compiler/field/field.leo
Normal file
11
tests/compiler/field/field.leo
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
namespace: Compile
|
||||||
|
expectation: Pass
|
||||||
|
input_file:
|
||||||
|
- inputs/fields.in
|
||||||
|
*/
|
||||||
|
|
||||||
|
function main(a: field) -> bool {
|
||||||
|
const negOneField: field = -1field;
|
||||||
|
return negOneField + a == 0field;
|
||||||
|
}
|
7
tests/compiler/field/inputs/fields.in
Normal file
7
tests/compiler/field/inputs/fields.in
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[main]
|
||||||
|
a: field = 1field;
|
||||||
|
b: field = 1field;
|
||||||
|
c: field = 2field;
|
||||||
|
|
||||||
|
[registers]
|
||||||
|
r: bool = false;
|
10
tests/compiler/field/mul.leo
Normal file
10
tests/compiler/field/mul.leo
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
namespace: Compile
|
||||||
|
expectation: Pass
|
||||||
|
input_file:
|
||||||
|
- inputs/fields.in
|
||||||
|
*/
|
||||||
|
|
||||||
|
function main(a: field, b: field, c: field) -> bool {
|
||||||
|
return a * b == c;
|
||||||
|
}
|
10
tests/compiler/field/negate.leo
Normal file
10
tests/compiler/field/negate.leo
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
namespace: Compile
|
||||||
|
expectation: Pass
|
||||||
|
input_file:
|
||||||
|
- inputs/fields.in
|
||||||
|
*/
|
||||||
|
|
||||||
|
function main(a: field, b: field) -> bool {
|
||||||
|
return -a == -b;
|
||||||
|
}
|
8
tests/compiler/field/no_space_between_literal.leo
Normal file
8
tests/compiler/field/no_space_between_literal.leo
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
namespace: Compile
|
||||||
|
expectation: Fail
|
||||||
|
*/
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
const f = 1 field;
|
||||||
|
}
|
3
tests/compiler/field/sub.leo
Normal file
3
tests/compiler/field/sub.leo
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
function main(a: field, b: field, c: field) -> bool {
|
||||||
|
return a - b == c;
|
||||||
|
}
|
3
tests/compiler/field/ternary.leo
Normal file
3
tests/compiler/field/ternary.leo
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
function main(a: field, b: field, c: field) -> bool {
|
||||||
|
return b == 1field ? a == 1field : c == 2field;
|
||||||
|
}
|
@ -20,6 +20,8 @@ const field_test: field = 2;
|
|||||||
const use_another_const = basic + 1;
|
const use_another_const = basic + 1;
|
||||||
const foo = Foo { width: 10, height: 20 };
|
const foo = Foo { width: 10, height: 20 };
|
||||||
const uno = uno();
|
const uno = uno();
|
||||||
|
const character = 'a';
|
||||||
|
const hello = "Hello, World!";
|
||||||
|
|
||||||
circuit Foo {
|
circuit Foo {
|
||||||
width: u32,
|
width: u32,
|
||||||
@ -47,5 +49,7 @@ function main(a: u32) -> bool {
|
|||||||
&& use_another_const == 9u32 // use another const test
|
&& use_another_const == 9u32 // use another const test
|
||||||
&& foo.width == 10u32 // circuit test
|
&& foo.width == 10u32 // circuit test
|
||||||
&& foo.height == 20u32
|
&& foo.height == 20u32
|
||||||
&& uno == 1u32; // function test
|
&& uno == 1u32 // function test
|
||||||
|
&& character == 'a' // char test
|
||||||
|
&& hello == "Hello, World!";
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
[main]
|
||||||
|
y: bool = true;
|
||||||
|
a: char = '👍';
|
||||||
|
|
||||||
|
[registers]
|
||||||
|
r0: bool = true;
|
@ -0,0 +1,6 @@
|
|||||||
|
[main]
|
||||||
|
y: bool = true;
|
||||||
|
a: [char; 5] = "😭😂👍😁😘";
|
||||||
|
|
||||||
|
[registers]
|
||||||
|
r0: bool = true;
|
9
tests/compiler/input_files/program_input/main_char.leo
Normal file
9
tests/compiler/input_files/program_input/main_char.leo
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
namespace: Compile
|
||||||
|
expectation: Pass
|
||||||
|
input_file: input/main_char.in
|
||||||
|
*/
|
||||||
|
|
||||||
|
function main(a: char, y: bool) -> bool {
|
||||||
|
return y == true && a == '👍';
|
||||||
|
}
|
@ -5,9 +5,5 @@ input_file: input/main_field.in
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function main(a: field, b: field, y: bool) -> bool {
|
function main(a: field, b: field, y: bool) -> bool {
|
||||||
// Change to assert when == is implemented for field.
|
return y == true && a == b;
|
||||||
console.log("a: {}", a);
|
|
||||||
console.log("b: {}", b);
|
|
||||||
|
|
||||||
return y == true;
|
|
||||||
}
|
}
|
||||||
|
9
tests/compiler/input_files/program_input/main_string.leo
Normal file
9
tests/compiler/input_files/program_input/main_string.leo
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
namespace: Compile
|
||||||
|
expectation: Pass
|
||||||
|
input_file: input/main_string.in
|
||||||
|
*/
|
||||||
|
|
||||||
|
function main(a: [char; 5], y: bool) -> bool {
|
||||||
|
return y == true && a == "😭😂👍😁😘";
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
[main]
|
||||||
|
y: bool = true;
|
||||||
|
|
||||||
|
[constants]
|
||||||
|
a: char = '👍';
|
||||||
|
|
||||||
|
[registers]
|
||||||
|
r0: bool = true;
|
@ -0,0 +1,8 @@
|
|||||||
|
[main]
|
||||||
|
y: bool = true;
|
||||||
|
|
||||||
|
[constants]
|
||||||
|
a: [char; 5] = "😭😂👍😁😘";
|
||||||
|
|
||||||
|
[registers]
|
||||||
|
r0: bool = true;
|
@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
namespace: Compile
|
||||||
|
expectation: Pass
|
||||||
|
input_file: input/main_char.in
|
||||||
|
*/
|
||||||
|
|
||||||
|
function main(const a: char, y: bool) -> bool {
|
||||||
|
return y == true && a == '👍';
|
||||||
|
}
|
@ -5,9 +5,5 @@ input_file: input/main_field.in
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function main(const a: field, const b: field, y: bool) -> bool {
|
function main(const a: field, const b: field, y: bool) -> bool {
|
||||||
// Change to assert when == is implemented for field.
|
return y == true && a == b;
|
||||||
console.log("a: {}", a);
|
|
||||||
console.log("b: {}", b);
|
|
||||||
|
|
||||||
return y == true;
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
namespace: Compile
|
||||||
|
expectation: Pass
|
||||||
|
input_file: input/main_string.in
|
||||||
|
*/
|
||||||
|
|
||||||
|
function main(const a: [char; 5], y: bool) -> bool {
|
||||||
|
return y == true && a == "😭😂👍😁😘";
|
||||||
|
}
|
22
tests/compiler/string/circuit.leo
Normal file
22
tests/compiler/string/circuit.leo
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
namespace: Compile
|
||||||
|
expectation: Pass
|
||||||
|
input_file:
|
||||||
|
- inputs/string_out.in
|
||||||
|
*/
|
||||||
|
|
||||||
|
circuit Foo {
|
||||||
|
s1: [char; 13];
|
||||||
|
}
|
||||||
|
|
||||||
|
function takes_string(s: [char; 13]) -> bool {
|
||||||
|
return s == "Hello, World!";
|
||||||
|
}
|
||||||
|
|
||||||
|
function main(s1: [char; 13]) -> [char; 13] {
|
||||||
|
let f = Foo { s1 };
|
||||||
|
let b = takes_string(s1);
|
||||||
|
|
||||||
|
let result = f.s1 == "Hello, World!" ? s1 : "abcdefghjklmn";
|
||||||
|
return result;
|
||||||
|
}
|
15
tests/compiler/string/equality.leo
Normal file
15
tests/compiler/string/equality.leo
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
namespace: Compile
|
||||||
|
expectation: Pass
|
||||||
|
input_file:
|
||||||
|
- inputs/string.in
|
||||||
|
- inputs/weird.in
|
||||||
|
*/
|
||||||
|
|
||||||
|
function main(s1: [char; 13], s2: [char; 4]) -> bool {
|
||||||
|
let hello: [char; 13] = "Hello, World!";
|
||||||
|
let part1 = "Good";
|
||||||
|
let part2 = " dog!";
|
||||||
|
let concat: [char; 9] = [...part1, ...part2];
|
||||||
|
return hello == s1 && "nope" != s2 && "es" == s2[1..3] && concat == "Good dog!";
|
||||||
|
}
|
6
tests/compiler/string/inputs/string.in
Normal file
6
tests/compiler/string/inputs/string.in
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[main]
|
||||||
|
s1: [char; 13] = "Hello, World!";
|
||||||
|
s2: [char; 4] = "test";
|
||||||
|
|
||||||
|
[registers]
|
||||||
|
out: bool = true;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user