Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 148 additions & 56 deletions src/ast.rs

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/compile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,10 @@ impl Expression {
res
}
ExpressionInner::Single(e) => e.compile(scope),
ExpressionInner::Error => Err(Error::CannotCompile(
"Compiled from poisoned tree".to_string(),
)
.with_span(*self.as_ref())),
}
}
}
Expand Down
5 changes: 2 additions & 3 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@ use itertools::Itertools;
use simplicity::elements;

use crate::lexer::Token;
use crate::parse::MatchPattern;
use crate::str::{AliasName, FunctionName, Identifier, JetName, ModuleName, WitnessName};
use crate::types::{ResolvedType, UIntType};

/// Area that an object spans inside a file.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
pub struct Span {
/// Position where the object starts, inclusively.
pub start: usize,
Expand Down Expand Up @@ -408,7 +407,7 @@ pub enum Error {
label: Option<String>,
found: Option<String>,
},
IncompatibleMatchArms(MatchPattern, MatchPattern),
IncompatibleMatchArms(String, String),
// TODO: Remove CompileError once SimplicityHL has a type system
// The SimplicityHL compiler should never produce ill-typed Simplicity code
// The compiler can only be this precise if it knows a type system at least as expressive as Simplicity's
Expand Down
10 changes: 5 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ impl TemplateProgram {
let mut error_handler = ErrorCollector::new(Arc::clone(&file));
let parse_program = parse::Program::parse_from_str_with_errors(&file, &mut error_handler);
if let Some(program) = parse_program {
let ast_program = ast::Program::analyze(&program).with_file(Arc::clone(&file))?;
Ok(Self {
simfony: ast_program,
file,
})
let ast_program = ast::Program::analyze(&program, &mut error_handler);
match ast_program {
Some(ast) => Ok(Self { simfony: ast, file }),
None => Err(ErrorCollector::to_string(&error_handler))?,
}
} else {
Err(ErrorCollector::to_string(&error_handler))?
}
Expand Down
113 changes: 55 additions & 58 deletions src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,13 @@ impl Expression {
span,
}
}

pub fn error(span: Span) -> Self {
Self {
inner: ExpressionInner::Error,
span,
}
}
}

impl_eq_hash!(Expression; inner);
Expand All @@ -301,6 +308,8 @@ pub enum ExpressionInner {
/// Then, the block returns the value of its final expression.
/// The block returns nothing (unit) if there is no final expression.
Block(Arc<[Statement]>, Option<Arc<Expression>>),
/// Error type
Error,
}

/// A single expression directly returns a value.
Expand Down Expand Up @@ -399,7 +408,7 @@ impl Match {
}
(MatchPattern::None, MatchPattern::Some(_, ty_r)) => AliasedType::option(ty_r.clone()),
(MatchPattern::False, MatchPattern::True) => AliasedType::boolean(),
_ => unreachable!("Match expressions have valid left and right arms"),
_ => AliasedType::error().with_span(*self.as_ref()),
}
}
}
Expand Down Expand Up @@ -614,6 +623,7 @@ impl TreeLike for ExprTree<'_> {
Tree::Unary(Self::Block(statements, maybe_expr))
}
ExpressionInner::Single(single) => Tree::Unary(Self::Single(single)),
ExpressionInner::Error => Tree::Nullary,
},
Self::Block(statements, maybe_expr) => Tree::Nary(
statements
Expand Down Expand Up @@ -851,8 +861,9 @@ macro_rules! impl_parse_wrapped_string {
I: ValueInput<'tokens, Token = Token<'src>, Span = Span>,
{
select! {
Token::Ident(ident) => Self::from_str_unchecked(ident)
Token::Ident(ident) => ident
}
.map_with(|s, e| Self::from_str_unchecked(s).with_span(e.span()))
.labelled($label)
}
}
Expand Down Expand Up @@ -935,13 +946,7 @@ impl<A: ChumskyParse + std::fmt::Debug> ParseFromStrWithErrors for A {

handler.update(parse_errs);

// TODO: We should return parsed result if we found errors, but because analyzing in `ast` module
// is not handling poisoned tree right now, we don't return parsed result
if handler.get().is_empty() {
ast
} else {
None
}
ast
}
}

Expand Down Expand Up @@ -1002,27 +1007,27 @@ impl ChumskyParse for AliasedType {
I: ValueInput<'tokens, Token = Token<'src>, Span = Span>,
{
let atom = select! {
Token::Ident(ident) => {
match ident
{
"u1" => AliasedType::u1(),
"u2" => AliasedType::u2(),
"u4" => AliasedType::u4(),
"u8" => AliasedType::u8(),
"u16" => AliasedType::u16(),
"u32" => AliasedType::u32(),
"u64" => AliasedType::u64(),
"u128" => AliasedType::u128(),
"u256" => AliasedType::u256(),
"Ctx8" | "Pubkey" | "Message64" | "Message" | "Signature" | "Scalar" | "Fe" | "Gej"
| "Ge" | "Point" | "Height" | "Time" | "Distance" | "Duration" | "Lock" | "Outpoint"
| "Confidential1" | "ExplicitAsset" | "Asset1" | "ExplicitAmount" | "Amount1"
| "ExplicitNonce" | "Nonce" | "TokenAmount1" => AliasedType::builtin(BuiltinAlias::from_str(ident).unwrap()),
"bool" => AliasedType::boolean(),
_ => AliasedType::alias(AliasName::from_str_unchecked(ident)),
}
},
};
Token::Ident(ident) => ident
}
.map_with(|ident, e| match ident {
"u1" => AliasedType::u1(),
"u2" => AliasedType::u2(),
"u4" => AliasedType::u4(),
"u8" => AliasedType::u8(),
"u16" => AliasedType::u16(),
"u32" => AliasedType::u32(),
"u64" => AliasedType::u64(),
"u128" => AliasedType::u128(),
"u256" => AliasedType::u256(),
"Ctx8" | "Pubkey" | "Message64" | "Message" | "Signature" | "Scalar" | "Fe" | "Gej"
| "Ge" | "Point" | "Height" | "Time" | "Distance" | "Duration" | "Lock"
| "Outpoint" | "Confidential1" | "ExplicitAsset" | "Asset1" | "ExplicitAmount"
| "Amount1" | "ExplicitNonce" | "Nonce" | "TokenAmount1" => {
AliasedType::builtin(BuiltinAlias::from_str(ident).unwrap())
}
"bool" => AliasedType::boolean(),
_ => AliasedType::alias(AliasName::from_str_unchecked(ident).with_span(e.span())),
});

let num = select! {
Token::DecLiteral(i) => i.clone()
Expand All @@ -1042,12 +1047,7 @@ impl ChumskyParse for AliasedType {
.then(ty.clone()),
Token::LAngle,
Token::RAngle,
|_| {
(
AliasedType::alias(AliasName::from_str_unchecked("error")),
AliasedType::alias(AliasName::from_str_unchecked("error")),
)
},
|_| (AliasedType::error(), AliasedType::error()),
);

let sum_type = just(Token::Ident("Either"))
Expand All @@ -1073,7 +1073,7 @@ impl ChumskyParse for AliasedType {
.map(|s: Vec<AliasedType>| AliasedType::tuple(s)),
Token::LParen,
Token::RParen,
|_| AliasedType::tuple(Vec::new()),
|_| AliasedType::error(),
)
.labelled("tuple");

Expand All @@ -1086,12 +1086,7 @@ impl ChumskyParse for AliasedType {
}),
Token::LBracket,
Token::RBracket,
|_| {
AliasedType::array(
AliasedType::alias(AliasName::from_str_unchecked("error")),
0,
)
},
|_| AliasedType::error(),
)
.labelled("array");

Expand All @@ -1113,18 +1108,13 @@ impl ChumskyParse for AliasedType {
})),
Token::LAngle,
Token::RAngle,
|_| {
(
AliasedType::alias(AliasName::from_str_unchecked("error")),
NonZeroPow2Usize::TWO,
)
},
|_| (AliasedType::error(), NonZeroPow2Usize::TWO),
))
.map(|(ty, size)| AliasedType::list(ty, size))
.labelled("List");

choice((sum_type, option_type, tuple, array, list, atom))
.map_with(|inner, _| inner)
.map_with(|inner, e| inner.with_span(e.span()))
.labelled("type")
})
}
Expand Down Expand Up @@ -1200,7 +1190,7 @@ impl ChumskyParse for Function {
(Token::LParen, Token::RParen),
(Token::LBracket, Token::RBracket),
],
Expression::empty,
Expression::error,
)))
.labelled("function body");

Expand Down Expand Up @@ -1437,7 +1427,8 @@ impl ChumskyParse for CallName {
Token::Macro("dbg!") => CallName::Debug,
};

let jet = select! { Token::Jet(s) => JetName::from_str_unchecked(s) }.map(CallName::Jet);
let jet = select! { Token::Jet(s) => s }
.map_with(|s, e| CallName::Jet(JetName::from_str_unchecked(s).with_span(e.span())));

let custom_func = FunctionName::parser().map(CallName::Custom);

Expand Down Expand Up @@ -1493,7 +1484,7 @@ impl ChumskyParse for Expression {
(Token::RAngle, Token::RAngle),
(Token::LBracket, Token::RBracket),
],
|span| Expression::empty(span).inner().clone(),
|span| Expression::error(span).inner().clone(),
);

let statements = statement
Expand Down Expand Up @@ -1591,10 +1582,16 @@ impl SingleExpression {
Token::DecLiteral(s) => SingleExpressionInner::Decimal(s),
Token::HexLiteral(s) => SingleExpressionInner::Hexadecimal(s),
Token::BinLiteral(s) => SingleExpressionInner::Binary(s),
Token::Witness(s) => SingleExpressionInner::Witness(WitnessName::from_str_unchecked(s)),
Token::Param(s) => SingleExpressionInner::Parameter(WitnessName::from_str_unchecked(s)),
};

let witness = select! { Token::Witness(s) => s}.map_with(|s, e| {
SingleExpressionInner::Witness(WitnessName::from_str_unchecked(s).with_span(e.span()))
});

let param = select! { Token::Param(s) => s}.map_with(|s, e| {
SingleExpressionInner::Parameter(WitnessName::from_str_unchecked(s).with_span(e.span()))
});

let call = Call::parser(expr.clone()).map(SingleExpressionInner::Call);

let match_expr = Match::parser(expr.clone()).map(SingleExpressionInner::Match);
Expand All @@ -1609,7 +1606,7 @@ impl SingleExpression {

choice((
left, right, some, none, boolean, match_expr, expression, list, array, tuple, call,
literal, variable,
literal, witness, param, variable,
))
.map_with(|inner, e| Self {
inner,
Expand Down Expand Up @@ -1738,7 +1735,7 @@ impl Match {

(p1, p2) => {
emit.emit(
Error::IncompatibleMatchArms(p1.clone(), p2.clone())
Error::IncompatibleMatchArms(p1.to_string(), p2.to_string())
.with_span(e.span()),
);
(first, second)
Expand All @@ -1754,7 +1751,7 @@ impl Match {
}
_ => {
let match_arm_fallback = MatchArm {
expression: Arc::new(Expression::empty(Span::new(0, 0))),
expression: Arc::new(Expression::error(Span::new(0, 0))),
pattern: MatchPattern::False,
};

Expand Down
Loading