From 272be0d249d5e3820b1688ecaf74c19cdf364854 Mon Sep 17 00:00:00 2001 From: James Harton Date: Mon, 22 Aug 2022 11:30:39 +1200 Subject: [PATCH] wip: use `miette` for compiler errors. --- .vscode/settings.json | 6 +- Cargo.lock | 260 ++++++++++++++++++ outrun-common/Cargo.toml | 1 + outrun-common/src/lib.rs | 2 + outrun-common/src/stack.rs | 5 +- outrun-compiler/Cargo.toml | 3 + outrun-compiler/src/error.rs | 74 ++++- outrun-compiler/src/grammar/ast.rs | 5 + outrun-compiler/src/grammar/error.rs | 139 ++++++++-- outrun-compiler/src/grammar/grammar.pest | 5 +- outrun-compiler/src/grammar/mod.rs | 52 ++-- outrun-compiler/src/grammar/visitor.rs | 41 ++- outrun-compiler/src/ir/error.rs | 32 ++- outrun-compiler/src/ir/expression.rs | 14 +- outrun-compiler/src/ir/function.rs | 17 +- outrun-compiler/src/ir/mod.rs | 4 +- outrun-compiler/src/ir/module.rs | 75 +++-- outrun-compiler/src/ir/notice.rs | 4 +- outrun-compiler/src/ir/type.rs | 4 - outrun-compiler/src/ir/visitor.rs | 172 ++++++++++-- outrun-compiler/src/lib.rs | 66 +---- outrun-compiler/src/source.rs | 96 +++++++ outrun-compiler/src/span.rs | 22 +- outrun-compiler/src/types.rs | 28 -- outrun-core/Cargo.toml | 1 + outrun-core/lib/{abs.run => abs.outrun} | 0 .../lib/{addition.run => addition.outrun} | 0 outrun-core/lib/{any.run => any.outrun} | 4 +- .../{bitwise_and.run => bitwise_and.outrun} | 0 .../lib/{bitwise_or.run => bitwise_or.outrun} | 0 .../{bitwise_xor.run => bitwise_xor.outrun} | 0 .../lib/{boolean.run => boolean.outrun} | 3 +- .../lib/{division.run => division.outrun} | 0 .../lib/{equality.run => equality.outrun} | 0 outrun-core/lib/{error.run => error.outrun} | 3 +- ...ponentiation.run => exponentiation.outrun} | 0 outrun-core/lib/{float.run => float.outrun} | 0 .../lib/{greater.run => greater.outrun} | 0 ...r_or_equal.run => greater_or_equal.outrun} | 0 .../lib/{integer.run => integer.outrun} | 0 outrun-core/lib/{less.run => less.outrun} | 0 ...less_or_equal.run => less_or_equal.outrun} | 0 .../{logical_and.run => logical_and.outrun} | 0 outrun-core/lib/logical_not.outrun | 13 + .../lib/{logical_or.run => logical_or.outrun} | 2 +- .../lib/{modulus.run => modulus.outrun} | 0 ...ltiplication.run => multiplication.outrun} | 0 .../lib/{negation.run => negation.outrun} | 0 outrun-core/lib/{option.run => option.outrun} | 3 +- .../core/{boolean.run => boolean.outrun} | 8 + .../outrun/core/{error.run => error.outrun} | 4 +- .../outrun/core/{float.run => float.outrun} | 0 .../core/{integer.run => integer.outrun} | 0 .../lib/outrun/core/{none.run => none.outrun} | 2 +- .../lib/outrun/core/{okay.run => okay.outrun} | 4 +- .../lib/outrun/core/{some.run => some.outrun} | 2 +- outrun-core/lib/{result.run => result.outrun} | 3 +- .../lib/{shift_left.run => shift_left.outrun} | 0 .../{shift_right.run => shift_right.outrun} | 0 .../{subtraction.run => subtraction.outrun} | 0 outrun-core/src/lib.rs | 84 +++--- 61 files changed, 974 insertions(+), 289 deletions(-) create mode 100644 outrun-compiler/src/source.rs delete mode 100644 outrun-compiler/src/types.rs rename outrun-core/lib/{abs.run => abs.outrun} (100%) rename outrun-core/lib/{addition.run => addition.outrun} (100%) rename outrun-core/lib/{any.run => any.outrun} (68%) rename outrun-core/lib/{bitwise_and.run => bitwise_and.outrun} (100%) rename outrun-core/lib/{bitwise_or.run => bitwise_or.outrun} (100%) rename outrun-core/lib/{bitwise_xor.run => bitwise_xor.outrun} (100%) rename outrun-core/lib/{boolean.run => boolean.outrun} (76%) rename outrun-core/lib/{division.run => division.outrun} (100%) rename outrun-core/lib/{equality.run => equality.outrun} (100%) rename outrun-core/lib/{error.run => error.outrun} (90%) rename outrun-core/lib/{exponentiation.run => exponentiation.outrun} (100%) rename outrun-core/lib/{float.run => float.outrun} (100%) rename outrun-core/lib/{greater.run => greater.outrun} (100%) rename outrun-core/lib/{greater_or_equal.run => greater_or_equal.outrun} (100%) rename outrun-core/lib/{integer.run => integer.outrun} (100%) rename outrun-core/lib/{less.run => less.outrun} (100%) rename outrun-core/lib/{less_or_equal.run => less_or_equal.outrun} (100%) rename outrun-core/lib/{logical_and.run => logical_and.outrun} (100%) create mode 100644 outrun-core/lib/logical_not.outrun rename outrun-core/lib/{logical_or.run => logical_or.outrun} (87%) rename outrun-core/lib/{modulus.run => modulus.outrun} (100%) rename outrun-core/lib/{multiplication.run => multiplication.outrun} (100%) rename outrun-core/lib/{negation.run => negation.outrun} (100%) rename outrun-core/lib/{option.run => option.outrun} (90%) rename outrun-core/lib/outrun/core/{boolean.run => boolean.outrun} (85%) rename outrun-core/lib/outrun/core/{error.run => error.outrun} (82%) rename outrun-core/lib/outrun/core/{float.run => float.outrun} (100%) rename outrun-core/lib/outrun/core/{integer.run => integer.outrun} (100%) rename outrun-core/lib/outrun/core/{none.run => none.outrun} (93%) rename outrun-core/lib/outrun/core/{okay.run => okay.outrun} (83%) rename outrun-core/lib/outrun/core/{some.run => some.outrun} (86%) rename outrun-core/lib/{result.run => result.outrun} (93%) rename outrun-core/lib/{shift_left.run => shift_left.outrun} (100%) rename outrun-core/lib/{shift_right.run => shift_right.outrun} (100%) rename outrun-core/lib/{subtraction.run => subtraction.outrun} (100%) diff --git a/.vscode/settings.json b/.vscode/settings.json index 3134a64..0f324e5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -19,9 +19,13 @@ "indoc", "Jiggawatts", "lexer", + "miette", "prec", "Scandroid", "struct", - "typeblock" + "textwrap", + "thiserror", + "typeblock", + "typeunion" ] } diff --git a/Cargo.lock b/Cargo.lock index 285005f..d7848f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,62 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + [[package]] name = "assert_matches" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "backtrace" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "block-buffer" version = "0.10.2" @@ -17,6 +67,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + [[package]] name = "cfg-if" version = "1.0.0" @@ -62,6 +118,27 @@ dependencies = [ "version_check", ] +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "is_ci" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb" + [[package]] name = "lazy_static" version = "1.4.0" @@ -74,6 +151,61 @@ version = "0.2.132" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "miette" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28d6092d7e94a90bb9ea8e6c26c99d5d112d49dda2afdb4f7ea8cf09e1a5a6d" +dependencies = [ + "atty", + "backtrace", + "miette-derive", + "once_cell", + "owo-colors", + "supports-color", + "supports-hyperlinks", + "supports-unicode", + "terminal_size", + "textwrap", + "thiserror", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2485ed7d1fe80704928e3eb86387439609bd0c6bb96db8208daa364cfd1e09" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "miniz_oxide" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +dependencies = [ + "adler", +] + +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.13.1" @@ -90,6 +222,9 @@ dependencies = [ [[package]] name = "outrun-common" version = "0.1.0" +dependencies = [ + "thiserror", +] [[package]] name = "outrun-compiler" @@ -97,18 +232,28 @@ version = "0.1.0" dependencies = [ "assert_matches", "lazy_static", + "miette", "outrun-common", "pest", "pest_derive", + "textwrap", + "thiserror", ] [[package]] name = "outrun-core" version = "0.1.0" dependencies = [ + "miette", "outrun-compiler", ] +[[package]] +name = "owo-colors" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "decf7381921fea4dcb2549c5667eda59b3ec297ab7e2b5fc33eac69d2e7da87b" + [[package]] name = "pest" version = "2.2.1" @@ -171,6 +316,29 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + [[package]] name = "sha-1" version = "0.10.0" @@ -182,6 +350,40 @@ dependencies = [ "digest", ] +[[package]] +name = "smawk" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043" + +[[package]] +name = "supports-color" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4872ced36b91d47bae8a214a683fe54e7078875b399dfa251df346c9b547d1f9" +dependencies = [ + "atty", + "is_ci", +] + +[[package]] +name = "supports-hyperlinks" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "590b34f7c5f01ecc9d78dba4b3f445f31df750a67621cf31626f3b7441ce6406" +dependencies = [ + "atty", +] + +[[package]] +name = "supports-unicode" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8b945e45b417b125a8ec51f1b7df2f8df7920367700d1f98aedd21e5735f8b2" +dependencies = [ + "atty", +] + [[package]] name = "syn" version = "1.0.99" @@ -193,6 +395,27 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "terminal_size" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "textwrap" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] + [[package]] name = "thiserror" version = "1.0.32" @@ -231,8 +454,45 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" +[[package]] +name = "unicode-linebreak" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a52dcaab0c48d931f7cc8ef826fa51690a08e1ea55117ef26f89864f532383f" +dependencies = [ + "regex", +] + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/outrun-common/Cargo.toml b/outrun-common/Cargo.toml index 94ee540..9a8d5bc 100644 --- a/outrun-common/Cargo.toml +++ b/outrun-common/Cargo.toml @@ -9,3 +9,4 @@ version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +thiserror = "1.0" diff --git a/outrun-common/src/lib.rs b/outrun-common/src/lib.rs index 0b97cfc..9212978 100644 --- a/outrun-common/src/lib.rs +++ b/outrun-common/src/lib.rs @@ -1,3 +1,5 @@ +extern crate thiserror; + pub mod indexed_vec; pub mod stack; pub mod unique_vec; diff --git a/outrun-common/src/stack.rs b/outrun-common/src/stack.rs index 37c5062..bd486cb 100644 --- a/outrun-common/src/stack.rs +++ b/outrun-common/src/stack.rs @@ -1,8 +1,11 @@ use std::default::Default; +use thiserror::Error; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Error)] pub enum StackError { + #[error("Stack overflow")] Overflow, + #[error("Stack underflow")] Underflow, } diff --git a/outrun-compiler/Cargo.toml b/outrun-compiler/Cargo.toml index bc07051..a67846a 100644 --- a/outrun-compiler/Cargo.toml +++ b/outrun-compiler/Cargo.toml @@ -10,9 +10,12 @@ version = "0.1.0" [dependencies] lazy_static = "1.4" +miette = {version = "5.3", features = ["fancy"]} outrun-common = {path = "../outrun-common"} pest = "2.2" pest_derive = "2.2" +textwrap = "0.15.0" +thiserror = "1.0" [dev-dependencies] assert_matches = "1.5" diff --git a/outrun-compiler/src/error.rs b/outrun-compiler/src/error.rs index c7a6e0c..acda2b2 100644 --- a/outrun-compiler/src/error.rs +++ b/outrun-compiler/src/error.rs @@ -1,20 +1,76 @@ use crate::grammar::Error as GrammarError; use crate::ir::Error as IrError; +use crate::source::Source; +use crate::span::Span; +use miette::Diagnostic; +use std::io::Error as IoError; +use std::num::{ParseFloatError, ParseIntError}; +use std::sync::Arc; +use thiserror::Error; -#[derive(Debug)] +#[derive(Debug, Error, Diagnostic)] pub enum Error { - ParseError(GrammarError), - IrError(IrError), + #[error("Error while parsing")] + #[diagnostic()] + ParseError { + #[source_code] + source: Arc, + #[source] + error: GrammarError, + #[label] + span: Span, + }, + #[error("Error while parsing")] + #[diagnostic()] + IrError { + #[source_code] + source: Arc, + #[source] + error: IrError, + #[label] + span: Option, + }, + #[error("I/O error")] + #[diagnostic()] + IoErrorWithSource { + #[source_code] + source: Arc, + #[source] + error: IoError, + }, + #[error("I/O error")] + #[diagnostic()] + IoError( + #[from] + #[source] + IoError, + ), } -impl From for Error { - fn from(error: GrammarError) -> Self { - Error::ParseError(error) +impl From<(Arc, GrammarError)> for Error { + fn from((source, error): (Arc, GrammarError)) -> Self { + let span = error.as_span(); + Error::ParseError { + source, + error, + span, + } } } -impl From for Error { - fn from(error: IrError) -> Self { - Error::IrError(error) +impl From<(Arc, IrError)> for Error { + fn from((source, error): (Arc, IrError)) -> Self { + let span = error.maybe_span(); + Self::IrError { + source, + error, + span, + } + } +} + +impl From<(Arc, IoError)> for Error { + fn from((source, error): (Arc, IoError)) -> Self { + Self::IoErrorWithSource { source, error } } } diff --git a/outrun-compiler/src/grammar/ast.rs b/outrun-compiler/src/grammar/ast.rs index 5b8c7ca..e7e7127 100644 --- a/outrun-compiler/src/grammar/ast.rs +++ b/outrun-compiler/src/grammar/ast.rs @@ -155,6 +155,7 @@ pub enum NodeKind { Float, GetLocal, Impl, + Index, Infix, Integer, Let, @@ -192,6 +193,10 @@ pub enum NodeValue { name: Box, props: HashMap, }, + Index { + expr: Box, + field_name: String, + }, Infix { op: Operator, lhs: Box, diff --git a/outrun-compiler/src/grammar/error.rs b/outrun-compiler/src/grammar/error.rs index 66a7299..cd3a44e 100644 --- a/outrun-compiler/src/grammar/error.rs +++ b/outrun-compiler/src/grammar/error.rs @@ -1,43 +1,136 @@ use super::Rule; use crate::span::Span; +use std::fmt; use std::num::{ParseFloatError, ParseIntError}; +use thiserror::Error; -#[derive(Debug)] +#[derive(Error, Debug)] pub enum Error { - ParseError(pest::error::Error), - ParseIntegerError(ParseIntError), - ParseFloatError(ParseFloatError), + ParseIntError(Span, #[source] ParseIntError), + ParseFloatError(Span, #[source] ParseFloatError), Unexpected { received: Rule, - expected: Option, + span: Span, + }, + UnexpectedExpected { + received: Rule, + expected: Rule, + span: Span, + }, + PestError { + positives: Vec, + negatives: Vec, span: Span, }, } impl Error { pub fn unexpected>(received: Rule, expected: Option, span: T) -> Error { - Error::Unexpected { - expected, - received, - span: span.into(), + if expected.is_some() { + Error::UnexpectedExpected { + received, + expected: expected.unwrap(), + span: span.into(), + } + } else { + Error::Unexpected { + received, + span: span.into(), + } + } + } + + pub fn as_span(&self) -> Span { + match self { + Error::ParseIntError(span, _) => *span, + Error::ParseFloatError(span, _) => *span, + Error::Unexpected { span, .. } => *span, + Error::UnexpectedExpected { span, .. } => *span, + Error::PestError { span, .. } => *span, + } + } +} + +fn to_list(rules: &[Rule]) -> String { + let mut result = String::new(); + let len = rules.len(); + + for (i, r) in rules.iter().enumerate() { + result.push_str(&format!("`{:?}`", r)); + + match len - i { + 1 => break, + 2 => result.push_str(" or "), + _ => result.push_str(", "), + } + } + + result +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::ParseIntError(_, error) => write!(f, "Unable to parse integer: {}", error), + Error::ParseFloatError(_, error) => write!(f, "Unable to parse float: {}", error), + Error::Unexpected { received, .. } => { + write!(f, "Unexpectedly received: `{:?}` token", received) + } + Error::UnexpectedExpected { + expected, received, .. + } => write!( + f, + "Expected `{:?}` token, but received `{:?}` token", + expected, received + ), + Error::PestError { + positives, + negatives, + .. + } => match (positives.len(), negatives.len()) { + (0, 0) => write!(f, "Unknown parsing error"), + (0, 1) => write!(f, "Unexpectedly received `{:?}` token", negatives[0]), + (0, _) => write!( + f, + "Unexpectedly received one of {} tokens", + to_list(negatives) + ), + (1, 0) => write!(f, "Expected `{:?}` token", positives[0]), + (_, 0) => write!(f, "Expected one of {} tokens", to_list(positives)), + (1, 1) => write!( + f, + "Expected `{:?}` token, but received `{:?}`", + positives[0], negatives[0] + ), + (1, _) => write!( + f, + "Expected `{:?}` token, but received one of {}", + positives[0], + to_list(negatives) + ), + (_, _) => write!( + f, + "Expected one of {} tokens, but received one of {}", + to_list(positives), + to_list(negatives) + ), + }, } } } impl From> for Error { - fn from(error: pest::error::Error) -> Self { - Error::ParseError(error) - } -} - -impl From for Error { - fn from(error: ParseIntError) -> Self { - Error::ParseIntegerError(error) - } -} - -impl From for Error { - fn from(error: ParseFloatError) -> Self { - Error::ParseFloatError(error) + fn from(err: pest::error::Error) -> Self { + match err.variant { + pest::error::ErrorVariant::ParsingError { + positives, + negatives, + } => Error::PestError { + positives, + negatives, + span: err.location.into(), + }, + _ => unreachable!("We don't use any custom errors in Pest"), + } } } diff --git a/outrun-compiler/src/grammar/grammar.pest b/outrun-compiler/src/grammar/grammar.pest index aea71f7..ba929db 100644 --- a/outrun-compiler/src/grammar/grammar.pest +++ b/outrun-compiler/src/grammar/grammar.pest @@ -48,7 +48,8 @@ body_expression = _{ let_binding | expression } expression = _{ infix | expression_inner } infix = { expression_inner ~ (binary_operator ~ expression_inner)+ } -expression_inner = _{ unary | literal | call_remote | call_local | local | braced_expression } +expression_inner = _{ unary | literal | index | call_remote | call_local | local | braced_expression } +index = { (local | literal | braced_expression) ~ "." ~ ident } call_remote = { typename ~ "." ~ ident ~ "(" ~ call_args ~ ")" } call_local = { local ~ "(" ~ call_args ~ ")" } call_args = _{ (expression ~ ("," ~ expression)*)? } @@ -57,7 +58,7 @@ let_binding = { "let" ~ ident ~ maybe_type_annotation ~ "=" ~ e unary = { unary_operator ~ (literal | local | braced_expression) } literal = _{ string | atom | boolean | float | integer | string | array | map | constructor } -ident = @{ ("_")? ~ LOWERCASE_LETTER ~ (LETTER | NUMBER | "_")* ~ ("?")? } +ident = @{ ("_" | LOWERCASE_LETTER) ~ (LETTER | NUMBER | "_")* ~ ("?")? } keyword = ${ ident ~ ":" } maybe_type_annotation = { ( ":" ~ typename )? } diff --git a/outrun-compiler/src/grammar/mod.rs b/outrun-compiler/src/grammar/mod.rs index 7c5190b..8e5e29b 100644 --- a/outrun-compiler/src/grammar/mod.rs +++ b/outrun-compiler/src/grammar/mod.rs @@ -3,24 +3,20 @@ mod error; mod precedence; mod visitor; +use crate::source::Source; pub use ast::Operator; pub use ast::{Node, NodeKind, NodeValue}; pub use error::Error; use pest::Parser; +use std::sync::Arc; #[derive(Parser)] #[grammar = "grammar/grammar.pest"] pub struct Grammar; -pub fn parse_file(input: &str) -> Result, Error> { - let pairs = Grammar::parse(Rule::file, input)?; - let nodes = visitor::visit_pairs(pairs)?; - - Ok(nodes) -} - -pub fn parse_input(input: &str) -> Result, Error> { - let pairs = Grammar::parse(Rule::input, input)?; +pub fn parse(source: Arc, rule: Option) -> Result, Error> { + let rule = rule.unwrap_or(Rule::file); + let pairs = Grammar::parse(rule, source.as_str())?; let nodes = visitor::visit_pairs(pairs)?; Ok(nodes) @@ -31,10 +27,14 @@ mod test { use super::*; use ast::{NodeKind, NodeValue, Operator}; + fn to_source(input: &str) -> Arc { + Arc::new(input.into()) + } + #[test] fn test_empty_struct() { let input = "struct Empty"; - let ast = parse_file(input).unwrap(); + let ast = parse(to_source(input), Some(Rule::file)).unwrap(); let node = ast.first().unwrap(); assert_eq!(node.kind, NodeKind::Struct); @@ -48,7 +48,7 @@ mod test { #[test] fn test_struct_with_fields() { let input = "struct WithFields(first: Boolean, second: Integer)"; - let ast = parse_file(input).unwrap(); + let ast = parse(to_source(input), Some(Rule::file)).unwrap(); let node = ast.first().unwrap(); assert_eq!(node.kind, NodeKind::Struct); @@ -76,7 +76,7 @@ mod test { end end "#; - let ast = parse_file(input).unwrap(); + let ast = parse(to_source(input), Some(Rule::file)).unwrap(); let node = ast.first().unwrap(); assert_eq!(node.kind, NodeKind::Struct); @@ -90,7 +90,7 @@ mod test { #[test] fn test_let_without_annotation() { let input = r#"let soundtrack = "Passing Breeze""#; - let ast = parse_input(input).unwrap(); + let ast = parse(to_source(input), Some(Rule::input)).unwrap(); let node = ast.first().unwrap(); assert_eq!(node.kind, NodeKind::Let); @@ -104,7 +104,7 @@ mod test { #[test] fn test_infix_binary() { let input = r#"1 + 2 * 3"#; - let ast = parse_input(input).unwrap(); + let ast = parse(to_source(input), Some(Rule::input)).unwrap(); let node = ast.first().unwrap(); assert_matches!(node.kind, NodeKind::Infix); @@ -123,7 +123,7 @@ mod test { #[test] fn test_unary() { let input = "-1"; - let ast = parse_input(input).unwrap(); + let ast = parse(to_source(input), Some(Rule::input)).unwrap(); let node = ast.first().unwrap(); assert_eq!(node.kind, NodeKind::Unary); @@ -135,7 +135,7 @@ mod test { #[test] fn test_call_remote() { let input = "Outrun.NIF.integer_add(self, other)"; - let ast = parse_input(input).unwrap(); + let ast = parse(to_source(input), Some(Rule::input)).unwrap(); let node = ast.first().unwrap(); assert_eq!(node.kind, NodeKind::RemoteCall); @@ -153,7 +153,7 @@ mod test { #[test] fn test_string() { let input = r#""this is a \u0008 string\n""#; - let ast = parse_input(input).unwrap(); + let ast = parse(to_source(input), Some(Rule::input)).unwrap(); let node = ast.first().unwrap(); assert_is_string(node, "this is a \u{08} string\n"); @@ -162,7 +162,7 @@ mod test { #[test] fn test_atom() { let input = r":GUNSHIP"; - let ast = parse_input(input).unwrap(); + let ast = parse(to_source(input), Some(Rule::input)).unwrap(); let node = ast.first().unwrap(); assert_is_atom(node, "GUNSHIP"); @@ -171,7 +171,7 @@ mod test { #[test] fn test_boolean_true() { let input = "true"; - let ast = parse_input(input).unwrap(); + let ast = parse(to_source(input), Some(Rule::input)).unwrap(); let node = ast.first().unwrap(); assert_is_boolean(node, true); @@ -180,7 +180,7 @@ mod test { #[test] fn test_boolean_false() { let input = "false"; - let ast = parse_input(input).unwrap(); + let ast = parse(to_source(input), Some(Rule::input)).unwrap(); let node = ast.first().unwrap(); assert_is_boolean(node, false); @@ -189,7 +189,7 @@ mod test { #[test] fn test_array() { let input = "[1, 2, 3]"; - let ast = parse_input(input).unwrap(); + let ast = parse(to_source(input), Some(Rule::input)).unwrap(); let node = ast.first().unwrap(); assert_eq!(node.kind, NodeKind::Array); @@ -204,7 +204,7 @@ mod test { #[test] fn test_map() { let input = "{a: 1, :b => 2}"; - let ast = parse_input(input).unwrap(); + let ast = parse(to_source(input), Some(Rule::input)).unwrap(); let node = ast.first().unwrap(); assert_eq!(node.kind, NodeKind::Map); @@ -224,7 +224,7 @@ mod test { #[test] fn test_constructor() { let input = "AB{a: 1, b: 2}"; - let ast = parse_input(input).unwrap(); + let ast = parse(to_source(input), Some(Rule::input)).unwrap(); let node = ast.first().unwrap(); assert_eq!(node.kind, NodeKind::Constructor); @@ -243,7 +243,7 @@ mod test { #[test] fn test_float() { let input = "1.21"; // Jiggawatts! - let ast = parse_input(input).unwrap(); + let ast = parse(to_source(input), Some(Rule::input)).unwrap(); let node = ast.first().unwrap(); assert_is_float(node, 1.21); @@ -252,7 +252,7 @@ mod test { #[test] fn test_local() { let input = "scandroid"; - let ast = parse_input(input).unwrap(); + let ast = parse(to_source(input), Some(Rule::input)).unwrap(); let node = ast.first().unwrap(); assert_is_get_local(node, "scandroid"); @@ -261,7 +261,7 @@ mod test { #[test] fn test_call_local() { let input = "fury_weekend(1, 2, 3)"; - let ast = parse_input(input).unwrap(); + let ast = parse(to_source(input), Some(Rule::input)).unwrap(); let node = ast.first().unwrap(); assert_eq!(node.kind, NodeKind::LocalCall); diff --git a/outrun-compiler/src/grammar/visitor.rs b/outrun-compiler/src/grammar/visitor.rs index 80af2e8..d7eb542 100644 --- a/outrun-compiler/src/grammar/visitor.rs +++ b/outrun-compiler/src/grammar/visitor.rs @@ -37,12 +37,9 @@ pub fn visit_pair(pair: Pair<'_, Rule>) -> Result { Rule::defs => visit_defs(pair), Rule::defstruct => visit_struct(pair), Rule::defuse => visit_use(pair), - Rule::documentation => Ok(Node::new( - NodeKind::Documentation, - NodeValue::Documentation(pair.as_str().to_string()), - pair.as_span(), - )), + Rule::documentation => visit_documentation(pair), Rule::float => visit_float(pair), + Rule::index => visit_index(pair), Rule::infix => climb(pair), Rule::integer => visit_integer(pair), Rule::let_binding => visit_let(pair), @@ -50,6 +47,7 @@ pub fn visit_pair(pair: Pair<'_, Rule>) -> Result { Rule::map => visit_map(pair), Rule::typeblock => visit_typeblock(pair), Rule::typename => visit_typename(pair), + Rule::typeunion => climb(pair), Rule::string => visit_string(pair), Rule::unary => visit_unary(pair), rule => Err(Error::unexpected(rule, None, pair.as_span())), @@ -164,16 +162,43 @@ fn visit_constructor(pair: Pair<'_, Rule>) -> Result { )) } +fn visit_documentation(pair: Pair<'_, Rule>) -> Result { + let span = pair.as_span(); + let docs = textwrap::dedent(pair.as_str()); + + Ok(Node::new( + NodeKind::Documentation, + NodeValue::Documentation(docs), + span, + )) +} + fn visit_float(pair: Pair<'_, Rule>) -> Result { assert_is_rule(&pair, Rule::float)?; let span = pair.as_span(); let value = pair.as_str().replace("_", ""); - let value = f64::from_str(&value)?; + let value = f64::from_str(&value).map_err(|e| Error::ParseFloatError(span.into(), e))?; Ok(Node::new(NodeKind::Float, NodeValue::Float(value), span)) } +fn visit_index(pair: Pair<'_, Rule>) -> Result { + assert_is_rule(&pair, Rule::index)?; + let span = pair.as_span(); + + let mut pairs = pair.into_inner(); + + let expr = visit_pair(pairs.next().unwrap()).map(Box::new)?; + let field_name = pairs.next().unwrap().as_str().to_string(); + + Ok(Node::new( + NodeKind::Index, + NodeValue::Index { expr, field_name }, + span, + )) +} + fn visit_struct(pair: Pair<'_, Rule>) -> Result { assert_is_rule(&pair, Rule::defstruct)?; let span = pair.as_span(); @@ -523,8 +548,10 @@ fn visit_integer(pair: Pair<'_, Rule>) -> Result { Rule::integer_octal => i64::from_str_radix(&str, 8), Rule::integer_binary => i64::from_str_radix(&str, 2), Rule::integer_decimal => i64::from_str_radix(&str, 10), + Rule::integer_zero => Ok(0), _ => unreachable!(), - }?; + } + .map_err(|e| Error::ParseIntError(span.into(), e))?; Ok(Node::new( NodeKind::Integer, diff --git a/outrun-compiler/src/ir/error.rs b/outrun-compiler/src/ir/error.rs index 3fb0f2f..cdf96d2 100644 --- a/outrun-compiler/src/ir/error.rs +++ b/outrun-compiler/src/ir/error.rs @@ -1,20 +1,38 @@ use crate::grammar::NodeKind; +use crate::span::Span; +use miette::Diagnostic; use outrun_common::StackError; +use thiserror::Error; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Error, Diagnostic)] pub enum Error { + #[diagnostic()] + #[error("Failed to parse Outrun source code file; unexpectedly received {received:?} node instead of {expected:?}")] ExpectedReceived { + #[label] + span: Span, expected: Vec, received: NodeKind, }, + #[error("Unknown type {0}")] UnknownType(String), - MissingProperty(String), - NotAFunction, - StackError(StackError), + #[error("Missing property {property}")] + MissingProperty { + #[label] + span: Span, + property: String, + }, + #[error("{0}")] + StackError(#[from] StackError), } -impl From for Error { - fn from(e: StackError) -> Self { - Error::StackError(e) +impl Error { + pub fn maybe_span(&self) -> Option { + match self { + Error::ExpectedReceived { span, .. } => Some(*span), + Error::UnknownType(_) => None, + Error::MissingProperty { span, .. } => Some(*span), + Error::StackError(_) => None, + } } } diff --git a/outrun-compiler/src/ir/expression.rs b/outrun-compiler/src/ir/expression.rs index fa34aba..3a6aa55 100644 --- a/outrun-compiler/src/ir/expression.rs +++ b/outrun-compiler/src/ir/expression.rs @@ -1,5 +1,5 @@ use super::Type; -use crate::grammar::{NodeValue, Operator}; +use crate::grammar::NodeValue; use crate::span::Span; use std::sync::Arc; @@ -18,11 +18,19 @@ pub enum ExpressionValue { Constructor(Vec), Float(f64), GetLocal(String), + Index { + expr: Box, + field_name: String, + }, Integer(i64), Let { name: String, value: Box, }, + LocalCall { + receiver: Box, + arguments: Vec, + }, Map(Vec<(Expression, Expression)>), RemoteCall { associated_type: Arc, @@ -30,10 +38,6 @@ pub enum ExpressionValue { arguments: Vec, }, String(String), - Unary { - op: Operator, - rhs: Box, - }, } impl From for ExpressionValue { diff --git a/outrun-compiler/src/ir/function.rs b/outrun-compiler/src/ir/function.rs index 5369a99..a50c84f 100644 --- a/outrun-compiler/src/ir/function.rs +++ b/outrun-compiler/src/ir/function.rs @@ -1,5 +1,6 @@ use super::{Expression, Type}; use crate::grammar::NodeKind; +use std::cell::RefCell; use std::sync::Arc; #[derive(Debug, Clone)] @@ -14,8 +15,8 @@ pub struct Function { pub name: String, pub arguments: Vec, pub r#type: Arc, - pub body: Vec, - pub guards: Vec, + pub body: RefCell>, + pub guards: RefCell>, pub access: Access, } @@ -30,11 +31,19 @@ impl Function { name, arguments, r#type, - body: Vec::new(), - guards: Vec::new(), + body: RefCell::new(Vec::new()), + guards: RefCell::new(Vec::new()), access: access.into(), } } + + pub fn push_body(&self, expr: Expression) { + self.body.borrow_mut().push(expr); + } + + pub fn push_guard(&self, expr: Expression) { + self.guards.borrow_mut().push(expr); + } } impl From for Access { diff --git a/outrun-compiler/src/ir/mod.rs b/outrun-compiler/src/ir/mod.rs index 2004891..bdb2680 100644 --- a/outrun-compiler/src/ir/mod.rs +++ b/outrun-compiler/src/ir/mod.rs @@ -11,10 +11,10 @@ pub use error::Error; pub use expression::{Expression, ExpressionValue}; pub use function::Function; pub use module::Module; -pub use notice::{Notice, NoticeKind, Severity}; +pub use notice::{Notice, NoticeKind, NoticeLevel}; pub use r#type::{Type, TypeVariable}; pub use visitor::{visit_node, visit_nodes}; -pub fn improve(mut module: Module) -> Result { +pub fn improve(module: Module) -> Result { stages::link_type_references(module) } diff --git a/outrun-compiler/src/ir/module.rs b/outrun-compiler/src/ir/module.rs index 0ed4763..e1f7557 100644 --- a/outrun-compiler/src/ir/module.rs +++ b/outrun-compiler/src/ir/module.rs @@ -1,29 +1,35 @@ -use super::{Error, Function, Notice, NoticeKind, Severity, Type, TypeVariable}; +use super::{Error, Function, Notice, NoticeKind, NoticeLevel, Type, TypeVariable}; +use crate::source::Source; use crate::span::Span; -use outrun_common::Stack; +use outrun_common::{Stack, StackError}; use std::cell::RefCell; -use std::path::{Path, PathBuf}; use std::sync::Arc; #[derive(Debug, Clone)] pub struct Module { - pub path: Option, - pub types: Stack>, - pub functions: Stack, + pub source: Arc, + pub types: Vec>, + pub functions: Vec>, pub docs: Stack, pub notices: Vec, + pub current_type: Stack>, + pub self_type: Stack>, + pub current_function: Stack>, type_id: u64, } impl Module { - pub fn new(path: &Path) -> Module { + pub fn new(source: Arc) -> Module { bootstrap(Module { - path: Some(path.to_owned()), - types: Stack::new(), - functions: Stack::new(), + source, + types: Vec::new(), + functions: Vec::new(), docs: Stack::new(), notices: Vec::new(), type_id: 0, + current_type: Stack::new(), + self_type: Stack::new(), + current_function: Stack::new(), }) } @@ -47,7 +53,7 @@ impl Module { name: name.to_string(), }); let r#type = Arc::new(Type::Variable { r#type, span }); - self.types.push(r#type.clone())?; + self.types.push(r#type.clone()); Ok(r#type) }) } @@ -57,7 +63,7 @@ impl Module { self.type_id += 1; let r#type = RefCell::new(TypeVariable::Unbound { id: type_id }); let r#type = Arc::new(Type::Variable { r#type, span: None }); - self.types.push(r#type.clone())?; + self.types.push(r#type.clone()); Ok(r#type) } @@ -74,7 +80,7 @@ impl Module { name: name.to_string(), span, }); - self.types.push(ty.clone()).unwrap(); + self.types.push(ty.clone()); ty }); @@ -92,17 +98,19 @@ impl Module { target, span, }); - self.types.push(ty.clone())?; + self.types.push(ty.clone()); Ok(ty) } - pub fn current_function(&mut self) -> Result<&mut Function, Error> { - self.functions.peek_mut().ok_or(Error::NotAFunction) + pub fn current_function(&mut self) -> Result<&mut Arc, Error> { + self.current_function + .peek_mut() + .ok_or(StackError::Underflow.into()) } pub fn info(&mut self, span: Option, kind: NoticeKind) { self.notices.push(Notice { - severity: Severity::Info, + level: NoticeLevel::Info, kind, span, }) @@ -110,7 +118,7 @@ impl Module { pub fn warning(&mut self, span: Option, kind: NoticeKind) { self.notices.push(Notice { - severity: Severity::Warning, + level: NoticeLevel::Warning, kind, span, }) @@ -118,7 +126,7 @@ impl Module { pub fn error(&mut self, span: Option, kind: NoticeKind) { self.notices.push(Notice { - severity: Severity::Error, + level: NoticeLevel::Error, kind, span, }) @@ -128,30 +136,17 @@ impl Module { where F: Fn(Arc) -> bool, { - self.types.find(|t| predicate((*t).clone())).cloned() - } -} - -impl Default for Module { - fn default() -> Self { - bootstrap(Module { - path: None, - types: Stack::new(), - functions: Stack::new(), - docs: Stack::new(), - notices: Vec::new(), - type_id: 0, - }) + self.types.iter().find(|t| predicate((*t).clone())).cloned() } } fn bootstrap(mut module: Module) -> Module { - module.types.push(Arc::new(Type::Atom)).unwrap(); - module.types.push(Arc::new(Type::Boolean)).unwrap(); - module.types.push(Arc::new(Type::Integer)).unwrap(); - module.types.push(Arc::new(Type::Float)).unwrap(); - module.types.push(Arc::new(Type::String)).unwrap(); - module.types.push(Arc::new(Type::Array)).unwrap(); - module.types.push(Arc::new(Type::Map)).unwrap(); + module.types.push(Arc::new(Type::Atom)); + module.types.push(Arc::new(Type::Boolean)); + module.types.push(Arc::new(Type::Integer)); + module.types.push(Arc::new(Type::Float)); + module.types.push(Arc::new(Type::String)); + module.types.push(Arc::new(Type::Array)); + module.types.push(Arc::new(Type::Map)); module } diff --git a/outrun-compiler/src/ir/notice.rs b/outrun-compiler/src/ir/notice.rs index 7fa2d0a..34389bb 100644 --- a/outrun-compiler/src/ir/notice.rs +++ b/outrun-compiler/src/ir/notice.rs @@ -1,7 +1,7 @@ use crate::span::Span; #[derive(Debug, Clone)] -pub enum Severity { +pub enum NoticeLevel { Info, Warning, Error, @@ -9,7 +9,7 @@ pub enum Severity { #[derive(Debug, Clone)] pub struct Notice { - pub severity: Severity, + pub level: NoticeLevel, pub kind: NoticeKind, pub span: Option, } diff --git a/outrun-compiler/src/ir/type.rs b/outrun-compiler/src/ir/type.rs index cb6312d..de519d4 100644 --- a/outrun-compiler/src/ir/type.rs +++ b/outrun-compiler/src/ir/type.rs @@ -64,10 +64,6 @@ pub enum Type { } impl Type { - pub fn is_protocol(&self) -> bool { - matches!(self, Type::Protocol { .. }) - } - pub fn is_named(&self) -> bool { match self { Type::Variable { r#type, .. } => r#type.borrow().is_named(), diff --git a/outrun-compiler/src/ir/visitor.rs b/outrun-compiler/src/ir/visitor.rs index 9eaf202..67173e0 100644 --- a/outrun-compiler/src/ir/visitor.rs +++ b/outrun-compiler/src/ir/visitor.rs @@ -35,7 +35,7 @@ fn consume_expression(module: Module, node: Node) -> Result { let (mut module, expression) = visit_expression(module, node)?; module .current_function() - .map(|fun| fun.body.push(expression))?; + .map(|fun| fun.push_body(expression))?; Ok(module) } @@ -51,6 +51,7 @@ fn consume_statement(module: Module, node: Node) -> Result { NodeKind::Struct => consume_type_statement(module, node), NodeKind::Use => visit_use_statement(module, node), other => Err(Error::ExpectedReceived { + span: node.span, expected: vec![ NodeKind::Block, NodeKind::DefPrivate, @@ -60,6 +61,7 @@ fn consume_statement(module: Module, node: Node) -> Result { NodeKind::Let, NodeKind::Protocol, NodeKind::Struct, + NodeKind::Use, ], received: other.clone(), }), @@ -102,7 +104,7 @@ fn eval_type_union(mut module: Module, node: Node) -> Result<(Module, Arc) types, span: node.span, }); - module.types.push(ty.clone())?; + module.types.push(ty.clone()); Ok((module, ty)) } } @@ -116,6 +118,7 @@ fn eval_type_union_collect( ) -> Result<(Module, Vec>), Error> { if !matches!(node.kind, NodeKind::Infix | NodeKind::TypeName) { return Err(Error::ExpectedReceived { + span: node.span, expected: vec![NodeKind::Infix, NodeKind::TypeName], received: node.kind.clone(), }); @@ -157,14 +160,17 @@ fn visit_expression(module: Module, node: Node) -> Result<(Module, Expression), NodeKind::Constructor => visit_constructor(module, node), NodeKind::Float => visit_float(module, node), NodeKind::GetLocal => visit_get_local(module, node), + NodeKind::Index => visit_index(module, node), NodeKind::Infix => visit_infix(module, node), NodeKind::Integer => visit_integer(module, node), NodeKind::Let => visit_let_expression(module, node), + NodeKind::LocalCall => visit_local_call(module, node), NodeKind::Map => visit_map(module, node), NodeKind::RemoteCall => visit_remote_call(module, node), NodeKind::String => visit_string(module, node), NodeKind::Unary => visit_unary(module, node), other => Err(Error::ExpectedReceived { + span: node.span, expected: vec![ NodeKind::Array, NodeKind::Atom, @@ -172,11 +178,15 @@ fn visit_expression(module: Module, node: Node) -> Result<(Module, Expression), NodeKind::Constructor, NodeKind::Float, NodeKind::GetLocal, + NodeKind::Index, + NodeKind::Infix, NodeKind::Integer, NodeKind::Let, + NodeKind::LocalCall, NodeKind::Map, NodeKind::RemoteCall, NodeKind::String, + NodeKind::Unary, ], received: other.clone(), }), @@ -302,6 +312,7 @@ fn visit_function_statement(mut module: Module, node: Node) -> Result Result { - let associated_type = module.types.peek().cloned()?; + let associated_type = module.current_type.peek().cloned()?; let span = node.span; let mut argument_names = Vec::new(); @@ -351,23 +362,53 @@ fn visit_function_statement(mut module: Module, node: Node) -> Result { + for node in nodes { + let (new_module, expr) = visit_expression(module, node.clone())?; + module = new_module; + module.current_function().map(|fun| fun.push_guard(expr))?; + } + } + _ => { + let (new_module, expr) = visit_expression(module, guard.clone())?; + module = new_module; + module.current_function().map(|fun| fun.push_guard(expr))?; + } + } } if let Some(body) = props.get("as") { - let (new_module, expr) = visit_expression(module, body.clone())?; - module = new_module; - module.current_function().map(|fun| fun.body.push(expr))? + match &body.value { + NodeValue::Block(nodes) => { + for node in nodes { + let (new_module, expr) = visit_expression(module, node.clone())?; + module = new_module; + module.current_function().map(|fun| fun.push_body(expr))?; + } + } + _ => { + let (new_module, expr) = visit_expression(module, body.clone())?; + module = new_module; + module.current_function().map(|fun| fun.push_body(expr))? + } + } } + module.current_function.pop()?; + for key in props.keys() { if key == "as" { continue; @@ -417,7 +458,10 @@ fn visit_impl(mut module: Module, node: Node) -> Result<(Module, Arc), Err .and_then(|name| module.reference_type(name, Some(node.span)))?; let augments = props .get("for") - .ok_or(Error::MissingProperty("for".to_string())) + .ok_or(Error::MissingProperty { + span: node.span, + property: "for".to_string(), + }) .and_then(|node| { let name = typename_to_string(&node)?; let ty = module.reference_type(name, Some(node.span))?; @@ -428,17 +472,22 @@ fn visit_impl(mut module: Module, node: Node) -> Result<(Module, Arc), Err let ty = Arc::new(Type::Impl { protocol, - augments, + augments: augments.clone(), documentation, span: node.span, }); - module.types.push(ty.clone())?; + module.types.push(ty.clone()); + module.current_type.push(ty.clone())?; + module.self_type.push(augments)?; if let Some(node) = props.get("as") { let new_module = consume_statement(module, node.clone())?; module = new_module; } + module.self_type.pop()?; + module.current_type.pop()?; + for key in props.keys() { if key == "as" { continue; @@ -459,6 +508,33 @@ fn visit_impl(mut module: Module, node: Node) -> Result<(Module, Arc), Err } } +fn visit_index(module: Module, node: Node) -> Result<(Module, Expression), Error> { + assert_is_kind(&node, NodeKind::Index)?; + + match node.value { + NodeValue::Index { expr, field_name } => { + let (mut module, expr) = visit_expression(module, *expr)?; + + let r#type = module.unbound_type()?; + + let value = ExpressionValue::Index { + expr: Box::new(expr), + field_name, + }; + + Ok(( + module, + Expression { + r#type, + span: node.span, + value, + }, + )) + } + _ => unreachable!(), + } +} + fn visit_infix(module: Module, node: Node) -> Result<(Module, Expression), Error> { assert_is_kind(&node, NodeKind::Infix)?; @@ -550,6 +626,44 @@ fn visit_let_expression(mut module: Module, node: Node) -> Result<(Module, Expre } } +fn visit_local_call(module: Module, node: Node) -> Result<(Module, Expression), Error> { + assert_is_kind(&node, NodeKind::LocalCall)?; + + match node.value { + NodeValue::LocalCall { + receiver, + arguments, + } => { + let (mut module, receiver) = visit_expression(module, *receiver)?; + + let receiver = Box::new(receiver); + let mut args = Vec::new(); + + for arg in arguments { + let (new_module, arg) = visit_expression(module, arg)?; + module = new_module; + args.push(arg); + } + + let value = ExpressionValue::LocalCall { + receiver, + arguments: args, + }; + let r#type = module.unbound_type()?; + + Ok(( + module, + Expression { + r#type, + span: node.span, + value, + }, + )) + } + _ => unreachable!(), + } +} + fn visit_map(mut module: Module, node: Node) -> Result<(Module, Expression), Error> { assert_is_kind(&node, NodeKind::Map)?; @@ -606,10 +720,18 @@ fn visit_protocol(mut module: Module, node: Node) -> Result<(Module, Arc), span: node.span, }); + module.types.push(ty.clone()); + module.current_type.push(ty.clone())?; + let self_type = module.unbound_type()?; + module.self_type.push(self_type)?; + if let Some(node) = props.get("as") { module = consume_statement(module, node.clone())?; } + module.self_type.pop()?; + module.current_type.pop()?; + for key in props.keys() { if key == "as" { continue; @@ -722,12 +844,17 @@ fn visit_struct(mut module: Module, node: Node) -> Result<(Module, Arc), E span: node.span, }); - module.types.push(r#type.clone())?; + module.types.push(r#type.clone()); + module.current_type.push(r#type.clone())?; + module.self_type.push(r#type.clone())?; if let Some(node) = props.get("as") { module = consume_statement(module, node.clone())?; } + module.self_type.pop()?; + module.current_type.pop()?; + for key in props.keys() { if key == "as" { continue; @@ -754,6 +881,7 @@ fn visit_type_statement(module: Module, node: Node) -> Result<(Module, Arc NodeKind::Protocol => visit_protocol(module, node), NodeKind::Impl => visit_impl(module, node), other => Err(Error::ExpectedReceived { + span: node.span, expected: vec![NodeKind::Struct, NodeKind::Protocol, NodeKind::Impl], received: other.clone(), }), @@ -832,18 +960,14 @@ fn assert_is_kind(node: &Node, kind: NodeKind) -> Result<(), Error> { } Err(Error::ExpectedReceived { + span: node.span, expected: vec![kind], received: node.kind.clone(), }) } -fn self_type(mut module: Module) -> Result<(Module, Arc), Error> { - let parent = module.types.peek().cloned()?; +fn self_type(module: Module) -> Result<(Module, Arc), Error> { + let parent = module.self_type.peek().cloned()?; - if parent.is_protocol() { - let ty = module.unbound_type()?; - Ok((module, ty)) - } else { - Ok((module, parent)) - } + Ok((module, parent)) } diff --git a/outrun-compiler/src/lib.rs b/outrun-compiler/src/lib.rs index b6692f8..e4a40a6 100644 --- a/outrun-compiler/src/lib.rs +++ b/outrun-compiler/src/lib.rs @@ -1,4 +1,6 @@ +extern crate outrun_common; extern crate pest; +extern crate thiserror; #[macro_use] extern crate pest_derive; @@ -10,66 +12,24 @@ extern crate lazy_static; #[macro_use] extern crate assert_matches; -extern crate outrun_common; - mod error; mod grammar; mod ir; +mod source; mod span; -mod types; + +use std::sync::Arc; pub use error::Error; +pub use ir::Module; +pub use source::Source; -pub fn compile_file(input: &str) -> Result<(), Error> { - let untyped_ast = grammar::parse_file(input)?; - // println!("AST: {:#?}", untyped_ast); +pub fn compile(source: Arc) -> Result { + let ast = grammar::parse(source.clone(), None).map_err(|e| (source.clone(), e))?; - let module = ir::Module::default(); - let module = ir::visit_nodes(module, untyped_ast)?; - let module = ir::improve(module)?; + let module = Module::new(source.clone()); + let module = ir::visit_nodes(module, ast).map_err(|e| (source.clone(), e))?; + let module = ir::improve(module).map_err(|e| (source.clone(), e))?; - println!("IR {:#?}", module); - - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_it_works() { - let input = r#" - use AbsoluteValue - use Option - use Outrun.NIF, as: NIF - - ```doc - # Outrun.Core.Integer - - The native implementation of `Integer` based around Rust's `i64` type. - - Has no constructor as it is created internally in the virtual machine by literals. - ``` - struct Outrun.Core.Integer, as: do - ```doc - Computes the absolute value of `self`. - - Returns none on overflow. - ``` - def abs(self: Self): Option, as: NIF.integer_abs(self) - - end - - impl AbsoluteValue, for: Outrun.Core.Integer, as: do - def abs(self: Self): Self, as: NIF.integer_abs(self) - end - "#; - - let result = compile_file(input); - - println!("{:#?}", result); - - assert!(false); - } + Ok(module) } diff --git a/outrun-compiler/src/source.rs b/outrun-compiler/src/source.rs new file mode 100644 index 0000000..5c05ecc --- /dev/null +++ b/outrun-compiler/src/source.rs @@ -0,0 +1,96 @@ +use crate::error::Error; +use miette::{MietteError, MietteSpanContents, SourceCode, SourceSpan, SpanContents}; +use std::path::{Path, PathBuf}; +use std::sync::Arc; + +#[derive(Debug, Clone)] +pub struct Source { + pub contents: String, + pub location: Option, +} + +impl Source { + pub fn from_file(path: &Path) -> Result { + let mut source = Source { + contents: String::new(), + location: Some(path.to_owned()), + }; + + source.contents = + std::fs::read_to_string(path).map_err(|e| (Arc::new(source.clone()), e))?; + + Ok(source) + } + + pub fn from_input(input: T) -> Result { + Ok(Source { + contents: input.to_string(), + location: None, + }) + } + + pub fn as_str(&self) -> &str { + &self.contents + } + + pub fn as_path(&self) -> Option<&Path> { + match self.location { + Some(ref location) => Some(location), + None => None, + } + } +} + +impl SourceCode for Source { + fn read_span<'a>( + &'a self, + span: &SourceSpan, + context_lines_before: usize, + context_lines_after: usize, + ) -> Result + 'a>, MietteError> { + let contents = self + .contents + .read_span(span, context_lines_before, context_lines_after)?; + + match self.location { + Some(ref location) => Ok(Box::new(MietteSpanContents::new_named( + location.to_string_lossy().to_string(), + contents.data(), + *contents.span(), + contents.line(), + contents.column(), + contents.line_count(), + ))), + None => Ok(Box::new(MietteSpanContents::new( + contents.data(), + *contents.span(), + contents.line(), + contents.column(), + contents.line_count(), + ))), + } + } +} + +impl From for Source { + fn from(contents: String) -> Source { + Source::from_input(contents).unwrap() + } +} + +impl From<&str> for Source { + fn from(contents: &str) -> Source { + Source::from_input(contents).unwrap() + } +} + +impl From for Source { + fn from(path: PathBuf) -> Source { + Source::from_file(&path).unwrap() + } +} +impl From<&Path> for Source { + fn from(path: &Path) -> Source { + Source::from_file(path).unwrap() + } +} diff --git a/outrun-compiler/src/span.rs b/outrun-compiler/src/span.rs index 478eab9..bd4e318 100644 --- a/outrun-compiler/src/span.rs +++ b/outrun-compiler/src/span.rs @@ -1,7 +1,10 @@ +use miette::SourceSpan; +use pest::error::InputLocation; + #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)] pub struct Span { - start: usize, - end: usize, + pub start: usize, + pub end: usize, } impl Span { @@ -34,3 +37,18 @@ impl From> for Span { Self::new(span.start(), span.end()) } } + +impl From for Span { + fn from(location: pest::error::InputLocation) -> Self { + match location { + InputLocation::Span(span) => span.into(), + InputLocation::Pos(pos) => pos.into(), + } + } +} + +impl Into for Span { + fn into(self) -> SourceSpan { + SourceSpan::new(self.start.into(), (self.end - self.start).into()) + } +} diff --git a/outrun-compiler/src/types.rs b/outrun-compiler/src/types.rs deleted file mode 100644 index 34f4c94..0000000 --- a/outrun-compiler/src/types.rs +++ /dev/null @@ -1,28 +0,0 @@ -use std::cell::RefCell; -use std::collections::HashMap; -use std::sync::Arc; - -#[derive(Debug, Clone, PartialEq)] -pub enum Type { - Struct { - name: String, - fields: HashMap>, - }, - Protocol { - name: String, - requires: Vec>, - }, - Function { - name: String, - associated_type: Arc, - arguments: HashMap>, - return_type: Arc, - }, - Variable(Arc>), -} - -#[derive(Debug, Clone, PartialEq)] -pub enum TypeVar { - Unbound { id: usize }, - Link { r#type: Arc }, -} diff --git a/outrun-core/Cargo.toml b/outrun-core/Cargo.toml index 5a85607..c9ce855 100644 --- a/outrun-core/Cargo.toml +++ b/outrun-core/Cargo.toml @@ -9,4 +9,5 @@ version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +miette = {version = "5.3", features = ["fancy"]} outrun-compiler = {path = "../outrun-compiler"} diff --git a/outrun-core/lib/abs.run b/outrun-core/lib/abs.outrun similarity index 100% rename from outrun-core/lib/abs.run rename to outrun-core/lib/abs.outrun diff --git a/outrun-core/lib/addition.run b/outrun-core/lib/addition.outrun similarity index 100% rename from outrun-core/lib/addition.run rename to outrun-core/lib/addition.outrun diff --git a/outrun-core/lib/any.run b/outrun-core/lib/any.outrun similarity index 68% rename from outrun-core/lib/any.run rename to outrun-core/lib/any.outrun index dda5374..7614bff 100644 --- a/outrun-core/lib/any.run +++ b/outrun-core/lib/any.outrun @@ -3,4 +3,6 @@ Defines a special protocol which applies to all Outrun types. Usually used in place of a generic type argument. ``` -protocol Any +protocol Any, as: do + def type_name(self: Self): String +end diff --git a/outrun-core/lib/bitwise_and.run b/outrun-core/lib/bitwise_and.outrun similarity index 100% rename from outrun-core/lib/bitwise_and.run rename to outrun-core/lib/bitwise_and.outrun diff --git a/outrun-core/lib/bitwise_or.run b/outrun-core/lib/bitwise_or.outrun similarity index 100% rename from outrun-core/lib/bitwise_or.run rename to outrun-core/lib/bitwise_or.outrun diff --git a/outrun-core/lib/bitwise_xor.run b/outrun-core/lib/bitwise_xor.outrun similarity index 100% rename from outrun-core/lib/bitwise_xor.run rename to outrun-core/lib/bitwise_xor.outrun diff --git a/outrun-core/lib/boolean.run b/outrun-core/lib/boolean.outrun similarity index 76% rename from outrun-core/lib/boolean.run rename to outrun-core/lib/boolean.outrun index 33cd629..1a4a952 100644 --- a/outrun-core/lib/boolean.run +++ b/outrun-core/lib/boolean.outrun @@ -1,4 +1,5 @@ use Outrun.Core.Boolean +use LogicalNot ```doc # Boolean @@ -16,5 +17,5 @@ protocol Boolean, as: do ```doc Is the value false? ``` - def false?(self: Self): Outrun.Core.Boolean, as: !true?(self) + def false?(self: Self): Outrun.Core.Boolean, as: LogicalNot.not(Boolean.true?(self)) end diff --git a/outrun-core/lib/division.run b/outrun-core/lib/division.outrun similarity index 100% rename from outrun-core/lib/division.run rename to outrun-core/lib/division.outrun diff --git a/outrun-core/lib/equality.run b/outrun-core/lib/equality.outrun similarity index 100% rename from outrun-core/lib/equality.run rename to outrun-core/lib/equality.outrun diff --git a/outrun-core/lib/error.run b/outrun-core/lib/error.outrun similarity index 90% rename from outrun-core/lib/error.run rename to outrun-core/lib/error.outrun index bd1a7b4..874c763 100644 --- a/outrun-core/lib/error.run +++ b/outrun-core/lib/error.outrun @@ -1,9 +1,10 @@ +use Any use Outrun.NIF, as: NIF +use String ```doc # Error ``` - protocol Error, as: do def message(self: Self): String diff --git a/outrun-core/lib/exponentiation.run b/outrun-core/lib/exponentiation.outrun similarity index 100% rename from outrun-core/lib/exponentiation.run rename to outrun-core/lib/exponentiation.outrun diff --git a/outrun-core/lib/float.run b/outrun-core/lib/float.outrun similarity index 100% rename from outrun-core/lib/float.run rename to outrun-core/lib/float.outrun diff --git a/outrun-core/lib/greater.run b/outrun-core/lib/greater.outrun similarity index 100% rename from outrun-core/lib/greater.run rename to outrun-core/lib/greater.outrun diff --git a/outrun-core/lib/greater_or_equal.run b/outrun-core/lib/greater_or_equal.outrun similarity index 100% rename from outrun-core/lib/greater_or_equal.run rename to outrun-core/lib/greater_or_equal.outrun diff --git a/outrun-core/lib/integer.run b/outrun-core/lib/integer.outrun similarity index 100% rename from outrun-core/lib/integer.run rename to outrun-core/lib/integer.outrun diff --git a/outrun-core/lib/less.run b/outrun-core/lib/less.outrun similarity index 100% rename from outrun-core/lib/less.run rename to outrun-core/lib/less.outrun diff --git a/outrun-core/lib/less_or_equal.run b/outrun-core/lib/less_or_equal.outrun similarity index 100% rename from outrun-core/lib/less_or_equal.run rename to outrun-core/lib/less_or_equal.outrun diff --git a/outrun-core/lib/logical_and.run b/outrun-core/lib/logical_and.outrun similarity index 100% rename from outrun-core/lib/logical_and.run rename to outrun-core/lib/logical_and.outrun diff --git a/outrun-core/lib/logical_not.outrun b/outrun-core/lib/logical_not.outrun new file mode 100644 index 0000000..17bf6b4 --- /dev/null +++ b/outrun-core/lib/logical_not.outrun @@ -0,0 +1,13 @@ +```doc +# LogicalNot + +Values which wish to implement the `!` unary operator must implement this protocol. +``` +protocol LogicalNot, as: do + ```doc + Return the logical inverse of the value. + + Must be implemented. + ``` + def not(self: Self): Boolean +end diff --git a/outrun-core/lib/logical_or.run b/outrun-core/lib/logical_or.outrun similarity index 87% rename from outrun-core/lib/logical_or.run rename to outrun-core/lib/logical_or.outrun index 8900f36..45a879d 100644 --- a/outrun-core/lib/logical_or.run +++ b/outrun-core/lib/logical_or.outrun @@ -19,6 +19,6 @@ protocol LogicalOr, as: do If `LogicalOr.true?` is true for the left-hand value then it is returned, otherwise the right-hand value is returned. ``` - def or(self: Self, _other: Any): Any, when: true?(self), as: self + def or(self: Self, _other: Any): Any, when: LogicalOr.true?(self), as: self def or(_self: Self, other: Any): Any, as: other end diff --git a/outrun-core/lib/modulus.run b/outrun-core/lib/modulus.outrun similarity index 100% rename from outrun-core/lib/modulus.run rename to outrun-core/lib/modulus.outrun diff --git a/outrun-core/lib/multiplication.run b/outrun-core/lib/multiplication.outrun similarity index 100% rename from outrun-core/lib/multiplication.run rename to outrun-core/lib/multiplication.outrun diff --git a/outrun-core/lib/negation.run b/outrun-core/lib/negation.outrun similarity index 100% rename from outrun-core/lib/negation.run rename to outrun-core/lib/negation.outrun diff --git a/outrun-core/lib/option.run b/outrun-core/lib/option.outrun similarity index 90% rename from outrun-core/lib/option.run rename to outrun-core/lib/option.outrun index fea64d2..4a7241c 100644 --- a/outrun-core/lib/option.run +++ b/outrun-core/lib/option.outrun @@ -3,6 +3,7 @@ use Boolean use Option use Outrun.Core.Some, as: Some use Outrun.Core.None, as: None +use LogicalNot ```doc # Option @@ -30,7 +31,7 @@ protocol Option, as: do ```doc Returns true if the `Option` cannot provide a value, false otherwise. ``` - def none?(option: Self): Boolean, as: !some?(option) + def none?(option: Self): Boolean, as: LogicalNot.not(Option.some?(option)) ```doc Create a new "some" value using the default some type. diff --git a/outrun-core/lib/outrun/core/boolean.run b/outrun-core/lib/outrun/core/boolean.outrun similarity index 85% rename from outrun-core/lib/outrun/core/boolean.run rename to outrun-core/lib/outrun/core/boolean.outrun index 4893917..8209833 100644 --- a/outrun-core/lib/outrun/core/boolean.run +++ b/outrun-core/lib/outrun/core/boolean.outrun @@ -43,3 +43,11 @@ impl LogicalAnd, for: Outrun.Core.Boolean, as: do defp boolean_to_option(result: Self): Option, when: result, as: Option.some(result) defp boolean_to_option(_result: Self): Option, as: Option.none() end + +impl LogicalNot, for: Outrun.Core.Boolean, as: do + ```doc + Concrete implementation of `!`. + ``` + def not(self: Self): Self, when: self, as: false + def not(self: Self): Self, as: true +end diff --git a/outrun-core/lib/outrun/core/error.run b/outrun-core/lib/outrun/core/error.outrun similarity index 82% rename from outrun-core/lib/outrun/core/error.run rename to outrun-core/lib/outrun/core/error.outrun index 2ab6252..54df098 100644 --- a/outrun-core/lib/outrun/core/error.run +++ b/outrun-core/lib/outrun/core/error.outrun @@ -7,7 +7,7 @@ use Result The default implementation of an "Error" result type. ``` struct Outrun.Core.Error(error: Error), as: do - defs new(error: Error), as: Self { error } + defs new(error: Error), as: Self { error: error } end impl Result, for: Outrun.Core.Error, as: do @@ -19,7 +19,7 @@ impl Result, for: Outrun.Core.Error, as: do end struct Outrun.Core.Error.UnwrapError(value: Outrun.Core.Error), as: do - defs new(value: Outrun.Core.Error), as: Self { value } + defs new(value: Outrun.Core.Error), as: Self { value: value } def message(self: Self): String, as: "Attempted to unwrap on an error result" end diff --git a/outrun-core/lib/outrun/core/float.run b/outrun-core/lib/outrun/core/float.outrun similarity index 100% rename from outrun-core/lib/outrun/core/float.run rename to outrun-core/lib/outrun/core/float.outrun diff --git a/outrun-core/lib/outrun/core/integer.run b/outrun-core/lib/outrun/core/integer.outrun similarity index 100% rename from outrun-core/lib/outrun/core/integer.run rename to outrun-core/lib/outrun/core/integer.outrun diff --git a/outrun-core/lib/outrun/core/none.run b/outrun-core/lib/outrun/core/none.outrun similarity index 93% rename from outrun-core/lib/outrun/core/none.run rename to outrun-core/lib/outrun/core/none.outrun index 9676bbf..b59c281 100644 --- a/outrun-core/lib/outrun/core/none.run +++ b/outrun-core/lib/outrun/core/none.outrun @@ -15,7 +15,7 @@ impl Option, for: Outrun.Core.None, as: do def none?(_self: Self): Boolean, as: true def unwrap(_self: Self): Any, as: do - Error.raise(Self.UnwrapError) + Error.raise(Self.UnwrapError{}) end defs new(): Self, as: Self {} diff --git a/outrun-core/lib/outrun/core/okay.run b/outrun-core/lib/outrun/core/okay.outrun similarity index 83% rename from outrun-core/lib/outrun/core/okay.run rename to outrun-core/lib/outrun/core/okay.outrun index bf7c277..e43e16a 100644 --- a/outrun-core/lib/outrun/core/okay.run +++ b/outrun-core/lib/outrun/core/okay.outrun @@ -8,7 +8,7 @@ use String The default implementation of an "Okay" result type. ``` struct Outrun.Core.Okay(value: Any), as: do - defs new(value: Any), as: Self { value } + defs new(value: Any), as: Self { value: value } end impl Result, for: Outrun.Core.Okay, as: do @@ -20,7 +20,7 @@ impl Result, for: Outrun.Core.Okay, as: do end struct Outrun.Core.Okay.UnwrapError(value: Outrun.Core.Okay), as: do - defs new(value: Outrun.Core.Okay), as: Self { value } + defs new(value: Outrun.Core.Okay), as: Self { value: value } def message(self: Self): String, as: "Attempted to unwrap_error on a successful result" end diff --git a/outrun-core/lib/outrun/core/some.run b/outrun-core/lib/outrun/core/some.outrun similarity index 86% rename from outrun-core/lib/outrun/core/some.run rename to outrun-core/lib/outrun/core/some.outrun index c7554f7..068020c 100644 --- a/outrun-core/lib/outrun/core/some.run +++ b/outrun-core/lib/outrun/core/some.outrun @@ -17,5 +17,5 @@ impl Option, for: Outrun.Core.Some, as: do def unwrap(self: Self): Any, as: self.value - defs new(value: Any): Self, as: Self { value } + defs new(value: Any): Self, as: Self { value: value } end diff --git a/outrun-core/lib/result.run b/outrun-core/lib/result.outrun similarity index 93% rename from outrun-core/lib/result.run rename to outrun-core/lib/result.outrun index 096e40b..26db12c 100644 --- a/outrun-core/lib/result.run +++ b/outrun-core/lib/result.outrun @@ -4,6 +4,7 @@ use Error use Result use Outrun.Core.Okay use Outrun.Core.Error +use LogicalNot ```doc # Result @@ -24,7 +25,7 @@ protocol Result, as: do ```doc Returns true if the result is an error. ``` - def error?(self: Self): Boolean, as: !okay?(self) + def error?(self: Self): Boolean, as: LogicalNot.not(okay?(self)) ```doc Returns the value of the result on success. diff --git a/outrun-core/lib/shift_left.run b/outrun-core/lib/shift_left.outrun similarity index 100% rename from outrun-core/lib/shift_left.run rename to outrun-core/lib/shift_left.outrun diff --git a/outrun-core/lib/shift_right.run b/outrun-core/lib/shift_right.outrun similarity index 100% rename from outrun-core/lib/shift_right.run rename to outrun-core/lib/shift_right.outrun diff --git a/outrun-core/lib/subtraction.run b/outrun-core/lib/subtraction.outrun similarity index 100% rename from outrun-core/lib/subtraction.run rename to outrun-core/lib/subtraction.outrun diff --git a/outrun-core/src/lib.rs b/outrun-core/src/lib.rs index ee8365d..5ac249f 100644 --- a/outrun-core/src/lib.rs +++ b/outrun-core/src/lib.rs @@ -1,49 +1,59 @@ +#[macro_use] +extern crate miette; + extern crate outrun_compiler; -// use outrun_compiler::compiler::Error; +use outrun_compiler::{Error, Source}; use std::fs; use std::path::{Path, PathBuf}; +use std::sync::Arc; -// pub fn init() -> Result<(), Error> { -// let path = Path::new(env!("CARGO_MANIFEST_DIR")); -// let path = path.join("lib"); -// let path = path.as_path(); +pub fn init() -> Result<(), Error> { + let path = Path::new(env!("CARGO_MANIFEST_DIR")); + let path = path.join("lib"); + let path = path.as_path(); -// let files = collect_files(path, Vec::new())?; + let files = collect_files(path, Vec::new())?; -// // for path in files.iter() { -// // outrun_compiler::compiler::compile_file(path)?; -// // } + for path in files.iter() { + let source = Source::from_file(path)?; + let source = Arc::new(source); + outrun_compiler::compile(source)?; + } -// Ok(()) -// } + Ok(()) +} -// fn collect_files(path: &Path, mut files: Vec) -> Result, Error> { -// let entries = fs::read_dir(path).map_err(|e| Error::from((path, e)))?; -// for entry in entries { -// let entry = entry.map_err(|e| Error::from((path, e)))?; -// let path = entry.path(); -// let path = path.as_path(); -// if path.is_dir() { -// files = collect_files(&path, files)?; -// } else if let Some(extension) = path.extension() { -// if extension == "run" { -// files.push(path.to_owned()); -// } -// } -// } +fn collect_files(path: &Path, mut files: Vec) -> Result, Error> { + let entries = fs::read_dir(path)?; + for entry in entries { + let entry = entry?; + let path = entry.path(); + let path = path.as_path(); + if path.is_dir() { + files = collect_files(&path, files)?; + } else if let Some(extension) = path.extension() { + if extension == "outrun" { + files.push(path.to_owned()); + } + } + } -// Ok(files) -// } + Ok(files) +} -// // #[cfg(test)] -// // mod test { -// // use super::*; +#[cfg(test)] +mod test { + use super::*; -// // #[test] -// // fn test_init() { -// // let result = init().unwrap(); -// // println!("result: {:?}", result); -// // assert!(false); -// // } -// // } + use miette::Result; + + #[test] + fn test_init() -> Result<()> { + let result = init()?; + + // println!("result: {:?}", result); + assert!(false); + Ok(()) + } +}