Skip to content

Commit

Permalink
introduce runner trait and merge parser and sem errors
Browse files Browse the repository at this point in the history
  • Loading branch information
SkymanOne committed Mar 26, 2024
1 parent 37e641d commit 8a1dd1f
Show file tree
Hide file tree
Showing 14 changed files with 135 additions and 67 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ members = [
"crates/folidity",
"crates/parser",
"crates/semantics",
"crates/verifier",
"crates/verifier",
]

[workspace.package]
Expand All @@ -23,7 +23,7 @@ version = "1.0.0"
folidity-parser = { path = "crates/parser" }
folidity-diagnostics = { path = "crates/diagnostics" }
folidity-semantics = { path = "crates/semantics" }
folidity-verifier = { path = "crates/semantics" }
folidity-verifier = { path = "crates/verifier" }
derive-node = { path = "crates/derive_node" }
logos = "0.14"
lalrpop-util = "0.20"
Expand Down
6 changes: 3 additions & 3 deletions crates/diagnostics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub fn disable_pretty_print() {
yansi::disable();
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub enum ErrorType {
Lexer,
Parser,
Expand All @@ -36,7 +36,7 @@ impl Display for ErrorType {
}
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub enum Level {
Info,
Warning,
Expand All @@ -54,7 +54,7 @@ impl<'a> From<Level> for ariadne::ReportKind<'a> {
}

/// Error report.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub struct Report {
/// Location of an error
pub loc: Span,
Expand Down
25 changes: 8 additions & 17 deletions crates/folidity/src/cmd/check.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
use anyhow::Result;
use ariadne::{
Color,
Fmt,
};
use folidity_parser::parse;
use folidity_semantics::resolve_semantics;
use folidity_semantics::ContractDefinition;
use std::ffi::OsString;

use clap::Args;

use super::{
build_report,
exec,
read_contract,
};

Expand All @@ -28,18 +25,12 @@ impl CheckCommand {
let parse_result = parse(&contract_contents);
match parse_result {
Ok(tree) => {
let def = resolve_semantics(&tree);
if def.diagnostics.is_empty() {
println!("{}", "Contract has no known errors".fg(Color::Green));
Ok(())
} else {
build_report(
&contract_contents,
&def.diagnostics,
self.contract.to_str().unwrap(),
);
anyhow::bail!("Syntactical checking failed.")
}
let _ = exec::<_, ContractDefinition>(
&tree,
&contract_contents,
self.contract.to_str().unwrap(),
)?;
Ok(())
}
Err(errors) => {
build_report(&contract_contents, &errors, self.contract.to_str().unwrap());
Expand Down
17 changes: 17 additions & 0 deletions crates/folidity/src/cmd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ use anyhow::{
};
use clap::Subcommand;
use folidity_diagnostics::Report;
use folidity_semantics::{
CompilationError,
Runner,
};
use yansi::Paint;

use self::{
Expand Down Expand Up @@ -72,3 +76,16 @@ pub fn build_report(content: &str, diagnostics: &[Report], file_name: &str) {
.unwrap();
}
}

/// Execute the compilation stage using the runner.
pub fn exec<S, W: Runner<S>>(input: &S, contract_contents: &str, file_name: &str) -> Result<W> {
W::run(input).map_err(|e| {
let reports = e.diagnostics();
build_report(contract_contents, reports, file_name);
match e {
CompilationError::Syntax(_) => anyhow::anyhow!("Syntactical error occurred"),
CompilationError::Formal(_) => anyhow::anyhow!("Verification failed"),
CompilationError::Emit(_) => anyhow::anyhow!("Compilation failed"),
}
})
}
2 changes: 2 additions & 0 deletions crates/parser/src/ast.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use super::Span;
use derive_node::Node;
use folidity_diagnostics::Report;

#[derive(Clone, Debug, PartialEq)]
pub struct Source {
pub declarations: Vec<Declaration>,
pub diagnostics: Vec<Report>,
}

#[derive(Clone, Debug, PartialEq, Node, Default)]
Expand Down
2 changes: 1 addition & 1 deletion crates/parser/src/folidity.lalrpop
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use lalrpop_util::ParseError;
grammar<'input, 'err>(errors: &'err mut Vec<ErrorRecovery<usize, Token<'input>, LexicalError>>);

pub FolidityTree: ast::Source = {
Declaration* => ast::Source { declarations: <> }
Declaration* => ast::Source { declarations: <>, diagnostics: vec![] }
}

Declaration: ast::Declaration = {
Expand Down
13 changes: 8 additions & 5 deletions crates/parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ use lexer::{
};
use std::ops::Range;

#[cfg(test)]
mod tests;

pub type Span = Range<usize>;

lalrpop_mod!(pub folidity);
Expand Down Expand Up @@ -44,8 +47,11 @@ pub fn parse(src: &str) -> Result<Source, Vec<Report>> {
reports.push(parser_error_to_report(&e));
Err(reports)
}
Ok(_) if !reports.is_empty() => Err(reports),
Ok(tree) => Ok(tree),
// Ok(_) if !reports.is_empty() => Err(reports),
Ok(mut tree) => {
tree.diagnostics.extend(reports);
Ok(tree)
}
}
}

Expand Down Expand Up @@ -114,6 +120,3 @@ fn parser_error_to_report(error: &ParseError<usize, Token<'_>, LexicalError>) ->
ParseError::User { error } => Report::from(error.clone()),
}
}

#[cfg(test)]
mod tests;
3 changes: 3 additions & 0 deletions crates/parser/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ fn test_factorial() -> Result<(), String> {
fn test_factorial_tree() -> Result<(), String> {
let tree = unwrap_tree(FACTORIAL_SRC)?;
let parsed = Source {
diagnostics: vec![],
declarations: vec![
Declaration::StateDeclaration(Box::new(StateDeclaration {
loc: 1..17,
Expand Down Expand Up @@ -427,6 +428,7 @@ fn () lists() {
fn test_lists() -> Result<(), String> {
let tree = unwrap_tree(LISTS_SRC)?;
let parsed = Source {
diagnostics: vec![],
declarations: vec![Declaration::FunDeclaration(Box::new(FunctionDeclaration {
loc: 1..148,
is_init: false,
Expand Down Expand Up @@ -583,6 +585,7 @@ fn test_structs_enums() -> Result<(), String> {
let parsed = unwrap_tree(STRUCTS_SRC)?;

let tree = Source {
diagnostics: vec![],
declarations: vec![
Declaration::StructDeclaration(Box::new(StructDeclaration {
loc: 1..47,
Expand Down
9 changes: 7 additions & 2 deletions crates/semantics/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ pub struct StructDeclaration {
pub fields: Vec<Param>,
}

#[derive(Clone, Debug, PartialEq, Node)]
#[derive(Clone, Debug, Node)]
pub struct ModelDeclaration {
/// Location span of the model.
pub loc: Span,
Expand All @@ -268,6 +268,8 @@ pub struct ModelDeclaration {
pub bounds: Vec<Expression>,
/// Is the parent model recursive.
pub recursive_parent: bool,
/// Scope table for the bounds context.
pub scope: Scope,
}

#[derive(Clone, Debug, PartialEq)]
Expand All @@ -278,7 +280,7 @@ pub enum StateBody {
Model(SymbolInfo),
}

#[derive(Clone, Debug, PartialEq, Node)]
#[derive(Clone, Debug, Node)]
pub struct StateDeclaration {
/// Location span of the model.
pub loc: Span,
Expand All @@ -293,6 +295,8 @@ pub struct StateDeclaration {
pub bounds: Vec<Expression>,
/// Is the parent state recursive.
pub recursive_parent: bool,
/// Scope table for the bounds context.
pub scope: Scope,
}

#[derive(Clone, Debug, PartialEq, Node)]
Expand Down Expand Up @@ -520,6 +524,7 @@ impl TypeVariant {

/// Extracts literal value, `T`, from the expression, if possible.
pub trait TryGetValue<T> {
#[allow(clippy::result_unit_err)]
fn try_get(&self) -> Result<T, ()>;
}

Expand Down
2 changes: 2 additions & 0 deletions crates/semantics/src/bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ pub fn resolve_bounds(contract: &mut ContractDefinition, delay: &DelayedDeclarat
continue;
};

contract.models[model_delay.i].scope = scope;
contract.models[model_delay.i].bounds = bounds;
}

Expand Down Expand Up @@ -115,6 +116,7 @@ pub fn resolve_bounds(contract: &mut ContractDefinition, delay: &DelayedDeclarat
};

contract.states[state_delay.i].bounds = bounds;
contract.states[state_delay.i].scope = scope;
}

for func_delay in &delay.functions {
Expand Down
21 changes: 13 additions & 8 deletions crates/semantics/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@ use folidity_parser::{
};
use indexmap::IndexMap;

use crate::ast::{
EnumDeclaration,
Function,
ModelDeclaration,
Param,
StateBody,
StateDeclaration,
StructDeclaration,
use crate::{
ast::{
EnumDeclaration,
Function,
ModelDeclaration,
Param,
StateBody,
StateDeclaration,
StructDeclaration,
},
symtable::Scope,
};

use crate::{
Expand Down Expand Up @@ -309,6 +312,7 @@ impl ContractDefinition {
parent: None,
bounds: Vec::new(),
recursive_parent: false,
scope: Scope::default(),
});

delay
Expand Down Expand Up @@ -339,6 +343,7 @@ impl ContractDefinition {
from: None,
bounds: Vec::new(),
recursive_parent: false,
scope: Scope::default(),
});

delay
Expand Down
69 changes: 48 additions & 21 deletions crates/semantics/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use bounds::resolve_bounds;
use contract::ContractDefinition;
pub use contract::ContractDefinition;
use folidity_diagnostics::Report;
use folidity_parser::ast::Source;
use functions::resolve_func_body;
use types::check_inheritance;

mod ast;
pub mod ast;
mod bounds;
mod contract;
mod expression;
Expand All @@ -17,29 +18,55 @@ mod types;
#[cfg(test)]
mod tests;

/// Resolves the contract's parsed tree into the semantically analysed and typed-checked
/// definition.
///
/// # Errors
/// [`ContractDefinition`] may contain errors stored in the `diagnostics` field.
pub fn resolve_semantics(source: &Source) -> ContractDefinition {
let mut definition = ContractDefinition::default();
let mut delay = definition.resolve_declarations(source);
definition.resolve_fields(&delay);
#[derive(Debug, Clone)]
pub enum CompilationError {
Syntax(Vec<Report>),
Formal(Vec<Report>),
Emit(Vec<Report>),
}

check_inheritance(&mut definition, &delay);
impl CompilationError {
pub fn diagnostics(&self) -> &Vec<Report> {
match self {
CompilationError::Syntax(r) => r,
CompilationError::Formal(r) => r,
CompilationError::Emit(r) => r,
}
}
}

// todo: add built-in function to environment.
/// Recursively walk the tree and modify the program artifacts.
pub trait Runner<S> {
fn run(source: &S) -> Result<Self, CompilationError>
where
Self: std::marker::Sized;
}

// we can now resolve functions and create scopes.
definition.resolve_functions(source, &mut delay);
impl Runner<Source> for ContractDefinition {
fn run(source: &Source) -> Result<ContractDefinition, CompilationError> {
let mut definition = ContractDefinition::default();
definition.diagnostics.extend(source.diagnostics.clone());
let mut delay = definition.resolve_declarations(source);
definition.resolve_fields(&delay);

// now we can resolve model bounds on all declarations.
resolve_bounds(&mut definition, &delay);
check_inheritance(&mut definition, &delay);

for f in &delay.functions {
let _ = resolve_func_body(&f.decl, f.i, &mut definition);
}
// todo: add built-in function to environment.

definition
// we can now resolve functions and create scopes.
definition.resolve_functions(source, &mut delay);

// now we can resolve model bounds on all declarations.
resolve_bounds(&mut definition, &delay);

for f in &delay.functions {
let _ = resolve_func_body(&f.decl, f.i, &mut definition);
}

if !definition.diagnostics.is_empty() {
return Err(CompilationError::Syntax(definition.diagnostics));
}

Ok(definition)
}
}
Loading

0 comments on commit 8a1dd1f

Please sign in to comment.