wip: use miette
for compiler errors.
This commit is contained in:
parent
336b2574b8
commit
272be0d249
61 changed files with 974 additions and 289 deletions
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
|
@ -19,9 +19,13 @@
|
|||
"indoc",
|
||||
"Jiggawatts",
|
||||
"lexer",
|
||||
"miette",
|
||||
"prec",
|
||||
"Scandroid",
|
||||
"struct",
|
||||
"typeblock"
|
||||
"textwrap",
|
||||
"thiserror",
|
||||
"typeblock",
|
||||
"typeunion"
|
||||
]
|
||||
}
|
||||
|
|
260
Cargo.lock
generated
260
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
extern crate thiserror;
|
||||
|
||||
pub mod indexed_vec;
|
||||
pub mod stack;
|
||||
pub mod unique_vec;
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>,
|
||||
#[source]
|
||||
error: GrammarError,
|
||||
#[label]
|
||||
span: Span,
|
||||
},
|
||||
#[error("Error while parsing")]
|
||||
#[diagnostic()]
|
||||
IrError {
|
||||
#[source_code]
|
||||
source: Arc<Source>,
|
||||
#[source]
|
||||
error: IrError,
|
||||
#[label]
|
||||
span: Option<Span>,
|
||||
},
|
||||
#[error("I/O error")]
|
||||
#[diagnostic()]
|
||||
IoErrorWithSource {
|
||||
#[source_code]
|
||||
source: Arc<Source>,
|
||||
#[source]
|
||||
error: IoError,
|
||||
},
|
||||
#[error("I/O error")]
|
||||
#[diagnostic()]
|
||||
IoError(
|
||||
#[from]
|
||||
#[source]
|
||||
IoError,
|
||||
),
|
||||
}
|
||||
|
||||
impl From<GrammarError> for Error {
|
||||
fn from(error: GrammarError) -> Self {
|
||||
Error::ParseError(error)
|
||||
impl From<(Arc<Source>, GrammarError)> for Error {
|
||||
fn from((source, error): (Arc<Source>, GrammarError)) -> Self {
|
||||
let span = error.as_span();
|
||||
Error::ParseError {
|
||||
source,
|
||||
error,
|
||||
span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IrError> for Error {
|
||||
fn from(error: IrError) -> Self {
|
||||
Error::IrError(error)
|
||||
impl From<(Arc<Source>, IrError)> for Error {
|
||||
fn from((source, error): (Arc<Source>, IrError)) -> Self {
|
||||
let span = error.maybe_span();
|
||||
Self::IrError {
|
||||
source,
|
||||
error,
|
||||
span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(Arc<Source>, IoError)> for Error {
|
||||
fn from((source, error): (Arc<Source>, IoError)) -> Self {
|
||||
Self::IoErrorWithSource { source, error }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,6 +155,7 @@ pub enum NodeKind {
|
|||
Float,
|
||||
GetLocal,
|
||||
Impl,
|
||||
Index,
|
||||
Infix,
|
||||
Integer,
|
||||
Let,
|
||||
|
@ -192,6 +193,10 @@ pub enum NodeValue {
|
|||
name: Box<Node>,
|
||||
props: HashMap<String, Node>,
|
||||
},
|
||||
Index {
|
||||
expr: Box<Node>,
|
||||
field_name: String,
|
||||
},
|
||||
Infix {
|
||||
op: Operator,
|
||||
lhs: Box<Node>,
|
||||
|
|
|
@ -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<Rule>),
|
||||
ParseIntegerError(ParseIntError),
|
||||
ParseFloatError(ParseFloatError),
|
||||
ParseIntError(Span, #[source] ParseIntError),
|
||||
ParseFloatError(Span, #[source] ParseFloatError),
|
||||
Unexpected {
|
||||
received: Rule,
|
||||
expected: Option<Rule>,
|
||||
span: Span,
|
||||
},
|
||||
UnexpectedExpected {
|
||||
received: Rule,
|
||||
expected: Rule,
|
||||
span: Span,
|
||||
},
|
||||
PestError {
|
||||
positives: Vec<Rule>,
|
||||
negatives: Vec<Rule>,
|
||||
span: Span,
|
||||
},
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn unexpected<T: Into<Span>>(received: Rule, expected: Option<Rule>, 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<pest::error::Error<Rule>> for Error {
|
||||
fn from(error: pest::error::Error<Rule>) -> Self {
|
||||
Error::ParseError(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseIntError> for Error {
|
||||
fn from(error: ParseIntError) -> Self {
|
||||
Error::ParseIntegerError(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseFloatError> for Error {
|
||||
fn from(error: ParseFloatError) -> Self {
|
||||
Error::ParseFloatError(error)
|
||||
fn from(err: pest::error::Error<Rule>) -> 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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 )? }
|
||||
|
||||
|
|
|
@ -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<Vec<Node>, Error> {
|
||||
let pairs = Grammar::parse(Rule::file, input)?;
|
||||
let nodes = visitor::visit_pairs(pairs)?;
|
||||
|
||||
Ok(nodes)
|
||||
}
|
||||
|
||||
pub fn parse_input(input: &str) -> Result<Vec<Node>, Error> {
|
||||
let pairs = Grammar::parse(Rule::input, input)?;
|
||||
pub fn parse(source: Arc<Source>, rule: Option<Rule>) -> Result<Vec<Node>, 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<Source> {
|
||||
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);
|
||||
|
|
|
@ -37,12 +37,9 @@ pub fn visit_pair(pair: Pair<'_, Rule>) -> Result<Node, Error> {
|
|||
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<Node, Error> {
|
|||
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<Node, Error> {
|
|||
))
|
||||
}
|
||||
|
||||
fn visit_documentation(pair: Pair<'_, Rule>) -> Result<Node, Error> {
|
||||
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<Node, Error> {
|
||||
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<Node, Error> {
|
||||
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<Node, Error> {
|
||||
assert_is_rule(&pair, Rule::defstruct)?;
|
||||
let span = pair.as_span();
|
||||
|
@ -523,8 +548,10 @@ fn visit_integer(pair: Pair<'_, Rule>) -> Result<Node, Error> {
|
|||
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,
|
||||
|
|
|
@ -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<NodeKind>,
|
||||
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<StackError> for Error {
|
||||
fn from(e: StackError) -> Self {
|
||||
Error::StackError(e)
|
||||
impl Error {
|
||||
pub fn maybe_span(&self) -> Option<Span> {
|
||||
match self {
|
||||
Error::ExpectedReceived { span, .. } => Some(*span),
|
||||
Error::UnknownType(_) => None,
|
||||
Error::MissingProperty { span, .. } => Some(*span),
|
||||
Error::StackError(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Expression>),
|
||||
Float(f64),
|
||||
GetLocal(String),
|
||||
Index {
|
||||
expr: Box<Expression>,
|
||||
field_name: String,
|
||||
},
|
||||
Integer(i64),
|
||||
Let {
|
||||
name: String,
|
||||
value: Box<Expression>,
|
||||
},
|
||||
LocalCall {
|
||||
receiver: Box<Expression>,
|
||||
arguments: Vec<Expression>,
|
||||
},
|
||||
Map(Vec<(Expression, Expression)>),
|
||||
RemoteCall {
|
||||
associated_type: Arc<Type>,
|
||||
|
@ -30,10 +38,6 @@ pub enum ExpressionValue {
|
|||
arguments: Vec<Expression>,
|
||||
},
|
||||
String(String),
|
||||
Unary {
|
||||
op: Operator,
|
||||
rhs: Box<Expression>,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<NodeValue> for ExpressionValue {
|
||||
|
|
|
@ -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<String>,
|
||||
pub r#type: Arc<Type>,
|
||||
pub body: Vec<Expression>,
|
||||
pub guards: Vec<Expression>,
|
||||
pub body: RefCell<Vec<Expression>>,
|
||||
pub guards: RefCell<Vec<Expression>>,
|
||||
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<NodeKind> for Access {
|
||||
|
|
|
@ -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<Module, Error> {
|
||||
pub fn improve(module: Module) -> Result<Module, Error> {
|
||||
stages::link_type_references(module)
|
||||
}
|
||||
|
|
|
@ -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<PathBuf>,
|
||||
pub types: Stack<Arc<Type>>,
|
||||
pub functions: Stack<Function>,
|
||||
pub source: Arc<Source>,
|
||||
pub types: Vec<Arc<Type>>,
|
||||
pub functions: Vec<Arc<Function>>,
|
||||
pub docs: Stack<String>,
|
||||
pub notices: Vec<Notice>,
|
||||
pub current_type: Stack<Arc<Type>>,
|
||||
pub self_type: Stack<Arc<Type>>,
|
||||
pub current_function: Stack<Arc<Function>>,
|
||||
type_id: u64,
|
||||
}
|
||||
|
||||
impl Module {
|
||||
pub fn new(path: &Path) -> Module {
|
||||
pub fn new(source: Arc<Source>) -> 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<Function>, Error> {
|
||||
self.current_function
|
||||
.peek_mut()
|
||||
.ok_or(StackError::Underflow.into())
|
||||
}
|
||||
|
||||
pub fn info(&mut self, span: Option<Span>, 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<Span>, 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<Span>, kind: NoticeKind) {
|
||||
self.notices.push(Notice {
|
||||
severity: Severity::Error,
|
||||
level: NoticeLevel::Error,
|
||||
kind,
|
||||
span,
|
||||
})
|
||||
|
@ -128,30 +136,17 @@ impl Module {
|
|||
where
|
||||
F: Fn(Arc<Type>) -> 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
|
||||
}
|
||||
|
|
|
@ -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<Span>,
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -35,7 +35,7 @@ fn consume_expression(module: Module, node: Node) -> Result<Module, Error> {
|
|||
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<Module, Error> {
|
|||
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<Module, Error> {
|
|||
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<Type>)
|
|||
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<Arc<Type>>), 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<Module, Er
|
|||
NodeKind::DefPublic | NodeKind::DefPrivate | NodeKind::DefStatic
|
||||
) {
|
||||
return Err(Error::ExpectedReceived {
|
||||
span: node.span,
|
||||
expected: vec![
|
||||
NodeKind::DefPublic,
|
||||
NodeKind::DefPrivate,
|
||||
|
@ -318,7 +329,7 @@ fn visit_function_statement(mut module: Module, node: Node) -> Result<Module, Er
|
|||
props,
|
||||
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<Module, Er
|
|||
documentation,
|
||||
span,
|
||||
});
|
||||
module.types.push(function_type.clone())?;
|
||||
module.types.push(function_type.clone());
|
||||
|
||||
let function = Function::new(name, argument_names, function_type, node.kind);
|
||||
module.functions.push(function.clone())?;
|
||||
let function = Arc::new(Function::new(
|
||||
name,
|
||||
argument_names,
|
||||
function_type,
|
||||
node.kind,
|
||||
));
|
||||
module.functions.push(function.clone());
|
||||
module.current_function.push(function.clone())?;
|
||||
|
||||
if let Some(guard) = props.get("when") {
|
||||
let (new_module, expr) = visit_expression(module, guard.clone())?;
|
||||
module = new_module;
|
||||
module.current_function().map(|fun| fun.guards.push(expr))?;
|
||||
match &guard.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_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<Type>), 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<Type>), 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<Type>), 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<Type>),
|
|||
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<Type>), 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<Type>
|
|||
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<Type>), Error> {
|
||||
let parent = module.types.peek().cloned()?;
|
||||
fn self_type(module: Module) -> Result<(Module, Arc<Type>), 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))
|
||||
}
|
||||
|
|
|
@ -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<Source>) -> Result<Module, Error> {
|
||||
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)
|
||||
}
|
||||
|
|
96
outrun-compiler/src/source.rs
Normal file
96
outrun-compiler/src/source.rs
Normal file
|
@ -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<PathBuf>,
|
||||
}
|
||||
|
||||
impl Source {
|
||||
pub fn from_file(path: &Path) -> Result<Source, Error> {
|
||||
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<T: ToString>(input: T) -> Result<Source, Error> {
|
||||
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<Box<dyn SpanContents<'a> + '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<String> 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<PathBuf> 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()
|
||||
}
|
||||
}
|
|
@ -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<pest::Span<'_>> for Span {
|
|||
Self::new(span.start(), span.end())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<pest::error::InputLocation> for Span {
|
||||
fn from(location: pest::error::InputLocation) -> Self {
|
||||
match location {
|
||||
InputLocation::Span(span) => span.into(),
|
||||
InputLocation::Pos(pos) => pos.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<SourceSpan> for Span {
|
||||
fn into(self) -> SourceSpan {
|
||||
SourceSpan::new(self.start.into(), (self.end - self.start).into())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String, Arc<Type>>,
|
||||
},
|
||||
Protocol {
|
||||
name: String,
|
||||
requires: Vec<Arc<Type>>,
|
||||
},
|
||||
Function {
|
||||
name: String,
|
||||
associated_type: Arc<Type>,
|
||||
arguments: HashMap<String, Arc<Type>>,
|
||||
return_type: Arc<Type>,
|
||||
},
|
||||
Variable(Arc<RefCell<TypeVar>>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum TypeVar {
|
||||
Unbound { id: usize },
|
||||
Link { r#type: Arc<Type> },
|
||||
}
|
|
@ -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"}
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -1,9 +1,10 @@
|
|||
use Any
|
||||
use Outrun.NIF, as: NIF
|
||||
use String
|
||||
|
||||
```doc
|
||||
# Error
|
||||
```
|
||||
|
||||
protocol Error, as: do
|
||||
def message(self: Self): String
|
||||
|
13
outrun-core/lib/logical_not.outrun
Normal file
13
outrun-core/lib/logical_not.outrun
Normal file
|
@ -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
|
|
@ -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
|
|
@ -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.
|
|
@ -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
|
|
@ -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
|
|
@ -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 {}
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
|
@ -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<PathBuf>) -> Result<Vec<PathBuf>, 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<PathBuf>) -> Result<Vec<PathBuf>, 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(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue