wip: more #134

Draft
james wants to merge 1 commit from more into main
11 changed files with 649 additions and 123 deletions

View file

@ -30,5 +30,8 @@
"thiserror",
"typeblock",
"typeunion"
],
"rust-analyzer.linkedProjects": [
"./outrun-parser/Cargo.toml"
]
}

153
Cargo.lock generated
View file

@ -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"

View file

@ -1,5 +1,6 @@
[workspace]
members = [
"outrun-parser", # "outrun-common", # "outrun-compiler",
"outrun-parser",
"outrun-compiler",
]

View file

@ -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"

View file

@ -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<Variant>,
received: Variant,
span: Location,
},
UnknownStatement {
expected: Vec<String>,
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<T: Debug>(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
}

138
outrun-compiler/src/ir.rs Normal file
View file

@ -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<ArcIntern<String>, Struct>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Struct {
name: ArcIntern<String>,
type_args: Vec<Type>,
fields: Vec<Field>,
location: Location,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Field {
name: ArcIntern<String>,
ty: Type,
location: Location,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Type {
Struct(Struct),
TypeArg(ArcIntern<String>),
}
pub fn build(module: Module) -> Result<Context, Error> {
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<String>, 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<Context, Error> {
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<Context, Error> {
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<Variant>) -> 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);
}
}

View file

@ -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(())
}

View file

@ -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<Expr>),
Atom(String),
Block(Vec<Expr>),
@ -205,7 +232,8 @@ fn visit_array(pair: Pair<'_, Rule>) -> Result<Expr, Error> {
}
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<Expr, Error> {
})
.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<Expr, Error> {
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<Expr, Error> {
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<Expr, Error> {
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<Expr, Error> {
.collect::<Result<Vec<Expr>, 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<Expr, Error> {
.collect::<Result<Vec<Expr>, 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<Expr, Error> {
.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<Expr, Error> {
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<Expr, Error> {
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<Expr, Error> {
.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<Expr, Error> {
.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<Expr, Error> {
.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<Expr, Error> {
.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<Expr, Error> {
.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<Expr, Error> {
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<Expr, Error> {
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<Expr, Error> {
}
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<Expr, Error> {
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<Expr, Error> {
};
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<Expr, Error> {
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<Expr, Error> {
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<Expr, Error> {
.collect::<Result<Vec<Expr>, 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<Expr, Error> {
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<T0>", 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!();
}

View file

@ -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<pest::error::Error<Rule>> for Error {
}
}
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Error::Io(err)
}
}

View file

@ -12,9 +12,64 @@ mod ast;
mod error;
mod grammar;
mod location;
mod module;
pub fn parse(input: &str) -> Result<Vec<ast::Expr>, 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<Module, error::Error> {
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<Module, error::Error> {
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())
}
}

View file

@ -0,0 +1,26 @@
use crate::ast::Expr;
use std::path::{Path, PathBuf};
#[derive(Debug, Clone, PartialEq)]
pub struct Module {
pub path: Option<PathBuf>,
pub id: u64,
pub nodes: Vec<Expr>,
}
impl Module {
pub fn new(nodes: Vec<Expr>, path: Option<&Path>, id: u64) -> Self {
match path {
None => Module {
path: None,
id,
nodes,
},
Some(path) => Module {
path: Some(path.into()),
id,
nodes,
},
}
}
}