فهرست منبع

Initial working prototype for WASM version

David Peter 2 سال پیش
والد
کامیت
6f7ea868a3

+ 15 - 7
numbat-wasm/src/html_formatter.rs → numbat-wasm/src/jquery_terminal_formatter.rs

@@ -1,8 +1,16 @@
 use numbat::markup::{FormatType, FormattedString, Formatter};
 
-pub struct HtmlFormatter;
+pub struct JqueryTerminalFormatter;
 
-impl Formatter for HtmlFormatter {
+pub fn jt_format(class: &str, content: &str) -> String {
+    if content.is_empty() {
+        "".into()
+    } else {
+        format!("[[;;;hl-{class}]{content}]")
+    }
+}
+
+impl Formatter for JqueryTerminalFormatter {
     fn format_part(
         &self,
         FormattedString(_output_type, format_type, s): &FormattedString,
@@ -11,13 +19,13 @@ impl Formatter for HtmlFormatter {
             FormatType::Whitespace => format!("{s}"),
             FormatType::Dimmed => format!("{s}"),
             FormatType::Text => format!("{s}"),
-            FormatType::String => format!("{s}"),
-            FormatType::Keyword => format!("<b>{s}</b>"),
-            FormatType::Value => format!("<span style=\"color:blue\">{s}</span>"),
-            FormatType::Unit => format!("<span style=\"color:green\">{s}</span>"),
+            FormatType::String => jt_format("emphasized", s),
+            FormatType::Keyword => format!("{s}"),
+            FormatType::Value => jt_format("value", s),
+            FormatType::Unit => format!("{s}"),
             FormatType::Identifier => format!("{s}"),
             FormatType::TypeIdentifier => format!("{s}"),
-            FormatType::Operator => format!("{s}"),
+            FormatType::Operator => jt_format("emphasized", s),
             FormatType::Decorator => format!("{s}"),
         }
     }

+ 31 - 13
numbat-wasm/src/lib.rs

@@ -1,14 +1,17 @@
-mod html_formatter;
+mod jquery_terminal_formatter;
 mod utils;
+mod wasm_importer;
 
-use html_formatter::HtmlFormatter;
+use jquery_terminal_formatter::{jt_format, JqueryTerminalFormatter};
 use numbat::markup::Formatter;
 use numbat::pretty_print::PrettyPrint;
-use numbat::resolver::{CodeSource, NullImporter};
+use numbat::resolver::CodeSource;
 use numbat::{Context, InterpreterResult};
 
 use wasm_bindgen::prelude::*;
 
+use crate::wasm_importer::WasmImporter;
+
 // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
 // allocator.
 #[cfg(feature = "wee_alloc")]
@@ -16,18 +19,33 @@ use wasm_bindgen::prelude::*;
 static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
 
 #[wasm_bindgen]
-pub fn interpret(code: &str) -> String {
+pub fn setup_panic_hook() {
     utils::set_panic_hook();
+}
+
+#[wasm_bindgen]
+pub struct Numbat {
+    ctx: Context,
+}
+
+#[wasm_bindgen]
+impl Numbat {
+    pub fn new() -> Self {
+        let mut ctx = Context::new(WasmImporter {});
+        let _ = ctx.interpret("use prelude", CodeSource::Internal).unwrap();
+        Numbat { ctx }
+    }
 
-    let html_formatter = HtmlFormatter {};
+    pub fn interpret(&mut self, code: &str) -> String {
+        let fmt = JqueryTerminalFormatter {};
 
-    let mut numbat = Context::new(NullImporter {});
-    match numbat.interpret(&code, CodeSource::Text) {
-        Ok((_, result)) => match result {
-            InterpreterResult::Value(q) => html_formatter.format(&q.pretty_print(), true),
-            InterpreterResult::Continue => "Nothing to show".into(),
-            InterpreterResult::Exit(_) => "Error!".into(),
-        },
-        Err(e) => format!("{:#}", e),
+        match self.ctx.interpret(&code, CodeSource::Text) {
+            Ok((_, result)) => match result {
+                InterpreterResult::Value(q) => fmt.format(&q.pretty_print(), true),
+                InterpreterResult::Continue => "".into(),
+                InterpreterResult::Exit(_) => jt_format("error", "Error!".into()),
+            },
+            Err(e) => format!("{:#}", e),
+        }
     }
 }

+ 45 - 0
numbat-wasm/src/wasm_importer.rs

@@ -0,0 +1,45 @@
+use std::path::PathBuf;
+
+use numbat::resolver::{ModuleImporter, ModulePath};
+
+#[derive(Debug, Clone, Default)]
+pub struct WasmImporter {}
+
+impl ModuleImporter for WasmImporter {
+    fn import(&self, path: &ModulePath) -> Option<(String, Option<PathBuf>)> {
+        let path = path.to_string();
+        let code = match path.as_str() {
+            "core::dimensions" => include_str!("../../modules/core/dimensions.nbt"),
+            "core::quantity" => include_str!("../../modules/core/quantity.nbt"),
+            "core::scalar" => include_str!("../../modules/core/scalar.nbt"),
+            "math::constants" => include_str!("../../modules/math/constants.nbt"),
+            "math::functions" => include_str!("../../modules/math/functions.nbt"),
+            "math::trigonometry_extra" => include_str!("../../modules/math/trigonometry_extra.nbt"),
+            "physics::constants" => include_str!("../../modules/physics/constants.nbt"),
+            "physics::temperature_conversion" => {
+                include_str!("../../modules/physics/temperature_conversion.nbt")
+            }
+            "prelude" => include_str!("../../modules/prelude.nbt"),
+            "units::astronomical" => include_str!("../../modules/units/astronomical.nbt"),
+            "units::bit" => include_str!("../../modules/units/bit.nbt"),
+            "units::cgs" => include_str!("../../modules/units/cgs.nbt"),
+            "units::currencies" => include_str!("../../modules/units/currencies.nbt"),
+            "units::currency" => include_str!("../../modules/units/currency.nbt"),
+            "units::fff" => include_str!("../../modules/units/fff.nbt"),
+            "units::hartree" => include_str!("../../modules/units/hartree.nbt"),
+            "units::imperial" => include_str!("../../modules/units/imperial.nbt"),
+            "units::misc" => include_str!("../../modules/units/misc.nbt"),
+            "units::nautical" => include_str!("../../modules/units/nautical.nbt"),
+            "units::partsperx" => include_str!("../../modules/units/partsperx.nbt"),
+            "units::placeholder" => include_str!("../../modules/units/placeholder.nbt"),
+            "units::si" => include_str!("../../modules/units/si.nbt"),
+            "units::stoney" => include_str!("../../modules/units/stoney.nbt"),
+            "units::time" => include_str!("../../modules/units/time.nbt"),
+            "units::us_customary" => include_str!("../../modules/units/us_customary.nbt"),
+
+            _ => return None,
+        };
+
+        Some((code.to_string(), None))
+    }
+}

+ 40 - 22
numbat-wasm/www/index.html

@@ -1,29 +1,47 @@
 <!DOCTYPE html>
-<html>
+<html lang="en">
   <head>
-    <meta charset="utf-8">
-    <title>Numbat</title>
-    <style>
-    #code {
-      font-size: 16px;
-    }
-    #run {
-      font-size: 18px;
-    }
-    #output {
-      font-size: 18px;
-    }
-    </style>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <meta name="description" content="High precision scientific calculator with full support for physical units">
+    <meta name="keywords" content="calculator,scientific,math,physics,unit,conversion,quantity,SI,imperial,precision">
+    <meta name="author" content="David Peter">
+    <!--<meta property="og:type" content="website">
+    <meta property="og:image" content="https://insect.sh/media/insect-banner.png">
+    <meta property="og:title" content="insect - scientific calculator">
+    <meta property="og:url" content="https://insect.sh/">
+    <meta property="og:description" content="High precision scientific calculator with full support for physical units.">
+    <meta name="twitter:card" content="summary_large_image">
+    <meta name="twitter:site" content="@sharkdp86">
+    <meta name="twitter:title" content="insect - scientific calculator">
+    <meta name="twitter:description" content="High precision scientific calculator with full support for physical units.">
+    <meta name="twitter:image:src" content="https://insect.sh/media/insect-banner.png">
+    <meta name="twitter:image:width" content="500">
+    <meta name="twitter:image:height" content="250">
+    <meta name="twitter:url" content="https://insect.sh/">-->
+    <title>Numbat - scientific calculator</title>
+    <link href="https://fonts.googleapis.com/css?family=Exo+2%7CFira+Mono:400,700" rel="stylesheet">
+    <link href="terminal.css" rel="stylesheet">
+    <link href="main.css" rel="stylesheet">
+    <!--<link rel="icon" type="image/png" href="media/insect-196x196.png" sizes="196x196">
+    <link rel="icon" type="image/png" href="media/insect-32x32.png" sizes="32x32">
+    <link rel="icon" type="image/png" href="media/insect-16x16.png" sizes="16x16">
+    <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="insect.sh scientific calculator">-->
+    <script src="keyboardevent-key-polyfill.js" type="text/javascript"></script>
+    <script src="jquery.min.js" type="text/javascript"></script>
+    <script src="jquery.terminal.min.js" type="text/javascript"></script>
+    <script src="jquery.mousewheel-min.js" type="text/javascript"></script>
   </head>
   <body>
-    <textarea id="code" rows="15" cols="80">dimension Stuff
-unit stuff: Stuff
-
-2 stuff * 3 stuff</textarea>
-    <br>
-    <button id="run">Run</button>
-    <br>
-    <div id="output"></div>
+    <div id="content">
+      
+      <p class="desc">high precision scientific calculator with full support for physical units.</p>
+      <div id="terminal"></div>
+      <p class="links"><a href="https://numbat.dev/doc/">Documentation</a> &middot; <a href="https://github.com/sharkdp/numbat">Source code</a></p>
+    </div>
+    <a href="https://github.com/sharkdp/numbat" class="github-corner" aria-label="View source on Github">
+      <svg width="80" height="80" viewBox="0 0 250 250" style="position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg>
+    </a>
     <noscript>This page contains webassembly and javascript content, please enable javascript in your browser.</noscript>
     <script src="./bootstrap.js"></script>
   </body>

+ 109 - 2
numbat-wasm/www/index.js

@@ -1,9 +1,116 @@
-import * as numbat from "numbat-wasm";
+import { setup_panic_hook, Numbat } from "numbat-wasm";
+
+setup_panic_hook();
+
+var numbat = Numbat.new();
 
 const runButton = document.getElementById("run");
 const codeTextarea = document.getElementById("code");
 
-runButton.addEventListener("click", event => {
+/*runButton.addEventListener("click", event => {
   const output = numbat.interpret(codeTextarea.value);
   document.getElementById("output").innerHTML = output;
+});*/
+
+// Load KeyboardEvent polyfill for old browsers
+keyboardeventKeyPolyfill.polyfill();
+  
+var clearCommands = ["clear", "cls", "quit", "exit"];
+
+function updateUrlQuery(query) {
+  /*url = new URL(window.location);
+  if (query == null) {
+    url.searchParams.delete('q');
+  } else {
+    url.searchParams.set('q', query);
+  }
+
+  history.replaceState(null, null, url);*/
+}
+
+function interpret(line) {
+  // Skip empty lines or line comments
+  var lineTrimmed = line.trim();
+  if (lineTrimmed === "" || lineTrimmed[0] === "#") {
+    return;
+  }
+
+  // Run Numbat
+  const output = numbat.interpret(line);
+
+  // Handle shell commands
+  /*if (clearCommands.indexOf(res.msgType) >= 0) {
+    // Clear screen:
+    this.clear();
+    return;
+  } else if (res.msgType === "quit") {
+    // Treat as reset:
+    this.clear();
+    insectEnv = Insect.initialEnvironment;
+    return;
+  } else if (res.msgType === "copy") {
+    // Copy result to clipboard:
+    if (res.msg === "") {
+      res.msg = "\nNo result to copy.\n";
+    } else {
+      navigator.clipboard.writeText(res.msg);
+      res.msg = "\nCopied result '" + res.msg + "' to clipboard.\n";
+    }
+  }*/
+  updateUrlQuery(line);
+
+  return output;
+}
+
+function emph(str) {
+  return "[[;;;hl-emphasized]" + str + "]";
+}
+
+function colored(col, str) {
+  return "[[;#" + col + ";]" + str + "]";
+}
+
+var visitedBefore = localStorage.getItem("visitedBefore") === "yes";
+var greeting = "";
+if (!visitedBefore) {
+  greeting = colored("75715E", "Welcome to Numbat. Type '?' if this is your first visit.");
+  localStorage.setItem("visitedBefore", "yes");
+} else {
+  greeting = colored("75715E", "Welcome to Numbat. Enter '?' for help.");
+}
+
+$(document).ready(function() {
+  var term = $('#terminal').terminal(interpret, {
+    greetings: greeting,
+    name: "terminal",
+    height: 550,
+    prompt: "[[;;;prompt]&gt; ]",
+    // clear: false, // do not include 'clear' command
+    // exit: false, // do not include 'exit' command
+    checkArity: false,
+    historySize: 200,
+    historyFilter(line) {
+      return line.trim() !== "";
+    },
+    completion(inp, cb) {
+      /*var identifiers = Insect.identifiers(insectEnv);
+
+      var keywords =
+        identifiers.concat(Insect.functions(insectEnv), Insect.supportedUnits, Insect.commands);
+
+      cb(keywords.sort());*/
+      cb([]);
+    },
+    onClear() {
+      updateUrlQuery(null);
+    }
+  });
+
+  // evaluate expression in query string if supplied (via opensearch)
+  if (location.search) {
+    var queryParams = new URLSearchParams(location.search);
+    if (queryParams.has("q")) {
+      term.exec(queryParams.get("q"));
+    }
+  }
 });

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
numbat-wasm/www/jquery.min.js


+ 2 - 0
numbat-wasm/www/jquery.mousewheel-min.js

@@ -0,0 +1,2 @@
+(function(c){function g(a){var b=a||window.event,i=[].slice.call(arguments,1),e=0,h=0,f=0;a=c.event.fix(b);a.type="mousewheel";if(b.wheelDelta)e=b.wheelDelta/120;if(b.detail)e=-b.detail/3;f=e;if(b.axis!==undefined&&b.axis===b.HORIZONTAL_AXIS){f=0;h=-1*e}if(b.wheelDeltaY!==undefined)f=b.wheelDeltaY/120;if(b.wheelDeltaX!==undefined)h=-1*b.wheelDeltaX/120;i.unshift(a,e,h,f);return(c.event.dispatch||c.event.handle).apply(this,i)}var d=["DOMMouseScroll","mousewheel"];if(c.event.fixHooks)for(var j=d.length;j;)c.event.fixHooks[d[--j]]=
+c.event.mouseHooks;c.event.special.mousewheel={setup:function(){if(this.addEventListener)for(var a=d.length;a;)this.addEventListener(d[--a],g,false);else this.onmousewheel=g},teardown:function(){if(this.removeEventListener)for(var a=d.length;a;)this.removeEventListener(d[--a],g,false);else this.onmousewheel=null}};c.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})})(jQuery);

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 45 - 0
numbat-wasm/www/jquery.terminal.min.js


+ 121 - 0
numbat-wasm/www/keyboardevent-key-polyfill.js

@@ -0,0 +1,121 @@
+/* global define, KeyboardEvent, module */
+
+(function () {
+
+  var keyboardeventKeyPolyfill = {
+    polyfill: polyfill,
+    keys: {
+      3: 'Cancel',
+      6: 'Help',
+      8: 'Backspace',
+      9: 'Tab',
+      12: 'Clear',
+      13: 'Enter',
+      16: 'Shift',
+      17: 'Control',
+      18: 'Alt',
+      19: 'Pause',
+      20: 'CapsLock',
+      27: 'Escape',
+      28: 'Convert',
+      29: 'NonConvert',
+      30: 'Accept',
+      31: 'ModeChange',
+      32: ' ',
+      33: 'PageUp',
+      34: 'PageDown',
+      35: 'End',
+      36: 'Home',
+      37: 'ArrowLeft',
+      38: 'ArrowUp',
+      39: 'ArrowRight',
+      40: 'ArrowDown',
+      41: 'Select',
+      42: 'Print',
+      43: 'Execute',
+      44: 'PrintScreen',
+      45: 'Insert',
+      46: 'Delete',
+      48: ['0', ')'],
+      49: ['1', '!'],
+      50: ['2', '@'],
+      51: ['3', '#'],
+      52: ['4', '$'],
+      53: ['5', '%'],
+      54: ['6', '^'],
+      55: ['7', '&'],
+      56: ['8', '*'],
+      57: ['9', '('],
+      91: 'OS',
+      93: 'ContextMenu',
+      144: 'NumLock',
+      145: 'ScrollLock',
+      181: 'VolumeMute',
+      182: 'VolumeDown',
+      183: 'VolumeUp',
+      186: [';', ':'],
+      187: ['=', '+'],
+      188: [',', '<'],
+      189: ['-', '_'],
+      190: ['.', '>'],
+      191: ['/', '?'],
+      192: ['`', '~'],
+      219: ['[', '{'],
+      220: ['\\', '|'],
+      221: [']', '}'],
+      222: ["'", '"'],
+      224: 'Meta',
+      225: 'AltGraph',
+      246: 'Attn',
+      247: 'CrSel',
+      248: 'ExSel',
+      249: 'EraseEof',
+      250: 'Play',
+      251: 'ZoomOut'
+    }
+  };
+
+  // Function keys (F1-24).
+  var i;
+  for (i = 1; i < 25; i++) {
+    keyboardeventKeyPolyfill.keys[111 + i] = 'F' + i;
+  }
+
+  // Printable ASCII characters.
+  var letter = '';
+  for (i = 65; i < 91; i++) {
+    letter = String.fromCharCode(i);
+    keyboardeventKeyPolyfill.keys[i] = [letter.toLowerCase(), letter.toUpperCase()];
+  }
+
+  function polyfill () {
+    if (!('KeyboardEvent' in window) ||
+        'key' in KeyboardEvent.prototype) {
+      return false;
+    }
+
+    // Polyfill `key` on `KeyboardEvent`.
+    var proto = {
+      get: function (x) {
+        var key = keyboardeventKeyPolyfill.keys[this.which || this.keyCode];
+
+        if (Array.isArray(key)) {
+          key = key[+this.shiftKey];
+        }
+
+        return key;
+      }
+    };
+    Object.defineProperty(KeyboardEvent.prototype, 'key', proto);
+    return proto;
+  }
+
+  if (typeof define === 'function' && define.amd) {
+    define('keyboardevent-key-polyfill', keyboardeventKeyPolyfill);
+  } else if (typeof exports !== 'undefined' && typeof module !== 'undefined') {
+    module.exports = keyboardeventKeyPolyfill;
+  } else if (window) {
+    window.keyboardeventKeyPolyfill = keyboardeventKeyPolyfill;
+  }
+
+})();

+ 144 - 0
numbat-wasm/www/main.css

@@ -0,0 +1,144 @@
+html {
+    font-family: 'Exo 2', sans-serif;
+    background-color: #ECF0F1;
+}
+
+.github-corner svg {
+    fill: #272822;
+    color: #ECF0F1;
+}
+
+body {
+    font-size: 1.2em;
+    margin: 0px;
+    margin-top: 18px;
+}
+
+#content {
+    max-width: 90%;
+    width: 850px;
+    padding-left: 20px;
+    padding-right: 20px;
+    margin: 0 auto;
+}
+
+h1 {
+    font-weight: 400;
+    font-size: 3em;
+    line-height: 100%;
+    margin-bottom: 0;
+    padding: 0;
+}
+
+p.desc {
+    margin-top: 0.6em;
+    margin-bottom: 1.8em;
+}
+
+p.links {
+    font-size: 0.8em;
+    text-align: center;
+}
+
+p.links a {
+    color: #aaa;
+}
+
+#terminal {
+    border-radius: 4px;
+    box-shadow: 0px 3px 8px #1a1a1a;
+}
+
+/* jQuery Terminal */
+
+.terminal {
+    --color: #F8F8F2;
+    --background: #272822;
+    --size: 1.6;
+    --link-color: #F8F8F2;
+    font-family: 'Fira Mono', monospace;
+}
+
+.terminal, .terminal .terminal-fill {
+    padding: 1.2em;
+    margin-bottom: 2em;
+}
+
+.terminal,
+.terminal-output > :not(.raw) span,
+.terminal-output > :not(.raw) a,
+.terminal-output > :not(.raw) div,
+.cmd,
+.cmd span,
+.cmd div {
+    font-family: 'Fira Mono', monospace;
+}
+
+.terminal-output > div {
+    padding-bottom: 1em;
+}
+
+.terminal-output > div.terminal-command {
+    padding-bottom: 0em;
+}
+
+.prompt {
+    font-weight: bold;
+    /* workaround for https://github.com/sharkdp/insect/issues/78 */
+    letter-spacing: 1px;
+}
+
+/* syntax highlighting */
+
+.hl-emphasized {
+    font-weight: 700 !important;
+}
+
+.hl-error {
+    color: #F92672 !important;
+}
+
+.hl-value {
+    color: #66D9EF !important;
+}
+
+.hl-identifier {
+    color: #FD971F !important;
+}
+
+.hl-function {
+    font-style: italic !important;
+}
+
+.hl-unit {
+    color: #A6E22E !important;
+}
+
+/* Github Badge */
+
+.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}
+
+@media (prefers-color-scheme: dark) {
+    html {
+        background: #1A1A1A;
+        color: #ECF0F1;
+    }
+
+    .github-corner svg {
+        fill: #ECF0F1;
+        color: #1A1A1A;
+    }
+
+    #terminal {
+        box-shadow: 0px 3px 8px #121212;
+    }
+
+    #insect-logo {
+        color: black;
+        filter: drop-shadow(0 0 10px #ECF0F1);
+    }
+
+    #insect-logo > path {
+        display: none;
+    }
+}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 15 - 0
numbat-wasm/www/terminal.css


برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است