From c237e9d5ec741485fd370b790735f85cd6b3874b Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 20 Sep 2023 14:23:24 -0700 Subject: [PATCH 01/19] Added wasm dependencies --- Cargo.lock | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 6 +++ 2 files changed, 114 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 62b4b82..8a0e193 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,6 +14,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + [[package]] name = "cfg-if" version = "1.0.0" @@ -28,6 +34,7 @@ dependencies = [ "num", "termion", "toml", + "wasm-bindgen", ] [[package]] @@ -52,6 +59,12 @@ version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + [[package]] name = "memchr" version = "2.5.0" @@ -140,6 +153,30 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "proc-macro2" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -173,6 +210,17 @@ dependencies = [ "serde", ] +[[package]] +name = "syn" +version = "2.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "termion" version = "2.0.1" @@ -219,6 +267,66 @@ dependencies = [ "winnow", ] +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + [[package]] name = "winnow" version = "0.4.6" diff --git a/Cargo.toml b/Cargo.toml index 052610a..1df7360 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ path = "src/main.rs" [lib] name = "daisycalc" path = "src/lib.rs" +crate-type = ["cdylib", "rlib"] [profile.release] opt-level = 3 @@ -32,10 +33,15 @@ cfg-if = "1.0.0" num = "0.4.1" #astro-float = "0.7.1" +[package.metadata.wasm-pack.profile.release] +wasm-opt = false [target.'cfg(target_family = "unix")'.dependencies] termion = "2.0.1" +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = "0.2" + [build-dependencies] toml = "0.7.4" \ No newline at end of file From 470c3a49ed06e12ddf0b784fd6b94cb6eb7db4ff Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 20 Sep 2023 14:23:46 -0700 Subject: [PATCH 02/19] Added wasm implementations for formattedtext --- src/formattedtext/formattedtext.rs | 33 +++++++++ src/formattedtext/mod.rs | 12 ++++ .../unix_backend.rs} | 67 +++++-------------- src/formattedtext/wasm_backend.rs | 12 ++++ 4 files changed, 72 insertions(+), 52 deletions(-) create mode 100644 src/formattedtext/formattedtext.rs create mode 100644 src/formattedtext/mod.rs rename src/{formattedtext.rs => formattedtext/unix_backend.rs} (87%) create mode 100644 src/formattedtext/wasm_backend.rs diff --git a/src/formattedtext/formattedtext.rs b/src/formattedtext/formattedtext.rs new file mode 100644 index 0000000..45b5a5d --- /dev/null +++ b/src/formattedtext/formattedtext.rs @@ -0,0 +1,33 @@ +use std::ops::Add; + + +#[derive(Debug)] +#[derive(Clone)] +pub struct FormattedText { + pub(super) text: String +} + +impl ToString for FormattedText { + fn to_string(&self) -> String { return self.text.clone(); } +} + +impl FormattedText { + pub fn new(s: String) -> FormattedText { + return FormattedText { + text: s + } + } + + pub fn push(&mut self, s: &str) { + self.text.push_str(s); + } +} + + +impl Add for FormattedText { + type Output = Self; + + fn add(self, other: Self) -> Self::Output { + return FormattedText::new(format!("{}{}", self.text, other.text)); + } +} \ No newline at end of file diff --git a/src/formattedtext/mod.rs b/src/formattedtext/mod.rs new file mode 100644 index 0000000..d30b4f7 --- /dev/null +++ b/src/formattedtext/mod.rs @@ -0,0 +1,12 @@ +mod formattedtext; +pub use formattedtext::FormattedText; + + +// Select write implementation by target system +cfg_if::cfg_if! { + if #[cfg(target_family = "unix")] { + mod unix_backend; + } else if #[cfg(target_arch = "wasm32")] { + mod wasm_backend; + } +} \ No newline at end of file diff --git a/src/formattedtext.rs b/src/formattedtext/unix_backend.rs similarity index 87% rename from src/formattedtext.rs rename to src/formattedtext/unix_backend.rs index ed7c6ef..857ffed 100644 --- a/src/formattedtext.rs +++ b/src/formattedtext/unix_backend.rs @@ -1,34 +1,12 @@ +use super::FormattedText; use std::io::Write; +use crate::context::Context; + use termion::raw::RawTerminal; use termion::color; use termion::style; use termion::clear; use termion::cursor; -use std::ops::Add; -use crate::context::Context; - - -#[derive(Debug)] -#[derive(Clone)] -pub struct FormattedText { - text: String -} - -impl ToString for FormattedText { - fn to_string(&self) -> String { return self.text.clone(); } -} - - -fn format_map_none(c: char) -> Option { - Some(match c { - 'n'|'i'|'t'|'a'| - 'e'|'c'|'s'|'r'| - 'p' - => { "".to_string() }, - _ => { return None } - }) -} - fn format_map_ansi(c: char) -> Option { Some(match c { @@ -65,6 +43,17 @@ fn format_map_ansi(c: char) -> Option { } + +fn format_map_none(c: char) -> Option { + Some(match c { + 'n'|'i'|'t'|'a'| + 'e'|'c'|'s'|'r'| + 'p' + => { "".to_string() }, + _ => { return None } + }) +} + // style::reset also resets color. // Make sure color comes AFTER style reset. fn format_map_full(c: char) -> Option { @@ -102,9 +91,6 @@ fn format_map_full(c: char) -> Option { }) } - - - impl FormattedText { pub fn newline(stdout: &mut RawTerminal) -> Result<(), std::io::Error> { write!(stdout, "\n")?; @@ -119,20 +105,6 @@ impl FormattedText { _ => unreachable!("Invalid term_color_type") } } -} - - -impl FormattedText { - pub fn new(s: String) -> FormattedText { - return FormattedText { - text: s - } - } - - pub fn push(&mut self, s: &str) { - self.text.push_str(s); - } - pub fn write(&self, context: &Context, stdout: &mut RawTerminal) -> Result<(), std::io::Error> { @@ -155,7 +127,7 @@ impl FormattedText { '[' => { let a = chars.next().unwrap(); - // Handle double [[ as escaped [ + // Treat double [[ as escaped [ if a == '[' { s.push('['); } let b = chars.next().unwrap(); @@ -189,13 +161,4 @@ impl FormattedText { write!(stdout, "\r{}", s)?; return Ok(()); } -} - - -impl Add for FormattedText { - type Output = Self; - - fn add(self, other: Self) -> Self::Output { - return FormattedText::new(format!("{}{}", self.text, other.text)); - } } \ No newline at end of file diff --git a/src/formattedtext/wasm_backend.rs b/src/formattedtext/wasm_backend.rs new file mode 100644 index 0000000..8235705 --- /dev/null +++ b/src/formattedtext/wasm_backend.rs @@ -0,0 +1,12 @@ +use super::FormattedText; + +impl FormattedText { + pub fn newline() -> Result<(), ()> { + print!("\n"); + return Ok(()); + } + + pub fn write(&self) -> String { + return self.text.clone(); + } +} \ No newline at end of file From dbde7cb6f4ab66ba5241e2b7de63cae51171f8ae Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 20 Sep 2023 14:24:15 -0700 Subject: [PATCH 03/19] Comments --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 1df7360..cd74d21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ num = "0.4.1" #astro-float = "0.7.1" [package.metadata.wasm-pack.profile.release] +# wasm-opt doesn't work, need to figure out why wasm-opt = false [target.'cfg(target_family = "unix")'.dependencies] From 1bd0b2580b953a75f69d3f182f4b4518a7429e83 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 20 Sep 2023 14:24:39 -0700 Subject: [PATCH 04/19] Added wasm binding --- src/lib.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 1e85639..d0737fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,24 @@ pub use crate::errors::DaisyError; pub use crate::evaluate::evaluate; +cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + use wasm_bindgen::prelude::*; + + #[wasm_bindgen] + pub fn dostr(x: String) -> String{ + let mut context = Context::new(); + let x = x.trim().to_string(); + let r = do_string(&mut context, &x); + + match r { + Ok(t) | Err(t) => { + return t.write(); + } + } + } + } +} #[inline(always)] pub fn do_string( From 4aba24ec691d29a13d5b8b62c12d8aaefd9e97f9 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 20 Sep 2023 14:29:14 -0700 Subject: [PATCH 05/19] Added HTML test page --- index.html | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 index.html diff --git a/index.html b/index.html new file mode 100644 index 0000000..e6acdaa --- /dev/null +++ b/index.html @@ -0,0 +1,37 @@ + + + + + + + +
+
+
+ $ + +
+
+ + + + + From 0c07cb258b2f3c7977959ba1724f9d1415bc40ad Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 21 Sep 2023 12:02:11 -0700 Subject: [PATCH 06/19] Promptbuffer now uses FormattedText --- src/promptbuffer.rs | 89 +++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 48 deletions(-) diff --git a/src/promptbuffer.rs b/src/promptbuffer.rs index 0b6bc6b..dc9d7d1 100644 --- a/src/promptbuffer.rs +++ b/src/promptbuffer.rs @@ -1,9 +1,7 @@ use std::collections::VecDeque; -use std::io::Write; -use termion::raw::RawTerminal; -use daisycalc::FormattedText; -use daisycalc::parser::substitute_cursor; -use daisycalc::Context; +use crate::FormattedText; +use crate::parser::substitute_cursor; +use crate::Context; const PROMPT_STR: &str = "==> "; @@ -18,12 +16,49 @@ pub struct PromptBuffer { // 1 means "on last item of history" hist_cursor: usize, - buffer: String, + pub buffer: String, buffer_changed: bool, cursor: usize, last_print_len: usize } +impl PromptBuffer { + // Same as write_primpt, but pretends there is no cursor + pub fn write_prompt_nocursor(&mut self, context: &Context) -> FormattedText { + let tmp = self.cursor; + self.cursor = 0; + let r = self.write_prompt(context); + self.cursor = tmp; + return r; + } + + pub fn write_prompt(&mut self, context: &Context) -> FormattedText { + let l = self.buffer.chars().count(); + let i = if l == 0 {0} else {l - self.cursor}; + + // Draw prettyprinted expression + let (display_c, s) = substitute_cursor(context, &self.get_contents(), i); + + let mut tx = FormattedText::new("".to_string()); + + tx.push(&format!("\r[p]{PROMPT_STR}[n]{s}")); + + + // If this string is shorter, clear the remaining old one. + if s.chars().count() < self.last_print_len { + tx.push(&" ".repeat(self.last_print_len - s.chars().count())); + } + + let q = (display_c + PROMPT_STR.chars().count()) as u16; + tx.push(&format!("\r[cursorright{q}]")); + + self.last_print_len = s.chars().count(); + + return tx; + } +} + + impl PromptBuffer { pub fn new(maxlen: usize) -> PromptBuffer { return PromptBuffer { @@ -37,48 +72,6 @@ impl PromptBuffer { }; } - // Same as write_primpt, but pretends there is no cursor - pub fn write_prompt_nocursor(&mut self, context: &Context, stdout: &mut RawTerminal) -> Result<(), std::io::Error> { - let tmp = self.cursor; - self.cursor = 0; - let r = self.write_prompt(context, stdout); - self.cursor = tmp; - return r; - } - - pub fn write_prompt(&mut self, context: &Context, stdout: &mut RawTerminal) -> Result<(), std::io::Error> { - let l = self.buffer.chars().count(); - let i = if l == 0 {0} else {l - self.cursor}; - - // Draw prettyprinted expression - let (display_c, s) = substitute_cursor(context, &self.get_contents(), i); - - write!( - stdout, "\r{}{PROMPT_STR}{}{}", - FormattedText::format_map('p', context).unwrap(), - FormattedText::format_map('n', context).unwrap(), - s - )?; - - // If this string is shorter, clear the remaining old one. - if s.chars().count() < self.last_print_len { - write!( - stdout, "{}", - " ".repeat(self.last_print_len - s.chars().count()), - )?; - } - - write!( - stdout, "\r{}", - termion::cursor::Right((display_c + PROMPT_STR.chars().count()) as u16) - )?; - - stdout.flush()?; - self.last_print_len = s.chars().count(); - - return Ok(()); - } - // Prompt methods pub fn get_contents(&self) -> &String {&self.buffer} From 7d78d0d74d462f5d8a9dc643ed0104a5e95c9fbf Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 21 Sep 2023 12:02:40 -0700 Subject: [PATCH 07/19] Improved formattedtext --- src/formattedtext/formattedtext.rs | 7 ++ src/formattedtext/unix_backend.rs | 149 ++++++++++++++++------------- src/formattedtext/wasm_backend.rs | 80 ++++++++++++++-- 3 files changed, 163 insertions(+), 73 deletions(-) diff --git a/src/formattedtext/formattedtext.rs b/src/formattedtext/formattedtext.rs index 45b5a5d..53188f3 100644 --- a/src/formattedtext/formattedtext.rs +++ b/src/formattedtext/formattedtext.rs @@ -1,4 +1,5 @@ use std::ops::Add; +use std::ops::AddAssign; #[derive(Debug)] @@ -30,4 +31,10 @@ impl Add for FormattedText { fn add(self, other: Self) -> Self::Output { return FormattedText::new(format!("{}{}", self.text, other.text)); } +} + +impl AddAssign for FormattedText where { + fn add_assign(&mut self, other: Self) { + self.text.push_str(&other.text); + } } \ No newline at end of file diff --git a/src/formattedtext/unix_backend.rs b/src/formattedtext/unix_backend.rs index 857ffed..789e14a 100644 --- a/src/formattedtext/unix_backend.rs +++ b/src/formattedtext/unix_backend.rs @@ -8,33 +8,33 @@ use termion::style; use termion::clear; use termion::cursor; -fn format_map_ansi(c: char) -> Option { - Some(match c { - 'n' => { // Normal text +fn format_map_ansi(s: &str) -> Option { + Some(match s { + "n" => { // Normal text format!("{}{}", color::Fg(color::Reset), color::Bg(color::Reset)) }, - 'i' => { // Normal italic text + "i" => { // Normal italic text format!("{}{}", color::Fg(color::Reset), color::Bg(color::Reset)) }, - 't' => { // Title text (should be cyan) + "t" => { // Title text (should be cyan) format!("{}{}", color::Fg(color::AnsiValue(6)), color::Bg(color::Reset)) }, - 'a' => { // Colored text (should be pink) + "a" => { // Colored text (should be pink) format!("{}{}", color::Fg(color::AnsiValue(5)), color::Bg(color::Reset)) }, - 'e' => { // Error titles (should be red) + "e" => { // Error titles (should be red) format!("{}{}", color::Fg(color::AnsiValue(1)), color::Bg(color::Reset)) }, - 'c' => { // Console text (inverted black on white) + "c" => { // Console text (inverted black on white) format!("{}{}", color::Fg(color::AnsiValue(0)), color::Bg(color::AnsiValue(7))) }, - 'p' => { // Input prompt (how ==> is styled) (should be blue) + "p" => { // Input prompt (how ==> is styled) (should be blue) format!("{}{}", color::Fg(color::AnsiValue(4)), color::Bg(color::Reset)) }, - 's' => { // Repeat prompt (how => is styled) (should be pink) + "s" => { // Repeat prompt (how => is styled) (should be pink) format!("{}{}", color::Fg(color::AnsiValue(5)), color::Bg(color::Reset)) }, - 'r' => { // Result prompt (how = is styled) (should be green) + "r" => { // Result prompt (how = is styled) (should be green) format!("{}{}", color::Fg(color::AnsiValue(2)), color::Bg(color::Reset)) }, @@ -44,11 +44,11 @@ fn format_map_ansi(c: char) -> Option { -fn format_map_none(c: char) -> Option { - Some(match c { - 'n'|'i'|'t'|'a'| - 'e'|'c'|'s'|'r'| - 'p' +fn format_map_none(s: &str) -> Option { + Some(match s { + "n"|"i"|"t"|"a"| + "e"|"c"|"s"|"r"| + "p" => { "".to_string() }, _ => { return None } }) @@ -56,33 +56,33 @@ fn format_map_none(c: char) -> Option { // style::reset also resets color. // Make sure color comes AFTER style reset. -fn format_map_full(c: char) -> Option { - Some(match c { - 'n' => { // Normal text +fn format_map_full(s: &str) -> Option { + Some(match s { + "n" => { // Normal text format!("{}{}", style::Reset, color::Fg(color::Reset)) }, - 'i' => { // Normal italic text + "i" => { // Normal italic text format!("{}{}", color::Fg(color::Reset), style::Italic) }, - 't' => { // Title text + "t" => { // Title text format!("{}{}", color::Fg(color::Magenta), style::Bold) }, - 'a' => { // Colored text + "a" => { // Colored text format!("{}{}", style::Reset, color::Fg(color::Magenta)) }, - 'e' => { // Error titles + "e" => { // Error titles format!("{}{}", color::Fg(color::Red), style::Bold) }, - 'c' => { // Console text + "c" => { // Console text format!("{}{}", color::Fg(color::LightBlack), style::Italic) }, - 'p' => { // Input prompt (how ==> is styled) + "p" => { // Input prompt (how ==> is styled) format!("{}{}", color::Fg(color::Blue), style::Bold) }, - 's' => { // Repeat prompt (how => is styled) + "s" => { // Repeat prompt (how => is styled) format!("{}{}", color::Fg(color::Magenta), style::Bold) }, - 'r' => { // Result prompt (how = is styled) + "r" => { // Result prompt (how = is styled) format!("{}{}", color::Fg(color::Green), style::Bold) }, @@ -97,68 +97,83 @@ impl FormattedText { return Ok(()); } - pub fn format_map(c: char, context: &Context) -> Option { + pub fn format_map(s: &str, context: &Context) -> Option { match context.config.term_color_type { - 0 => format_map_none(c), - 1 => format_map_ansi(c), - 2 => format_map_full(c), + 0 => format_map_none(s), + 1 => format_map_ansi(s), + 2 => format_map_full(s), _ => unreachable!("Invalid term_color_type") } } pub fn write(&self, context: &Context, stdout: &mut RawTerminal) -> Result<(), std::io::Error> { - if self.text == "[clear]" { - write!( - stdout, - "{}{}", - clear::All, - cursor::Goto(1, 1) - )?; - return Ok(()); - } - - - let mut s = String::new(); + let mut word = String::new(); + let mut reading = false; // are we reading a word? let mut chars = self.text.chars(); + let mut out = String::new(); while let Some(c) = chars.next() { + match c { '[' => { - let a = chars.next().unwrap(); + if reading { + // Discard old word, start reading again. + out.push_str(&word); + word.clear(); + } + + // Start reading a new word + reading = true; + word.push(c); + }, - // Treat double [[ as escaped [ - if a == '[' { s.push('['); } + ']' => { + if !reading { + out.push(c); + } else { + word.push(c); - let b = chars.next().unwrap(); - match (a, b) { - (c, ']') => { // Normal text + let f = Self::format_map(&word[1..word.len()-1], context); - let q = Self::format_map(c, context); - - if q.is_some() { - s.push_str(&q.unwrap()); - } else { - s.push('['); - s.push(a); - s.push(b); - } - }, - - _ => { - s.push('['); - s.push(a); - s.push(b); + if f.is_some() { + out.push_str(&f.unwrap()); + } else if word == "[clear]" { + out.push_str(&format!( + "{}{}", + clear::All, + cursor::Goto(1, 1) + )); + } else if word.starts_with("[cursorright") { + let n: u16 = word[12..word.len()-1].parse().unwrap(); + out.push_str(&format!( + "{}", + cursor::Right(n), + )); + } else { + out.push_str(&word); } + + reading = false; + word.clear(); } }, - '\n' => { s.push_str("\r\n") }, - _ => s.push(c) + + '\n' => { + if reading { word.push_str("\r\n"); } + else { out.push_str("\r\n"); } + }, + + _ => { + if reading { word.push(c); } + else { out.push(c); } + } } } - write!(stdout, "\r{}", s)?; + write!(stdout, "\r{}", out)?; + stdout.flush()?; return Ok(()); } } \ No newline at end of file diff --git a/src/formattedtext/wasm_backend.rs b/src/formattedtext/wasm_backend.rs index 8235705..61ac800 100644 --- a/src/formattedtext/wasm_backend.rs +++ b/src/formattedtext/wasm_backend.rs @@ -1,12 +1,80 @@ use super::FormattedText; -impl FormattedText { - pub fn newline() -> Result<(), ()> { - print!("\n"); - return Ok(()); - } +fn format_map(s: &str) -> Option { + Some(match s { + "n" => {"\x1B[0m"}, + "i" => {"\x1B[3m"}, + "t" => {"\x1B[1;35m"}, + "a" => {"\x1B[0;35m"}, + "e" => {"\x1B[1;31m"}, + "c" => {"\x1B[3;90m"}, + "p" => {"\x1B[1;34m"}, + "s" => {"\x1B[1;35m"}, + "r" => {"\x1B[1;32m"}, + _ => { return None } + }.to_string()) +} + +impl FormattedText { pub fn write(&self) -> String { - return self.text.clone(); + + let mut word = String::new(); + let mut reading = false; // are we reading a word? + let mut chars = self.text.chars(); + let mut out = String::new(); + + while let Some(c) = chars.next() { + + match c { + '[' => { + if reading { + // Discard old word, start reading again. + out.push_str(&word); + word.clear(); + } + + // Start reading a new word + reading = true; + word.push(c); + }, + + ']' => { + if !reading { + out.push(c); + } else { + word.push(c); + + let f = format_map(&word[1..word.len()-1]); + + if f.is_some() { + out.push_str(&f.unwrap()); + } else if word == "[clear]" { + out.push_str(&format!("\x1B[2J\x1B[H")); + } else if word.starts_with("[cursorright") { + let n: u16 = word[12..word.len()-1].parse().unwrap(); + out.push_str(&format!("\x1B[{n}C")); + } else { + out.push_str(&word); + } + + reading = false; + word.clear(); + } + }, + + '\n' => { + if reading { word.push_str("\r\n"); } + else { out.push_str("\r\n"); } + }, + + _ => { + if reading { word.push(c); } + else { out.push(c); } + } + } + } + + return out; } } \ No newline at end of file From cb9f69948c812abefce4d0542f86ef40bcf7505b Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 21 Sep 2023 12:03:01 -0700 Subject: [PATCH 08/19] Migrate to new formattedtext & promptbuffer --- src/main.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index ac2c86d..640d9eb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,3 @@ -pub mod promptbuffer; - use std::io::stdout; use std::io::stdin; use std::env; @@ -11,11 +9,10 @@ use termion::{ color::DetectColors }; -use promptbuffer::PromptBuffer; +use daisycalc::PromptBuffer; use daisycalc::command; use daisycalc::Context; use daisycalc::FormattedText; - use daisycalc::do_string; #[cfg(test)] @@ -70,7 +67,8 @@ pub fn main() -> Result<(), std::io::Error> { 'outer: loop { - pb.write_prompt(&mut context, &mut stdout)?; + let t = pb.write_prompt(&mut context); + t.write(&context, &mut stdout)?; let stdin = stdin(); for c in stdin.keys() { @@ -79,7 +77,10 @@ pub fn main() -> Result<(), std::io::Error> { '\n' => { // Print again without cursor, in case we pressed enter // while inside a substitution - pb.write_prompt_nocursor(&mut context, &mut stdout)?; + let t = pb.write_prompt_nocursor(&mut context); + t.write(&context, &mut stdout)?; + + let in_str = pb.enter(); FormattedText::newline(&mut stdout)?; if in_str == "" { break; } @@ -115,7 +116,8 @@ pub fn main() -> Result<(), std::io::Error> { }; }; - pb.write_prompt(&mut context, &mut stdout)?; + let t = pb.write_prompt(&mut context); + t.write(&context, &mut stdout)?; } } From 921bd4d7045e524f75a629cb5dc0e0b471ba1d4d Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 21 Sep 2023 12:03:28 -0700 Subject: [PATCH 09/19] Added proper WASM api --- src/lib.rs | 84 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 74 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d0737fc..ceed6cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,28 +10,92 @@ mod context; mod formattedtext; mod errors; mod evaluate; +mod promptbuffer; pub use crate::formattedtext::FormattedText; pub use crate::context::Context; pub use crate::errors::DaisyError; pub use crate::evaluate::evaluate; +pub use crate::promptbuffer::PromptBuffer; + cfg_if::cfg_if! { if #[cfg(target_arch = "wasm32")] { use wasm_bindgen::prelude::*; + #[derive(Debug)] + pub struct State { + pub context: Context, + pub promptbuffer: PromptBuffer + } + #[wasm_bindgen] - pub fn dostr(x: String) -> String{ - let mut context = Context::new(); - let x = x.trim().to_string(); - let r = do_string(&mut context, &x); - - match r { - Ok(t) | Err(t) => { - return t.write(); - } - } + pub extern fn daisy_init() -> *mut State { + Box::into_raw(Box::new(State { + context: Context::new(), + promptbuffer: PromptBuffer::new(64) + })) + } + + #[wasm_bindgen] + pub extern fn daisy_free(state: *mut State) { + unsafe { drop(Box::from_raw(state)) }; + } + + #[wasm_bindgen] + pub fn daisy_prompt(state: *mut State) -> String { + let t = unsafe { (*state).promptbuffer.write_prompt(&mut (*state).context) }; + return t.write(); + } + + + #[wasm_bindgen] + pub fn daisy_char(state: *mut State, s: String) -> String { + let mut out = FormattedText::new("".to_string()); + + match &s[..] { + "\r" => { + // Print again without cursor, in case we pressed enter + // while inside a substitution + let t = unsafe { (*state).promptbuffer.write_prompt_nocursor(&mut (*state).context) }; + out += t; + + + let in_str = unsafe { (*state).promptbuffer.enter() }; + out += FormattedText::new("\n".to_string()); + if in_str == "" { + return format!("\r\n{}", daisy_prompt(state)); + } + + if in_str.trim() == "quit" { + return "[quit]".to_string(); + } else { + let r = crate::do_string( unsafe { &mut (*state).context }, &in_str); + + match r { + Ok(t) | Err(t) => { + out += t; + } + } + } + }, + + "\x7F" => { unsafe { (*state).promptbuffer.backspace(); } }, + "\x1B[3~" => { unsafe { (*state).promptbuffer.delete(); } }, + "\x1B[D" => { unsafe { (*state).promptbuffer.cursor_left(); } }, + "\x1B[C" => { unsafe { (*state).promptbuffer.cursor_right(); } }, + "\x1B[A" => { unsafe { (*state).promptbuffer.hist_up(); } }, + "\x1B[B" => { unsafe { (*state).promptbuffer.hist_down(); } }, + + //'\x04' | '\x03' + //=> { break 'outer; }, + + _ => { unsafe { (*state).promptbuffer.add_char(s.chars().next().unwrap()); } }, + }; + + let t = unsafe { (*state).promptbuffer.write_prompt(&mut (*state).context) }; + return (out + t).write(); } } } From f9382616b211b2f255d3c14d8e1dbbdd91405828 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 21 Sep 2023 12:03:44 -0700 Subject: [PATCH 10/19] Cleaned up art --- misc/banner.svg | 137 ++++++++++++++++++++++++ misc/daisy-dark.svg | 137 ++++++++++++++++++++++++ misc/daisy-light.svg | 137 ++++++++++++++++++++++++ misc/daisy.svg | 240 ------------------------------------------- 4 files changed, 411 insertions(+), 240 deletions(-) create mode 100644 misc/banner.svg create mode 100644 misc/daisy-dark.svg create mode 100644 misc/daisy-light.svg delete mode 100644 misc/daisy.svg diff --git a/misc/banner.svg b/misc/banner.svg new file mode 100644 index 0000000..efa19b7 --- /dev/null +++ b/misc/banner.svg @@ -0,0 +1,137 @@ + + + + diff --git a/misc/daisy-dark.svg b/misc/daisy-dark.svg new file mode 100644 index 0000000..2eb030b --- /dev/null +++ b/misc/daisy-dark.svg @@ -0,0 +1,137 @@ + + + + diff --git a/misc/daisy-light.svg b/misc/daisy-light.svg new file mode 100644 index 0000000..a4610fb --- /dev/null +++ b/misc/daisy-light.svg @@ -0,0 +1,137 @@ + + + + diff --git a/misc/daisy.svg b/misc/daisy.svg deleted file mode 100644 index 4fb6cc0..0000000 --- a/misc/daisy.svg +++ /dev/null @@ -1,240 +0,0 @@ - - - -DaisyDaisyDaisy From 69ba2ab715596a50cd3019d899beb726e904873a Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 21 Sep 2023 12:06:13 -0700 Subject: [PATCH 11/19] Improved WASM webpage --- index.html | 148 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 130 insertions(+), 18 deletions(-) diff --git a/index.html b/index.html index e6acdaa..21422db 100644 --- a/index.html +++ b/index.html @@ -1,37 +1,149 @@ + + + + - -
-
-
- $ - -
+ + +
+ + - From a70fd5f0e5d2adaf3a4a92fe89caf9838bb5c29f Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 21 Sep 2023 14:07:21 -0700 Subject: [PATCH 12/19] Cleaned up site files --- .gitignore | 3 +-- README.md | 2 +- site/.gitignore | 3 +++ index.html => site/index.html | 5 ++--- site/package.json | 5 +++++ ...ns Mono Regular Nerd Font Complete Mono.ttf | Bin 0 -> 972172 bytes {misc => site/resources}/banner.svg | 0 {misc => site/resources}/daisy-dark.svg | 0 {misc => site/resources}/daisy-light.svg | 0 .../resources/readme-banner.png | Bin 10 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 site/.gitignore rename index.html => site/index.html (93%) create mode 100644 site/package.json create mode 100755 site/resources/Fantasque Sans Mono Regular Nerd Font Complete Mono.ttf rename {misc => site/resources}/banner.svg (100%) rename {misc => site/resources}/daisy-dark.svg (100%) rename {misc => site/resources}/daisy-light.svg (100%) rename misc/banner.png => site/resources/readme-banner.png (100%) diff --git a/.gitignore b/.gitignore index ee19741..5cdd539 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ /target /src/target -/pkg -*.pkg.* \ No newline at end of file +/pkg \ No newline at end of file diff --git a/README.md b/README.md index fe53747..053efbc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![](./misc/banner.png) +![](./site/misc/readme-banner.png) A high-precision scientific calculator with support for units, derivatives, and more. diff --git a/site/.gitignore b/site/.gitignore new file mode 100644 index 0000000..6bd0e57 --- /dev/null +++ b/site/.gitignore @@ -0,0 +1,3 @@ +/node_modules +/package-lock.json +/pkg \ No newline at end of file diff --git a/index.html b/site/index.html similarity index 93% rename from index.html rename to site/index.html index 21422db..1714f03 100644 --- a/index.html +++ b/site/index.html @@ -2,12 +2,11 @@ -