Skip to content

Commit

Permalink
Compiler executable 1 (#9)
Browse files Browse the repository at this point in the history
* fix state member access in functions

* add example and `new` command

* cleanup

* read bytes directly from templates

* Add checking and error reporting

* improve error reporting

* remove auto generated folidity files

* prettify output and disable yansi in tests

* update readme in template

* add changelog
  • Loading branch information
SkymanOne authored Mar 25, 2024
1 parent 2ae0733 commit 3bd02fa
Show file tree
Hide file tree
Showing 24 changed files with 521 additions and 173 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -307,4 +307,6 @@ TSWLatexianTemp*
Cargo.lock
.DS_Store

reports/ecsproject.pdf
reports/ecsproject.pdf

/auto
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Changelog

Contains important changes as part of the project development adhering to [semantic versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Added
- Compiler executable 1 - [#9](https://github.com/SkymanOne/folidity/pull/9)
- Semantics 1 - [#7](https://github.com/SkymanOne/folidity/pull/7)
- Parser - [#4](https://github.com/SkymanOne/folidity/pull/4)
- Grammar specification - [#1](https://github.com/SkymanOne/folidity/pull/1)
8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,10 @@ num-bigint = "0.4"
num-rational = "0.4"
num-traits = "0.2"
algonaut_core = "0.4"
hex = "0.4"
hex = "0.4"
regex = "1.10"
clap = { version ="4.5", features = ["derive"]}
ariadne = { version = "0.4", features = ["auto-color"] }
anyhow = "1.0"
walkdir = "2.5"
yansi = "1.0"
4 changes: 3 additions & 1 deletion crates/diagnostics/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ version.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
lalrpop-util = { workspace = true }
lalrpop-util = { workspace = true }
ariadne = { workspace = true }
yansi = { workspace = true }
37 changes: 36 additions & 1 deletion crates/diagnostics/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
use std::ops::Range;
use std::{
fmt::Display,
ops::Range,
};

pub type Span = Range<usize>;

pub use yansi::{
Color,
Paint,
};

pub fn disable_pretty_print() {
yansi::disable();
}

#[derive(Debug, Clone)]
pub enum ErrorType {
Lexer,
Expand All @@ -11,13 +23,36 @@ pub enum ErrorType {
Functional,
}

impl Display for ErrorType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut word = |s: &str| -> std::fmt::Result { write!(f, "{s}") };
match self {
ErrorType::Lexer => word("Lexical error"),
ErrorType::Parser => word("Parser error"),
ErrorType::Semantics => word("Semantic error"),
ErrorType::Type => word("Type error"),
ErrorType::Functional => word("Functional error"),
}
}
}

#[derive(Debug, Clone)]
pub enum Level {
Info,
Warning,
Error,
}

impl<'a> From<Level> for ariadne::ReportKind<'a> {
fn from(val: Level) -> Self {
match &val {
Level::Info => Self::Advice,
Level::Warning => Self::Warning,
Level::Error => Self::Error,
}
}
}

/// Error report.
#[derive(Debug, Clone)]
pub struct Report {
Expand Down
9 changes: 8 additions & 1 deletion crates/folidity/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,11 @@ version.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
folidity-parser = { workspace = true }
folidity-parser = { workspace = true }
folidity-semantics = { workspace = true }
folidity-diagnostics = { workspace = true }
clap = { workspace = true }
ariadne = { workspace = true }
anyhow = { workspace = true }
walkdir = { workspace = true }
yansi = { workspace = true }
50 changes: 50 additions & 0 deletions crates/folidity/src/cmd/check.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use anyhow::Result;
use ariadne::{
Color,
Fmt,
};
use folidity_parser::parse;
use folidity_semantics::resolve_semantics;
use std::ffi::OsString;

use clap::Args;

use super::{
build_report,
read_contract,
};

/// Check the contract's code for parser, semantic and type errors.
#[derive(Args)]
pub struct CheckCommand {
/// Contract's file name
#[clap(value_parser)]
contract: OsString,
}

impl CheckCommand {
pub fn run(&self) -> Result<()> {
let contract_contents = read_contract(&self.contract)?;
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.")
}
}
Err(errors) => {
build_report(&contract_contents, &errors, self.contract.to_str().unwrap());
anyhow::bail!("Error during parsing")
}
}
}
}
74 changes: 74 additions & 0 deletions crates/folidity/src/cmd/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use std::{
ffi::OsString,
fs::File,
io::Read,
path::Path,
};

use anyhow::{
Context,
Result,
};
use clap::Subcommand;
use folidity_diagnostics::Report;
use yansi::Paint;

use self::{
check::CheckCommand,
new::NewCommand,
};
use ariadne::{
Color,
Label,
Report as PrettyReport,
Source,
};

mod check;
mod new;

#[derive(Subcommand)]
pub enum Commands {
New(NewCommand),
Check(CheckCommand),
}

impl Commands {
pub fn run(&self) -> Result<()> {
match self {
Commands::New(cmd) => cmd.run(),
Commands::Check(cmd) => cmd.run(),
}
}
}

pub fn read_contract(path_str: &OsString) -> Result<String> {
let path = Path::new(path_str);
if !path.exists() {
anyhow::bail!("File does not exist.");
}
let s = path.file_name().context("This is not a valid path.")?;
if !s.to_string_lossy().ends_with(".fol") {
anyhow::bail!("File is not a valid folidity contract.")
}
let mut file = File::open(path).context("Could not open a file.")?;
let mut buffer = String::new();
file.read_to_string(&mut buffer)
.context("Failed to read file contents")?;
Ok(buffer)
}

pub fn build_report(content: &str, diagnostics: &[Report], file_name: &str) {
for r in diagnostics {
PrettyReport::build(r.level.clone().into(), file_name, r.loc.start)
.with_message(format!("{} detected.", r.error_type.cyan().underline()))
.with_label(
Label::new((file_name, r.loc.clone()))
.with_message(r.message.clone())
.with_color(Color::Yellow),
)
.finish()
.print((file_name, Source::from(content)))
.unwrap();
}
}
60 changes: 60 additions & 0 deletions crates/folidity/src/cmd/new.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use anyhow::Result;
use std::{
ffi::OsString,
fs::{
create_dir,
File,
},
io::Write,
path::Path,
};
use walkdir::WalkDir;

use clap::Args;

/// Creates a new templated `folidity` counter project.
/// with a basic contract, README and approval teal code.
#[derive(Args)]
pub struct NewCommand {
/// Path to the new project.
/// If empty, the project will be created in the current dir.
#[clap(value_parser)]
name: Option<OsString>,
}

impl NewCommand {
pub fn run(&self) -> Result<()> {
let out_dir = self.name.clone().unwrap_or(OsString::from("."));
let out_path = Path::new(&out_dir);
if out_path.exists() {
for entry in WalkDir::new(out_path)
.follow_links(true)
.into_iter()
.filter_map(|e| e.ok())
{
let f_name = entry.file_name().to_string_lossy();
let sec = entry.metadata()?.modified()?;

if f_name.ends_with(".fol") && sec.elapsed()?.as_secs() < 86400 {
anyhow::bail!(
"Project with this name already exist in {}",
out_dir.to_str().unwrap()
);
}
}
} else {
create_dir(&out_dir).map(|_| anyhow::anyhow!("Cannot create project directory."))?;
}

let contract_content = include_bytes!("../../../../examples/counter/counter.fol");
let readme_content = include_bytes!("../../../../examples/counter/README.md");

let mut contract_file = File::create(Path::new(&out_dir).join("counter.fol"))?;
contract_file.write_all(contract_content)?;

let mut readme_file = File::create(Path::new(&out_dir).join("README.md"))?;
readme_file.write_all(readme_content)?;

Ok(())
}
}
29 changes: 28 additions & 1 deletion crates/folidity/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
use clap::Parser;
use cmd::Commands;
use yansi::{
Color,
Paint,
};

mod cmd;

#[derive(Parser)]
#[command(author = env!("CARGO_PKG_AUTHORS"), version = concat!("version ", env!("CARGO_PKG_VERSION")), about = env!("CARGO_PKG_DESCRIPTION"), subcommand_required = true)]
struct Cli {
#[command(subcommand)]
command: Commands,
}

fn main() {
println!("Hello, world!");
let cli = Cli::parse();
match cli.command.run() {
Ok(()) => {}
Err(err) => {
eprintln!(
"{} {}",
"ERROR:".fg(Color::Red).bold(),
err.to_string().fg(Color::Red)
);
std::process::exit(1);
}
}
}
2 changes: 1 addition & 1 deletion crates/parser/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ lalrpop-util = { workspace = true }
thiserror = { workspace = true }
derive-node = { workspace = true }
folidity-diagnostics = { workspace = true }
regex = "*"
regex = { workspace = true }

[build-dependencies] # <-- We added this and everything after!
lalrpop = { workspace = true }
7 changes: 7 additions & 0 deletions crates/parser/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,13 @@ impl FuncReturnType {
FuncReturnType::ParamType(pty) => &pty.ty.ty,
}
}

pub fn loc(&self) -> &Span {
match self {
FuncReturnType::Type(ty) => &ty.loc,
FuncReturnType::ParamType(param) => &param.loc,
}
}
}

#[derive(Clone, Debug, PartialEq, Node)]
Expand Down
2 changes: 1 addition & 1 deletion crates/semantics/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ num-bigint = { workspace = true }
num-rational = { workspace = true }
num-traits = { workspace = true }
algonaut_core = { workspace = true }
hex = { workspace = true }
hex = { workspace = true }
2 changes: 1 addition & 1 deletion crates/semantics/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ pub struct Param {
pub struct ViewState {
pub loc: Span,
/// State type identifier.
pub ty: usize,
pub ty: SymbolInfo,
/// Variable name identifier.
pub name: Identifier,
}
Expand Down
Loading

0 comments on commit 3bd02fa

Please sign in to comment.