diff --git a/.vscode/settings.json b/.vscode/settings.json index e34e2e1..d7336f4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -30,5 +30,8 @@ "thiserror", "typeblock", "typeunion" + ], + "rust-analyzer.linkedProjects": [ + "./outrun-parser/Cargo.toml" ] } diff --git a/Cargo.lock b/Cargo.lock index 995fb99..3c25766 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" +dependencies = [ + "const-random", +] + [[package]] name = "ahash" version = "0.7.6" @@ -37,6 +46,24 @@ dependencies = [ "memchr", ] +[[package]] +name = "arc-interner" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "655ae79d4a7661f2f9a8f6daf95af32897eafdea938df06aeb127cea02b5f4ac" +dependencies = [ + "ahash 0.3.8", + "dashmap", + "once_cell", + "serde", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "backtrace" version = "0.3.67" @@ -88,6 +115,28 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "const-random" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368a7a772ead6ce7e1de82bfb04c485f3db8ec744f72925af5735e29a22cc18e" +dependencies = [ + "const-random-macro", + "proc-macro-hack", +] + +[[package]] +name = "const-random-macro" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb" +dependencies = [ + "getrandom", + "once_cell", + "proc-macro-hack", + "tiny-keccak", +] + [[package]] name = "cpufeatures" version = "0.2.5" @@ -97,6 +146,12 @@ dependencies = [ "libc", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -107,6 +162,16 @@ dependencies = [ "typenum", ] +[[package]] +name = "dashmap" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" +dependencies = [ + "cfg-if", + "num_cpus", +] + [[package]] name = "digest" version = "0.10.6" @@ -117,6 +182,12 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + [[package]] name = "errno" version = "0.2.8" @@ -171,7 +242,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash", + "ahash 0.7.6", ] [[package]] @@ -180,6 +251,16 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "io-lifetimes" version = "1.0.7" @@ -209,6 +290,15 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb" +[[package]] +name = "itertools" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" +dependencies = [ + "either", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -274,6 +364,25 @@ dependencies = [ "adler", ] +[[package]] +name = "nom" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c349f68f25f596b9f44cf0e7c69752a5c633b0550c3ff849518bfba0233774a" +dependencies = [ + "memchr", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "object" version = "0.30.3" @@ -289,6 +398,16 @@ version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +[[package]] +name = "outrun-compiler" +version = "0.1.0" +dependencies = [ + "arc-interner", + "outrun-parser", + "polytype", + "thiserror", +] + [[package]] name = "outrun-parser" version = "0.1.0" @@ -350,6 +469,23 @@ dependencies = [ "sha2", ] +[[package]] +name = "polytype" +version = "6.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d78b55dfffb4b7c498c5e255baa5946a453d07337231ed00e10aede50692c5" +dependencies = [ + "indexmap", + "itertools", + "nom", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + [[package]] name = "proc-macro2" version = "1.0.63" @@ -405,6 +541,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "serde" +version = "1.0.173" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91f70896d6720bc714a4a57d22fc91f1db634680e65c8efe13323f1fa38d53f" + [[package]] name = "sha2" version = "0.10.6" @@ -513,6 +655,15 @@ dependencies = [ "syn 2.0.22", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "typenum" version = "1.16.0" diff --git a/Cargo.toml b/Cargo.toml index 8b007ba..dfe2e7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ - "outrun-parser", # "outrun-common", # "outrun-compiler", + "outrun-parser", + "outrun-compiler", ] diff --git a/outrun-compiler/Cargo.toml b/outrun-compiler/Cargo.toml new file mode 100644 index 0000000..1d298bd --- /dev/null +++ b/outrun-compiler/Cargo.toml @@ -0,0 +1,12 @@ +[package] +edition = "2021" +name = "outrun-compiler" +version = "0.1.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +arc-interner = "0.7" +outrun-parser = {path = "../outrun-parser"} +polytype = "6.2" +thiserror = "1.0" diff --git a/outrun-compiler/src/error.rs b/outrun-compiler/src/error.rs new file mode 100644 index 0000000..a2c834f --- /dev/null +++ b/outrun-compiler/src/error.rs @@ -0,0 +1,72 @@ +use core::fmt::Debug; +use outrun_parser::Error as ParserError; +use outrun_parser::{Location, Variant}; +use std::fmt; +use std::fmt::Write as _; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum Error { + Parse(#[source] ParserError), + UnexpectedExpression { + expected: Vec, + received: Variant, + span: Location, + }, + UnknownStatement { + expected: Vec, + received: String, + span: Location, + }, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::Parse(e) => write!(f, "{}", e), + Error::UnexpectedExpression { + expected, received, .. + } => { + if expected.is_empty() { + write!(f, "Unexpected received: `{received:?}` expression") + } else { + write!( + f, + "Expected one of {} expressions, but received: `{received:?}`", + to_list(expected) + ) + } + } + Error::UnknownStatement { + expected, received, .. + } => { + if expected.is_empty() { + write!(f, "Unexpected received: `{received}` statement") + } else { + write!( + f, + "Expected one of {} statements, but received: `{received}`", + to_list(expected) + ) + } + } + } + } +} + +fn to_list(exprs: &[T]) -> String { + let mut result = String::new(); + let len = exprs.len(); + + for (i, r) in exprs.iter().enumerate() { + let _ = write!(result, "`{r:?}`"); + + match len - i { + 1 => break, + 2 => result.push_str(" or "), + _ => result.push_str(", "), + } + } + + result +} diff --git a/outrun-compiler/src/ir.rs b/outrun-compiler/src/ir.rs new file mode 100644 index 0000000..1ff51ce --- /dev/null +++ b/outrun-compiler/src/ir.rs @@ -0,0 +1,138 @@ +use crate::error::Error; +use arc_interner::ArcIntern; +use outrun_parser::{Data, Expr, Location, Module, Variant}; +use std::collections::HashMap; + +#[derive(Debug, Clone, PartialEq)] +pub struct Context { + module: Module, + structs: HashMap, Struct>, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Struct { + name: ArcIntern, + type_args: Vec, + fields: Vec, + location: Location, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Field { + name: ArcIntern, + ty: Type, + location: Location, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Type { + Struct(Struct), + TypeArg(ArcIntern), +} + +pub fn build(module: Module) -> Result { + let context = Context { + module: module.clone(), + structs: HashMap::new(), + }; + + module + .nodes + .iter() + .try_fold(context, |context, node| match &node.variant { + Variant::Statement => visit_statement(node, context), + other => Err(Error::UnexpectedExpression { + expected: vec![Variant::Statement], + received: other.clone(), + span: node.location, + }), + }) +} + +fn visit_atom(node: &Expr, context: Context) -> Result<(ArcIntern, Context), Error> { + assert_expression_is(node, Variant::Atom)?; + + match &node.data { + Data::Atom(value) => Ok((ArcIntern::new(value.to_string()), context)), + _ => unreachable!(), + } +} + +fn visit_statement(node: &Expr, context: Context) -> Result { + assert_expression_is(node, Variant::Statement)?; + + let Data::Statement(def_type, ..) = &node.data else { panic!() }; + + let (atom, context) = visit_atom(def_type, context)?; + + match atom.as_str() { + "struct" => visit_statement_struct(node, context), + def_type => Err(Error::UnknownStatement { + expected: vec!["struct".to_string()], + received: def_type.to_string(), + span: node.location, + }), + } +} + +fn visit_statement_struct(node: &Expr, context: Context) -> Result { + assert_expression_is(node, Variant::Statement)?; + + let Data::Statement(_, r#type, def_args, stmt_args) = &node.data else { panic!() }; + + println!("{:?}", r#type); + // println!("{:?}", def_args); + // println!("{:?}", stmt_args); + + assert_expression_in(node, vec![Variant::TypeIdentifier, Variant::TypeGeneric])?; + + match r#type.data { + Data::TypeIdentifier(name) => { + let name = ArcIntern::new(name.to_string()); + let fields = def_args + .iter() + .try_fold(context, |context, (key, value)| {}) + .collect(); + } + _ => unreachable!(), + } + + Ok(context) +} + +fn assert_expression_is(node: &Expr, expected: Variant) -> Result<(), Error> { + if node.variant == expected { + Ok(()) + } else { + Err(Error::UnexpectedExpression { + expected: vec![expected], + received: node.variant.clone(), + span: node.location, + }) + } +} + +fn assert_expression_in(node: &Expr, expected: Vec) -> Result<(), Error> { + if expected.contains(&node.variant) { + Ok(()) + } else { + Err(Error::UnexpectedExpression { + expected, + received: node.variant.clone(), + span: node.location, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use outrun_parser::parse_input; + + #[test] + fn test_visit_statement_struct_simple() { + let module = parse_input("struct Foo ( bar: Integer )").unwrap(); + let context = build(module).unwrap(); + assert_eq!(context.structs.len(), 1); + } +} diff --git a/outrun-compiler/src/lib.rs b/outrun-compiler/src/lib.rs new file mode 100644 index 0000000..fafdb7f --- /dev/null +++ b/outrun-compiler/src/lib.rs @@ -0,0 +1,10 @@ +extern crate arc_interner; +extern crate outrun_parser; +extern crate polytype; + +mod error; +mod ir; + +pub fn compile(module: outrun_parser::Module) -> Result<(), error::Error> { + Ok(()) +} diff --git a/outrun-parser/src/ast.rs b/outrun-parser/src/ast.rs index 36d32fd..078320c 100644 --- a/outrun-parser/src/ast.rs +++ b/outrun-parser/src/ast.rs @@ -8,12 +8,39 @@ use pest::{ #[derive(Debug, Clone, PartialEq)] pub struct Expr { - inner: Variant, - location: Location, + pub variant: Variant, + pub data: Data, + pub location: Location, } #[derive(Debug, Clone, PartialEq)] pub enum Variant { + Array, + Atom, + Block, + Boolean, + CallLocal, + CallRemote, + Constructor, + Float, + Infix, + IntegerBinary, + IntegerOctal, + IntegerDecimal, + IntegerHexadecimal, + IntegerZero, + Local, + Map, + Prefix, + Statement, + String, + TypeGeneric, + TypeIdentifier, + TypeVariable, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Data { Array(Vec), Atom(String), Block(Vec), @@ -205,7 +232,8 @@ fn visit_array(pair: Pair<'_, Rule>) -> Result { } Ok(Expr { - inner: Variant::Array(nodes), + variant: Variant::Array, + data: Data::Array(nodes), location, }) } @@ -225,11 +253,13 @@ fn visit_atom(pair: Pair<'_, Rule>) -> Result { }) .and_then(visit_atom), Rule::ident => Ok(Expr { - inner: Variant::Atom(pair.as_str().to_owned()), + variant: Variant::Atom, + data: Data::Atom(pair.as_str().to_owned()), location: pair.as_span().into(), }), Rule::identp => Ok(Expr { - inner: Variant::Atom(pair.as_str().to_owned()), + variant: Variant::Atom, + data: Data::Atom(pair.as_str().to_owned()), location: pair.as_span().into(), }), _ => unreachable!(), @@ -245,7 +275,8 @@ fn visit_block(pair: Pair<'_, Rule>) -> Result { nodes.push(visit_pair(pair)?); } Ok(Expr { - inner: Variant::Block(nodes), + variant: Variant::Block, + data: Data::Block(nodes), location: pair.as_span().into(), }) } @@ -265,11 +296,13 @@ fn visit_bool(pair: Pair<'_, Rule>) -> Result { match inner.as_rule() { Rule::bool_true => Ok(Expr { - inner: Variant::Boolean(true), + variant: Variant::Boolean, + data: Data::Boolean(true), location: inner.as_span().into(), }), Rule::bool_false => Ok(Expr { - inner: Variant::Boolean(false), + variant: Variant::Boolean, + data: Data::Boolean(false), location: inner.as_span().into(), }), rule => Err(Error::UnexpectedRule { @@ -303,7 +336,8 @@ fn visit_call_local(pair: Pair<'_, Rule>) -> Result { span: pair.as_span().into(), }) .map(|pair| Expr { - inner: Variant::Local(pair.as_str().to_string()), + variant: Variant::Local, + data: Data::Local(pair.as_str().to_string()), location: pair.as_span().into(), }) })?; @@ -313,7 +347,8 @@ fn visit_call_local(pair: Pair<'_, Rule>) -> Result { .collect::, Error>>()?; Ok(Expr { - inner: Variant::CallLocal(Box::new(local), args), + variant: Variant::CallLocal, + data: Data::CallLocal(Box::new(local), args), location, }) } @@ -347,7 +382,8 @@ fn visit_call_remote(pair: Pair<'_, Rule>) -> Result { .collect::, Error>>()?; Ok(Expr { - inner: Variant::CallRemote(Box::new(r#type), fname, args), + variant: Variant::CallRemote, + data: Data::CallRemote(Box::new(r#type), fname, args), location, }) } @@ -376,7 +412,8 @@ fn visit_constructor(pair: Pair<'_, Rule>) -> Result { .and_then(visit_keyword_list)?; Ok(Expr { - inner: Variant::Constructor(Box::new(r#type), properties), + variant: Variant::Constructor, + data: Data::Constructor(Box::new(r#type), properties), location, }) } @@ -390,7 +427,8 @@ fn visit_expr(pair: Pair<'_, Rule>) -> Result { let location = lhs.location.extend(rhs.location); Expr { - inner: Variant::Infix(Box::new(lhs), op.as_rule().into(), Box::new(rhs)), + variant: Variant::Infix, + data: Data::Infix(Box::new(lhs), op.as_rule().into(), Box::new(rhs)), location, } }) @@ -398,7 +436,8 @@ fn visit_expr(pair: Pair<'_, Rule>) -> Result { let location = rhs.location.extend(prefix.as_span().into()); Expr { - inner: Variant::Prefix(prefix.as_rule().into(), Box::new(rhs)), + variant: Variant::Prefix, + data: Data::Prefix(prefix.as_rule().into(), Box::new(rhs)), location, } }) @@ -416,7 +455,8 @@ fn visit_flt(pair: Pair<'_, Rule>) -> Result { .map_err(|e| Error::parse_float_error(e, pair.as_span().into()))?; Ok(Expr { - inner: Variant::Float(value), + variant: Variant::Float, + data: Data::Float(value), location: pair.as_span().into(), }) } @@ -440,7 +480,8 @@ fn visit_int(pair: Pair<'_, Rule>) -> Result { .map_err(|e| Error::parse_int_error(e, inner.as_span().into()))?; Ok(Expr { - inner: Variant::IntegerBinary(value), + variant: Variant::IntegerBinary, + data: Data::IntegerBinary(value), location: pair.as_span().into(), }) } @@ -449,7 +490,8 @@ fn visit_int(pair: Pair<'_, Rule>) -> Result { .map_err(|e| Error::parse_int_error(e, inner.as_span().into()))?; Ok(Expr { - inner: Variant::IntegerOctal(value), + variant: Variant::IntegerOctal, + data: Data::IntegerOctal(value), location: pair.as_span().into(), }) } @@ -460,7 +502,8 @@ fn visit_int(pair: Pair<'_, Rule>) -> Result { .map_err(|e| Error::parse_int_error(e, inner.as_span().into()))?; Ok(Expr { - inner: Variant::IntegerDecimal(value), + variant: Variant::IntegerDecimal, + data: Data::IntegerDecimal(value), location: pair.as_span().into(), }) } @@ -469,12 +512,14 @@ fn visit_int(pair: Pair<'_, Rule>) -> Result { .map_err(|e| Error::parse_int_error(e, inner.as_span().into()))?; Ok(Expr { - inner: Variant::IntegerHexadecimal(value), + variant: Variant::IntegerHexadecimal, + data: Data::IntegerHexadecimal(value), location: pair.as_span().into(), }) } Rule::int_zero => Ok(Expr { - inner: Variant::IntegerZero, + variant: Variant::IntegerZero, + data: Data::IntegerZero, location: pair.as_span().into(), }), rule => Err(Error::UnexpectedRule { @@ -496,7 +541,8 @@ fn visit_key(pair: Pair<'_, Rule>) -> Result { match pair.as_rule() { Rule::kw_key => Ok(Expr { - inner: Variant::Atom( + variant: Variant::Atom, + data: Data::Atom( pair.clone() .into_inner() .next() @@ -560,7 +606,8 @@ fn visit_local(pair: Pair<'_, Rule>) -> Result { assert_is_rule(&pair, Rule::local)?; Ok(Expr { - inner: Variant::Local(pair.as_str().to_owned()), + variant: Variant::Local, + data: Data::Local(pair.as_str().to_owned()), location: pair.as_span().into(), }) } @@ -610,7 +657,8 @@ fn visit_map(pair: Pair<'_, Rule>) -> Result { } Ok(Expr { - inner: Variant::Map(nodes), + variant: Variant::Map, + data: Data::Map(nodes), location, }) } @@ -628,10 +676,7 @@ fn visit_stmt(pair: Pair<'_, Rule>) -> Result { received: pair.as_rule(), span: pair.as_span().into(), }) - .map(|pair| Expr { - inner: Variant::Atom(pair.as_str().to_string()), - location: pair.as_span().into(), - })?; + .and_then(visit_atom)?; let def_type = inner .next() @@ -697,7 +742,8 @@ fn visit_stmt(pair: Pair<'_, Rule>) -> Result { }; Ok(Expr { - inner: Variant::Statement(Box::new(stmt_type), Box::new(def_type), def_args, stmt_args), + variant: Variant::Statement, + data: Data::Statement(Box::new(stmt_type), Box::new(def_type), def_args, stmt_args), location, }) } @@ -717,7 +763,8 @@ fn visit_str(pair: Pair<'_, Rule>) -> Result { let content = build_str(inner)?; Ok(Expr { - inner: Variant::String(content), + variant: Variant::String, + data: Data::String(content), location: pair.as_span().into(), }) } @@ -818,7 +865,8 @@ fn visit_type_basic(pair: Pair<'_, Rule>) -> Result { span: pair.as_span().into(), }) .map(|pair| Expr { - inner: Variant::TypeIdentifier(pair.as_str().to_owned()), + variant: Variant::TypeIdentifier, + data: Data::TypeIdentifier(pair.as_str().to_owned()), location, }) } @@ -851,7 +899,8 @@ fn visit_type_generic(pair: Pair<'_, Rule>) -> Result { .collect::, Error>>()?; Ok(Expr { - inner: Variant::TypeGeneric(Box::new(name), args), + variant: Variant::TypeGeneric, + data: Data::TypeGeneric(Box::new(name), args), location, }) } @@ -862,7 +911,8 @@ fn visit_type_variable(pair: Pair<'_, Rule>) -> Result { let location = pair.as_span().into(); Ok(Expr { - inner: Variant::TypeVariable(pair.as_str().to_owned()), + variant: Variant::TypeVariable, + data: Data::TypeVariable(pair.as_str().to_owned()), location, }) } @@ -901,7 +951,7 @@ mod test { let pairs = parse(":marty", Rule::input).unwrap(); let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - assert_eq!(nodes[0].inner, Variant::Atom("marty".to_owned())); + assert_eq!(nodes[0].data, Data::Atom("marty".to_owned())); } #[test] @@ -909,7 +959,7 @@ mod test { let pairs = parse("true", Rule::input).unwrap(); let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - assert_eq!(nodes[0].inner, Variant::Boolean(true)); + assert_eq!(nodes[0].data, Data::Boolean(true)); } #[test] @@ -917,7 +967,7 @@ mod test { let pairs = parse("false", Rule::input).unwrap(); let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - assert_eq!(nodes[0].inner, Variant::Boolean(false)); + assert_eq!(nodes[0].data, Data::Boolean(false)); } #[test] @@ -925,7 +975,7 @@ mod test { let pairs = parse("1.21", Rule::input).unwrap(); let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - assert_eq!(nodes[0].inner, Variant::Float(1.21)); + assert_eq!(nodes[0].data, Data::Float(1.21)); } #[test] @@ -934,10 +984,10 @@ mod test { let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::Infix(lhs, op, rhs) = nodes[0].inner.clone() { - assert_eq!(lhs.inner, Variant::IntegerDecimal(1)); + if let Data::Infix(lhs, op, rhs) = nodes[0].data.clone() { + assert_eq!(lhs.data, Data::IntegerDecimal(1)); assert_eq!(op, InfixOp::Add); - assert_eq!(rhs.inner, Variant::IntegerDecimal(2)); + assert_eq!(rhs.data, Data::IntegerDecimal(2)); } else { panic!(); } @@ -949,9 +999,9 @@ mod test { let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::Prefix(prefix_op, expr) = nodes[0].inner.clone() { + if let Data::Prefix(prefix_op, expr) = nodes[0].data.clone() { assert_eq!(prefix_op, PrefixOp::Plus); - assert_eq!(expr.inner, Variant::IntegerDecimal(1)); + assert_eq!(expr.data, Data::IntegerDecimal(1)); } else { panic!(); } @@ -963,7 +1013,7 @@ mod test { let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::String(value) = nodes[0].inner.clone() { + if let Data::String(value) = nodes[0].data.clone() { assert_eq!(value, ""); } else { panic!(); @@ -976,7 +1026,7 @@ mod test { let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::String(value) = nodes[0].inner.clone() { + if let Data::String(value) = nodes[0].data.clone() { assert_eq!(value, "abcdefghijklmnopqrstuvwxyz0123456789"); } else { panic!(); @@ -989,7 +1039,7 @@ mod test { let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::String(value) = nodes[0].inner.clone() { + if let Data::String(value) = nodes[0].data.clone() { assert_eq!(value, " \x07 \x08 \x0c \n \r \t \x0b \\ "); } else { panic!(); @@ -1002,7 +1052,7 @@ mod test { let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::String(value) = nodes[0].inner.clone() { + if let Data::String(value) = nodes[0].data.clone() { assert_eq!(value, " \u{f4a9} "); } else { panic!(); @@ -1015,7 +1065,7 @@ mod test { let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::Array(values) = nodes[0].inner.clone() { + if let Data::Array(values) = nodes[0].data.clone() { assert_eq!(values.len(), 0); } else { panic!(); @@ -1028,10 +1078,10 @@ mod test { let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::Array(values) = nodes[0].inner.clone() { - assert_eq!(values[0].inner, Variant::IntegerDecimal(1)); - assert_eq!(values[1].inner, Variant::IntegerDecimal(2)); - assert_eq!(values[2].inner, Variant::IntegerDecimal(3)); + if let Data::Array(values) = nodes[0].data.clone() { + assert_eq!(values[0].data, Data::IntegerDecimal(1)); + assert_eq!(values[1].data, Data::IntegerDecimal(2)); + assert_eq!(values[2].data, Data::IntegerDecimal(3)); } else { panic!(); } @@ -1043,7 +1093,7 @@ mod test { let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::Map(values) = nodes[0].inner.clone() { + if let Data::Map(values) = nodes[0].data.clone() { assert_eq!(values.len(), 0); } else { panic!(); @@ -1056,14 +1106,14 @@ mod test { let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::Map(values) = nodes[0].inner.clone() { + if let Data::Map(values) = nodes[0].data.clone() { assert_eq!(values.len(), 3); - assert_eq!(values[0].0.inner, Variant::Atom("a".to_owned())); - assert_eq!(values[0].1.inner, Variant::IntegerDecimal(1)); - assert_eq!(values[1].0.inner, Variant::Atom("b".to_owned())); - assert_eq!(values[1].1.inner, Variant::IntegerDecimal(2)); - assert_eq!(values[2].0.inner, Variant::Atom("c".to_owned())); - assert_eq!(values[2].1.inner, Variant::IntegerDecimal(3)); + assert_eq!(values[0].0.data, Data::Atom("a".to_owned())); + assert_eq!(values[0].1.data, Data::IntegerDecimal(1)); + assert_eq!(values[1].0.data, Data::Atom("b".to_owned())); + assert_eq!(values[1].1.data, Data::IntegerDecimal(2)); + assert_eq!(values[2].0.data, Data::Atom("c".to_owned())); + assert_eq!(values[2].1.data, Data::IntegerDecimal(3)); } else { panic!(); } @@ -1075,14 +1125,14 @@ mod test { let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::Map(values) = nodes[0].inner.clone() { + if let Data::Map(values) = nodes[0].data.clone() { assert_eq!(values.len(), 3); - assert_eq!(values[0].0.inner, Variant::Atom("a".to_owned())); - assert_eq!(values[0].1.inner, Variant::IntegerDecimal(1)); - assert_eq!(values[1].0.inner, Variant::Atom("b".to_owned())); - assert_eq!(values[1].1.inner, Variant::IntegerDecimal(2)); - assert_eq!(values[2].0.inner, Variant::Atom("c".to_owned())); - assert_eq!(values[2].1.inner, Variant::IntegerDecimal(3)); + assert_eq!(values[0].0.data, Data::Atom("a".to_owned())); + assert_eq!(values[0].1.data, Data::IntegerDecimal(1)); + assert_eq!(values[1].0.data, Data::Atom("b".to_owned())); + assert_eq!(values[1].1.data, Data::IntegerDecimal(2)); + assert_eq!(values[2].0.data, Data::Atom("c".to_owned())); + assert_eq!(values[2].1.data, Data::IntegerDecimal(3)); } else { panic!(); } @@ -1094,7 +1144,7 @@ mod test { let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::TypeIdentifier(type_name) = nodes[0].inner.clone() { + if let Data::TypeIdentifier(type_name) = nodes[0].data.clone() { assert_eq!(type_name, "MartyMcFly"); } else { panic!(); @@ -1106,15 +1156,15 @@ mod test { let pairs = parse("MartyMcFly", Rule::input).unwrap(); let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::TypeGeneric(type_name, args) = nodes[0].inner.clone() { - if let Variant::TypeIdentifier(type_name) = type_name.inner { + if let Data::TypeGeneric(type_name, args) = nodes[0].data.clone() { + if let Data::TypeIdentifier(type_name) = type_name.data { assert_eq!(type_name, "MartyMcFly"); } else { panic!(); } assert_eq!(args.len(), 1); - assert_eq!(args[0].inner, Variant::TypeVariable("T0".to_owned())); + assert_eq!(args[0].data, Data::TypeVariable("T0".to_owned())); } else { panic!(); } @@ -1125,7 +1175,7 @@ mod test { let pairs = parse("(1)", Rule::input).unwrap(); let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - assert_eq!(nodes[0].inner, Variant::IntegerDecimal(1)); + assert_eq!(nodes[0].data, Data::IntegerDecimal(1)); } #[test] @@ -1133,7 +1183,7 @@ mod test { let pairs = parse("a", Rule::input).unwrap(); let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - assert_eq!(nodes[0].inner, Variant::Local("a".to_owned())); + assert_eq!(nodes[0].data, Data::Local("a".to_owned())); } #[test] @@ -1142,8 +1192,8 @@ mod test { let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::Constructor(r#type, properties) = nodes[0].inner.clone() { - assert_eq!(r#type.inner, Variant::TypeIdentifier("Marty".to_string())); + if let Data::Constructor(r#type, properties) = nodes[0].data.clone() { + assert_eq!(r#type.data, Data::TypeIdentifier("Marty".to_string())); assert!(properties.is_empty()); } else { panic!(); @@ -1156,13 +1206,13 @@ mod test { let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::Constructor(r#type, properties) = nodes[0].inner.clone() { - assert_eq!(r#type.inner, Variant::TypeIdentifier("Marty".to_string())); + if let Data::Constructor(r#type, properties) = nodes[0].data.clone() { + assert_eq!(r#type.data, Data::TypeIdentifier("Marty".to_string())); assert_eq!(properties.len(), 2); - assert_eq!(properties[0].0.inner, Variant::Atom("a".to_owned())); - assert_eq!(properties[0].1.inner, Variant::IntegerDecimal(1)); - assert_eq!(properties[1].0.inner, Variant::Atom("b".to_owned())); - assert_eq!(properties[1].1.inner, Variant::IntegerDecimal(2)); + assert_eq!(properties[0].0.data, Data::Atom("a".to_owned())); + assert_eq!(properties[0].1.data, Data::IntegerDecimal(1)); + assert_eq!(properties[1].0.data, Data::Atom("b".to_owned())); + assert_eq!(properties[1].1.data, Data::IntegerDecimal(2)); } else { panic!(); } @@ -1174,8 +1224,8 @@ mod test { let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::CallLocal(local, args) = nodes[0].inner.clone() { - assert_eq!(local.inner, Variant::Local("a".to_owned())); + if let Data::CallLocal(local, args) = nodes[0].data.clone() { + assert_eq!(local.data, Data::Local("a".to_owned())); assert!(args.is_empty()); } else { panic!(); @@ -1188,11 +1238,11 @@ mod test { let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::CallLocal(local, args) = nodes[0].inner.clone() { - assert_eq!(local.inner, Variant::Local("a".to_owned())); + if let Data::CallLocal(local, args) = nodes[0].data.clone() { + assert_eq!(local.data, Data::Local("a".to_owned())); assert_eq!(args.len(), 2); - assert_eq!(args[0].inner, Variant::IntegerDecimal(1)); - assert_eq!(args[1].inner, Variant::IntegerDecimal(2)); + assert_eq!(args[0].data, Data::IntegerDecimal(1)); + assert_eq!(args[1].data, Data::IntegerDecimal(2)); } else { panic!(); } @@ -1204,8 +1254,8 @@ mod test { let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::CallRemote(r#type, fname, args) = nodes[0].inner.clone() { - assert_eq!(r#type.inner, Variant::TypeIdentifier("A".to_string())); + if let Data::CallRemote(r#type, fname, args) = nodes[0].data.clone() { + assert_eq!(r#type.data, Data::TypeIdentifier("A".to_string())); assert_eq!(fname, "a"); assert!(args.is_empty()); } else { @@ -1219,12 +1269,12 @@ mod test { let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::CallRemote(r#type, fname, args) = nodes[0].inner.clone() { - assert_eq!(r#type.inner, Variant::TypeIdentifier("A".to_string())); + if let Data::CallRemote(r#type, fname, args) = nodes[0].data.clone() { + assert_eq!(r#type.data, Data::TypeIdentifier("A".to_string())); assert_eq!(fname, "a"); assert_eq!(args.len(), 2); - assert_eq!(args[0].inner, Variant::IntegerDecimal(1)); - assert_eq!(args[1].inner, Variant::IntegerDecimal(2)); + assert_eq!(args[0].data, Data::IntegerDecimal(1)); + assert_eq!(args[1].data, Data::IntegerDecimal(2)); } else { panic!(); } @@ -1236,9 +1286,9 @@ mod test { let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::Statement(deftype, defname, defargs, stmtargs) = nodes[0].inner.clone() { - assert_eq!(deftype.inner, Variant::Atom("hello".to_string())); - assert_eq!(defname.inner, Variant::Atom("world".to_string())); + if let Data::Statement(deftype, defname, defargs, stmtargs) = nodes[0].data.clone() { + assert_eq!(deftype.data, Data::Atom("hello".to_string())); + assert_eq!(defname.data, Data::Atom("world".to_string())); assert!(defargs.is_empty()); assert!(stmtargs.is_empty()); } else { @@ -1252,12 +1302,12 @@ mod test { let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::Statement(deftype, defname, defargs, stmtargs) = nodes[0].inner.clone() { - assert_eq!(deftype.inner, Variant::Atom("hello".to_string())); - assert_eq!(defname.inner, Variant::Atom("world".to_string())); + if let Data::Statement(deftype, defname, defargs, stmtargs) = nodes[0].data.clone() { + assert_eq!(deftype.data, Data::Atom("hello".to_string())); + assert_eq!(defname.data, Data::Atom("world".to_string())); assert_eq!(defargs.len(), 1); - assert_eq!(defargs[0].0.inner, Variant::Atom("a".to_string())); - assert_eq!(defargs[0].1.inner, Variant::IntegerDecimal(1)); + assert_eq!(defargs[0].0.data, Data::Atom("a".to_string())); + assert_eq!(defargs[0].1.data, Data::IntegerDecimal(1)); assert!(stmtargs.is_empty()); } else { panic!(); @@ -1270,19 +1320,19 @@ mod test { let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::Statement(deftype, defname, defargs, stmtargs) = nodes[0].inner.clone() { - assert_eq!(deftype.inner, Variant::Atom("hello".to_string())); - assert_eq!(defname.inner, Variant::Atom("world".to_string())); + if let Data::Statement(deftype, defname, defargs, stmtargs) = nodes[0].data.clone() { + assert_eq!(deftype.data, Data::Atom("hello".to_string())); + assert_eq!(defname.data, Data::Atom("world".to_string())); assert_eq!(defargs.len(), 2); - assert_eq!(defargs[0].0.inner, Variant::Atom("a".to_string())); - assert_eq!(defargs[0].1.inner, Variant::IntegerDecimal(1)); - assert_eq!(defargs[1].0.inner, Variant::Atom("b".to_string())); - assert_eq!(defargs[1].1.inner, Variant::IntegerDecimal(2)); + assert_eq!(defargs[0].0.data, Data::Atom("a".to_string())); + assert_eq!(defargs[0].1.data, Data::IntegerDecimal(1)); + assert_eq!(defargs[1].0.data, Data::Atom("b".to_string())); + assert_eq!(defargs[1].1.data, Data::IntegerDecimal(2)); assert_eq!(stmtargs.len(), 1); - assert_eq!(stmtargs[0].0.inner, Variant::Atom("as".to_string())); - assert_eq!(stmtargs[0].1.inner, Variant::IntegerDecimal(3)); + assert_eq!(stmtargs[0].0.data, Data::Atom("as".to_string())); + assert_eq!(stmtargs[0].1.data, Data::IntegerDecimal(3)); } else { panic!(); } @@ -1294,19 +1344,19 @@ mod test { let nodes = build(pairs).unwrap(); assert_eq!(nodes.len(), 1); - if let Variant::Statement(deftype, defname, defargs, stmtargs) = nodes[0].inner.clone() { - assert_eq!(deftype.inner, Variant::Atom("hello".to_string())); - assert_eq!(defname.inner, Variant::TypeIdentifier("World".to_string())); + if let Data::Statement(deftype, defname, defargs, stmtargs) = nodes[0].data.clone() { + assert_eq!(deftype.data, Data::Atom("hello".to_string())); + assert_eq!(defname.data, Data::TypeIdentifier("World".to_string())); assert_eq!(defargs.len(), 2); - assert_eq!(defargs[0].0.inner, Variant::Atom("a".to_string())); - assert_eq!(defargs[0].1.inner, Variant::IntegerDecimal(1)); - assert_eq!(defargs[1].0.inner, Variant::Atom("b".to_string())); - assert_eq!(defargs[1].1.inner, Variant::IntegerDecimal(2)); + assert_eq!(defargs[0].0.data, Data::Atom("a".to_string())); + assert_eq!(defargs[0].1.data, Data::IntegerDecimal(1)); + assert_eq!(defargs[1].0.data, Data::Atom("b".to_string())); + assert_eq!(defargs[1].1.data, Data::IntegerDecimal(2)); assert_eq!(stmtargs.len(), 1); - assert_eq!(stmtargs[0].0.inner, Variant::Atom("as".to_string())); - assert_eq!(stmtargs[0].1.inner, Variant::IntegerDecimal(3)); + assert_eq!(stmtargs[0].0.data, Data::Atom("as".to_string())); + assert_eq!(stmtargs[0].1.data, Data::IntegerDecimal(3)); } else { panic!(); } diff --git a/outrun-parser/src/error.rs b/outrun-parser/src/error.rs index e7dc679..6051f3b 100644 --- a/outrun-parser/src/error.rs +++ b/outrun-parser/src/error.rs @@ -26,6 +26,7 @@ pub enum Error { received: String, span: Location, }, + Io(#[source] std::io::Error), } impl Error { @@ -84,7 +85,7 @@ impl fmt::Display for Error { } else { write!( f, - "Expected one of {} tokens, but received `{received:?}` token", + "Expected one of {} tokens, but received `{received:?}`", to_list(expected) ) } @@ -134,6 +135,7 @@ impl fmt::Display for Error { to_list(negatives) ), }, + Error::Io(error) => write!(f, "IO error: {error}"), } } } @@ -153,3 +155,9 @@ impl From> for Error { } } } + +impl From for Error { + fn from(err: std::io::Error) -> Self { + Error::Io(err) + } +} diff --git a/outrun-parser/src/lib.rs b/outrun-parser/src/lib.rs index 06ca3e2..4532020 100644 --- a/outrun-parser/src/lib.rs +++ b/outrun-parser/src/lib.rs @@ -12,9 +12,64 @@ mod ast; mod error; mod grammar; mod location; +mod module; -pub fn parse(input: &str) -> Result, error::Error> { +pub use ast::{Data, Expr, InfixOp, PrefixOp, Variant}; +pub use error::Error; +pub use location::Location; +pub use module::Module; + +use std::collections::hash_map::DefaultHasher; +use std::hash::{Hash, Hasher}; +use std::path::Path; + +pub fn parse_input(input: &str) -> Result { let pairs = grammar::parse(input, grammar::Rule::input)?; - let ast = ast::build(pairs)?; - Ok(ast) + let nodes = ast::build(pairs)?; + + let mut hasher = DefaultHasher::new(); + input.hash(&mut hasher); + + Ok(Module::new(nodes, None, hasher.finish())) +} + +pub fn parse_file(path: &Path) -> Result { + let input = std::fs::read_to_string(path)?; + let pairs = grammar::parse(&input, grammar::Rule::input)?; + let nodes = ast::build(pairs)?; + + let mut hasher = DefaultHasher::new(); + path.hash(&mut hasher); + input.hash(&mut hasher); + + Ok(Module::new(nodes, Some(path), hasher.finish())) +} + +#[cfg(test)] +mod test { + use super::*; + use std::env; + use std::fs::File; + use std::io::Write; + + #[test] + fn test_parse_input() { + let module = parse_input("def wat()").unwrap(); + assert!(module.path.is_none()); + assert_ne!(module.id, 0); + assert!(!module.nodes.is_empty()) + } + + #[test] + fn test_parse_file() { + let path = env::temp_dir().join("test.outrun"); + + let mut file = File::create(path.clone()).unwrap(); + file.write_all("def wat()".as_bytes()).unwrap(); + + let module = parse_file(&path).unwrap(); + assert_eq!(module.path.unwrap(), path); + assert_ne!(module.id, 0); + assert!(!module.nodes.is_empty()) + } } diff --git a/outrun-parser/src/module.rs b/outrun-parser/src/module.rs new file mode 100644 index 0000000..729214f --- /dev/null +++ b/outrun-parser/src/module.rs @@ -0,0 +1,26 @@ +use crate::ast::Expr; +use std::path::{Path, PathBuf}; + +#[derive(Debug, Clone, PartialEq)] +pub struct Module { + pub path: Option, + pub id: u64, + pub nodes: Vec, +} + +impl Module { + pub fn new(nodes: Vec, path: Option<&Path>, id: u64) -> Self { + match path { + None => Module { + path: None, + id, + nodes, + }, + Some(path) => Module { + path: Some(path.into()), + id, + nodes, + }, + } + } +}