From a345ffcc69a13cbaea450618d830106a00219abb Mon Sep 17 00:00:00 2001 From: Kisaragi Marine Date: Sat, 30 Nov 2024 23:58:24 +0900 Subject: [PATCH] refactor: rustfmt --- package/origlang-ast/src/after_parse.rs | 12 +- package/origlang-ast/src/lib.rs | 10 +- package/origlang-cli/src/args.rs | 54 +- package/origlang-cli/src/error.rs | 8 +- package/origlang-cli/src/main.rs | 6 +- package/origlang-cli/src/task.rs | 2 +- package/origlang-cli/src/task/emit.rs | 26 +- package/origlang-cli/src/task/interpret.rs | 12 +- package/origlang-cli/src/task/repl.rs | 33 +- .../origlang-compiler-entrypoint/src/lib.rs | 35 +- .../src/lib.rs | 14 +- package/origlang-diagnostics/src/lib.rs | 6 +- .../build.rs | 35 +- .../src/main.rs | 16 +- package/origlang-interop/src/lib.rs | 30 +- package/origlang-ir-optimizer/src/ir1.rs | 204 +++-- .../origlang-ir-optimizer/src/ir1/tests.rs | 110 +-- package/origlang-ir-optimizer/src/lib.rs | 2 +- package/origlang-ir-optimizer/src/lower.rs | 66 +- package/origlang-ir-optimizer/src/preset.rs | 19 +- package/origlang-ir/src/lib.rs | 51 +- package/origlang-lexer/src/boundary.rs | 2 +- package/origlang-lexer/src/error.rs | 12 +- package/origlang-lexer/src/lib.rs | 233 +++--- package/origlang-lexer/src/tests.rs | 250 +++--- package/origlang-lexer/src/token.rs | 8 +- package/origlang-parser/src/error.rs | 27 +- package/origlang-parser/src/lib.rs | 2 +- package/origlang-parser/src/parser.rs | 643 ++++++++++----- package/origlang-parser/src/recover.rs | 8 +- package/origlang-runtime/src/invoke_once.rs | 3 +- package/origlang-runtime/src/lib.rs | 164 ++-- package/origlang-source-span/src/lib.rs | 13 +- package/origlang-testsuite/src/main.rs | 741 +++++++++++++----- package/origlang-token-stream/src/lib.rs | 29 +- package/origlang-typecheck/src/type_check.rs | 360 ++++++--- .../src/type_check/error.rs | 15 +- package/origlang-typesystem-model/src/lib.rs | 76 +- 38 files changed, 2167 insertions(+), 1170 deletions(-) diff --git a/package/origlang-ast/src/after_parse.rs b/package/origlang-ast/src/after_parse.rs index ad20dc4b..7d25f2bc 100644 --- a/package/origlang-ast/src/after_parse.rs +++ b/package/origlang-ast/src/after_parse.rs @@ -1,7 +1,7 @@ //! パース時の優先順位が消去された構造体の定義 -use derive_more::Display; use crate::{Identifier, Statement}; +use derive_more::Display; #[derive(Eq, PartialEq, Clone, Debug)] pub enum Expression { @@ -18,7 +18,7 @@ pub enum Expression { UnitLiteral, /// 変数 Variable { - ident: Identifier + ident: Identifier, }, /// 四則演算、比較演算、等価性判定 BinaryOperator { @@ -38,11 +38,15 @@ pub enum Expression { }, Tuple { expressions: Vec, - } + }, } impl Expression { - pub fn binary>(operator: Operator, lhs: Self, rhs: Self) -> Self { + pub fn binary>( + operator: Operator, + lhs: Self, + rhs: Self, + ) -> Self { Self::BinaryOperator { lhs: Box::new(lhs), rhs: Box::new(rhs), diff --git a/package/origlang-ast/src/lib.rs b/package/origlang-ast/src/lib.rs index bbe7afbd..0469f2ab 100644 --- a/package/origlang-ast/src/lib.rs +++ b/package/origlang-ast/src/lib.rs @@ -1,8 +1,8 @@ #![deny(clippy::all)] #![warn(clippy::pedantic, clippy::nursery)] -use std::fmt::{Display, Formatter}; use crate::after_parse::Expression; +use std::fmt::{Display, Formatter}; pub mod after_parse; @@ -10,7 +10,7 @@ pub mod after_parse; #[expect(clippy::module_name_repetitions)] #[derive(Debug, Clone, Eq, PartialEq)] pub struct RootAst { - pub statement: Vec + pub statement: Vec, } #[derive(Eq, PartialEq, Clone, Debug)] @@ -53,7 +53,7 @@ pub enum Statement { expression: Expression, }, Block { - inner_statements: Vec + inner_statements: Vec, }, Comment { content: Comment, @@ -67,7 +67,7 @@ pub enum Statement { #[derive(Eq, PartialEq, Clone, Debug)] pub struct Comment { - pub content: String + pub content: String, } #[derive(Eq, PartialEq, Clone, Debug, Hash)] @@ -121,4 +121,4 @@ impl Display for TypeSignature { } } } -} \ No newline at end of file +} diff --git a/package/origlang-cli/src/args.rs b/package/origlang-cli/src/args.rs index bf362dda..29664383 100644 --- a/package/origlang-cli/src/args.rs +++ b/package/origlang-cli/src/args.rs @@ -1,28 +1,27 @@ // clap issue?: https://github.com/clap-rs/clap/issues/4733 #![warn(clippy::almost_swapped)] +use crate::error::TaskExecutionError; +use crate::task::emit::UnstableEmit; +use crate::task::interpret::Interpret; +use crate::task::repl::Repl; +use crate::task::Task; +use clap::{Parser, Subcommand}; use std::num::NonZeroUsize; use std::path::PathBuf; use std::string::FromUtf8Error; -use clap::{Parser, Subcommand}; use strum::EnumString; use thiserror::Error; -use crate::task::interpret::Interpret; -use crate::task::repl::Repl; -use crate::task::Task; -use crate::error::TaskExecutionError; -use crate::task::emit::UnstableEmit; - #[derive(Parser)] pub struct Args { #[clap(subcommand)] - sub_command: SubCom + sub_command: SubCom, } pub enum ParseSource { RawSource(String), - FromFile(PathBuf) + FromFile(PathBuf), } #[derive(Debug, Error)] @@ -37,19 +36,23 @@ impl ParseSource { pub fn source(&self) -> Result { match self { Self::RawSource(a) => Ok(a.clone()), - Self::FromFile(path) => { - Ok(std::fs::read_to_string(path)?) - } + Self::FromFile(path) => Ok(std::fs::read_to_string(path)?), } } } - impl Args { pub fn execute(self) -> Result<(), TaskExecutionError> { - let load_either = |input_file: Option, input_source: Option| { - input_file.map_or_else(|| input_source.map_or_else(|| panic!("please specify file or source"), ParseSource::RawSource), ParseSource::FromFile) + input_file.map_or_else( + || { + input_source.map_or_else( + || panic!("please specify file or source"), + ParseSource::RawSource, + ) + }, + ParseSource::FromFile, + ) }; match self.sub_command { @@ -58,14 +61,20 @@ impl Args { task.execute(())?; Ok(()) } - SubCom::Execute { input_file, input_source, max_stack_size_on_main_thread } => { + SubCom::Execute { + input_file, + input_source, + max_stack_size_on_main_thread, + } => { let task = Interpret; let source = load_either(input_file, input_source); if let Some(ssize) = max_stack_size_on_main_thread { let a = std::thread::scope(move |a| { - std::thread::Builder::new().stack_size(ssize.get()).spawn_scoped(a, move || { - task.execute(source) - }).unwrap().join() + std::thread::Builder::new() + .stack_size(ssize.get()) + .spawn_scoped(a, move || task.execute(source)) + .unwrap() + .join() }); a.map_err(TaskExecutionError::ThreadJoin)??; } else { @@ -73,7 +82,12 @@ impl Args { } Ok(()) } - SubCom::Emit { emit, input_file, input_source, optimize_level } => { + SubCom::Emit { + emit, + input_file, + input_source, + optimize_level, + } => { let task = UnstableEmit { phase: emit }; let source = load_either(input_file, input_source); diff --git a/package/origlang-cli/src/error.rs b/package/origlang-cli/src/error.rs index 47058921..54b432d0 100644 --- a/package/origlang-cli/src/error.rs +++ b/package/origlang-cli/src/error.rs @@ -1,9 +1,9 @@ -use std::any::Any; -use thiserror::Error; +use crate::args::ReadSourceError; use origlang_parser::error::ParserError; -use origlang_typecheck::type_check::error::TypeCheckError; use origlang_runtime::RuntimeError; -use crate::args::ReadSourceError; +use origlang_typecheck::type_check::error::TypeCheckError; +use std::any::Any; +use thiserror::Error; #[derive(Error, Debug)] #[expect(clippy::module_name_repetitions)] diff --git a/package/origlang-cli/src/main.rs b/package/origlang-cli/src/main.rs index 40e9599b..779e1b54 100644 --- a/package/origlang-cli/src/main.rs +++ b/package/origlang-cli/src/main.rs @@ -1,13 +1,13 @@ #![deny(clippy::all)] #![warn(clippy::pedantic, clippy::nursery)] -use clap::Parser; use crate::args::Args; use crate::error::TaskExecutionError; +use clap::Parser; -mod task; mod args; mod error; +mod task; fn main() -> Result<(), TaskExecutionError> { let args = Args::parse(); @@ -15,7 +15,7 @@ fn main() -> Result<(), TaskExecutionError> { if let Err(e) = args.execute() { log::error!("{e}", e = &e); - return Err(e) + return Err(e); } Ok(()) } diff --git a/package/origlang-cli/src/task.rs b/package/origlang-cli/src/task.rs index eaa3e9c1..dc92e62f 100644 --- a/package/origlang-cli/src/task.rs +++ b/package/origlang-cli/src/task.rs @@ -1,6 +1,6 @@ +pub mod emit; pub mod interpret; pub mod repl; -pub mod emit; pub trait Task { type Environment; diff --git a/package/origlang-cli/src/task/emit.rs b/package/origlang-cli/src/task/emit.rs index 3f8a6031..adbc584a 100644 --- a/package/origlang-cli/src/task/emit.rs +++ b/package/origlang-cli/src/task/emit.rs @@ -1,15 +1,15 @@ -use thiserror::Error; +use crate::args::{EmitPhase, OptimizeLevel, ParseSource, ReadSourceError}; +use crate::error::TaskExecutionError; +use crate::task::Task; +use origlang_ir::{IntoVerbatimSequencedIR, IR1, IR2}; +use origlang_ir_optimizer::lower::{EachStep, LowerStep, TheTranspiler}; +use origlang_ir_optimizer::preset::{NoOptimization, SimpleOptimization}; use origlang_lexer::Lexer; -use origlang_parser::parser::Parser; use origlang_parser::error::ParserError; +use origlang_parser::parser::Parser; use origlang_typecheck::type_check::error::TypeCheckError; use origlang_typecheck::type_check::TypeChecker; -use origlang_ir::{IntoVerbatimSequencedIR, IR1, IR2}; -use origlang_ir_optimizer::lower::{EachStep, LowerStep, TheTranspiler}; -use origlang_ir_optimizer::preset::{NoOptimization, SimpleOptimization}; -use crate::args::{EmitPhase, OptimizeLevel, ParseSource, ReadSourceError}; -use crate::error::TaskExecutionError; -use crate::task::Task; +use thiserror::Error; pub struct UnstableEmit { pub(crate) phase: EmitPhase, @@ -47,7 +47,7 @@ impl Task for UnstableEmit { let a = lexer.next(); println!("{a:?}"); if a.data.is_error() || a.data.is_end() { - return Ok(()) + return Ok(()); } } } @@ -57,7 +57,7 @@ impl Task for UnstableEmit { if self.phase == EmitPhase::Ast { println!("{root:#?}"); - return Ok(()) + return Ok(()); } let checker = TypeChecker::new(); @@ -65,7 +65,7 @@ impl Task for UnstableEmit { if self.phase == EmitPhase::TypedAst { println!("{expr:#?}"); - return Ok(()) + return Ok(()); } let ir_sequence = expr.into_ir(); @@ -81,7 +81,7 @@ impl Task for UnstableEmit { let ir_sequence: Vec = the_lower.optimizer().optimize(ir_sequence); println!("{ir_sequence:#?}"); - return Ok(()) + return Ok(()); } let ir_sequence = the_lower.lower(ir_sequence); @@ -94,4 +94,4 @@ impl Task for UnstableEmit { Ok(()) } -} \ No newline at end of file +} diff --git a/package/origlang-cli/src/task/interpret.rs b/package/origlang-cli/src/task/interpret.rs index f3489838..b488a350 100644 --- a/package/origlang-cli/src/task/interpret.rs +++ b/package/origlang-cli/src/task/interpret.rs @@ -1,13 +1,13 @@ -use std::time::Instant; -use origlang_parser::parser::Parser; -use origlang_runtime::{PrintToStdout, Runtime}; -use crate::task::Task; +use crate::args::ParseSource; use crate::error::TaskExecutionError; -use origlang_typecheck::type_check::TypeChecker; +use crate::task::Task; use origlang_ir::IntoVerbatimSequencedIR; use origlang_ir_optimizer::lower::{LowerStep, TheTranspiler}; use origlang_ir_optimizer::preset::NoOptimization; -use crate::args::ParseSource; +use origlang_parser::parser::Parser; +use origlang_runtime::{PrintToStdout, Runtime}; +use origlang_typecheck::type_check::TypeChecker; +use std::time::Instant; pub struct Interpret; diff --git a/package/origlang-cli/src/task/repl.rs b/package/origlang-cli/src/task/repl.rs index a10daa29..d64ea66c 100644 --- a/package/origlang-cli/src/task/repl.rs +++ b/package/origlang-cli/src/task/repl.rs @@ -1,24 +1,24 @@ -use std::fmt::{Debug, Display}; -use std::io::{stdout, Write}; -use std::ops::Range; -use ariadne::{Report, ReportKind, Source}; -use crate::task::Task; use crate::error::TaskExecutionError; -use origlang_platform::CTRL_D_NL; -use origlang_parser::parser::Parser; -use origlang_parser::error::ParserErrorInner; -use origlang_runtime::{PrintToStdout, Runtime}; -use origlang_typecheck::type_check::TypeChecker; +use crate::task::Task; +use ariadne::{Report, ReportKind, Source}; use origlang_ir::{IntoVerbatimSequencedIR, IR2}; use origlang_ir_optimizer::lower::{LowerStep, TheTranspiler}; use origlang_ir_optimizer::preset::NoOptimization; +use origlang_parser::error::ParserErrorInner; +use origlang_parser::parser::Parser; +use origlang_platform::CTRL_D_NL; +use origlang_runtime::{PrintToStdout, Runtime}; +use origlang_typecheck::type_check::TypeChecker; use origlang_typesystem_model::TypedRootAst; +use std::fmt::{Debug, Display}; +use std::io::{stdout, Write}; +use std::ops::Range; struct Dummy((), Source); impl ariadne::Cache<()> for Dummy { type Storage = String; - + fn fetch(&mut self, _id: &()) -> Result<&Source, Box> { Ok(&self.1) } @@ -59,7 +59,7 @@ impl Task for Repl { dbg!(&line); // FIXME: this is buggy in Windows, causing infinite loop if line == CTRL_D_NL { - break + break; } let parser = Parser::create(line.as_str()); match parser.parse() { @@ -70,7 +70,11 @@ impl Task for Repl { Err(error_message) => { let error = error_message.kind(); let handled = false; - if let ParserErrorInner::PartiallyParsed { hint: _, intermediate_state: _ } = &error { + if let ParserErrorInner::PartiallyParsed { + hint: _, + intermediate_state: _, + } = &error + { // TODO: insta-expression eval // TODO: revisit this /* @@ -114,7 +118,8 @@ impl Task for Repl { .with_message(error.to_string()) .finish(); - d.write(Dummy((), Source::from(line)), std::io::stderr()).expect("TODO: panic message"); + d.write(Dummy((), Source::from(line)), std::io::stderr()) + .expect("TODO: panic message"); } } } diff --git a/package/origlang-compiler-entrypoint/src/lib.rs b/package/origlang-compiler-entrypoint/src/lib.rs index ebd88cf2..4d7a8c69 100644 --- a/package/origlang-compiler-entrypoint/src/lib.rs +++ b/package/origlang-compiler-entrypoint/src/lib.rs @@ -4,17 +4,17 @@ use std::any::type_name; use log::debug; -use thiserror::Error; use origlang_ast::Statement; -use origlang_lexer::token::Token as LexerToken; -use origlang_parser::parser::Parser; -use origlang_parser::error::ParserError; -use origlang_typecheck::type_check::error::TypeCheckError; -use origlang_typecheck::type_check::TypeChecker; use origlang_diagnostics::{Diagnostic, DiagnosticSink}; use origlang_ir::{IntoVerbatimSequencedIR, IR1, IR2}; use origlang_ir_optimizer::lower::TheTranspiler; use origlang_ir_optimizer::preset::{NoOptimization, OptimizationPreset}; +use origlang_lexer::token::Token as LexerToken; +use origlang_parser::error::ParserError; +use origlang_parser::parser::Parser; +use origlang_typecheck::type_check::error::TypeCheckError; +use origlang_typecheck::type_check::TypeChecker; +use thiserror::Error; pub struct TheCompiler { /// The linter, built-in diagnostics, etc... See [`ScannerRegistry`]. @@ -25,7 +25,8 @@ pub struct TheCompiler { impl TheCompiler { /// Creates new instance without any optimization, scanner, nor diagnostic receiver. - #[must_use] pub fn new() -> Self { + #[must_use] + pub fn new() -> Self { Self { scanner: ScannerRegistry::default(), optimization_preset: OptimizationPresetCollection::none(), @@ -40,7 +41,11 @@ impl TheCompiler { self } - #[must_use] pub fn register_diagnostic_receiver(mut self, receiver: Box) -> Self { + #[must_use] + pub fn register_diagnostic_receiver( + mut self, + receiver: Box, + ) -> Self { debug!("registered {ds}", ds = type_name::()); self.diagnostic_receivers.push(receiver as _); @@ -49,7 +54,10 @@ impl TheCompiler { } #[must_use] - pub fn optimization_preset(mut self, modify: impl FnOnce(&mut OptimizationPresetCollection)) -> Self { + pub fn optimization_preset( + mut self, + modify: impl FnOnce(&mut OptimizationPresetCollection), + ) -> Self { modify(&mut self.optimization_preset); self @@ -61,12 +69,15 @@ impl TheCompiler { let typeck = TypeChecker::new().check(root)?; let ir0_seq = typeck.into_ir(); let the_transpiler = TheTranspiler { - optimization_preset: &self.optimization_preset + optimization_preset: &self.optimization_preset, }; let ir1_seq = ir0_seq; - let (pre, post): (Vec<_>, Vec<_>) = - self.scanner.ir1_scanner.iter().partition(|x| matches!(x, PreOrPost::Pre(..))); + let (pre, post): (Vec<_>, Vec<_>) = self + .scanner + .ir1_scanner + .iter() + .partition(|x| matches!(x, PreOrPost::Pre(..))); for s in pre { if let PreOrPost::Pre(s) = s { diff --git a/package/origlang-compiler-scanner-example/src/lib.rs b/package/origlang-compiler-scanner-example/src/lib.rs index 2bae020e..c95c8920 100644 --- a/package/origlang-compiler-scanner-example/src/lib.rs +++ b/package/origlang-compiler-scanner-example/src/lib.rs @@ -3,11 +3,11 @@ #[cfg(test)] mod tests { - use std::cell::Cell; use origlang_compiler_entrypoint::{PreOrPost, Scanner, TheCompiler}; use origlang_diagnostics::{CauseTree, Diagnostic, DiagnosticSeverity, DiagnosticSink}; use origlang_ir::IR1; use origlang_source_span::CanonicalSourceSpan; + use std::cell::Cell; struct MyScanner; @@ -16,10 +16,8 @@ mod tests { values .iter() .filter(|x| match x { - IR1::UpdateVariable { ident, .. } => { - ident.as_name() == "foo" - } - _ => false + IR1::UpdateVariable { ident, .. } => ident.as_name() == "foo", + _ => false, }) .map(|_| Box::new(GiveItMorePreciseName) as Box<_>) .collect() @@ -51,7 +49,7 @@ mod tests { } struct TestDiagnosticReceiver { - triggered: Cell + triggered: Cell, } impl DiagnosticSink for TestDiagnosticReceiver { @@ -70,7 +68,9 @@ mod tests { #[test] fn it_works() { let entry = TheCompiler::new(); - let tdr = TestDiagnosticReceiver { triggered: Cell::new(false) }; + let tdr = TestDiagnosticReceiver { + triggered: Cell::new(false), + }; let a = entry .scanners(|s| { diff --git a/package/origlang-diagnostics/src/lib.rs b/package/origlang-diagnostics/src/lib.rs index 2ed8c4dd..51db2d45 100644 --- a/package/origlang-diagnostics/src/lib.rs +++ b/package/origlang-diagnostics/src/lib.rs @@ -34,11 +34,11 @@ impl Diagnostic for Box { pub enum CauseTree { Branch { reason: Box, - children: Vec + children: Vec, }, Leaf { - reason: Box - } + reason: Box, + }, } #[derive(Eq, PartialEq, Copy, Clone, Debug)] diff --git a/package/origlang-interop-frontend-webserver/build.rs b/package/origlang-interop-frontend-webserver/build.rs index 827c098c..9394c070 100644 --- a/package/origlang-interop-frontend-webserver/build.rs +++ b/package/origlang-interop-frontend-webserver/build.rs @@ -7,7 +7,11 @@ fn main() { let www = cd.join("www"); - let interop_dir = cd.parent().expect("no parent").join("origlang-interop").join("pkg"); + let interop_dir = cd + .parent() + .expect("no parent") + .join("origlang-interop") + .join("pkg"); println!("interop_dir: {}", interop_dir.display()); for interop_file in read_dir(interop_dir).expect("fail") { @@ -15,9 +19,12 @@ fn main() { let from = x.path(); let name = x.file_name(); if name == *".gitignore" { - continue + continue; } - let common_parent = from.iter().take(from.components().count() - 3).collect::(); + let common_parent = from + .iter() + .take(from.components().count() - 3) + .collect::(); println!("cpar: {}", common_parent.display()); let to = www.join(name); @@ -27,8 +34,11 @@ fn main() { copy_file(from, to).expect("copy failed, please build origlang-interop first."); } - let useful_example_dir = cd.parent().expect("parent") - .parent().expect("parent.parent") + let useful_example_dir = cd + .parent() + .expect("parent") + .parent() + .expect("parent.parent") .join("compile") .join("positive") .join("non-perf"); @@ -39,14 +49,17 @@ fn main() { } std::fs::copy( useful_example_dir.join("99_bottles_of_beer.origlang"), - www_example_dir.join("99_bottles_of_beer.origlang") - ).expect("99 bottles"); + www_example_dir.join("99_bottles_of_beer.origlang"), + ) + .expect("99 bottles"); std::fs::copy( useful_example_dir.join("hello_world.origlang"), - www_example_dir.join("hello_world.origlang") - ).expect("Hello world"); + www_example_dir.join("hello_world.origlang"), + ) + .expect("Hello world"); std::fs::copy( useful_example_dir.join("fizz_buzz.origlang"), - www_example_dir.join("fizz_buzz.origlang") - ).expect("Fizz Buzz"); + www_example_dir.join("fizz_buzz.origlang"), + ) + .expect("Fizz Buzz"); } diff --git a/package/origlang-interop-frontend-webserver/src/main.rs b/package/origlang-interop-frontend-webserver/src/main.rs index 29ca2860..f9007a5c 100644 --- a/package/origlang-interop-frontend-webserver/src/main.rs +++ b/package/origlang-interop-frontend-webserver/src/main.rs @@ -2,15 +2,13 @@ #![warn(clippy::pedantic, clippy::nursery)] use actix_files::Files; -use actix_web::{App, HttpServer}; use actix_web::dev::Service; +use actix_web::{App, HttpServer}; #[actix_web::main] async fn main() { HttpServer::new(|| { - App::new().service( - Files::new("/", "www") - ).wrap_fn(|sr, t| { + App::new().service(Files::new("/", "www")).wrap_fn(|sr, t| { let fut = t.call(sr); async { let x = fut.await?; @@ -19,11 +17,11 @@ async fn main() { } }) }) - .bind(("127.0.0.1", 17821)) - .expect("port binding") - .run() - .await - .expect("fail"); + .bind(("127.0.0.1", 17821)) + .expect("port binding") + .run() + .await + .expect("fail"); println!("Hello, world!"); } diff --git a/package/origlang-interop/src/lib.rs b/package/origlang-interop/src/lib.rs index c8487e64..e18e725a 100644 --- a/package/origlang-interop/src/lib.rs +++ b/package/origlang-interop/src/lib.rs @@ -2,22 +2,22 @@ #![warn(clippy::pedantic, clippy::nursery)] use js_sys::JsString; -use thiserror::Error; -use wasm_bindgen::{JsCast, JsValue}; -use wasm_bindgen::prelude::wasm_bindgen; -use web_sys::console; -use origlang_parser::parser::Parser; -use origlang_parser::error::ParserError; -use origlang_typecheck::type_check::error::TypeCheckError; -use origlang_typecheck::type_check::TypeChecker; use origlang_ir::IntoVerbatimSequencedIR; use origlang_ir_optimizer::lower::{LowerStep, TheTranspiler}; use origlang_ir_optimizer::preset::NoOptimization; +use origlang_parser::error::ParserError; +use origlang_parser::parser::Parser; use origlang_runtime::{OutputAccumulator, Runtime, TypeBox}; +use origlang_typecheck::type_check::error::TypeCheckError; +use origlang_typecheck::type_check::TypeChecker; use origlang_typesystem_model::TypedRootAst; +use thiserror::Error; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::{JsCast, JsValue}; +use web_sys::console; #[wasm_bindgen] -extern { +extern "C" { fn get_source() -> JsValue; fn set_compile_error(s: JsString); @@ -57,7 +57,7 @@ impl OutputAccumulator for PseudoStdout { } struct Timer<'a> { - name: &'a str + name: &'a str, } impl<'a> Timer<'a> { @@ -93,12 +93,12 @@ pub fn run() { }; src.dyn_ref::().map_or_else( - || { - panic!("get_source implementation did not return string, this is IMPLEMENTATION BUG") - }, + || panic!("get_source implementation did not return string, this is IMPLEMENTATION BUG"), |src| { let inner: fn(&JsString) -> Result<(), ExecutionError> = |src| { - let src = src.as_string().expect("Source code must not contain invalid surrogate codepoint"); + let src = src + .as_string() + .expect("Source code must not contain invalid surrogate codepoint"); let parser = { let _ = Timer::new("parse.construction"); Parser::create(&src) @@ -132,6 +132,6 @@ pub fn run() { if let Err(e) = inner(src) { set_compile_error(JsString::from(e.to_string())); } - } + }, ); } diff --git a/package/origlang-ir-optimizer/src/ir1.rs b/package/origlang-ir-optimizer/src/ir1.rs index 5b3158f6..a2594c98 100644 --- a/package/origlang-ir-optimizer/src/ir1.rs +++ b/package/origlang-ir-optimizer/src/ir1.rs @@ -1,13 +1,13 @@ #[cfg(test)] mod tests; -use std::cmp::Ordering; +pub use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub}; use origlang_ast::after_parse::BinaryOperatorKind; use origlang_ir::IR1; use origlang_typesystem_model::{TypedExpression, TypedIntLiteral}; -pub use num_traits::{CheckedAdd, CheckedSub, CheckedDiv, CheckedMul}; +use std::cmp::Ordering; -trait OutputCompareResultAsSelf : Sized + PartialOrd + PartialEq { +trait OutputCompareResultAsSelf: Sized + PartialOrd + PartialEq { fn compare_self(&self, other: &Self) -> Option; } @@ -61,15 +61,17 @@ pub struct FoldBinaryOperatorInvocationWithConstant(pub Vec); impl FoldBinaryOperatorInvocationWithConstant { #[must_use = "Dropping return value implies drop optimized IRs"] pub fn optimize(self) -> Vec { - self.0.into_iter().map(|ir| { - match ir { + self.0 + .into_iter() + .map(|ir| match ir { IR1::Output(value) => IR1::Output(Self::walk_expression(value)), IR1::UpdateVariable { ident, value } => IR1::UpdateVariable { - ident, value: Self::walk_expression(value), + ident, + value: Self::walk_expression(value), }, - other => other - } - }).collect() + other => other, + }) + .collect() } #[expect(clippy::cast_lossless, clippy::too_many_lines)] @@ -77,29 +79,43 @@ impl FoldBinaryOperatorInvocationWithConstant { #[must_use] fn walk_expression(expr: TypedExpression) -> TypedExpression { let (lhs, rhs, operator, return_type) = match expr { - TypedExpression::If { condition, then, els, return_type } => { + TypedExpression::If { + condition, + then, + els, + return_type, + } => { return TypedExpression::If { condition: Box::new(Self::walk_expression(*condition)), then: Box::new(Self::walk_expression(*then)), els: Box::new(Self::walk_expression(*els)), - return_type + return_type, } } - TypedExpression::Block { inner, final_expression, return_type } => { + TypedExpression::Block { + inner, + final_expression, + return_type, + } => { return TypedExpression::Block { inner, final_expression: Box::new(Self::walk_expression(*final_expression)), - return_type + return_type, } } TypedExpression::Tuple { expressions } => { - return TypedExpression::Tuple { expressions: expressions.into_iter().map(Self::walk_expression).collect() } + return TypedExpression::Tuple { + expressions: expressions.into_iter().map(Self::walk_expression).collect(), + } } - TypedExpression::BinaryOperator { lhs, rhs, operator, return_type } => (lhs, rhs, operator, return_type), + TypedExpression::BinaryOperator { + lhs, + rhs, + operator, + return_type, + } => (lhs, rhs, operator, return_type), // these expressions are optimal for this path - other => { - return other - } + other => return other, }; // handle recursively @@ -112,7 +128,9 @@ impl FoldBinaryOperatorInvocationWithConstant { let rhs_ref = rhs.as_ref(); let operands = (lhs_ref, rhs_ref); - if let (TypedExpression::IntLiteral(lhs_lit), TypedExpression::IntLiteral(rhs_lit)) = operands { + if let (TypedExpression::IntLiteral(lhs_lit), TypedExpression::IntLiteral(rhs_lit)) = + operands + { if lhs_lit.actual_type() == rhs_lit.actual_type() { macro_rules! fold_opt_to_discriminator { ($binary_function:expr, $arg1:expr, $arg2:expr, $into:ident) => { @@ -121,11 +139,11 @@ impl FoldBinaryOperatorInvocationWithConstant { lhs, rhs, operator, - return_type + return_type, }, - |v| TypedExpression::IntLiteral(TypedIntLiteral::$into(v)) + |v| TypedExpression::IntLiteral(TypedIntLiteral::$into(v)), ) - } + }; } macro_rules! fold_int_literals { @@ -133,22 +151,22 @@ impl FoldBinaryOperatorInvocationWithConstant { match (lhs_lit, rhs_lit) { (TypedIntLiteral::Generic(lhs), TypedIntLiteral::Generic(rhs)) => { fold_opt_to_discriminator!($method, lhs, rhs, Generic) - }, + } (TypedIntLiteral::Bit64(lhs), TypedIntLiteral::Bit64(rhs)) => { fold_opt_to_discriminator!($method, lhs, rhs, Bit64) - }, + } (TypedIntLiteral::Bit32(lhs), TypedIntLiteral::Bit32(rhs)) => { fold_opt_to_discriminator!($method, lhs, rhs, Bit32) - }, + } (TypedIntLiteral::Bit16(lhs), TypedIntLiteral::Bit16(rhs)) => { fold_opt_to_discriminator!($method, lhs, rhs, Bit16) - }, + } (TypedIntLiteral::Bit8(lhs), TypedIntLiteral::Bit8(rhs)) => { fold_opt_to_discriminator!($method, lhs, rhs, Bit8) - }, + } _ => unreachable!(), } - } + }; } match operator { @@ -157,7 +175,9 @@ impl FoldBinaryOperatorInvocationWithConstant { BinaryOperatorKind::Minus => fold_int_literals!(CheckedSub::checked_sub), BinaryOperatorKind::Multiply => fold_int_literals!(CheckedMul::checked_mul), BinaryOperatorKind::Divide => fold_int_literals!(CheckedDiv::checked_div), - BinaryOperatorKind::ThreeWay => fold_int_literals!(OutputCompareResultAsSelf::compare_self), + BinaryOperatorKind::ThreeWay => { + fold_int_literals!(OutputCompareResultAsSelf::compare_self) + } compare => Self::fold_compare_into_bool_literal(lhs_lit, rhs_lit, compare), } } else { @@ -165,10 +185,14 @@ impl FoldBinaryOperatorInvocationWithConstant { lhs, rhs, operator, - return_type + return_type, } } - } else if let (TypedExpression::BooleanLiteral(lhs_lit), TypedExpression::BooleanLiteral(rhs_lit)) = operands { + } else if let ( + TypedExpression::BooleanLiteral(lhs_lit), + TypedExpression::BooleanLiteral(rhs_lit), + ) = operands + { match operator { BinaryOperatorKind::Equal => TypedExpression::BooleanLiteral(lhs_lit == rhs_lit), BinaryOperatorKind::NotEqual => TypedExpression::BooleanLiteral(lhs_lit != rhs_lit), @@ -176,7 +200,7 @@ impl FoldBinaryOperatorInvocationWithConstant { lhs, rhs, operator, - return_type + return_type, }, } } else { @@ -184,44 +208,58 @@ impl FoldBinaryOperatorInvocationWithConstant { lhs, rhs, operator, - return_type + return_type, } } } #[expect(clippy::redundant_closure_call)] - fn fold_compare_into_bool_literal(lhs: &TypedIntLiteral, rhs: &TypedIntLiteral, compare: BinaryOperatorKind) -> TypedExpression { + fn fold_compare_into_bool_literal( + lhs: &TypedIntLiteral, + rhs: &TypedIntLiteral, + compare: BinaryOperatorKind, + ) -> TypedExpression { // [T, O] =>> (T, T) => O macro_rules! poly_input_lambda { ($closure:expr) => { match (lhs, rhs) { (TypedIntLiteral::Generic(lhs), TypedIntLiteral::Generic(rhs)) => { $closure(lhs, rhs) - }, + } (TypedIntLiteral::Bit64(lhs), TypedIntLiteral::Bit64(rhs)) => { $closure(lhs, rhs) - }, + } (TypedIntLiteral::Bit32(lhs), TypedIntLiteral::Bit32(rhs)) => { $closure(lhs, rhs) - }, + } (TypedIntLiteral::Bit16(lhs), TypedIntLiteral::Bit16(rhs)) => { $closure(lhs, rhs) - }, - (TypedIntLiteral::Bit8(lhs), TypedIntLiteral::Bit8(rhs)) => { - $closure(lhs, rhs) - }, + } + (TypedIntLiteral::Bit8(lhs), TypedIntLiteral::Bit8(rhs)) => $closure(lhs, rhs), _ => unreachable!(), } }; } match compare { - BinaryOperatorKind::More => poly_input_lambda!(|lhs, rhs| TypedExpression::BooleanLiteral(lhs > rhs)), - BinaryOperatorKind::MoreEqual => poly_input_lambda!(|lhs, rhs| TypedExpression::BooleanLiteral(lhs >= rhs)), - BinaryOperatorKind::Less => poly_input_lambda!(|lhs, rhs| TypedExpression::BooleanLiteral(lhs < rhs)), - BinaryOperatorKind::LessEqual => poly_input_lambda!(|lhs, rhs| TypedExpression::BooleanLiteral(lhs <= rhs)), - BinaryOperatorKind::Equal => poly_input_lambda!(|lhs, rhs| TypedExpression::BooleanLiteral(lhs == rhs)), - BinaryOperatorKind::NotEqual => poly_input_lambda!(|lhs, rhs| TypedExpression::BooleanLiteral(lhs != rhs)), + BinaryOperatorKind::More => { + poly_input_lambda!(|lhs, rhs| TypedExpression::BooleanLiteral(lhs > rhs)) + } + BinaryOperatorKind::MoreEqual => { + poly_input_lambda!(|lhs, rhs| TypedExpression::BooleanLiteral(lhs >= rhs)) + } + BinaryOperatorKind::Less => { + poly_input_lambda!(|lhs, rhs| TypedExpression::BooleanLiteral(lhs < rhs)) + } + BinaryOperatorKind::LessEqual => { + poly_input_lambda!(|lhs, rhs| TypedExpression::BooleanLiteral(lhs <= rhs)) + } + BinaryOperatorKind::Equal => { + poly_input_lambda!(|lhs, rhs| TypedExpression::BooleanLiteral(lhs == rhs)) + } + BinaryOperatorKind::NotEqual => { + poly_input_lambda!(|lhs, rhs| TypedExpression::BooleanLiteral(lhs != rhs)) + } #[expect(clippy::panic)] other => panic!("operator {other} is not supported by this function"), } @@ -234,21 +272,27 @@ pub struct FoldIfWithConstantCondition(pub Vec); impl FoldIfWithConstantCondition { #[must_use = "return value is optimized IR, dropping it will waste it"] pub fn optimize(self) -> Vec { - self.0.into_iter().map(|x| { - match x { - IR1::Output(expression) => { - IR1::Output(Self::walk_expression(expression)) - } - IR1::UpdateVariable { ident, value: expr } => { - IR1::UpdateVariable { ident, value: Self::walk_expression(expr) } - } - other => other - } - }).collect() + self.0 + .into_iter() + .map(|x| match x { + IR1::Output(expression) => IR1::Output(Self::walk_expression(expression)), + IR1::UpdateVariable { ident, value: expr } => IR1::UpdateVariable { + ident, + value: Self::walk_expression(expr), + }, + other => other, + }) + .collect() } fn walk_expression(expr: TypedExpression) -> TypedExpression { - if let TypedExpression::If { condition, then, els, return_type } = expr { + if let TypedExpression::If { + condition, + then, + els, + return_type, + } = expr + { let condition_lit = condition.as_ref(); if let TypedExpression::BooleanLiteral(const_cond) = condition_lit { dbg!(&then); @@ -258,7 +302,12 @@ impl FoldIfWithConstantCondition { *els } } else { - TypedExpression::If { condition, then, els, return_type } + TypedExpression::If { + condition, + then, + els, + return_type, + } } } else { expr @@ -272,21 +321,26 @@ pub struct InlineSimpleBlock(pub Vec); impl InlineSimpleBlock { #[must_use = "return value is optimized IR, dropping it will waste it"] pub fn optimize(self) -> Vec { - self.0.into_iter().map(|x| match x { - IR1::Output(x) => IR1::Output(Self::walk_expression(x)), - IR1::UpdateVariable { ident, value } => IR1::UpdateVariable { - ident, - value: Self::walk_expression(value) - }, - other => other, - }).collect() + self.0 + .into_iter() + .map(|x| match x { + IR1::Output(x) => IR1::Output(Self::walk_expression(x)), + IR1::UpdateVariable { ident, value } => IR1::UpdateVariable { + ident, + value: Self::walk_expression(value), + }, + other => other, + }) + .collect() } - + fn walk_expression(checked: TypedExpression) -> TypedExpression { match checked { - TypedExpression::Block { inner, final_expression, return_type: _ } if inner.is_empty() => { - *final_expression - } + TypedExpression::Block { + inner, + final_expression, + return_type: _, + } if inner.is_empty() => *final_expression, other => other, } } @@ -297,7 +351,11 @@ pub struct EliminateAfterExit(pub Vec); impl EliminateAfterExit { #[must_use = "return value is optimized IR, dropping it will waste it"] pub fn optimize(self) -> Vec { - let mut x = self.0.into_iter().take_while(|x| *x != IR1::Exit).collect::>(); + let mut x = self + .0 + .into_iter() + .take_while(|x| *x != IR1::Exit) + .collect::>(); x.push(IR1::Exit); x } diff --git a/package/origlang-ir-optimizer/src/ir1/tests.rs b/package/origlang-ir-optimizer/src/ir1/tests.rs index 3cd67b3f..2eee33df 100644 --- a/package/origlang-ir-optimizer/src/ir1/tests.rs +++ b/package/origlang-ir-optimizer/src/ir1/tests.rs @@ -1,12 +1,15 @@ +use crate::ir1::{ + EliminateAfterExit, FoldBinaryOperatorInvocationWithConstant, FoldIfWithConstantCondition, + InlineSimpleBlock, +}; use origlang_ast::after_parse::BinaryOperatorKind; use origlang_ir::IR1; use origlang_typesystem_model::{Type, TypedExpression, TypedIntLiteral}; -use crate::ir1::{EliminateAfterExit, FoldBinaryOperatorInvocationWithConstant, FoldIfWithConstantCondition, InlineSimpleBlock}; #[test] fn fold_binary_operator_is_recursive() { - let opt_output = FoldBinaryOperatorInvocationWithConstant(vec![ - IR1::Output(TypedExpression::BinaryOperator { + let opt_output = FoldBinaryOperatorInvocationWithConstant(vec![IR1::Output( + TypedExpression::BinaryOperator { lhs: Box::new(TypedExpression::BinaryOperator { lhs: Box::new(TypedExpression::IntLiteral(TypedIntLiteral::Generic(1))), rhs: Box::new(TypedExpression::IntLiteral(TypedIntLiteral::Generic(2))), @@ -16,80 +19,89 @@ fn fold_binary_operator_is_recursive() { rhs: Box::new(TypedExpression::IntLiteral(TypedIntLiteral::Generic(3))), operator: BinaryOperatorKind::Plus, return_type: Type::GenericInteger, - }) - ]).optimize(); + }, + )]) + .optimize(); - assert_eq!(opt_output, vec![ - IR1::Output(TypedExpression::IntLiteral(TypedIntLiteral::Generic(6))) - ]); + assert_eq!( + opt_output, + vec![IR1::Output(TypedExpression::IntLiteral( + TypedIntLiteral::Generic(6) + ))] + ); } #[test] fn fold_comparison_between_integrals_into_boolean_literal() { // TODO: test all comparison operator - let opt_output = FoldBinaryOperatorInvocationWithConstant(vec![ - IR1::Output(TypedExpression::BinaryOperator { + let opt_output = FoldBinaryOperatorInvocationWithConstant(vec![IR1::Output( + TypedExpression::BinaryOperator { lhs: Box::new(TypedExpression::IntLiteral(TypedIntLiteral::Generic(2))), rhs: Box::new(TypedExpression::IntLiteral(TypedIntLiteral::Generic(3))), operator: BinaryOperatorKind::Less, return_type: Type::Boolean, - }) - ]).optimize(); + }, + )]) + .optimize(); - assert_eq!(opt_output, [IR1::Output(TypedExpression::BooleanLiteral(true))]); + assert_eq!( + opt_output, + [IR1::Output(TypedExpression::BooleanLiteral(true))] + ); } #[test] fn fold_if_with_constant_condition() { - let opt_output = FoldIfWithConstantCondition(vec![ - IR1::Output(TypedExpression::If { - condition: Box::new(TypedExpression::BooleanLiteral(true)), - then: Box::new(TypedExpression::IntLiteral(TypedIntLiteral::Generic(1))), - els: Box::new(TypedExpression::IntLiteral(TypedIntLiteral::Generic(2))), - return_type: Type::GenericInteger, - }) - ]).optimize(); - - assert_eq!(opt_output, vec![ - IR1::Output(TypedExpression::IntLiteral(TypedIntLiteral::Generic(1))) - ]); + let opt_output = FoldIfWithConstantCondition(vec![IR1::Output(TypedExpression::If { + condition: Box::new(TypedExpression::BooleanLiteral(true)), + then: Box::new(TypedExpression::IntLiteral(TypedIntLiteral::Generic(1))), + els: Box::new(TypedExpression::IntLiteral(TypedIntLiteral::Generic(2))), + return_type: Type::GenericInteger, + })]) + .optimize(); - let opt_output = FoldIfWithConstantCondition(vec![ - IR1::Output(TypedExpression::If { - condition: Box::new(TypedExpression::BooleanLiteral(false)), - then: Box::new(TypedExpression::IntLiteral(TypedIntLiteral::Generic(1))), - els: Box::new(TypedExpression::IntLiteral(TypedIntLiteral::Generic(2))), - return_type: Type::GenericInteger, - }) - ]).optimize(); + assert_eq!( + opt_output, + vec![IR1::Output(TypedExpression::IntLiteral( + TypedIntLiteral::Generic(1) + ))] + ); - assert_eq!(opt_output, vec![ - IR1::Output(TypedExpression::IntLiteral(TypedIntLiteral::Generic(2))) - ]); + let opt_output = FoldIfWithConstantCondition(vec![IR1::Output(TypedExpression::If { + condition: Box::new(TypedExpression::BooleanLiteral(false)), + then: Box::new(TypedExpression::IntLiteral(TypedIntLiteral::Generic(1))), + els: Box::new(TypedExpression::IntLiteral(TypedIntLiteral::Generic(2))), + return_type: Type::GenericInteger, + })]) + .optimize(); + assert_eq!( + opt_output, + vec![IR1::Output(TypedExpression::IntLiteral( + TypedIntLiteral::Generic(2) + ))] + ); } #[test] fn inline_simple_block() { - let opt_output = InlineSimpleBlock(vec![ - IR1::Output(TypedExpression::Block { - inner: vec![], - final_expression: Box::new(TypedExpression::UnitLiteral), - return_type: Type::Unit, - }) - ]).optimize(); + let opt_output = InlineSimpleBlock(vec![IR1::Output(TypedExpression::Block { + inner: vec![], + final_expression: Box::new(TypedExpression::UnitLiteral), + return_type: Type::Unit, + })]) + .optimize(); - assert_eq!(opt_output, vec![ - IR1::Output(TypedExpression::UnitLiteral) - ]); + assert_eq!(opt_output, vec![IR1::Output(TypedExpression::UnitLiteral)]); } #[test] fn cut_down_after_exit() { let opt_output = EliminateAfterExit(vec![ IR1::Exit, - IR1::Output(TypedExpression::BooleanLiteral(false)) - ]).optimize(); + IR1::Output(TypedExpression::BooleanLiteral(false)), + ]) + .optimize(); - assert_eq!(opt_output, [ IR1::Exit ]); + assert_eq!(opt_output, [IR1::Exit]); } diff --git a/package/origlang-ir-optimizer/src/lib.rs b/package/origlang-ir-optimizer/src/lib.rs index 3c05786b..ec11825f 100644 --- a/package/origlang-ir-optimizer/src/lib.rs +++ b/package/origlang-ir-optimizer/src/lib.rs @@ -2,5 +2,5 @@ #![warn(clippy::pedantic, clippy::nursery)] pub mod ir1; -pub mod preset; pub mod lower; +pub mod preset; diff --git a/package/origlang-ir-optimizer/src/lower.rs b/package/origlang-ir-optimizer/src/lower.rs index dd3fe532..474a48a0 100644 --- a/package/origlang-ir-optimizer/src/lower.rs +++ b/package/origlang-ir-optimizer/src/lower.rs @@ -1,10 +1,10 @@ +use crate::preset::OptimizationPreset; use origlang_ir::{CompiledTypedExpression, IntoVerbatimSequencedIR, IR1, IR2}; use origlang_typesystem_model::{TypedExpression, TypedStatement}; -use crate::preset::OptimizationPreset; // TODO: 最適化と診断の送信とloweringを一手に担う高レベルなAPIを用意する pub struct TheTranspiler<'t> { - pub optimization_preset: &'t dyn EachStep + pub optimization_preset: &'t dyn EachStep, } impl<'t> TheTranspiler<'t> { @@ -31,32 +31,53 @@ impl TheTranspiler<'_> { TypedExpression::BooleanLiteral(v) => CompiledTypedExpression::BooleanLiteral(v), TypedExpression::StringLiteral(v) => CompiledTypedExpression::StringLiteral(v), TypedExpression::UnitLiteral => CompiledTypedExpression::UnitLiteral, - TypedExpression::Variable { ident, tp } => CompiledTypedExpression::Variable { - ident, tp - }, - TypedExpression::BinaryOperator { lhs, rhs, operator, return_type } => CompiledTypedExpression::BinaryOperator { + TypedExpression::Variable { ident, tp } => { + CompiledTypedExpression::Variable { ident, tp } + } + TypedExpression::BinaryOperator { + lhs, + rhs, + operator, + return_type, + } => CompiledTypedExpression::BinaryOperator { lhs: Box::new(self.compile_typed_expression(*lhs)), rhs: Box::new(self.compile_typed_expression(*rhs)), operator, return_type, }, - TypedExpression::If { condition, then, els, return_type } => CompiledTypedExpression::If { + TypedExpression::If { + condition, + then, + els, + return_type, + } => CompiledTypedExpression::If { condition: Box::new(self.compile_typed_expression(*condition)), then: Box::new(self.compile_typed_expression(*then)), els: Box::new(self.compile_typed_expression(*els)), return_type, }, - TypedExpression::Block { inner, final_expression, return_type } => CompiledTypedExpression::Block { - inner: inner.into_iter().flat_map(|x| self.compile_typed_statement(x)).collect(), + TypedExpression::Block { + inner, + final_expression, + return_type, + } => CompiledTypedExpression::Block { + inner: inner + .into_iter() + .flat_map(|x| self.compile_typed_statement(x)) + .collect(), final_expression: Box::new(self.compile_typed_expression(*final_expression)), return_type, }, TypedExpression::Tuple { expressions } => CompiledTypedExpression::Tuple { - expressions: expressions.into_iter().map(|x| self.compile_typed_expression(x)).collect() + expressions: expressions + .into_iter() + .map(|x| self.compile_typed_expression(x)) + .collect(), }, TypedExpression::ExtractTuple { expr, index } => { CompiledTypedExpression::ExtractTuple { - expr: Box::new(self.compile_typed_expression(*expr)), index + expr: Box::new(self.compile_typed_expression(*expr)), + index, } } } @@ -76,13 +97,20 @@ pub trait LowerStep { impl LowerStep for TheTranspiler<'_> { fn lower(&self, ir: Vec) -> Vec { - ir.into_iter().map(|x| match x { - IR1::Output(expr) => IR2::Output(self.compile_typed_expression(expr)), - IR1::UpdateVariable { ident, value } => IR2::UpdateVariable { ident, value: self.compile_typed_expression(value) }, - IR1::PushScope => IR2::PushScope, - IR1::PopScope => IR2::PopScope, - IR1::Exit => IR2::Exit, - IR1::EvalAndForget { expression } => IR2::EvalAndForget { expression: self.compile_typed_expression(expression) } - }).collect() + ir.into_iter() + .map(|x| match x { + IR1::Output(expr) => IR2::Output(self.compile_typed_expression(expr)), + IR1::UpdateVariable { ident, value } => IR2::UpdateVariable { + ident, + value: self.compile_typed_expression(value), + }, + IR1::PushScope => IR2::PushScope, + IR1::PopScope => IR2::PopScope, + IR1::Exit => IR2::Exit, + IR1::EvalAndForget { expression } => IR2::EvalAndForget { + expression: self.compile_typed_expression(expression), + }, + }) + .collect() } } diff --git a/package/origlang-ir-optimizer/src/preset.rs b/package/origlang-ir-optimizer/src/preset.rs index 10d1158c..52d49b6c 100644 --- a/package/origlang-ir-optimizer/src/preset.rs +++ b/package/origlang-ir-optimizer/src/preset.rs @@ -1,5 +1,5 @@ -use tap::Pipe; use origlang_ir::{IR1, IR2}; +use tap::Pipe; pub trait OptimizationPreset { /// 最適化をする。 @@ -18,8 +18,8 @@ impl OptimizationPreset for NoOptimization { #[expect(unused_imports)] use crate::ir1::EliminateAfterExit; - seq - .pipe(EliminateAfterExit).pipe(EliminateAfterExit::optimize) + seq.pipe(EliminateAfterExit) + .pipe(EliminateAfterExit::optimize) } } @@ -38,11 +38,14 @@ impl OptimizationPreset for SimpleOptimization { #[expect(clippy::wildcard_imports)] use crate::ir1::*; - seq - .pipe(FoldBinaryOperatorInvocationWithConstant).pipe(|x| x.optimize()) - .pipe(FoldIfWithConstantCondition).pipe(|x| x.optimize()) - .pipe(InlineSimpleBlock).pipe(|x| x.optimize()) - .pipe(EliminateAfterExit).pipe(|x| x.optimize()) + seq.pipe(FoldBinaryOperatorInvocationWithConstant) + .pipe(|x| x.optimize()) + .pipe(FoldIfWithConstantCondition) + .pipe(|x| x.optimize()) + .pipe(InlineSimpleBlock) + .pipe(|x| x.optimize()) + .pipe(EliminateAfterExit) + .pipe(|x| x.optimize()) } } diff --git a/package/origlang-ir/src/lib.rs b/package/origlang-ir/src/lib.rs index 6169386d..fedf6444 100644 --- a/package/origlang-ir/src/lib.rs +++ b/package/origlang-ir/src/lib.rs @@ -1,10 +1,12 @@ #![deny(clippy::all, clippy::panicking_unwrap, clippy::panic)] #![warn(clippy::pedantic, clippy::nursery)] -use std::collections::VecDeque; use origlang_ast::after_parse::BinaryOperatorKind; use origlang_ast::Identifier; -use origlang_typesystem_model::{Type, TypedExpression, TypedIntLiteral, TypedRootAst, TypedStatement}; +use origlang_typesystem_model::{ + Type, TypedExpression, TypedIntLiteral, TypedRootAst, TypedStatement, +}; +use std::collections::VecDeque; pub trait IntoVerbatimSequencedIR { fn into_ir(self) -> Vec; @@ -16,21 +18,26 @@ impl IntoVerbatimSequencedIR for TypedStatement { match statement { Self::Print { expression } => { - vec![ - (IR1::Output(expression)) - ] + vec![(IR1::Output(expression))] } - Self::VariableDeclaration { identifier, expression } - | Self::VariableAssignment { identifier, expression } => { + Self::VariableDeclaration { + identifier, + expression, + } + | Self::VariableAssignment { + identifier, + expression, + } => { vec![ (IR1::UpdateVariable { ident: identifier, value: expression, - }) + }), ] } Self::Block { inner_statements } => { - let mut vec = inner_statements.into_iter() + let mut vec = inner_statements + .into_iter() .flat_map(::into_ir) .collect::>(); vec.push_front(IR1::PushScope); @@ -40,14 +47,15 @@ impl IntoVerbatimSequencedIR for TypedStatement { Self::Exit => vec![(IR1::Exit)], Self::EvalAndForget { expression } => { vec![(IR1::EvalAndForget { expression })] - }, + } } } } impl IntoVerbatimSequencedIR for TypedRootAst { fn into_ir(self) -> Vec { - self.statements.into_iter() + self.statements + .into_iter() .flat_map(::into_ir) .collect() } @@ -55,7 +63,9 @@ impl IntoVerbatimSequencedIR for TypedRootAst { impl IntoVerbatimSequencedIR for Vec { fn into_ir(self) -> Vec { - self.into_iter().flat_map(IntoVerbatimSequencedIR::into_ir).collect() + self.into_iter() + .flat_map(IntoVerbatimSequencedIR::into_ir) + .collect() } } @@ -70,8 +80,8 @@ pub enum IR1 { PopScope, Exit, EvalAndForget { - expression: TypedExpression - } + expression: TypedExpression, + }, } /// Same as [`IR1`], except that statements in blocks are lowered to this type. @@ -86,8 +96,8 @@ pub enum IR2 { PopScope, Exit, EvalAndForget { - expression: CompiledTypedExpression - } + expression: CompiledTypedExpression, + }, } #[derive(Eq, PartialEq, Debug, Clone)] @@ -110,7 +120,7 @@ pub enum CompiledTypedExpression { condition: Box, then: Box, els: Box, - return_type: Type + return_type: Type, }, Block { inner: Vec, @@ -118,7 +128,10 @@ pub enum CompiledTypedExpression { return_type: Type, }, Tuple { - expressions: Vec + expressions: Vec, + }, + ExtractTuple { + expr: Box, + index: usize, }, - ExtractTuple { expr: Box, index: usize }, } diff --git a/package/origlang-lexer/src/boundary.rs b/package/origlang-lexer/src/boundary.rs index be2e9b4c..0aa64781 100644 --- a/package/origlang-lexer/src/boundary.rs +++ b/package/origlang-lexer/src/boundary.rs @@ -52,7 +52,7 @@ impl From for Utf8CharStride { 3 => Self::Three, 4 => Self::Four, // SAFETY: this branch is actually not reachable because of res' range. - _ => unsafe { unreachable_unchecked() } + _ => unsafe { unreachable_unchecked() }, } } } diff --git a/package/origlang-lexer/src/error.rs b/package/origlang-lexer/src/error.rs index 379aefbe..dbb3a113 100644 --- a/package/origlang-lexer/src/error.rs +++ b/package/origlang-lexer/src/error.rs @@ -1,22 +1,22 @@ +use crate::boundary::Utf8CharBoundaryStartByte; use std::convert::Infallible; use thiserror::Error; -use crate::boundary::Utf8CharBoundaryStartByte; #[derive(Error, Debug, Eq, PartialEq)] #[expect(clippy::module_name_repetitions)] pub enum LexerError { - #[error("Invalid suffix for integer literal. Supported suffixes are [`i8`, `i16`, `i32`, `i64`]")] + #[error( + "Invalid suffix for integer literal. Supported suffixes are [`i8`, `i16`, `i32`, `i64`]" + )] InvalidSuffix, #[error("Internal compiler error: {0}")] OutOfRange(#[from] OutOfRangeError), #[error("Unclosed string literal was found")] UnclosedStringLiteral, #[error("Input is malformed UTF-8")] - MalformedAsUtf8 { - boundary: Utf8CharBoundaryStartByte, - }, + MalformedAsUtf8 { boundary: Utf8CharBoundaryStartByte }, #[error("never: {0}")] - Never(#[from] Infallible) + Never(#[from] Infallible), } #[derive(Debug, Error, Eq, PartialEq)] diff --git a/package/origlang-lexer/src/lib.rs b/package/origlang-lexer/src/lib.rs index 3e62e229..d797139b 100644 --- a/package/origlang-lexer/src/lib.rs +++ b/package/origlang-lexer/src/lib.rs @@ -1,34 +1,40 @@ #![deny(clippy::all)] #![warn(clippy::pedantic, clippy::nursery)] +mod boundary; pub mod error; #[cfg(test)] mod tests; pub mod token; -mod boundary; use std::cell::Cell; -use std::num::NonZeroUsize; -use log::{debug, trace, warn}; use self::error::LexerError; -use origlang_ast::{Comment, Identifier}; -use origlang_source_span::{SourcePosition as SourcePos, Pointed as WithPosition}; use crate::boundary::{Utf8CharBoundaryStartByte, Utf8CharStride}; use crate::error::OutOfRangeError; use crate::token::{TemporalLexerUnwindToken, Token}; +use log::{debug, trace, warn}; +use origlang_ast::{Comment, Identifier}; +use origlang_source_span::{Pointed as WithPosition, SourcePosition as SourcePos}; +use std::num::NonZeroUsize; -static KEYWORDS: [&str; 12] = - ["var", "if", "else", "then", "exit", "true", "false", "print", "block", "end", "type", "_"]; +static KEYWORDS: [&str; 12] = [ + "var", "if", "else", "then", "exit", "true", "false", "print", "block", "end", "type", "_", +]; trait AssociateWithPos { - fn with_pos(self, lexer: &Lexer) -> WithPosition where Self: Sized; + fn with_pos(self, lexer: &Lexer) -> WithPosition + where + Self: Sized; } impl AssociateWithPos for T { - fn with_pos(self, lexer: &Lexer) -> WithPosition where Self: Sized { + fn with_pos(self, lexer: &Lexer) -> WithPosition + where + Self: Sized, + { WithPosition { position: lexer.current_pos(), - data: self + data: self, } } } @@ -60,7 +66,7 @@ impl<'src> Lexer<'src> { line: Cell::new(1.try_into().expect("unreachable!!")), #[expect(clippy::missing_panics_doc)] column: Cell::new(1.try_into().expect("unreachable!!")), - } + }, } } } @@ -72,7 +78,7 @@ impl Lexer<'_> { while !self.reached_end() { if self.try_and_eat_str(" ") || self.try_and_eat_str("\t") { } else { - break + break; } } trace!("drain_space: end ^^^^^^^^^^^^^^^^^^^"); @@ -84,19 +90,22 @@ impl Lexer<'_> { trace!("lexer:try:{s:?}"); let start = self.source_bytes_nth.get(); let end_exclusive = start.as_usize() + s.len(); - let Some(b) = self.source.get(start.as_usize()..end_exclusive) else { return false }; + let Some(b) = self.source.get(start.as_usize()..end_exclusive) else { + return false; + }; if s != b { - return false + return false; } self.set_current_index(Utf8CharBoundaryStartByte::new(end_exclusive)); - + true } fn next_inner(&self) -> Token { - let v = - self.reached_end().then_some(Token::EndOfFile) + let v = self + .reached_end() + .then_some(Token::EndOfFile) .or_else(|| self.try_and_eat_str("\r\n").then_some(Token::NewLine)) .or_else(|| self.try_and_eat_str("\n").then_some(Token::NewLine)) .or_else(|| self.try_and_eat_str("==").then_some(Token::PartEqEq)) @@ -117,38 +126,38 @@ impl Lexer<'_> { .or_else(|| self.try_and_eat_str(">").then_some(Token::SymMore)) .or_else(|| self.try_and_eat_str("!=").then_some(Token::PartBangEq)) .or_else(|| self.try_and_eat_str("!").then_some(Token::SymBang)) - .or_else(|| self.try_and_eat_str("\"").then(|| self.scan_string_literal())) + .or_else(|| { + self.try_and_eat_str("\"") + .then(|| self.scan_string_literal()) + }) .or_else(|| self.scan_digits()) .or_else(|| self.try_and_eat_str(",").then_some(Token::SymComma)) .or_else(|| self.try_and_eat_str(":").then_some(Token::SymColon)) .or_else(|| { - self.scan_identifier() - .ok() - .flatten() - .map(|scanned| { - let is_keyword = KEYWORDS.contains(&scanned.as_name()); - if is_keyword { - match scanned.as_name() { - "var" => Token::VarKeyword, - "true" => Token::KeywordTrue, - "false" => Token::KeywordFalse, - "if" => Token::KeywordIf, - "then" => Token::KeywordThen, - "else" => Token::KeywordElse, - "print" => Token::KeywordPrint, - "block" => Token::KeywordBlock, - "end" => Token::KeywordEnd, - "exit" => Token::KeywordExit, - "type" => Token::KeywordType, - "_" => Token::SymUnderscore, - other => Token::Reserved { - matched: other.to_string(), - } - } - } else { - Token::Identifier { inner: scanned } + self.scan_identifier().ok().flatten().map(|scanned| { + let is_keyword = KEYWORDS.contains(&scanned.as_name()); + if is_keyword { + match scanned.as_name() { + "var" => Token::VarKeyword, + "true" => Token::KeywordTrue, + "false" => Token::KeywordFalse, + "if" => Token::KeywordIf, + "then" => Token::KeywordThen, + "else" => Token::KeywordElse, + "print" => Token::KeywordPrint, + "block" => Token::KeywordBlock, + "end" => Token::KeywordEnd, + "exit" => Token::KeywordExit, + "type" => Token::KeywordType, + "_" => Token::SymUnderscore, + other => Token::Reserved { + matched: other.to_string(), + }, } - }) + } else { + Token::Identifier { inner: scanned } + } + }) }) // dont eager evaluate .unwrap_or_else(|| { @@ -157,32 +166,33 @@ impl Lexer<'_> { let index = current_boundary.as_usize(); let stride = this.current_char_stride()?; - - let s = unsafe { this.source.get_unchecked(index..(index + stride.as_usize())) }; + let s = unsafe { + this.source + .get_unchecked(index..(index + stride.as_usize())) + }; #[expect(clippy::or_fun_call)] // latter is fine because it does not cost let c = s.chars().next().ok_or(this.report_out_of_range_error())?; - Ok(c) } - + Token::UnexpectedChar { // TODO: this is cold path, so may convert boundary to char_nth. index: self.source_bytes_nth.get(), char: current_char(self).expect("unexpected_char"), } }); - + v } pub fn next(&self) -> WithPosition { debug!("-------------------------------------------------"); self.drain_space(); - + if self.reached_end() { - return Token::EndOfFile.with_pos(self) + return Token::EndOfFile.with_pos(self); } let pos = self.current_pos(); @@ -205,14 +215,14 @@ impl Lexer<'_> { fn scan_digit_suffix_opt(&self) -> Option<&str> { if self.reached_end() { - return None + return None; } for s in ["i8", "i16", "i32", "i64"] { let a = self.try_and_eat_str(s); if a { - return Some(s) + return Some(s); } } @@ -230,10 +240,10 @@ impl Lexer<'_> { if b.is_ascii_digit() { plus += 1; } else { - break + break; } } else { - break + break; } } @@ -245,23 +255,24 @@ impl Lexer<'_> { self.set_current_index(Utf8CharBoundaryStartByte::new(end_inclusive)); let scanned = self.source[start..end_inclusive].to_string(); - let builtin_suffix = self.scan_digit_suffix_opt().map(|s| s.to_string().into_boxed_str()); + let builtin_suffix = self + .scan_digit_suffix_opt() + .map(|s| s.to_string().into_boxed_str()); debug!("digit: done ({scanned} {builtin_suffix:?})"); - Some( - Token::Digits { - sequence: scanned, - suffix: builtin_suffix, - } - ) + Some(Token::Digits { + sequence: scanned, + suffix: builtin_suffix, + }) } - } fn scan_string_literal(&self) -> Token { let start = self.source_bytes_nth.get().as_usize(); - let rel_pos = self.source[start..].find('"').unwrap_or(self.source.len() - start); + let rel_pos = self.source[start..] + .find('"') + .unwrap_or(self.source.len() - start); self.advance_bytes(rel_pos + 1); let s = self.source[start..(start + rel_pos)].to_string(); @@ -269,7 +280,9 @@ impl Lexer<'_> { } fn advance_bytes(&self, advance: usize) { - self.set_current_index(Utf8CharBoundaryStartByte::new(self.source_bytes_nth.get().as_usize() + advance)); + self.set_current_index(Utf8CharBoundaryStartByte::new( + self.source_bytes_nth.get().as_usize() + advance, + )); } #[inline(never)] @@ -279,7 +292,7 @@ impl Lexer<'_> { debug!("index: {old} -> {future_index:?}"); if old == new { - return + return; } let current_line = self.line().get(); @@ -288,14 +301,15 @@ impl Lexer<'_> { if old < new { // forward let new_line = current_line + src[old..new].bytes().filter(|x| *x == b'\n').count(); - let new_col = src[old..new].rfind('\n').map_or_else(|| { - let mut c = self.column().get(); - c += new - old; + let new_col = src[old..new].rfind('\n').map_or_else( + || { + let mut c = self.column().get(); + c += new - old; - c - }, |old_relative| { - new - (old + old_relative) - }); + c + }, + |old_relative| new - (old + old_relative), + ); self.set_line(NonZeroUsize::new(new_line).expect("overflow")); self.set_column(NonZeroUsize::new(new_col).expect("overflow")); @@ -303,26 +317,29 @@ impl Lexer<'_> { // THIS BRANCH IS IMPORTANT!!! OTHERWISE, RESET OPERATION WILL NOT WORK!!! // back let new_line = current_line - src[new..old].bytes().filter(|x| *x == b'\n').count(); - let new_col = src[new..old].find('\n').map_or_else(|| { - let mut c = self.column().get(); - c -= old - new; - - c - }, |new_relative| { - // .......................NEW.................OLD - // |<--------N------>| - let nr = new + new_relative; - src[..nr].rfind('\n').map_or(nr, |most_recent_nl| { - // ..............NEW.................OLD - // |<--------N------>| - // |<-----MRN-------------->| - - // this is effectively static assertion, should not - // cost on runtime. - assert!(most_recent_nl < nr); - nr - most_recent_nl - }) - }); + let new_col = src[new..old].find('\n').map_or_else( + || { + let mut c = self.column().get(); + c -= old - new; + + c + }, + |new_relative| { + // .......................NEW.................OLD + // |<--------N------>| + let nr = new + new_relative; + src[..nr].rfind('\n').map_or(nr, |most_recent_nl| { + // ..............NEW.................OLD + // |<--------N------>| + // |<-----MRN-------------->| + + // this is effectively static assertion, should not + // cost on runtime. + assert!(most_recent_nl < nr); + nr - most_recent_nl + }) + }, + ); self.set_line(NonZeroUsize::new(new_line).expect("overflow: line")); self.set_column(NonZeroUsize::new(new_col).expect("overflow: col")); @@ -338,9 +355,7 @@ impl Lexer<'_> { let content = self.source[start..(start + rel_pos)].to_string(); Token::Comment { - content: Comment { - content - } + content: Comment { content }, } } @@ -360,7 +375,7 @@ impl Lexer<'_> { } else { return Err(LexerError::MalformedAsUtf8 { boundary: current_boundary, - }) + }); }; Ok(stride) @@ -408,12 +423,12 @@ impl Lexer<'_> { if b.is_ascii_alphanumeric() || b == b'_' { plus += 1; } else { - break + break; } } Err(e) => { warn!("discarding error: {e}"); - break + break; } } } @@ -430,11 +445,19 @@ impl Lexer<'_> { } fn current_byte(&self) -> Result { - self.source.as_bytes().get(self.source_bytes_nth.get().as_usize()).copied().ok_or_else(|| self.report_out_of_range_error()) + self.source + .as_bytes() + .get(self.source_bytes_nth.get().as_usize()) + .copied() + .ok_or_else(|| self.report_out_of_range_error()) } fn byte_skip_n(&self, skip: usize) -> Result { - self.source.as_bytes().get(self.source_bytes_nth.get().as_usize() + skip).copied().ok_or_else(|| self.report_out_of_range_error()) + self.source + .as_bytes() + .get(self.source_bytes_nth.get().as_usize() + skip) + .copied() + .ok_or_else(|| self.report_out_of_range_error()) } fn report_out_of_range_error(&self) -> LexerError { @@ -443,20 +466,20 @@ impl Lexer<'_> { max: self.source.len(), }) } - + fn line(&self) -> NonZeroUsize { self.lc_manager.line.get() } - + fn set_line(&self, line: NonZeroUsize) { debug!("line: {old} -> {new}", old = self.line(), new = line); self.lc_manager.line.set(line); } - + fn column(&self) -> NonZeroUsize { self.lc_manager.column.get() } - + fn set_column(&self, column: NonZeroUsize) { debug!("column: {old} -> {new}", old = self.column(), new = column); self.lc_manager.column.set(column); diff --git a/package/origlang-lexer/src/tests.rs b/package/origlang-lexer/src/tests.rs index 1d3373cb..9f291a6d 100644 --- a/package/origlang-lexer/src/tests.rs +++ b/package/origlang-lexer/src/tests.rs @@ -1,14 +1,17 @@ -use origlang_ast::Identifier; use crate::{Lexer, Token}; +use origlang_ast::Identifier; fn test(str_lit: &str) { let src = format!("var x = \"{str_lit}\"\n"); let p = Lexer::create(&src); assert_eq!(p.next().data, Token::VarKeyword); - assert_eq!(p.next().data, Token::Identifier { - inner: Identifier::new("x".to_string()), - }); + assert_eq!( + p.next().data, + Token::Identifier { + inner: Identifier::new("x".to_string()), + } + ); assert_eq!(p.next().data, Token::SymEq); assert_eq!(p.next().data, Token::StringLiteral(str_lit.to_string())); } @@ -93,8 +96,8 @@ fn parse_string_literal_mixed_4_3() { test("\u{10000}あ"); } -use origlang_source_span::{Pointed, SourcePosition}; use crate::boundary::Utf8CharBoundaryStartByte; +use origlang_source_span::{Pointed, SourcePosition}; #[test] fn token_location() { @@ -102,70 +105,100 @@ fn token_location() { let src = "var x = 1\nvar y = 2\n"; let lexer = Lexer::create(src); - assert_eq!(lexer.next(), Pointed { - data: Token::VarKeyword, - position: SourcePosition::try_new((1, 1)).unwrap(), - }); - - assert_eq!(lexer.next(), Pointed { - data: Token::Identifier { - inner: Identifier::new("x".to_string()) - }, - position: SourcePosition::try_new((1, 5)).unwrap() - }); - - assert_eq!(lexer.next(), Pointed { - data: Token::SymEq, - position: SourcePosition::try_new((1, 7)).unwrap() - }); - - assert_eq!(lexer.next(), Pointed { - data: Token::Digits { - sequence: "1".to_string(), - suffix: None, - }, - position: SourcePosition::try_new((1, 9)).unwrap() - }); - - assert_eq!(lexer.next(), Pointed { - data: Token::NewLine, - position: SourcePosition::try_new((1, 10)).unwrap() - }); - - assert_eq!(lexer.next(), Pointed { - data: Token::VarKeyword, - position: SourcePosition::try_new((2, 1)).unwrap() - }); - - assert_eq!(lexer.next(), Pointed { - data: Token::Identifier { - inner: Identifier::new("y".to_string()) - }, - position: SourcePosition::try_new((2, 5)).unwrap() - }); - - assert_eq!(lexer.next(), Pointed { - data: Token::SymEq, - position: SourcePosition::try_new((2, 7)).unwrap() - }); - - assert_eq!(lexer.next(), Pointed { - data: Token::Digits { - sequence: "2".to_string(), - suffix: None, - }, - position: SourcePosition::try_new((2, 9)).unwrap() - }); + assert_eq!( + lexer.next(), + Pointed { + data: Token::VarKeyword, + position: SourcePosition::try_new((1, 1)).unwrap(), + } + ); + + assert_eq!( + lexer.next(), + Pointed { + data: Token::Identifier { + inner: Identifier::new("x".to_string()) + }, + position: SourcePosition::try_new((1, 5)).unwrap() + } + ); + + assert_eq!( + lexer.next(), + Pointed { + data: Token::SymEq, + position: SourcePosition::try_new((1, 7)).unwrap() + } + ); + + assert_eq!( + lexer.next(), + Pointed { + data: Token::Digits { + sequence: "1".to_string(), + suffix: None, + }, + position: SourcePosition::try_new((1, 9)).unwrap() + } + ); + + assert_eq!( + lexer.next(), + Pointed { + data: Token::NewLine, + position: SourcePosition::try_new((1, 10)).unwrap() + } + ); + + assert_eq!( + lexer.next(), + Pointed { + data: Token::VarKeyword, + position: SourcePosition::try_new((2, 1)).unwrap() + } + ); + + assert_eq!( + lexer.next(), + Pointed { + data: Token::Identifier { + inner: Identifier::new("y".to_string()) + }, + position: SourcePosition::try_new((2, 5)).unwrap() + } + ); + + assert_eq!( + lexer.next(), + Pointed { + data: Token::SymEq, + position: SourcePosition::try_new((2, 7)).unwrap() + } + ); + + assert_eq!( + lexer.next(), + Pointed { + data: Token::Digits { + sequence: "2".to_string(), + suffix: None, + }, + position: SourcePosition::try_new((2, 9)).unwrap() + } + ); } #[test] fn digit_regression() { const D: &str = "123456"; let lexer = Lexer::create(D); - assert_eq!(lexer.next().data, Token::Digits { - sequence: D.to_string(), - suffix: None, - }); + assert_eq!( + lexer.next().data, + Token::Digits { + sequence: D.to_string(), + suffix: None, + } + ); const EMPTY: &str = ""; let lexer = Lexer::create(EMPTY); @@ -184,49 +217,70 @@ fn crlf_positive() { fn crlf_negative() { const S: &str = "\r"; let lexer = Lexer::create(S); - assert_eq!(lexer.next().data, Token::UnexpectedChar { - index: Utf8CharBoundaryStartByte::new(0), - char: '\r', - }); + assert_eq!( + lexer.next().data, + Token::UnexpectedChar { + index: Utf8CharBoundaryStartByte::new(0), + char: '\r', + } + ); } #[test] fn off_by_one_range_regression() { const S: &str = "9"; let lexer = Lexer::create(S); - assert_eq!(lexer.next(), Pointed { - data: Token::Digits { - sequence: "9".to_string(), - suffix: None, - }, - position: SourcePosition::try_new((1, 1)).unwrap() - }); + assert_eq!( + lexer.next(), + Pointed { + data: Token::Digits { + sequence: "9".to_string(), + suffix: None, + }, + position: SourcePosition::try_new((1, 1)).unwrap() + } + ); } #[test] fn skip_whitespace_only_lines() { - let lexer = Lexer::create(" \n \n \nprint 1"); - assert_eq!(lexer.next(), Pointed { - data: Token::NewLine, - position: SourcePosition::try_new((1, 5)).unwrap() - }); - assert_eq!(lexer.next(), Pointed { - data: Token::NewLine, - position: SourcePosition::try_new((2, 5)).unwrap() - }); - assert_eq!(lexer.next(), Pointed { - data: Token::NewLine, - position: SourcePosition::try_new((3, 5)).unwrap() - }); - assert_eq!(lexer.next(), Pointed { - data: Token::KeywordPrint, - position: SourcePosition::try_new((4, 1)).unwrap() - }); - assert_eq!(lexer.next(), Pointed { - data: Token::Digits { - sequence: "1".to_string(), - suffix: None, - }, - position: SourcePosition::try_new((4, 7)).unwrap(), - }); + let lexer = Lexer::create(" \n \n \nprint 1"); + assert_eq!( + lexer.next(), + Pointed { + data: Token::NewLine, + position: SourcePosition::try_new((1, 5)).unwrap() + } + ); + assert_eq!( + lexer.next(), + Pointed { + data: Token::NewLine, + position: SourcePosition::try_new((2, 5)).unwrap() + } + ); + assert_eq!( + lexer.next(), + Pointed { + data: Token::NewLine, + position: SourcePosition::try_new((3, 5)).unwrap() + } + ); + assert_eq!( + lexer.next(), + Pointed { + data: Token::KeywordPrint, + position: SourcePosition::try_new((4, 1)).unwrap() + } + ); + assert_eq!( + lexer.next(), + Pointed { + data: Token::Digits { + sequence: "1".to_string(), + suffix: None, + }, + position: SourcePosition::try_new((4, 7)).unwrap(), + } + ); } diff --git a/package/origlang-lexer/src/token.rs b/package/origlang-lexer/src/token.rs index 962a7ec5..c0a1ddcf 100644 --- a/package/origlang-lexer/src/token.rs +++ b/package/origlang-lexer/src/token.rs @@ -1,7 +1,7 @@ -use origlang_ast::{Comment, Identifier}; use crate::boundary::Utf8CharBoundaryStartByte; -use crate::Lexer; use crate::token::internal::DisplayToken; +use crate::Lexer; +use origlang_ast::{Comment, Identifier}; pub struct TemporalLexerUnwindToken { unwind_index: Utf8CharBoundaryStartByte, @@ -11,7 +11,7 @@ impl TemporalLexerUnwindToken { #[must_use = "call Self::reset to invoke"] pub const fn new(reset_to: Utf8CharBoundaryStartByte) -> Self { Self { - unwind_index: reset_to + unwind_index: reset_to, } } @@ -27,7 +27,7 @@ pub enum Token { }, Digits { sequence: String, - suffix: Option> + suffix: Option>, }, UnexpectedChar { index: Utf8CharBoundaryStartByte, diff --git a/package/origlang-parser/src/error.rs b/package/origlang-parser/src/error.rs index 430e89c1..9bf12a06 100644 --- a/package/origlang-parser/src/error.rs +++ b/package/origlang-parser/src/error.rs @@ -1,12 +1,12 @@ -use thiserror::Error as ThisError; -use origlang_source_span::{Pointed, SourcePosition as SourcePos, SourcePosition}; -use std::fmt::{Display, Formatter}; +use crate::parser::TokenKind; +use crate::recover::{IntermediateStateCandidate, PartiallyParseFixCandidate}; use derive_more::Display; -use std::num::ParseIntError; use origlang_lexer::error::LexerError; use origlang_lexer::token::Token; -use crate::parser::TokenKind; -use crate::recover::{IntermediateStateCandidate, PartiallyParseFixCandidate}; +use origlang_source_span::{Pointed, SourcePosition as SourcePos, SourcePosition}; +use std::fmt::{Display, Formatter}; +use std::num::ParseIntError; +use thiserror::Error as ThisError; #[derive(ThisError, Debug, Eq, PartialEq)] pub struct ParserError(Pointed); @@ -22,7 +22,7 @@ impl ParserError { pub const fn new(kind: ParserErrorInner, position: SourcePos) -> Self { Self(Pointed { data: kind, - position + position, }) } @@ -43,27 +43,20 @@ pub enum ParserErrorInner { #[error("lexer error: {_0}")] LexerError(#[from] LexerError), #[error("unconsumed token found: {token:?}")] - UnconsumedToken { - token: Token - }, + UnconsumedToken { token: Token }, #[error("statement must be terminated by a newline")] StatementTerminationError, #[error("EOF Error")] EndOfFileError, #[error("Expected {pat}, but got {unmatch:?}")] - UnexpectedToken { - pat: TokenKind, - unmatch: Token, - }, + UnexpectedToken { pat: TokenKind, unmatch: Token }, #[error("Incomplete program snippet. Check hint for fix candidates. hint:{hint:?} state:{intermediate_state:?}")] PartiallyParsed { hint: Vec, intermediate_state: Vec, }, #[error("input sequence cannot be parsed as a int literal: {error}")] - UnParsableIntLiteral { - error: ParseIntError - }, + UnParsableIntLiteral { error: ParseIntError }, #[error("int literal type of {tp} must be in range ({min}..={max}), but its value is {value}")] OverflowedLiteral { tp: Box, diff --git a/package/origlang-parser/src/lib.rs b/package/origlang-parser/src/lib.rs index 9989110e..2911f360 100644 --- a/package/origlang-parser/src/lib.rs +++ b/package/origlang-parser/src/lib.rs @@ -1,6 +1,6 @@ #![deny(clippy::all)] #![warn(clippy::pedantic, clippy::nursery)] -pub mod parser; pub mod error; +pub mod parser; pub mod recover; diff --git a/package/origlang-parser/src/parser.rs b/package/origlang-parser/src/parser.rs index f541c0e2..b5a2006d 100644 --- a/package/origlang-parser/src/parser.rs +++ b/package/origlang-parser/src/parser.rs @@ -1,19 +1,21 @@ use origlang_ast::{AtomicPattern, RootAst, Statement, TypeSignature}; -use origlang_source_span::{Pointed as WithPosition, Pointed, SourcePosition as SourcePos, SourcePosition}; -use origlang_lexer::Lexer; -use origlang_lexer::token::Token; use origlang_lexer::token::internal::DisplayToken; +use origlang_lexer::token::Token; +use origlang_lexer::Lexer; +use origlang_source_span::{ + Pointed as WithPosition, Pointed, SourcePosition as SourcePos, SourcePosition, +}; -use origlang_ast::after_parse::{BinaryOperatorKind, Expression}; -use std::string::ToString; +use crate::error::ParserErrorInner::EndOfFileError; +use crate::error::{ParserError, ParserErrorInner, UnexpectedTupleLiteralElementCount}; +use crate::parser::TokenKind::IntLiteral; +use crate::recover::PartiallyParseFixCandidate; use derive_more::Display; use log::{debug, warn}; use num_traits::Bounded; -use crate::error::{ParserError, ParserErrorInner, UnexpectedTupleLiteralElementCount}; -use crate::error::ParserErrorInner::EndOfFileError; -use crate::recover::PartiallyParseFixCandidate; -use crate::parser::TokenKind::IntLiteral; +use origlang_ast::after_parse::{BinaryOperatorKind, Expression}; use origlang_token_stream::TokenStream; +use std::string::ToString; #[derive(Display, Debug, Eq, PartialEq, Clone)] pub enum TokenKind { @@ -48,7 +50,7 @@ impl TokenKind { } pub struct Parser { - lexer: TokenStream + lexer: TokenStream, } impl Parser { @@ -57,10 +59,10 @@ impl Parser { pub fn create(source: &str) -> Self { Self::new(Lexer::create(source).into()) } - + pub const fn new(token_stream: TokenStream) -> Self { Self { - lexer: token_stream + lexer: token_stream, } } } @@ -72,19 +74,30 @@ impl Parser { /// プログラムのパースに失敗したときErr。 pub fn parse(&self) -> Result { let mut statements = vec![]; - while self.lexer.peek().is_some_and(|x| x.data != Token::EndOfFile) { + while self + .lexer + .peek() + .is_some_and(|x| x.data != Token::EndOfFile) + { let res = self.parse_statement()?; statements.push(res); } { - let t = self.lexer.peek().unwrap_or(&self.lexer.end_of_file_token()).clone(); + let t = self + .lexer + .peek() + .unwrap_or(&self.lexer.end_of_file_token()) + .clone(); self.lexer.next(); match t.data { Token::EndOfFile | Token::NewLine => Ok(RootAst { statement: statements, }), - other => Err(ParserError::new(ParserErrorInner::UnconsumedToken { token: other }, t.position)), + other => Err(ParserError::new( + ParserErrorInner::UnconsumedToken { token: other }, + t.position, + )), } } } @@ -96,35 +109,31 @@ impl Parser { } if self.lexer.peek().is_none() { - return Ok(Statement::Exit) + return Ok(Statement::Exit); } - let head1 = self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?; + let head1 = self.lexer.peek().ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })?; let head = &head1.data; let pos = head1.position; let s = match head { - Token::Identifier { .. } => { - self.parse_variable_assignment()? - } - Token::VarKeyword => { - self.parse_variable_declaration()? - } + Token::Identifier { .. } => self.parse_variable_assignment()?, + Token::VarKeyword => self.parse_variable_declaration()?, Token::KeywordPrint => { // assuming expression self.lexer.next(); let expr = self.parse_lowest_precedence_expression()?; - Statement::Print { - expression: expr - } - } - Token::KeywordBlock => { - self.parse_block_scope()? + Statement::Print { expression: expr } } + Token::KeywordBlock => self.parse_block_scope()?, Token::Comment { content } => { self.lexer.next(); - Statement::Comment { content: content.clone() } + Statement::Comment { + content: content.clone(), + } } Token::KeywordExit => { self.lexer.next(); @@ -136,15 +145,30 @@ impl Parser { self.lexer.next(); let Some(Token::Identifier { inner: aliased }) = aliased.map(|x| &x.data) else { - let unmatch = aliased.map(|x| &x.data).cloned().unwrap_or(Token::EndOfFile); + let unmatch = aliased + .map(|x| &x.data) + .cloned() + .unwrap_or(Token::EndOfFile); let position = aliased.map_or(self.lexer.last_position, |x| x.position); - return Err(Self::create_unexpected_token_error(TokenKind::Identifier, unmatch, position)); + return Err(Self::create_unexpected_token_error( + TokenKind::Identifier, + unmatch, + position, + )); }; self.read_and_consume_or_report_unexpected_token(&Token::SymEq)?; let Ok(replace_with) = self.lexer.parse_fallible(|| self.parse_type()) else { - let p = self.lexer.peek().unwrap_or(&self.lexer.end_of_file_token()).clone(); - return Err(Self::create_unexpected_token_error(TokenKind::StartOfTypeSignature, p.data, p.position)); + let p = self + .lexer + .peek() + .unwrap_or(&self.lexer.end_of_file_token()) + .clone(); + return Err(Self::create_unexpected_token_error( + TokenKind::StartOfTypeSignature, + p.data, + p.position, + )); }; Statement::TypeAliasDeclaration { @@ -153,23 +177,28 @@ impl Parser { } } x => { - return Err(Self::create_unexpected_token_error(TokenKind::Statement, x.clone(), pos)) + return Err(Self::create_unexpected_token_error( + TokenKind::Statement, + x.clone(), + pos, + )) } }; // 文は絶対に改行かEOFで終わる必要がある let next = self.lexer.peek(); self.lexer.next(); - + if next.map(|x| &x.data) != Some(&Token::NewLine) && next.is_some() { - Err(ParserError::new(ParserErrorInner::PartiallyParsed { - hint: vec![ - PartiallyParseFixCandidate::InsertAfter { - tokens: vec![ Token::NewLine ] - } - ], + Err(ParserError::new( + ParserErrorInner::PartiallyParsed { + hint: vec![PartiallyParseFixCandidate::InsertAfter { + tokens: vec![Token::NewLine], + }], intermediate_state: vec![], - }, next.map_or(self.lexer.last_position, |x| x.position))) + }, + next.map_or(self.lexer.last_position, |x| x.position), + )) } else { Ok(s) } @@ -180,34 +209,50 @@ impl Parser { /// 違反した場合はErr。 fn parse_first(&self) -> Result { debug!("expr:first"); - let token = self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?; - + let token = self.lexer.peek().ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })?; + match &token.data { Token::Identifier { inner } => { // consume self.lexer.next(); Ok(Expression::Variable { - ident: inner.clone() + ident: inner.clone(), }) } Token::SymUnderscore => { self.lexer.next(); - Err(ParserError::new(ParserErrorInner::UnderscoreCanNotBeRightHandExpression, token.position,)) + Err(ParserError::new( + ParserErrorInner::UnderscoreCanNotBeRightHandExpression, + token.position, + )) } Token::Digits { .. } => { - self.parse_int_literal().map(|(parsed, suffix)| { - Expression::IntLiteral { value: parsed, suffix } - }) + self.parse_int_literal() + .map(|(parsed, suffix)| Expression::IntLiteral { + value: parsed, + suffix, + }) } Token::SymLeftPar => { - assert_eq!(self.lexer.peek().map(|x| &x.data), Some(&Token::SymLeftPar)); self.lexer.next(); // FIXME: (1 == 2)を受け付けない - if self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?.data == Token::SymRightPar { + if self + .lexer + .peek() + .ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })? + .data + == Token::SymRightPar + { self.lexer.next(); Ok(Expression::UnitLiteral) - } else if let Ok(expr_tuple) = self.lexer.parse_fallible(|| self.parse_tuple_expression()) { + } else if let Ok(expr_tuple) = + self.lexer.parse_fallible(|| self.parse_tuple_expression()) + { Ok(expr_tuple) } else { let inner_expression = self.parse_lowest_precedence_expression()?; @@ -223,18 +268,19 @@ impl Parser { self.lexer.next(); Ok(Expression::BooleanLiteral(false)) } - Token::EndOfFile => { - Err(ParserError::new(EndOfFileError, token.position,)) - } + Token::EndOfFile => Err(ParserError::new(EndOfFileError, token.position)), Token::StringLiteral(s) => { self.lexer.next(); Ok(Expression::StringLiteral(s.clone())) } - e => Err(Self::create_unexpected_token_error(TokenKind::First, e.clone(), token.position)) + e => Err(Self::create_unexpected_token_error( + TokenKind::First, + e.clone(), + token.position, + )), } } - fn parse_tuple_expression(&self) -> Result { self.lexer.parse_fallible(|| { debug!("expr:tuple"); @@ -242,12 +288,18 @@ impl Parser { let mut buf = vec![]; while let Ok(e) = self.parse_lowest_precedence_expression() { buf.push(e); - let peek = self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?; + let peek = self.lexer.peek().ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })?; if peek.data == Token::SymRightPar { self.lexer.next(); - break + break; } else if peek.data != Token::SymComma { - return Err(Self::create_unexpected_token_error(TokenKind::Only(Token::SymComma.display()), peek.data.clone(), peek.position)) + return Err(Self::create_unexpected_token_error( + TokenKind::Only(Token::SymComma.display()), + peek.data.clone(), + peek.position, + )); } self.lexer.next(); @@ -257,15 +309,39 @@ impl Parser { if bl == 0 { // disallow () - return Err(ParserError::new(ParserErrorInner::InsufficientElementsForTupleLiteral(UnexpectedTupleLiteralElementCount::Zero), self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?.position,)) + return Err(ParserError::new( + ParserErrorInner::InsufficientElementsForTupleLiteral( + UnexpectedTupleLiteralElementCount::Zero, + ), + self.lexer + .peek() + .ok_or_else(|| { + ParserError::new( + ParserErrorInner::EndOfFileError, + self.lexer.last_position, + ) + })? + .position, + )); } else if bl == 1 { // disallow (expr) - return Err(ParserError::new(ParserErrorInner::InsufficientElementsForTupleLiteral(UnexpectedTupleLiteralElementCount::One), self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?.position,)) - } - - Ok(Expression::Tuple { - expressions: buf - }) + return Err(ParserError::new( + ParserErrorInner::InsufficientElementsForTupleLiteral( + UnexpectedTupleLiteralElementCount::One, + ), + self.lexer + .peek() + .ok_or_else(|| { + ParserError::new( + ParserErrorInner::EndOfFileError, + self.lexer.last_position, + ) + })? + .position, + )); + } + + Ok(Expression::Tuple { expressions: buf }) }) } /// 現在のトークン位置から乗除算をパースする。 @@ -273,12 +349,13 @@ impl Parser { debug!("expr:mul"); let first_term = self.parse_first()?; if self.lexer.peek().is_none() { - return Ok(first_term) + return Ok(first_term); } - let next_token = self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?; - let asterisk_or_slash = |token: &Token| { - token == &Token::SymAsterisk || token == &Token::SymSlash - }; + let next_token = self.lexer.peek().ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })?; + let asterisk_or_slash = + |token: &Token| token == &Token::SymAsterisk || token == &Token::SymSlash; if asterisk_or_slash(&next_token.data) { // SymAsterisk | SymSlash @@ -286,17 +363,20 @@ impl Parser { let operator_token = next_token; let lhs = first_term; let rhs = self.parse_first()?; - let get_operator_from_token = |token: &WithPosition| { - match &token.data { - Token::SymAsterisk => Ok(BinaryOperatorKind::Multiply), - Token::SymSlash => Ok(BinaryOperatorKind::Divide), - e => Err(Self::create_unexpected_token_error(TokenKind::MultiplicativeOps, e.clone(), token.position)) - } + let get_operator_from_token = |token: &WithPosition| match &token.data { + Token::SymAsterisk => Ok(BinaryOperatorKind::Multiply), + Token::SymSlash => Ok(BinaryOperatorKind::Divide), + e => Err(Self::create_unexpected_token_error( + TokenKind::MultiplicativeOps, + e.clone(), + token.position, + )), }; let mut acc = Expression::binary(get_operator_from_token(operator_token)?, lhs, rhs); - let mut operator_token = self.lexer.peek() - .ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?; + let mut operator_token = self.lexer.peek().ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })?; while asterisk_or_slash(&operator_token.data) { // SymAsterisk | SymSlash self.lexer.next(); @@ -304,7 +384,9 @@ impl Parser { // 左結合になるように詰め替える // これは特に除算のときに欠かせない処理である acc = Expression::binary(get_operator_from_token(operator_token)?, acc, new_rhs); - operator_token = self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?; + operator_token = self.lexer.peek().ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })?; } Ok(acc) } else { @@ -320,14 +402,15 @@ impl Parser { debug!("expr:add"); let first_term = self.parse_multiplicative()?; if self.lexer.peek().is_none() { - return Ok(first_term) + return Ok(first_term); } let Some(next_token) = self.lexer.peek() else { - return Err(ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position)) - }; - let plus_or_minus = |token: &Token| { - token == &Token::SymPlus || token == &Token::SymMinus + return Err(ParserError::new( + ParserErrorInner::EndOfFileError, + self.lexer.last_position, + )); }; + let plus_or_minus = |token: &Token| token == &Token::SymPlus || token == &Token::SymMinus; if plus_or_minus(&next_token.data) { // SymPlus | SymMinus @@ -335,16 +418,20 @@ impl Parser { let operator_token = next_token; let lhs = first_term; let rhs = self.parse_multiplicative()?; - let get_operator_from_token = |token: &WithPosition| { - match &token.data { - Token::SymPlus => Ok(BinaryOperatorKind::Plus), - Token::SymMinus => Ok(BinaryOperatorKind::Minus), - e => Err(Self::create_unexpected_token_error(TokenKind::AdditiveOps, e.clone(), token.position)) - } + let get_operator_from_token = |token: &WithPosition| match &token.data { + Token::SymPlus => Ok(BinaryOperatorKind::Plus), + Token::SymMinus => Ok(BinaryOperatorKind::Minus), + e => Err(Self::create_unexpected_token_error( + TokenKind::AdditiveOps, + e.clone(), + token.position, + )), }; let mut acc = Expression::binary(get_operator_from_token(operator_token)?, lhs, rhs); - let mut operator_token = self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?; + let mut operator_token = self.lexer.peek().ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })?; while plus_or_minus(&operator_token.data) { // SymPlus | SymMinus self.lexer.next(); @@ -352,7 +439,9 @@ impl Parser { // 左結合になるように詰め替える // これは特に減算のときに欠かせない処理である acc = Expression::binary(get_operator_from_token(operator_token)?, acc, new_rhs); - operator_token = self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?; + operator_token = self.lexer.peek().ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })?; } Ok(acc) } else { @@ -365,36 +454,43 @@ impl Parser { debug!("expr:shift"); let first_term = self.parse_additive()?; if self.lexer.peek().is_none() { - return Ok(first_term) + return Ok(first_term); } - let next_token = self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?; - - let is_relation_operator = |token: &Token| { - matches!(token, Token::PartLessLess | Token::PartMoreMore) - }; + let next_token = self.lexer.peek().ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })?; + + let is_relation_operator = + |token: &Token| matches!(token, Token::PartLessLess | Token::PartMoreMore); if is_relation_operator(&next_token.data) { self.lexer.next(); let operator_token = next_token; let lhs = first_term; let rhs = self.parse_relation_expression()?; - let get_operator_from_token = |token: &WithPosition| { - match &token.data { - Token::PartLessLess => Ok(BinaryOperatorKind::ShiftLeft), - Token::PartMoreMore => Ok(BinaryOperatorKind::ShiftRight), - e => Err(Self::create_unexpected_token_error(TokenKind::ShiftOps, e.clone(), token.position)) - } + let get_operator_from_token = |token: &WithPosition| match &token.data { + Token::PartLessLess => Ok(BinaryOperatorKind::ShiftLeft), + Token::PartMoreMore => Ok(BinaryOperatorKind::ShiftRight), + e => Err(Self::create_unexpected_token_error( + TokenKind::ShiftOps, + e.clone(), + token.position, + )), }; let mut acc = Expression::binary(get_operator_from_token(operator_token)?, lhs, rhs); - let mut operator_token = self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?; - + let mut operator_token = self.lexer.peek().ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })?; + while is_relation_operator(&operator_token.data) { self.lexer.next(); let new_rhs = self.parse_relation_expression()?; // 左結合になるように詰め替える acc = Expression::binary(get_operator_from_token(operator_token)?, acc, new_rhs); - operator_token = self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?; + operator_token = self.lexer.peek().ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })?; } Ok(acc) @@ -408,11 +504,20 @@ impl Parser { debug!("expr:rel"); let first_term = self.parse_shift_expression()?; if self.lexer.peek().is_none() { - return Ok(first_term) + return Ok(first_term); } - let next_token = self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?; + let next_token = self.lexer.peek().ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })?; let is_relation_operator = |token: &Token| { - matches!(token, Token::PartLessEq | Token::PartMoreEq | Token::SymLess | Token::SymMore | Token::PartLessEqMore) + matches!( + token, + Token::PartLessEq + | Token::PartMoreEq + | Token::SymLess + | Token::SymMore + | Token::PartLessEqMore + ) }; if is_relation_operator(&next_token.data) { @@ -420,25 +525,31 @@ impl Parser { let operator_token = next_token; let lhs = first_term; let rhs = self.parse_shift_expression()?; - let get_operator_from_token = |token: &WithPosition| { - match &token.data { - Token::PartLessEq => Ok(BinaryOperatorKind::LessEqual), - Token::PartMoreEq => Ok(BinaryOperatorKind::MoreEqual), - Token::SymLess => Ok(BinaryOperatorKind::Less), - Token::SymMore => Ok(BinaryOperatorKind::More), - Token::PartLessEqMore => Ok(BinaryOperatorKind::ThreeWay), - e => Err(Self::create_unexpected_token_error(TokenKind::ComparisonOps, e.clone(), token.position)) - } + let get_operator_from_token = |token: &WithPosition| match &token.data { + Token::PartLessEq => Ok(BinaryOperatorKind::LessEqual), + Token::PartMoreEq => Ok(BinaryOperatorKind::MoreEqual), + Token::SymLess => Ok(BinaryOperatorKind::Less), + Token::SymMore => Ok(BinaryOperatorKind::More), + Token::PartLessEqMore => Ok(BinaryOperatorKind::ThreeWay), + e => Err(Self::create_unexpected_token_error( + TokenKind::ComparisonOps, + e.clone(), + token.position, + )), }; let mut acc = Expression::binary(get_operator_from_token(operator_token)?, lhs, rhs); - let mut operator_token = self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?; + let mut operator_token = self.lexer.peek().ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })?; while is_relation_operator(&operator_token.data) { self.lexer.next(); let new_rhs = self.parse_additive()?; // 左結合になるように詰め替える acc = Expression::binary(get_operator_from_token(operator_token)?, acc, new_rhs); - operator_token = self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?; + operator_token = self.lexer.peek().ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })?; } Ok(acc) } else { @@ -451,34 +562,41 @@ impl Parser { debug!("expr:eq"); let first_term = self.parse_relation_expression()?; if self.lexer.peek().is_none() { - return Ok(first_term) + return Ok(first_term); } - let next_token = self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?; - let is_relation_operator = |token: &Token| { - matches!(token, Token::PartEqEq | Token::PartBangEq) - }; + let next_token = self.lexer.peek().ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })?; + let is_relation_operator = + |token: &Token| matches!(token, Token::PartEqEq | Token::PartBangEq); if is_relation_operator(&next_token.data) { self.lexer.next(); let operator_token = next_token; let lhs = first_term; let rhs = self.parse_relation_expression()?; - let get_operator_from_token = |token: &WithPosition| { - match &token.data { - Token::PartEqEq => Ok(BinaryOperatorKind::Equal), - Token::PartBangEq => Ok(BinaryOperatorKind::NotEqual), - e => Err(Self::create_unexpected_token_error(TokenKind::EqualityOps, e.clone(), token.position)) - } + let get_operator_from_token = |token: &WithPosition| match &token.data { + Token::PartEqEq => Ok(BinaryOperatorKind::Equal), + Token::PartBangEq => Ok(BinaryOperatorKind::NotEqual), + e => Err(Self::create_unexpected_token_error( + TokenKind::EqualityOps, + e.clone(), + token.position, + )), }; let mut acc = Expression::binary(get_operator_from_token(operator_token)?, lhs, rhs); - let mut operator_token = self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?; + let mut operator_token = self.lexer.peek().ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })?; while is_relation_operator(&operator_token.data) { self.lexer.next(); let new_rhs = self.parse_relation_expression()?; // 左結合になるように詰め替える acc = Expression::binary(get_operator_from_token(operator_token)?, acc, new_rhs); - operator_token = self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?; + operator_token = self.lexer.peek().ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })?; } Ok(acc) } else { @@ -493,46 +611,73 @@ impl Parser { debug!("expr:lit:int"); let n = self.lexer.peek(); self.lexer.next(); - let Some(Pointed { data: Token::Digits { sequence, suffix }, position }) = n else { - let unmatch =n.map_or(&Token::EndOfFile, |x| &x.data).clone(); + let Some(Pointed { + data: Token::Digits { sequence, suffix }, + position, + }) = n + else { + let unmatch = n.map_or(&Token::EndOfFile, |x| &x.data).clone(); let position = n.map_or(self.lexer.last_position, |x| x.position); - return Err(Self::create_unexpected_token_error(TokenKind::IntLiteral, unmatch, position)); + return Err(Self::create_unexpected_token_error( + TokenKind::IntLiteral, + unmatch, + position, + )); }; - let x = sequence.as_str().parse::().map_err(|e| ParserError::new(ParserErrorInner::UnParsableIntLiteral { - error: e - }, n.map_or(self.lexer.last_position, |x| x.position) ))?; - - fn check_bounds>(ty: &str, token_pos: SourcePos, v: i64) -> Result<(i64, Option>), ParserError> { + let x = sequence.as_str().parse::().map_err(|e| { + ParserError::new( + ParserErrorInner::UnParsableIntLiteral { error: e }, + n.map_or(self.lexer.last_position, |x| x.position), + ) + })?; + + fn check_bounds>( + ty: &str, + token_pos: SourcePos, + v: i64, + ) -> Result<(i64, Option>), ParserError> { let s = ty.to_string().into_boxed_str(); if v < As::min_value().into() || As::max_value().into() < v { - Err(ParserError::new(ParserErrorInner::OverflowedLiteral { + Err(ParserError::new( + ParserErrorInner::OverflowedLiteral { tp: s, min: As::min_value().into(), max: As::max_value().into(), - value: v - }, token_pos)) + value: v, + }, + token_pos, + )) } else { Ok((v, Some(s))) } } - let (i, suffix) = suffix.as_ref().map(|y| { - match y.as_ref() { - "i8" => check_bounds::("i8", *position, x), + let (i, suffix) = suffix + .as_ref() + .map(|y| match y.as_ref() { + "i8" => check_bounds::("i8", *position, x), "i16" => check_bounds::("i16", *position, x), "i32" => check_bounds::("i32", *position, x), "i64" => check_bounds::("i64", *position, x), - _ => unreachable!() - } - }).unwrap_or(Ok((x, None)))?; + _ => unreachable!(), + }) + .unwrap_or(Ok((x, None)))?; Ok((i, suffix.map(|x| x.to_string().into_boxed_str()))) } fn parse_type(&self) -> Result { - let Some(WithPosition { position, data: maybe_tp }) = self.lexer.peek() else { - return Err(Self::create_unexpected_token_error(TokenKind::StartOfTypeSignature, Token::EndOfFile, self.lexer.last_position)) + let Some(WithPosition { + position, + data: maybe_tp, + }) = self.lexer.peek() + else { + return Err(Self::create_unexpected_token_error( + TokenKind::StartOfTypeSignature, + Token::EndOfFile, + self.lexer.last_position, + )); }; self.lexer.next(); @@ -548,9 +693,29 @@ impl Parser { let x = self.parse_type()?; debug!("`- {x:?}"); vec.push(x); - debug!("{:?}", self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?.data); - if self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?.data != Token::SymComma { - break + debug!( + "{:?}", + self.lexer + .peek() + .ok_or_else(|| ParserError::new( + ParserErrorInner::EndOfFileError, + self.lexer.last_position + ))? + .data + ); + if self + .lexer + .peek() + .ok_or_else(|| { + ParserError::new( + ParserErrorInner::EndOfFileError, + self.lexer.last_position, + ) + })? + .data + != Token::SymComma + { + break; } self.lexer.next(); @@ -563,17 +728,24 @@ impl Parser { if vec.len() < 2 { let l = vec.len(); warn!("type:tuple = error (not enough length = {l})"); - Err(ParserError::new(ParserErrorInner::InsufficientElementsForTupleLiteral(match l { - 0 => UnexpectedTupleLiteralElementCount::Zero, - 1 => UnexpectedTupleLiteralElementCount::One, - _ => unreachable!(), - }), *position)) + Err(ParserError::new( + ParserErrorInner::InsufficientElementsForTupleLiteral(match l { + 0 => UnexpectedTupleLiteralElementCount::Zero, + 1 => UnexpectedTupleLiteralElementCount::One, + _ => unreachable!(), + }), + *position, + )) } else { Ok(TypeSignature::Tuple(vec)) } }) } - other_token => Err(Self::create_unexpected_token_error(TokenKind::StartOfTypeSignature, other_token.clone(), *position)) + other_token => Err(Self::create_unexpected_token_error( + TokenKind::StartOfTypeSignature, + other_token.clone(), + *position, + )), } } @@ -583,21 +755,27 @@ impl Parser { let pattern = self.parse_atomic_pattern()?; // optionally, allow type annotation - let type_annotation = self.lexer.parse_fallible(|| { - match self.lexer.peek() { - Some(Pointed { data: Token::SymColon, .. }) => {}, - _ => return Err(()) - } - - self.lexer.next(); + let type_annotation = self + .lexer + .parse_fallible(|| { + match self.lexer.peek() { + Some(Pointed { + data: Token::SymColon, + .. + }) => {} + _ => return Err(()), + } - // FIXME: discarding error - let x = self.parse_type().map_err(|e| { - warn!("{e:?}"); - })?; + self.lexer.next(); - Ok(x) - }).ok(); + // FIXME: discarding error + let x = self.parse_type().map_err(|e| { + warn!("{e:?}"); + })?; + + Ok(x) + }) + .ok(); debug!("decl:var:annotation: {type_annotation:?}"); @@ -614,18 +792,26 @@ impl Parser { fn parse_variable_assignment(&self) -> Result { debug!("assign:var"); - let ident_token = self.lexer.peek().unwrap_or(&self.lexer.end_of_file_token()).clone(); + let ident_token = self + .lexer + .peek() + .unwrap_or(&self.lexer.end_of_file_token()) + .clone(); self.lexer.next(); let Token::Identifier { inner: name } = ident_token.data else { - return Err(Self::create_unexpected_token_error(TokenKind::Identifier, ident_token.data, ident_token.position)) + return Err(Self::create_unexpected_token_error( + TokenKind::Identifier, + ident_token.data, + ident_token.position, + )); }; - + self.read_and_consume_or_report_unexpected_token(&Token::SymEq)?; debug!("assign:var:expr"); let expression = self.parse_lowest_precedence_expression()?; Ok(Statement::VariableAssignment { identifier: name, - expression + expression, }) } @@ -636,7 +822,15 @@ impl Parser { fn parse_if_expression(&self) -> Result { debug!("expr:if"); - if self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?.data == Token::KeywordIf { + if self + .lexer + .peek() + .ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })? + .data + == Token::KeywordIf + { self.lexer.next(); let condition = self.parse_lowest_precedence_expression()?; self.read_and_consume_or_report_unexpected_token(&Token::KeywordThen)?; @@ -656,7 +850,15 @@ impl Parser { fn parse_block_scope(&self) -> Result { debug!("statement:block"); self.read_and_consume_or_report_unexpected_token(&Token::KeywordBlock)?; - if self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?.data == Token::NewLine { + if self + .lexer + .peek() + .ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })? + .data + == Token::NewLine + { self.lexer.next(); } @@ -673,7 +875,15 @@ impl Parser { fn parse_block_expression(&self) -> Result { debug!("expr:block"); - if self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?.data == Token::KeywordBlock { + if self + .lexer + .peek() + .ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })? + .data + == Token::KeywordBlock + { self.lexer.next(); self.read_and_consume_or_report_unexpected_token(&Token::NewLine)?; let mut statements = vec![]; @@ -681,13 +891,21 @@ impl Parser { statements.push(v); } let final_expression = Box::new(self.parse_lowest_precedence_expression()?); - if self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?.data == Token::NewLine { + if self + .lexer + .peek() + .ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })? + .data + == Token::NewLine + { self.lexer.next(); } self.read_and_consume_or_report_unexpected_token(&Token::KeywordEnd)?; Ok(Expression::Block { intermediate_statements: statements, - final_expression + final_expression, }) } else { self.parse_if_expression() @@ -704,9 +922,17 @@ impl Parser { debug!("pattern:tuple[{}] = {pattern:?}", v.len()); v.push(pattern); - if self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?.data == Token::SymRightPar { + if self + .lexer + .peek() + .ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })? + .data + == Token::SymRightPar + { debug!("pattern:tuple:end"); - break + break; } self.read_and_consume_or_report_unexpected_token(&Token::SymComma)?; @@ -719,7 +945,9 @@ impl Parser { fn parse_atomic_pattern(&self) -> Result { debug!("pattern:atomic"); - let it = self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?; + let it = self.lexer.peek().ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })?; match &it.data { Token::Identifier { inner: name } => { @@ -730,31 +958,46 @@ impl Parser { self.lexer.next(); Ok(AtomicPattern::Discard) } - Token::SymLeftPar => { - self.parse_tuple_destruct_pattern() - } - other_token => Err(Self::create_unexpected_token_error(TokenKind::Identifier, other_token.clone(), it.position)), + Token::SymLeftPar => self.parse_tuple_destruct_pattern(), + other_token => Err(Self::create_unexpected_token_error( + TokenKind::Identifier, + other_token.clone(), + it.position, + )), } } /// 現在のトークンが指定されたトークンならそのトークンをそのまま返した上でレキサーを1個進める。そうではないなら[`ParseError::UnexpectedToken`]を返す。 - fn read_and_consume_or_report_unexpected_token(&self, token: &Token) -> Result<(), ParserError> { - let peek = self.lexer.peek().ok_or_else(|| ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position))?; + fn read_and_consume_or_report_unexpected_token( + &self, + token: &Token, + ) -> Result<(), ParserError> { + let peek = self.lexer.peek().ok_or_else(|| { + ParserError::new(ParserErrorInner::EndOfFileError, self.lexer.last_position) + })?; if &peek.data == token { self.lexer.next(); Ok(()) } else { - Err(Self::create_unexpected_token_error(TokenKind::only(token), peek.data.clone(), peek.position)) + Err(Self::create_unexpected_token_error( + TokenKind::only(token), + peek.data.clone(), + peek.position, + )) } } - - const fn create_unexpected_token_error(expected_kind: TokenKind, token: Token, position: SourcePosition) -> ParserError { + + const fn create_unexpected_token_error( + expected_kind: TokenKind, + token: Token, + position: SourcePosition, + ) -> ParserError { ParserError::new( ParserErrorInner::UnexpectedToken { pat: expected_kind, unmatch: token, }, - position + position, ) } } diff --git a/package/origlang-parser/src/recover.rs b/package/origlang-parser/src/recover.rs index da8c5a6c..beca24a2 100644 --- a/package/origlang-parser/src/recover.rs +++ b/package/origlang-parser/src/recover.rs @@ -12,11 +12,7 @@ pub enum PartiallyParseFixCandidate { #[display(fmt = "No fixes available")] None, #[display(fmt = "Insert before")] - InsertBefore { - tokens: Vec, - }, + InsertBefore { tokens: Vec }, #[display(fmt = "Insert after")] - InsertAfter { - tokens: Vec, - }, + InsertAfter { tokens: Vec }, } diff --git a/package/origlang-runtime/src/invoke_once.rs b/package/origlang-runtime/src/invoke_once.rs index fade35b2..068ff3b8 100644 --- a/package/origlang-runtime/src/invoke_once.rs +++ b/package/origlang-runtime/src/invoke_once.rs @@ -1,6 +1,5 @@ use std::cell::{Cell, RefCell}; -use std::mem::{MaybeUninit}; - +use std::mem::MaybeUninit; #[derive(Debug)] pub struct InvokeOnce { diff --git a/package/origlang-runtime/src/lib.rs b/package/origlang-runtime/src/lib.rs index 3e86d46b..88dbbbd7 100644 --- a/package/origlang-runtime/src/lib.rs +++ b/package/origlang-runtime/src/lib.rs @@ -3,19 +3,19 @@ mod invoke_once; +use derive_more::{Display, From}; +use log::debug; +use origlang_ast::after_parse::BinaryOperatorKind; +use origlang_ast::Identifier; +use origlang_ir::{CompiledTypedExpression, IR2}; use std::cell::RefCell; use std::collections::{HashMap, VecDeque}; use std::fmt::{Debug, Display, Formatter}; -use derive_more::{Display, From}; -use log::debug; use tap::Conv; use thiserror::Error; -use origlang_ast::Identifier; -use origlang_ast::after_parse::BinaryOperatorKind; -use origlang_ir::{CompiledTypedExpression, IR2}; -use origlang_typesystem_model::{DisplayRecordType, Type, TypedIntLiteral}; use crate::invoke_once::InvokeOnce; +use origlang_typesystem_model::{DisplayRecordType, Type, TypedIntLiteral}; #[derive(From)] pub struct Coerced(i64); @@ -36,17 +36,21 @@ impl From for TypeBox { } #[derive(Error, Debug)] -pub enum TypeBoxUnwrapError { -} +pub enum TypeBoxUnwrapError {} #[derive(PartialEq, Eq, Clone, Debug)] pub struct DisplayTupleValue { - pub boxes: Vec + pub boxes: Vec, } impl Display for DisplayTupleValue { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let s = self.boxes.iter().map(ToString::to_string).collect::>().join(", "); + let s = self + .boxes + .iter() + .map(ToString::to_string) + .collect::>() + .join(", "); let s = format!("({s})"); f.write_str(&s) } @@ -60,7 +64,12 @@ pub struct DisplayRecordValue { impl Display for DisplayRecordValue { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let s = self.values.iter().map(ToString::to_string).collect::>().join(", "); + let s = self + .values + .iter() + .map(ToString::to_string) + .collect::>() + .join(", "); let name = &self.name; let s = format!("{name} {{{s}}}"); f.write_str(&s) @@ -111,13 +120,17 @@ impl TypeBox { Self::Int16(_) => Type::Int16, Self::Int32(_) => Type::Int32, Self::Int64(_) => Type::Int64, - Self::Tuple(t) => Type::Tuple(t.boxes.iter().map(Self::get_type).collect::>().into()), - Self::Record(r) => Type::Record( - DisplayRecordType::new( - r.name.clone(), - r.values.iter().map(Self::get_type).collect::>() - ) + Self::Tuple(t) => Type::Tuple( + t.boxes + .iter() + .map(Self::get_type) + .collect::>() + .into(), ), + Self::Record(r) => Type::Record(DisplayRecordType::new( + r.name.clone(), + r.values.iter().map(Self::get_type).collect::>(), + )), } } } @@ -130,7 +143,7 @@ pub struct Scope { impl Scope { fn empty() -> Self { Self { - variables: (HashMap::new()) + variables: (HashMap::new()), } } } @@ -187,7 +200,7 @@ impl Runtime { Self { scopes, o, - on_exit: None + on_exit: None, } } @@ -196,24 +209,26 @@ impl Runtime { // info!("{ast:?}", ast = &ast); let x = seq; // info!("{x:?}", x = &x); - x - .iter().for_each(|x| self.invoke(x)); + x.iter().for_each(|x| self.invoke(x)); &self.o } pub fn execute(&self, ir: &[IR2]) { ir.iter().for_each(|x| self.invoke(x)); } - + pub fn invoke(&self, ir: &IR2) { match ir { IR2::Output(e) => { - self.o.as_ref().borrow_mut().output(e.evaluate(self).expect("runtime exception")); + self.o + .as_ref() + .borrow_mut() + .output(e.evaluate(self).expect("runtime exception")); } IR2::UpdateVariable { ident, value } => { self.upsert_member_to_current_scope( ident.clone(), - value.evaluate(self).expect("self exception") + value.evaluate(self).expect("self exception"), ); } IR2::PushScope => { @@ -224,7 +239,9 @@ impl Runtime { } IR2::Exit => { if let Some(x) = self.on_exit.as_ref() { - x.try_get().expect("exit receiver is already called!").on_exit(); + x.try_get() + .expect("exit receiver is already called!") + .on_exit(); } } IR2::EvalAndForget { expression } => { @@ -246,7 +263,10 @@ impl Runtime { fn pop_scope(&self) { debug!("exit scope({len})", len = self.scopes.borrow().len()); - self.scopes.borrow_mut().pop_front().expect("scope must not be empty"); + self.scopes + .borrow_mut() + .pop_front() + .expect("scope must not be empty"); } fn search_member(&self, identifier: &Identifier) -> Option { @@ -265,9 +285,7 @@ impl Runtime { #[expect(clippy::module_name_repetitions)] pub enum RuntimeError { #[error("variable {identifier} is not defined in current scope")] - UndefinedVariable { - identifier: Identifier, - }, + UndefinedVariable { identifier: Identifier }, } type EvaluateResult = Result; @@ -298,8 +316,9 @@ macro_rules! f { ::std::cmp::Ordering::Equal => 0, ::std::cmp::Ordering::Greater => 1, } - }.into(), - BinaryOperatorKind::Equal | BinaryOperatorKind::NotEqual => unreachable!() + } + .into(), + BinaryOperatorKind::Equal | BinaryOperatorKind::NotEqual => unreachable!(), }; Ok(ret) @@ -324,8 +343,10 @@ macro_rules! f { ::std::cmp::Ordering::Equal => 0, ::std::cmp::Ordering::Greater => 1, } - }.conv::<$intermediate>().into(), - BinaryOperatorKind::Equal | BinaryOperatorKind::NotEqual => unreachable!() + } + .conv::<$intermediate>() + .into(), + BinaryOperatorKind::Equal | BinaryOperatorKind::NotEqual => unreachable!(), }; Ok(ret) @@ -341,10 +362,18 @@ macro_rules! indicate_type_checker_bug { }; } -fn evaluate_bin_op(runtime: &Runtime, lhs: &CompiledTypedExpression, rhs: &CompiledTypedExpression, operator: &BinaryOperatorKind) -> EvaluateResult { +fn evaluate_bin_op( + runtime: &Runtime, + lhs: &CompiledTypedExpression, + rhs: &CompiledTypedExpression, + operator: &BinaryOperatorKind, +) -> EvaluateResult { let lhs = lhs.evaluate(runtime)?; let rhs = rhs.evaluate(runtime)?; - if matches!(operator, BinaryOperatorKind::Equal | BinaryOperatorKind::NotEqual) { + if matches!( + operator, + BinaryOperatorKind::Equal | BinaryOperatorKind::NotEqual + ) { return if lhs.get_type() == rhs.get_type() { let ret = match operator { BinaryOperatorKind::Equal => lhs == rhs, @@ -353,26 +382,28 @@ fn evaluate_bin_op(runtime: &Runtime, lhs: &CompiledTypedExpression, rhs: &Compi }; Ok(ret.into()) } else { - indicate_type_checker_bug!(context = "type checker must deny equality check between different types") - } + indicate_type_checker_bug!( + context = "type checker must deny equality check between different types" + ) + }; } match (lhs, rhs) { (TypeBox::NonCoercedInteger(lhs), TypeBox::NonCoercedInteger(rhs)) => { f!(lhs, operator, rhs as NonCoerced) - }, + } (TypeBox::Int8(lhs), TypeBox::Int8(rhs)) => { f!(lhs, operator, rhs) - }, + } (TypeBox::Int16(lhs), TypeBox::Int16(rhs)) => { f!(lhs, operator, rhs) - }, + } (TypeBox::Int32(lhs), TypeBox::Int32(rhs)) => { f!(lhs, operator, rhs) - }, + } (TypeBox::Int64(lhs), TypeBox::Int64(rhs)) => { f!(lhs, operator, rhs as Coerced) - }, + } (TypeBox::String(lhs), TypeBox::String(rhs)) => { let mut ret = lhs; // give hint to compiler @@ -380,7 +411,9 @@ fn evaluate_bin_op(runtime: &Runtime, lhs: &CompiledTypedExpression, rhs: &Compi ret += rhs.as_str(); Ok(ret.into()) } - _ => indicate_type_checker_bug!(context = "type checker must deny operator application between different type") + _ => indicate_type_checker_bug!( + context = "type checker must deny operator application between different type" + ), } } @@ -399,13 +432,24 @@ impl CanBeEvaluated for CompiledTypedExpression { Self::StringLiteral(s) => Ok(s.clone().into()), Self::UnitLiteral => Ok(().into()), Self::Variable { ident, tp: _ } => { - runtime.search_member(ident) - .ok_or(RuntimeError::UndefinedVariable { identifier: ident.clone() }) - }, - Self::BinaryOperator { lhs, rhs, operator, return_type: _ } => { - evaluate_bin_op(runtime, lhs, rhs, operator) + runtime + .search_member(ident) + .ok_or(RuntimeError::UndefinedVariable { + identifier: ident.clone(), + }) } - Self::If { condition, then: then_clause_value, els: else_clause_value, return_type: _ } => { + Self::BinaryOperator { + lhs, + rhs, + operator, + return_type: _, + } => evaluate_bin_op(runtime, lhs, rhs, operator), + Self::If { + condition, + then: then_clause_value, + els: else_clause_value, + return_type: _, + } => { let ret = condition.as_ref().evaluate(runtime)?; if let TypeBox::Boolean(b) = ret { runtime.push_scope(); @@ -422,7 +466,11 @@ impl CanBeEvaluated for CompiledTypedExpression { indicate_type_checker_bug!(context = "if clause's expression must be Bool") } } - Self::Block { inner: intermediate_statements, final_expression, return_type: _ } => { + Self::Block { + inner: intermediate_statements, + final_expression, + return_type: _, + } => { runtime.push_scope(); runtime.execute(intermediate_statements); runtime.pop_scope(); @@ -435,20 +483,16 @@ impl CanBeEvaluated for CompiledTypedExpression { res.push(e.evaluate(runtime)?); } - Ok(TypeBox::Tuple(DisplayTupleValue { - boxes: res - })) + Ok(TypeBox::Tuple(DisplayTupleValue { boxes: res })) } Self::ExtractTuple { expr, index } => { let x = expr.evaluate(runtime)?; match x { - TypeBox::Tuple(x) => { - x.boxes.get(*index).map_or_else( - || indicate_type_checker_bug!(context = "tuple_destruction: out of bounds"), - |x| Ok(x.clone()) - ) - } - _other => indicate_type_checker_bug!(context = "must be tuple") + TypeBox::Tuple(x) => x.boxes.get(*index).map_or_else( + || indicate_type_checker_bug!(context = "tuple_destruction: out of bounds"), + |x| Ok(x.clone()), + ), + _other => indicate_type_checker_bug!(context = "must be tuple"), } } } diff --git a/package/origlang-source-span/src/lib.rs b/package/origlang-source-span/src/lib.rs index 171b99a5..51fa17ea 100644 --- a/package/origlang-source-span/src/lib.rs +++ b/package/origlang-source-span/src/lib.rs @@ -14,9 +14,7 @@ pub struct SourcePosition { impl SourcePosition { #[must_use] pub const fn new(line: NonZeroUsize, column: NonZeroUsize) -> Self { - Self { - line, column - } + Self { line, column } } pub fn try_new(lc: impl TryIntoSourcePosition) -> Result { @@ -77,7 +75,12 @@ mod tests { #[test] fn source_pos_order() { // 辞書式順序の理解があっているかどうか - assert!(SourcePosition::try_new((1, 1)).unwrap() < SourcePosition::try_new((1, 2)).unwrap()); - assert!(SourcePosition::try_new((1, usize::MAX)).unwrap() < SourcePosition::try_new((2, 1)).unwrap()); + assert!( + SourcePosition::try_new((1, 1)).unwrap() < SourcePosition::try_new((1, 2)).unwrap() + ); + assert!( + SourcePosition::try_new((1, usize::MAX)).unwrap() + < SourcePosition::try_new((2, 1)).unwrap() + ); } } diff --git a/package/origlang-testsuite/src/main.rs b/package/origlang-testsuite/src/main.rs index 049f5104..7e951849 100644 --- a/package/origlang-testsuite/src/main.rs +++ b/package/origlang-testsuite/src/main.rs @@ -9,17 +9,17 @@ fn main() { } use log::{debug, info}; -use thiserror::Error; -use origlang_runtime::{Accumulate, DisplayTupleValue, Runtime, TypeBox}; -use origlang_ast::{AtomicPattern, Comment, Identifier, RootAst, Statement, TypeSignature}; use origlang_ast::after_parse::{BinaryOperatorKind, Expression}; -use origlang_parser::error::{ParserError, ParserErrorInner}; -use origlang_typecheck::type_check::error::TypeCheckError; -use origlang_typecheck::type_check::TypeChecker; +use origlang_ast::{AtomicPattern, Comment, Identifier, RootAst, Statement, TypeSignature}; use origlang_ir::IntoVerbatimSequencedIR; use origlang_ir_optimizer::lower::{EachStep, LowerStep, TheTranspiler}; use origlang_ir_optimizer::preset::NoOptimization; +use origlang_parser::error::{ParserError, ParserErrorInner}; +use origlang_runtime::{Accumulate, DisplayTupleValue, Runtime, TypeBox}; use origlang_source_span::SourcePosition; +use origlang_typecheck::type_check::error::TypeCheckError; +use origlang_typecheck::type_check::TypeChecker; +use thiserror::Error; type Err = TestFailureCause; @@ -65,7 +65,10 @@ impl Test { Self::evaluated_expressions_with_optimization_preset(src, &NoOptimization) } - fn evaluated_expressions_with_optimization_preset(src: &str, preset: &dyn EachStep) -> Result, Err> { + fn evaluated_expressions_with_optimization_preset( + src: &str, + preset: &dyn EachStep, + ) -> Result, Err> { use origlang_parser::parser::Parser; debug!("src:\n{}", src); let source = src; @@ -93,57 +96,134 @@ impl Test { let root_ast = parser.parse()?; Ok(root_ast) } - + fn print_literal() { - assert_eq!(Self::evaluated_expressions("print 123456\n").expect("properly parsed and typed"), type_boxes![123456 => NonCoercedInteger]); - assert_eq!(Self::evaluated_expressions("print 1\nprint 2\n").expect("properly parsed and typed"), type_boxes![1 => NonCoercedInteger, 2 => NonCoercedInteger]); + assert_eq!( + Self::evaluated_expressions("print 123456\n").expect("properly parsed and typed"), + type_boxes![123456 => NonCoercedInteger] + ); + assert_eq!( + Self::evaluated_expressions("print 1\nprint 2\n").expect("properly parsed and typed"), + type_boxes![1 => NonCoercedInteger, 2 => NonCoercedInteger] + ); } fn simple_variable_assignment() { - assert_eq!(Self::evaluated_expressions("var x = 1\nprint x\n").expect("properly parsed and typed"), type_boxes![1 => NonCoercedInteger]); - assert_eq!(Self::evaluated_expressions("var x = 1\nvar y = x\nprint y\n").expect("properly parsed and typed"), type_boxes![1 => NonCoercedInteger]); + assert_eq!( + Self::evaluated_expressions("var x = 1\nprint x\n").expect("properly parsed and typed"), + type_boxes![1 => NonCoercedInteger] + ); + assert_eq!( + Self::evaluated_expressions("var x = 1\nvar y = x\nprint y\n") + .expect("properly parsed and typed"), + type_boxes![1 => NonCoercedInteger] + ); } fn op_plus() { - assert_eq!(Self::evaluated_expressions("print 1 + 2\n").expect("properly parsed and typed"), type_boxes![3 => NonCoercedInteger]); - assert_eq!(Self::evaluated_expressions("var x = 1\nprint 1 + x\n").expect("properly parsed and typed"), type_boxes![2 => NonCoercedInteger]); - assert_eq!(Self::evaluated_expressions("var x = 1\nvar y = 3\nprint x + y\n").expect("properly parsed and typed"), type_boxes![4 => NonCoercedInteger]); + assert_eq!( + Self::evaluated_expressions("print 1 + 2\n").expect("properly parsed and typed"), + type_boxes![3 => NonCoercedInteger] + ); + assert_eq!( + Self::evaluated_expressions("var x = 1\nprint 1 + x\n") + .expect("properly parsed and typed"), + type_boxes![2 => NonCoercedInteger] + ); + assert_eq!( + Self::evaluated_expressions("var x = 1\nvar y = 3\nprint x + y\n") + .expect("properly parsed and typed"), + type_boxes![4 => NonCoercedInteger] + ); - assert_eq!(Self::evaluated_expressions("var x = 1\nvar y = 2\nvar z = 3\nprint x + y + z\n").expect("properly parsed and typed"), type_boxes![6 => NonCoercedInteger]); + assert_eq!( + Self::evaluated_expressions("var x = 1\nvar y = 2\nvar z = 3\nprint x + y + z\n") + .expect("properly parsed and typed"), + type_boxes![6 => NonCoercedInteger] + ); } fn op_minus() { - assert_eq!(Self::evaluated_expressions("print 1 - 2\n").expect("properly parsed and typed"), type_boxes![-1 => NonCoercedInteger]); - assert_eq!(Self::evaluated_expressions("var x = 1\nprint 1 - x\n").expect("properly parsed and typed"), type_boxes![0 => NonCoercedInteger]); - assert_eq!(Self::evaluated_expressions("var x = 1\nvar y = 3\nprint x - y\n").expect("properly parsed and typed"), type_boxes![-2 => NonCoercedInteger]); + assert_eq!( + Self::evaluated_expressions("print 1 - 2\n").expect("properly parsed and typed"), + type_boxes![-1 => NonCoercedInteger] + ); + assert_eq!( + Self::evaluated_expressions("var x = 1\nprint 1 - x\n") + .expect("properly parsed and typed"), + type_boxes![0 => NonCoercedInteger] + ); + assert_eq!( + Self::evaluated_expressions("var x = 1\nvar y = 3\nprint x - y\n") + .expect("properly parsed and typed"), + type_boxes![-2 => NonCoercedInteger] + ); - assert_eq!(Self::evaluated_expressions("var x = 1\nvar y = 2\nvar z = 3\nprint z - x - y\n").expect("properly parsed and typed"), type_boxes![0 => NonCoercedInteger]); + assert_eq!( + Self::evaluated_expressions("var x = 1\nvar y = 2\nvar z = 3\nprint z - x - y\n") + .expect("properly parsed and typed"), + type_boxes![0 => NonCoercedInteger] + ); } fn expr_parenthesised() { // paren test - assert_eq!(Self::evaluated_expressions("print (1)\n").expect("properly parsed and typed"), type_boxes![1 => NonCoercedInteger]); - assert_eq!(Self::evaluated_expressions("print 3 - (2 - 1)\n").expect("properly parsed and typed"), type_boxes![2 => NonCoercedInteger]); - assert_eq!(Self::evaluated_expressions("print (3 - 2) - 1\n").expect("properly parsed and typed"), type_boxes![0 => NonCoercedInteger]); + assert_eq!( + Self::evaluated_expressions("print (1)\n").expect("properly parsed and typed"), + type_boxes![1 => NonCoercedInteger] + ); + assert_eq!( + Self::evaluated_expressions("print 3 - (2 - 1)\n").expect("properly parsed and typed"), + type_boxes![2 => NonCoercedInteger] + ); + assert_eq!( + Self::evaluated_expressions("print (3 - 2) - 1\n").expect("properly parsed and typed"), + type_boxes![0 => NonCoercedInteger] + ); } fn op_multiply() { // multiply test - assert_eq!(Self::evaluated_expressions("print 3 * 2\n").expect("properly parsed and typed"), type_boxes![6 => NonCoercedInteger]); - assert_eq!(Self::evaluated_expressions("print 3 * 2 + 1\n").expect("properly parsed and typed"), type_boxes![7 => NonCoercedInteger]); - assert_ne!(Self::evaluated_expressions("print 3 * 2 + 1\n").expect("properly parsed and typed"), type_boxes![9 => NonCoercedInteger]); - assert_eq!(Self::evaluated_expressions("print 3 * (2 + 1)\n").expect("properly parsed and typed"), type_boxes![9 => NonCoercedInteger]); - assert_ne!(Self::evaluated_expressions("print 3 * (2 + 1)\n").expect("properly parsed and typed"), type_boxes![7 => NonCoercedInteger]); + assert_eq!( + Self::evaluated_expressions("print 3 * 2\n").expect("properly parsed and typed"), + type_boxes![6 => NonCoercedInteger] + ); + assert_eq!( + Self::evaluated_expressions("print 3 * 2 + 1\n").expect("properly parsed and typed"), + type_boxes![7 => NonCoercedInteger] + ); + assert_ne!( + Self::evaluated_expressions("print 3 * 2 + 1\n").expect("properly parsed and typed"), + type_boxes![9 => NonCoercedInteger] + ); + assert_eq!( + Self::evaluated_expressions("print 3 * (2 + 1)\n").expect("properly parsed and typed"), + type_boxes![9 => NonCoercedInteger] + ); + assert_ne!( + Self::evaluated_expressions("print 3 * (2 + 1)\n").expect("properly parsed and typed"), + type_boxes![7 => NonCoercedInteger] + ); } fn literal_bool() { // boolean literal test - assert_eq!(Self::evaluated_expressions("print true\n").expect("properly parsed and typed"), type_boxes![true => Boolean]); - assert_ne!(Self::evaluated_expressions("print true\n").expect("properly parsed and typed"), type_boxes![false => Boolean]); - assert_eq!(Self::evaluated_expressions("print false\n").expect("properly parsed and typed"), type_boxes![false => Boolean]); - assert_ne!(Self::evaluated_expressions("print false\n").expect("properly parsed and typed"), type_boxes![true => Boolean]); - - + assert_eq!( + Self::evaluated_expressions("print true\n").expect("properly parsed and typed"), + type_boxes![true => Boolean] + ); + assert_ne!( + Self::evaluated_expressions("print true\n").expect("properly parsed and typed"), + type_boxes![false => Boolean] + ); + assert_eq!( + Self::evaluated_expressions("print false\n").expect("properly parsed and typed"), + type_boxes![false => Boolean] + ); + assert_ne!( + Self::evaluated_expressions("print false\n").expect("properly parsed and typed"), + type_boxes![true => Boolean] + ); } fn run_all() { @@ -176,118 +256,267 @@ impl Test { Self::test_shift(); Self::trigger_confusion(); Self::test_tuple_destruction(); - - } fn test_less() { // less equal - assert_eq!(Self::evaluated_expressions("print 1 <= 0\n").expect("properly parsed and typed"), type_boxes![false => Boolean]); - assert_eq!(Self::evaluated_expressions("print 1 <= 1\n").expect("properly parsed and typed"), type_boxes![true => Boolean]); - assert_eq!(Self::evaluated_expressions("print 1 <= 2\n").expect("properly parsed and typed"), type_boxes![true => Boolean]); + assert_eq!( + Self::evaluated_expressions("print 1 <= 0\n").expect("properly parsed and typed"), + type_boxes![false => Boolean] + ); + assert_eq!( + Self::evaluated_expressions("print 1 <= 1\n").expect("properly parsed and typed"), + type_boxes![true => Boolean] + ); + assert_eq!( + Self::evaluated_expressions("print 1 <= 2\n").expect("properly parsed and typed"), + type_boxes![true => Boolean] + ); // less equal reflexibility - assert_eq!(Self::evaluated_expressions("print 1 <= 0 == 0 >= 1\n").expect("properly parsed and typed"), type_boxes![true => Boolean]); - assert_eq!(Self::evaluated_expressions("print 1 <= 1 == 1 >= 1\n").expect("properly parsed and typed"), type_boxes![true => Boolean]); - assert_eq!(Self::evaluated_expressions("print 1 <= 2 == 2 >= 1\n").expect("properly parsed and typed"), type_boxes![true => Boolean]); + assert_eq!( + Self::evaluated_expressions("print 1 <= 0 == 0 >= 1\n") + .expect("properly parsed and typed"), + type_boxes![true => Boolean] + ); + assert_eq!( + Self::evaluated_expressions("print 1 <= 1 == 1 >= 1\n") + .expect("properly parsed and typed"), + type_boxes![true => Boolean] + ); + assert_eq!( + Self::evaluated_expressions("print 1 <= 2 == 2 >= 1\n") + .expect("properly parsed and typed"), + type_boxes![true => Boolean] + ); // less - assert_eq!(Self::evaluated_expressions("print 1 < 0\n").expect("properly parsed and typed"), type_boxes![false => Boolean]); - assert_eq!(Self::evaluated_expressions("print 1 < 1\n").expect("properly parsed and typed"), type_boxes![false => Boolean]); - assert_eq!(Self::evaluated_expressions("print 1 < 2\n").expect("properly parsed and typed"), type_boxes![true => Boolean]); + assert_eq!( + Self::evaluated_expressions("print 1 < 0\n").expect("properly parsed and typed"), + type_boxes![false => Boolean] + ); + assert_eq!( + Self::evaluated_expressions("print 1 < 1\n").expect("properly parsed and typed"), + type_boxes![false => Boolean] + ); + assert_eq!( + Self::evaluated_expressions("print 1 < 2\n").expect("properly parsed and typed"), + type_boxes![true => Boolean] + ); // less equal reflexibility - assert_eq!(Self::evaluated_expressions("print 1 < 0 == 0 > 1\n").expect("properly parsed and typed"), type_boxes![true => Boolean]); - assert_eq!(Self::evaluated_expressions("print 1 < 1 == 1 > 1\n").expect("properly parsed and typed"), type_boxes![true => Boolean]); - assert_eq!(Self::evaluated_expressions("print 1 < 2 == 2 > 1\n").expect("properly parsed and typed"), type_boxes![true => Boolean]); + assert_eq!( + Self::evaluated_expressions("print 1 < 0 == 0 > 1\n") + .expect("properly parsed and typed"), + type_boxes![true => Boolean] + ); + assert_eq!( + Self::evaluated_expressions("print 1 < 1 == 1 > 1\n") + .expect("properly parsed and typed"), + type_boxes![true => Boolean] + ); + assert_eq!( + Self::evaluated_expressions("print 1 < 2 == 2 > 1\n") + .expect("properly parsed and typed"), + type_boxes![true => Boolean] + ); } fn test_more() { // more equal - assert_eq!(Self::evaluated_expressions("print 1 >= 0\n").expect("properly parsed and typed"), type_boxes![true => Boolean]); - assert_eq!(Self::evaluated_expressions("print 1 >= 1\n").expect("properly parsed and typed"), type_boxes![true => Boolean]); - assert_eq!(Self::evaluated_expressions("print 1 >= 2\n").expect("properly parsed and typed"), type_boxes![false => Boolean]); + assert_eq!( + Self::evaluated_expressions("print 1 >= 0\n").expect("properly parsed and typed"), + type_boxes![true => Boolean] + ); + assert_eq!( + Self::evaluated_expressions("print 1 >= 1\n").expect("properly parsed and typed"), + type_boxes![true => Boolean] + ); + assert_eq!( + Self::evaluated_expressions("print 1 >= 2\n").expect("properly parsed and typed"), + type_boxes![false => Boolean] + ); // more equal reflexibility - assert_eq!(Self::evaluated_expressions("print 1 >= 0 == 0 <= 1\n").expect("properly parsed and typed"), type_boxes![true => Boolean]); - assert_eq!(Self::evaluated_expressions("print 1 >= 1 == 1 <= 1\n").expect("properly parsed and typed"), type_boxes![true => Boolean]); - assert_eq!(Self::evaluated_expressions("print 1 >= 2 == 2 <= 1\n").expect("properly parsed and typed"), type_boxes![true => Boolean]); + assert_eq!( + Self::evaluated_expressions("print 1 >= 0 == 0 <= 1\n") + .expect("properly parsed and typed"), + type_boxes![true => Boolean] + ); + assert_eq!( + Self::evaluated_expressions("print 1 >= 1 == 1 <= 1\n") + .expect("properly parsed and typed"), + type_boxes![true => Boolean] + ); + assert_eq!( + Self::evaluated_expressions("print 1 >= 2 == 2 <= 1\n") + .expect("properly parsed and typed"), + type_boxes![true => Boolean] + ); // more - assert_eq!(Self::evaluated_expressions("print 1 > 0\n").expect("properly parsed and typed"), type_boxes![true => Boolean]); - assert_eq!(Self::evaluated_expressions("print 1 > 1\n").expect("properly parsed and typed"), type_boxes![false => Boolean]); - assert_eq!(Self::evaluated_expressions("print 1 > 2\n").expect("properly parsed and typed"), type_boxes![false => Boolean]); + assert_eq!( + Self::evaluated_expressions("print 1 > 0\n").expect("properly parsed and typed"), + type_boxes![true => Boolean] + ); + assert_eq!( + Self::evaluated_expressions("print 1 > 1\n").expect("properly parsed and typed"), + type_boxes![false => Boolean] + ); + assert_eq!( + Self::evaluated_expressions("print 1 > 2\n").expect("properly parsed and typed"), + type_boxes![false => Boolean] + ); // more reflexibility - assert_eq!(Self::evaluated_expressions("print 1 > 0 == 0 < 1\n").expect("properly parsed and typed"), type_boxes![true => Boolean]); - assert_eq!(Self::evaluated_expressions("print 1 > 1 == 1 < 1\n").expect("properly parsed and typed"), type_boxes![true => Boolean]); - assert_eq!(Self::evaluated_expressions("print 1 > 2 == 2 < 1\n").expect("properly parsed and typed"), type_boxes![true => Boolean]); - - + assert_eq!( + Self::evaluated_expressions("print 1 > 0 == 0 < 1\n") + .expect("properly parsed and typed"), + type_boxes![true => Boolean] + ); + assert_eq!( + Self::evaluated_expressions("print 1 > 1 == 1 < 1\n") + .expect("properly parsed and typed"), + type_boxes![true => Boolean] + ); + assert_eq!( + Self::evaluated_expressions("print 1 > 2 == 2 < 1\n") + .expect("properly parsed and typed"), + type_boxes![true => Boolean] + ); } fn test_spaceship() { // spaceship operator - assert_eq!(Self::evaluated_expressions("print 1 <=> 0\n").expect("properly parsed and typed"), type_boxes![1 => NonCoercedInteger]); - assert_eq!(Self::evaluated_expressions("print 1 <=> 1\n").expect("properly parsed and typed"), type_boxes![0 => NonCoercedInteger]); - assert_eq!(Self::evaluated_expressions("print 1 <=> 2\n").expect("properly parsed and typed"), type_boxes![-1 => NonCoercedInteger]); - - + assert_eq!( + Self::evaluated_expressions("print 1 <=> 0\n").expect("properly parsed and typed"), + type_boxes![1 => NonCoercedInteger] + ); + assert_eq!( + Self::evaluated_expressions("print 1 <=> 1\n").expect("properly parsed and typed"), + type_boxes![0 => NonCoercedInteger] + ); + assert_eq!( + Self::evaluated_expressions("print 1 <=> 2\n").expect("properly parsed and typed"), + type_boxes![-1 => NonCoercedInteger] + ); } fn test_equality_operator() { - assert_eq!(Self::evaluated_expressions("print 42 == 42\n").expect("properly parsed and typed"), type_boxes![true => Boolean]); - assert_eq!(Self::evaluated_expressions("print 42 == 21\n").expect("properly parsed and typed"), type_boxes![false => Boolean]); - assert_eq!(Self::evaluated_expressions("print 42 != 42\n").expect("properly parsed and typed"), type_boxes![false => Boolean]); - assert_eq!(Self::evaluated_expressions("print 42 != 21\n").expect("properly parsed and typed"), type_boxes![true => Boolean]); - + assert_eq!( + Self::evaluated_expressions("print 42 == 42\n").expect("properly parsed and typed"), + type_boxes![true => Boolean] + ); + assert_eq!( + Self::evaluated_expressions("print 42 == 21\n").expect("properly parsed and typed"), + type_boxes![false => Boolean] + ); + assert_eq!( + Self::evaluated_expressions("print 42 != 42\n").expect("properly parsed and typed"), + type_boxes![false => Boolean] + ); + assert_eq!( + Self::evaluated_expressions("print 42 != 21\n").expect("properly parsed and typed"), + type_boxes![true => Boolean] + ); } fn test_if_expression() { - assert_eq!(Self::evaluated_expressions("print if true then 1 else 2\n").expect("properly parsed and typed"), type_boxes![1 => NonCoercedInteger]); - assert_ne!(Self::evaluated_expressions("print if true then 1 else 2\n").expect("properly parsed and typed"), type_boxes![2 => NonCoercedInteger]); - assert_eq!(Self::evaluated_expressions("print if false then 1 else 2\n").expect("properly parsed and typed"), type_boxes![2 => NonCoercedInteger]); - assert_ne!(Self::evaluated_expressions("print if false then 1 else 2\n").expect("properly parsed and typed"), type_boxes![1 => NonCoercedInteger]); - + assert_eq!( + Self::evaluated_expressions("print if true then 1 else 2\n") + .expect("properly parsed and typed"), + type_boxes![1 => NonCoercedInteger] + ); + assert_ne!( + Self::evaluated_expressions("print if true then 1 else 2\n") + .expect("properly parsed and typed"), + type_boxes![2 => NonCoercedInteger] + ); + assert_eq!( + Self::evaluated_expressions("print if false then 1 else 2\n") + .expect("properly parsed and typed"), + type_boxes![2 => NonCoercedInteger] + ); + assert_ne!( + Self::evaluated_expressions("print if false then 1 else 2\n") + .expect("properly parsed and typed"), + type_boxes![1 => NonCoercedInteger] + ); } fn test_parenthesised_expression() { - assert_eq!(Self::evaluated_expressions("print (1 == 2)\n").expect("properly parsed and typed"), type_boxes![false => Boolean]); - + assert_eq!( + Self::evaluated_expressions("print (1 == 2)\n").expect("properly parsed and typed"), + type_boxes![false => Boolean] + ); } fn test_string_literal() { - assert_eq!(Self::evaluated_expressions("print \"123\"\n").expect("properly parsed and typed"), type_boxes!("123".to_string() => String)); - + assert_eq!( + Self::evaluated_expressions("print \"123\"\n").expect("properly parsed and typed"), + type_boxes!("123".to_string() => String) + ); } fn test_string_concat() { - assert_eq!(Self::evaluated_expressions("print \"123\" + \"456\"\n").expect("properly parsed and typed"), type_boxes!("123456".to_string() => String)); - + assert_eq!( + Self::evaluated_expressions("print \"123\" + \"456\"\n") + .expect("properly parsed and typed"), + type_boxes!("123456".to_string() => String) + ); } fn test_unit_literal() { - assert_eq!(Self::evaluated_expressions("print ()\n").expect("properly parsed and typed"), vec![TypeBox::Unit]); - assert_eq!(Self::evaluated_expressions("print ((((()))))\n").expect("properly parsed and typed"), vec![TypeBox::Unit]); + assert_eq!( + Self::evaluated_expressions("print ()\n").expect("properly parsed and typed"), + vec![TypeBox::Unit] + ); + assert_eq!( + Self::evaluated_expressions("print ((((()))))\n").expect("properly parsed and typed"), + vec![TypeBox::Unit] + ); } fn test_coerced_int_literal() { - assert_eq!(Self::evaluated_expressions("print 0i8\n").expect("properly parsed and typed"), type_boxes![0 => Int8]); - assert_eq!(Self::evaluated_expressions("print 0i16\n").expect("properly parsed and typed"), type_boxes![0 => Int16]); - assert_eq!(Self::evaluated_expressions("print 0i32\n").expect("properly parsed and typed"), type_boxes![0 => Int32]); - assert_eq!(Self::evaluated_expressions("print 0i64\n").expect("properly parsed and typed"), type_boxes![0 => Int64]); - - + assert_eq!( + Self::evaluated_expressions("print 0i8\n").expect("properly parsed and typed"), + type_boxes![0 => Int8] + ); + assert_eq!( + Self::evaluated_expressions("print 0i16\n").expect("properly parsed and typed"), + type_boxes![0 => Int16] + ); + assert_eq!( + Self::evaluated_expressions("print 0i32\n").expect("properly parsed and typed"), + type_boxes![0 => Int32] + ); + assert_eq!( + Self::evaluated_expressions("print 0i64\n").expect("properly parsed and typed"), + type_boxes![0 => Int64] + ); } fn test_infix_op_does_not_cause_panic_by_arithmetic_overflow() { // NOTE: this test covers other coerced int types as well, as long as the `f!` macro handles their match and computation. - assert_eq!(Self::evaluated_expressions("print 16i8 * 16i8\n").expect("properly parsed and typed"), type_boxes![0 => Int8]); - assert_eq!(Self::evaluated_expressions("print 127i8 + 127i8 + 2i8\n").expect("properly parsed and typed"), type_boxes![0 => Int8]); - assert_eq!(Self::evaluated_expressions("print 0i8 - (127i8 + 127i8 + 1i8)\n").expect("properly parsed and typed"), type_boxes![1 => Int8]); - assert_eq!(Self::evaluated_expressions("print (127i8 + 127i8 + 2i8) / 1i8\n").expect("properly parsed and typed"), type_boxes![0 => Int8]); - - + assert_eq!( + Self::evaluated_expressions("print 16i8 * 16i8\n").expect("properly parsed and typed"), + type_boxes![0 => Int8] + ); + assert_eq!( + Self::evaluated_expressions("print 127i8 + 127i8 + 2i8\n") + .expect("properly parsed and typed"), + type_boxes![0 => Int8] + ); + assert_eq!( + Self::evaluated_expressions("print 0i8 - (127i8 + 127i8 + 1i8)\n") + .expect("properly parsed and typed"), + type_boxes![1 => Int8] + ); + assert_eq!( + Self::evaluated_expressions("print (127i8 + 127i8 + 2i8) / 1i8\n") + .expect("properly parsed and typed"), + type_boxes![0 => Int8] + ); } fn test_overflowed_literal() { @@ -298,14 +527,18 @@ impl Test { const MAX: i64 = <$t>::MAX as i64; const V: i64 = MAX + 1; let src = format!("print {V}{x}", x = stringify!($t)); - let e = Self::evaluated_expressions(src.as_str()).expect_err("this operation should fail"); + let e = Self::evaluated_expressions(src.as_str()) + .expect_err("this operation should fail"); if let TestFailureCause::Parser(e) = e { - assert_eq!(e.kind(), &ParserErrorInner::OverflowedLiteral { - tp: stringify!($t).to_string().into_boxed_str(), - min: <$t>::MIN as i64, - max: MAX, - value: V, - }); + assert_eq!( + e.kind(), + &ParserErrorInner::OverflowedLiteral { + tp: stringify!($t).to_string().into_boxed_str(), + min: <$t>::MIN as i64, + max: MAX, + value: V, + } + ); } else { panic!("{e:?} is not Parser error: {e}", e = &e); } @@ -315,25 +548,33 @@ impl Test { gen!(i8); gen!(i16); gen!(i32); - - } fn test_variable_reassign() { - assert_eq!(Self::evaluated_expressions("var a = 1\na = 2\nprint a\n").expect("properly parsed and typed"), type_boxes![2 => NonCoercedInteger]); - - + assert_eq!( + Self::evaluated_expressions("var a = 1\na = 2\nprint a\n") + .expect("properly parsed and typed"), + type_boxes![2 => NonCoercedInteger] + ); } fn test_block_scope() { - assert_eq!(Self::evaluated_expressions(r#"var a = 1 + assert_eq!( + Self::evaluated_expressions( + r#"var a = 1 block var a = 2 print a end print a -"#).expect("properly parsed and typed"), type_boxes![2 => NonCoercedInteger, 1 => NonCoercedInteger]); - assert_eq!(Self::evaluated_expressions(r#"var a = 1 +"# + ) + .expect("properly parsed and typed"), + type_boxes![2 => NonCoercedInteger, 1 => NonCoercedInteger] + ); + assert_eq!( + Self::evaluated_expressions( + r#"var a = 1 var discard = block if true then block var a = 2 @@ -345,8 +586,14 @@ print a () end end -"#).expect("properly parsed and typed"), type_boxes![2 => NonCoercedInteger]); - assert_eq!(Self::evaluated_expressions(r#"var a = 1 +"# + ) + .expect("properly parsed and typed"), + type_boxes![2 => NonCoercedInteger] + ); + assert_eq!( + Self::evaluated_expressions( + r#"var a = 1 block block block @@ -355,50 +602,67 @@ block end end end -"#).expect("properly parsed and typed"), type_boxes![2 => NonCoercedInteger]); - - +"# + ) + .expect("properly parsed and typed"), + type_boxes![2 => NonCoercedInteger] + ); } fn test_tuple_type() { info!("test_tuple"); - assert_eq!(Self::evaluated_expressions(r#"var a = (1, 2) + assert_eq!( + Self::evaluated_expressions( + r#"var a = (1, 2) var b = (3, 4) print a -"#).expect("properly parsed and typed"), &[TypeBox::Tuple(DisplayTupleValue { boxes: vec![TypeBox::NonCoercedInteger(1), TypeBox::NonCoercedInteger(2)]})]); - - assert_eq!(Self::ast(r#"var a: (Int32, Int32) = (1i32, 2i32) -"#).expect("properly parsed").statement, [Statement::VariableDeclaration { - pattern: AtomicPattern::Bind(Identifier::new("a".to_string())), - expression: Expression::Tuple { - expressions: vec![ - Expression::IntLiteral { - value: 1, - suffix: Some("i32".to_string().into_boxed_str()), - }, - Expression::IntLiteral { - value: 2, - suffix: Some("i32".to_string().into_boxed_str()) - } - ], - }, - type_annotation: Some( - TypeSignature::Tuple(vec![ +"# + ) + .expect("properly parsed and typed"), + &[TypeBox::Tuple(DisplayTupleValue { + boxes: vec![TypeBox::NonCoercedInteger(1), TypeBox::NonCoercedInteger(2)] + })] + ); + + assert_eq!( + Self::ast( + r#"var a: (Int32, Int32) = (1i32, 2i32) +"# + ) + .expect("properly parsed") + .statement, + [Statement::VariableDeclaration { + pattern: AtomicPattern::Bind(Identifier::new("a".to_string())), + expression: Expression::Tuple { + expressions: vec![ + Expression::IntLiteral { + value: 1, + suffix: Some("i32".to_string().into_boxed_str()), + }, + Expression::IntLiteral { + value: 2, + suffix: Some("i32".to_string().into_boxed_str()) + } + ], + }, + type_annotation: Some(TypeSignature::Tuple(vec![ TypeSignature::Simple(Identifier::new("Int32".to_string())), TypeSignature::Simple(Identifier::new("Int32".to_string())), - ]) - ), - }]); - + ])), + }] + ); } fn test_comment() { info!("test_comment"); assert_eq!( - Self::ast(r#"//Hello, World! + Self::ast( + r#"//Hello, World! print 1 -"#).expect("properly parsed"), +"# + ) + .expect("properly parsed"), RootAst { statement: vec![ Statement::Comment { @@ -416,9 +680,12 @@ print 1 } ); assert_eq!( - Self::ast(r#"print 1 + Self::ast( + r#"print 1 //Hello, World! -"#).expect("properly parsed"), +"# + ) + .expect("properly parsed"), RootAst { statement: vec![ Statement::Print { @@ -435,42 +702,68 @@ print 1 ] } ); - - } fn test_exit() { - assert_eq!(Self::ast("exit\n").expect("properly parsed").statement, [ Statement::Exit ]); - assert_eq!(Self::evaluated_expressions("exit\n").expect("properly parsed and typed"), []); + assert_eq!( + Self::ast("exit\n").expect("properly parsed").statement, + [Statement::Exit] + ); + assert_eq!( + Self::evaluated_expressions("exit\n").expect("properly parsed and typed"), + [] + ); } fn test_underscore_discard() { - assert_eq!(Self::evaluated_expressions("var _ = 1\n").expect("properly parsed and typed"), []); - assert_eq!(Self::evaluated_expressions("var a = block\n print 1\n()\nend\n").expect("FATAL: shouldn't fail"), type_boxes![ 1 => NonCoercedInteger ]); - assert_eq!(Self::evaluated_expressions("var _ = block\n print 1\n()\nend\n").expect("properly parsed and typed"), type_boxes![ 1 => NonCoercedInteger ]); + assert_eq!( + Self::evaluated_expressions("var _ = 1\n").expect("properly parsed and typed"), + [] + ); + assert_eq!( + Self::evaluated_expressions("var a = block\n print 1\n()\nend\n") + .expect("FATAL: shouldn't fail"), + type_boxes![ 1 => NonCoercedInteger ] + ); + assert_eq!( + Self::evaluated_expressions("var _ = block\n print 1\n()\nend\n") + .expect("properly parsed and typed"), + type_boxes![ 1 => NonCoercedInteger ] + ); assert_eq!( Self::evaluated_expressions("var _ = _\n"), - Err( - TestFailureCause::Parser( - ParserError::new(ParserErrorInner::UnderscoreCanNotBeRightHandExpression, SourcePosition::try_new((1, 9)).unwrap()) - ) - ) - + Err(TestFailureCause::Parser(ParserError::new( + ParserErrorInner::UnderscoreCanNotBeRightHandExpression, + SourcePosition::try_new((1, 9)).unwrap() + ))) ); - - } fn test_type_alias() { - assert_eq!(Self::ast("type Ik = Int32\n").expect("properly parsed").statement, [ Statement::TypeAliasDeclaration { - new_name: Identifier::new("Ik".to_string()), replace_with: TypeSignature::Simple(Identifier::new("Int32".to_string())) } ]); - assert_eq!(Self::evaluated_expressions("type Ik = Int32\n").expect("properly parsed and typed"), []); - assert_eq!(Self::evaluated_expressions("type Ik = Int32\nvar t: Ik = 0i32\nprint t\n").expect("properly parsed and typed"), type_boxes![0 => Int32]); + assert_eq!( + Self::ast("type Ik = Int32\n") + .expect("properly parsed") + .statement, + [Statement::TypeAliasDeclaration { + new_name: Identifier::new("Ik".to_string()), + replace_with: TypeSignature::Simple(Identifier::new("Int32".to_string())) + }] + ); + assert_eq!( + Self::evaluated_expressions("type Ik = Int32\n").expect("properly parsed and typed"), + [] + ); + assert_eq!( + Self::evaluated_expressions("type Ik = Int32\nvar t: Ik = 0i32\nprint t\n") + .expect("properly parsed and typed"), + type_boxes![0 => Int32] + ); } fn test_shift() { - assert_eq!(Self::ast("var a = 1 << 2\n").expect("fail").statement, [ - Statement::VariableDeclaration { + assert_eq!( + Self::ast("var a = 1 << 2\n").expect("fail").statement, + [Statement::VariableDeclaration { pattern: AtomicPattern::Bind(Identifier::new("a".to_owned())), expression: Expression::BinaryOperator { lhs: Box::new(Expression::IntLiteral { @@ -484,12 +777,17 @@ print 1 operator: BinaryOperatorKind::ShiftLeft, }, type_annotation: None, - } - ]); + }] + ); - assert_eq!(Self::evaluated_expressions("var t = 1i32 << 2i32\nprint t\n").expect("properly parsed and typed"), type_boxes![4 => Int32]); - assert_eq!(Self::ast("var a = 4 >> 2\n").expect("fail").statement, [ - Statement::VariableDeclaration { + assert_eq!( + Self::evaluated_expressions("var t = 1i32 << 2i32\nprint t\n") + .expect("properly parsed and typed"), + type_boxes![4 => Int32] + ); + assert_eq!( + Self::ast("var a = 4 >> 2\n").expect("fail").statement, + [Statement::VariableDeclaration { pattern: AtomicPattern::Bind(Identifier::new("a".to_owned())), expression: Expression::BinaryOperator { lhs: Box::new(Expression::IntLiteral { @@ -503,10 +801,14 @@ print 1 operator: BinaryOperatorKind::ShiftRight, }, type_annotation: None, - } - ]); + }] + ); - assert_eq!(Self::evaluated_expressions("var t = 4i32 >> 2i32\nprint t\n").expect("properly parsed and typed"), type_boxes![1 => Int32]); + assert_eq!( + Self::evaluated_expressions("var t = 4i32 >> 2i32\nprint t\n") + .expect("properly parsed and typed"), + type_boxes![1 => Int32] + ); } fn trigger_confusion() { @@ -514,56 +816,109 @@ print 1 match Self::evaluated_expressions("var _ = ()") { Ok(e) => assert_eq!(e, type_boxes![]), - Err(e) => { errors.push(e) } + Err(e) => errors.push(e), } //* match Self::evaluated_expressions("var _ = ((), ())") { Ok(e) => assert_eq!(e, type_boxes![]), - Err(e) => { errors.push(e) } + Err(e) => errors.push(e), } match Self::evaluated_expressions("var (_, _) = ((), ())") { Ok(e) => assert_eq!(e, type_boxes![]), - Err(e) => { errors.push(e) } + Err(e) => errors.push(e), } match Self::evaluated_expressions("var (_, _) = (1, 2)") { Ok(e) => assert_eq!(e, type_boxes![]), - Err(e) => { errors.push(e) } - + Err(e) => errors.push(e), } - + //*/ - if !errors.is_empty() { panic!("ouch!: {errors:#?}"); } } - + fn test_tuple_destruction() { // literal - assert_eq!(Self::evaluated_expressions("var (a, b) = (1i32, 2i32)\nprint a\nprint b\n").expect("properly parsed and typed"), type_boxes![1 => Int32, 2 => Int32]); + assert_eq!( + Self::evaluated_expressions("var (a, b) = (1i32, 2i32)\nprint a\nprint b\n") + .expect("properly parsed and typed"), + type_boxes![1 => Int32, 2 => Int32] + ); assert_eq!( Self::evaluated_expressions("var (a, b, c, d, e, f, g, h) = (1i32, 2i32, 3i32, 4i32, 5i32, 6i32, 7i32, 8i32)\nprint a\nprint b\nprint c\nprint d\nprint e\nprint f\nprint g\nprint h\n").expect("properly parsed and typed"), type_boxes![1 => Int32, 2 => Int32, 3 => Int32, 4 => Int32, 5 => Int32, 6 => Int32, 7 => Int32, 8 => Int32] ); - assert_eq!(Self::evaluated_expressions("var (a, _) = (1i32, 2i32)\nprint a\n").expect("properly parsed and typed"), type_boxes![1 => Int32]); - assert_eq!(Self::evaluated_expressions("var (a, _) = (1i32, ())").expect("properly parsed and typed"), type_boxes![]); - assert_eq!(Self::evaluated_expressions("var (a, _) = (1i32, block\n()\nend)").expect("properly parsed and typed"), type_boxes![]); - assert_eq!(Self::evaluated_expressions("var (a, _) = (1i32, block\nprint 2i32\n()\nend)").expect("properly parsed and typed"), type_boxes![2 => Int32]); - assert_eq!(Self::evaluated_expressions("var (a, _) = (46i32, if true then 178i32 else 251i32)\nprint a\n").expect("properly parsed and typed"), type_boxes![46 => Int32]); + assert_eq!( + Self::evaluated_expressions("var (a, _) = (1i32, 2i32)\nprint a\n") + .expect("properly parsed and typed"), + type_boxes![1 => Int32] + ); + assert_eq!( + Self::evaluated_expressions("var (a, _) = (1i32, ())") + .expect("properly parsed and typed"), + type_boxes![] + ); + assert_eq!( + Self::evaluated_expressions("var (a, _) = (1i32, block\n()\nend)") + .expect("properly parsed and typed"), + type_boxes![] + ); + assert_eq!( + Self::evaluated_expressions("var (a, _) = (1i32, block\nprint 2i32\n()\nend)") + .expect("properly parsed and typed"), + type_boxes![2 => Int32] + ); + assert_eq!( + Self::evaluated_expressions( + "var (a, _) = (46i32, if true then 178i32 else 251i32)\nprint a\n" + ) + .expect("properly parsed and typed"), + type_boxes![46 => Int32] + ); // literal (nested) - assert_eq!(Self::evaluated_expressions("var (a, (b, c)) = (1i32, (2i32, 3i32))\nprint a\nprint b\nprint c\n").expect("properly parsed and typed"), type_boxes![1 => Int32, 2 => Int32, 3 => Int32]); - assert_eq!(Self::evaluated_expressions("var (a, (b, _)) = (1i32, (2i32, 3i32))\nprint a\nprint b\n").expect("properly parsed and typed"), type_boxes![1 => Int32, 2 => Int32]); + assert_eq!( + Self::evaluated_expressions( + "var (a, (b, c)) = (1i32, (2i32, 3i32))\nprint a\nprint b\nprint c\n" + ) + .expect("properly parsed and typed"), + type_boxes![1 => Int32, 2 => Int32, 3 => Int32] + ); + assert_eq!( + Self::evaluated_expressions( + "var (a, (b, _)) = (1i32, (2i32, 3i32))\nprint a\nprint b\n" + ) + .expect("properly parsed and typed"), + type_boxes![1 => Int32, 2 => Int32] + ); // var - assert_eq!(Self::evaluated_expressions("var z = (1i32, 2i32)\nvar (a, b) = z\nprint a\nprint b").expect("properly parsed and typed"), type_boxes![1 => Int32, 2 => Int32]); - assert_eq!(Self::evaluated_expressions("var z = (1i32, 2i32, 3i32)\nvar (a, b, c) = z\nprint a\nprint b\nprint c").expect("properly parsed and typed"), type_boxes![1 => Int32, 2 => Int32, 3 => Int32]); + assert_eq!( + Self::evaluated_expressions("var z = (1i32, 2i32)\nvar (a, b) = z\nprint a\nprint b") + .expect("properly parsed and typed"), + type_boxes![1 => Int32, 2 => Int32] + ); + assert_eq!( + Self::evaluated_expressions( + "var z = (1i32, 2i32, 3i32)\nvar (a, b, c) = z\nprint a\nprint b\nprint c" + ) + .expect("properly parsed and typed"), + type_boxes![1 => Int32, 2 => Int32, 3 => Int32] + ); // var (nested) - assert_eq!(Self::evaluated_expressions("var z = (1i32, (21i32, 30i32))\nvar (a, (b, c)) = z\nprint a\nprint b\nprint c").expect("properly parsed and typed"), type_boxes![1 => Int32, 21 => Int32, 30 => Int32], "var nest"); + assert_eq!( + Self::evaluated_expressions( + "var z = (1i32, (21i32, 30i32))\nvar (a, (b, c)) = z\nprint a\nprint b\nprint c" + ) + .expect("properly parsed and typed"), + type_boxes![1 => Int32, 21 => Int32, 30 => Int32], + "var nest" + ); assert_eq!(Self::evaluated_expressions("var z = (1i32, (25i32, 38i32))\nvar (a, y) = z\nvar (b, c) = y\nprint a\nprint b\nprint c").expect("properly parsed and typed"), type_boxes![1 => Int32, 25 => Int32, 38 => Int32], "double-var nest"); } } diff --git a/package/origlang-token-stream/src/lib.rs b/package/origlang-token-stream/src/lib.rs index f3d24726..9b5c0a72 100644 --- a/package/origlang-token-stream/src/lib.rs +++ b/package/origlang-token-stream/src/lib.rs @@ -1,13 +1,13 @@ #![deny(clippy::all)] #![warn(clippy::pedantic, clippy::nursery)] +use log::{debug, warn}; +use origlang_lexer::token::Token; +use origlang_lexer::Lexer; +use origlang_source_span::{Pointed, SourcePosition}; use std::backtrace::Backtrace; use std::cell::Cell; use std::panic::Location; -use log::{debug, warn}; -use origlang_source_span::{Pointed, SourcePosition}; -use origlang_lexer::Lexer; -use origlang_lexer::token::Token; pub struct TokenStream { concrete: Vec>, @@ -20,10 +20,10 @@ impl TokenStream { Self { last_position: tokens.last().map_or_else( || SourcePosition::new(1.try_into().unwrap(), 1.try_into().unwrap()), - |x| x.position + |x| x.position, ), concrete: tokens, - current_index: Cell::new(0) + current_index: Cell::new(0), } } @@ -37,14 +37,17 @@ impl TokenStream { warn!("out of bound: {o}"); warn!("stacktrace: \n{}", Backtrace::force_capture()); } - + ret } pub const fn end_of_file_token(&self) -> Pointed { - Pointed { data: Token::EndOfFile, position: self.last_position } + Pointed { + data: Token::EndOfFile, + position: self.last_position, + } } - + /// advance position by one. #[track_caller] pub fn next(&self) { @@ -75,16 +78,16 @@ impl TokenStream { impl From> for TokenStream { fn from(value: Lexer<'_>) -> Self { let mut buf = vec![]; - + loop { let n = value.next(); if n.data == Token::EndOfFile { break; } - + buf.push(n); } - + Self::new(buf) } -} \ No newline at end of file +} diff --git a/package/origlang-typecheck/src/type_check.rs b/package/origlang-typecheck/src/type_check.rs index 42d5b679..905dd4ba 100644 --- a/package/origlang-typecheck/src/type_check.rs +++ b/package/origlang-typecheck/src/type_check.rs @@ -1,14 +1,15 @@ pub mod error; - +use log::debug; +use origlang_ast::after_parse::{BinaryOperatorKind, Expression}; +use origlang_ast::{AtomicPattern, Identifier, RootAst, Statement, TypeSignature}; +use origlang_typesystem_model::{ + AssignableQueryAnswer, Type, TypedExpression, TypedIntLiteral, TypedRootAst, TypedStatement, +}; use std::cell::RefCell; use std::collections::hash_map::RandomState; use std::collections::{HashMap, VecDeque}; use std::hash::BuildHasher; -use log::debug; -use origlang_ast::after_parse::{BinaryOperatorKind, Expression}; -use origlang_ast::{AtomicPattern, Identifier, RootAst, Statement, TypeSignature}; -use origlang_typesystem_model::{AssignableQueryAnswer, Type, TypedExpression, TypedIntLiteral, TypedRootAst, TypedStatement}; use crate::type_check::error::TypeCheckError; @@ -32,23 +33,23 @@ impl TryIntoTypeCheckedForm for Expression { let lit = suffix.as_ref().map_or_else( || TypedIntLiteral::Generic(value), |s| match &**s { - "i8" => TypedIntLiteral::Bit8(value as i8), + "i8" => TypedIntLiteral::Bit8(value as i8), "i16" => TypedIntLiteral::Bit16(value as i16), "i32" => TypedIntLiteral::Bit32(value as i32), "i64" => TypedIntLiteral::Bit64(value), - _ => unreachable!() - } + _ => unreachable!(), + }, ); Ok(TypedExpression::IntLiteral(lit)) - }, + } Self::BooleanLiteral(v) => Ok(TypedExpression::BooleanLiteral(v)), Self::StringLiteral(v) => Ok(TypedExpression::StringLiteral(v)), Self::UnitLiteral => Ok(TypedExpression::UnitLiteral), Self::Variable { ident } => { let tp = checker.ctx.borrow().lookup_variable_type(&ident)?; Ok(TypedExpression::Variable { ident, tp }) - }, + } Self::BinaryOperator { lhs, rhs, operator } => { // <--- let lhs_expr = checker.check(*lhs)?; @@ -57,31 +58,40 @@ impl TryIntoTypeCheckedForm for Expression { let rhs_type = rhs_expr.actual_type(); match operator { - BinaryOperatorKind::Plus => { - match (lhs_type, rhs_type) { - (Type::GenericInteger, Type::GenericInteger) => Ok(TypedExpression::BinaryOperator { + BinaryOperatorKind::Plus => match (lhs_type, rhs_type) { + (Type::GenericInteger, Type::GenericInteger) => { + Ok(TypedExpression::BinaryOperator { lhs: Box::new(lhs_expr), rhs: Box::new(rhs_expr), operator, return_type: Type::GenericInteger, - }), - (Type::String, Type::String) => Ok(TypedExpression::BinaryOperator { + }) + } + (Type::String, Type::String) => Ok(TypedExpression::BinaryOperator { + lhs: Box::new(lhs_expr), + rhs: Box::new(rhs_expr), + operator, + return_type: Type::String, + }), + (x, y) if x == y && x.is_int_family() => { + Ok(TypedExpression::BinaryOperator { lhs: Box::new(lhs_expr), rhs: Box::new(rhs_expr), operator, - return_type: Type::String - }), - (x, y) if x == y && x.is_int_family() => Ok(TypedExpression::BinaryOperator { - lhs: Box::new(lhs_expr), - rhs: Box::new(rhs_expr), + return_type: x, + }) + } + (other_lhs, other_rhs) => { + Err(TypeChecker::invalid_combination_for_binary_operator( + Type::GenericInteger, operator, - return_type: x - }), - (other_lhs, other_rhs) => - Err(TypeChecker::invalid_combination_for_binary_operator(Type::GenericInteger, operator, Type::GenericInteger, other_lhs, other_rhs)) + Type::GenericInteger, + other_lhs, + other_rhs, + )) } - } - | BinaryOperatorKind::Minus + }, + BinaryOperatorKind::Minus | BinaryOperatorKind::Multiply | BinaryOperatorKind::Divide | BinaryOperatorKind::ThreeWay @@ -91,20 +101,32 @@ impl TryIntoTypeCheckedForm for Expression { | BinaryOperatorKind::LessEqual => { { match (&lhs_type, &rhs_type) { - (Type::GenericInteger, Type::GenericInteger) => Ok(TypedExpression::BinaryOperator { - lhs: Box::new(lhs_expr), - rhs: Box::new(rhs_expr), - operator, - return_type: Type::GenericInteger, - }), - (x, y) if x == y && x.is_int_family() => Ok(TypedExpression::BinaryOperator { - lhs: Box::new(lhs_expr), - rhs: Box::new(rhs_expr), - operator, - // it is effectively Copy - return_type: x.clone(), - }), - (_, _) => Err(TypeChecker::invalid_combination_for_binary_operator(Type::GenericInteger, operator, Type::GenericInteger, lhs_type.clone(), rhs_type.clone())) + (Type::GenericInteger, Type::GenericInteger) => { + Ok(TypedExpression::BinaryOperator { + lhs: Box::new(lhs_expr), + rhs: Box::new(rhs_expr), + operator, + return_type: Type::GenericInteger, + }) + } + (x, y) if x == y && x.is_int_family() => { + Ok(TypedExpression::BinaryOperator { + lhs: Box::new(lhs_expr), + rhs: Box::new(rhs_expr), + operator, + // it is effectively Copy + return_type: x.clone(), + }) + } + (_, _) => { + Err(TypeChecker::invalid_combination_for_binary_operator( + Type::GenericInteger, + operator, + Type::GenericInteger, + lhs_type.clone(), + rhs_type.clone(), + )) + } } } } @@ -120,7 +142,7 @@ impl TryIntoTypeCheckedForm for Expression { Err(TypeCheckError::UnableToUnifyBinaryOperatorOutputType { operator: BinaryOperatorKind::Equal, got_lhs: lhs_type, - got_rhs: rhs_type + got_rhs: rhs_type, }) } } @@ -136,7 +158,7 @@ impl TryIntoTypeCheckedForm for Expression { Err(TypeCheckError::UnableToUnifyBinaryOperatorOutputType { operator: BinaryOperatorKind::NotEqual, got_lhs: lhs_type, - got_rhs: rhs_type + got_rhs: rhs_type, }) } } @@ -146,7 +168,7 @@ impl TryIntoTypeCheckedForm for Expression { lhs: Box::new(lhs_expr), rhs: Box::new(rhs_expr), operator, - return_type: lhs_type + return_type: lhs_type, }) } else { Err(TypeCheckError::UnableToUnifyBinaryOperatorOutputType { @@ -158,7 +180,11 @@ impl TryIntoTypeCheckedForm for Expression { } } } - Self::If { condition, then_clause_value, else_clause_value } => { + Self::If { + condition, + then_clause_value, + else_clause_value, + } => { let cond_expr = checker.check(*condition)?; let then_expr = checker.check(*then_clause_value)?; let else_expr = checker.check(*else_clause_value)?; @@ -177,7 +203,7 @@ impl TryIntoTypeCheckedForm for Expression { } else { Err(TypeCheckError::UnableToUnityIfExpression { then_clause_type: then_type, - else_clause_type: else_type + else_clause_type: else_type, }) } } else { @@ -188,7 +214,10 @@ impl TryIntoTypeCheckedForm for Expression { }) } } - Self::Block { intermediate_statements, final_expression } => { + Self::Block { + intermediate_statements, + final_expression, + } => { // Please don't ignore intermediate statements. // TODO: add test for it let mut checked_intermediates = Vec::with_capacity(intermediate_statements.len()); @@ -211,32 +240,33 @@ impl TryIntoTypeCheckedForm for Expression { checked_expressions.push(checker.check(expr)?); } - Ok(TypedExpression::Tuple { expressions: checked_expressions }) + Ok(TypedExpression::Tuple { + expressions: checked_expressions, + }) } } } } fn handle_atomic_pattern( - expr: TypedExpression, element_binding: &AtomicPattern, checker: &TypeChecker, + expr: TypedExpression, + element_binding: &AtomicPattern, + checker: &TypeChecker, ) -> Result, TypeCheckError> { match element_binding { - AtomicPattern::Discard => { - Ok(vec![TypedStatement::EvalAndForget { - expression: expr, - }]) - }, + AtomicPattern::Discard => Ok(vec![TypedStatement::EvalAndForget { expression: expr }]), AtomicPattern::Bind(identifier) => { - checker.ctx.borrow_mut().add_known_variable(identifier.clone(), expr.actual_type()); + checker + .ctx + .borrow_mut() + .add_known_variable(identifier.clone(), expr.actual_type()); Ok(vec![TypedStatement::VariableDeclaration { identifier: identifier.clone(), expression: expr, }]) } - AtomicPattern::Tuple(tp) => { - desugar_recursive_pattern_match(tp, expr, checker) - } + AtomicPattern::Tuple(tp) => desugar_recursive_pattern_match(tp, expr, checker), } } @@ -246,7 +276,9 @@ enum RecursivePatternMatchDesugarStrategy { } fn desugar_recursive_pattern_match( - outer_destruction: &[AtomicPattern], mut rhs: TypedExpression, checker: &TypeChecker, + outer_destruction: &[AtomicPattern], + mut rhs: TypedExpression, + checker: &TypeChecker, ) -> Result, TypeCheckError> { // this is intentionally written in loop way because deep recursion causes stack-overflow during type-checking. loop { @@ -258,40 +290,59 @@ fn desugar_recursive_pattern_match( debug!("non-tuple expression"); break Err(TypeCheckError::UnsatisfiablePattern { pattern: AtomicPattern::Tuple(outer_destruction.to_vec()), - expression: TypedExpression::Variable { ident, tp: tp.clone() }, + expression: TypedExpression::Variable { + ident, + tp: tp.clone(), + }, expr_type: tp, - }) + }); }; let tuple_element_types = tuple_element_types.0; if outer_destruction.len() == tuple_element_types.len() { rhs = TypedExpression::Tuple { - expressions: tuple_element_types.iter().enumerate().map(|(i, _)| TypedExpression::ExtractTuple { - expr: Box::new( - TypedExpression::Variable { ident: ident.clone(), tp: Type::tuple(tuple_element_types.clone()) } - ), - index: i, - }).collect(), + expressions: tuple_element_types + .iter() + .enumerate() + .map(|(i, _)| TypedExpression::ExtractTuple { + expr: Box::new(TypedExpression::Variable { + ident: ident.clone(), + tp: Type::tuple(tuple_element_types.clone()), + }), + index: i, + }) + .collect(), }; - continue + continue; } debug!("tuple arity mismatch"); break Err(TypeCheckError::UnsatisfiablePattern { pattern: AtomicPattern::Tuple(outer_destruction.to_vec()), - expression: TypedExpression::Variable { ident, tp: Type::tuple(tuple_element_types.clone()) }, + expression: TypedExpression::Variable { + ident, + tp: Type::tuple(tuple_element_types.clone()), + }, expr_type: Type::tuple(tuple_element_types), - }) + }); } - TypedExpression::Block { inner: _, final_expression, return_type: _ } => { + TypedExpression::Block { + inner: _, + final_expression, + return_type: _, + } => { // TODO: how can we handle inner statement? rhs = *final_expression; continue; } TypedExpression::Tuple { expressions } => { - let m = outer_destruction.iter().zip(expressions).map(|(element_binding, expression)| { - handle_atomic_pattern(expression, element_binding, checker) - }).collect::>(); + let m = outer_destruction + .iter() + .zip(expressions) + .map(|(element_binding, expression)| { + handle_atomic_pattern(expression, element_binding, checker) + }) + .collect::>(); let mut k = vec![]; @@ -300,17 +351,31 @@ fn desugar_recursive_pattern_match( k.extend(y); } - break Ok(k) + break Ok(k); } TypedExpression::ExtractTuple { expr, index } => { let expr = *expr; let expr = match expr { - TypedExpression::If { condition, then, els, return_type } => { - RecursivePatternMatchDesugarStrategy::Simple(TypedExpression::If { condition, then, els, return_type: return_type.as_tuple().expect("oops 15").0[index].clone() }) - } - TypedExpression::Block { inner, final_expression, return_type } => { - RecursivePatternMatchDesugarStrategy::Simple(TypedExpression::Block { inner, final_expression, return_type: return_type.as_tuple().expect("oops 4").0[index].clone() }) - } + TypedExpression::If { + condition, + then, + els, + return_type, + } => RecursivePatternMatchDesugarStrategy::Simple(TypedExpression::If { + condition, + then, + els, + return_type: return_type.as_tuple().expect("oops 15").0[index].clone(), + }), + TypedExpression::Block { + inner, + final_expression, + return_type, + } => RecursivePatternMatchDesugarStrategy::Simple(TypedExpression::Block { + inner, + final_expression, + return_type: return_type.as_tuple().expect("oops 4").0[index].clone(), + }), TypedExpression::Tuple { expressions } => { RecursivePatternMatchDesugarStrategy::Simple(expressions[index].clone()) } @@ -330,10 +395,14 @@ fn desugar_recursive_pattern_match( let new_ident = checker.make_fresh_identifier(); let tp = expr.actual_type().as_tuple().expect("oh").0[index].clone(); let v = TypedExpression::Variable { - ident: new_ident.clone(), tp: tp.clone() + ident: new_ident.clone(), + tp: tp.clone(), }; - checker.ctx.borrow_mut().add_known_variable(new_ident.clone(), tp); + checker + .ctx + .borrow_mut() + .add_known_variable(new_ident.clone(), tp); let v = desugar_recursive_pattern_match(outer_destruction, v, checker)?; let mut r = VecDeque::from(v); @@ -341,11 +410,11 @@ fn desugar_recursive_pattern_match( identifier: new_ident, expression: TypedExpression::ExtractTuple { expr: Box::new(expr), - index + index, }, }); - break Ok(r.into_iter().collect::>()) + break Ok(r.into_iter().collect::>()); } } } @@ -355,40 +424,39 @@ fn desugar_recursive_pattern_match( pattern: AtomicPattern::Tuple(outer_destruction.to_vec()), expr_type: other.actual_type(), expression: other, - }) + }); } } } } -fn extract_pattern(expr: TypedExpression, pattern: &AtomicPattern, type_annotation: Option, checker: &TypeChecker) -> Result, TypeCheckError> { +fn extract_pattern( + expr: TypedExpression, + pattern: &AtomicPattern, + type_annotation: Option, + checker: &TypeChecker, +) -> Result, TypeCheckError> { let Some(type_name) = type_annotation else { // no annotations, just set its type (type-inference) from the expr - return handle_atomic_pattern(expr, pattern, checker) + return handle_atomic_pattern(expr, pattern, checker); }; let Ok(dest) = checker.lower_type_signature_into_type(&type_name) else { - return Err(TypeCheckError::UnknownType { - name: type_name - }) + return Err(TypeCheckError::UnknownType { name: type_name }); }; match dest.is_assignable(&expr.actual_type()) { - AssignableQueryAnswer::Yes => { - handle_atomic_pattern(expr, pattern, checker) - }, + AssignableQueryAnswer::Yes => handle_atomic_pattern(expr, pattern, checker), AssignableQueryAnswer::PossibleIfCoerceSourceImplicitly => { Err(TypeCheckError::UnassignableType { from: expr.actual_type(), to: dest, }) } - AssignableQueryAnswer::No => { - Err(TypeCheckError::UnassignableType { - from: expr.actual_type(), - to: dest, - }) - } + AssignableQueryAnswer::No => Err(TypeCheckError::UnassignableType { + from: expr.actual_type(), + to: dest, + }), } } @@ -398,13 +466,22 @@ impl TryIntoTypeCheckedForm for Statement { fn type_check(self, checker: &TypeChecker) -> Result { match self { - Self::Print { expression } => checker.check(expression).map(|e| vec![TypedStatement::Print { expression: e }]), - Self::VariableDeclaration { pattern, expression, type_annotation } => { + Self::Print { expression } => checker + .check(expression) + .map(|e| vec![TypedStatement::Print { expression: e }]), + Self::VariableDeclaration { + pattern, + expression, + type_annotation, + } => { let checked = checker.check(expression)?; extract_pattern(checked, &pattern, type_annotation, checker) } - Self::VariableAssignment { identifier, expression } => { + Self::VariableAssignment { + identifier, + expression, + } => { let checked = checker.check(expression)?; let expected_type = checker.ctx.borrow().lookup_variable_type(&identifier)?; if checked.actual_type() == expected_type { @@ -431,13 +508,19 @@ impl TryIntoTypeCheckedForm for Statement { }]) } Self::Comment { .. } => Ok(vec![TypedStatement::Block { - inner_statements: vec![] + inner_statements: vec![], }]), Self::Exit => Ok(vec![TypedStatement::Exit]), - Self::TypeAliasDeclaration { new_name, replace_with } => { - checker.ctx.borrow_mut().known_aliases.insert(new_name, checker.lower_type_signature_into_type(&replace_with).map_err(|()| TypeCheckError::UnknownType { - name: replace_with, - })?); + Self::TypeAliasDeclaration { + new_name, + replace_with, + } => { + checker.ctx.borrow_mut().known_aliases.insert( + new_name, + checker + .lower_type_signature_into_type(&replace_with) + .map_err(|()| TypeCheckError::UnknownType { name: replace_with })?, + ); Ok(vec![]) } @@ -456,32 +539,34 @@ impl TryIntoTypeCheckedForm for RootAst { vec.extend(checker.check(x)?); } - Ok(TypedRootAst { - statements: vec, - }) + Ok(TypedRootAst { statements: vec }) } } pub struct TypeChecker { - ctx: RefCell + ctx: RefCell, } impl TypeChecker { #[expect(clippy::result_unit_err)] pub fn lower_type_signature_into_type(&self, p0: &TypeSignature) -> Result { match p0 { - TypeSignature::Simple(ident) => { - match ident.as_name() { - "Bool" => Ok(Type::Boolean), - "String" => Ok(Type::String), - "Unit" => Ok(Type::Unit), - "Int8" => Ok(Type::Int8), - "Int16" => Ok(Type::Int16), - "Int32" => Ok(Type::Int32), - "Int64" => Ok(Type::Int64), - _other => self.ctx.borrow().known_aliases.get(ident).cloned().ok_or(()) - } - } + TypeSignature::Simple(ident) => match ident.as_name() { + "Bool" => Ok(Type::Boolean), + "String" => Ok(Type::String), + "Unit" => Ok(Type::Unit), + "Int8" => Ok(Type::Int8), + "Int16" => Ok(Type::Int16), + "Int32" => Ok(Type::Int32), + "Int64" => Ok(Type::Int64), + _other => self + .ctx + .borrow() + .known_aliases + .get(ident) + .cloned() + .ok_or(()), + }, TypeSignature::Tuple(x) => { let mut types = Vec::with_capacity(x.capacity()); for ts in x { @@ -500,9 +585,16 @@ impl TypeChecker { let hello = RandomState::new().hash_one(()); let m = hello.to_ne_bytes(); let m = [ - b'_', b'_', - m[0].clamp(b'a', b'z'), m[1].clamp(b'a', b'z'), m[2].clamp(b'a', b'z'), m[3].clamp(b'a', b'z'), - m[4].clamp(b'a', b'z'), m[5].clamp(b'a', b'z'), m[6].clamp(b'a', b'z'), m[7].clamp(b'a', b'z'), + b'_', + b'_', + m[0].clamp(b'a', b'z'), + m[1].clamp(b'a', b'z'), + m[2].clamp(b'a', b'z'), + m[3].clamp(b'a', b'z'), + m[4].clamp(b'a', b'z'), + m[5].clamp(b'a', b'z'), + m[6].clamp(b'a', b'z'), + m[7].clamp(b'a', b'z'), ]; Identifier::new(core::str::from_utf8(&m).expect("not panic").to_owned()) @@ -516,13 +608,19 @@ impl Default for TypeChecker { } impl TypeChecker { - const fn invalid_combination_for_binary_operator(accepted_lhs: Type, operator: BinaryOperatorKind, accepted_rhs: Type, got_lhs: Type, got_rhs: Type) -> TypeCheckError { + const fn invalid_combination_for_binary_operator( + accepted_lhs: Type, + operator: BinaryOperatorKind, + accepted_rhs: Type, + got_lhs: Type, + got_rhs: Type, + ) -> TypeCheckError { TypeCheckError::InvalidCombinationForBinaryOperator { accepted_lhs, operator, accepted_rhs, got_lhs, - got_rhs + got_rhs, } } @@ -538,7 +636,6 @@ impl TypeChecker { pub fn check(&self, t: T) -> Result { t.type_check(self) } - } pub struct Context { @@ -556,7 +653,10 @@ impl Context { } fn lookup_variable_type(&self, variable_name: &Identifier) -> Result { - self.known_typed_variables.get(variable_name).cloned().ok_or_else(|| TypeCheckError::UndefinedIdentifier(variable_name.clone())) + self.known_typed_variables + .get(variable_name) + .cloned() + .ok_or_else(|| TypeCheckError::UndefinedIdentifier(variable_name.clone())) } fn add_known_variable(&mut self, variable_ident: Identifier, tp: Type) { diff --git a/package/origlang-typecheck/src/type_check/error.rs b/package/origlang-typecheck/src/type_check/error.rs index b15e1ff1..ca9b97fd 100644 --- a/package/origlang-typecheck/src/type_check/error.rs +++ b/package/origlang-typecheck/src/type_check/error.rs @@ -1,8 +1,8 @@ -use thiserror::Error; use origlang_ast::after_parse::BinaryOperatorKind; use origlang_ast::{AtomicPattern, Identifier, TypeSignature}; -use origlang_typesystem_model::TypedExpression; use origlang_typesystem_model::Type; +use origlang_typesystem_model::TypedExpression; +use thiserror::Error; #[derive(Debug, Eq, PartialEq, Clone, Error)] #[expect(clippy::module_name_repetitions)] @@ -35,18 +35,13 @@ pub enum TypeCheckError { actual_type: Type, }, #[error("value of {from} cannot be assigned to {to}")] - UnassignableType { - from: Type, - to: Type, - }, + UnassignableType { from: Type, to: Type }, #[error("type {name} is not defined")] - UnknownType { - name: TypeSignature, - }, + UnknownType { name: TypeSignature }, #[error("pattern {pattern} may not be satisfied where the expression has type of {expr_type}")] UnsatisfiablePattern { pattern: AtomicPattern, expression: TypedExpression, expr_type: Type, - } + }, } diff --git a/package/origlang-typesystem-model/src/lib.rs b/package/origlang-typesystem-model/src/lib.rs index b087b51b..812b6fff 100644 --- a/package/origlang-typesystem-model/src/lib.rs +++ b/package/origlang-typesystem-model/src/lib.rs @@ -1,10 +1,10 @@ #![deny(clippy::all)] #![warn(clippy::pedantic, clippy::nursery)] -use std::fmt::{Display, Formatter}; use derive_more::Display; use origlang_ast::after_parse::BinaryOperatorKind; use origlang_ast::Identifier; +use std::fmt::{Display, Formatter}; // TODO: this is implementation detail, should be unreachable. #[derive(Clone, Eq, PartialEq, Debug)] @@ -12,7 +12,12 @@ pub struct DisplayTupleType(pub Vec); impl Display for DisplayTupleType { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let content = self.0.iter().map(ToString::to_string).collect::>().join(", "); + let content = self + .0 + .iter() + .map(ToString::to_string) + .collect::>() + .join(", "); let output = format!("({content})"); f.write_str(&output) @@ -33,15 +38,22 @@ pub struct DisplayRecordType { } impl DisplayRecordType { - #[must_use] pub fn new(identifier: Identifier, components: Vec) -> Self { + #[must_use] + pub fn new(identifier: Identifier, components: Vec) -> Self { Self { - identifier, components + identifier, + components, } } } impl Display for DisplayRecordType { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let serialized_component = self.components.iter().map(ToString::to_string).collect::>().join(", "); + let serialized_component = self + .components + .iter() + .map(ToString::to_string) + .collect::>() + .join(", "); let identifier = &self.identifier; let output = format!("{identifier} {{{serialized_component}}}"); @@ -92,15 +104,21 @@ pub enum Type { } impl Type { - #[must_use] pub const fn is_int_family(&self) -> bool { - matches!(self, Self::GenericInteger | Self::Int8 | Self::Int16 | Self::Int32 | Self::Int64) + #[must_use] + pub const fn is_int_family(&self) -> bool { + matches!( + self, + Self::GenericInteger | Self::Int8 | Self::Int16 | Self::Int32 | Self::Int64 + ) } - #[must_use] pub fn tuple(tuple_elements: Vec) -> Self { + #[must_use] + pub fn tuple(tuple_elements: Vec) -> Self { Self::Tuple(DisplayTupleType(tuple_elements)) } - - #[must_use] pub fn is_assignable(&self, from: &Self) -> AssignableQueryAnswer { + + #[must_use] + pub fn is_assignable(&self, from: &Self) -> AssignableQueryAnswer { if self.is_int_family() && from == &Self::GenericInteger { AssignableQueryAnswer::PossibleIfCoerceSourceImplicitly } else if self == from { @@ -110,7 +128,8 @@ impl Type { } } - #[must_use] pub const fn as_tuple(&self) -> Option<&DisplayTupleType> { + #[must_use] + pub const fn as_tuple(&self) -> Option<&DisplayTupleType> { if let Self::Tuple(x) = self { Some(x) } else { @@ -128,7 +147,7 @@ pub enum AssignableQueryAnswer { #[derive(Clone, Eq, PartialEq, Debug)] pub struct TypedRootAst { - pub statements: Vec + pub statements: Vec, } #[derive(Clone, Eq, PartialEq, Debug)] @@ -145,7 +164,7 @@ pub enum TypedStatement { expression: TypedExpression, }, Block { - inner_statements: Vec + inner_statements: Vec, }, EvalAndForget { expression: TypedExpression, @@ -173,7 +192,7 @@ pub enum TypedExpression { condition: Box, then: Box, els: Box, - return_type: Type + return_type: Type, }, Block { inner: Vec, @@ -181,31 +200,41 @@ pub enum TypedExpression { return_type: Type, }, Tuple { - expressions: Vec + expressions: Vec, }, ExtractTuple { expr: Box, - index: usize + index: usize, }, } impl TypedExpression { - #[must_use] pub fn actual_type(&self) -> Type { + #[must_use] + pub fn actual_type(&self) -> Type { match self { Self::IntLiteral(i) => i.actual_type(), Self::BooleanLiteral(_) => Type::Boolean, Self::StringLiteral(_) => Type::String, Self::UnitLiteral => Type::Unit, Self::Variable { tp, .. } => tp.clone(), - Self::BinaryOperator { return_type, .. } | Self::If { return_type, .. } | Self::Block { return_type, .. } => return_type.clone(), - Self::Tuple { expressions } => Type::tuple(expressions.iter().map(Self::actual_type).collect()), - Self::ExtractTuple { expr, index } => { - expr.actual_type().as_tuple().map(|y| y.0[*index].clone()).expect("the underlying expression must be tuple and index must be within its bound") + Self::BinaryOperator { return_type, .. } + | Self::If { return_type, .. } + | Self::Block { return_type, .. } => return_type.clone(), + Self::Tuple { expressions } => { + Type::tuple(expressions.iter().map(Self::actual_type).collect()) } + Self::ExtractTuple { expr, index } => expr + .actual_type() + .as_tuple() + .map(|y| y.0[*index].clone()) + .expect( + "the underlying expression must be tuple and index must be within its bound", + ), } } - #[must_use] pub fn tuple_arity(&self) -> Option { + #[must_use] + pub fn tuple_arity(&self) -> Option { if let Self::Tuple { expressions } = self { Some(expressions.len()) } else { @@ -224,7 +253,8 @@ pub enum TypedIntLiteral { } impl TypedIntLiteral { - #[must_use] pub const fn actual_type(&self) -> Type { + #[must_use] + pub const fn actual_type(&self) -> Type { match self { Self::Generic(_) => Type::GenericInteger, Self::Bit64(_) => Type::Int64,