diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 7c49201..4ffea52 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -207,4 +207,28 @@ No changelog available. 1. IR target. 1. Runtime flags. ### Fixed -1. Full issue coverage now. \ No newline at end of file +1. Full issue coverage now. + +## v8 -- Semantic solver +### Added +1. Local module importing from filesystem. +1. Added `README.md` in `Cargo.toml`. +### Changed +1. Contextual semantic solver replaced old heuristic solver. +1. Improved filetree and directories. +### Removed +1. Old numerical solver. +### Fixed +1. Imported modules now must have `.msm` file extension. + +## v9 -- Semantic-driven disambiguation +### Added +1. Functions. +1. Ambiguity resolution for function calls and implicit multiplication based on context. +### Changed +1. Definitions now are done with `:=` instead of `==` and `=` always produces equations. +### Removed +1. Old declaration constructs. +1. Locking mechanics. +### Fixed +1. CLI behaviour under stress. \ No newline at end of file diff --git a/.github/README.md b/.github/README.md index b095403..500f859 100644 --- a/.github/README.md +++ b/.github/README.md @@ -45,7 +45,7 @@ Mathsys can be tried out at the [online playground](https://abscissa.eu/playgrou ### Local installation -Install the latest version via `pip`: +Install the latest version via `cargo`: ```sh cargo install mathsys diff --git a/.gitignore b/.gitignore index b2f3fdf..db86686 100644 --- a/.gitignore +++ b/.gitignore @@ -7,9 +7,7 @@ #> IGNORE -> FOLDER INFO **/.DS_Store +.DS_Store #> IGNORE -> SYMLINK -mathsys - -#> IGNORE -> TEST FILE -file.msm \ No newline at end of file +mathsys \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 00fa925..1101432 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -36,11 +36,19 @@ "backgroundColor": "transparent", "bold": false, "italic": true - } + }, + { + "tag": "!", + "color": "#00FF00", + "strikethrough": false, + "underline": false, + "backgroundColor": "#FF00FF", + "bold": true, + "italic": false + }, ], "rust-analyzer.inlayHints.genericParameterHints.const.enable": false, "rust-analyzer.inlayHints.parameterHints.enable": false, "rust-analyzer.inlayHints.typeHints.enable": false, - "rust-analyzer.inlayHints.renderColons": false, - "python.analysis.ignore": [".venv/**"] + "rust-analyzer.inlayHints.renderColons": false } \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index cb6b8de..8ba2e73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -99,7 +99,7 @@ checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "mathsys" -version = "7.0.0" +version = "9.0.0" dependencies = [ "ahash", "colored", @@ -108,6 +108,7 @@ dependencies = [ "regex", "rustc_version_runtime", "smallvec", + "strum", "strum_macros", ] @@ -207,6 +208,12 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" + [[package]] name = "strum_macros" version = "0.27.2" diff --git a/Cargo.toml b/Cargo.toml index 9838954..c6e7fdf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,16 @@ [package] name = "mathsys" -version = "7.0.0" +version = "9.0.0" edition = "2024" authors = ["Alejandro Vaz "] description = "The Natural Language of Math." license = "MIT" repository = "https://github.com/alejandro-vaz/mathsys" +readme = ".github/README.md" + +[lib] +name = "mathsys" +path = "lib.rs" [[bin]] name = "mathsys" @@ -25,5 +30,6 @@ indexmap = "2.13.0" ahash = "0.8.12" smallvec = "1.15.1" rustc_version_runtime = "0.3.0" +strum = "0.27.2" strum_macros = "0.27.2" enum_dispatch = "0.3.13" \ No newline at end of file diff --git a/backends/latex.rs b/backends/latex.rs index ae9b8f1..747a5db 100644 --- a/backends/latex.rs +++ b/backends/latex.rs @@ -4,7 +4,10 @@ //> HEAD -> PRELUDE use crate::prelude::{ - LazyLock, Regex, Map, Captures + LazyLock, + Regex, + Map, + Captures }; diff --git a/backends/mod.rs b/backends/mod.rs new file mode 100644 index 0000000..d95b50c --- /dev/null +++ b/backends/mod.rs @@ -0,0 +1,74 @@ +//^ +//^ HEAD +//^ + +//> HEAD -> MODULES +pub(super) mod latex; + +//> HEAD -> PRELUDE +use crate::prelude::{ + dispatch +}; + +//> HEAD -> LOCAL +use self::super::{ + solver::{ + nonterminal::{ + Item, + NonTerminal + }, + context::Context + }, + syntax::{ + Start, + level1::{ + Definition, + Function, + Level1, + Node, + Equation, + Use + }, + level2::{ + Level2, + Expression + }, + level3::{ + Level3, + Term + }, + level4::{ + Limit, + Level4, + Factor + }, + level5::{ + Absolute, + Level5, + Infinite, + Variable, + Nest, + Tensor, + Whole, + Undefined, + Rational, + Call + } + } +}; + + +//^ +//^ TRAITS +//^ + +//> TRAITS -> SPAWN +pub(super) trait Spawn: Backends { + fn spawn(items: Vec, context: Option<&mut Context>) -> NonTerminal; +} + +//> BACKENDS -> TRAIT +#[dispatch] +pub(super) trait Backends { + fn latex(&self) -> String; +} \ No newline at end of file diff --git a/backends/traits.rs b/backends/traits.rs deleted file mode 100644 index 6902ce9..0000000 --- a/backends/traits.rs +++ /dev/null @@ -1,36 +0,0 @@ -//^ -//^ HEAD -//^ - -//> HEAD -> PRELUDE -use crate::prelude::{ - dispatch -}; - -//> HEAD -> LOCAL -use super::super::{ - solver::nonterminal::{Item, NonTerminal}, - syntax::{ - level1::{Declaration, Definition, Level1, Node, Equation, Use}, - level2::{Level2, Expression}, - level3::{Level3, Term}, - level4::{Limit, Level4, Factor}, - level5::{Absolute, Level5, Infinite, Variable, Nest, Tensor, Whole, Undefined, Rational} - } -}; - - -//^ -//^ TRAITS -//^ - -//> TRAITS -> SPAWN -pub trait Spawn: Backends { - fn summon(items: Vec) -> NonTerminal; -} - -//> BACKENDS -> TRAIT -#[dispatch] -pub trait Backends { - fn latex(&self) -> String; -} \ No newline at end of file diff --git a/entry.rs b/entry.rs index d431b3e..1a4c913 100644 --- a/entry.rs +++ b/entry.rs @@ -4,7 +4,11 @@ //> HEAD -> PRELUDE use crate::prelude::{ - FilePath, readFile, writeFile + FilePath, + readFile, + writeFile, + EnumString, + AsRefStr }; //> HEAD -> LOCAL @@ -29,32 +33,47 @@ pub enum Argument { //^ //> INPUT -> FILE -#[derive(Clone, Debug)] -pub struct File { - pub name: FilePath -} impl File { - pub fn read(&self) -> Result {readFile(&self.name).ok().ok_or_else(|| Issue::FileNotFound(self.name.to_str().unwrap().to_string()))} - pub async fn write(&self, extension: &str, content: String) -> () { - let mut path = self.name.clone(); +#[derive(Clone)] +pub struct File (pub FilePath); impl File { + pub fn read(&self) -> Result {readFile(&self.0).ok().ok_or_else(|| Issue::FileNotFound(self.0.to_str().unwrap().to_string()))} + pub fn write(&self, extension: &str, content: String) -> () { + let mut path = self.0.clone(); path.set_extension(extension); writeFile(path, content).unwrap(); } } //> INPUT -> FLAG -#[derive(Clone, Debug)] -pub struct Flag { - pub value: String +#[derive(EnumString, Clone, Copy, AsRefStr)] +#[strum(serialize_all = "lowercase")] +pub enum Flag { + Quiet, + Verbose } //> INPUT -> ALIAS #[derive(Clone, Debug)] -pub struct Alias { - pub letters: Vec -} +pub struct Alias(pub Vec); impl Alias {pub fn expand(self) -> Result, Issue> { + let mut converted = Vec::new(); + for letter in &self.0 {converted.push(match letter { + 'q' => Flag::Quiet, + 'v' => Flag::Verbose, + other => return Err(Issue::UnknownAliasCharacter { + alias: self, + at: converted.len() + }) + })} + return Ok(converted); +}} //> INPUT -> TARGET -#[derive(Clone, Debug)] -pub struct Target { - pub name: String +#[derive(EnumString, Clone, AsRefStr, Copy)] +#[strum(serialize_all = "lowercase")] +pub enum Target { + Help, + Version, + Tokens, + Check, + Ast, + Latex } \ No newline at end of file diff --git a/file.msm b/file.msm deleted file mode 100644 index 336a159..0000000 --- a/file.msm +++ /dev/null @@ -1,7 +0,0 @@ -x = 2 + 3*7/4 - -e == lim n->inf of (1 + 1/n)^n^ - -pi = 3.14159 - -L = |alpha - 4| diff --git a/issues.rs b/issues.rs index 7895185..e8eaa70 100644 --- a/issues.rs +++ b/issues.rs @@ -4,15 +4,25 @@ //> HEAD -> PRELUDE use crate::prelude::{ - exit, Error, Display, Formatter, Rst, Colored, currentDir, readDir, AsRefStr, Debug + Error, + Display, + Formatter, + Rst, + Colored, + currentDir, + readDir, + AsRefStr, + Debug }; //> HEAD -> LOCAL use super::{ - TARGETS, - FLAGLIASES, - tokenizer::tokenizer::MAXLEN, - entry::{Flag, Alias} + tokenizer::MAXLEN, + entry::{ + Alias, + Flag, + Target + } }; @@ -32,7 +42,7 @@ enum Style { } //> DEFAULTS -> COLOR -fn color(string: impl Colored, style: Style) -> String {return (match style { +fn color(string: impl Colored, style: Style) -> String {return match style { Style::Code => string.truecolor(0xAA, 0xAA, 0xAA), Style::Place => string.cyan(), Style::Object => string.purple().bold(), @@ -40,25 +50,25 @@ fn color(string: impl Colored, style: Style) -> String {return (match style { Style::Flag => string.blue(), Style::Alias => string.yellow(), Style::Issue => string.red() -}).to_string()} +}.to_string()} //^ //^ ISSUES //^ -//> ISSUES -> NEWIS -#[derive(AsRefStr)] +//> ISSUES -> ALL +#[derive(AsRefStr, Debug)] pub enum Issue { UnknownToken { line: u32, column: u32, code: String }, - UnknownTarget(String), + UnknownArgument(String), MissingFile, InputTooLong, - UnknownFlag(Flag), + UnknownFlag(String), UnknownAliasCharacter { alias: Alias, at: usize @@ -67,7 +77,7 @@ pub enum Issue { SyntaxError, FileNotFound(String) } impl Issue { - pub fn consume(self) -> ! {println!("{self}"); exit(&self as *const Issue as i32)} + pub fn consume(self) -> u8 {println!("{self}"); &self as *const Issue as u8} fn content(&self) -> String {match self { Issue::UnknownToken {line, column, code} => format!( "Unknown token at line {},\n\n {}\n {}", @@ -75,10 +85,10 @@ pub enum Issue { color(code as &str, Style::Code), " ".repeat(*column as usize - 1) + &color("^", Style::Place) ), - Issue::UnknownTarget(target) => format!( - "Unknown target {}.\n\nAvailable targets:\n{}", - color(target as &str, Style::Target), - TARGETS.map(|target| "- ".to_string() + &color(target.0, Style::Target) + ", " + target.1).join("\n") + Issue::UnknownArgument(argument) => format!( + "Unknown argument {}.\n\nAvailable targets:\n{}", + color(argument as &str, Style::Code), + TARGETS.iter().map(|target| "- ".to_string() + &color(target.0.as_ref(), Style::Target) + ": " + target.1).collect::>().join("\n"), ), Issue::MissingFile => format!( "Missing input file. Files available in {}:\n{}", @@ -90,19 +100,19 @@ pub enum Issue { ), Issue::UnknownFlag(flag) => format!( "Unknown flag provided: {}\n\nValid flags and aliases:\n{}", - color(&("--".to_string() + &flag.value) as &str, Style::Flag), - FLAGLIASES.map(|tuple| "- ".to_string() + &color(&('-'.to_string() + &tuple.0.to_string()) as &str, Style::Alias) + ", " + &color(&("--".to_string() + tuple.1) as &str, Style::Flag) + ": " + tuple.2).join("\n") + color(&("--".to_string() + flag.as_ref()) as &str, Style::Flag), + FLAGLIASES.map(|tuple| "- ".to_string() + &color(&('-'.to_string() + &tuple.0.to_string()) as &str, Style::Alias) + ", " + &color(&("--".to_string() + &tuple.1.as_ref()) as &str, Style::Flag) + ": " + tuple.2).join("\n") ), Issue::UnknownAliasCharacter {alias, at} => format!( "Unknown alias character provided: {}\n{}\n\nValid flags and aliases:\n{}", - color(&('-'.to_string() + &alias.letters.iter().collect::()) as &str, Style::Alias), + color(&('-'.to_string() + &alias.0.iter().collect::()) as &str, Style::Alias), " ".repeat(35 + at) + &color("^", Style::Place), - FLAGLIASES.map(|tuple| "- ".to_string() + &color(&('-'.to_string() + &tuple.0.to_string()) as &str, Style::Alias) + ", " + &color(&("--".to_string() + tuple.1) as &str, Style::Flag) + ": " + tuple.2).join("\n") + FLAGLIASES.map(|tuple| "- ".to_string() + &color(&('-'.to_string() + &tuple.0.to_string()) as &str, Style::Alias) + ", " + &color(&("--".to_string() + &tuple.1.as_ref()) as &str, Style::Flag) + ": " + tuple.2).join("\n") ), Issue::GetHelp => format!( "Available targets:\n{}\n\nAvailable flags and aliases:\n{}", - TARGETS.map(|target| "- ".to_string() + &color(target.0, Style::Target) + ": " + target.1).join("\n"), - FLAGLIASES.map(|tuple| "- ".to_string() + &color(&('-'.to_string() + &tuple.0.to_string()) as &str, Style::Alias) + ", " + &color(&("--".to_string() + tuple.1) as &str, Style::Flag) + ": " + tuple.2).join("\n") + TARGETS.iter().map(|target| "- ".to_string() + &color(target.0.as_ref(), Style::Target) + ": " + target.1).collect::>().join("\n"), + FLAGLIASES.map(|tuple| "- ".to_string() + &color(&('-'.to_string() + &tuple.0.to_string()) as &str, Style::Alias) + ", " + &color(&("--".to_string() + &tuple.1.as_ref()) as &str, Style::Flag) + ": " + tuple.2).join("\n") ), Issue::SyntaxError => format!( "Syntax error." @@ -122,4 +132,20 @@ pub enum Issue { color("\n>\n> ", Style::Issue), self.content().replace("\n", &color("\n> ", Style::Issue)), color("\n>\n", Style::Issue) -))}} impl Debug for Issue {fn fmt(&self, formatter: &mut Formatter) -> Rst {Display::fmt(&self, formatter)}} impl Error for Issue {} \ No newline at end of file +))}} impl Error for Issue {} + +//> ISSUE -> TARGETS +static TARGETS: [(Target, &'static str); 6] = [ + (Target::Help, "show this informative menu"), + (Target::Version, "check current Mathsys version"), + (Target::Tokens, "tokenize the input file"), + (Target::Check, "validate the semantics"), + (Target::Ast, "build the abstract syntax tree"), + (Target::Latex, "show the latex program equivalency") +]; + +//> ISSUE -> FLAGS AND ALIASES +static FLAGLIASES: [(char, Flag, &'static str); 2] = [ + ('q', Flag::Quiet, "silence the output"), + ('v', Flag::Verbose, "increase program verbosity") +]; \ No newline at end of file diff --git a/lib.rs b/lib.rs new file mode 100644 index 0000000..73291ce --- /dev/null +++ b/lib.rs @@ -0,0 +1,139 @@ +//^ +//^ HEAD +//^ + +//> HEAD -> FLAGS +#![allow(unused_variables)] +#![allow(nonstandard_style)] +#![feature(try_trait_v2)] +#![feature(default_field_values)] +#![feature(if_let_guard)] + +//> HEAD -> MODULES +pub mod prelude; +mod issues; +mod entry; +mod tokenizer; +mod parser; +mod solver; +mod syntax; +mod backends; +pub mod settings; + +//> HEAD -> PRELUDE +use self::prelude::{ + Time, Duration, ARCH, OS, rustcv +}; + +//> HEAD -> LOCAL +use self::{ + syntax::Start, + tokenizer::{ + token::ShallowToken, + Tokenizer, + MAXLEN + }, + parser::Parser, + solver::Solver, + backends::Backends, + issues::Issue, + settings::Settings, + entry::Target +}; + + +//^ +//^ PIPELINE +//^ + +//> PIPELINE -> VERSION +static VERSION: usize = 9; + +//> PIPELINE -> BASE TRANSFORMERS +pub static TRANSFORMERS: Transformers = Transformers::new(); + +//> PIPELINE -> TRANSFORMERS +pub struct Transformers { + tokenizer: Tokenizer, + parser: Parser, + solver: Solver +} impl Transformers { + const fn new() -> Self {return Transformers { + tokenizer: Tokenizer::new(), + parser: Parser::new(), + solver: Solver::new() + }} + pub fn run(&self, settings: &Settings) -> Vec { + let mut time = Time::now(); + let mut results = Vec::new(); + for target in &settings.targets { + results.push(Run { + time: time.elapsed(), + data: self.compute(target, settings) + }); + time = Time::now(); + } + return results; + } + fn compute(&self, target: &Target, settings: &Settings) -> Result {Ok(match target { + Target::Help => return Err(Issue::GetHelp), + Target::Version => Data::Version { + mathsys: VERSION, + rust: rustcv().minor, + .. + }, + Target::Tokens => {let tokens = self.tokens(settings)?; Data::Tokens { + length: tokens.len(), + percentage: tokens.len() as f32 / MAXLEN as f32, + tokens: tokens, + .. + }}, + Target::Check => match self.start(settings) { + Ok(start) => Data::Check, + Err(issue) => return Err(issue) + }, + Target::Ast => Data::Ast { + start: self.start(settings)? + }, + Target::Latex => Data::Latex { + representation: self.start(settings)?.latex() + } + })} + fn content(&self, settings: &Settings) -> Result {settings.files.iter().next().ok_or(Issue::MissingFile)?.read()} + fn tokens(&self, settings: &Settings) -> Result, Issue> {Ok(self.tokenizer.run(&self.content(settings)?, settings)?.into_iter().map(|token| token.fixate()).collect())} + fn start(&self, settings: &Settings) -> Result { + let content = self.content(settings)?; + let mut start = self.solver.run(&self.parser.run(&self.tokenizer.run(&content, settings)?, settings))?; + start.modules(self, settings)?; + return Ok(start); + } +} + +//> PIPELINE -> RUN +pub struct Run { + pub time: Duration, + pub data: Result +} + +//> PIPELINE -> DATA +pub enum Data { + Version { + mathsys: usize, + architecture: &'static str = ARCH, + os: &'static str = OS, + rust: u64 + }, + Tokens { + length: usize, + percentage: f32, + tokens: Vec, + maximum: usize = MAXLEN + }, + Check, + Ast { + start: Start + }, + Latex { + representation: String + } +} \ No newline at end of file diff --git a/main.rs b/main.rs index a2e61a6..46c8dcd 100644 --- a/main.rs +++ b/main.rs @@ -5,228 +5,40 @@ //> HEAD -> FLAGS #![allow(unused_variables)] #![allow(nonstandard_style)] -#![feature(try_trait_v2)] -#![feature(default_field_values)] - -//> HEAD -> MODULES -mod prelude; -mod issues; -mod entry; -mod tokenizer { - pub mod tokenizer; -} -mod parser { - pub mod grammar; - pub mod parser; -} -mod solver { - pub mod nonterminal; - pub mod solver; -} -mod syntax { - pub mod level1; - pub mod level2; - pub mod level3; - pub mod level4; - pub mod level5; - pub mod start; -} -mod backends { - pub mod latex; - pub mod traits; -} - -//> HEAD -> PRELUDE -use crate::prelude::{ - ARCH, OS, Time, rustcv, getArguments -}; +#![feature(if_let_guard)] //> HEAD -> LOCAL -use self::{ - syntax::start::Start, - tokenizer::tokenizer::{ShallowToken, Tokenizer, MAXLEN}, - parser::parser::Parser, - solver::solver::Solver, - backends::traits::Backends, - issues::Issue, - entry::{Argument, File, Flag, Target, Alias} +use mathsys::{ + prelude::ExitCode, + TRANSFORMERS, + settings::{ + Settings, + noise::Noise + }, + Data }; -//> HEAD -> VERSION -pub static VERSION: usize = 7; - - -//^ -//^ PIPELINE -//^ - -//> PIPELINE -> TRANSFORMERS -pub struct Transformers { - tokenizer: Tokenizer, - parser: Parser, - solver: Solver, - settings: Settings, - time: Time -} impl Transformers {pub fn new(arguments: &[Argument]) -> Result {return Ok(Transformers { - tokenizer: Tokenizer::new(), - parser: Parser::new(), - solver: Solver::new(), - settings: Settings::set(arguments)?, - time: Time::now() -})}} - -//> PIPELINE -> HELP -pub fn help(transformers: &mut Transformers) -> Result<(), Issue> {return Err(Issue::GetHelp)} - -//> PIPELINE -> VERSION -pub fn version(transformers: &mut Transformers) -> Result {return Ok(VERSION)} - -//> PIPELINE -> TOKENS -pub fn tokens(transformers: &mut Transformers) -> Result, Issue> { - let content = transformers.settings.file.as_ref().ok_or(Issue::MissingFile)?.read()?; - let tokens = transformers.tokenizer.run(&content, &transformers.settings)?; - return Ok(tokens.into_iter().map(|token| token.fixate()).collect()); -} - -//> PIPELINE -> LENGTH -pub fn length(transformers: &mut Transformers) -> Result { - let content = transformers.settings.file.as_ref().ok_or(Issue::MissingFile)?.read()?; - let tokens = transformers.tokenizer.run(&content, &transformers.settings)?; - return Ok(tokens.len()); -} - -//> PIPELINE -> CHECK -pub fn check(transformers: &mut Transformers) -> Result<(), Issue> { - let content = transformers.settings.file.as_ref().ok_or(Issue::MissingFile)?.read()?; - let tokens = transformers.tokenizer.run(&content, &transformers.settings)?; - let pool = transformers.parser.run(&tokens, &transformers.settings); - let start = transformers.solver.run(&pool)?; - return Ok(()); -} - -//> PIPELINE -> AST -pub fn ast(transformers: &mut Transformers) -> Result { - let content = transformers.settings.file.as_ref().ok_or(Issue::MissingFile)?.read()?; - let tokens = transformers.tokenizer.run(&content, &transformers.settings)?; - let pool = transformers.parser.run(&tokens, &transformers.settings); - let start = transformers.solver.run(&pool)?; - return Ok(start); -} - -//> PIPELINE -> LATEX -pub fn latex(transformers: &mut Transformers) -> Result { - let content = transformers.settings.file.as_ref().ok_or(Issue::MissingFile)?.read()?; - let tokens = transformers.tokenizer.run(&content, &transformers.settings)?; - let pool = transformers.parser.run(&tokens, &transformers.settings); - let start = transformers.solver.run(&pool)?; - return Ok(start.latex()); -} - - -//^ -//^ TARGETS -//^ - -//> TARGETS -> NOISE -enum Noise { - Debug, - Verbose, - Normal, - Quiet, - Zero -} impl Noise { - fn change(&mut self, shift: bool) -> () {*self = match self { - Noise::Debug => if shift {Noise::Debug} else {Noise::Verbose}, - Noise::Normal => if shift {Noise::Verbose} else {Noise::Quiet}, - Noise::Quiet => if shift {Noise::Normal} else {Noise::Zero}, - Noise::Verbose => if shift {Noise::Debug} else {Noise::Normal}, - Noise::Zero => if shift {Noise::Quiet} else {Noise::Zero} - }} - fn verbose(&self) -> bool {match self { - Noise::Debug | Noise::Verbose => true, - other => false - }} - fn quiet(&self) -> bool {match self { - Noise::Quiet | Noise::Zero => true, - other => false - }} - fn debug(&self) -> bool {if let Noise::Debug = self {true} else {false}} - fn zero(&self) -> bool {if let Noise::Zero = self {true} else {false}} -} - -//> TARGETS -> SETTINGS -pub struct Settings { - file: Option = None, - target: Option = None, - noise: Noise = Noise::Normal -} impl Settings { - pub fn set(arguments: &[Argument]) -> Result { - let mut settings = Settings {..}; - for argument in arguments {settings.apply(argument)?} - return Ok(settings); - } - pub fn apply(&mut self, argument: &Argument) -> Result<(), Issue> {return Ok(match argument { - Argument::Alias(alias) => for (index, letter) in alias.letters.iter().enumerate() {if let Some((character, aliasing)) = FLAGLIASES.iter().find_map(|(key, second, third)| if key == letter {Some((key, second))} else {None}) { - self.apply(&Argument::Flag(Flag {value: aliasing.to_string()}))?; - } else {return Err(Issue::UnknownAliasCharacter { - alias: alias.clone(), - at: index - })}} - Argument::File(file) => if self.file.is_none() {self.file = Some(file.clone())}, - Argument::Flag(flag) => match &flag.value.to_lowercase() as &str { - name if name == FLAGLIASES[0].1 => return Err(Issue::GetHelp), - name if name == FLAGLIASES[1].1 => self.noise.change(false), - name if name == FLAGLIASES[2].1 => self.noise.change(true), - other => return Err(Issue::UnknownFlag(flag.clone())) - }, - Argument::Target(target) => if self.target.is_none() {self.target = Some(target.clone())} - })} -} - //> TARGETS -> WRAPPER -pub fn main() -> Result<(), Issue> { - let mut transformers = Transformers::new(&getArguments().skip(1).map(|argument| match &argument { - file if file.split(".").last().unwrap() == "msm" => Argument::File(File { - name: argument.into() - }), - flag if flag.starts_with("--") => Argument::Flag(Flag { - value: argument.chars().skip(2).collect() - }), - alias if alias.starts_with("-") => Argument::Alias(Alias { - letters: argument.chars().skip(1).collect() - }), - target => Argument::Target(Target { - name: argument - }), - }).collect::>())?; - match &transformers.settings.target.clone().unwrap_or(Target {name: String::from("help")}).name as &str { - target if target == TARGETS[0].0 => help(&mut transformers)?, - target if target == TARGETS[1].0 => println!("Running Mathsys v{} on {}/{}/{}", version(&mut transformers)?, OS, ARCH, rustcv()), - target if target == TARGETS[2].0 => println!("{:#?}", tokens(&mut transformers)?), - target if target == TARGETS[3].0 => {let len = length(&mut transformers)?; println!("Token length: {len} / {MAXLEN} ({}%)", len as f32 / MAXLEN as f32 * 100.0)}, - target if target == TARGETS[4].0 => println!("{}", {check(&mut transformers)?; "Valid"}), - target if target == TARGETS[5].0 => println!("{:#?}", ast(&mut transformers)?), - target if target == TARGETS[6].0 => println!("{}", latex(&mut transformers)?), - other => return Err(Issue::UnknownTarget(other.to_string())) +pub fn main() -> ExitCode { + let settings = match Settings::cli() { + Ok(settings) => settings, + Err(issue) => return ExitCode::from(issue.consume()) }; - if transformers.settings.noise.verbose() {println!("Execution time: {:?}", transformers.time.elapsed())} - return Ok(()); -} - -//> TARGETS -> LIST -pub static TARGETS: [(&'static str, &'static str); 7] = [ - ("help", "show this informative menu"), - ("version", "check current Mathsys version"), - ("tokens", "tokenize the input file"), - ("length", "show length of token stream"), - ("check", "validate the semantics"), - ("ast", "build the abstract syntax tree"), - ("latex", "show the latex program equivalency") -]; - -//> TARGETS -> FLAGS AND ALIASES -pub static FLAGLIASES: [(char, &'static str, &'static str); 3] = [ - ('h', "help", "show help menu"), - ('q', "quiet", "silence the output"), - ('v', "verbose", "increase program verbosity") -]; \ No newline at end of file + for result in TRANSFORMERS.run(&settings) { + match result.data { + Err(issue) => return ExitCode::from(issue.consume()), + Ok(Data::Version {mathsys, architecture, os, rust}) => if settings.noise.verbose() {println!("Running Mathsys v{mathsys} on {architecture}.{os}~{rust}.")} else {println!("Running Mathsys v{mathsys}.")}, + Ok(Data::Tokens {length, tokens, maximum, percentage}) => match settings.noise { + Noise::Debug => println!("{length} / {maximum} ({percentage}%)\n{tokens:#?}"), + Noise::Verbose => println!("{length} / {maximum} ({percentage}%)"), + Noise::Normal | Noise::Quiet => println!("{length} / {maximum}"), + Noise::Zero => println!("{length}") + }, + Ok(Data::Check) => if !settings.noise.quiet() {println!("No issues found.")}, + Ok(Data::Ast {start}) => println!("{start:#?}"), + Ok(Data::Latex {representation}) => println!("{representation}") + } + if settings.noise.debug() {println!("Executed in {:#?}.", result.time)}; + }; + return ExitCode::SUCCESS; +} \ No newline at end of file diff --git a/parser/grammar.rs b/parser/grammar.rs index f4a3ec5..54140a3 100644 --- a/parser/grammar.rs +++ b/parser/grammar.rs @@ -4,13 +4,16 @@ //> HEAD -> PRELUDE use crate::prelude::{ - Map, Regex, Set, LazyLock + Map, + Regex, + Set, + LazyLock }; //> HEAD -> LOCAL use super::super::{ solver::nonterminal::Object, - tokenizer::tokenizer::Kind + tokenizer::token::Kind }; @@ -19,13 +22,13 @@ use super::super::{ //^ //> EBNF -> SYNTAX -pub static GRAMMAR: LazyLock>>> = LazyLock::new(|| Extensor::new().run(" +pub(super) static GRAMMAR: LazyLock>>> = LazyLock::new(|| Extensor::new().run(" //> EBNF -> START Start -> (NEWLINES? Level1 SPACES? (NEWLINES Level1 SPACES?)*)? NEWLINES? ENDOFFILE //> EBNF -> 1ºLEVEL -Declaration -> Variable SPACES? EQUALITY SPACES? Level2 -Definition -> Variable SPACES? BINDING SPACES? Level2 +Definition -> Variable SPACES? DEFINITION SPACES? Level2 +Function -> Variable OPEN SPACES? (Variable (SPACES? COMMA SPACES? Variable)* SPACES?)? CLOSE SPACES? DEFINITION SPACES? Level2 Node -> Level2 Equation -> Level2 SPACES? EQUALITY SPACES? Level2 Use -> USE SPACES MODULE @@ -49,13 +52,14 @@ Whole -> NUMBER Absolute -> PIPE SPACES? Level2 SPACES? PIPE Undefined -> UNDEFINED Rational -> RATIONAL +Call -> Variable OPEN SPACES? (Level2 (SPACES? COMMA SPACES? Level2)* SPACES?)? CLOSE //> EBNF -> LEVELS -Level1 -> Declaration | Definition | Node | Equation | Use +Level1 -> Definition | Function | Node | Equation | Use Level2 -> Expression Level3 -> Term Level4 -> Factor | Limit -Level5 -> Infinite | Variable | Nest | Tensor | Whole | Absolute | Undefined | Rational +Level5 -> Infinite | Variable | Nest | Tensor | Whole | Absolute | Undefined | Rational | Call ")); //> EBNF -> PATTERNS @@ -70,7 +74,7 @@ struct Extensor { multiple: Map, optional: Map, } impl Extensor { - pub fn new() -> Extensor {return Self { + fn new() -> Extensor {return Self { counter: 0, parentheses: Map::new(), more: Map::new(), @@ -85,7 +89,7 @@ struct Extensor { self.optional.clear(); return Set::new(); } - pub fn run(&mut self, ebnf: &str) -> Map>> { + fn run(&mut self, ebnf: &str) -> Map>> { let mut rules = self.reset(); for line in ebnf.lines().map(str::trim) { if line.is_empty() || line.starts_with("//") {continue} @@ -160,8 +164,8 @@ struct Extensor { //^ //> GRAMMAR -> RULE -#[derive(Eq, Hash, PartialEq, Clone, Copy)] -pub enum Rule { +#[derive(Eq, Hash, PartialEq, Clone)] +pub(super) enum Rule { NonTerminal(Object), Internal(u8) } impl From<&str> for Rule {fn from(value: &str) -> Self {match value { @@ -172,8 +176,8 @@ pub enum Rule { "Level3" => Rule::NonTerminal(Object::Level3), "Level4" => Rule::NonTerminal(Object::Level4), "Level5" => Rule::NonTerminal(Object::Level5), - "Declaration" => Rule::NonTerminal(Object::Declaration), "Definition" => Rule::NonTerminal(Object::Definition), + "Function" => Rule::NonTerminal(Object::Function), "Node" => Rule::NonTerminal(Object::Node), "Equation" => Rule::NonTerminal(Object::Equation), "Use" => Rule::NonTerminal(Object::Use), @@ -189,6 +193,7 @@ pub enum Rule { "Absolute" => Rule::NonTerminal(Object::Absolute), "Undefined" => Rule::NonTerminal(Object::Undefined), "Rational" => Rule::NonTerminal(Object::Rational), + "Call" => Rule::NonTerminal(Object::Call), other => panic!("{other}") }}} impl Into for Rule {fn into(self) -> Symbol {return match self { Rule::NonTerminal(object) => Symbol::NonTerminal(object), @@ -197,7 +202,7 @@ pub enum Rule { //> GRAMMAR -> SYMBOL #[derive(Clone, PartialEq, Eq, Hash)] -pub enum Symbol { +pub(super) enum Symbol { NonTerminal(Object), Internal(u8), Kind(Kind) @@ -209,7 +214,7 @@ pub enum Symbol { "COMMENT" => Symbol::Kind(Kind::COMMENT), "RATIONAL" => Symbol::Kind(Kind::RATIONAL), "SIGN" => Symbol::Kind(Kind::SIGN), - "BINDING" => Symbol::Kind(Kind::BINDING), + "DEFINITION" => Symbol::Kind(Kind::DEFINITION), "CLOSE" => Symbol::Kind(Kind::CLOSE), "COMMA" => Symbol::Kind(Kind::COMMA), "ENTER" => Symbol::Kind(Kind::ENTER), diff --git a/parser/parser.rs b/parser/mod.rs similarity index 76% rename from parser/parser.rs rename to parser/mod.rs index 9e399cb..c7ee61d 100644 --- a/parser/parser.rs +++ b/parser/mod.rs @@ -2,18 +2,32 @@ //^ HEAD //^ +//> HEAD -> MODULES +pub(super) mod grammar; + //> HEAD -> PRELUDE use crate::prelude::{ - FastMap, FastSet, Deque, SmallVec + FastMap, + FastSet, + Deque, + SmallVec }; //> HEAD -> LOCAL -use super::{ - grammar::{GRAMMAR, Rule, Symbol}, +use self::{ + grammar::{ + GRAMMAR, + Rule, + Symbol + }, super::{ Settings, solver::nonterminal::Object, - tokenizer::tokenizer::{BindedToken, ORDER, Responsibility} + tokenizer::token::{ + BindedToken, + ORDER, + Responsibility + } } }; @@ -37,7 +51,7 @@ struct State { } #[inline(always)] pub fn next(&self) -> State {return State { - rule: self.rule, + rule: self.rule.clone(), variant: self.variant, slot: self.slot + 1, starting: self.starting @@ -46,15 +60,15 @@ struct State { //> RESOURCES -> BACKPOINTER #[derive(Clone, PartialEq, Eq, Hash)] -pub struct Backpointer<'parsing> { - pub symbol: Part<'parsing>, - pub start: u32, - pub end: u32 +pub(super) struct Backpointer<'parsing> { + pub(super) symbol: Part<'parsing>, + start: u32, + end: u32 } //> RESOURCES -> PART #[derive(Clone, PartialEq, Eq, Hash)] -pub enum Part<'parsing> { +pub(super) enum Part<'parsing> { NonTerminal(Object), Internal(u8), Token(BindedToken<'parsing>) @@ -65,18 +79,18 @@ pub enum Part<'parsing> { //^ //> PARSER -> MINPOINTERS -pub static MINPOINTERS: usize = 2; +pub(super) static MINPOINTERS: usize = 2; //> PARSER -> STRUCT -pub struct Parser {} impl Parser { - pub fn new() -> Self {return Parser {}} +pub(super) struct Parser {} impl Parser { + pub(super) const fn new() -> Self {return Parser {}} #[inline(always)] fn wait(&self, index: u32, state: &State, waiting: &mut Vec>>) -> () {if let Some(symbol) = state.at() {waiting[index as usize].entry(symbol).or_default().insert(state.clone());}} #[inline(always)] fn recall<'exists, 'parsing>(&self, index: u32, state: &State, chart: &'exists mut Vec; MINPOINTERS]>>>>) -> &'exists mut FastSet; MINPOINTERS]>> {return chart[index as usize].entry(state.clone()).or_default()} #[inline(always)] fn review<'exists, 'parsing>(&self, index: u32, state: &State, chart: &'exists mut Vec; MINPOINTERS]>>>>) -> &'exists FastSet; MINPOINTERS]>> {return chart[index as usize].entry(state.clone()).or_default()} - pub fn run<'parsing>(&mut self, tokens: &Vec>, settings: &Settings) -> FastMap, FastSet; MINPOINTERS]>>> { + pub(super) fn run<'parsing>(&self, tokens: &Vec>, settings: &Settings) -> FastMap, FastSet; MINPOINTERS]>>> { let mut filtered = tokens.clone(); filtered.retain(|token| if let Responsibility::Null = ORDER.get(&token.kind).unwrap().1 {false} else {true}); let mut chart = Vec::new(); @@ -112,14 +126,14 @@ pub struct Parser {} impl Parser { return pool; } #[inline(always)] - fn complete<'parsing>(&mut self, index: u32, state: State, agenda: &mut Deque, completed: &mut FastSet, pool: &mut FastMap, FastSet; MINPOINTERS]>>>, waiting: &mut Vec>>, chart: &mut Vec; MINPOINTERS]>>>>) -> () { - for awaiting in waiting[state.starting as usize].get(&state.rule.into()).cloned().unwrap_or_default() { + fn complete<'parsing>(&self, index: u32, state: State, agenda: &mut Deque, completed: &mut FastSet, pool: &mut FastMap, FastSet; MINPOINTERS]>>>, waiting: &mut Vec>>, chart: &mut Vec; MINPOINTERS]>>>>) -> () { + for awaiting in waiting[state.starting as usize].get(&state.rule.clone().into()).cloned().unwrap_or_default() { let advanced = awaiting.next(); let stored = self.review(index, &advanced, chart).len(); let pointer = Backpointer { - symbol: match state.rule { - Rule::Internal(code) => Part::Internal(code), - Rule::NonTerminal(object) => Part::NonTerminal(object) + symbol: match &state.rule { + Rule::Internal(code) => Part::Internal(*code), + Rule::NonTerminal(object) => Part::NonTerminal(object.clone()) }, start: state.starting, end: index @@ -137,7 +151,7 @@ pub struct Parser {} impl Parser { completed.insert(state); } #[inline(always)] - fn scan<'parsing>(&mut self, index: u32, state: State, agenda: &mut Deque, token: BindedToken<'parsing>, waiting: &mut Vec>>, chart: &mut Vec; MINPOINTERS]>>>>) -> () { + fn scan<'parsing>(&self, index: u32, state: State, agenda: &mut Deque, token: BindedToken<'parsing>, waiting: &mut Vec>>, chart: &mut Vec; MINPOINTERS]>>>>) -> () { let addable = self.recall(index, &state, chart).clone().into_iter().map(|mut element| {element.push(Backpointer { symbol: Part::Token(token.clone()), start: index, @@ -148,7 +162,7 @@ pub struct Parser {} impl Parser { self.wait(index + 1, &following, waiting); } #[inline(always)] - fn predict(&mut self, index: u32, state: State, agenda: &mut Deque, waiting: &mut Vec>>, chart: &mut Vec>>>) -> () { + fn predict(&self, index: u32, state: State, agenda: &mut Deque, waiting: &mut Vec>>, chart: &mut Vec>>>) -> () { let rule = match state.at().unwrap() { Symbol::Internal(code) => Rule::Internal(code), Symbol::NonTerminal(object) => Rule::NonTerminal(object), @@ -156,7 +170,7 @@ pub struct Parser {} impl Parser { }; for variant in 0..GRAMMAR[&rule].len() { let possibility = State { - rule: rule, + rule: rule.clone(), variant: variant as u8, slot: 0, starting: index diff --git a/prelude.rs b/prelude.rs index d55e5a2..0fdfe6d 100644 --- a/prelude.rs +++ b/prelude.rs @@ -18,9 +18,12 @@ pub use std::{ write as writeFile, read_dir as readDir }, - time::Instant as Time, - process::exit, + time::{ + Instant as Time, + Duration + }, error::Error, + process::ExitCode, fmt::{ Formatter, Display, @@ -37,34 +40,40 @@ pub use std::{ }; //> PRELUDE -> COLORED -pub use colored::Colorize as Colored; +pub(super) use colored::Colorize as Colored; //> PRELUDE -> REGEX -pub use regex::bytes::{ +pub(super) use regex::bytes::{ Regex, RegexSet, Captures }; //> PRELUDE -> INDEXMAP -pub use indexmap::IndexMap; +pub(super) use indexmap::IndexMap; //> PRELUDE -> AHASH -pub use ahash::{ +pub(super) use ahash::{ AHashMap as FastMap, AHashSet as FastSet }; //> PRELUDE -> SMALLVEC -pub use smallvec::{ +pub(super) use smallvec::{ SmallVec }; //> PRELUDE -> RUSTC_VERSION_RUNTIME -pub use rustc_version_runtime::version as rustcv; +pub use rustc_version_runtime::{ + version as rustcv, + Version +}; //> PRELUDE -> STRUM_MACROS -pub use strum_macros::AsRefStr; +pub(super) use strum_macros::{ + AsRefStr, + EnumString +}; //> PRELUDE -> ENUM_DISPATCH -pub use enum_dispatch::enum_dispatch as dispatch; \ No newline at end of file +pub(super) use enum_dispatch::enum_dispatch as dispatch; \ No newline at end of file diff --git a/settings/mod.rs b/settings/mod.rs new file mode 100644 index 0000000..39f7cc9 --- /dev/null +++ b/settings/mod.rs @@ -0,0 +1,60 @@ +//^ +//^ HEAD +//^ + +//> HEAD -> MODULES +pub mod noise; + +//> HEAD -> PRELUDE +use super::prelude::{ + getArguments +}; + +//> HEAD -> LOCAL +use self::{ + super::{ + entry::{ + Argument, + Flag, + File, + Target, + Alias, + }, + issues::Issue + }, + noise::Noise +}; + + +//^ +//^ SETTINGS +//^ + +//> SETTINGS -> SETTINGS +pub struct Settings { + pub(super) files: Vec = Vec::new(), + pub targets: Vec = Vec::new(), + pub noise: Noise = Noise::Normal +} impl Settings { + pub fn cli() -> Result {return Settings::set(getArguments().skip(1).map(|argument| match &argument { + file if file.split(".").last().unwrap() == "msm" => Ok(Argument::File(File(argument.into()))), + flag if flag.starts_with("--") => Ok(Argument::Flag(Flag::try_from(&argument as &str).ok().ok_or(Issue::UnknownFlag(argument))?)), + alias if alias.starts_with("-") => Ok(Argument::Alias(Alias(argument.chars().skip(1).collect()))), + process if let Ok(target) = argument.parse::() => Ok(Argument::Target(target)), + other => Err(Issue::UnknownArgument(argument)) + }).collect::, Issue>>()?)} + pub fn set(arguments: Vec) -> Result { + let mut settings = Settings {..}; + for argument in arguments {settings.apply(argument)?} + return Ok(settings); + } + fn apply(&mut self, argument: Argument) -> Result<(), Issue> {return Ok(match argument { + Argument::Alias(alias) => for flag in alias.expand()? {self.apply(Argument::Flag(flag))?}, + Argument::File(file) => self.files.push(file), + Argument::Flag(flag) => match flag { + Flag::Quiet => self.noise.change(false), + Flag::Verbose => self.noise.change(true) + }, + Argument::Target(target) => self.targets.push(target) + })} +} \ No newline at end of file diff --git a/settings/noise.rs b/settings/noise.rs new file mode 100644 index 0000000..ccbf914 --- /dev/null +++ b/settings/noise.rs @@ -0,0 +1,30 @@ +//^ +//^ NOISE +//^ + +//> NOISE -> DEFINITION +pub enum Noise { + Debug, + Verbose, + Normal, + Quiet, + Zero +} impl Noise { + pub(super) fn change(&mut self, shift: bool) -> () {*self = match self { + Noise::Debug => if shift {Noise::Debug} else {Noise::Verbose}, + Noise::Normal => if shift {Noise::Verbose} else {Noise::Quiet}, + Noise::Quiet => if shift {Noise::Normal} else {Noise::Zero}, + Noise::Verbose => if shift {Noise::Debug} else {Noise::Normal}, + Noise::Zero => if shift {Noise::Quiet} else {Noise::Zero} + }} + pub fn verbose(&self) -> bool {match self { + Noise::Debug | Noise::Verbose => true, + other => false + }} + pub fn quiet(&self) -> bool {match self { + Noise::Quiet | Noise::Zero => true, + other => false + }} + pub fn debug(&self) -> bool {if let Noise::Debug = self {true} else {false}} + pub fn zero(&self) -> bool {if let Noise::Zero = self {true} else {false}} +} \ No newline at end of file diff --git a/solver/context.rs b/solver/context.rs new file mode 100644 index 0000000..9508df3 --- /dev/null +++ b/solver/context.rs @@ -0,0 +1,27 @@ +//^ +//^ HEAD +//^ + +//> HEAD -> PRELUDE +use crate::prelude::{ + Set +}; + +//> HEAD -> LOCAL +use super::super::syntax::level5::Variable; + + +//^ +//^ CONTEXT +//^ + +//> CONTEXT -> STRUCT +pub(crate) struct Context { + functions: Set +} impl Context { + pub(super) fn new() -> Self {return Context { + functions: Set::new() + }} + pub(crate) fn registerFn(&mut self, variable: &Variable) -> () {self.functions.insert(variable.name.clone());} + pub(crate) fn isFn(&self, variable: &Variable) -> bool {self.functions.contains(&variable.name)} +} \ No newline at end of file diff --git a/solver/mod.rs b/solver/mod.rs new file mode 100644 index 0000000..5901871 --- /dev/null +++ b/solver/mod.rs @@ -0,0 +1,106 @@ +//^ +//^ HEAD +//^ + +//> HEAD -> MODULES +pub(super) mod context; +pub(super) mod nonterminal; + +//> HEAD -> PRELUDE +use crate::prelude::{ + FastMap, + FastSet, + SmallVec +}; + +//> HEAD -> LOCAL +use self::{ + nonterminal::{ + Partition, + Object, + NonTerminal, + Item + }, + context::Context, + super::{ + issues::Issue, + tokenizer::token::{ + ORDER, + Responsibility + }, + parser::{ + Backpointer, + MINPOINTERS, + Part + }, + syntax::{ + Start, + level4::{ + Level4, + Factor + }, + level5::Level5 + } + } +}; + + +//^ +//^ SOLVER +//^ + +//> SOLVER -> STRUCT +pub(super) struct Solver {} impl Solver { + pub(super) const fn new() -> Self {return Solver {}} + pub(super) fn run<'resolving>(&self, pool: &FastMap, FastSet; MINPOINTERS]>>>) -> Result { + let Partition::NonTerminal(NonTerminal::Start(start)) = self.build(pool, pool.iter().map(|item| item.0).find(|backpointer| if let Part::NonTerminal(Object::Start) = backpointer.symbol {true} else {false}).ok_or(Issue::SyntaxError)?, &mut Context::new(), true) else {return Err(Issue::SyntaxError)}; + return Ok(start); + } + fn build<'resolving, 'active>(&self, pool: &'active FastMap, FastSet; MINPOINTERS]>>>, node: &'active Backpointer<'resolving>, context: &mut Context, write: bool) -> Partition<'resolving> { + return if let Part::Token(token) = &node.symbol {Partition::Token(token.clone())} else { + let mut children = Vec::new(); + for item in self.solve(&mut pool.get(node).unwrap().clone().into_iter().collect::; MINPOINTERS]>>>(), pool, context, &node.symbol) {match self.build(pool, &item, context, true) { + Partition::Internal(items) => children.extend(items), + Partition::NonTerminal(item) => children.push(Item::NonTerminal(item)), + Partition::Token(token) => if let Responsibility::Total = ORDER.get(&token.kind).unwrap().1 {children.push(Item::Token(token))} + }} + match node.symbol.clone() { + Part::NonTerminal(object) => Partition::NonTerminal(object.summon(children, if write {Some(context)} else {None})), + Part::Internal(code) => Partition::Internal(children), + other => unreachable!() + } + }; + } + fn solve<'resolving>(&self, candidates: &mut Vec; MINPOINTERS]>>, pool: &FastMap, FastSet; MINPOINTERS]>>>, context: &mut Context, building: &Part) -> SmallVec<[Backpointer<'resolving>; MINPOINTERS]> {for index in 0.. { + match candidates.len() { + 0 => panic!(), + 1 => return candidates.pop().unwrap(), + _ => () + }; + candidates.retain(|derivation| derivation.get(index).is_some()); + let mut symbols = Vec::new(); + for derivation in candidates.iter() { + let pointer = &derivation[index]; + if !symbols.iter().any(|thing| *thing == pointer) {symbols.push(pointer)} + } + let built = symbols.into_iter().map(|symbol| (symbol, self.build(pool, symbol, context, false))).collect::>(); + let mut winner = &built[0]; + for contender in built.iter().skip(1) { + let Partition::NonTerminal(first) = &winner.1 else {panic!()}; + let Partition::NonTerminal(second) = &contender.1 else {panic!()}; + if self.choose(first, second, context, false, building) {winner = contender} + }; + let cloned = winner.0.clone(); + candidates.retain(|derivation| derivation[index] == cloned); + }; unreachable!()} + fn choose(&self, first: &NonTerminal, second: &NonTerminal, context: &Context, reincident: bool, building: &Part) -> bool {return match (building, first, second) { + (at, NonTerminal::Level4(Level4::Factor(Factor { + value: Level5::Variable(variable), + .. + })), NonTerminal::Level4(Level4::Factor(Factor { + value: Level5::Call(call), + .. + }))) => context.isFn(variable), + other => return if reincident {panic!("{first:?} && {second:?}")} else {!self.choose(second, first, context, true, building)} + }} +} \ No newline at end of file diff --git a/solver/nonterminal.rs b/solver/nonterminal.rs index 7f2de36..8da02cd 100644 --- a/solver/nonterminal.rs +++ b/solver/nonterminal.rs @@ -2,17 +2,53 @@ //^ HEAD //^ +//> HEAD -> PRELUDE +use crate::prelude::{ + dispatch +}; + //> HEAD -> LOCAL -use super::super::{ - backends::traits::Spawn, - tokenizer::tokenizer::BindedToken, - syntax::{ - start::Start, - level1::{Level1, Declaration, Definition, Node, Equation, Use}, - level2::{Level2, Expression}, - level3::{Level3, Term}, - level4::{Level4, Factor, Limit}, - level5::{Level5, Absolute, Infinite, Nest, Rational, Tensor, Undefined, Variable, Whole} +use super::{ + context::Context, + super::{ + backends::Spawn, + tokenizer::token::BindedToken, + syntax::{ + Start, + level1::{ + Level1, + Definition, + Function, + Node, + Equation, + Use + }, + level2::{ + Level2, + Expression + }, + level3::{ + Level3, + Term + }, + level4::{ + Level4, + Factor, + Limit + }, + level5::{ + Level5, + Absolute, + Infinite, + Nest, + Rational, + Tensor, + Undefined, + Variable, + Whole, + Call + } + } } }; @@ -22,8 +58,9 @@ use super::super::{ //^ //> NONTERMINAL -> TRAIT +#[dispatch(Backends)] #[derive(Debug, Clone)] -pub enum NonTerminal { +pub(crate) enum NonTerminal { Start(Start), Level1(Level1), Level2(Level2), @@ -33,16 +70,16 @@ pub enum NonTerminal { } //> NONTERMINAL -> OBJECT -#[derive(Eq, Hash, PartialEq, Debug, Clone, Copy)] -pub enum Object { +#[derive(Eq, Hash, PartialEq, Clone)] +pub(crate) enum Object { Start, Level1, Level2, Level3, Level4, Level5, - Declaration, Definition, + Function, Node, Equation, Use, @@ -57,36 +94,34 @@ pub enum Object { Whole, Absolute, Undefined, - Rational + Rational, + Call } impl Object { - pub fn summon<'parsing>(&self, items: Vec) -> NonTerminal {return match self { - Object::Start => Start::summon(items), - Object::Level1 => if let Item::NonTerminal(NonTerminal::Level1(element)) = &items[0] {items.into_iter().next().unwrap().getnt()} else {panic!("{items:?}")}, - Object::Level2 => if let Item::NonTerminal(NonTerminal::Level2(element)) = &items[0] {items.into_iter().next().unwrap().getnt()} else {panic!("{items:?}")}, - Object::Level3 => if let Item::NonTerminal(NonTerminal::Level3(element)) = &items[0] {items.into_iter().next().unwrap().getnt()} else {panic!("{items:?}")}, - Object::Level4 => if let Item::NonTerminal(NonTerminal::Level4(element)) = &items[0] {items.into_iter().next().unwrap().getnt()} else {panic!("{items:?}")}, - Object::Level5 => if let Item::NonTerminal(NonTerminal::Level5(element)) = &items[0] {items.into_iter().next().unwrap().getnt()} else {panic!("{items:?}")}, - Object::Declaration => Declaration::summon(items), - Object::Definition => Definition::summon(items), - Object::Node => Node::summon(items), - Object::Equation => Equation::summon(items), - Object::Use => Use::summon(items), - Object::Expression => Expression::summon(items), - Object::Term => Term::summon(items), - Object::Factor => Factor::summon(items), - Object::Limit => Limit::summon(items), - Object::Infinite => Infinite::summon(items), - Object::Variable => Variable::summon(items), - Object::Nest => Nest::summon(items), - Object::Tensor => Tensor::summon(items), - Object::Whole => Whole::summon(items), - Object::Absolute => Absolute::summon(items), - Object::Undefined => Undefined::summon(items), - Object::Rational => Rational::summon(items) - }} - pub fn score(&self) -> usize {return match self { - Object::Declaration => 1, - other => 0 + pub(super) fn summon<'parsing>(self, items: Vec, context: Option<&mut Context>) -> NonTerminal {return match self { + Object::Start => Start::spawn(items, context), + Object::Level1 => if let Item::NonTerminal(nonterminal @ NonTerminal::Level1(_)) = items.into_iter().next().unwrap() {nonterminal} else {panic!()}, + Object::Level2 => if let Item::NonTerminal(nonterminal @ NonTerminal::Level2(_)) = items.into_iter().next().unwrap() {nonterminal} else {panic!()}, + Object::Level3 => if let Item::NonTerminal(nonterminal @ NonTerminal::Level3(_)) = items.into_iter().next().unwrap() {nonterminal} else {panic!()}, + Object::Level4 => if let Item::NonTerminal(nonterminal @ NonTerminal::Level4(_)) = items.into_iter().next().unwrap() {nonterminal} else {panic!()}, + Object::Level5 => if let Item::NonTerminal(nonterminal @ NonTerminal::Level5(_)) = items.into_iter().next().unwrap() {nonterminal} else {panic!()}, + Object::Definition => Definition::spawn(items, context), + Object::Function => Function::spawn(items, context), + Object::Node => Node::spawn(items, context), + Object::Equation => Equation::spawn(items, context), + Object::Use => Use::spawn(items, context), + Object::Expression => Expression::spawn(items, context), + Object::Term => Term::spawn(items, context), + Object::Factor => Factor::spawn(items, context), + Object::Limit => Limit::spawn(items, context), + Object::Infinite => Infinite::spawn(items, context), + Object::Variable => Variable::spawn(items, context), + Object::Nest => Nest::spawn(items, context), + Object::Tensor => Tensor::spawn(items, context), + Object::Whole => Whole::spawn(items, context), + Object::Absolute => Absolute::spawn(items, context), + Object::Undefined => Undefined::spawn(items, context), + Object::Rational => Rational::spawn(items, context), + Object::Call => Call::spawn(items, context) }} } @@ -97,17 +132,14 @@ pub enum Object { //> TEMPORAL -> ITEM #[derive(Clone, Debug)] -pub enum Item<'resolving> { +pub(crate) enum Item<'resolving> { NonTerminal(NonTerminal), Token(BindedToken<'resolving>) -} impl<'resolving> Item<'resolving> {fn getnt(self) -> NonTerminal {match self { - Item::NonTerminal(item) => return item, - other => panic!("{other:?}") -}}} +} //> TEMPORAL -> PARTITION #[derive(Clone)] -pub enum Partition<'resolving> { +pub(super) enum Partition<'resolving> { NonTerminal(NonTerminal), Internal(Vec>), Token(BindedToken<'resolving>) diff --git a/solver/solver.rs b/solver/solver.rs deleted file mode 100644 index 0814159..0000000 --- a/solver/solver.rs +++ /dev/null @@ -1,74 +0,0 @@ -//^ -//^ HEAD -//^ - -//> HEAD -> PRELUDE -use crate::prelude::{ - FastMap, FastSet, SmallVec -}; - -//> HEAD -> LOCAL -use super::{ - nonterminal::{Partition, Object, NonTerminal, Item}, - super::{ - issues::Issue, - tokenizer::tokenizer::{ORDER, Responsibility}, - parser::parser::{Backpointer, MINPOINTERS, Part}, - syntax::start::Start - } -}; - - -//^ -//^ SOLVER -//^ - -//> SOLVER -> STRUCT -pub struct Solver {} impl Solver { - pub fn new() -> Self {return Solver {}} - pub fn run<'resolving>(&self, pool: &FastMap, FastSet; MINPOINTERS]>>>) -> Result { - let mut memory = FastMap::new(); - let Partition::NonTerminal(NonTerminal::Start(start)) = self.best(pool, pool.iter().map(|item| item.0).find(|backpointer| if let Part::NonTerminal(Object::Start) = backpointer.symbol {true} else {false}).ok_or(Issue::SyntaxError)?, &mut memory).1.ok_or(Issue::SyntaxError)? else {return Err(Issue::SyntaxError)}; - return Ok(start); - } - fn best<'resolving, 'active>(&self, pool: &'active FastMap, FastSet; MINPOINTERS]>>>, node: &'active Backpointer<'resolving>, memory: &mut FastMap, (usize, Option>)>) -> (usize, Option>) { - if let Some(result) = memory.get(node) {return result.clone()}; - if let Part::Token(token) = node.symbol.clone() { - let result = (0, Some(Partition::Token(token))); - memory.insert(node.clone(), result.clone()); - return result; - }; - let mut bcore = 0; - let mut btree = None; - if let Some(derivations) = pool.get(node) { - for derivation in derivations { - let mut score = match node.symbol.clone() { - Part::NonTerminal(object) => object.score(), - Part::Internal(code) => 0, - other => unreachable!() - }; - let mut children = Vec::new(); - for child in derivation { - let (ccore, ctree) = self.best(pool, child, memory); - score += ccore; - if let Some(tree) = ctree {match tree { - Partition::Internal(items) => children.extend(items), - Partition::NonTerminal(item) => children.push(Item::NonTerminal(item)), - Partition::Token(token) => if let Responsibility::Total = ORDER.get(&token.kind).unwrap().1 {children.push(Item::Token(token))}, - }}; - } - if score > bcore || btree.is_none() { - bcore = score; - btree = Some(match node.symbol.clone() { - Part::NonTerminal(object) => Partition::NonTerminal(object.summon(children)), - Part::Internal(code) => Partition::Internal(children), - other => unreachable!() - }); - } - } - }; - let result = (bcore, btree); - memory.insert(node.clone(), result.clone()); - return result; - } -} \ No newline at end of file diff --git a/syntax/level1.rs b/syntax/level1.rs index 56637db..1061565 100644 --- a/syntax/level1.rs +++ b/syntax/level1.rs @@ -9,12 +9,26 @@ use crate::prelude::{ //> HEAD -> LOCAL use super::{ - start::Start, + Start, level2::Level2, - level5::{Variable, Level5}, + level5::{ + Variable, + Level5 + }, super::{ - backends::traits::{Backends, Spawn}, - solver::nonterminal::{NonTerminal, Item} + Transformers, + issues::Issue, + Settings, + solver::context::Context, + backends::{ + Backends, + Spawn + }, + solver::nonterminal::{ + NonTerminal, + Item + }, + entry::File } }; @@ -26,43 +40,22 @@ use super::{ //> 1ºLEVEL -> NAMESPACE #[dispatch(Backends)] #[derive(Debug, Clone)] -pub enum Level1 { - Declaration, +pub(crate) enum Level1 { Definition, + Function, Node, Equation, Use } -//> 1ºLEVEL -> DECLARATION -#[derive(Debug, Clone)] -pub struct Declaration { - variable: Variable, - value: Level2 -} impl Backends for Declaration { - fn latex(&self) -> String {return format!("{}={}", self.variable.latex(), self.value.latex())} -} impl Spawn for Declaration {fn summon(items: Vec) -> NonTerminal { - let mut variable = None; - let mut value = None; - for item in items {match item { - Item::NonTerminal(NonTerminal::Level5(Level5::Variable(var))) => variable = Some(var), - Item::NonTerminal(NonTerminal::Level2(level2)) => value = Some(level2), - other => panic!() - }} - return NonTerminal::Level1(Level1::Declaration(Self { - variable: variable.unwrap(), - value: value.unwrap() - })) -}} - //> 1ºLEVEL -> DEFINITION #[derive(Debug, Clone)] -pub struct Definition { - variable: Variable, - value: Level2 +pub(crate) struct Definition { + pub(crate) variable: Variable, + pub(crate) value: Level2 } impl Backends for Definition { - fn latex(&self) -> String {return format!(r"{}\equiv {}", self.variable.latex(), self.value.latex())} -} impl Spawn for Definition {fn summon(items: Vec) -> NonTerminal { + fn latex(&self) -> String {return format!("{}:={}", self.variable.latex(), self.value.latex())} +} impl Spawn for Definition {fn spawn(items: Vec, context: Option<&mut Context>) -> NonTerminal { let mut variable = None; let mut value = None; for item in items {match item { @@ -76,13 +69,36 @@ pub struct Definition { })); }} +//> 1ºLEVEL -> FUNCTION +#[derive(Debug, Clone)] +pub(crate) struct Function { + pub(crate) variable: Variable, + pub(crate) arguments: Vec, + pub(crate) value: Level2 +} impl Backends for Function { + fn latex(&self) -> String {return format!("{}({}):={}", self.variable.latex(), self.arguments.iter().map(|argument| argument.latex()).collect::>().join(","), self.value.latex())} +} impl Spawn for Function {fn spawn(items: Vec, context: Option<&mut Context>) -> NonTerminal { + let mut iterator = items.into_iter(); + let Some(Item::NonTerminal(NonTerminal::Level5(Level5::Variable(variable)))) = iterator.next() else {panic!()}; + let Some(Item::NonTerminal(NonTerminal::Level2(value))) = iterator.next_back() else {panic!()}; + let arguments = iterator.map(|each| if let Item::NonTerminal(NonTerminal::Level5(Level5::Variable(argument))) = each {argument} else {panic!()}).collect::>(); + if let Some(context) = context { + context.registerFn(&variable); + } + return NonTerminal::Level1(Level1::Function(Self { + variable: variable, + arguments: arguments, + value: value + })) +}} + //> 1ºLEVEL -> NODE #[derive(Debug, Clone)] -pub struct Node { - value: Level2 +pub(crate) struct Node { + pub(crate) value: Level2 } impl Backends for Node { fn latex(&self) -> String {return self.value.latex()} -} impl Spawn for Node {fn summon(items: Vec) -> NonTerminal { +} impl Spawn for Node {fn spawn(items: Vec, context: Option<&mut Context>) -> NonTerminal { return NonTerminal::Level1(Level1::Node(Self { value: if let Item::NonTerminal(NonTerminal::Level2(level2)) = items.into_iter().next().unwrap() {level2} else {panic!()} })); @@ -90,12 +106,12 @@ pub struct Node { //> 1ºLEVEL -> EQUATION #[derive(Debug, Clone)] -pub struct Equation { - left: Level2, - right: Level2 +pub(crate) struct Equation { + pub(crate) left: Level2, + pub(crate) right: Level2 } impl Backends for Equation { fn latex(&self) -> String {return format!("{}={}", self.left.latex(), self.right.latex())} -} impl Spawn for Equation {fn summon(items: Vec) -> NonTerminal { +} impl Spawn for Equation {fn spawn(items: Vec, context: Option<&mut Context>) -> NonTerminal { let mut iterator = items.into_iter(); return NonTerminal::Level1(Level1::Equation(Self { left: if let Item::NonTerminal(NonTerminal::Level2(level2)) = iterator.next().unwrap() {level2} else {panic!()}, @@ -105,9 +121,9 @@ pub struct Equation { //> 1ºLEVEL -> USE #[derive(Debug, Clone)] -pub struct Use { - module: String, - start: Option +pub(crate) struct Use { + pub(crate) module: String, + pub(crate) start: Option } impl Backends for Use { fn latex(&self) -> String { let (start, end) = match &self.start { @@ -116,9 +132,15 @@ pub struct Use { }; return format!(r"{start}\text{{use {}}}{end}", self.module); } -} impl Spawn for Use {fn summon(items: Vec) -> NonTerminal { +} impl Spawn for Use {fn spawn(items: Vec, context: Option<&mut Context>) -> NonTerminal { return NonTerminal::Level1(Level1::Use(Self { - module: if let Item::Token(token) = items.into_iter().next().unwrap() {token.value.to_string()} else {panic!()}, + module: if let Item::Token(token) = items.into_iter().next().unwrap() {token.value.trim_matches('\"').to_string()} else {panic!()}, start: None })); -}} \ No newline at end of file +}} impl Use {pub fn load(&mut self, transformers: &Transformers, settings: &Settings) -> Result<(), Issue> {return Ok(if let Ok(content) = File(self.module.clone().into()).read() { + let tokens = transformers.tokenizer.run(&content, settings)?; + let pool = transformers.parser.run(&tokens, settings); + let mut start = transformers.solver.run(&pool)?; + start.modules(transformers, settings)?; + self.start = Some(start); +})}} \ No newline at end of file diff --git a/syntax/level2.rs b/syntax/level2.rs index b45dcb7..1b1cf32 100644 --- a/syntax/level2.rs +++ b/syntax/level2.rs @@ -4,15 +4,25 @@ //> HEAD -> PRELUDE use crate::prelude::{ - take, dispatch + take, + dispatch }; //> HEAD -> LOCAL use super::{ level3::Level3, super::{ - backends::traits::{Backends, Spawn}, - solver::nonterminal::{NonTerminal, Item} + backends::{ + Backends, + Spawn + }, + solver::{ + nonterminal::{ + NonTerminal, + Item + }, + context::Context + } } }; @@ -24,17 +34,17 @@ use super::{ //> 2ºLEVEL -> NAMESPACE #[dispatch(Backends)] #[derive(Debug, Clone)] -pub enum Level2 { +pub(crate) enum Level2 { Expression } //> 2ºLEVEL -> EXPRESSION #[derive(Debug, Clone)] -pub struct Expression { - terms: Vec<(Vec, Level3)> +pub(crate) struct Expression { + pub(crate) terms: Vec<(Vec, Level3)> } impl Backends for Expression { fn latex(&self) -> String {return self.terms.iter().map(|term| term.0.iter().map(|each| if *each {'+'} else {'-'}).collect::() + &term.1.latex()).collect::()} -} impl Spawn for Expression {fn summon(items: Vec) -> NonTerminal { +} impl Spawn for Expression {fn spawn(items: Vec, context: Option<&mut Context>) -> NonTerminal { let mut terms = Vec::new(); let mut current = Vec::new(); for item in items {match item { diff --git a/syntax/level3.rs b/syntax/level3.rs index 252cb1a..e89e48e 100644 --- a/syntax/level3.rs +++ b/syntax/level3.rs @@ -11,8 +11,17 @@ use crate::prelude::{ use super::{ level4::Level4, super::{ - backends::traits::{Backends, Spawn}, - solver::nonterminal::{NonTerminal, Item} + backends::{ + Backends, + Spawn + }, + solver::{ + nonterminal::{ + NonTerminal, + Item + }, + context::Context + } } }; @@ -24,22 +33,22 @@ use super::{ //> 3ºLEVEL -> NAMESPACE #[dispatch(Backends)] #[derive(Debug, Clone)] -pub enum Level3 { +pub(crate) enum Level3 { Term } //> 3ºLEVEL -> TERM #[derive(Debug, Clone)] -pub struct Term { - numerator: Vec, - denominator: Vec +pub(crate) struct Term { + pub(crate) numerator: Vec, + pub(crate) denominator: Vec } impl Backends for Term { fn latex(&self) -> String { let numerator = self.numerator.iter().map(|factor| factor.latex()).collect::>().join(r"\cdot "); let denominator = self.denominator.iter().map(|factor| factor.latex()).collect::>().join(r"\cdot "); return if denominator.is_empty() {numerator} else {format!(r"\frac{{{}}}{{{}}}", numerator, denominator)}; } -} impl Spawn for Term {fn summon(items: Vec) -> NonTerminal { +} impl Spawn for Term {fn spawn(items: Vec, context: Option<&mut Context>) -> NonTerminal { let mut numerator = Vec::new(); let mut denominator = Vec::new(); let mut location = true; @@ -51,5 +60,5 @@ pub struct Term { return NonTerminal::Level3(Level3::Term(Self { numerator: numerator, denominator: denominator - })) + })); }} \ No newline at end of file diff --git a/syntax/level4.rs b/syntax/level4.rs index 8468b77..f60074e 100644 --- a/syntax/level4.rs +++ b/syntax/level4.rs @@ -10,10 +10,23 @@ use crate::prelude::{ //> HEAD -> LOCAL use super::{ level2::Level2, - level5::{Level5, Variable, Nest}, + level5::{ + Level5, + Variable, + Nest + }, super::{ - backends::traits::{Backends, Spawn}, - solver::nonterminal::{NonTerminal, Item} + backends::{ + Backends, + Spawn + }, + solver::{ + nonterminal::{ + NonTerminal, + Item + }, + context::Context + } } }; @@ -25,22 +38,22 @@ use super::{ //> 4ºLEVEL -> NAMESPACE #[dispatch(Backends)] #[derive(Debug, Clone)] -pub enum Level4 { +pub(crate) enum Level4 { Factor, Limit } //> 4ºLEVEL -> FACTOR #[derive(Debug, Clone)] -pub struct Factor { - value: Level5, - exponent: Option +pub(crate) struct Factor { + pub(crate) value: Level5, + pub(crate) exponent: Option } impl Backends for Factor { fn latex(&self) -> String { let exponent = if let Some(level2) = &self.exponent {&format!("^{{{}}}", level2.latex())} else {""}; return self.value.latex() + &exponent; } -} impl Spawn for Factor {fn summon(items: Vec) -> NonTerminal { +} impl Spawn for Factor {fn spawn(items: Vec, context: Option<&mut Context>) -> NonTerminal { let mut iterator = items.into_iter(); return NonTerminal::Level4(Level4::Factor(Self { value: if let Item::NonTerminal(NonTerminal::Level5(level5)) = iterator.next().unwrap() {level5} else {panic!()}, @@ -50,19 +63,19 @@ pub struct Factor { //> 4ºLEVEL -> LIMIT #[derive(Debug, Clone)] -pub struct Limit { - variable: Variable, - approach: Level2, - direction: Option, - nest: Nest, - exponent: Option +pub(crate) struct Limit { + pub(crate) variable: Variable, + pub(crate) approach: Level2, + pub(crate) direction: Option, + pub(crate) nest: Nest, + pub(crate) exponent: Option } impl Backends for Limit { fn latex(&self) -> String { let direction = if let Some(value) = self.direction {if value {"+"} else {"-"}} else {""}; let exponent = if let Some(level2) = &self.exponent {&format!("^{{{}}}", level2.latex())} else {""}; return format!(r"\lim_{{\substack{{{}\to {}{direction}}}}}{}{exponent}", self.variable.latex(), self.approach.latex(), self.nest.latex()); } -} impl Spawn for Limit {fn summon(items: Vec) -> NonTerminal { +} impl Spawn for Limit {fn spawn(items: Vec, context: Option<&mut Context>) -> NonTerminal { let mut iterator = items.into_iter(); let Some(Item::NonTerminal(NonTerminal::Level5(Level5::Variable(variable)))) = iterator.next() else {panic!()}; let Some(Item::NonTerminal(NonTerminal::Level2(approach))) = iterator.next() else {panic!()}; diff --git a/syntax/level5.rs b/syntax/level5.rs index ac2d0e1..f8faefa 100644 --- a/syntax/level5.rs +++ b/syntax/level5.rs @@ -2,6 +2,8 @@ //^ HEAD //^ +use core::panic; + //> HEAD -> PRELUDE use crate::prelude::{ dispatch @@ -12,10 +14,17 @@ use super::{ level2::Level2, super::{ backends::{ - traits::{Backends, Spawn}, + Backends, + Spawn, latex::augmentVariables }, - solver::nonterminal::{NonTerminal, Item} + solver::{ + nonterminal::{ + NonTerminal, + Item + }, + context::Context + } } }; @@ -27,7 +36,7 @@ use super::{ //> 5ºLEVEL -> NAMESPACE #[dispatch(Backends)] #[derive(Debug, Clone)] -pub enum Level5 { +pub(crate) enum Level5 { Infinite, Variable, Nest, @@ -35,22 +44,25 @@ pub enum Level5 { Whole, Absolute, Undefined, - Rational + Rational, + Call } //> 5ºLEVEL -> INFINITE #[derive(Debug, Clone)] -pub struct Infinite {} impl Backends for Infinite { +pub(crate) struct Infinite {} impl Backends for Infinite { fn latex(&self) -> String {return r"\infty ".to_string()} -} impl Spawn for Infinite {fn summon(items: Vec) -> NonTerminal {return NonTerminal::Level5(Level5::Infinite(Self {}))}} +} impl Spawn for Infinite {fn spawn(items: Vec, context: Option<&mut Context>) -> NonTerminal { + return NonTerminal::Level5(Level5::Infinite(Self {})); +}} //> 5ºLEVEL -> VARIABLE #[derive(Debug, Clone)] -pub struct Variable { - name: String +pub(crate) struct Variable { + pub(crate) name: String } impl Backends for Variable { fn latex(&self) -> String {return augmentVariables(&self.name)} -} impl Spawn for Variable {fn summon(items: Vec) -> NonTerminal { +} impl Spawn for Variable {fn spawn(items: Vec, context: Option<&mut Context>) -> NonTerminal { return NonTerminal::Level5(Level5::Variable(Self { name: if let Item::Token(token) = items.into_iter().next().unwrap() {token.value.to_string()} else {panic!()} })); @@ -58,14 +70,14 @@ pub struct Variable { //> 5ºLEVEL -> NEST #[derive(Debug, Clone)] -pub struct Nest { - value: Option +pub(crate) struct Nest { + pub(crate) value: Option } impl Backends for Nest { fn latex(&self) -> String { let inside = if let Some(level2) = &self.value {&level2.latex()} else {""}; return format!(r"\left( {inside}\right) ") } -} impl Spawn for Nest {fn summon(items: Vec) -> NonTerminal { +} impl Spawn for Nest {fn spawn(items: Vec, context: Option<&mut Context>) -> NonTerminal { return NonTerminal::Level5(Level5::Nest(Self { value: if let Some(Item::NonTerminal(NonTerminal::Level2(level2))) = items.into_iter().next() {Some(level2)} else {None} })); @@ -73,26 +85,26 @@ pub struct Nest { //> 5ºLEVEL -> TENSOR #[derive(Debug, Clone)] -pub struct Tensor { - values: Vec +pub(crate) struct Tensor { + pub(crate) values: Vec } impl Backends for Tensor { fn latex(&self) -> String { let inside = if self.values.len() == 0 {r"\; "} else {&self.values.iter().map(|value| value.latex()).collect::>().join(r"\\ ")}; return format!(r"\begin{{bmatrix}}{inside}\end{{bmatrix}}"); } -} impl Spawn for Tensor {fn summon(items: Vec) -> NonTerminal { +} impl Spawn for Tensor {fn spawn(items: Vec, context: Option<&mut Context>) -> NonTerminal { return NonTerminal::Level5(Level5::Tensor(Self { values: items.into_iter().map(|item| if let Item::NonTerminal(NonTerminal::Level2(level2)) = item {level2} else {panic!()}).collect() - })) + })); }} //> 5ºLEVEL -> WHOLE #[derive(Debug, Clone)] -pub struct Whole { - number: String +pub(crate) struct Whole { + pub(crate) number: String } impl Backends for Whole { fn latex(&self) -> String {return self.number.clone()} -} impl Spawn for Whole {fn summon(items: Vec) -> NonTerminal { +} impl Spawn for Whole {fn spawn(items: Vec, context: Option<&mut Context>) -> NonTerminal { return NonTerminal::Level5(Level5::Whole(Self { number: if let Item::Token(token) = items.into_iter().next().unwrap() {token.value.to_string()} else {panic!()} })); @@ -100,30 +112,47 @@ pub struct Whole { //> 5ºLEVEL -> ABSOLUTE #[derive(Debug, Clone)] -pub struct Absolute { - value: Level2 +pub(crate) struct Absolute { + pub(crate) value: Level2 } impl Backends for Absolute { fn latex(&self) -> String {return format!(r"\left| {}\right| ", self.value.latex())} -} impl Spawn for Absolute {fn summon(items: Vec) -> NonTerminal { +} impl Spawn for Absolute {fn spawn(items: Vec, context: Option<&mut Context>) -> NonTerminal { return NonTerminal::Level5(Level5::Absolute(Self { value: if let Item::NonTerminal(NonTerminal::Level2(level2)) = items.into_iter().next().unwrap() {level2} else {panic!()} - })) + })); }} //> 5ºLEVEL -> UNDEFINED #[derive(Debug, Clone)] -pub struct Undefined {} impl Backends for Undefined { +pub(crate) struct Undefined {} impl Backends for Undefined { fn latex(&self) -> String {return r"\left. ?\right. ".to_string()} -} impl Spawn for Undefined {fn summon(items: Vec) -> NonTerminal {return NonTerminal::Level5(Level5::Undefined(Self {}))}} +} impl Spawn for Undefined {fn spawn(items: Vec, context: Option<&mut Context>) -> NonTerminal { + return NonTerminal::Level5(Level5::Undefined(Self {})); +}} //> 5ºLEVEL -> RATIONAL #[derive(Debug, Clone)] -pub struct Rational { - number: String +pub(crate) struct Rational { + pub(crate) number: String } impl Backends for Rational { fn latex(&self) -> String {return self.number.clone()} -} impl Spawn for Rational {fn summon(items: Vec) -> NonTerminal { +} impl Spawn for Rational {fn spawn(items: Vec, context: Option<&mut Context>) -> NonTerminal { return NonTerminal::Level5(Level5::Rational(Self { number: if let Item::Token(token) = items.into_iter().next().unwrap() {token.value.to_string()} else {panic!()} + })); +}} + +//> 5ºLEVEL -> CALL +#[derive(Debug, Clone)] +pub(crate) struct Call { + pub(crate) to: Variable, + pub(crate) with: Vec +} impl Backends for Call { + fn latex(&self) -> String {return format!("{}({})", self.to.latex(), self.with.iter().map(|argument| argument.latex()).collect::>().join(","))} +} impl Spawn for Call {fn spawn(items: Vec, context: Option<&mut Context>) -> NonTerminal { + let mut iterator = items.into_iter(); + return NonTerminal::Level5(Level5::Call(Self { + to: if let Item::NonTerminal(NonTerminal::Level5(Level5::Variable(variable))) = iterator.next().unwrap() {variable} else {panic!()}, + with: iterator.map(|element| if let Item::NonTerminal(NonTerminal::Level2(level2)) = element {level2} else {panic!()}).collect() })) }} \ No newline at end of file diff --git a/syntax/mod.rs b/syntax/mod.rs new file mode 100644 index 0000000..23f3178 --- /dev/null +++ b/syntax/mod.rs @@ -0,0 +1,53 @@ +//^ +//^ HEAD +//^ + +//> HEAD -> MODULES +pub(super) mod level1; +pub(super) mod level2; +pub(super) mod level3; +pub(super) mod level4; +pub(super) mod level5; + +//> HEAD -> LOCAL +use self::{ + level1::Level1, + super::{ + issues::Issue, + Transformers, + Settings, + solver::context::Context, + backends::{ + Spawn, + Backends + }, + solver::nonterminal::{ + Item, + NonTerminal + } + } +}; + + +//^ +//^ START +//^ + +//> START -> CLASS +#[derive(Debug, Clone)] +pub struct Start { + pub(super) stream: Vec +} impl Backends for Start { + fn latex(&self) -> String { + let (start, end) = match self.stream.len() { + 0 => ("", ""), + 1 => (r"\(", r"\)"), + other => (r"\[", r"\]") + }; + return format!("{start}{}{end}", self.stream.iter().map(|element| element.latex()).collect::>().join(r"\\ ")); + } +} impl Spawn for Start {fn spawn(items: Vec, context: Option<&mut Context>) -> NonTerminal { + return NonTerminal::Start(Self { + stream: items.into_iter().map(|element| if let Item::NonTerminal(NonTerminal::Level1(level1)) = element {level1} else {panic!("{element:?}")}).collect() + }) +}} impl Start {pub(super) fn modules(&mut self, transformers: &Transformers, settings: &Settings) -> Result<(), Issue> {return Ok(for statement in &mut self.stream {if let Level1::Use(element) = statement {element.load(transformers, settings)?}})}} \ No newline at end of file diff --git a/syntax/start.rs b/syntax/start.rs deleted file mode 100644 index 7be0d06..0000000 --- a/syntax/start.rs +++ /dev/null @@ -1,34 +0,0 @@ -//^ -//^ HEAD -//^ - -//> HEAD -> LOCAL -use super::{ - level1::Level1, - super::{ - backends::traits::{Spawn, Backends}, - solver::nonterminal::{Item, NonTerminal} - } -}; - - -//^ -//^ START -//^ - -//> START -> CLASS -#[derive(Debug, Clone)] -pub struct Start { - stream: Vec -} impl Backends for Start { - fn latex(&self) -> String { - let (start, end) = match self.stream.len() { - 0 => ("", ""), - 1 => (r"\(", r"\)"), - other => (r"\[", r"\]") - }; - return format!("{start}{}{end}", self.stream.iter().map(|element| element.latex()).collect::>().join(r"\\ ")); - } -} impl Spawn for Start {fn summon(items: Vec) -> NonTerminal {return NonTerminal::Start(Self { - stream: items.into_iter().map(|element| if let Item::NonTerminal(NonTerminal::Level1(level1)) = element {level1} else {panic!("{element:?}")}).collect() -})}} \ No newline at end of file diff --git a/tokenizer/mod.rs b/tokenizer/mod.rs new file mode 100644 index 0000000..b3e82b7 --- /dev/null +++ b/tokenizer/mod.rs @@ -0,0 +1,83 @@ +//^ +//^ HEAD +//^ + +//> HEAD -> MODULES +pub(super) mod token; + +//> HEAD -> PRELUDE +use super::prelude::{ + LazyLock, + RegexSet +}; + +//> HEAD -> LOCAL +use self::{ + token::{ + ORDER, + BindedToken, + Kind + }, + super::{ + Settings, + issues::Issue + } +}; + + +//^ +//^ REGEX +//^ + +//> REGEX -> SET +static REGEXSET: LazyLock = LazyLock::new(|| RegexSet::new(ORDER.iter().map(|each| each.1.0.as_str())).unwrap()); + + +//^ +//^ TOKENIZER +//^ + +//> TOKENIZER -> MAXLEN +pub static MAXLEN: usize = 0xFFF; + +//> TOKENIZER -> POSITION +struct Position { + column: u32 = 1, + line: u32 = 1, + cursor: u64 = 0 +} + +//> TOKENIZER -> STRUCT +pub(super) struct Tokenizer {} impl Tokenizer { + pub(super) const fn new() -> Tokenizer {return Self {}} + pub(super) fn run<'tokenizing>(&self, content: &'tokenizing str, settings: &Settings) -> Result>, Issue> { + let mut position = Position {..}; + let mut tokens = Vec::with_capacity(MAXLEN); + while tokens.len() != MAXLEN { + let (token, length) = self.next(content, position.cursor).ok_or_else(|| Issue::UnknownToken { + line: position.line, + column: position.column, + code: content.lines().nth(position.line as usize - 1).unwrap().to_string() + })?; + match token.kind { + Kind::NEWLINES => {position.line += length as u32; position.column = 1}, + Kind::ENDOFFILE => {tokens.push(token); return Ok(tokens)}, + other => position.column += length as u32 + } + tokens.push(token); + position.cursor += length as u64; + }; + return Err(Issue::InputTooLong); + } + #[inline(always)] + fn next<'tokenizing>(&self, content: &'tokenizing str, cursor: u64) -> Option<(BindedToken<'tokenizing>, usize)> { + let mut best: Option<(Kind, usize)> = None; + let slice = content[cursor as usize..].as_bytes(); + for chance in REGEXSET.matches_at(slice, 0) { + let current = ORDER.get_index(chance).unwrap(); + let (kind, length) = (current.0, current.1.0.find_at(slice, 0).unwrap().len()); + if best.is_none() || best.unwrap().1 < length {best = Some((*kind, length))} + } + return if let Some(data) = best {Some((BindedToken::new(cursor, str::from_utf8(&slice[..data.1]).unwrap(), data.0), data.1))} else {None} + } +} \ No newline at end of file diff --git a/tokenizer/token.rs b/tokenizer/token.rs new file mode 100644 index 0000000..3a221c1 --- /dev/null +++ b/tokenizer/token.rs @@ -0,0 +1,112 @@ +//^ +//^ HEAD +//^ + +//> HEAD -> PRELUDE +use crate::prelude::{ + LazyLock, + IndexMap, + Regex +}; + +//^ +//^ TOKEN +//^ + +//> TOKEN -> RESPONSIBILITY +pub(crate) enum Responsibility { + Null, + Structural, + Total +} + +//> TOKEN -> KIND +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub(crate) enum Kind { + IDENTIFIER, + MODULE, + NUMBER, + OPERATOR, + COMMENT, + RATIONAL, + SIGN, + DEFINITION, + CLOSE, + COMMA, + ENTER, + EQUALITY, + EXIT, + EXPONENTIATION, + INFINITE, + LIMIT, + NEWLINES, + OF, + OPEN, + PIPE, + SPACES, + TO, + UNDEFINED, + USE, + ENDOFFILE +} + +//> TOKEN -> ORDER +pub(crate) static ORDER: LazyLock> = LazyLock::new(|| {[ + (Kind::UNDEFINED, (Regex::new(r#"^\?"#).unwrap(), Responsibility::Structural)), + (Kind::LIMIT, (Regex::new("^lim").unwrap(), Responsibility::Structural)), + (Kind::PIPE, (Regex::new(r#"^\|"#).unwrap(), Responsibility::Structural)), + (Kind::TO, (Regex::new("^->").unwrap(), Responsibility::Structural)), + (Kind::OF, (Regex::new("^of").unwrap(), Responsibility::Structural)), + (Kind::INFINITE, (Regex::new("^inf").unwrap(), Responsibility::Structural)), + (Kind::USE, (Regex::new("^use").unwrap(), Responsibility::Structural)), + (Kind::IDENTIFIER, (Regex::new("^[A-Za-zº$%]+").unwrap(), Responsibility::Total)), + (Kind::EXPONENTIATION, (Regex::new(r#"^\^"#).unwrap(), Responsibility::Structural)), + (Kind::RATIONAL, (Regex::new(r#"^[0-9]*\.[0-9]+"#).unwrap(), Responsibility::Total)), + (Kind::NUMBER, (Regex::new("^[0-9]+").unwrap(), Responsibility::Total)), + (Kind::DEFINITION, (Regex::new("^:=").unwrap(), Responsibility::Structural)), + (Kind::EQUALITY, (Regex::new("^=").unwrap(), Responsibility::Structural)), + (Kind::OPERATOR, (Regex::new(r#"^[\*\/]"#).unwrap(), Responsibility::Total)), + (Kind::SIGN, (Regex::new("^[+-]").unwrap(), Responsibility::Total)), + (Kind::OPEN, (Regex::new(r#"^\("#).unwrap(), Responsibility::Structural)), + (Kind::CLOSE, (Regex::new(r#"^\)"#).unwrap(), Responsibility::Structural)), + (Kind::ENTER, (Regex::new(r#"^\["#).unwrap(), Responsibility::Structural)), + (Kind::COMMA, (Regex::new(r#"^,"#).unwrap(), Responsibility::Structural)), + (Kind::EXIT, (Regex::new(r#"^\]"#).unwrap(), Responsibility::Structural)), + (Kind::SPACES, (Regex::new("^ +").unwrap(), Responsibility::Structural)), + (Kind::NEWLINES, (Regex::new(r#"^\n+"#).unwrap(), Responsibility::Structural)), + (Kind::MODULE, (Regex::new(r#"^"[a-z]+\.msm""#).unwrap(), Responsibility::Total)), + (Kind::COMMENT, (Regex::new(r"^\#[^\n]*").unwrap(), Responsibility::Null)), + (Kind::ENDOFFILE, (Regex::new("^$").unwrap(), Responsibility::Structural)) +].into_iter().collect()}); + + +//^ +//^ INSTANCES +//^ + +//> INSTANCES -> BINDED +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub(crate) struct BindedToken<'processing> { + start: u64, + pub(crate) value: &'processing str, + pub(crate) kind: Kind +} impl<'processing> BindedToken<'processing> { + #[inline(always)] + pub(super) fn new(start: u64, value: &'processing str, kind: Kind) -> Self {return BindedToken { + start: start, + value: value, + kind: kind + }} + #[inline(always)] + pub(crate) fn fixate(self) -> ShallowToken {return ShallowToken { + start: self.start, + kind: self.kind + }} +} + +//> INSTANCES -> SHALLOW +#[derive(Debug)] +pub struct ShallowToken { + start: u64, + kind: Kind +} \ No newline at end of file diff --git a/tokenizer/tokenizer.rs b/tokenizer/tokenizer.rs deleted file mode 100644 index 9b0ecaf..0000000 --- a/tokenizer/tokenizer.rs +++ /dev/null @@ -1,164 +0,0 @@ -//^ -//^ HEAD -//^ - -//> HEAD -> PRELUDE -use crate::prelude::{ - Regex, LazyLock, IndexMap, RegexSet -}; - -//> HEAD -> LOCAL -use super::super::{ - Settings, - issues::Issue -}; - - -//^ -//^ TOKENS -//^ - -//> TOKENS -> RESPONSIBILITY -pub enum Responsibility { - Null, - Structural, - Total -} - -//> TOKENS -> KIND -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum Kind { - IDENTIFIER, - MODULE, - NUMBER, - OPERATOR, - COMMENT, - RATIONAL, - SIGN, - BINDING, - CLOSE, - COMMA, - ENTER, - EQUALITY, - EXIT, - EXPONENTIATION, - INFINITE, - LIMIT, - NEWLINES, - OF, - OPEN, - PIPE, - SPACES, - TO, - UNDEFINED, - USE, - ENDOFFILE -} - -//> TOKENS -> BINDED -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct BindedToken<'processing> { - start: u64, - pub value: &'processing str, - pub kind: Kind -} impl<'processing> BindedToken<'processing> { - #[inline(always)] - fn new(start: u64, value: &'processing str, kind: Kind) -> Self {return BindedToken { - start: start, - value: value, - kind: kind - }} - #[inline(always)] - pub fn fixate(self) -> ShallowToken {return ShallowToken { - start: self.start, - kind: self.kind - }} -} - -//> TOKENS -> SHALLOW -#[derive(Debug)] -pub struct ShallowToken { - start: u64, - kind: Kind -} - -//> TOKENS -> ORDER -pub static ORDER: LazyLock> = LazyLock::new(|| {[ - (Kind::UNDEFINED, (Regex::new(r#"^\?"#).unwrap(), Responsibility::Structural)), - (Kind::LIMIT, (Regex::new("^lim").unwrap(), Responsibility::Structural)), - (Kind::PIPE, (Regex::new(r#"^\|"#).unwrap(), Responsibility::Structural)), - (Kind::TO, (Regex::new("^->").unwrap(), Responsibility::Structural)), - (Kind::OF, (Regex::new("^of").unwrap(), Responsibility::Structural)), - (Kind::INFINITE, (Regex::new("^inf").unwrap(), Responsibility::Structural)), - (Kind::USE, (Regex::new("^use").unwrap(), Responsibility::Structural)), - (Kind::IDENTIFIER, (Regex::new("^[A-Za-zº$%]+").unwrap(), Responsibility::Total)), - (Kind::EXPONENTIATION, (Regex::new(r#"^\^"#).unwrap(), Responsibility::Structural)), - (Kind::RATIONAL, (Regex::new(r#"^[0-9]*\.[0-9]+"#).unwrap(), Responsibility::Total)), - (Kind::NUMBER, (Regex::new("^[0-9]+").unwrap(), Responsibility::Total)), - (Kind::BINDING, (Regex::new("^==").unwrap(), Responsibility::Structural)), - (Kind::EQUALITY, (Regex::new("^=").unwrap(), Responsibility::Structural)), - (Kind::OPERATOR, (Regex::new(r#"^[\*\/]"#).unwrap(), Responsibility::Total)), - (Kind::SIGN, (Regex::new("^[+-]").unwrap(), Responsibility::Total)), - (Kind::OPEN, (Regex::new(r#"^\("#).unwrap(), Responsibility::Structural)), - (Kind::CLOSE, (Regex::new(r#"^\)"#).unwrap(), Responsibility::Structural)), - (Kind::ENTER, (Regex::new(r#"^\["#).unwrap(), Responsibility::Structural)), - (Kind::COMMA, (Regex::new(r#"^,"#).unwrap(), Responsibility::Structural)), - (Kind::EXIT, (Regex::new(r#"^\]"#).unwrap(), Responsibility::Structural)), - (Kind::SPACES, (Regex::new("^ +").unwrap(), Responsibility::Structural)), - (Kind::NEWLINES, (Regex::new(r#"^\n+"#).unwrap(), Responsibility::Structural)), - (Kind::MODULE, (Regex::new(r#"^"[a-z]+""#).unwrap(), Responsibility::Total)), - (Kind::COMMENT, (Regex::new(r"^\#[^\n]*").unwrap(), Responsibility::Null)), - (Kind::ENDOFFILE, (Regex::new("^$").unwrap(), Responsibility::Structural)) -].into_iter().collect()}); - -//> TOKENS -> REGEXSET -static REGEXSET: LazyLock = LazyLock::new(|| RegexSet::new(ORDER.iter().map(|each| each.1.0.as_str())).unwrap()); - - -//^ -//^ TOKENIZER -//^ - -//> TOKENIZER -> MAXLEN -pub static MAXLEN: usize = 0xFFF; - -//> TOKENIZER -> STRUCT -pub struct Tokenizer { - column: u32 = 1, - line: u32 = 1, - cursor: u64 = 0 -} impl Tokenizer { - pub fn new() -> Tokenizer {return Self {..}} - pub fn run<'tokenizing>(&mut self, content: &'tokenizing str, settings: &Settings) -> Result>, Issue> { - self.column = 1; - self.line = 1; - self.cursor = 0; - let mut tokens = Vec::with_capacity(MAXLEN); - while tokens.len() != MAXLEN { - let (token, length) = self.next(content).ok_or_else(|| Issue::UnknownToken { - line: self.line, - column: self.column, - code: content.lines().nth(self.line as usize - 1).unwrap().to_string() - })?; - match token.kind { - Kind::NEWLINES => {self.line += length as u32; self.column = 1}, - Kind::ENDOFFILE => {tokens.push(token); return Ok(tokens)}, - other => self.column += length as u32 - } - tokens.push(token); - self.cursor += length as u64; - }; - return Err(Issue::InputTooLong); - } - #[inline(always)] - fn next<'tokenizing>(&self, content: &'tokenizing str) -> Option<(BindedToken<'tokenizing>, usize)> { - let mut best: Option<(Kind, usize)> = None; - let slice = content[self.cursor as usize..].as_bytes(); - for chance in REGEXSET.matches_at(slice, 0) { - let current = ORDER.get_index(chance).unwrap(); - let (kind, length) = (current.0, current.1.0.find_at(slice, 0).unwrap().len()); - if best.is_none() || best.unwrap().1 < length {best = Some((*kind, length))} - } - return if let Some(data) = best {Some((BindedToken::new(self.cursor, str::from_utf8(&slice[..data.1]).unwrap(), data.0), data.1))} else {None} - } -} \ No newline at end of file