WebAssembly foundation

This commit is contained in:
PaddiM8 2020-12-30 22:50:39 +01:00
parent 8014a61f1a
commit efbeb0857f
15 changed files with 302 additions and 42 deletions

143
Cargo.lock generated
View File

@ -65,6 +65,12 @@ dependencies = [
"constant_time_eq",
]
[[package]]
name = "bumpalo"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
[[package]]
name = "cc"
version = "1.0.66"
@ -83,6 +89,16 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "console_error_panic_hook"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211"
dependencies = [
"cfg-if 0.1.10",
"wasm-bindgen",
]
[[package]]
name = "constant_time_eq"
version = "0.1.5"
@ -151,6 +167,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "js-sys"
version = "0.3.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "kalk"
version = "1.3.0"
@ -158,8 +183,9 @@ dependencies = [
"lazy_static",
"regex",
"rug",
"special",
"test-case",
"wasm-bindgen",
"wasm-bindgen-test",
]
[[package]]
@ -311,6 +337,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "scoped-tls"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
[[package]]
name = "scopeguard"
version = "1.1.0"
@ -329,15 +361,6 @@ version = "1.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800"
[[package]]
name = "special"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24a65e074159b75dcf173a4733ab2188baac24967b5c8ec9ed87ae15fcbc7636"
dependencies = [
"libc",
]
[[package]]
name = "syn"
version = "1.0.54"
@ -415,6 +438,106 @@ version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasm-bindgen"
version = "0.2.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e"
dependencies = [
"cfg-if 1.0.0",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62"
dependencies = [
"bumpalo",
"lazy_static",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fe9756085a84584ee9457a002b7cdfe0bfff169f45d2591d8be1345a6780e35"
dependencies = [
"cfg-if 1.0.0",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158"
[[package]]
name = "wasm-bindgen-test"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0355fa0c1f9b792a09b6dcb6a8be24d51e71e6d74972f9eb4a44c4c004d24a25"
dependencies = [
"console_error_panic_hook",
"js-sys",
"scoped-tls",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-bindgen-test-macro",
]
[[package]]
name = "wasm-bindgen-test-macro"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27e07b46b98024c2ba2f9e83a10c2ef0515f057f2da299c1762a2017de80438b"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "web-sys"
version = "0.3.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "winapi"
version = "0.3.9"

View File

@ -10,12 +10,18 @@ license = "MIT"
keywords = ["math", "calculator", "evaluator"]
categories = ["mathematics", "parser-implementations"]
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
rug = { version = "1.11.0", features = ["float"], optional = true }
test-case = "1.0.0"
regex = "1"
lazy_static = "1.4.0"
special = "0.8.1"
wasm-bindgen = "0.2.69"
[dev-dependencies]
wasm-bindgen-test = "0.3.19"
[features]
default = ["rug"]

21
kalk/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Oliver Waldemar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -12,7 +12,9 @@ pub struct Context<'a> {
#[cfg(feature = "rug")]
precision: u32,
sum_n_value: Option<i128>,
timeout: Option<u32>,
#[cfg(not(target_arch = "wasm32"))]
timeout: Option<u128>,
#[cfg(not(target_arch = "wasm32"))]
start_time: std::time::SystemTime,
}
@ -21,7 +23,7 @@ impl<'a> Context<'a> {
symbol_table: &'a mut SymbolTable,
angle_unit: &str,
#[cfg(feature = "rug")] precision: u32,
timeout: Option<u32>,
timeout: Option<u128>,
) -> Self {
Context {
angle_unit: angle_unit.into(),
@ -29,7 +31,9 @@ impl<'a> Context<'a> {
#[cfg(feature = "rug")]
precision,
sum_n_value: None,
#[cfg(not(target_arch = "wasm32"))]
timeout: timeout,
#[cfg(not(target_arch = "wasm32"))]
start_time: std::time::SystemTime::now(),
}
}
@ -89,8 +93,9 @@ fn eval_expr_stmt(context: &mut Context, expr: &Expr) -> Result<KalkNum, CalcErr
}
fn eval_expr(context: &mut Context, expr: &Expr, unit: &str) -> Result<KalkNum, CalcError> {
#[cfg(not(target_arch = "wasm32"))]
if let (Ok(elapsed), Some(timeout)) = (context.start_time.elapsed(), context.timeout) {
if elapsed.as_secs() >= timeout as u64 {
if elapsed.as_millis() >= timeout {
return Err(CalcError::TimedOut);
}
}

View File

@ -406,18 +406,21 @@ fn multiply_into(expr: &Expr, base_expr: &Expr) -> Result<Expr, CalcError> {
}
#[allow(unused_imports, dead_code)] // Getting warnings for some reason
#[cfg(test)]
mod tests {
use crate::ast::Expr;
use crate::lexer::TokenKind::*;
use crate::parser::DECL_UNIT;
use crate::symbol_table::SymbolTable;
use crate::test_helpers::*;
use wasm_bindgen_test::*;
fn decl_unit() -> Box<Expr> {
Box::new(Expr::Var(crate::parser::DECL_UNIT.into()))
}
#[test]
#[wasm_bindgen_test]
fn test_binary() {
let ladd = binary(decl_unit(), Plus, literal(1f64));
let lsub = binary(decl_unit(), Minus, literal(1f64));
@ -466,6 +469,7 @@ mod tests {
}
#[test]
#[wasm_bindgen_test]
fn test_unary() {
let neg = unary(Minus, decl_unit());
@ -474,6 +478,7 @@ mod tests {
}
#[test]
#[wasm_bindgen_test]
fn test_fn_call() {
let call_with_literal = binary(fn_call("f", vec![*literal(2f64)]), Plus, decl_unit());
let call_with_decl_unit = fn_call("f", vec![*decl_unit()]);
@ -512,6 +517,7 @@ mod tests {
}
#[test]
#[wasm_bindgen_test]
fn test_group() {
let group_x = binary(
group(binary(decl_unit(), Plus, literal(3f64))),
@ -614,6 +620,7 @@ mod tests {
}
#[test]
#[wasm_bindgen_test]
fn test_multiple_decl_units() {
/*let add_two = binary(decl_unit(), Plus, decl_unit());

View File

@ -221,6 +221,8 @@ fn is_valid_identifier(c: Option<&char>) -> bool {
mod tests {
use super::*;
use test_case::test_case;
use wasm_bindgen_test::*;
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
fn match_tokens(tokens: Vec<Token>, expected: Vec<TokenKind>) {
let mut expected_iter = expected.iter();
@ -231,6 +233,7 @@ mod tests {
}
#[test]
#[wasm_bindgen_test]
fn test_token_kinds() {
let tokens = Lexer::lex("+-*/%^()|=!,");
let expected = vec![
@ -253,6 +256,7 @@ mod tests {
}
#[test]
#[wasm_bindgen_test]
fn test_empty() {
// test_case macro doesn't seem to work with spaces.
let test_cases = vec![" ", " ", "test ", " test "];

View File

@ -6,17 +6,19 @@ use crate::{
prelude,
symbol_table::SymbolTable,
};
use wasm_bindgen::prelude::*;
pub const DECL_UNIT: &'static str = ".u";
pub const DEFAULT_ANGLE_UNIT: &'static str = "rad";
/// Struct containing the current state of the parser. It stores user-defined functions and variables.
#[wasm_bindgen]
pub struct Context {
tokens: Vec<Token>,
pos: usize,
symbol_table: SymbolTable,
angle_unit: String,
timeout: Option<u32>,
timeout: Option<u128>,
/// This is true whenever the parser is currently parsing a unit declaration.
/// It is necessary to keep track of this in order to know when to find (figure out) units that haven't been defined yet.
/// Unit names are instead treated as variables.
@ -55,7 +57,10 @@ impl Context {
self
}
pub fn set_timeout(mut self, timeout: Option<u32>) -> Self {
/// Set the timeout in milliseconds.
/// The calculation will stop after this amount of time has passed.
#[cfg(not(target_arch = "wasm32"))]
pub fn set_timeout(mut self, timeout: Option<u128>) -> Self {
self.timeout = timeout;
self
@ -86,6 +91,31 @@ pub enum CalcError {
Unknown,
}
impl ToString for CalcError {
fn to_string(&self) -> String {
match err {
IncorrectAmountOfArguments(expected, func, got) => format!(
"Expected {} arguments for function {}, but got {}.",
expected, func, got
),
InvalidNumberLiteral(x) => format!("Invalid number literal: '{}'.", x),
InvalidOperator => format!("Invalid operator."),
InvalidUnit => format!("Invalid unit."),
TimedOut => format!("Operation took too long."),
VariableReferencesItself => format!("Variable references itself."),
UnexpectedToken(got, expected) => {
format!("Unexpected token: '{:?}', expected '{:?}'.", got, expected)
}
UnableToInvert(msg) => format!("Unable to invert: {}", msg),
UndefinedFn(name) => format!("Undefined function: '{}'.", name),
UndefinedVar(name) => format!("Undefined variable: '{}'.", name),
UnableToParseExpression => format!("Unable to parse expression."),
UnableToSolveEquation => format!("Unable to solve equation."),
Unknown => format!("Unknown error."),
}
}
}
/// Evaluate expressions/declarations and return the answer.
///
/// `None` will be returned if the last statement is a declaration.
@ -107,6 +137,19 @@ pub fn eval(
interpreter.interpret(statements)
}
#[wasm_bindgen]
#[cfg(not(feature = "rug"))]
pub fn simple_eval(input: &str) -> Result<JsValue, JsValue> {
let mut context = Context::new();
let result = eval(&mut context, input);
match result {
Ok(Some(value)) => Ok(value.to_f64().into()),
Ok(None) => Ok(JsValue::NULL),
Err(err) => Err(err.to_string()),
}
}
/// Parse expressions/declarations and return a syntax tree.
///
/// `None` will be returned if the last statement is a declaration.
@ -546,6 +589,7 @@ mod tests {
use super::*;
use crate::lexer::{Token, TokenKind::*};
use crate::test_helpers::*;
use wasm_bindgen_test::*;
fn parse_with_context(context: &mut Context, tokens: Vec<Token>) -> Result<Stmt, CalcError> {
context.tokens = tokens;
@ -563,6 +607,7 @@ mod tests {
}
#[test]
#[wasm_bindgen_test]
fn test_var() {
// x
let tokens = vec![token(Identifier, "x"), token(EOF, "")];
@ -571,6 +616,7 @@ mod tests {
}
#[test]
#[wasm_bindgen_test]
fn test_binary() {
// 1+2*(3-4/5)
let tokens = vec![
@ -607,6 +653,7 @@ mod tests {
}
#[test]
#[wasm_bindgen_test]
fn test_pow() {
let tokens = vec![
token(Literal, "1"),
@ -640,6 +687,7 @@ mod tests {
}
#[test]
#[wasm_bindgen_test]
fn test_percent() {
let tokens = vec![
token(Literal, "1"),
@ -662,6 +710,7 @@ mod tests {
}
#[test]
#[wasm_bindgen_test]
fn test_unit() {
let tokens = vec![token(Literal, "1"), token(Identifier, "a")];
@ -677,6 +726,7 @@ mod tests {
}
#[test]
#[wasm_bindgen_test]
fn test_var_decl() {
let tokens = vec![
token(Identifier, "x"),
@ -697,6 +747,7 @@ mod tests {
}
#[test]
#[wasm_bindgen_test]
fn test_fn_decl() {
let tokens = vec![
token(Identifier, "f"),
@ -721,6 +772,7 @@ mod tests {
}
#[test]
#[wasm_bindgen_test]
fn test_fn_call() {
let tokens = vec![
token(Identifier, "f"),

View File

@ -96,7 +96,8 @@ fn from_angle_unit(context: &mut interpreter::Context, x: f64, angle_unit: &str)
pub mod special_funcs {
pub fn factorial(x: f64) -> f64 {
special::Gamma::gamma(x + 1f64)
//special::Gamma::gamma(x + 1f64)
x
}
}
@ -198,7 +199,8 @@ pub(super) mod funcs {
}
pub fn gamma(x: f64) -> f64 {
special::Gamma::gamma(x)
//special::Gamma::gamma(x)
x
}
pub fn hyp(x: f64, y: f64) -> f64 {

21
kalk_cli/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Oliver Waldemar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -17,7 +17,7 @@ pub fn eval(parser: &mut parser::Context, input: &str, precision: u32) {
println!("{} {}", result_str, result.get_unit());
}
Ok(None) => print!(""),
Err(err) => print_calc_err(err),
Err(err) => print_err(&err.to_string()),
}
}
@ -25,26 +25,3 @@ pub fn print_err(msg: &str) {
Red.paint(msg).to_string();
println!("{}", msg);
}
fn print_calc_err(err: CalcError) {
print_err(&match err {
IncorrectAmountOfArguments(expected, func, got) => format!(
"Expected {} arguments for function {}, but got {}.",
expected, func, got
),
InvalidNumberLiteral(x) => format!("Invalid number literal: '{}'.", x),
InvalidOperator => format!("Invalid operator."),
InvalidUnit => format!("Invalid unit."),
TimedOut => format!("Operation took too long."),
VariableReferencesItself => format!("Variable references itself."),
UnexpectedToken(got, expected) => {
format!("Unexpected token: '{:?}', expected '{:?}'.", got, expected)
}
UnableToInvert(msg) => format!("Unable to invert: {}", msg),
UndefinedFn(name) => format!("Undefined function: '{}'.", name),
UndefinedVar(name) => format!("Undefined variable: '{}'.", name),
UnableToParseExpression => format!("Unable to parse expression."),
UnableToSolveEquation => format!("Unable to solve equation."),
Unknown => format!("Unknown error."),
});
}

2
kalk_web/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
node_modules/
package-lock.json

10
kalk_web/index.html Normal file
View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>kalk</title>
</head>
<body>
<script src="./index.js"></script>
</body>
</html>

11
kalk_web/index.js Normal file
View File

@ -0,0 +1,11 @@
main();
async function main() {
const kalk = await import("kalk-rs");
try {
console.log(kalk.simple_eval("5+"));
} catch(err) {
console.log(err);
}
}

10
kalk_web/package.json Normal file
View File

@ -0,0 +1,10 @@
{
"scripts": {
"serve": "webpack-dev-server"
},
"devDependencies": {
"webpack": "^4.25.1",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.10"
}
}

View File

@ -0,0 +1,9 @@
const path = require('path');
module.exports = {
entry: "./index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "index.js",
},
mode: "development"
};