diff --git a/.gitignore b/.gitignore index 575b752..0071c41 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ /target .idea/ -programs/good/cc/ output.s output a.out -compiler/src/passes/parse/grammar.rs \ No newline at end of file +compiler/src/passes/parse/grammar.rs diff --git a/Cargo.lock b/Cargo.lock index 5a26066..bbac391 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -138,11 +138,11 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" dependencies = [ - "async-lock", + "async-lock 2.8.0", "async-task", "concurrent-queue", "fastrand 1.9.0", - "futures-lite", + "futures-lite 1.13.0", "slab", ] @@ -155,9 +155,9 @@ dependencies = [ "async-channel", "async-executor", "async-io 1.13.0", - "async-lock", + "async-lock 2.8.0", "blocking", - "futures-lite", + "futures-lite 1.13.0", "once_cell", ] @@ -167,11 +167,11 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ - "async-lock", + "async-lock 2.8.0", "autocfg", "cfg-if", "concurrent-queue", - "futures-lite", + "futures-lite 1.13.0", "log", "parking", "polling 2.8.0", @@ -183,15 +183,15 @@ dependencies = [ [[package]] name = "async-io" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10da8f3146014722c89e7859e1d7bb97873125d7346d10ca642ffab794355828" +checksum = "41ed9d5715c2d329bf1b4da8d60455b99b187f27ba726df2883799af9af60997" dependencies = [ - "async-lock", + "async-lock 3.0.0", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite", + "futures-lite 2.0.1", "parking", "polling 3.3.0", "rustix 0.38.21", @@ -210,6 +210,17 @@ dependencies = [ "event-listener 2.5.3", ] +[[package]] +name = "async-lock" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e900cdcd39bb94a14487d3f7ef92ca222162e6c7c3fe7cb3550ea75fb486ed" +dependencies = [ + "event-listener 3.0.1", + "event-listener-strategy", + "pin-project-lite", +] + [[package]] name = "async-process" version = "1.8.1" @@ -217,12 +228,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" dependencies = [ "async-io 1.13.0", - "async-lock", + "async-lock 2.8.0", "async-signal", "blocking", "cfg-if", "event-listener 3.0.1", - "futures-lite", + "futures-lite 1.13.0", "rustix 0.38.21", "windows-sys", ] @@ -233,8 +244,8 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" dependencies = [ - "async-io 2.1.0", - "async-lock", + "async-io 2.2.0", + "async-lock 2.8.0", "atomic-waker", "cfg-if", "futures-core", @@ -255,13 +266,13 @@ dependencies = [ "async-channel", "async-global-executor", "async-io 1.13.0", - "async-lock", + "async-lock 2.8.0", "async-process", "crossbeam-utils", "futures-channel", "futures-core", "futures-io", - "futures-lite", + "futures-lite 1.13.0", "gloo-timers", "kv-log-macro", "log", @@ -302,7 +313,7 @@ checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -423,11 +434,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c36a4d0d48574b3dd360b4b7d95cc651d2b6557b6402848a27d4b228a473e2a" dependencies = [ "async-channel", - "async-lock", + "async-lock 2.8.0", "async-task", "fastrand 2.0.1", "futures-io", - "futures-lite", + "futures-lite 1.13.0", "piper", "tracing", ] @@ -530,7 +541,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -767,9 +778,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" +checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e" dependencies = [ "libc", "windows-sys", @@ -792,6 +803,16 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "event-listener-strategy" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96b852f1345da36d551b9473fa1e2b1eb5c5195585c6c018118bc92a8d91160" +dependencies = [ + "event-listener 3.0.1", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "1.9.0" @@ -857,7 +878,7 @@ checksum = "528dcee17708842e42ec482eb77946d0f80b0a23aec649f67f916a32f1275007" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -913,6 +934,16 @@ dependencies = [ "waker-fn", ] +[[package]] +name = "futures-lite" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3831c2651acb5177cbd83943f3d9c8912c5ad03c76afcc0e9511ba568ec5ebb" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.29" @@ -921,7 +952,7 @@ checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -1272,9 +1303,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.149" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libgit2-sys" @@ -1290,6 +1321,17 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall", +] + [[package]] name = "libssh2-sys" version = "0.3.0" @@ -1419,7 +1461,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -1539,9 +1581,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.94" +version = "0.9.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f55da20b29f956fb01f0add8683eb26ee13ebe3ebd935e49898717c6b4b2830" +checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9" dependencies = [ "cc", "libc", @@ -1579,7 +1621,7 @@ checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", + "redox_syscall", "smallvec", "windows-targets", ] @@ -1707,6 +1749,30 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.69" @@ -1804,15 +1870,6 @@ dependencies = [ "rand_core 0.3.1", ] -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -1824,12 +1881,12 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ "getrandom", - "redox_syscall 0.2.16", + "libredox", "thiserror", ] @@ -2053,9 +2110,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.190" +version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" dependencies = [ "serde_derive", ] @@ -2071,13 +2128,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.190" +version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -2275,9 +2332,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.38" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -2314,7 +2371,7 @@ checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", "fastrand 2.0.1", - "redox_syscall 0.4.1", + "redox_syscall", "rustix 0.38.21", "windows-sys", ] @@ -2342,15 +2399,15 @@ dependencies = [ [[package]] name = "test_each_file" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c06c73675e53d92b6206f762aa1b2e63cc3603f083473cf3141eee0c70eb29" +checksum = "9d2b9676fc7f7fa8f8426cd82ff8c0db19359a356c3c837969acb1baa5eab31e" dependencies = [ - "itertools 0.11.0", "pathdiff", + "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -2381,7 +2438,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -2462,7 +2519,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -2700,7 +2757,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", "wasm-bindgen-shared", ] @@ -2734,7 +2791,7 @@ checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2894,9 +2951,9 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.21" +version = "0.7.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686b7e407015242119c33dab17b8f61ba6843534de936d94368856528eae4dcc" +checksum = "8cd369a67c0edfef15010f980c3cbe45d7f651deac2cd67ce097cd801de16557" dependencies = [ "byteorder", "zerocopy-derive", @@ -2904,11 +2961,11 @@ dependencies = [ [[package]] name = "zerocopy-derive" -version = "0.7.21" +version = "0.7.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020f3dfe25dfc38dfea49ce62d5d45ecdd7f0d8a724fa63eb36b6eba4ec76806" +checksum = "c2f140bda219a26ccc0cdb03dba58af72590c53b22642577d88a927bc5c87d6b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml index deaf6a4..fe608eb 100644 --- a/compiler/Cargo.toml +++ b/compiler/Cargo.toml @@ -9,10 +9,10 @@ thiserror = "1.0.50" bitflags = "2.4.1" petgraph = "0.6.4" itertools = "0.11.0" -serde = { version = "1.0.190", features = ["derive"] } +serde = { version = "1.0.192", features = ["derive"] } clap = { version = "4.4.7", features = ["derive"] } miette = { version = "5.10.0", features = ["fancy"] } -zerocopy = { version = "0.7.21", features = ["derive"] } +zerocopy = { version = "0.7.25", features = ["derive"] } lalrpop-util = { version = "0.20.0", features = ["lexer", "unicode"] } derive_more = "0.99.17" functor_derive = "0.2.3" @@ -21,5 +21,5 @@ functor_derive = "0.2.3" lalrpop = "0.20.0" [dev-dependencies] -test_each_file = "0.1.0" +test_each_file = "0.1.1" tempfile = "3.8.1" diff --git a/compiler/src/interpreter.rs b/compiler/src/interpreter.rs index fe0d8f8..99cc621 100644 --- a/compiler/src/interpreter.rs +++ b/compiler/src/interpreter.rs @@ -1,62 +1,41 @@ -use crate::passes::parse::Lit; +use crate::passes::validate::TLit; use derive_more::Display; use std::collections::HashMap; use std::fmt::Display; use std::hash::Hash; -use std::io::stdin; use std::vec::IntoIter; pub trait IO { - fn read(&mut self) -> Lit; - fn print(&mut self, v: Lit); -} - -struct StdIO {} - -impl IO for StdIO { - fn read(&mut self) -> Lit { - print!("> "); - let mut input = String::new(); - stdin() - .read_line(&mut input) - .expect("IO error or something"); - input - .trim_end() - .parse() - .expect("Provided input was not a valid i64") - } - - fn print(&mut self, v: Lit) { - println!("{v}"); - } + fn read(&mut self) -> TLit; + fn print(&mut self, v: TLit); } pub struct TestIO { - inputs: IntoIter, - outputs: Vec, + inputs: IntoIter, + outputs: Vec, } impl TestIO { - pub fn new(inputs: Vec) -> Self { + pub fn new(inputs: Vec) -> Self { Self { inputs: inputs.into_iter(), outputs: Vec::new(), } } - pub fn outputs(&self) -> &Vec { + pub fn outputs(&self) -> &Vec { &self.outputs } } impl IO for TestIO { - fn read(&mut self) -> Lit { + fn read(&mut self) -> TLit { self.inputs .next() .expect("Test tried to read more input than were available.") } - fn print(&mut self, v: Lit) { + fn print(&mut self, v: TLit) { self.outputs.push(v); } } @@ -77,12 +56,12 @@ pub enum Val<'p, A: Copy + Hash + Eq + Display> { }, } -impl<'p, A: Copy + Hash + Eq + Display> From for Val<'p, A> { - fn from(value: Lit) -> Self { +impl<'p, A: Copy + Hash + Eq + Display> From for Val<'p, A> { + fn from(value: TLit) -> Self { match value { - Lit::Int { val } => Val::Int { val }, - Lit::Bool { val } => Val::Bool { val }, - Lit::Unit => Val::Unit, + TLit::Int { val } => Val::Int { val: val as i64 }, + TLit::Bool { val } => Val::Bool { val }, + TLit::Unit => Val::Unit, } } } diff --git a/compiler/src/lib.rs b/compiler/src/lib.rs index 21232fd..a3e72cf 100644 --- a/compiler/src/lib.rs +++ b/compiler/src/lib.rs @@ -1,18 +1,28 @@ #![allow(clippy::module_inception)] +extern crate core; + pub mod interpreter; pub mod passes; pub mod utils; use crate::passes::parse::parse::parse_program; +use miette::{NamedSource, Report}; use std::fs::File; use std::path::Path; -pub fn compile(program: &str, output: &Path) -> miette::Result<()> { +pub fn compile(program: &str, filename: &str, output: &Path) -> miette::Result<()> { let mut file = File::create(output).unwrap(); - parse_program(program)? - .validate()? + let add_source = + |error| Report::with_source_code(error, NamedSource::new(filename, program.to_string())); + + parse_program(program) + .map_err(Into::into) + .map_err(add_source)? + .validate() + .map_err(Into::into) + .map_err(add_source)? .uniquify() .reveal() .atomize() diff --git a/compiler/src/main.rs b/compiler/src/main.rs index 9dabaf0..fcc3bdd 100644 --- a/compiler/src/main.rs +++ b/compiler/src/main.rs @@ -1,12 +1,11 @@ -use crate::MainError::IOResult; use clap::Parser; use compiler::compile; use compiler::passes::parse::parse::PrettyParseError; use compiler::passes::validate::ValidateError; -use miette::Diagnostic; +use miette::{Diagnostic, IntoDiagnostic}; +use std::fs; use std::io::Read; use std::path::Path; -use std::{fs, io}; use thiserror::Error; #[derive(Debug, Error, Diagnostic)] @@ -17,9 +16,6 @@ enum MainError { #[error(transparent)] #[diagnostic(transparent)] ValidateError(#[from] ValidateError), - #[error(transparent)] - #[diagnostic()] - IOResult(#[from] io::Error), } #[derive(Parser, Debug)] @@ -34,20 +30,19 @@ struct Args { output: Option, } -fn read_from_stdin() -> Result { +fn read_from_stdin() -> Result { let mut program = String::new(); - io::stdin().read_to_string(&mut program)?; + std::io::stdin().read_to_string(&mut program)?; Ok(program) } fn main() -> miette::Result<()> { let args = Args::parse(); - let program = args - .input - .as_ref() - .map_or_else(read_from_stdin, fs::read_to_string) - .map_err(IOResult)?; + let (program, filename) = match args.input.as_ref() { + None => (read_from_stdin().into_diagnostic()?, "stdin"), + Some(file) => (fs::read_to_string(file).into_diagnostic()?, file.as_str()), + }; let output: &str = args.output.as_deref().unwrap_or_else(|| { args.input.as_ref().map_or_else( @@ -56,5 +51,5 @@ fn main() -> miette::Result<()> { ) }); - compile(&program, Path::new(&output)) + compile(&program, filename, Path::new(&output)) } diff --git a/compiler/src/passes/atomize/atomize.rs b/compiler/src/passes/atomize/atomize.rs index 41f1071..6afeb6e 100644 --- a/compiler/src/passes/atomize/atomize.rs +++ b/compiler/src/passes/atomize/atomize.rs @@ -11,29 +11,30 @@ impl<'p> PrgRevealed<'p> { defs: self .defs .into_iter() - .map(|(sym, def)| { - let def = match def { - Def::Fn { - sym, - params, - typ, - bdy, - } => Def::Fn { - sym, - params, - typ, - bdy: atomize_expr(bdy), - }, - Def::TypeDef { sym, def } => Def::TypeDef { sym, def }, - }; - (sym, def) - }) + .map(|(sym, def)| (sym, atomize_def(def))) .collect(), entry: self.entry, } } } +fn atomize_def<'p>(def: Def<'p, UniqueSym<'p>, RExpr<'p>>) -> Def<'p, UniqueSym<'p>, AExpr<'p>> { + match def { + Def::Fn { + sym, + params, + typ, + bdy, + } => Def::Fn { + sym, + params, + typ, + bdy: atomize_expr(bdy), + }, + Def::TypeDef { sym, def } => Def::TypeDef { sym, def }, + } +} + fn atomize_expr(expr: RExpr) -> AExpr { match expr { RExpr::Lit { val, typ } => AExpr::Atom { diff --git a/compiler/src/passes/atomize/mod.rs b/compiler/src/passes/atomize/mod.rs index 988e04b..62aed8a 100644 --- a/compiler/src/passes/atomize/mod.rs +++ b/compiler/src/passes/atomize/mod.rs @@ -1,9 +1,9 @@ pub mod atomize; use crate::passes::parse::types::Type; -use crate::passes::parse::{Def, Lit, Op}; +use crate::passes::parse::{Def, Op}; use crate::passes::uniquify::PrgUniquified; -use crate::passes::validate::TExpr; +use crate::passes::validate::{TExpr, TLit}; use crate::utils::gen_sym::UniqueSym; use std::collections::HashMap; @@ -105,7 +105,7 @@ impl<'p> AExpr<'p> { #[derive(Debug, PartialEq, Copy, Clone)] pub enum Atom<'p> { - Val { val: Lit }, + Val { val: TLit }, Var { sym: UniqueSym<'p> }, } diff --git a/compiler/src/passes/eliminate_algebraic/interpret.rs b/compiler/src/passes/eliminate_algebraic/interpret.rs index 8391a32..b828e97 100644 --- a/compiler/src/passes/eliminate_algebraic/interpret.rs +++ b/compiler/src/passes/eliminate_algebraic/interpret.rs @@ -2,7 +2,8 @@ use crate::interpreter::Val; use crate::interpreter::IO; use crate::passes::atomize::Atom; use crate::passes::eliminate_algebraic::{EExpr, ETail, PrgEliminated}; -use crate::passes::parse::{Lit, Op}; +use crate::passes::parse::Op; +use crate::passes::validate::TLit; use crate::utils::gen_sym::UniqueSym; use crate::utils::push_map::PushMap; use derive_more::Display; @@ -53,12 +54,12 @@ impl<'p> From> for Val<'p, UniqueSym<'p>> { } } -impl<'p> From for EVal<'p> { - fn from(value: Lit) -> Self { +impl<'p> From for EVal<'p> { + fn from(value: TLit) -> Self { match value { - Lit::Int { val } => Self::Int { val }, - Lit::Bool { val } => Self::Bool { val }, - Lit::Unit => Self::Unit, + TLit::Int { val } => Self::Int { val: val as i64 }, + TLit::Bool { val } => Self::Bool { val }, + TLit::Unit => Self::Unit, } } } @@ -108,7 +109,9 @@ impl<'p> PrgEliminated<'p> { (Op::Read, []) => io.read().into(), (Op::Print, [v]) => { let val = self.interpret_atom(v, scope); - io.print(Lit::Int { val: val.int() }); + io.print(TLit::Int { + val: val.int() as i32, + }); val } (Op::Plus, [e1, e2]) => { diff --git a/compiler/src/passes/explicate/explicate.rs b/compiler/src/passes/explicate/explicate.rs index 5a570d2..db97725 100644 --- a/compiler/src/passes/explicate/explicate.rs +++ b/compiler/src/passes/explicate/explicate.rs @@ -1,7 +1,8 @@ use crate::passes::atomize::{AExpr, Atom, PrgAtomized}; use crate::passes::explicate::{CExpr, CTail, PrgExplicated}; use crate::passes::parse::types::Type; -use crate::passes::parse::{Def, Lit, Op, TypeDef}; +use crate::passes::parse::{Def, Op, TypeDef}; +use crate::passes::validate::TLit; use crate::utils::gen_sym::{gen_sym, UniqueSym}; use std::collections::HashMap; @@ -167,7 +168,7 @@ fn explicate_assign<'p>( explicate_assign( sym, AExpr::Atom { - atm: Atom::Val { val: Lit::Unit }, + atm: Atom::Val { val: TLit::Unit }, typ: Type::Unit, }, tail, @@ -229,7 +230,7 @@ fn explicate_pred<'p>( args: vec![ Atom::Var { sym }, Atom::Val { - val: Lit::Bool { val: true }, + val: TLit::Bool { val: true }, }, ], typ: Type::Bool, @@ -239,7 +240,7 @@ fn explicate_pred<'p>( }, AExpr::Atom { atm: Atom::Val { - val: Lit::Bool { val }, + val: TLit::Bool { val }, }, .. } => { @@ -383,7 +384,7 @@ fn explicate_pred<'p>( AExpr::FunRef { .. } | AExpr::Atom { atm: Atom::Val { - val: Lit::Int { .. } | Lit::Unit, + val: TLit::Int { .. } | TLit::Unit, }, .. } diff --git a/compiler/src/passes/explicate/interpret.rs b/compiler/src/passes/explicate/interpret.rs index 8eaf7c6..abbf53c 100644 --- a/compiler/src/passes/explicate/interpret.rs +++ b/compiler/src/passes/explicate/interpret.rs @@ -2,7 +2,8 @@ use crate::interpreter::Val; use crate::interpreter::IO; use crate::passes::atomize::Atom; use crate::passes::explicate::{CExpr, CTail, PrgExplicated}; -use crate::passes::parse::{Lit, Op}; +use crate::passes::parse::Op; +use crate::passes::validate::TLit; use crate::utils::gen_sym::UniqueSym; use crate::utils::push_map::PushMap; use std::collections::HashMap; @@ -46,7 +47,9 @@ impl<'p> PrgExplicated<'p> { (Op::Read, []) => io.read().into(), (Op::Print, [v]) => { let val = self.interpret_atom(v, scope); - io.print(Lit::Int { val: val.int() }); + io.print(TLit::Int { + val: val.int() as i32, + }); val } (Op::Plus, [e1, e2]) => { diff --git a/compiler/src/passes/parse/grammar.lalrpop b/compiler/src/passes/parse/grammar.lalrpop index ad05b70..6274d0f 100644 --- a/compiler/src/passes/parse/grammar.lalrpop +++ b/compiler/src/passes/parse/grammar.lalrpop @@ -1,7 +1,8 @@ -use std::str::FromStr; use crate::passes::parse::{Def, TypeDef, Expr, Lit, Op, Param}; use crate::passes::parse::PrgParsed; use crate::passes::parse::Type; +use crate::passes::parse::Spanned; +use functor_derive::Functor; grammar; @@ -20,7 +21,6 @@ match { "struct", "enum", "switch", - "never", // Structural tokens "(", @@ -37,7 +37,7 @@ match { "=>", // Identifier - r"[_a-zA-Z][_a-zA-Z0-9]*", + r"[_a-zA-Z][_a-zA-Z0-9]*" => identifier, // Integer operators "+", @@ -60,7 +60,7 @@ match { "true", "false", "unit", - r"[0-9]+", + r"[0-9]+" => integer, // Logical operators "^", @@ -93,7 +93,7 @@ pub Program: PrgParsed<'input> = { } } -Def: Def<'input, &'input str, Expr<'input, &'input str>> = { +Def: Def<'input, &'input str, Spanned>> = { "struct" "{" ":" )>> "}" => Def::TypeDef { sym, def: TypeDef::Struct { fields }, @@ -102,11 +102,11 @@ Def: Def<'input, &'input str, Expr<'input, &'input str>> = { sym, def: TypeDef::Enum { variants }, }, - "fn" "(" > ")" " )?> "{" "}" => Def::Fn { + "fn" "(" > ")" " )?> "{" > "}" => Def::Fn { sym, params, typ: typ.unwrap_or(Type::Unit), - bdy: bdy.unwrap_or(Expr::Lit { val: Lit::Unit }), + bdy: bdy.fmap(|bdy| bdy.unwrap_or(Expr::Lit { val: Lit::Unit })), }, } @@ -142,59 +142,75 @@ Type: Type<&'input str> = { // Num/Bool/Ident Expr = ExprStmt; -ExprStmt: Expr<'input, &'input str> = { - "let" "=" > ";" => Expr::Let { +ExprStmt: Expr<'input> = { + "let" "=" >> ";" > => Expr::Let { sym, mutable: mutable.is_some(), bnd: Box::new(bnd), - bdy: Box::new(bdy.unwrap_or(Expr::Lit { val: Lit::Unit })), + bdy: Box::new(bdy.fmap(|bdy| bdy.unwrap_or(Expr::Lit { val: Lit::Unit }))), }, - ";" => Expr::Seq { + > ";" > => Expr::Seq { stmt: Box::new(stmt), - cnt: Box::new(cnt.unwrap_or(Expr::Lit { val: Lit::Unit })), + cnt: Box::new(cnt.fmap(|cnt| cnt.unwrap_or(Expr::Lit { val: Lit::Unit }))), }, ExprInStmt, } -ExprInStmt: Expr<'input, &'input str> = { - "=" > => Expr::Assign { +ExprInStmt: Expr<'input> = { + > "=" >> => Expr::Assign { sym, bnd: Box::new(bnd), }, - "if" > "{" "}" "}")?> => Expr::If { + "if" >> "{" > "}" > "}")?> => Expr::If { cnd: Box::new(cnd), thn: Box::new(thn), - els: Box::new(els.unwrap_or(Expr::Lit { val: Lit::Unit })), + els: Box::new(els.unwrap_or(Spanned { span: (l, r), inner: Expr::Lit { val: Lit::Unit }})), }, - "loop" "{" "}" => Expr::Loop { + "loop" "{" > "}" => Expr::Loop { bdy: Box::new(bdy), }, - "while" > "{" "}" => Expr::Loop { - bdy: Box::new(Expr::If { - cnd: Box::new(cnd), - thn: Box::new(bdy), - els: Box::new(Expr::Seq { - stmt: Box::new(Expr::Break { bdy: Box::new(Expr::Lit { val: Lit::Unit }) }), - cnt: Box::new(Expr::Lit { val: Lit::Unit }), - }), + // todo: the spans in this desugaring do not make a lot sense. + "while" >> "{" > "}" => Expr::Loop { + bdy: Box::new(Spanned { + span: (l, r), + inner: Expr::If { + cnd: Box::new(cnd), + thn: Box::new(bdy), + els: Box::new(Spanned { + span: (l, r), + inner: Expr::Seq { + stmt: Box::new(Spanned { + span: (l, r), + inner: Expr::Break { bdy: Box::new(Spanned { + span: (l, r), + inner: Expr::Lit { val: Lit::Unit }, + })}, + }), + cnt: Box::new(Spanned { + span: (l, r), + inner: Expr::Lit { val: Lit::Unit }, + }), + }, + }), + }, }), }, - "switch" > "{" "(" ")" "=>" )>> "}" => Expr::Switch { + "switch" >> "{" "(" ")" "=>" > )>> "}" => Expr::Switch { enm: Box::new(enm), arms: arms.into_iter().map(|(s1, s2, e)| (s1, s2, Box::new(e))).collect(), }, - "break" ?> => Expr::Break { - bdy: Box::new(bdy.unwrap_or(Expr::Lit { val: Lit::Unit })), + "break" ?>> => Expr::Break { + bdy: Box::new(bdy.fmap(|bdy| bdy.unwrap_or(Expr::Lit { val: Lit::Unit }))), }, - "return" ?> => Expr::Return { - bdy: Box::new(bdy.unwrap_or(Expr::Lit { val: Lit::Unit })), + "return" ?>> => Expr::Return { + bdy: Box::new(bdy.fmap(|bdy| bdy.unwrap_or(Expr::Lit { val: Lit::Unit }))), }, "continue" => Expr::Continue, ExprLogicalOr, } -BinaryOps: Expr<'input, &'input str> = { - > => Expr::Prim { +BinaryOps: Expr<'input> = { + >> > => Expr::Prim { op, args: vec![e1, e2], }, @@ -233,44 +249,44 @@ UnaryOp: Op = { "!" => Op::Not, } -ExprUnary: Expr<'input, &'input str> = { - > => Expr::Prim { +ExprUnary: Expr<'input> = { + >> => Expr::Prim { op, args: vec![e], }, ExprAccess, } -ExprAccess: Expr<'input, &'input str> = { - > "." => Expr::AccessField { +ExprAccess: Expr<'input> = { + >> "." => Expr::AccessField { strct: Box::new(strct), field, }, ExprCall, } -ExprCall: Expr<'input, &'input str> = { +ExprCall: Expr<'input> = { "read" "(" ")" => Expr::Prim { op: Op::Read, args: vec![], }, - "print" "(" ")" => Expr::Prim { + "print" "(" > ")" => Expr::Prim { op: Op::Print, args: vec![e], }, - > "(" > ")" => Expr::Apply { + >> "(" >> ")" => Expr::Apply { fun: Box::new(fun), args, }, ExprAtom, } -ExprAtom: Expr<'input, &'input str> = { - => Expr::Lit{ val: Lit::Int { val }}, - => Expr::Lit { val: Lit::Bool { val }}, +ExprAtom: Expr<'input> = { + => Expr::Lit { val: Lit::Int { val } }, + => Expr::Lit { val: Lit::Bool { val } }, "unit" => Expr::Lit { val: Lit::Unit }, => Expr::Var { sym }, - "::" "(" ")" => Expr::Variant { + "::" "(" > ")" => Expr::Variant { enum_sym, variant_sym, bdy: Box::new(bdy), @@ -279,25 +295,23 @@ ExprAtom: Expr<'input, &'input str> = { , } -Struct: Expr<'input, &'input str> = { +Struct: Expr<'input> = { "{" > "}" => Expr::Struct { sym, fields, }, } -StructArg : (&'input str, Expr<'input, &'input str>) = { - ":" , - => (<>, Expr::Var { sym: <> }) +StructArg : (&'input str, Spanned>) = { + ":" >, + => (sym, Spanned { span: (l, r), inner: Expr::Var { sym } }) } -Never: Expr<'input, &'input str> = { - "never" => panic!("The reserved keyword 'never' should never be parsed.") -} +Never: Expr<'input> = {}; -Ident: &'input str = r"[_a-zA-Z][_a-zA-Z0-9]*"; +Ident: &'input str = identifier; -Num: i64 = => i64::from_str(s).unwrap(); +Num: &'input str = ; Bool: bool = { "true" => true, @@ -314,3 +328,6 @@ Comma: Vec = { } } } + +Spanned: Spanned = => Spanned { span: (l, r), inner }; + diff --git a/compiler/src/passes/parse/interpreter.rs b/compiler/src/passes/parse/interpreter.rs index df19cb3..3bbfc5d 100644 --- a/compiler/src/passes/parse/interpreter.rs +++ b/compiler/src/passes/parse/interpreter.rs @@ -1,8 +1,8 @@ use crate::interpreter::Val; use crate::interpreter::IO; -use crate::passes::parse::{Def, Lit, Op}; +use crate::passes::parse::{Def, Op}; use crate::passes::uniquify::PrgUniquified; -use crate::passes::validate::TExpr; +use crate::passes::validate::{TExpr, TLit}; use crate::utils::gen_sym::UniqueSym; use crate::utils::push_map::PushMap; use std::collections::HashMap; @@ -73,7 +73,9 @@ impl<'p> PrgUniquified<'p> { (Op::Read, []) => io.read().into(), (Op::Print, [v]) => { let val = b!(self.interpret_expr(v, scope, io)); - io.print(Lit::Int { val: val.int() }); + io.print(TLit::Int { + val: val.int() as i32, + }); val } (Op::Plus, [e1, e2]) => { diff --git a/compiler/src/passes/parse/mod.rs b/compiler/src/passes/parse/mod.rs index bcbe7d1..2a0e805 100644 --- a/compiler/src/passes/parse/mod.rs +++ b/compiler/src/passes/parse/mod.rs @@ -9,16 +9,16 @@ pub mod parse; pub mod types; use derive_more::Display; +use functor_derive::Functor; use std::fmt::Display; use std::hash::Hash; -use std::str::FromStr; use types::Type; /// A parsed program with global definitions and an entry point. #[derive(Debug, PartialEq)] pub struct PrgParsed<'p> { /// The global program definitions. - pub defs: Vec>>, + pub defs: Vec>>>, /// The symbol representing the entry point of the program. pub entry: &'p str, } @@ -86,23 +86,23 @@ pub struct Param { /// Expressions are generic and can use symbols that are either `&str` or /// [`UniqueSym`](crate::utils::gen_sym::UniqueSym) for all passes after uniquify. #[derive(Debug, PartialEq)] -pub enum Expr<'p, A: Copy + Hash + Eq> { +pub enum Expr<'p> { /// A literal value. See [`Lit`]. Lit { /// Value of the literal. See [`Lit`]. - val: Lit, + val: Lit<'p>, }, /// A variable. Var { /// Symbol representing the variable. - sym: A, + sym: &'p str, }, /// A primitive operation with an arbitrary number of arguments. Prim { /// Primitive operation (e.g. `Xor`). See [`Op`]. op: Op, /// Arguments used by the primitive operation. - args: Vec>, + args: Vec>>, }, /// A let binding. /// @@ -111,13 +111,13 @@ pub enum Expr<'p, A: Copy + Hash + Eq> { /// The variable can be immutable or mutable depending on the presence of the `mut` keyword. Let { /// Symbol representing the newly introduced variable. - sym: A, + sym: &'p str, /// Indicates whether the variable is mutable (true) or immutable (false). mutable: bool, /// The expression to which the variable is bound. - bnd: Box>, + bnd: Box>>, /// The expression that is evaluated using the new variable binding. - bdy: Box>, + bdy: Box>>, }, /// An if statement. /// @@ -125,11 +125,11 @@ pub enum Expr<'p, A: Copy + Hash + Eq> { /// the result is true, it executes the `thn` expression; otherwise, it executes the `els` expression. If { /// The conditional expression that determines the execution path. - cnd: Box>, + cnd: Box>>, /// The expression to execute if the condition is true. - thn: Box>, + thn: Box>>, /// The expression to execute if the condition is false. - els: Box>, + els: Box>>, }, /// A function application. /// @@ -137,9 +137,9 @@ pub enum Expr<'p, A: Copy + Hash + Eq> { /// evaluated to obtain a function symbol, which is invoked with the arguments in `args`. Apply { /// The expression that, when evaluated, represents the function symbol to be invoked. - fun: Box>, + fun: Box>>, /// The ordered arguments that are passed to the function. - args: Vec>, + args: Vec>>, }, /// A loop construct. /// @@ -147,7 +147,7 @@ pub enum Expr<'p, A: Copy + Hash + Eq> { /// expression is evaluated. Loop { /// The expression that defines the body of the loop. - bdy: Box>, + bdy: Box>>, }, /// A break statement. /// @@ -155,7 +155,7 @@ pub enum Expr<'p, A: Copy + Hash + Eq> { /// current loop and returns the value of the `bdy` expression from the loop upon termination. Break { /// The expression to be evaluated and returned from the loop. - bdy: Box>, + bdy: Box>>, }, /// A continue statement. /// @@ -167,7 +167,7 @@ pub enum Expr<'p, A: Copy + Hash + Eq> { /// The `Return` expression exits the current function and returns the value of the `bdy` expression. Return { /// The expression to be evaluated and returned from the function. - bdy: Box>, + bdy: Box>>, }, /// A sequence of two expressions. /// @@ -176,9 +176,9 @@ pub enum Expr<'p, A: Copy + Hash + Eq> { /// the `cnt` expression is evaluated. Seq { /// The first expression to be executed in the sequence. - stmt: Box>, + stmt: Box>>, /// The second expression to be executed in the sequence. - cnt: Box>, + cnt: Box>>, }, /// A variable assignment. /// @@ -187,41 +187,47 @@ pub enum Expr<'p, A: Copy + Hash + Eq> { /// Only mutable or uninitialized immutable variables can be assigned a new value. Assign { /// Symbol representing the variable to which the assignment is made. - sym: A, + sym: Spanned<&'p str>, /// The expression whose result is assigned to the variable. - bnd: Box>, + bnd: Box>>, }, /// An instance of a struct. /// /// todo: documentation Struct { - sym: A, - fields: Vec<(&'p str, Expr<'p, A>)>, + sym: &'p str, + fields: Vec<(&'p str, Spanned>)>, }, /// A variant of an enum. /// /// todo: documentation Variant { - enum_sym: A, - variant_sym: A, - bdy: Box>, + enum_sym: &'p str, + variant_sym: &'p str, + bdy: Box>>, }, /// A field access. /// /// todo: documentation AccessField { - strct: Box>, + strct: Box>>, field: &'p str, }, /// A switch statement. /// /// todo: documentation Switch { - enm: Box>, - arms: Vec<(A, A, Box>)>, + enm: Box>>, + arms: Vec<(&'p str, &'p str, Box>>)>, }, } +#[derive(Debug, PartialEq, Functor)] +pub struct Spanned { + pub span: (usize, usize), + pub inner: T, +} + /// A primitive operation. #[derive(Copy, Clone, Debug, PartialEq)] pub enum Op { @@ -263,10 +269,10 @@ pub enum Op { /// A literal value. #[derive(Copy, Clone, Debug, PartialEq, Display)] -pub enum Lit { +pub enum Lit<'p> { /// Integer literal, representing a signed 64-bit number. #[display(fmt = "{val}")] - Int { val: i64 }, + Int { val: &'p str }, /// Boolean literal, representing a value of *true* or *false*. #[display(fmt = "{}", r#"if *val { "true" } else { "false" }"#)] Bool { val: bool }, @@ -275,59 +281,6 @@ pub enum Lit { Unit, } -impl Lit { - /// Returns the integer value if `Lit` is `Int`. - /// # Panics - /// Panics if `Lit` is not `Int`. - #[must_use] - pub fn int(self) -> i64 { - if let Lit::Int { val } = self { - val - } else { - panic!() - } - } - - /// Returns the boolean value if `Lit` is `Bool`. - /// # Panics - /// Panics if `Lit` is not `Bool`. - #[must_use] - pub fn bool(self) -> bool { - if let Lit::Bool { val } = self { - val - } else { - panic!() - } - } -} - -// todo: we probably want to get rid off this -impl From for i64 { - fn from(value: Lit) -> Self { - match value { - Lit::Int { val } => val, - Lit::Bool { val } => val as i64, - Lit::Unit => 0, - } - } -} - -// This implementation is used by the parser. -impl FromStr for Lit { - type Err = (); - - fn from_str(s: &str) -> Result { - Ok(match s { - "false" => Lit::Bool { val: false }, - "true" => Lit::Bool { val: true }, - "unit" => Lit::Unit, - s => Lit::Int { - val: s.parse().map_err(|_| ())?, - }, - }) - } -} - #[cfg(test)] mod tests { use crate::utils::split_test::split_test; diff --git a/compiler/src/passes/parse/parse.rs b/compiler/src/passes/parse/parse.rs index 63adc2a..d824426 100644 --- a/compiler/src/passes/parse/parse.rs +++ b/compiler/src/passes/parse/parse.rs @@ -1,26 +1,56 @@ use crate::passes::parse::grammar::ProgramParser; use crate::passes::parse::PrgParsed; -use miette::{Diagnostic, SourceSpan}; +use itertools::Itertools; +use lalrpop_util::lexer::Token; +use lalrpop_util::ParseError; +use miette::Diagnostic; use thiserror::Error; #[derive(Error, Debug, Diagnostic)] -#[error("Parse error!")] -#[diagnostic( - code(oops::my::bad), - url(docsrs), - help("try doing it better next time?") -)] -pub struct PrettyParseError { - #[source_code] - src: String, - - #[label("Failed to parse here")] - fail: SourceSpan, +pub enum PrettyParseError { + #[error("Parse error: Invalid token.")] + InvalidToken { + #[label("Failed to parse here, invalid token starting here")] + fail: (usize, usize), + }, + #[error("Parse error: Unexpected token.")] + UnexpectedToken { + #[label("Failed to parse here, expected one of: {expected}")] + fail: (usize, usize), + expected: String, + }, + #[error("Parse error: Unexpected end of file.")] + UnexpectedEOF { + #[label("Unexpected end-of-file, expected one of: {expected}")] + fail: (usize, usize), + expected: String, + }, } pub fn parse_program(src: &str) -> Result { - ProgramParser::new().parse(src).map_err(|e| { - dbg!(e); - panic!(); - }) + ProgramParser::new().parse(src).map_err(From::from) +} + +impl<'p> From, &'p str>> for PrettyParseError { + fn from(value: ParseError, &'p str>) -> Self { + match value { + ParseError::InvalidToken { location } => PrettyParseError::InvalidToken { + fail: (location, 1), + }, + ParseError::UnrecognizedEof { location, expected } => PrettyParseError::UnexpectedEOF { + fail: (location, 1), + expected: expected.into_iter().format(", ").to_string(), + }, + ParseError::UnrecognizedToken { token, expected } => { + PrettyParseError::UnexpectedToken { + fail: (token.0, token.2 - token.0), + expected: expected.into_iter().format(", ").to_string(), + } + } + ParseError::ExtraToken { .. } => { + unreachable!("Our grammar always consumes the entire input.") + } + ParseError::User { .. } => unreachable!("No custom `ParseError`s are implemented."), + } + } } diff --git a/compiler/src/passes/reveal_functions/mod.rs b/compiler/src/passes/reveal_functions/mod.rs index 8d8ad6d..b6dc993 100644 --- a/compiler/src/passes/reveal_functions/mod.rs +++ b/compiler/src/passes/reveal_functions/mod.rs @@ -1,9 +1,9 @@ pub mod reveal_functions; use crate::passes::parse::types::Type; -use crate::passes::parse::{Def, Lit, Op}; +use crate::passes::parse::{Def, Op}; use crate::passes::uniquify::PrgUniquified; -use crate::passes::validate::TExpr; +use crate::passes::validate::{TExpr, TLit}; use crate::utils::gen_sym::UniqueSym; use std::collections::HashMap; @@ -16,7 +16,7 @@ pub struct PrgRevealed<'p> { #[derive(Debug, PartialEq)] pub enum RExpr<'p> { Lit { - val: Lit, + val: TLit, typ: Type>, }, Var { diff --git a/compiler/src/passes/reveal_functions/reveal_functions.rs b/compiler/src/passes/reveal_functions/reveal_functions.rs index 0c7d8d7..87ed233 100644 --- a/compiler/src/passes/reveal_functions/reveal_functions.rs +++ b/compiler/src/passes/reveal_functions/reveal_functions.rs @@ -13,30 +13,33 @@ impl<'p> PrgUniquified<'p> { defs: self .defs .into_iter() - .map(|(sym, def)| { - let def = match def { - Def::Fn { - sym, - params, - typ, - bdy, - } => Def::Fn { - sym, - params, - typ, - bdy: reveal_expr(bdy, &mut scope), - }, - Def::TypeDef { sym, def } => Def::TypeDef { sym, def }, - }; - - (sym, def) - }) + .map(|(sym, def)| (sym, reveal_def(def, &mut scope))) .collect(), entry: self.entry, } } } +fn reveal_def<'p>( + def: Def<'p, UniqueSym<'p>, TExpr<'p, UniqueSym<'p>>>, + scope: &mut PushMap, ()>, +) -> Def<'p, UniqueSym<'p>, RExpr<'p>> { + match def { + Def::Fn { + sym, + params, + typ, + bdy, + } => Def::Fn { + sym, + params, + typ, + bdy: reveal_expr(bdy, scope), + }, + Def::TypeDef { sym, def } => Def::TypeDef { sym, def }, + } +} + fn reveal_expr<'p>( expr: TExpr<'p, UniqueSym<'p>>, scope: &mut PushMap, ()>, diff --git a/compiler/src/passes/select/interpreter.rs b/compiler/src/passes/select/interpreter.rs index 356d9b7..137aa03 100644 --- a/compiler/src/passes/select/interpreter.rs +++ b/compiler/src/passes/select/interpreter.rs @@ -1,9 +1,10 @@ use crate::interpreter::IO; use crate::passes::conclude::X86Concluded; -use crate::passes::parse::Lit; + use crate::passes::select::{ Block, Cnd, Instr, Reg, VarArg, X86Selected, CALLEE_SAVED, CALLER_SAVED, }; +use crate::passes::validate::TLit; use crate::utils::gen_sym::UniqueSym; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -357,7 +358,7 @@ impl<'p, I: IO> X86Interpreter<'p, I> { .unwrap() .parse() .unwrap(); - self.io.print(Lit::Int { val }); + self.io.print(TLit::Int { val }); self.write_buffer.clear(); } val => { diff --git a/compiler/src/passes/validate/mod.rs b/compiler/src/passes/validate/mod.rs index 58009a1..dd3c132 100644 --- a/compiler/src/passes/validate/mod.rs +++ b/compiler/src/passes/validate/mod.rs @@ -3,12 +3,14 @@ mod type_check; pub mod validate; use crate::passes::parse::types::Type; -use crate::passes::parse::{Def, Lit, Op}; +use crate::passes::parse::{Def, Op}; use crate::passes::validate::type_check::error::TypeError; +use derive_more::Display; use miette::Diagnostic; use std::collections::HashMap; use std::fmt::Display; use std::hash::Hash; +use std::str::FromStr; use thiserror::Error; #[derive(Debug, PartialEq)] @@ -20,7 +22,7 @@ pub struct PrgTypeChecked<'p> { #[derive(Debug, PartialEq)] pub enum TExpr<'p, A: Copy + Hash + Eq + Display> { Lit { - val: Lit, + val: TLit, typ: Type, }, Var { @@ -97,6 +99,68 @@ pub enum TExpr<'p, A: Copy + Hash + Eq + Display> { }, } +#[derive(Copy, Clone, Debug, PartialEq, Display)] +pub enum TLit { + #[display(fmt = "{val}")] + Int { val: i32 }, + #[display(fmt = "{}", r#"if *val { "true" } else { "false" }"#)] + Bool { val: bool }, + #[display(fmt = "unit")] + Unit, +} + +impl TLit { + /// Returns the integer value if `TLit` is `Int`. + /// # Panics + /// Panics if `TLit` is not `Int`. + #[must_use] + pub fn int(self) -> i64 { + if let TLit::Int { val } = self { + val as i64 + } else { + panic!() + } + } + + /// Returns the boolean value if `TLit` is `Bool`. + /// # Panics + /// Panics if `TLit` is not `Bool`. + #[must_use] + pub fn bool(self) -> bool { + if let TLit::Bool { val } = self { + val + } else { + panic!() + } + } +} + +impl From for i64 { + fn from(value: TLit) -> Self { + match value { + TLit::Int { val } => val as i64, + TLit::Bool { val } => val as i64, + TLit::Unit => 0, + } + } +} + +// This implementation is used by the parser. +impl FromStr for TLit { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + "false" => TLit::Bool { val: false }, + "true" => TLit::Bool { val: true }, + "unit" => TLit::Unit, + s => TLit::Int { + val: s.parse().map_err(|_| ())?, + }, + }) + } +} + impl<'p, A: Copy + Hash + Eq + Display> TExpr<'p, A> { pub fn typ(&self) -> &Type { match self { @@ -121,9 +185,9 @@ impl<'p, A: Copy + Hash + Eq + Display> TExpr<'p, A> { } #[derive(Debug, Error, Diagnostic)] -#[diagnostic()] pub enum ValidateError { #[error(transparent)] + #[diagnostic(transparent)] TypeError(#[from] TypeError), #[error("The program doesn't have a main function.")] NoMain, diff --git a/compiler/src/passes/validate/type_check/error.rs b/compiler/src/passes/validate/type_check/error.rs index 16c60fa..48afb0f 100644 --- a/compiler/src/passes/validate/type_check/error.rs +++ b/compiler/src/passes/validate/type_check/error.rs @@ -3,19 +3,31 @@ use miette::Diagnostic; use thiserror::Error; #[derive(Debug, Error, Diagnostic)] -#[diagnostic()] pub enum TypeError { - #[error("Variable '{sym}' was not declared yet.")] - UndeclaredVar { sym: String }, - #[error("Types were mismatched. Expected '{expect}', but found '{got}'.")] - TypeMismatchExpect { + #[error("Encountered an undeclared variable.")] + UndeclaredVar { + sym: String, + #[label = "This variable `{sym}` was not declared yet"] + span: (usize, usize), + }, + #[error("Type was mismatched.")] + MismatchedType { expect: Type, got: Type, + #[label = "Expected this to be of type `{expect}`, but got `{got}`"] + span: (usize, usize), }, #[error("Types were mismatched. Expected function, but found '{got}'.")] TypeMismatchExpectFn { got: Type }, #[error("Types were mismatched. Expected '{t1}' and '{t2}' to be equal.")] - TypeMismatchEqual { t1: Type, t2: Type }, + MismatchedTypes { + t1: Type, + t2: Type, + #[label = "This has type `{t1}`"] + span_t1: (usize, usize), + #[label = "but this has type `{t2}`"] + span_t2: (usize, usize), + }, #[error("There are multiple functions named `{sym}`.")] DuplicateFunction { sym: String }, #[error("Function `{sym}` has duplicate argument names.")] @@ -40,4 +52,10 @@ pub enum TypeError { TypeShouldBeStruct { typ: Type }, #[error("The type definition `{sym}` is not sized.'")] UnsizedType { sym: String }, + + #[error("Integer out of bounds.")] + IntegerOutOfBounds { + #[label = "This number does not fit in an i32: `-2147483648..=2147483647`"] + span: (usize, usize), + }, } diff --git a/compiler/src/passes/validate/type_check/mod.rs b/compiler/src/passes/validate/type_check/mod.rs index a1d1cfc..ecafe6b 100644 --- a/compiler/src/passes/validate/type_check/mod.rs +++ b/compiler/src/passes/validate/type_check/mod.rs @@ -1,13 +1,27 @@ pub mod error; mod uncover_globals; +mod validate_access_field; +mod validate_apply; +mod validate_assign; +mod validate_break; +mod validate_continue; mod validate_expr; +mod validate_if; +mod validate_let; +mod validate_lit; +mod validate_loop; mod validate_prim; +mod validate_return; +mod validate_seq; mod validate_struct; +mod validate_switch; mod validate_type; mod validate_typedef; +mod validate_var; +mod validate_variant; use crate::passes::parse::types::Type; -use crate::passes::parse::{Def, PrgParsed, TypeDef}; +use crate::passes::parse::{Def, PrgParsed, Spanned, TypeDef}; use crate::passes::validate::type_check::error::TypeError; use crate::passes::validate::type_check::error::TypeError::*; use crate::passes::validate::type_check::uncover_globals::uncover_globals; @@ -85,7 +99,7 @@ impl<'p> PrgParsed<'p> { Def::Fn { sym, params, - bdy, + bdy: bdy.inner, typ, }, )) @@ -109,31 +123,40 @@ impl<'p> PrgParsed<'p> { } pub fn expect_type_eq<'p>( - e1: &TExpr<'p, &'p str>, - e2: &TExpr<'p, &'p str>, + e1: &Spanned>, + e2: &Spanned>, ) -> Result, TypeError> { - let t1 = e1.typ(); - let t2 = e2.typ(); + let t1 = e1.inner.typ(); + let t2 = e2.inner.typ(); + expect( t1 == t2, - TypeMismatchEqual { + MismatchedTypes { t1: t1.clone().fmap(str::to_string), t2: t2.clone().fmap(str::to_string), + span_t1: s(e1.span), + span_t2: s(e2.span), }, )?; + Ok(t1.clone()) } pub fn expect_type<'p>( - expr: &TExpr<'p, &'p str>, + expr: &Spanned>, expected: &Type<&'p str>, ) -> Result<(), TypeError> { - let t = expr.typ(); + let typ = expr.inner.typ(); expect( - t == expected, - TypeMismatchExpect { - got: t.clone().fmap(str::to_string), + typ == expected, + MismatchedType { + got: typ.clone().fmap(str::to_string), expect: expected.clone().fmap(str::to_string), + span: s(expr.span), }, ) } + +pub fn s(span: (usize, usize)) -> (usize, usize) { + (span.0, span.1 - span.0) +} diff --git a/compiler/src/passes/validate/type_check/validate_access_field.rs b/compiler/src/passes/validate/type_check/validate_access_field.rs new file mode 100644 index 0000000..61df5b9 --- /dev/null +++ b/compiler/src/passes/validate/type_check/validate_access_field.rs @@ -0,0 +1,48 @@ +use crate::passes::parse::types::Type; +use crate::passes::parse::{Expr, Spanned, TypeDef}; +use crate::passes::validate::type_check::error::TypeError; +use crate::passes::validate::type_check::error::TypeError::*; +use crate::passes::validate::type_check::validate_expr::validate_expr; +use crate::passes::validate::type_check::{Env, EnvEntry}; +use crate::passes::validate::TExpr; + +pub fn validate_access_field<'p>( + strct: Spanned>, + field: &'p str, + span: (usize, usize), + env: &mut Env<'_, 'p>, +) -> Result>, TypeError> { + let strct = validate_expr(strct, env)?; + + let Type::Var { sym } = strct.inner.typ() else { + return Err(TypeShouldBeStruct { + typ: strct.inner.typ().clone().fmap(str::to_string), + }); + }; + + let EnvEntry::Def { + def: TypeDef::Struct { + fields: def_fields, .. + }, + } = &env.scope[sym] + else { + return Err(VariableShouldBeStruct { + sym: sym.to_string(), + }); + }; + + let Some((_, typ)) = def_fields.iter().find(|&(sym, _)| *sym == field) else { + return Err(UnknownStructField { + sym: sym.to_string(), + }); + }; + + Ok(Spanned { + span, + inner: TExpr::AccessField { + strct: Box::new(strct.inner), + field, + typ: typ.clone(), + }, + }) +} diff --git a/compiler/src/passes/validate/type_check/validate_apply.rs b/compiler/src/passes/validate/type_check/validate_apply.rs new file mode 100644 index 0000000..2642199 --- /dev/null +++ b/compiler/src/passes/validate/type_check/validate_apply.rs @@ -0,0 +1,49 @@ +use crate::passes::parse::types::Type; +use crate::passes::parse::{Expr, Spanned}; +use crate::passes::validate::type_check::error::TypeError; +use crate::passes::validate::type_check::error::TypeError::*; +use crate::passes::validate::type_check::validate_expr::validate_expr; +use crate::passes::validate::type_check::{expect_type, Env}; +use crate::passes::validate::TExpr; +use crate::utils::expect::expect; +use functor_derive::Functor; + +pub fn validate_apply<'p>( + fun: Spanned>, + args: Vec>>, + span: (usize, usize), + env: &mut Env<'_, 'p>, +) -> Result>, TypeError> { + let fun = validate_expr(fun, env)?; + let args = args + .into_iter() + .map(|arg| validate_expr(arg, env)) + .collect::, _>>()?; + + let Type::Fn { params, typ } = fun.inner.typ() else { + return Err(TypeMismatchExpectFn { + got: fun.inner.typ().clone().fmap(str::to_string), + }); + }; + + expect( + params.len() == args.len(), + ArgCountMismatch { + expected: params.len(), + got: args.len(), + }, + )?; + + for (arg, param_type) in args.iter().zip(params.iter()) { + expect_type(arg, param_type)?; + } + + Ok(Spanned { + span, + inner: TExpr::Apply { + typ: (**typ).clone(), + fun: Box::new(fun.inner), + args: args.fmap(|arg| arg.inner), + }, + }) +} diff --git a/compiler/src/passes/validate/type_check/validate_assign.rs b/compiler/src/passes/validate/type_check/validate_assign.rs new file mode 100644 index 0000000..60afeaf --- /dev/null +++ b/compiler/src/passes/validate/type_check/validate_assign.rs @@ -0,0 +1,44 @@ +use crate::passes::parse::types::Type; +use crate::passes::parse::{Expr, Spanned}; +use crate::passes::validate::type_check::error::TypeError; +use crate::passes::validate::type_check::error::TypeError::*; +use crate::passes::validate::type_check::validate_expr::validate_expr; +use crate::passes::validate::type_check::{s, Env, EnvEntry}; +use crate::passes::validate::TExpr; +use crate::utils::expect::expect; + +pub fn validate_assign<'p>( + sym: Spanned<&'p str>, + bnd: Spanned>, + span: (usize, usize), + env: &mut Env<'_, 'p>, +) -> Result>, TypeError> { + let entry = env.scope.get(&sym.inner).ok_or(UndeclaredVar { + sym: (*sym.inner).to_string(), + span: s(sym.span), + })?; + + let EnvEntry::Type { mutable, .. } = entry else { + return Err(VariableShouldBeExpr { + sym: (*sym.inner).to_string(), + }); + }; + + expect( + *mutable, + ModifyImmutable { + sym: (*sym.inner).to_string(), + }, + )?; + + let bnd = validate_expr(bnd, env)?; + + Ok(Spanned { + span, + inner: TExpr::Assign { + sym: sym.inner, + bnd: Box::new(bnd.inner), + typ: Type::Unit, + }, + }) +} diff --git a/compiler/src/passes/validate/type_check/validate_break.rs b/compiler/src/passes/validate/type_check/validate_break.rs new file mode 100644 index 0000000..b1a79bb --- /dev/null +++ b/compiler/src/passes/validate/type_check/validate_break.rs @@ -0,0 +1,32 @@ +use crate::passes::parse::types::Type; +use crate::passes::parse::{Expr, Spanned}; +use crate::passes::validate::type_check::error::TypeError; +use crate::passes::validate::type_check::error::TypeError::*; +use crate::passes::validate::type_check::validate_expr::validate_expr; +use crate::passes::validate::type_check::{expect_type, Env}; +use crate::passes::validate::TExpr; +use crate::utils::expect::expect; + +pub fn validate_break<'p>( + bdy: Spanned>, + span: (usize, usize), + env: &mut Env<'_, 'p>, +) -> Result>, TypeError> { + expect(env.in_loop, BreakOutsideLoop)?; + + let bdy = validate_expr(bdy, env)?; + + if let Some(loop_type) = env.loop_type { + expect_type(&bdy, loop_type)?; + } else { + *env.loop_type = Some(bdy.inner.typ().clone()); + } + + Ok(Spanned { + span, + inner: TExpr::Break { + bdy: Box::new(bdy.inner), + typ: Type::Never, + }, + }) +} diff --git a/compiler/src/passes/validate/type_check/validate_continue.rs b/compiler/src/passes/validate/type_check/validate_continue.rs new file mode 100644 index 0000000..596fb18 --- /dev/null +++ b/compiler/src/passes/validate/type_check/validate_continue.rs @@ -0,0 +1,13 @@ +use crate::passes::parse::types::Type; +use crate::passes::parse::Spanned; +use crate::passes::validate::type_check::error::TypeError; +use crate::passes::validate::TExpr; + +pub fn validate_continue<'p>( + span: (usize, usize), +) -> Result>, TypeError> { + Ok(Spanned { + span, + inner: TExpr::Continue { typ: Type::Never }, + }) +} diff --git a/compiler/src/passes/validate/type_check/validate_expr.rs b/compiler/src/passes/validate/type_check/validate_expr.rs index a1f9224..20954e5 100644 --- a/compiler/src/passes/validate/type_check/validate_expr.rs +++ b/compiler/src/passes/validate/type_check/validate_expr.rs @@ -1,214 +1,45 @@ -use crate::passes::parse::types::Type; -use crate::passes::parse::{Expr, Lit, TypeDef}; +use crate::passes::parse::{Expr, Spanned}; use crate::passes::validate::type_check::error::TypeError; -use crate::passes::validate::type_check::error::TypeError::*; +use crate::passes::validate::type_check::validate_access_field::validate_access_field; +use crate::passes::validate::type_check::validate_apply::validate_apply; +use crate::passes::validate::type_check::validate_assign::validate_assign; +use crate::passes::validate::type_check::validate_break::validate_break; +use crate::passes::validate::type_check::validate_continue::validate_continue; +use crate::passes::validate::type_check::validate_if::validate_if; +use crate::passes::validate::type_check::validate_let::validate_let; +use crate::passes::validate::type_check::validate_lit::validate_lit; +use crate::passes::validate::type_check::validate_loop::validate_loop; use crate::passes::validate::type_check::validate_prim::validate_prim; +use crate::passes::validate::type_check::validate_return::validate_return; +use crate::passes::validate::type_check::validate_seq::validate_seq; use crate::passes::validate::type_check::validate_struct::validate_struct; -use crate::passes::validate::type_check::{expect_type, expect_type_eq, Env, EnvEntry}; +use crate::passes::validate::type_check::validate_switch::validate_switch; +use crate::passes::validate::type_check::validate_var::validate_var; +use crate::passes::validate::type_check::validate_variant::validate_variant; +use crate::passes::validate::type_check::Env; use crate::passes::validate::TExpr; -use crate::utils::expect::expect; +#[rustfmt::skip] pub fn validate_expr<'p>( - expr: Expr<'p, &'p str>, + expr: Spanned>, env: &mut Env<'_, 'p>, -) -> Result, TypeError> { - Ok(match expr { - Expr::Lit { val } => TExpr::Lit { - val, - typ: match val { - Lit::Int { .. } => Type::Int, - Lit::Bool { .. } => Type::Bool, - Lit::Unit => Type::Unit, - }, - }, - Expr::Var { sym } => { - let entry = env.scope.get(&sym).ok_or(UndeclaredVar { - sym: (*sym).to_string(), - })?; - - if let EnvEntry::Type { typ, .. } = entry { - TExpr::Var { - sym, - typ: typ.clone(), - } - } else { - return Err(VariableShouldBeExpr { - sym: (*sym).to_string(), - }); - } - } - Expr::Prim { op, args } => validate_prim(env, op, args)?, - Expr::Let { - sym, - mutable, - bnd, - bdy, - } => { - let bnd = validate_expr(*bnd, env)?; - let bdy = env.push( - sym, - EnvEntry::Type { - mutable, - typ: bnd.typ().clone(), - }, - |env| validate_expr(*bdy, env), - )?; - - TExpr::Let { - typ: bdy.typ().clone(), - sym, - bnd: Box::new(bnd), - bdy: Box::new(bdy), - } - } - Expr::If { cnd, thn, els } => { - let cnd = validate_expr(*cnd, env)?; - let thn = validate_expr(*thn, env)?; - let els = validate_expr(*els, env)?; - - expect_type(&cnd, &Type::Bool)?; - expect_type_eq(&thn, &els)?; - - TExpr::If { - typ: thn.typ().clone(), - cnd: Box::new(cnd), - thn: Box::new(thn), - els: Box::new(els), - } - } - Expr::Apply { fun, args } => { - let fun = validate_expr(*fun, env)?; - let args = args - .into_iter() - .map(|arg| validate_expr(arg, env)) - .collect::, _>>()?; - - let Type::Fn { params, typ } = fun.typ() else { - return Err(TypeMismatchExpectFn { - got: fun.typ().clone().fmap(str::to_string), - }); - }; - - expect( - params.len() == args.len(), - ArgCountMismatch { - expected: params.len(), - got: args.len(), - }, - )?; - - for (arg, param_type) in args.iter().zip(params.iter()) { - expect_type(arg, param_type)?; - } - - TExpr::Apply { - typ: (**typ).clone(), - fun: Box::new(fun), - args, - } - } - Expr::Loop { bdy } => { - let mut loop_type = None; - let mut env = Env { - scope: env.scope, - loop_type: &mut loop_type, - in_loop: true, - return_type: env.return_type, - }; - let bdy = validate_expr(*bdy, &mut env)?; - TExpr::Loop { - bdy: Box::new(bdy), - typ: loop_type.unwrap_or(Type::Never), - } - } - Expr::Break { bdy } => { - expect(env.in_loop, BreakOutsideLoop)?; - - let bdy = validate_expr(*bdy, env)?; - - if let Some(loop_type) = env.loop_type { - expect_type(&bdy, loop_type)?; - } else { - *env.loop_type = Some(bdy.typ().clone()); - } - - TExpr::Break { - bdy: Box::new(bdy), - typ: Type::Never, - } - } - Expr::Seq { stmt, cnt } => { - let stmt = validate_expr(*stmt, env)?; - let cnt = validate_expr(*cnt, env)?; - - TExpr::Seq { - typ: cnt.typ().clone(), - stmt: Box::new(stmt), - cnt: Box::new(cnt), - } - } - Expr::Assign { sym, bnd } => { - let entry = env.scope.get(&sym).ok_or(UndeclaredVar { - sym: (*sym).to_string(), - })?; - - let EnvEntry::Type { typ: _, mutable } = entry else { - return Err(VariableShouldBeExpr { - sym: (*sym).to_string(), - }); - }; - - expect( - *mutable, - ModifyImmutable { - sym: (*sym).to_string(), - }, - )?; - - let bnd = validate_expr(*bnd, env)?; - TExpr::Assign { - sym, - bnd: Box::new(bnd), - typ: Type::Unit, - } - } - Expr::Continue => TExpr::Continue { typ: Type::Never }, - Expr::Return { bdy } => { - let bdy = validate_expr(*bdy, env)?; - expect_type(&bdy, env.return_type)?; - TExpr::Return { - bdy: Box::new(bdy), - typ: Type::Never, - } - } - Expr::Struct { sym, fields } => validate_struct(env, sym, fields)?, - Expr::AccessField { strct, field } => { - let strct = validate_expr(*strct, env)?; - - let Type::Var { sym } = strct.typ() else { - return Err(TypeShouldBeStruct { - typ: strct.typ().clone().fmap(str::to_string), - }); - }; - - #[rustfmt::skip] - let EnvEntry::Def { def: TypeDef::Struct { fields: def_fields, .. } } = &env.scope[sym] else { - return Err(VariableShouldBeStruct { sym: sym.to_string() }); - }; - - let Some((_, typ)) = def_fields.iter().find(|&(sym, _)| *sym == field) else { - return Err(UnknownStructField { - sym: sym.to_string(), - }); - }; - - TExpr::AccessField { - strct: Box::new(strct), - field, - typ: typ.clone(), - } - } - Expr::Variant { .. } => todo!(), - Expr::Switch { .. } => todo!(), - }) +) -> Result>, TypeError> { + match expr.inner { + Expr::Lit { val } => validate_lit(val, expr.span), + Expr::Var { sym } => validate_var(sym, expr.span, env), + Expr::Prim { op, args } => validate_prim(op, args, expr.span, env), + Expr::Let { sym, mutable, bnd, bdy, } => validate_let(sym, mutable, *bnd, *bdy, expr.span, env), + Expr::If { cnd, thn, els } => validate_if(*cnd, *thn, *els, expr.span, env), + Expr::Apply { fun, args } => validate_apply(*fun, args, expr.span, env), + Expr::Loop { bdy } => validate_loop(*bdy, expr.span, env), + Expr::Break { bdy } => validate_break(*bdy, expr.span, env), + Expr::Continue => validate_continue(expr.span), + Expr::Return { bdy } => validate_return(*bdy, expr.span, env), + Expr::Seq { stmt, cnt } => validate_seq(*stmt, *cnt, expr.span, env), + Expr::Assign { sym, bnd } => validate_assign(sym, *bnd, expr.span, env), + Expr::Struct { sym, fields } => validate_struct(sym, fields, expr.span, env), + Expr::Variant { enum_sym, variant_sym, bdy } => validate_variant(enum_sym, variant_sym, *bdy, expr.span), + Expr::AccessField { strct, field } => validate_access_field(*strct, field, expr.span, env), + Expr::Switch { enm, arms } => validate_switch(*enm, arms, expr.span), + } } diff --git a/compiler/src/passes/validate/type_check/validate_if.rs b/compiler/src/passes/validate/type_check/validate_if.rs new file mode 100644 index 0000000..2447f52 --- /dev/null +++ b/compiler/src/passes/validate/type_check/validate_if.rs @@ -0,0 +1,31 @@ +use crate::passes::parse::types::Type; +use crate::passes::parse::{Expr, Spanned}; +use crate::passes::validate::type_check::error::TypeError; +use crate::passes::validate::type_check::validate_expr::validate_expr; +use crate::passes::validate::type_check::{expect_type, expect_type_eq, Env}; +use crate::passes::validate::TExpr; + +pub fn validate_if<'p>( + cnd: Spanned>, + thn: Spanned>, + els: Spanned>, + span: (usize, usize), + env: &mut Env<'_, 'p>, +) -> Result>, TypeError> { + let cnd = validate_expr(cnd, env)?; + let thn = validate_expr(thn, env)?; + let els = validate_expr(els, env)?; + + expect_type(&cnd, &Type::Bool)?; + expect_type_eq(&thn, &els)?; + + Ok(Spanned { + span, + inner: TExpr::If { + typ: thn.inner.typ().clone(), + cnd: Box::new(cnd.inner), + thn: Box::new(thn.inner), + els: Box::new(els.inner), + }, + }) +} diff --git a/compiler/src/passes/validate/type_check/validate_let.rs b/compiler/src/passes/validate/type_check/validate_let.rs new file mode 100644 index 0000000..2d92e61 --- /dev/null +++ b/compiler/src/passes/validate/type_check/validate_let.rs @@ -0,0 +1,34 @@ +use crate::passes::parse::{Expr, Spanned}; +use crate::passes::validate::type_check::error::TypeError; +use crate::passes::validate::type_check::validate_expr::validate_expr; +use crate::passes::validate::type_check::{Env, EnvEntry}; +use crate::passes::validate::TExpr; + +pub fn validate_let<'p>( + sym: &'p str, + mutable: bool, + bnd: Spanned>, + bdy: Spanned>, + span: (usize, usize), + env: &mut Env<'_, 'p>, +) -> Result>, TypeError> { + let bnd = validate_expr(bnd, env)?; + let bdy = env.push( + sym, + EnvEntry::Type { + mutable, + typ: bnd.inner.typ().clone(), + }, + |env| validate_expr(bdy, env), + )?; + + Ok(Spanned { + span, + inner: TExpr::Let { + typ: bdy.inner.typ().clone(), + sym, + bnd: Box::new(bnd.inner), + bdy: Box::new(bdy.inner), + }, + }) +} diff --git a/compiler/src/passes/validate/type_check/validate_lit.rs b/compiler/src/passes/validate/type_check/validate_lit.rs new file mode 100644 index 0000000..a670a8b --- /dev/null +++ b/compiler/src/passes/validate/type_check/validate_lit.rs @@ -0,0 +1,34 @@ +use crate::passes::parse::types::Type; +use crate::passes::parse::{Lit, Spanned}; +use crate::passes::validate::type_check::error::TypeError; +use crate::passes::validate::type_check::error::TypeError::*; +use crate::passes::validate::type_check::s; +use crate::passes::validate::{TExpr, TLit}; + +pub fn validate_lit<'p>( + val: Lit, + span: (usize, usize), +) -> Result>, TypeError> { + let inner = match val { + Lit::Int { val } => { + let val = val + .parse() + .map_err(|_| IntegerOutOfBounds { span: s(span) })?; + + TExpr::Lit { + val: TLit::Int { val }, + typ: Type::Int, + } + } + Lit::Bool { val } => TExpr::Lit { + val: TLit::Bool { val }, + typ: Type::Bool, + }, + Lit::Unit => TExpr::Lit { + val: TLit::Unit, + typ: Type::Unit, + }, + }; + + Ok(Spanned { span, inner }) +} diff --git a/compiler/src/passes/validate/type_check/validate_loop.rs b/compiler/src/passes/validate/type_check/validate_loop.rs new file mode 100644 index 0000000..8b7878c --- /dev/null +++ b/compiler/src/passes/validate/type_check/validate_loop.rs @@ -0,0 +1,29 @@ +use crate::passes::parse::types::Type; +use crate::passes::parse::{Expr, Spanned}; +use crate::passes::validate::type_check::error::TypeError; +use crate::passes::validate::type_check::validate_expr::validate_expr; +use crate::passes::validate::type_check::Env; +use crate::passes::validate::TExpr; + +pub fn validate_loop<'p>( + bdy: Spanned>, + span: (usize, usize), + env: &mut Env<'_, 'p>, +) -> Result>, TypeError> { + let mut loop_type = None; + let mut env = Env { + scope: env.scope, + loop_type: &mut loop_type, + in_loop: true, + return_type: env.return_type, + }; + let bdy = validate_expr(bdy, &mut env)?; + + Ok(Spanned { + span, + inner: TExpr::Loop { + bdy: Box::new(bdy.inner), + typ: loop_type.unwrap_or(Type::Never), + }, + }) +} diff --git a/compiler/src/passes/validate/type_check/validate_prim.rs b/compiler/src/passes/validate/type_check/validate_prim.rs index eeefe6a..ca2f3aa 100644 --- a/compiler/src/passes/validate/type_check/validate_prim.rs +++ b/compiler/src/passes/validate/type_check/validate_prim.rs @@ -1,15 +1,17 @@ use crate::passes::parse::types::Type; -use crate::passes::parse::{Expr, Op}; +use crate::passes::parse::{Expr, Op, Spanned}; use crate::passes::validate::type_check::error::TypeError; use crate::passes::validate::type_check::validate_expr::validate_expr; use crate::passes::validate::type_check::{expect_type, expect_type_eq, Env}; use crate::passes::validate::TExpr; +use functor_derive::Functor; pub fn validate_prim<'p>( - env: &mut Env<'_, 'p>, op: Op, - args: Vec>, -) -> Result, TypeError> { + args: Vec>>, + span: (usize, usize), + env: &mut Env<'_, 'p>, +) -> Result>, TypeError> { let args = args .into_iter() .map(|arg| validate_expr(arg, env)) @@ -52,5 +54,12 @@ pub fn validate_prim<'p>( _ => panic!("Found incorrect operator during type checking"), }; - Ok(TExpr::Prim { op, args, typ }) + Ok(Spanned { + span, + inner: TExpr::Prim { + op, + args: args.fmap(|arg| arg.inner), + typ, + }, + }) } diff --git a/compiler/src/passes/validate/type_check/validate_return.rs b/compiler/src/passes/validate/type_check/validate_return.rs new file mode 100644 index 0000000..1f29f99 --- /dev/null +++ b/compiler/src/passes/validate/type_check/validate_return.rs @@ -0,0 +1,24 @@ +use crate::passes::parse::types::Type; +use crate::passes::parse::{Expr, Spanned}; +use crate::passes::validate::type_check::error::TypeError; +use crate::passes::validate::type_check::validate_expr::validate_expr; +use crate::passes::validate::type_check::{expect_type, Env}; +use crate::passes::validate::TExpr; + +pub fn validate_return<'p>( + bdy: Spanned>, + span: (usize, usize), + env: &mut Env<'_, 'p>, +) -> Result>, TypeError> { + let bdy = validate_expr(bdy, env)?; + + expect_type(&bdy, env.return_type)?; + + Ok(Spanned { + span, + inner: TExpr::Return { + bdy: Box::new(bdy.inner), + typ: Type::Never, + }, + }) +} diff --git a/compiler/src/passes/validate/type_check/validate_seq.rs b/compiler/src/passes/validate/type_check/validate_seq.rs new file mode 100644 index 0000000..9f4f51c --- /dev/null +++ b/compiler/src/passes/validate/type_check/validate_seq.rs @@ -0,0 +1,24 @@ +use crate::passes::parse::{Expr, Spanned}; +use crate::passes::validate::type_check::error::TypeError; +use crate::passes::validate::type_check::validate_expr::validate_expr; +use crate::passes::validate::type_check::Env; +use crate::passes::validate::TExpr; + +pub fn validate_seq<'p>( + stmt: Spanned>, + cnt: Spanned>, + span: (usize, usize), + env: &mut Env<'_, 'p>, +) -> Result>, TypeError> { + let stmt = validate_expr(stmt, env)?; + let cnt = validate_expr(cnt, env)?; + + Ok(Spanned { + span, + inner: TExpr::Seq { + typ: cnt.inner.typ().clone(), + stmt: Box::new(stmt.inner), + cnt: Box::new(cnt.inner), + }, + }) +} diff --git a/compiler/src/passes/validate/type_check/validate_struct.rs b/compiler/src/passes/validate/type_check/validate_struct.rs index 6c927c5..bba7c44 100644 --- a/compiler/src/passes/validate/type_check/validate_struct.rs +++ b/compiler/src/passes/validate/type_check/validate_struct.rs @@ -1,26 +1,34 @@ use crate::passes::parse::types::Type; -use crate::passes::parse::{Expr, TypeDef}; +use crate::passes::parse::{Expr, Spanned, TypeDef}; use crate::passes::validate::type_check::error::TypeError; use crate::passes::validate::type_check::error::TypeError::*; -use crate::passes::validate::type_check::expect_type; use crate::passes::validate::type_check::validate_expr::validate_expr; -use crate::passes::validate::type_check::{Env, EnvEntry}; +use crate::passes::validate::type_check::{expect_type, Env, EnvEntry}; use crate::passes::validate::TExpr; use crate::utils::expect::expect; +use functor_derive::Functor; use std::collections::{HashMap, HashSet}; pub fn validate_struct<'p>( - env: &mut Env<'_, 'p>, sym: &'p str, - fields: Vec<(&'p str, Expr<'p, &'p str>)>, -) -> Result, TypeError> { + fields: Vec<(&'p str, Spanned>)>, + span: (usize, usize), + env: &mut Env<'_, 'p>, +) -> Result>, TypeError> { let entry = env.scope.get(&sym).ok_or(UndeclaredVar { sym: sym.to_string(), + span: (0, 0), // todo: not correct })?; - #[rustfmt::skip] - let EnvEntry::Def { def: TypeDef::Struct { fields: def_fields, .. } } = &entry else { - return Err(VariableShouldBeStruct { sym: sym.to_string() }); + let EnvEntry::Def { + def: TypeDef::Struct { + fields: def_fields, .. + }, + } = &entry + else { + return Err(VariableShouldBeStruct { + sym: sym.to_string(), + }); }; let mut new_provided_fields = HashSet::new(); @@ -62,9 +70,12 @@ pub fn validate_struct<'p>( )?; } - Ok(TExpr::Struct { - sym, - fields, - typ: Type::Var { sym }, + Ok(Spanned { + span, + inner: TExpr::Struct { + sym, + fields: fields.fmap(|(sym, field)| (sym, field.inner)), + typ: Type::Var { sym }, + }, }) } diff --git a/compiler/src/passes/validate/type_check/validate_switch.rs b/compiler/src/passes/validate/type_check/validate_switch.rs new file mode 100644 index 0000000..3ac0af0 --- /dev/null +++ b/compiler/src/passes/validate/type_check/validate_switch.rs @@ -0,0 +1,11 @@ +use crate::passes::parse::{Expr, Spanned}; +use crate::passes::validate::type_check::error::TypeError; +use crate::passes::validate::TExpr; + +pub fn validate_switch<'p>( + _enm: Spanned, + _arms: Vec<(&str, &str, Box>)>, + _span: (usize, usize), +) -> Result>, TypeError> { + todo!() +} diff --git a/compiler/src/passes/validate/type_check/validate_type.rs b/compiler/src/passes/validate/type_check/validate_type.rs index 7e16d5d..1d9c987 100644 --- a/compiler/src/passes/validate/type_check/validate_type.rs +++ b/compiler/src/passes/validate/type_check/validate_type.rs @@ -24,6 +24,7 @@ pub fn validate_type<'p>( scope.contains(sym), UndeclaredVar { sym: sym.to_string(), + span: (0, 0), // todo: not correct }, )?; } diff --git a/compiler/src/passes/validate/type_check/validate_var.rs b/compiler/src/passes/validate/type_check/validate_var.rs new file mode 100644 index 0000000..4955ecc --- /dev/null +++ b/compiler/src/passes/validate/type_check/validate_var.rs @@ -0,0 +1,30 @@ +use crate::passes::parse::Spanned; +use crate::passes::validate::type_check::error::TypeError; +use crate::passes::validate::type_check::error::TypeError::*; +use crate::passes::validate::type_check::{s, Env, EnvEntry}; +use crate::passes::validate::TExpr; + +pub fn validate_var<'p>( + sym: &'p str, + span: (usize, usize), + env: &Env<'_, 'p>, +) -> Result>, TypeError> { + let entry = env.scope.get(&sym).ok_or(UndeclaredVar { + sym: (*sym).to_string(), + span: s(span), + })?; + + let EnvEntry::Type { typ, .. } = entry else { + return Err(VariableShouldBeExpr { + sym: (*sym).to_string(), + }); + }; + + Ok(Spanned { + span, + inner: TExpr::Var { + sym, + typ: typ.clone(), + }, + }) +} diff --git a/compiler/src/passes/validate/type_check/validate_variant.rs b/compiler/src/passes/validate/type_check/validate_variant.rs new file mode 100644 index 0000000..aa3eff5 --- /dev/null +++ b/compiler/src/passes/validate/type_check/validate_variant.rs @@ -0,0 +1,12 @@ +use crate::passes::parse::{Expr, Spanned}; +use crate::passes::validate::type_check::error::TypeError; +use crate::passes::validate::TExpr; + +pub fn validate_variant<'p>( + _enum_sym: &str, + _variant_sym: &str, + _bdy: Spanned, + _span: (usize, usize), +) -> Result>, TypeError> { + todo!() +} diff --git a/compiler/src/utils/split_test.rs b/compiler/src/utils/split_test.rs index a0af81a..582d6af 100644 --- a/compiler/src/utils/split_test.rs +++ b/compiler/src/utils/split_test.rs @@ -1,6 +1,7 @@ use crate::interpreter::Val; use crate::passes::parse::parse::parse_program; use crate::passes::parse::{Lit, PrgParsed}; +use crate::passes::validate::TLit; use std::cell::OnceCell; use std::hash::Hash; use std::str::SplitWhitespace; @@ -8,7 +9,7 @@ use std::str::SplitWhitespace; /// Splits the inputs, expected outputs and expected return from the test. /// The values must be preceded by `//*` and `inp:`, `out:` or `ret:`. #[must_use] -pub fn split_test(test: &str) -> (Vec, Vec, Lit, PrgParsed) { +pub fn split_test(test: &str) -> (Vec, Vec, TLit, PrgParsed) { let mut input = OnceCell::new(); let mut output = OnceCell::new(); let mut expected_return = OnceCell::new(); @@ -33,7 +34,7 @@ pub fn split_test(test: &str) -> (Vec, Vec, Lit, PrgParsed) { ( input.take().unwrap_or_default(), output.take().unwrap_or_default(), - expected_return.take().unwrap_or(Lit::Unit), - parse_program(test).unwrap(), + expected_return.take().unwrap_or(TLit::Unit), + parse_program(test).unwrap(), // todo: pass test file name ) } diff --git a/compiler/tests/integration.rs b/compiler/tests/integration.rs index 6585070..b833daf 100644 --- a/compiler/tests/integration.rs +++ b/compiler/tests/integration.rs @@ -1,6 +1,6 @@ #![cfg(unix)] -use compiler::passes::parse::Lit; +use compiler::passes::validate::TLit; use compiler::utils::split_test::split_test; use std::fs::OpenOptions; use std::io::{BufRead, Write}; @@ -73,7 +73,7 @@ fn integration([test]: [&str; 1]) { ); for (got, expected) in out.stdout.lines().map(|r| r.unwrap()).zip(expected_output) { - assert_eq!(got.parse::().unwrap(), expected); + assert_eq!(got.parse::().unwrap(), expected); } } diff --git a/integer_oob b/integer_oob new file mode 100644 index 0000000..e69de29 diff --git a/invalid_token b/invalid_token new file mode 100644 index 0000000..e69de29 diff --git a/programs/fail/parse/invalid_token.test b/programs/fail/parse/invalid_token.test new file mode 100644 index 0000000..3420e70 --- /dev/null +++ b/programs/fail/parse/invalid_token.test @@ -0,0 +1,3 @@ +fn main() -> Int { + @ +} diff --git a/programs/fail/parse/min_arity.test b/programs/fail/parse/min_arity.test index 16d8da8..14a2125 100644 --- a/programs/fail/parse/min_arity.test +++ b/programs/fail/parse/min_arity.test @@ -1,3 +1,5 @@ +//* err: ParseToken fn main() -> Int { - +// ^ } diff --git a/programs/fail/parse/unexpected_eof.test b/programs/fail/parse/unexpected_eof.test new file mode 100644 index 0000000..4a3ed75 --- /dev/null +++ b/programs/fail/parse/unexpected_eof.test @@ -0,0 +1 @@ +fn main() -> Int { diff --git a/programs/fail/parse/unexpected_token.rs b/programs/fail/parse/unexpected_token.rs new file mode 100644 index 0000000..16d8da8 --- /dev/null +++ b/programs/fail/parse/unexpected_token.rs @@ -0,0 +1,3 @@ +fn main() -> Int { + - +} diff --git a/programs/fail/type_check/eq_unmatched.test b/programs/fail/type_check/eq_unmatched.test index b4eec4b..b0db94a 100644 --- a/programs/fail/type_check/eq_unmatched.test +++ b/programs/fail/type_check/eq_unmatched.test @@ -1,3 +1,3 @@ fn main() -> Int { - t == 1 + true == 1 } diff --git a/programs/fail/type_check/integer_oob.test b/programs/fail/type_check/integer_oob.test new file mode 100644 index 0000000..cce1c1d --- /dev/null +++ b/programs/fail/type_check/integer_oob.test @@ -0,0 +1,3 @@ +fn main() -> Int { + 2147483648 +} diff --git a/programs/fail/type_check/let_wrong_type.test b/programs/fail/type_check/let_wrong_type.test new file mode 100644 index 0000000..508b6e0 --- /dev/null +++ b/programs/fail/type_check/let_wrong_type.test @@ -0,0 +1,4 @@ +fn main() { + let x = 2; + x +} diff --git a/programs/fail/type_check/undeclared_var.test b/programs/fail/type_check/undeclared_var.test new file mode 100644 index 0000000..9fec7b4 --- /dev/null +++ b/programs/fail/type_check/undeclared_var.test @@ -0,0 +1,3 @@ +fn main() { + x +} diff --git a/programs/fail/type_check/undeclared_var_assign.test b/programs/fail/type_check/undeclared_var_assign.test new file mode 100644 index 0000000..75e0c32 --- /dev/null +++ b/programs/fail/type_check/undeclared_var_assign.test @@ -0,0 +1,3 @@ +fn main() { + x = 2 +} diff --git a/programs/good/booleans/and_nested.test b/programs/good/booleans/and_nested.test new file mode 100644 index 0000000..34bd0d8 --- /dev/null +++ b/programs/good/booleans/and_nested.test @@ -0,0 +1,4 @@ +//* ret: false +fn main() -> Bool { + (true && false) && (false && true) +} diff --git a/programs/good/booleans/bool_in_let1.test b/programs/good/booleans/bool_in_let1.test new file mode 100644 index 0000000..a740804 --- /dev/null +++ b/programs/good/booleans/bool_in_let1.test @@ -0,0 +1,5 @@ +//* ret: true +fn main() -> Bool { + let x = !(true && false); + x +} diff --git a/programs/good/booleans/bool_in_let2.test b/programs/good/booleans/bool_in_let2.test new file mode 100644 index 0000000..ae75f50 --- /dev/null +++ b/programs/good/booleans/bool_in_let2.test @@ -0,0 +1,5 @@ +//* ret: true +fn main() -> Bool { + let x = ! (let y = true; !y); + x +} diff --git a/programs/good/booleans/if_false.test b/programs/good/booleans/if_false.test new file mode 100644 index 0000000..63a7d7a --- /dev/null +++ b/programs/good/booleans/if_false.test @@ -0,0 +1,4 @@ +//* ret: 2 +fn main() -> Int { + if false { 1} else {2} +} \ No newline at end of file diff --git a/programs/good/booleans/if_gt.test b/programs/good/booleans/if_gt.test new file mode 100644 index 0000000..090bdf4 --- /dev/null +++ b/programs/good/booleans/if_gt.test @@ -0,0 +1,4 @@ +//* ret: 1 +fn main() -> Int { + if 5 > 3 {1} else {2} +} \ No newline at end of file diff --git a/programs/good/booleans/if_lazy1.test b/programs/good/booleans/if_lazy1.test new file mode 100644 index 0000000..ad6d452 --- /dev/null +++ b/programs/good/booleans/if_lazy1.test @@ -0,0 +1,5 @@ +//* ret: 1 +//* out: 1 +fn main() -> Int { + if 5 > 3 { print(1) } else { print(2) } +} \ No newline at end of file diff --git a/programs/good/booleans/if_lazy2.test b/programs/good/booleans/if_lazy2.test new file mode 100644 index 0000000..f23594c --- /dev/null +++ b/programs/good/booleans/if_lazy2.test @@ -0,0 +1,5 @@ +//* ret: 2 +//* out: 2 +fn main() -> Int { + if 5 < 3 { print(1) } else { print(2) } +} \ No newline at end of file diff --git a/programs/good/booleans/if_lt.test b/programs/good/booleans/if_lt.test new file mode 100644 index 0000000..700702c --- /dev/null +++ b/programs/good/booleans/if_lt.test @@ -0,0 +1,4 @@ +//* ret: 2 +fn main() -> Int { + if 5 < 3 { 1 } else { 2} +} \ No newline at end of file diff --git a/programs/good/booleans/if_nested.test b/programs/good/booleans/if_nested.test new file mode 100644 index 0000000..2e8c0d7 --- /dev/null +++ b/programs/good/booleans/if_nested.test @@ -0,0 +1,4 @@ +//* ret: 2 +fn main() -> Int { + (if 5 > 3 { 1 } else { 2 }) + 1 +} diff --git a/programs/good/booleans/if_nested1.test b/programs/good/booleans/if_nested1.test new file mode 100644 index 0000000..91b685f --- /dev/null +++ b/programs/good/booleans/if_nested1.test @@ -0,0 +1,4 @@ +//* ret: 1 +fn main() -> Int { + if (if true { true } else {false}) {1 } else {2} +} \ No newline at end of file diff --git a/programs/good/booleans/if_nested2.test b/programs/good/booleans/if_nested2.test new file mode 100644 index 0000000..19d0794 --- /dev/null +++ b/programs/good/booleans/if_nested2.test @@ -0,0 +1,4 @@ +//* ret: 2 +fn main() -> Int { + if (if false { true } else { false }) { 1 } else { 2 } +} \ No newline at end of file diff --git a/programs/good/booleans/if_nested3.test b/programs/good/booleans/if_nested3.test new file mode 100644 index 0000000..bc4c682 --- /dev/null +++ b/programs/good/booleans/if_nested3.test @@ -0,0 +1,4 @@ +//* ret: 1 +fn main() -> Int { + if (if 5 > 3 { true } else { false}) {1} else {2} +} \ No newline at end of file diff --git a/programs/good/booleans/if_nested4.test b/programs/good/booleans/if_nested4.test new file mode 100644 index 0000000..b7d4cd3 --- /dev/null +++ b/programs/good/booleans/if_nested4.test @@ -0,0 +1,4 @@ +//* ret: 2 +fn main() -> Int { + if (if 5 < 3 { true} else {false}) {1} else {2} +} \ No newline at end of file diff --git a/programs/good/booleans/if_not_eq.test b/programs/good/booleans/if_not_eq.test new file mode 100644 index 0000000..b5e88ac --- /dev/null +++ b/programs/good/booleans/if_not_eq.test @@ -0,0 +1,4 @@ +//* ret: 2 +fn main() -> Int { + if !(5 >= 3) {1} else {2} +} \ No newline at end of file diff --git a/programs/good/booleans/if_not_false.test b/programs/good/booleans/if_not_false.test new file mode 100644 index 0000000..0573eb9 --- /dev/null +++ b/programs/good/booleans/if_not_false.test @@ -0,0 +1,4 @@ +//* ret: 1 +fn main() -> Int { + if !false {1} else {2} +} \ No newline at end of file diff --git a/programs/good/booleans/if_not_lte.test b/programs/good/booleans/if_not_lte.test new file mode 100644 index 0000000..2b1eaa5 --- /dev/null +++ b/programs/good/booleans/if_not_lte.test @@ -0,0 +1,4 @@ +//* ret: 1 +fn main() -> Int { + if !(5 <= 3) {1} else {2} +} diff --git a/programs/good/booleans/if_not_true.test b/programs/good/booleans/if_not_true.test new file mode 100644 index 0000000..2031f68 --- /dev/null +++ b/programs/good/booleans/if_not_true.test @@ -0,0 +1,4 @@ +//* ret: 2 +fn main() -> Int { + if !true {1} else {2} +} diff --git a/programs/good/booleans/if_true.test b/programs/good/booleans/if_true.test new file mode 100644 index 0000000..4daa8ad --- /dev/null +++ b/programs/good/booleans/if_true.test @@ -0,0 +1,4 @@ +//* ret: 1 +fn main() -> Int { + if true {1} else {2} +} diff --git a/programs/good/booleans/if_with_let_inside.test b/programs/good/booleans/if_with_let_inside.test new file mode 100644 index 0000000..3a99d45 --- /dev/null +++ b/programs/good/booleans/if_with_let_inside.test @@ -0,0 +1,4 @@ +//* ret: 2 +fn main() -> Int { + if (let x = 5; x > 5) {1} else {2} +} diff --git a/programs/good/booleans/if_with_let_outside_false.test b/programs/good/booleans/if_with_let_outside_false.test new file mode 100644 index 0000000..bee4991 --- /dev/null +++ b/programs/good/booleans/if_with_let_outside_false.test @@ -0,0 +1,5 @@ +//* ret: 1 +fn main() -> Int { + let x = true; + if x { 1 } else {2} +} diff --git a/programs/good/booleans/if_with_let_outside_true.test b/programs/good/booleans/if_with_let_outside_true.test new file mode 100644 index 0000000..58d4a64 --- /dev/null +++ b/programs/good/booleans/if_with_let_outside_true.test @@ -0,0 +1,5 @@ +//* ret: 2 +fn main() -> Int { + let x = false; + if x { 1 } else { 2 } +} diff --git a/programs/good/booleans/or_eq.test b/programs/good/booleans/or_eq.test new file mode 100644 index 0000000..f35960d --- /dev/null +++ b/programs/good/booleans/or_eq.test @@ -0,0 +1,4 @@ +//* ret: true +fn main() -> Bool { + false == true || false == false +} diff --git a/programs/good/booleans/or_gt.test b/programs/good/booleans/or_gt.test new file mode 100644 index 0000000..4dda657 --- /dev/null +++ b/programs/good/booleans/or_gt.test @@ -0,0 +1,4 @@ +//* ret: true +fn main() -> Bool { + 5 > 3 || 3 > 5 +} diff --git a/programs/good/booleans/or_nested.test b/programs/good/booleans/or_nested.test new file mode 100644 index 0000000..2cbd69e --- /dev/null +++ b/programs/good/booleans/or_nested.test @@ -0,0 +1,4 @@ +//* ret: true +fn main() -> Bool { + ( true && true) || false +} diff --git a/programs/good/integration/mixed1.test b/programs/good/integration/mixed1.test new file mode 100644 index 0000000..6fc94c6 --- /dev/null +++ b/programs/good/integration/mixed1.test @@ -0,0 +1,7 @@ +//* inp: 45 3 +//* ret: 42 +fn main() -> Int { + let x = read(); + let y = read(); + x +- y +} diff --git a/programs/good/integration/mixed10.test b/programs/good/integration/mixed10.test new file mode 100644 index 0000000..023b935 --- /dev/null +++ b/programs/good/integration/mixed10.test @@ -0,0 +1,7 @@ +//* inp: 40 2 +//* ret: 42 +fn main() -> Int { + let b = read(); + let c = read(); + b + c +} diff --git a/programs/good/integration/mixed11.test b/programs/good/integration/mixed11.test new file mode 100644 index 0000000..74d4d7c --- /dev/null +++ b/programs/good/integration/mixed11.test @@ -0,0 +1,11 @@ +//* inp: 1 1 2 2 3 3 +//* ret: 42 +fn main() -> Int { + let x1 = read(); + let x2 = read(); + let x3 = read(); + let x4 = read(); + let x5 = read(); + let x6 = read(); + (x1 - x2) + (x3 - x4) + (x5 - x6) + 42 +} diff --git a/programs/good/integration/mixed2.test b/programs/good/integration/mixed2.test new file mode 100644 index 0000000..0cdb31c --- /dev/null +++ b/programs/good/integration/mixed2.test @@ -0,0 +1,7 @@ +//* inp: 0 0 +//* ret: 42 +fn main() -> Int { + let x = read(); + let y = read(); + x + y + 42 +} diff --git a/programs/good/integration/mixed3.test b/programs/good/integration/mixed3.test new file mode 100644 index 0000000..09fe788 --- /dev/null +++ b/programs/good/integration/mixed3.test @@ -0,0 +1,7 @@ +//* ret: 42 +fn main() -> Int { + let x = 30; + let z = x + 14; + let y = 2; + z + -y +} diff --git a/programs/good/integration/mixed4.test b/programs/good/integration/mixed4.test new file mode 100644 index 0000000..e94f1ae --- /dev/null +++ b/programs/good/integration/mixed4.test @@ -0,0 +1,9 @@ +//* ret: 42 +fn main() -> Int { + let v = 1; + let w = 46; + let x = v + 7; + let y = 4 + x; + let z = x + w; + z + -y +} diff --git a/programs/good/integration/mixed5.test b/programs/good/integration/mixed5.test new file mode 100644 index 0000000..08fd60a --- /dev/null +++ b/programs/good/integration/mixed5.test @@ -0,0 +1,9 @@ +//* ret: 42 +fn main() -> Int { + let a = 1; + let b = 42; + let g = b; + let e = a + b; + let d = g; + d +} diff --git a/programs/good/integration/mixed6.test b/programs/good/integration/mixed6.test new file mode 100644 index 0000000..fefcd66 --- /dev/null +++ b/programs/good/integration/mixed6.test @@ -0,0 +1,8 @@ +//* inp: 4 7 +//* ret: 33 +fn main() -> Int { + let x = 30; + let y = read(); + let z = read(); + x + (z - y) +} diff --git a/programs/good/integration/mixed8.test b/programs/good/integration/mixed8.test new file mode 100644 index 0000000..b2aa4ab --- /dev/null +++ b/programs/good/integration/mixed8.test @@ -0,0 +1,9 @@ +//* inp: 82 +//* ret: 82 +fn main() -> Int { + let a = read(); + let b = a; + let c = b; + let d = c; + d +} diff --git a/programs/good/integration/mixed9.test b/programs/good/integration/mixed9.test new file mode 100644 index 0000000..0b839fa --- /dev/null +++ b/programs/good/integration/mixed9.test @@ -0,0 +1,7 @@ +//* inp: 40 5 7 +//* ret: 42 +fn main() -> Int { + let x = read(); + let y = read(); + x + (let z = read(); z + -y) +} diff --git a/programs/good/lvar/arith1.test b/programs/good/lvar/arith1.test new file mode 100644 index 0000000..a017981 --- /dev/null +++ b/programs/good/lvar/arith1.test @@ -0,0 +1,4 @@ +//* ret: 22 +fn main() -> Int { + (32 + (-10)) +} diff --git a/programs/good/lvar/arith2.test b/programs/good/lvar/arith2.test new file mode 100644 index 0000000..58018d6 --- /dev/null +++ b/programs/good/lvar/arith2.test @@ -0,0 +1,4 @@ +//* ret: 42 +fn main() -> Int { + ((10 + 11) + (25 + -4)) +} diff --git a/programs/good/lvar/arith3.test b/programs/good/lvar/arith3.test new file mode 100644 index 0000000..74e036c --- /dev/null +++ b/programs/good/lvar/arith3.test @@ -0,0 +1,4 @@ +//* ret: 42 +fn main() -> Int { + 30 + 10 + 2 +} diff --git a/programs/good/lvar/arith4.test b/programs/good/lvar/arith4.test new file mode 100644 index 0000000..a44c00d --- /dev/null +++ b/programs/good/lvar/arith4.test @@ -0,0 +1,4 @@ +//* ret: 42 +fn main() -> Int { + 52 + -(3 + 7) +} diff --git a/programs/good/lvar/arith5.test b/programs/good/lvar/arith5.test new file mode 100644 index 0000000..396b8c7 --- /dev/null +++ b/programs/good/lvar/arith5.test @@ -0,0 +1,4 @@ +//* ret: 42 +fn main() -> Int { + 10 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 +} diff --git a/programs/good/lvar/arith6.test b/programs/good/lvar/arith6.test new file mode 100644 index 0000000..d8e23e0 --- /dev/null +++ b/programs/good/lvar/arith6.test @@ -0,0 +1,4 @@ +//* ret: 42 +fn main() -> Int { + -(-42) +} diff --git a/programs/good/lvar/arith7.test b/programs/good/lvar/arith7.test new file mode 100644 index 0000000..4c7224c --- /dev/null +++ b/programs/good/lvar/arith7.test @@ -0,0 +1,4 @@ +//* ret: -40 +fn main() -> Int { + (-10 - 20) + (20 - 30) +} diff --git a/programs/good/lvar/io2.test b/programs/good/lvar/io2.test new file mode 100644 index 0000000..7b12221 --- /dev/null +++ b/programs/good/lvar/io2.test @@ -0,0 +1,6 @@ +//* inp: 12 13 +//* out: -1 +//* ret: -1 +fn main() -> Int { + print(read() - read()) +} diff --git a/programs/good/lvar/io3.test b/programs/good/lvar/io3.test new file mode 100644 index 0000000..d2bad4c --- /dev/null +++ b/programs/good/lvar/io3.test @@ -0,0 +1,6 @@ +//* inp: 12 13 +//* out: 12 13 +//* ret: 25 +fn main() -> Int { + print(read()) + print(read()) +} diff --git a/programs/good/lvar/let2.test b/programs/good/lvar/let2.test new file mode 100644 index 0000000..b277c44 --- /dev/null +++ b/programs/good/lvar/let2.test @@ -0,0 +1,7 @@ +//* inp: 3 5 +//* ret: -2 +fn main() -> Int { + let x = read(); + let y = read(); + x - y +} \ No newline at end of file diff --git a/programs/good/lvar/let3.test b/programs/good/lvar/let3.test new file mode 100644 index 0000000..a88f38c --- /dev/null +++ b/programs/good/lvar/let3.test @@ -0,0 +1,8 @@ +//* inp: 3 5 11 +//* ret: -13 +fn main() -> Int { + let x = read(); + let y = read(); + let z = read(); + x - y - z +} \ No newline at end of file diff --git a/programs/good/lvar/let4.test b/programs/good/lvar/let4.test new file mode 100644 index 0000000..e8ea154 --- /dev/null +++ b/programs/good/lvar/let4.test @@ -0,0 +1,6 @@ +//* inp: 8 +//* ret: 8 +fn main() -> Int { + let x = (let y = read(); y); + x +} diff --git a/programs/good/lvar/let5.test b/programs/good/lvar/let5.test new file mode 100644 index 0000000..4625f25 --- /dev/null +++ b/programs/good/lvar/let5.test @@ -0,0 +1,6 @@ +//* inp: 8 5 +//* ret: 3 +fn main() -> Int { + let x = (let y = read(); let z = read(); y - z); + x +} diff --git a/programs/good/lvar/let6.test b/programs/good/lvar/let6.test new file mode 100644 index 0000000..69e78a7 --- /dev/null +++ b/programs/good/lvar/let6.test @@ -0,0 +1,7 @@ +//* inp: 7 9 211 +//* ret: 227 +fn main() -> Int { + let x = (let y = read(); let z = read(); y + z); + let w = read(); + x + w +} diff --git a/programs/good/lvar/let7.test b/programs/good/lvar/let7.test new file mode 100644 index 0000000..9087ff2 --- /dev/null +++ b/programs/good/lvar/let7.test @@ -0,0 +1,5 @@ +//* inp: 7 9 +//* ret: -2 +fn main() -> Int { + (let x = read(); x) - (let y = read(); y) +} diff --git a/programs/good/lvar/let8.test b/programs/good/lvar/let8.test new file mode 100644 index 0000000..26ef0e9 --- /dev/null +++ b/programs/good/lvar/let8.test @@ -0,0 +1,6 @@ +//* inp: 7 9 +//* ret: -30 +fn main() -> Int { + let y = (read() - read()); + (let z = y; z - 8) - (18 - y) +} diff --git a/programs/good/lvar/let9.test b/programs/good/lvar/let9.test new file mode 100644 index 0000000..852fd28 --- /dev/null +++ b/programs/good/lvar/let9.test @@ -0,0 +1,6 @@ +//* inp: 7 9 +//* ret: 16 +fn main() -> Int { + let x = read(); + x + (let x = read(); x) +} diff --git a/unexpected_eof b/unexpected_eof new file mode 100644 index 0000000..e69de29