├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── p64lang_cli ├── Cargo.toml ├── src │ └── main.rs └── test_scripts │ ├── fib_iterative.p64pl │ ├── fib_recursive.p64pl │ └── fib_recursive_cache.p64pl ├── p64lang_wasm ├── .gitignore ├── Cargo.toml ├── build.sh ├── src │ └── lib.rs └── web │ ├── index.html │ ├── index.js │ ├── package-lock.json │ ├── package.json │ ├── webpack.config.js │ └── webpack.prod.js └── src ├── ast.rs ├── interpreter.rs ├── lib.rs ├── parser.rs └── runtime.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | 5 | /p64lang_cli/target 6 | /p64lang_wasm/target 7 | /p64lang/Cargo.lock 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "p64lang" 3 | version = "0.1.0" 4 | authors = ["Simon Pugnet "] 5 | 6 | [features] 7 | default = ["std"] 8 | no_std = ["nom/alloc"] 9 | std = ["nom/std"] 10 | 11 | [dependencies] 12 | nom = { version = "^4.1", default-features = false } 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Simon Pugnet 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # p64lang_rust 2 | Simple language parser, interpreter and CLI built in Rust, to be used for baremetal/no_std environments. 3 | 4 | ## Introduction 5 | This repository contains three crates: - 6 | 7 | - p64lang: library containing a parser and interpreter for the work-in-progress P64PL language; 8 | - p64lang_cli: binary crate which includes to above library and provides a simple CLI for executing P64PL programs from stdin; and 9 | - p64lang_wasm: a library crate intended for the wasm32-unknown-unknown target with an associated WebPack project providing a simple web interface to the p64lang parser and interpreter. 10 | 11 | The eventual goal of this project is to create a baremetal/no_std interpreted language for use within another (as yet unreleased) project. The p64lang crate includes a `no_std` feature which compiles the parser and interpreter without using the Rust standard library. This is an optional feature, the `std` feature is enabled by default. 12 | 13 | ## Example 14 | 15 | For a live example of the parser and interpreter running in the browser as a WASM module, please [take a look at this example on my website](https://www.polaris64.net/resources/programming/p64lang_wasm/). 16 | -------------------------------------------------------------------------------- /p64lang_cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "p64lang_cli" 3 | version = "0.1.0" 4 | authors = ["Simon Pugnet "] 5 | 6 | [dependencies] 7 | p64lang = { path = "../", default-features = false, features = ["std"] } 8 | -------------------------------------------------------------------------------- /p64lang_cli/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate p64lang; 2 | 3 | use std::io::{self, Read}; 4 | 5 | use p64lang::interpret; 6 | use p64lang::interpreter::Scope; 7 | use p64lang::runtime::insert_native_functions; 8 | 9 | fn main() { 10 | let mut buffer = String::new(); 11 | io::stdin() 12 | .read_to_string(&mut buffer) 13 | .expect("Unable to read input"); 14 | 15 | let mut scope = Scope::new(); 16 | insert_native_functions(&mut scope); 17 | let res = interpret(&buffer, scope); 18 | println!("Result: {:?}", res.exec_result); 19 | } 20 | -------------------------------------------------------------------------------- /p64lang_cli/test_scripts/fib_iterative.p64pl: -------------------------------------------------------------------------------- 1 | fn fib(n) { 2 | if n < 1 { return 0; }; 3 | let res = 0; 4 | let prev = 1; 5 | let i = n; 6 | loop { 7 | let temp = res; 8 | let res = res + prev; 9 | let prev = temp; 10 | let i = i - 1; 11 | if i == 0 { break; }; 12 | }; 13 | return res; 14 | }; 15 | 16 | let counter = 0; 17 | loop { 18 | println("fib(", counter, ") = ", fib(counter)); 19 | let counter = counter + 1; 20 | if counter > 28 { break; }; 21 | } 22 | -------------------------------------------------------------------------------- /p64lang_cli/test_scripts/fib_recursive.p64pl: -------------------------------------------------------------------------------- 1 | fn fib(n) { 2 | if n <= 1 { return n; }; 3 | return fib(n - 2) + fib(n - 1); 4 | }; 5 | 6 | let counter = 0; 7 | loop { 8 | println("fib(", counter, ") = ", fib(counter)); 9 | let counter = counter + 1; 10 | if counter > 28 { break; }; 11 | } 12 | -------------------------------------------------------------------------------- /p64lang_cli/test_scripts/fib_recursive_cache.p64pl: -------------------------------------------------------------------------------- 1 | let fib_cache = []; 2 | 3 | fn fib(n) { 4 | if n <= 1 { return n; }; 5 | let a = fib_cache[n - 1]; 6 | let b = fib_cache[n - 2]; 7 | if !a { 8 | fib_cache[n - 1] = fib(n - 1); 9 | let a = fib_cache[n - 1]; 10 | }; 11 | if !b { 12 | fib_cache[n - 2] = fib(n - 2); 13 | let b = fib_cache[n - 2]; 14 | }; 15 | return a + b; 16 | }; 17 | 18 | let counter = 0; 19 | loop { 20 | println("fib(", counter, ") = ", fib(counter)); 21 | let counter = counter + 1; 22 | if counter > 28 { break; }; 23 | } 24 | -------------------------------------------------------------------------------- /p64lang_wasm/.gitignore: -------------------------------------------------------------------------------- 1 | web/node_modules 2 | web/p64lang_wasm* 3 | web/dist 4 | -------------------------------------------------------------------------------- /p64lang_wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "p64lang_wasm" 3 | version = "0.1.0" 4 | authors = ["Simon Pugnet "] 5 | 6 | [lib] 7 | crate-type = ["cdylib"] 8 | 9 | [dependencies] 10 | wasm-bindgen = "0.2" 11 | p64lang = { path = "../", default-features = false, features = ["no_std"] } 12 | -------------------------------------------------------------------------------- /p64lang_wasm/build.sh: -------------------------------------------------------------------------------- 1 | cargo +nightly build --release --target wasm32-unknown-unknown 2 | wasm-bindgen target/wasm32-unknown-unknown/release/p64lang_wasm.wasm --out-dir ./web 3 | -------------------------------------------------------------------------------- /p64lang_wasm/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | #[macro_use] 4 | extern crate alloc; 5 | 6 | extern crate p64lang; 7 | extern crate wasm_bindgen; 8 | 9 | use core::any::Any; 10 | use alloc::fmt::Write; 11 | use alloc::rc::Rc; 12 | use alloc::string::String; 13 | 14 | use wasm_bindgen::prelude::*; 15 | 16 | use p64lang::ast::{NativeFunction, Value}; 17 | use p64lang::interpreter::{Scope, ScopeChain}; 18 | use p64lang::interpret; 19 | 20 | struct NFPrint; 21 | impl NativeFunction for NFPrint { 22 | fn execute<'src>(&self, _scopes: &mut ScopeChain<'src>, args: &[Value<'src>]) -> Value<'src> { 23 | let mut buf = String::new(); 24 | for arg in args { 25 | match arg { 26 | Value::Int(x) => write!(buf, "{}", x).unwrap_or_default(), 27 | Value::Real(x) => write!(buf, "{}", x).unwrap_or_default(), 28 | Value::Str(x) => write!(buf, "{}", x).unwrap_or_default(), 29 | _ => write!(buf, "{:?}", arg).unwrap_or_default(), 30 | }; 31 | } 32 | js_print(buf.as_str(), false); 33 | Value::None 34 | } 35 | 36 | fn as_any(&self) -> &dyn Any { 37 | self 38 | } 39 | } 40 | 41 | struct NFPrintLn; 42 | impl NativeFunction for NFPrintLn { 43 | fn execute<'src>(&self, _scopes: &mut ScopeChain<'src>, args: &[Value<'src>]) -> Value<'src> { 44 | let mut buf = String::new(); 45 | for arg in args { 46 | match arg { 47 | Value::Int(x) => write!(buf, "{}", x).unwrap_or_default(), 48 | Value::Real(x) => write!(buf, "{}", x).unwrap_or_default(), 49 | Value::Str(x) => write!(buf, "{}", x).unwrap_or_default(), 50 | _ => write!(buf, "{:?}", arg).unwrap_or_default(), 51 | }; 52 | } 53 | js_print(buf.as_str(), true); 54 | Value::None 55 | } 56 | 57 | fn as_any(&self) -> &dyn Any { 58 | self 59 | } 60 | } 61 | 62 | #[wasm_bindgen(raw_module = "./index.js")] 63 | extern { 64 | fn js_print(s: &str, nl: bool); 65 | } 66 | 67 | #[wasm_bindgen] 68 | pub fn interpret_str(src: &str) -> String { 69 | let mut scope = Scope::new(); 70 | scope.native_funcs.insert("print", Rc::new(NFPrint {})); 71 | scope.native_funcs.insert("println", Rc::new(NFPrintLn {})); 72 | let res = interpret(src, scope); 73 | format!("Result: {:?}", res.exec_result) 74 | } 75 | -------------------------------------------------------------------------------- /p64lang_wasm/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 19 | P64Lang WASM 20 | 21 | 22 |
23 | 24 |
25 |
26 |
27 |
28 |

P64Lang WASM

29 |

30 | This page demonstrates the 31 | P64Lang 32 | parser and interpreter running as a WASM module. 33 |

34 |

35 | Enter some code in the "Script Source" text box then 36 | click "Run!" to parse and, if successful, execute the 37 | script. The results will be displayed in the "Output" 38 | panel. 39 |

40 | Back to site 41 |
42 |
43 |
44 |
45 | 46 |
47 | 48 |
49 |
50 |
51 | 52 |
53 |
54 |

Script source

55 |
56 |
57 | 64 | 65 |
66 |
67 |
68 |
69 |

Actions

70 |
71 |
72 | 73 | 74 |
75 |
76 |
77 |
78 |
79 | 80 | 81 | 82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |

Output

90 |
91 |
92 |

 93 |             
94 |
95 |
96 |
97 |
98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /p64lang_wasm/web/index.js: -------------------------------------------------------------------------------- 1 | const js = import('./p64lang_wasm'); 2 | 3 | function get_source() { 4 | return document.getElementById('txt_source'); 5 | } 6 | 7 | function get_stdout() { 8 | return document.getElementById('stdout'); 9 | } 10 | 11 | function set_exec_time(ms) { 12 | document.getElementById('exec_time').innerText = `Execution time: ${ms}ms`; 13 | } 14 | 15 | function display_error(msg) { 16 | const container = document.getElementById('errors'); 17 | const div = document.createElement('div'); 18 | div.classList.toggle('alert'); 19 | div.classList.toggle('alert-danger'); 20 | div.innerText = `ERROR: ${msg}`; 21 | container.appendChild(div); 22 | } 23 | 24 | export function js_print(s, nl) { 25 | const div_stdout = get_stdout(); 26 | div_stdout.innerHTML = div_stdout.innerHTML + s + (nl ? "
" : ''); 27 | } 28 | 29 | js.then((js) => { 30 | 31 | const btn_run = document.getElementById('btn_run'); 32 | const btn_clr = document.getElementById('btn_clr'); 33 | 34 | btn_run.addEventListener('click', () => { 35 | const t0 = performance.now(); 36 | const s = js.interpret_str(get_source().value); 37 | const t1 = performance.now(); 38 | js_print(s, true); 39 | set_exec_time(t1 - t0); 40 | }); 41 | 42 | btn_clr.addEventListener('click', () => { 43 | get_stdout().innerHTML = ''; 44 | }); 45 | 46 | btn_load1.addEventListener('click', () => { 47 | get_source().value = 'fn fib(n) {\n\ 48 | if n <= 1 { return n; };\n\ 49 | return fib(n - 2) + fib(n - 1);\n\ 50 | };\n\n\ 51 | let counter = 0;\n\ 52 | loop {\n\ 53 | println("fib(", counter, ") = ", fib(counter));\n\ 54 | let counter = counter + 1;\n\ 55 | if counter > 20 { break; };\n\ 56 | }'; 57 | }); 58 | 59 | btn_load2.addEventListener('click', () => { 60 | get_source().value = 'fn fib(n) {\n\ 61 | if n < 1 { return 0; };\n\ 62 | let res = 0;\n\ 63 | let prev = 1;\n\ 64 | let i = n;\n\ 65 | loop {\n\ 66 | let temp = res;\n\ 67 | let res = res + prev;\n\ 68 | let prev = temp;\n\ 69 | let i = i - 1;\n\ 70 | if i == 0 { break; };\n\ 71 | };\n\ 72 | return res;\n\ 73 | };\n\n\ 74 | let counter = 0;\n\ 75 | loop {\n\ 76 | println("fib(", counter, ") = ", fib(counter));\n\ 77 | let counter = counter + 1;\n\ 78 | if counter > 28 { break; };\n\ 79 | }'; 80 | }); 81 | 82 | btn_load3.addEventListener('click', () => { 83 | get_source().value = 'let fib_cache = [];\n\n\ 84 | fn fib(n) {\n\ 85 | if n <= 1 { return n; };\n\ 86 | let a = fib_cache[n - 1];\n\ 87 | let b = fib_cache[n - 2];\n\ 88 | if !a {\n\ 89 | fib_cache[n - 1] = fib(n - 1);\n\ 90 | let a = fib_cache[n - 1];\n\ 91 | };\n\ 92 | if !b {\n\ 93 | fib_cache[n - 2] = fib(n - 2);\n\ 94 | let b = fib_cache[n - 2];\n\ 95 | };\n\ 96 | return a + b;\n\ 97 | };\n\n\ 98 | let counter = 0;\n\ 99 | loop {\n\ 100 | println("fib(", counter, ") = ", fib(counter));\n\ 101 | let counter = counter + 1;\n\ 102 | if counter > 28 { break; };\n\ 103 | }'; 104 | }); 105 | 106 | }).catch((err) => { 107 | display_error(`Unable to load WASM module: ${err.toString()}`); 108 | }); 109 | -------------------------------------------------------------------------------- /p64lang_wasm/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "p64lang_wasm", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "webpack --config webpack.prod.js && cp index.html dist/", 8 | "serve": "webpack-dev-server" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "webpack": "^4.41.5", 14 | "webpack-cli": "^3.3.10", 15 | "webpack-dev-server": "^3.10.2", 16 | "webpack-merge": "^4.2.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /p64lang_wasm/web/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | output: { 6 | path: path.resolve(__dirname, 'dist'), 7 | filename: 'index.js', 8 | }, 9 | mode: 'development', 10 | }; 11 | -------------------------------------------------------------------------------- /p64lang_wasm/web/webpack.prod.js: -------------------------------------------------------------------------------- 1 | const merge = require('webpack-merge'); 2 | const common = require('./webpack.config.js'); 3 | 4 | module.exports = merge(common, { 5 | mode: 'development', 6 | }); 7 | -------------------------------------------------------------------------------- /src/ast.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(feature = "no_std"))] 2 | use std::any::Any; 3 | #[cfg(feature = "no_std")] 4 | use core::any::Any; 5 | 6 | #[cfg(not(feature = "no_std"))] 7 | use std::collections::HashMap; 8 | #[cfg(feature = "no_std")] 9 | use alloc::collections::BTreeMap; 10 | 11 | #[cfg(feature = "no_std")] 12 | use alloc::boxed::Box; 13 | #[cfg(feature = "no_std")] 14 | use alloc::vec::Vec; 15 | 16 | use interpreter::ScopeChain; 17 | 18 | // --- Types --- 19 | 20 | /// Result of executing an Executable 21 | #[derive(Clone, Debug, PartialEq)] 22 | pub enum ExecResult<'src> { 23 | Break, 24 | Error(&'static str), 25 | None, 26 | Return(Value<'src>), 27 | } 28 | 29 | /// Language expression 30 | /// 31 | /// Numbers, strings, lists, function calls, identifiers and operations thereon. Anything that can 32 | /// be evaluated to a Value. 33 | #[derive(Clone, Debug, PartialEq)] 34 | pub enum Expr<'src> { 35 | BinOp(Box>, Opcode, Box>), 36 | Bool(bool), 37 | Dict(Vec<(Ident<'src>, Box>)>), 38 | FuncCall(Ident<'src>, Vec>>), 39 | Id(Ident<'src>), 40 | Int(isize), 41 | ListElement(Ident<'src>, Box>), 42 | List(Vec>>), 43 | None, 44 | Real(f64), 45 | Str(&'src str), 46 | UnaryOp(Opcode, Box>), 47 | } 48 | 49 | /// Script-defined functions 50 | /// 51 | /// Contains a list of statements (StmtBlock) that are executed when the Function is called, and a 52 | /// list of argument Idents that will be assigned to actual values during the call. 53 | #[derive(Debug)] 54 | pub struct Function<'src> { 55 | pub args: Vec>, 56 | pub stmts: StmtBlock<'src>, 57 | } 58 | 59 | /// Language identifier 60 | /// 61 | /// Used to represent a variable or function name. 62 | pub type Ident<'src> = &'src str; 63 | 64 | /// Operation codes 65 | /// 66 | /// Contains variants representing various operations that can be performed on expressions, such as 67 | /// arithmetic, logical and relational. 68 | #[derive(Clone, Debug, PartialEq)] 69 | pub enum Opcode { 70 | Add, 71 | Div, 72 | Equal, 73 | GreaterThan, 74 | GreaterThanOrEqual, 75 | LessThan, 76 | LessThanOrEqual, 77 | LogicalAnd, 78 | LogicalOr, 79 | LogicalXor, 80 | Mod, 81 | Mul, 82 | Not, 83 | NotEqual, 84 | Sub, 85 | } 86 | 87 | /// Language statements 88 | /// 89 | /// Any single program instruction, such as a variable assignment, function call, conditional, 90 | /// loop. 91 | #[derive(Clone, Debug, PartialEq)] 92 | pub enum Stmt<'src> { 93 | Break, 94 | Expr(Expr<'src>), 95 | FnDef(Ident<'src>, Vec>, StmtBlock<'src>), 96 | If(Expr<'src>, StmtBlock<'src>), 97 | IfElse(Expr<'src>, StmtBlock<'src>, StmtBlock<'src>), 98 | Let(Ident<'src>, Expr<'src>), 99 | ListItemAssignment(Ident<'src>, Expr<'src>, Expr<'src>), 100 | Loop(StmtBlock<'src>), 101 | Return(Expr<'src>), 102 | } 103 | 104 | /// Statement block 105 | /// 106 | /// A block of zero or more Stmts 107 | pub type StmtBlock<'src> = Vec>; 108 | 109 | /// Result of evaluating an Evaluatable 110 | #[derive(Clone, Debug, PartialEq)] 111 | pub enum Value<'src> { 112 | Bool(bool), 113 | 114 | #[cfg(feature = "no_std")] 115 | Dict(BTreeMap, Value<'src>>), 116 | 117 | #[cfg(not(feature = "no_std"))] 118 | Dict(HashMap, Value<'src>>), 119 | 120 | Int(isize), 121 | List(Vec>), 122 | None, 123 | Real(f64), 124 | Str(&'src str), 125 | } 126 | 127 | // --- Traits --- 128 | 129 | /// Trait allowing various language elements to be evaluated 130 | pub trait Evaluatable<'src> { 131 | fn eval(&self, scopes: &mut ScopeChain<'src>) -> Value<'src>; 132 | } 133 | 134 | /// Trait allowing various language elements to be executed 135 | pub trait Executable<'src> { 136 | fn exec(&self, scopes: &mut ScopeChain<'src>) -> ExecResult<'src>; 137 | } 138 | 139 | /// Trait used to allow structs to be called from a script 140 | /// 141 | /// The `execute()` method will be called via the script interpreter with the current ScopeChain 142 | /// and a list of argument values. 143 | pub trait NativeFunction { 144 | fn execute<'src>(&self, scopes: &mut ScopeChain<'src>, args: &[Value<'src>]) -> Value<'src>; 145 | fn as_any(&self) -> &dyn Any; 146 | } 147 | -------------------------------------------------------------------------------- /src/interpreter.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(feature = "no_std"))] 2 | use std::collections::HashMap; 3 | #[cfg(feature = "no_std")] 4 | use alloc::collections::BTreeMap; 5 | 6 | #[cfg(not(feature = "no_std"))] 7 | use std::rc::Rc; 8 | #[cfg(feature = "no_std")] 9 | use alloc::rc::Rc; 10 | #[cfg(feature = "no_std")] 11 | use alloc::vec::Vec; 12 | 13 | #[cfg(feature = "no_std")] 14 | use core::f64::EPSILON; 15 | 16 | 17 | #[cfg(feature = "no_std")] 18 | /* 19 | * When not using the standard library, the f64::abs() method is not available. 20 | * Define a trait and implement it for f64 here so that an implementation is 21 | * available. 22 | */ 23 | trait CoreAbs { 24 | fn abs(self) -> f64; 25 | } 26 | 27 | #[cfg(feature = "no_std")] 28 | impl CoreAbs for f64 { 29 | fn abs(self) -> f64 { 30 | // TODO: find better solution for f64::abs() in no_std situations 31 | if self < 0f64 { 32 | -self 33 | } else { 34 | self 35 | } 36 | } 37 | } 38 | 39 | 40 | #[cfg(not(feature = "no_std"))] 41 | use std::f64::EPSILON; 42 | 43 | 44 | use ast::{ 45 | Evaluatable, ExecResult, Executable, Expr, Function, Ident, NativeFunction, Opcode, Stmt, 46 | StmtBlock, Value, 47 | }; 48 | 49 | /// Language scope struct 50 | /// 51 | /// Contains HashMaps mapping Idents to Functions, NativeFunctions and Values (variables) in the 52 | /// scope 53 | #[derive(Default)] 54 | pub struct Scope<'src> { 55 | 56 | #[cfg(not(feature = "no_std"))] 57 | pub funcs: HashMap, Rc>>, 58 | #[cfg(feature = "no_std")] 59 | pub funcs: BTreeMap, Rc>>, 60 | 61 | #[cfg(not(feature = "no_std"))] 62 | pub native_funcs: HashMap, Rc>, 63 | #[cfg(feature = "no_std")] 64 | pub native_funcs: BTreeMap, Rc>, 65 | 66 | // TODO: vars: HashMap to avoid clone? 67 | #[cfg(not(feature = "no_std"))] 68 | pub vars: HashMap, Value<'src>>, 69 | #[cfg(feature = "no_std")] 70 | pub vars: BTreeMap, Value<'src>>, 71 | } 72 | impl<'src> Scope<'src> { 73 | /// Create an emptycope Scope 74 | pub fn new() -> Scope<'src> { 75 | Scope { 76 | #[cfg(not(feature = "no_std"))] 77 | funcs: HashMap::new(), 78 | #[cfg(not(feature = "no_std"))] 79 | native_funcs: HashMap::new(), 80 | #[cfg(not(feature = "no_std"))] 81 | vars: HashMap::new(), 82 | #[cfg(feature = "no_std")] 83 | funcs: BTreeMap::new(), 84 | #[cfg(feature = "no_std")] 85 | native_funcs: BTreeMap::new(), 86 | #[cfg(feature = "no_std")] 87 | vars: BTreeMap::new(), 88 | } 89 | } 90 | 91 | /// When creating a Scope for a Function invocation, inserts variables for each of the 92 | /// Function's arguments with the values passed to the invocation. 93 | pub fn from_args(args: &[(&Ident<'src>, &Value<'src>)]) -> Scope<'src> { 94 | let mut scope = Scope::new(); 95 | for arg in args { 96 | scope.vars.insert(arg.0, arg.1.clone()); 97 | } 98 | scope 99 | } 100 | } 101 | 102 | /// Chain of Scopes 103 | /// 104 | /// - A stack of Scopes. 105 | /// - Contains methods to resolve variables, Functions, etc and to modify Scope items. 106 | /// - Each function call pushes a new Scope onto the current ScopeChain. 107 | /// - All evaluations/executions require a ScopeChain. 108 | #[derive(Default)] 109 | pub struct ScopeChain<'src> { 110 | scopes: Vec>, 111 | } 112 | impl<'src> ScopeChain<'src> { 113 | /// Creates an empty ScopeChain 114 | pub fn new() -> ScopeChain<'src> { 115 | ScopeChain { scopes: vec![] } 116 | } 117 | 118 | /// Creates a new ScopeChain with a single root Scope 119 | pub fn from_scope(scope: Scope<'src>) -> ScopeChain<'src> { 120 | ScopeChain { 121 | scopes: vec![scope], 122 | } 123 | } 124 | 125 | /// Pushes a new Scope onto the stack 126 | pub fn push(&mut self, scope: Scope<'src>) { 127 | self.scopes.push(scope); 128 | } 129 | 130 | /// Pops the last Scope from the stack 131 | pub fn pop(&mut self) -> Option> { 132 | self.scopes.pop() 133 | } 134 | 135 | /// Inserts a Function into the last Scope with the Ident `key` 136 | pub fn insert_func(&mut self, key: &'src str, val: Function<'src>) { 137 | match self.scopes.last_mut() { 138 | Some(ref mut scope) => scope.funcs.insert(key, Rc::new(val)), 139 | _ => None, 140 | }; 141 | } 142 | 143 | /// Inserts a Value `val` into the dict identified by `key` at index `idx` 144 | pub fn insert_dict_item(&mut self, key: &'src str, idx: &'src str, val: Value<'src>) { 145 | for scope in self.scopes.iter_mut().rev() { 146 | if let Some(ref mut scope_val) = scope.vars.get_mut(key) { 147 | if let Value::Dict(ref mut dict) = scope_val { 148 | dict.insert(idx, val); 149 | break; 150 | } 151 | } 152 | } 153 | } 154 | 155 | /// Inserts a Value `val` into the list identified by `key` at index `idx` 156 | pub fn insert_list_item(&mut self, key: &'src str, idx: usize, val: Value<'src>) { 157 | for scope in self.scopes.iter_mut().rev() { 158 | if let Some(ref mut scope_val) = scope.vars.get_mut(key) { 159 | if let Value::List(ref mut lst) = scope_val { 160 | if lst.len() <= idx { 161 | lst.resize(idx + 1, Value::None); 162 | } 163 | lst[idx] = val; 164 | break; 165 | } 166 | } 167 | } 168 | } 169 | 170 | /// Inserts or updates a Value for a variable identified by `key` 171 | pub fn insert_var(&mut self, key: &'src str, val: Value<'src>) { 172 | match self.scopes.last_mut() { 173 | Some(ref mut scope) => scope.vars.insert(key, val), 174 | _ => None, 175 | }; 176 | } 177 | 178 | /// Searches from last to first Scope for a Function identified by `key` and returns a 179 | /// reference 180 | pub fn resolve_func(&self, key: &'src str) -> Option>> { 181 | for scope in self.scopes.iter().rev() { 182 | if let Some(x) = scope.funcs.get(key) { 183 | return Some(Rc::clone(x)); 184 | } 185 | } 186 | None 187 | } 188 | 189 | /// Searches from last to first Scope for a NativeFunction identified by `key` and returns a 190 | /// reference 191 | pub fn resolve_native_func(&self, key: &'src str) -> Option> { 192 | for scope in self.scopes.iter().rev() { 193 | if let Some(x) = scope.native_funcs.get(key) { 194 | return Some(Rc::clone(x)); 195 | } 196 | } 197 | None 198 | } 199 | 200 | /// Searches from last to first Scope for a variable identified by `key` and returns a 201 | /// reference to its Value 202 | pub fn resolve_var(&self, key: &'src str) -> Option<&Value<'src>> { 203 | for scope in self.scopes.iter().rev() { 204 | if let Some(ref x) = scope.vars.get(key) { 205 | return Some(x); 206 | } 207 | } 208 | None 209 | } 210 | } 211 | 212 | impl Opcode { 213 | /// Calculates an Opcode's integer result given left and right operands 214 | fn calc_i(&self, l: isize, r: isize) -> isize { 215 | match *self { 216 | Opcode::Add => l + r, 217 | Opcode::Div => l / r, 218 | Opcode::Mod => l % r, 219 | Opcode::Mul => l * r, 220 | Opcode::Sub => l - r, 221 | _ => 0, 222 | } 223 | } 224 | 225 | /// Calculates an Opcode's floating-point result given left and right operands 226 | fn calc_f(&self, l: f64, r: f64) -> f64 { 227 | match *self { 228 | Opcode::Add => l + r, 229 | Opcode::Div => l / r, 230 | Opcode::Mul => l * r, 231 | Opcode::Sub => l - r, 232 | _ => 0f64, 233 | } 234 | } 235 | 236 | /// Evaluates the Opcode given left and right operands according to the operand types 237 | fn eval<'src>(&self, l: Value<'src>, r: Value<'src>) -> Value<'src> { 238 | match *self { 239 | Opcode::Add | Opcode::Mul | Opcode::Sub => match (l, r) { 240 | (Value::Int(l), Value::Int(r)) => Value::Int(self.calc_i(l, r)), 241 | (Value::Int(l), Value::Real(r)) => Value::Real(self.calc_f(l as f64, r)), 242 | (Value::Real(l), Value::Int(r)) => Value::Real(self.calc_f(l, r as f64)), 243 | (Value::Real(l), Value::Real(r)) => Value::Real(self.calc_f(l, r)), 244 | (_, _) => Value::None, 245 | }, 246 | Opcode::Div => match (l, r) { 247 | (Value::Int(l), Value::Int(r)) => Value::Real(self.calc_f(l as f64, r as f64)), 248 | (Value::Int(l), Value::Real(r)) => Value::Real(self.calc_f(l as f64, r)), 249 | (Value::Real(l), Value::Int(r)) => Value::Real(self.calc_f(l, r as f64)), 250 | (Value::Real(l), Value::Real(r)) => Value::Real(self.calc_f(l, r)), 251 | (_, _) => Value::None, 252 | }, 253 | Opcode::Mod => match (l, r) { 254 | (Value::Int(l), Value::Int(r)) => Value::Int(self.calc_i(l, r)), 255 | (_, _) => Value::None, 256 | }, 257 | Opcode::Equal 258 | | Opcode::NotEqual 259 | | Opcode::LessThan 260 | | Opcode::GreaterThan 261 | | Opcode::LessThanOrEqual 262 | | Opcode::GreaterThanOrEqual 263 | | Opcode::LogicalAnd 264 | | Opcode::LogicalOr 265 | | Opcode::LogicalXor => self.logical(l, r), 266 | 267 | _ => Value::None, 268 | } 269 | } 270 | 271 | /// Evaluates the unary Opcode given Value of the operand 272 | fn eval_unary<'src>(&self, x: &Value<'src>) -> Value<'src> { 273 | match *self { 274 | Opcode::Not => match x { 275 | Value::Bool(x) => Value::Bool(!x), 276 | Value::None => Value::Bool(true), 277 | _ => Value::Bool(false), 278 | }, 279 | _ => Value::None, 280 | } 281 | } 282 | 283 | /// Calculates an Opcode's logical result given left and right operands 284 | fn logical<'src>(&self, l: Value<'src>, r: Value<'src>) -> Value<'src> { 285 | 286 | match *self { 287 | Opcode::Equal => match (l, r) { 288 | (Value::Int(l), Value::Int(r)) => Value::Bool(l == r), 289 | (Value::Int(l), Value::Real(r)) => Value::Bool(((l as f64) - r).abs() <= EPSILON), 290 | (Value::Real(l), Value::Int(r)) => Value::Bool((l - (r as f64)).abs() <= EPSILON), 291 | (Value::Real(l), Value::Real(r)) => Value::Bool((l - r).abs() <= EPSILON), 292 | (Value::Str(l), Value::Str(r)) => Value::Bool(l == r), 293 | (_, _) => Value::None, 294 | }, 295 | Opcode::NotEqual => match (l, r) { 296 | (Value::Int(l), Value::Int(r)) => Value::Bool(l != r), 297 | (Value::Int(l), Value::Real(r)) => Value::Bool(((l as f64) - r).abs() > EPSILON), 298 | (Value::Real(l), Value::Int(r)) => Value::Bool((l - (r as f64)).abs() > EPSILON), 299 | (Value::Real(l), Value::Real(r)) => Value::Bool((l - r).abs() > EPSILON), 300 | (Value::Str(l), Value::Str(r)) => Value::Bool(l != r), 301 | (_, _) => Value::None, 302 | }, 303 | Opcode::LessThan => match (l, r) { 304 | (Value::Int(l), Value::Int(r)) => Value::Bool(l < r), 305 | (Value::Int(l), Value::Real(r)) => Value::Bool((l as f64) < r), 306 | (Value::Real(l), Value::Int(r)) => Value::Bool(l < r as f64), 307 | (Value::Real(l), Value::Real(r)) => Value::Bool(l < r), 308 | (Value::Str(l), Value::Str(r)) => Value::Bool(l < r), 309 | (_, _) => Value::None, 310 | }, 311 | Opcode::GreaterThan => match (l, r) { 312 | (Value::Int(l), Value::Int(r)) => Value::Bool(l > r), 313 | (Value::Int(l), Value::Real(r)) => Value::Bool(l as f64 > r), 314 | (Value::Real(l), Value::Int(r)) => Value::Bool(l > r as f64), 315 | (Value::Real(l), Value::Real(r)) => Value::Bool(l > r), 316 | (Value::Str(l), Value::Str(r)) => Value::Bool(l > r), 317 | (_, _) => Value::None, 318 | }, 319 | Opcode::LessThanOrEqual => match (l, r) { 320 | (Value::Int(l), Value::Int(r)) => Value::Bool(l <= r), 321 | (Value::Int(l), Value::Real(r)) => Value::Bool(l as f64 <= r), 322 | (Value::Real(l), Value::Int(r)) => Value::Bool(l <= r as f64), 323 | (Value::Real(l), Value::Real(r)) => Value::Bool(l <= r), 324 | (Value::Str(l), Value::Str(r)) => Value::Bool(l <= r), 325 | (_, _) => Value::None, 326 | }, 327 | Opcode::GreaterThanOrEqual => match (l, r) { 328 | (Value::Int(l), Value::Int(r)) => Value::Bool(l >= r), 329 | (Value::Int(l), Value::Real(r)) => Value::Bool(l as f64 >= r), 330 | (Value::Real(l), Value::Int(r)) => Value::Bool(l >= r as f64), 331 | (Value::Real(l), Value::Real(r)) => Value::Bool(l >= r), 332 | (Value::Str(l), Value::Str(r)) => Value::Bool(l >= r), 333 | (_, _) => Value::None, 334 | }, 335 | Opcode::LogicalAnd => match (l, r) { 336 | (Value::Bool(l), Value::Bool(r)) => Value::Bool(l && r), 337 | (_, _) => Value::None, 338 | }, 339 | Opcode::LogicalOr => match (l, r) { 340 | (Value::Bool(l), Value::Bool(r)) => Value::Bool(l || r), 341 | (_, _) => Value::None, 342 | }, 343 | Opcode::LogicalXor => match (l, r) { 344 | (Value::Bool(l), Value::Bool(r)) => Value::Bool((l || r) && !(l && r)), 345 | (_, _) => Value::None, 346 | }, 347 | _ => Value::None, 348 | } 349 | } 350 | } 351 | 352 | impl<'src> Function<'src> { 353 | /// Executes the Function 354 | /// 355 | /// - Creates a new Function Scope 356 | /// - Executes the Function's statements (StmtBlock) 357 | /// - Removes the Function's Scope 358 | /// - Returns the Function result Value 359 | pub fn execute(&self, scopes: &mut ScopeChain<'src>, args: &[Value<'src>]) -> Value<'src> { 360 | // Create local scope 361 | let scope = Scope::from_args( 362 | &self 363 | .args 364 | .iter() 365 | .zip(args) 366 | .collect::>(), 367 | ); 368 | 369 | // Push new function scope onto chain 370 | scopes.push(scope); 371 | 372 | // Evaluate Function StmtBlock 373 | let res = match self.stmts.exec(scopes) { 374 | ExecResult::Return(x) => x, 375 | _ => Value::None, 376 | }; 377 | 378 | // Pop function Scope from chain 379 | scopes.pop(); 380 | 381 | res 382 | } 383 | } 384 | 385 | impl<'src> Evaluatable<'src> for Expr<'src> { 386 | /// Evaluate an Expr 387 | fn eval(&self, scopes: &mut ScopeChain<'src>) -> Value<'src> { 388 | match *self { 389 | Expr::BinOp(ref l, ref opc, ref r) => opc.eval(l.eval(scopes), r.eval(scopes)), 390 | Expr::Bool(x) => Value::Bool(x), 391 | Expr::Dict(ref items) => { 392 | #[cfg(not(feature = "no_std"))] 393 | let mut map = HashMap::::new(); 394 | #[cfg(feature = "no_std")] 395 | let mut map = BTreeMap::::new(); 396 | for item in items.iter() { 397 | map.insert(item.0, item.1.eval(scopes)); 398 | } 399 | Value::Dict(map) 400 | }, 401 | Expr::FuncCall(ref func_id, ref args) => { 402 | let eval_args = args.iter().map(|x| x.eval(scopes)).collect::>>(); 403 | match scopes.resolve_func(func_id) { 404 | Some(f) => f.execute(scopes, &eval_args), 405 | None => match scopes.resolve_native_func(func_id) { 406 | Some(f) => f.execute(scopes, &eval_args), 407 | None => Value::None, 408 | }, 409 | } 410 | } 411 | Expr::Id(ref x) => match scopes.resolve_var(x) { 412 | 413 | // TODO: remove clone() requirement 414 | Some(x) => x.clone(), 415 | 416 | None => Value::None, 417 | }, 418 | Expr::Int(x) => Value::Int(x), 419 | Expr::List(ref exprs) => { 420 | Value::List( 421 | exprs 422 | .iter() 423 | .map(|x| x.eval(scopes)) 424 | .collect::>>() 425 | ) 426 | } 427 | Expr::ListElement(ref id, ref expr) => { 428 | 429 | // Match index: Value::Str for Dict index, Value::Int for List index 430 | let coll_idx = expr.eval(scopes); 431 | let var = scopes.resolve_var(id); 432 | 433 | match var { 434 | Some(ref val) => match coll_idx { 435 | 436 | // Int index: val must be a List 437 | Value::Int(idx) => match val { 438 | Value::List(ref list) => match list.get(idx as usize) { 439 | Some(x) => x.clone(), 440 | None => Value::None, 441 | }, 442 | _ => Value::None, 443 | }, 444 | 445 | // Str index: val must be a Dict 446 | Value::Str(ref s) => match val { 447 | Value::Dict(ref dict) => match dict.get(s) { 448 | Some(x) => x.clone(), 449 | None => Value::None, 450 | }, 451 | _ => Value::None, 452 | }, 453 | 454 | _ => Value::None, 455 | } 456 | None => Value::None, 457 | } 458 | } 459 | Expr::None => Value::None, 460 | Expr::Real(x) => Value::Real(x), 461 | Expr::Str(x) => Value::Str(x), 462 | Expr::UnaryOp(ref opc, ref x) => opc.eval_unary(&x.eval(scopes)), 463 | } 464 | } 465 | } 466 | 467 | impl<'src> Executable<'src> for Stmt<'src> { 468 | /// Execute a Stmt 469 | fn exec(&self, scopes: &mut ScopeChain<'src>) -> ExecResult<'src> { 470 | match *self { 471 | // Break from a loop 472 | Stmt::Break => ExecResult::Break, 473 | 474 | // Single Expr (e.g. function call) 475 | Stmt::Expr(ref exp) => { 476 | exp.eval(scopes); 477 | ExecResult::None 478 | } 479 | 480 | // Create a new Function in the Scope 481 | Stmt::FnDef(ref fn_id, ref arg_ids, ref stmts) => { 482 | scopes.insert_func( 483 | fn_id, 484 | Function { 485 | args: arg_ids.clone(), 486 | stmts: stmts.clone(), 487 | }, 488 | ); 489 | ExecResult::None 490 | } 491 | 492 | // If condition without an else 493 | Stmt::If(ref cond, ref stmts) => { 494 | if let Value::Bool(b) = cond.eval(scopes) { 495 | if b { 496 | stmts.exec(scopes) 497 | } else { 498 | ExecResult::None 499 | } 500 | } else { 501 | ExecResult::None 502 | } 503 | } 504 | 505 | // If condition with an else 506 | Stmt::IfElse(ref cond, ref stmts, ref else_stmts) => { 507 | if let Value::Bool(b) = cond.eval(scopes) { 508 | if b { 509 | stmts.exec(scopes) 510 | } else { 511 | else_stmts.exec(scopes) 512 | } 513 | } else { 514 | else_stmts.exec(scopes) 515 | } 516 | } 517 | 518 | // Evaluate "expr" and update variable table (key: "id") with result. Value of the Let 519 | // is None. 520 | Stmt::Let(ref id, ref expr) => { 521 | let eval_res = expr.eval(scopes); 522 | scopes.insert_var(id, eval_res); 523 | ExecResult::None 524 | } 525 | 526 | // Assign a Value to a list item (integer index) 527 | Stmt::ListItemAssignment(ref id, ref idx, ref val) => { 528 | let idx = idx.eval(scopes); 529 | let val = val.eval(scopes); 530 | match idx { 531 | Value::Int(x) => scopes.insert_list_item(id, x as usize, val), 532 | Value::Str(x) => scopes.insert_dict_item(id, &x, val), 533 | _ => {}, 534 | }; 535 | ExecResult::None 536 | } 537 | 538 | // Execute a loop until the result of executing a loop Stmt is ExecResult::Break 539 | Stmt::Loop(ref stmts) => loop { 540 | let res = stmts.exec(scopes); 541 | if let ExecResult::Break = res { 542 | return ExecResult::None; 543 | } 544 | }, 545 | 546 | // Return from a Function 547 | Stmt::Return(ref expr) => ExecResult::Return(expr.eval(scopes)), 548 | } 549 | } 550 | } 551 | 552 | impl<'src> Executable<'src> for StmtBlock<'src> { 553 | /// Execute StmtBlock: execute all Stmts in turn, stopping prematurely if an ExecResult::Break 554 | /// or ExecResult::Return is encountered. 555 | fn exec(&self, scopes: &mut ScopeChain<'src>) -> ExecResult<'src> { 556 | for stmt in self { 557 | let res = stmt.exec(scopes); 558 | match res { 559 | ExecResult::Return(_) => { return res; }, 560 | ExecResult::Break => { return ExecResult::Break }, 561 | _ => {}, 562 | } 563 | } 564 | ExecResult::None 565 | } 566 | } 567 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(feature = "no_std", no_std)] 2 | 3 | #[cfg(feature = "no_std")] 4 | #[macro_use] 5 | extern crate alloc; 6 | 7 | #[macro_use] 8 | extern crate nom; 9 | 10 | pub mod ast; 11 | pub mod interpreter; 12 | mod parser; 13 | pub mod runtime; 14 | 15 | use ast::{ExecResult, Executable}; 16 | use interpreter::{Scope, ScopeChain}; 17 | use parser::parse; 18 | use runtime::insert_native_functions; 19 | 20 | /// Result of parsing and executing code 21 | /// 22 | /// - `exec_result`: actual resulting value from execution 23 | /// - `scope_chain`: ScopeChain after execution 24 | pub struct InterpretResult<'src> { 25 | pub exec_result: ExecResult<'src>, 26 | pub scope_chain: ScopeChain<'src>, 27 | } 28 | 29 | /// Gets a Scope containing the runtime module's default NativeFunctions 30 | pub fn get_default_global_scope<'src>() -> Scope<'src> { 31 | let mut scope = Scope::new(); 32 | insert_native_functions(&mut scope); 33 | scope 34 | } 35 | 36 | /// Interprets given source code under a Scope 37 | /// 38 | /// # Params 39 | /// 40 | /// - `src: &str`: source code to parse and execute 41 | /// - `global_scope: Scope`: root scope under which to execute the code 42 | /// 43 | pub fn interpret<'src>(src: &'src str, global_scope: Scope<'src>) -> InterpretResult<'src> { 44 | let mut scopes = ScopeChain::from_scope(global_scope); 45 | let er = match parse(src) { 46 | Ok(stmts) => stmts.exec(&mut scopes), 47 | Err(s) => ExecResult::Error(s), 48 | }; 49 | InterpretResult { 50 | exec_result: er, 51 | scope_chain: scopes, 52 | } 53 | } 54 | 55 | #[cfg(all(test, not(feature = "no_std")))] 56 | mod tests { 57 | use std::any::Any; 58 | use std::cell::RefCell; 59 | use std::collections::HashMap; 60 | use std::rc::Rc; 61 | 62 | use super::*; 63 | 64 | use ast::{Executable, Expr, Ident, Opcode, NativeFunction, Stmt, Value}; 65 | use interpreter::{Scope, ScopeChain}; 66 | use parser::parse; 67 | 68 | struct TestPrint { 69 | calls: RefCell, 70 | } 71 | impl TestPrint { 72 | pub fn get_calls(&self) -> usize { 73 | self.calls.borrow().clone() 74 | } 75 | pub fn assert_calls(&self, num: usize) { 76 | assert_eq!(num, self.get_calls()); 77 | } 78 | } 79 | impl NativeFunction for TestPrint { 80 | fn execute<'src>(&self, _scopes: &mut ScopeChain<'src>, _args: &[Value<'src>]) -> Value<'src> { 81 | self.calls.replace(self.get_calls() + 1); 82 | Value::None 83 | } 84 | fn as_any(&self) -> &dyn Any { 85 | self 86 | } 87 | } 88 | struct TestPrintLn { 89 | calls: RefCell, 90 | } 91 | impl TestPrintLn { 92 | pub fn get_calls(&self) -> usize { 93 | self.calls.borrow().clone() 94 | } 95 | pub fn assert_calls(&self, num: usize) { 96 | assert_eq!(num, self.get_calls()); 97 | } 98 | } 99 | impl NativeFunction for TestPrintLn { 100 | fn execute<'src>(&self, _scopes: &mut ScopeChain<'src>, _args: &[Value<'src>]) -> Value<'src> { 101 | self.calls.replace(self.get_calls() + 1); 102 | Value::None 103 | } 104 | fn as_any(&self) -> &dyn Any { 105 | self 106 | } 107 | } 108 | 109 | fn insert_test_functions(scope: &mut Scope) { 110 | let test_print = Rc::new(TestPrint { 111 | calls: RefCell::new(0), 112 | }); 113 | let test_println = Rc::new(TestPrintLn { 114 | calls: RefCell::new(0), 115 | }); 116 | scope.native_funcs.insert("print", test_print); 117 | scope.native_funcs.insert("println", test_println); 118 | } 119 | 120 | #[test] 121 | fn let_stmt() { 122 | 123 | // Test parsing 124 | assert_eq!( 125 | Ok(vec![ 126 | Stmt::Let( 127 | "a", 128 | Expr::BinOp(Box::new(Expr::Int(1)), Opcode::Add, Box::new(Expr::Int(2))) 129 | ) 130 | ]), 131 | parse("let a = 1 + 2;") 132 | ); 133 | 134 | let mut scopes = ScopeChain::from_scope(Scope::new()); 135 | 136 | // Test evaluation of expression using an undefined variable 137 | assert_eq!( 138 | ExecResult::Return(Value::None), 139 | parse("return a + 1").unwrap().exec(&mut scopes) 140 | ); 141 | 142 | // Test evaluation of a Let statement 143 | assert_eq!(None, scopes.resolve_var("a")); 144 | assert_eq!( 145 | ExecResult::Return(Value::Int(3)), 146 | parse("let a = 1 + 2; return a;").unwrap().exec(&mut scopes) 147 | ); 148 | assert_eq!(Some(&Value::Int(3)), scopes.resolve_var("a")); 149 | 150 | // Test evaluation of expressions using variable "a" (now defined in "scope") 151 | assert_eq!( 152 | ExecResult::Return(Value::Int(4)), 153 | parse("let b = a + 1; return b;").unwrap().exec(&mut scopes) 154 | ); 155 | assert_eq!( 156 | ExecResult::Return(Value::Int(9)), 157 | parse("let b = a * a; return b;").unwrap().exec(&mut scopes) 158 | ); 159 | assert_eq!( 160 | ExecResult::Return(Value::Real(1.5f64)), 161 | parse("let b = a / 2; return b;").unwrap().exec(&mut scopes) 162 | ); 163 | } 164 | 165 | #[test] 166 | fn literals() { 167 | 168 | // Bools 169 | assert_eq!( 170 | ExecResult::Return(Value::Bool(true)), 171 | interpret("return true;", Scope::new()).exec_result 172 | ); 173 | assert_eq!( 174 | ExecResult::Return(Value::Bool(false)), 175 | interpret("return false;", Scope::new()).exec_result 176 | ); 177 | 178 | // Ints 179 | assert_eq!( 180 | ExecResult::Return(Value::Int(42)), 181 | interpret("return 42;", Scope::new()).exec_result 182 | ); 183 | assert_eq!( 184 | ExecResult::Return(Value::Int(-42)), 185 | interpret("return -42;", Scope::new()).exec_result 186 | ); 187 | 188 | // Reals 189 | assert_eq!( 190 | ExecResult::Return(Value::Real(1.618f64)), 191 | interpret("return 1.618;", Scope::new()).exec_result 192 | ); 193 | assert_eq!( 194 | ExecResult::Return(Value::Real(-1.618f64)), 195 | interpret("return -1.618;", Scope::new()).exec_result 196 | ); 197 | assert_eq!( 198 | ExecResult::Return(Value::Real(0.618f64)), 199 | interpret("return .618;", Scope::new()).exec_result 200 | ); 201 | assert_eq!( 202 | ExecResult::Return(Value::Real(-0.618f64)), 203 | interpret("return -.618;", Scope::new()).exec_result 204 | ); 205 | 206 | // Strings 207 | assert_eq!( 208 | ExecResult::Return(Value::Str("Hello")), 209 | interpret(r#"return "Hello";"#, Scope::new()).exec_result 210 | ); 211 | assert_eq!( 212 | ExecResult::Return(Value::Str("Hello world!")), 213 | interpret(r#"return "Hello world!";"#, Scope::new()).exec_result 214 | ); 215 | assert_eq!( 216 | ExecResult::Return(Value::Str("Hello'world!")), 217 | interpret(r#"return "Hello'world!";"#, Scope::new()).exec_result 218 | ); 219 | // TODO: escaped " in Strings 220 | //assert_eq!("Str(\"Hello\"world!\")", format!("{:?}", ExprParser::new().parse(r#""Hello\"world!""#).unwrap())); 221 | 222 | // Ids 223 | assert_eq!( 224 | Ok(vec![Stmt::Expr(Expr::Id("a"))]), 225 | parse("a") 226 | ); 227 | assert_eq!( 228 | Ok(vec![Stmt::Expr(Expr::Id("_a"))]), 229 | parse("_a") 230 | ); 231 | assert_eq!( 232 | Ok(vec![Stmt::Expr(Expr::Id("a123"))]), 233 | parse("a123") 234 | ); 235 | assert_eq!( 236 | Ok(vec![Stmt::Expr(Expr::Id("a123_45"))]), 237 | parse("a123_45") 238 | ); 239 | } 240 | 241 | #[test] 242 | fn operator_precedence() { 243 | // Test language expression precedence 244 | // 1 + (2 * 3 / 4) + 42 = 1 + 1.5 + 42 = Real(44.5) 245 | let scopes = interpret("fn test(b) { return b; }; let a = 1 + 2 * 3 / 4 + test(42);", Scope::new()).scope_chain; 246 | assert_eq!(Some(&Value::Real(44.5)), scopes.resolve_var("a")); 247 | } 248 | 249 | #[test] 250 | fn scope_inheritance() { 251 | let scopes = interpret("let a = 1; fn test(z) { return a + z; }; let b = test(2); let c = a;", Scope::new()).scope_chain; 252 | assert_eq!(Some(&Value::Int(1)), scopes.resolve_var("a")); 253 | assert_eq!(Some(&Value::Int(3)), scopes.resolve_var("b")); 254 | assert_eq!(Some(&Value::Int(1)), scopes.resolve_var("c")); 255 | } 256 | 257 | #[test] 258 | fn lib_interpret() { 259 | let mut scope = Scope::new(); 260 | insert_test_functions(&mut scope); 261 | let res = interpret("return 42", scope); 262 | match res.exec_result { 263 | ExecResult::None => assert!(false, "interpret() should not have returned None"), 264 | ExecResult::Break => assert!(false, "interpret() should not have returned Break"), 265 | ExecResult::Return(x) => assert_eq!(Value::Int(42), x), 266 | ExecResult::Error(e) => assert!(false, e), 267 | }; 268 | res.scope_chain 269 | .resolve_native_func("print") 270 | .unwrap() 271 | .as_any() 272 | .downcast_ref::() 273 | .unwrap() 274 | .assert_calls(0); 275 | res.scope_chain 276 | .resolve_native_func("println") 277 | .unwrap() 278 | .as_any() 279 | .downcast_ref::() 280 | .unwrap() 281 | .assert_calls(0); 282 | 283 | // Complex function (Fibonacci) 284 | let src = r#" 285 | fn fib(n) { 286 | if n <= 0 { return 0; }; 287 | let count = n; 288 | let prev = 0; 289 | let res = 1; 290 | loop { 291 | let temp = res; 292 | let res = res + prev; 293 | let prev = temp; 294 | print(res); 295 | print(", "); 296 | let count = count - 1; 297 | if count <= 1 { 298 | break; 299 | }; 300 | }; 301 | println(""); 302 | return res; 303 | }; 304 | 305 | return fib(8); 306 | "#; 307 | let mut scope = Scope::new(); 308 | insert_test_functions(&mut scope); 309 | let res = interpret(src, scope); 310 | match res.exec_result { 311 | ExecResult::None => assert!(false, "interpret() should not have returned None"), 312 | ExecResult::Break => assert!(false, "interpret() should not have returned Break"), 313 | ExecResult::Return(x) => assert_eq!(Value::Int(21), x), 314 | ExecResult::Error(e) => assert!(false, e), 315 | }; 316 | 317 | // print should have been invoked twice per loop (=14) 318 | // println should have been invoked once (after loop) 319 | res.scope_chain 320 | .resolve_native_func("print") 321 | .unwrap() 322 | .as_any() 323 | .downcast_ref::() 324 | .unwrap() 325 | .assert_calls(14); 326 | res.scope_chain 327 | .resolve_native_func("println") 328 | .unwrap() 329 | .as_any() 330 | .downcast_ref::() 331 | .unwrap() 332 | .assert_calls(1); 333 | 334 | // Complex function (recursive factorial) 335 | let src = r#" 336 | fn fact(n) { 337 | if n <= 0 { 338 | return 0; 339 | }; 340 | if n == 1 { 341 | return 1; 342 | } else { 343 | return n * fact(n - 1); 344 | }; 345 | }; 346 | 347 | return fact(4); 348 | "#; 349 | let mut scope = Scope::new(); 350 | insert_test_functions(&mut scope); 351 | let res = interpret(src, scope); 352 | match res.exec_result { 353 | ExecResult::None => assert!(false, "interpret() should not have returned None"), 354 | ExecResult::Break => assert!(false, "interpret() should not have returned Break"), 355 | ExecResult::Return(x) => assert_eq!(Value::Int(24), x), 356 | ExecResult::Error(e) => assert!(false, e), 357 | }; 358 | res.scope_chain 359 | .resolve_native_func("print") 360 | .unwrap() 361 | .as_any() 362 | .downcast_ref::() 363 | .unwrap() 364 | .assert_calls(0); 365 | res.scope_chain 366 | .resolve_native_func("println") 367 | .unwrap() 368 | .as_any() 369 | .downcast_ref::() 370 | .unwrap() 371 | .assert_calls(0); 372 | } 373 | 374 | #[test] 375 | fn bin_ops() { 376 | 377 | // Test evaluation of relational expressions 378 | assert_eq!(ExecResult::Return(Value::Bool(true)), interpret("return 1 == 1;", Scope::new()).exec_result); 379 | assert_eq!(ExecResult::Return(Value::Bool(false)), interpret("return 1 != 1;", Scope::new()).exec_result); 380 | assert_eq!(ExecResult::Return(Value::Bool(false)), interpret("return 1 == 2;", Scope::new()).exec_result); 381 | assert_eq!(ExecResult::Return(Value::Bool(false)), interpret("return 1 > 2;", Scope::new()).exec_result); 382 | assert_eq!(ExecResult::Return(Value::Bool(true)), interpret("return 2 > 1;", Scope::new()).exec_result); 383 | assert_eq!(ExecResult::Return(Value::Bool(true)), interpret("return 1 < 2;", Scope::new()).exec_result); 384 | assert_eq!(ExecResult::Return(Value::Bool(false)), interpret("return 2 < 1;", Scope::new()).exec_result); 385 | assert_eq!(ExecResult::Return(Value::Bool(true)), interpret("return 2 >= 2;", Scope::new()).exec_result); 386 | assert_eq!(ExecResult::Return(Value::Bool(false)), interpret("return 2 >= 3;", Scope::new()).exec_result); 387 | assert_eq!(ExecResult::Return(Value::Bool(true)), interpret("return 2 <= 2;", Scope::new()).exec_result); 388 | assert_eq!(ExecResult::Return(Value::Bool(true)), interpret("return 2 <= 3;", Scope::new()).exec_result); 389 | assert_eq!(ExecResult::Return(Value::Bool(true)), interpret(r#"return "a" == "a";"#, Scope::new()).exec_result); 390 | assert_eq!(ExecResult::Return(Value::Bool(false)), interpret(r#"return "a" == "b";"#, Scope::new()).exec_result); 391 | assert_eq!(ExecResult::Return(Value::Bool(true)), interpret("return (1 + 3) > 3", Scope::new()).exec_result); 392 | 393 | // Test evaluation of arithmetic expressions 394 | 395 | // + 396 | assert_eq!(ExecResult::Return(Value::Int(3)), interpret("return 1 + 2;", Scope::new()).exec_result); 397 | assert_eq!(ExecResult::Return(Value::Real(3.3f64)), interpret("return 1 + 2.3;", Scope::new()).exec_result); 398 | assert_eq!(ExecResult::Return(Value::Real(3.2f64)), interpret("return 1.2 + 2;", Scope::new()).exec_result); 399 | assert_eq!(ExecResult::Return(Value::Real(3.5f64)), interpret("return 1.2 + 2.3;", Scope::new()).exec_result); 400 | 401 | // - 402 | assert_eq!(ExecResult::Return(Value::Int(-1)), interpret("return 1 - 2;", Scope::new()).exec_result); 403 | assert_eq!(ExecResult::Return(Value::Real(-1.5f64)), interpret("return 1 - 2.5;", Scope::new()).exec_result); 404 | assert_eq!(ExecResult::Return(Value::Real(-0.8f64)), interpret("return 1.2 - 2;", Scope::new()).exec_result); 405 | assert_eq!(ExecResult::Return(Value::Real(-1.3f64)), interpret("return 1.2 - 2.5;", Scope::new()).exec_result); 406 | 407 | // * 408 | assert_eq!(ExecResult::Return(Value::Int(6)), interpret("return 2 * 3;", Scope::new()).exec_result); 409 | assert_eq!(ExecResult::Return(Value::Real(6.8f64)), interpret("return 2 * 3.4;", Scope::new()).exec_result); 410 | assert_eq!(ExecResult::Return(Value::Real(7.5f64)), interpret("return 2.5 * 3;", Scope::new()).exec_result); 411 | assert_eq!(ExecResult::Return(Value::Real(3.75f64)), interpret("return 2.5 * 1.5;", Scope::new()).exec_result); 412 | 413 | // / 414 | assert_eq!(ExecResult::Return(Value::Real(3f64)), interpret("return 6 / 2;", Scope::new()).exec_result); 415 | assert_eq!(ExecResult::Return(Value::Real(3.35f64)), interpret("return 6.7 / 2;", Scope::new()).exec_result); 416 | assert_eq!(ExecResult::Return(Value::Real(2.4f64)), interpret("return 6 / 2.5;", Scope::new()).exec_result); 417 | assert_eq!(ExecResult::Return(Value::Real(2.68f64)), interpret("return 6.7 / 2.5;", Scope::new()).exec_result); 418 | 419 | // % 420 | assert_eq!(ExecResult::Return(Value::Int(4)), interpret("return 16 % 6;", Scope::new()).exec_result); 421 | assert_eq!(ExecResult::Return(Value::None), interpret("return 16 % 12.1;", Scope::new()).exec_result); 422 | assert_eq!(ExecResult::Return(Value::None), interpret("return 16.1 % 12;", Scope::new()).exec_result); 423 | assert_eq!(ExecResult::Return(Value::None), interpret("return 16.1 % 12.1;", Scope::new()).exec_result); 424 | } 425 | 426 | #[test] 427 | fn logical_truth_tables() { 428 | assert_eq!(ExecResult::Return(Value::Bool(true)), interpret("return true && true;", Scope::new()).exec_result); 429 | assert_eq!(ExecResult::Return(Value::Bool(false)), interpret("return true && false;", Scope::new()).exec_result); 430 | assert_eq!(ExecResult::Return(Value::Bool(false)), interpret("return false && true;", Scope::new()).exec_result); 431 | assert_eq!(ExecResult::Return(Value::Bool(false)), interpret("return false && false;", Scope::new()).exec_result); 432 | 433 | assert_eq!(ExecResult::Return(Value::Bool(true)), interpret("return true || true;", Scope::new()).exec_result); 434 | assert_eq!(ExecResult::Return(Value::Bool(true)), interpret("return true || false;", Scope::new()).exec_result); 435 | assert_eq!(ExecResult::Return(Value::Bool(true)), interpret("return false || true;", Scope::new()).exec_result); 436 | assert_eq!(ExecResult::Return(Value::Bool(false)), interpret("return false || false;", Scope::new()).exec_result); 437 | 438 | assert_eq!(ExecResult::Return(Value::Bool(false)), interpret("return true ^ true;", Scope::new()).exec_result); 439 | assert_eq!(ExecResult::Return(Value::Bool(true)), interpret("return true ^ false;", Scope::new()).exec_result); 440 | assert_eq!(ExecResult::Return(Value::Bool(true)), interpret("return false ^ true;", Scope::new()).exec_result); 441 | assert_eq!(ExecResult::Return(Value::Bool(false)), interpret("return false ^ false;", Scope::new()).exec_result); 442 | } 443 | 444 | #[test] 445 | fn stmt_block() { 446 | // Test evaluation of a full StmtBlock with a new Scope 447 | let scopes = interpret("let abc = 1 + 2; let bcd = 3 + 4; let cde = abc * bcd;", Scope::new()).scope_chain; 448 | assert_eq!(Some(&Value::Int(3)), scopes.resolve_var("abc")); 449 | assert_eq!(Some(&Value::Int(7)), scopes.resolve_var("bcd")); 450 | assert_eq!(Some(&Value::Int(21)), scopes.resolve_var("cde")); 451 | } 452 | 453 | #[test] 454 | fn functions() { 455 | // Test function definitions and calls 456 | let scopes = interpret( 457 | "fn add(a, b) { let c = a + b; return c; let c = 123; }; let res = add(1, 2 + 3);", 458 | Scope::new() 459 | ).scope_chain; 460 | assert_eq!(Some(&Value::Int(6)), scopes.resolve_var("res")); 461 | 462 | // Functions without arguments 463 | let scopes = interpret( 464 | "fn test() { return 42; }; let res = test();", 465 | Scope::new() 466 | ).scope_chain; 467 | assert_eq!(Some(&Value::Int(42)), scopes.resolve_var("res")); 468 | } 469 | 470 | #[test] 471 | fn conditionals() { 472 | // Test conditional If/IfElse statements 473 | let mut scopes = interpret( 474 | "let a = 1; if 1 == 1 { let a = 2; } else { let a = 3; }; if 1 != 2 { let a = 4; }", 475 | Scope::new() 476 | ).scope_chain; 477 | assert_eq!(Some(&Value::Int(4)), scopes.resolve_var("a")); 478 | let mut scopes = interpret("if (1 == 2) || (1 == 1) { let a = 5; };", scopes.pop().unwrap()).scope_chain; 479 | assert_eq!(Some(&Value::Int(5)), scopes.resolve_var("a")); 480 | let mut scopes = interpret("if (1 == 1) && (2 == 2) { let a = 6; };", scopes.pop().unwrap()).scope_chain; 481 | assert_eq!(Some(&Value::Int(6)), scopes.resolve_var("a")); 482 | let mut scopes = interpret("if (1 == 1) ^ (2 == 2) { let a = 7; };", scopes.pop().unwrap()).scope_chain; 483 | assert_eq!(Some(&Value::Int(6)), scopes.resolve_var("a")); 484 | let scopes = interpret("if 1 == 1 ^ 2 == 2 { let a = 8; };", scopes.pop().unwrap()).scope_chain; 485 | assert_eq!(Some(&Value::Int(6)), scopes.resolve_var("a")); 486 | } 487 | 488 | #[test] 489 | fn loops() { 490 | // Test loop 491 | let scopes = interpret( 492 | "let a = 0; let b = 1; loop { let a = a + 1; let b = b * 2; if a > 5 { break; }; };", 493 | Scope::new() 494 | ).scope_chain; 495 | assert_eq!(Some(&Value::Int(6)), scopes.resolve_var("a")); 496 | assert_eq!(Some(&Value::Int(64)), scopes.resolve_var("b")); 497 | } 498 | 499 | #[test] 500 | fn unary_ops() { 501 | // Test unary operators 502 | let scopes = interpret("let a = !(1 == 1); let b = !(2 < 1);", Scope::new()).scope_chain; 503 | assert_eq!(Some(&Value::Bool(false)), scopes.resolve_var("a")); 504 | assert_eq!(Some(&Value::Bool(true)), scopes.resolve_var("b")); 505 | 506 | // Test unary operators and Boolean literals 507 | let scopes = interpret("let a = true; let b = false; let c = !a; let d = !a && !b;", Scope::new()).scope_chain; 508 | assert_eq!(Some(&Value::Bool(true)), scopes.resolve_var("a")); 509 | assert_eq!(Some(&Value::Bool(false)), scopes.resolve_var("b")); 510 | assert_eq!(Some(&Value::Bool(false)), scopes.resolve_var("c")); 511 | assert_eq!(Some(&Value::Bool(false)), scopes.resolve_var("d")); 512 | } 513 | 514 | #[test] 515 | fn native_functions() { 516 | struct TestFunc {}; 517 | impl NativeFunction for TestFunc { 518 | fn execute<'src>(&self, _scopes: &mut ScopeChain<'src>, args: &[Value<'src>]) -> Value<'src> { 519 | match args[0] { 520 | Value::Int(x) => Value::Int(x + 40), 521 | _ => Value::None, 522 | } 523 | } 524 | fn as_any(&self) -> &dyn Any { 525 | self 526 | } 527 | }; 528 | let test_func = TestFunc {}; 529 | let mut scope = Scope::new(); 530 | scope 531 | .native_funcs 532 | .insert("test_func", Rc::new(test_func)); 533 | 534 | let scopes = interpret("let a = test_func(1) + 1; let b = test_func(12) * 3;", scope).scope_chain; 535 | assert_eq!(Some(&Value::Int(42)), scopes.resolve_var("a")); 536 | assert_eq!(Some(&Value::Int(156)), scopes.resolve_var("b")); 537 | } 538 | 539 | #[test] 540 | fn lists() { 541 | let scopes = interpret("let a = [1, \"test\", 2]; let b = a[1];", Scope::new()).scope_chain; 542 | assert_eq!( 543 | Some(&Value::List(vec![ 544 | Value::Int(1), 545 | Value::Str("test"), 546 | Value::Int(2) 547 | ])), 548 | scopes.resolve_var("a") 549 | ); 550 | assert_eq!( 551 | Some(&Value::Str("test")), 552 | scopes.resolve_var("b") 553 | ); 554 | 555 | let scopes = interpret( 556 | "let a = [1, \"test\", 2]; a[0] = 40 + 2; a[4] = \"test2\"; let b = a[0]; let c = a[3]; let d = a[4];", 557 | Scope::new() 558 | ).scope_chain; 559 | assert_eq!( 560 | Some(&Value::List(vec![ 561 | Value::Int(42), 562 | Value::Str("test"), 563 | Value::Int(2), 564 | Value::None, 565 | Value::Str("test2"), 566 | ])), 567 | scopes.resolve_var("a") 568 | ); 569 | assert_eq!(Some(&Value::Int(42)), scopes.resolve_var("b")); 570 | assert_eq!(Some(&Value::None), scopes.resolve_var("c")); 571 | assert_eq!( 572 | Some(&Value::Str("test2")), 573 | scopes.resolve_var("d") 574 | ); 575 | } 576 | 577 | #[test] 578 | fn dicts() { 579 | let scopes = interpret( 580 | "let a = {\"d1\": 1 + 2, \"d2\": \"second\"}; let b = a[\"d1\"]; a[\"d2\"] = \"third\"; a[\"d3\"] = \"fourth\";", 581 | Scope::new() 582 | ).scope_chain; 583 | let mut expected = HashMap::::new(); 584 | expected.insert("d1", Value::Int(3)); 585 | expected.insert("d2", Value::Str("third")); 586 | expected.insert("d3", Value::Str("fourth")); 587 | assert_eq!(&Value::Dict(expected), scopes.resolve_var("a").unwrap()); 588 | assert_eq!(Some(&Value::Int(3)), scopes.resolve_var("b")); 589 | } 590 | } 591 | -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(feature = "no_std"))] 2 | use std::ops::Neg; 3 | #[cfg(feature = "no_std")] 4 | use core::ops::Neg; 5 | 6 | #[cfg(feature = "no_std")] 7 | use alloc::boxed::Box; 8 | #[cfg(feature = "no_std")] 9 | use alloc::vec::Vec; 10 | 11 | use nom::{alpha, digit, digit0}; 12 | use nom::types::CompleteStr; 13 | 14 | use ast::{Expr, Ident, Opcode, Stmt, StmtBlock}; 15 | 16 | /* 17 | * Takes an optional sign (&str, "+" or "-") and a number and returns the correct signed number 18 | * according to the sign. 19 | * 20 | * Generic over any type implementing std::Ops::Neg (allows the unary "-" operator to be applied). 21 | * The unary "-" operator returns a Neg::Output, so we specify that T must be bound to the Neg 22 | * trait where its Output type is also T. 23 | */ 24 | fn signed_number>(sign: Option, num: T) -> T { 25 | match sign { 26 | None => num, 27 | Some(c) => match c.0 { 28 | "-" => -num, 29 | _ => num, 30 | }, 31 | } 32 | } 33 | 34 | // Parser for a number's sign: either "+" or "-" 35 | named!(number_sign, alt!(tag!("+") | tag!("-"))); 36 | 37 | /* 38 | * Parser for a single real number: optional number_sign followed by a real number (optional 39 | * integer component, period, decimal digits). 40 | */ 41 | named!(real, 42 | do_parse!( 43 | sign: opt!(number_sign) >> 44 | num: map_res!( 45 | 46 | // recognize! returns the consumed output if the inner parser was successful. So, the 47 | // entire input parsed by tuple! (e.g. "123.456") should be returned. 48 | recognize!( 49 | 50 | // Build a resulting tuple such as ("123", ".", "456") for "123.456". 51 | tuple!(digit0, tag!("."), digit) 52 | ), 53 | 54 | // The result will be a string like "123.456" as recognize! returned all matching 55 | // chars, so parse this as an f64. 56 | |s: CompleteStr| s.0.parse::() 57 | ) >> 58 | ( signed_number(sign, num) ) 59 | ) 60 | ); 61 | 62 | // Parser for a single integer number: optional number_sign followed by an integer number 63 | named!(int, 64 | do_parse!( 65 | sign: opt!(number_sign) >> 66 | num: map_res!( 67 | digit, 68 | |s: CompleteStr| s.0.parse::() 69 | ) >> 70 | ( signed_number(sign, num) ) 71 | ) 72 | ); 73 | 74 | /// Returns true if the char is valid for an identifier (not in first position) 75 | fn is_ident_char(c: char) -> bool { 76 | match c { 77 | 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => true, 78 | _ => false 79 | } 80 | } 81 | 82 | // Parser for a single language identifier (e.g. "name1") 83 | named!(ident, 84 | map!( 85 | recognize!(pair!(alt!(alpha | tag!("_")), take_while!(is_ident_char))), 86 | |s: CompleteStr| s.0 87 | ) 88 | ); 89 | 90 | 91 | // --- Expressions --- 92 | 93 | // Parser for logical (&&, ||, ^) Opcodes 94 | named!(logical_opcode, 95 | alt!( 96 | map!(tag!("&&"), |_| Opcode::LogicalAnd) | 97 | map!(tag!("||"), |_| Opcode::LogicalOr) | 98 | map!(tag!("^"), |_| Opcode::LogicalXor) 99 | ) 100 | ); 101 | 102 | // Parser for relational Opcodes (e.g. <, >=, !=) 103 | named!(relational_opcode, 104 | alt!( 105 | map!(tag!("<="), |_| Opcode::LessThanOrEqual) | 106 | map!(tag!(">="), |_| Opcode::GreaterThanOrEqual) | 107 | map!(tag!("=="), |_| Opcode::Equal) | 108 | map!(tag!("!="), |_| Opcode::NotEqual) | 109 | map!(tag!("<"), |_| Opcode::LessThan) | 110 | map!(tag!(">"), |_| Opcode::GreaterThan) 111 | ) 112 | ); 113 | 114 | // Parser for "*", "/", "%" Opcodes 115 | named!(product_opcode, 116 | alt!( 117 | map!(tag!("*"), |_| Opcode::Mul) | 118 | map!(tag!("/"), |_| Opcode::Div) | 119 | map!(tag!("%"), |_| Opcode::Mod) 120 | ) 121 | ); 122 | 123 | // Parser for "+", "-" Opcodes 124 | named!(sum_opcode, 125 | alt!( 126 | map!(tag!("+"), |_| Opcode::Add) | 127 | map!(tag!("-"), |_| Opcode::Sub) 128 | ) 129 | ); 130 | 131 | // Parser for an expression term: parses either an "expr" delimited by 132 | // parentheses (recursion) or another language value type 133 | named!(term, 134 | alt!( 135 | ws!(delimited!(tag!("("), expr, tag!(")"))) | 136 | ws!(value_expr) 137 | ) 138 | ); 139 | 140 | // Parser for logical expressions (e.g. true && false) 141 | named!(logical_expr, 142 | alt!( 143 | do_parse!( 144 | lhs: term >> 145 | op: ws!(logical_opcode) >> 146 | rhs: logical_expr >> 147 | ( Expr::BinOp(Box::new(lhs), op, Box::new(rhs)) ) 148 | ) | 149 | term 150 | ) 151 | ); 152 | 153 | // Parser for relational expressions (e.g. 1 < 2) 154 | named!(relational_expr, 155 | alt!( 156 | do_parse!( 157 | lhs: logical_expr >> 158 | op: ws!(relational_opcode) >> 159 | rhs: relational_expr >> 160 | ( Expr::BinOp(Box::new(lhs), op, Box::new(rhs)) ) 161 | ) | 162 | logical_expr 163 | ) 164 | ); 165 | 166 | // Parser for product expressions (e.g. 2 * 3) 167 | named!(product_expr, 168 | alt!( 169 | do_parse!( 170 | lhs: relational_expr >> 171 | op: ws!(product_opcode) >> 172 | rhs: product_expr >> 173 | ( Expr::BinOp(Box::new(lhs), op, Box::new(rhs)) ) 174 | ) | 175 | relational_expr 176 | ) 177 | ); 178 | 179 | // Parser for sum expressions (e.g. 1 + 2) 180 | named!(sum_expr, 181 | alt!( 182 | do_parse!( 183 | lhs: product_expr >> 184 | op: ws!(sum_opcode) >> 185 | rhs: sum_expr >> 186 | ( Expr::BinOp(Box::new(lhs), op, Box::new(rhs)) ) 187 | ) | 188 | product_expr 189 | ) 190 | ); 191 | 192 | // Parser for any language expression 193 | named!(expr, 194 | call!(sum_expr) 195 | ); 196 | 197 | // Parser for Boolean literals 198 | named!(bool_literal, 199 | alt!( 200 | tag_no_case!("true") => { |_| true } | 201 | tag_no_case!("false") => { |_| false } 202 | ) 203 | ); 204 | 205 | // Parser for Dict literals 206 | named!(dict_literal, 207 | map!( 208 | delimited!( 209 | ws!(tag!("{")), 210 | separated_list!(ws!(tag!(",")), map!(key_val_pair, |(k, v)| (k, Box::new(v)))), 211 | ws!(tag!("}")) 212 | ), 213 | Expr::Dict 214 | ) 215 | ); 216 | 217 | // Parser for float literals (calls real) 218 | named!(float_literal, call!(real)); 219 | 220 | // Parser for function call expressions 221 | named!(func_call, 222 | do_parse!( 223 | id: ident >> 224 | args: delimited!( 225 | ws!(tag!("(")), 226 | separated_list!(ws!(tag!(",")), map!(expr, Box::new)), 227 | ws!(tag!(")")) 228 | ) >> 229 | ( Expr::FuncCall(id, args) ) 230 | ) 231 | ); 232 | 233 | // Parser for int literals 234 | named!(int_literal, 235 | call!(int) 236 | ); 237 | 238 | // Parser for a key (string) / value (expr) pair 239 | named!(key_val_pair, 240 | do_parse!( 241 | key: str_literal >> 242 | ws!(tag!(":")) >> 243 | val: expr >> 244 | (key, val) 245 | ) 246 | ); 247 | 248 | // Parser for list elements: list identifier and index 249 | named!(list_element, 250 | do_parse!( 251 | id: ident >> 252 | idx: delimited!( 253 | ws!(tag!("[")), 254 | map!(expr, Box::new), 255 | ws!(tag!("]")) 256 | ) >> 257 | ( Expr::ListElement(id, idx) ) 258 | ) 259 | ); 260 | 261 | // Parser for a List literal 262 | named!(list_literal, 263 | map!( 264 | delimited!( 265 | ws!(tag!("[")), 266 | separated_list!(ws!(tag!(",")), map!(expr, Box::new)), 267 | ws!(tag!("]")) 268 | ), 269 | Expr::List 270 | ) 271 | ); 272 | 273 | // Parser for string literals (characters enclosed by '"' characters) 274 | named!(str_literal, 275 | alt!( 276 | map!( 277 | delimited!(char!('"'), is_not!("\""), char!('"')), 278 | |x: CompleteStr| x.0 279 | ) | 280 | map!(tag!(r#""""#), |_| "") 281 | ) 282 | ); 283 | 284 | // Parser for a unary Opcode (e.g. "!") 285 | named!(unary_opcode, 286 | alt!( 287 | tag!("!") => { |_| Opcode::Not } 288 | ) 289 | ); 290 | 291 | // Parser for any unary operation (e.g. "!true") 292 | named!(unary_op, 293 | do_parse!( 294 | op: unary_opcode >> 295 | t: expr >> 296 | ( Expr::UnaryOp(op, Box::new(t)) ) 297 | ) 298 | ); 299 | 300 | // Parser for any language expression that results in a single value 301 | named!(value_expr, 302 | alt!( 303 | map!(float_literal, Expr::Real) | 304 | map!(int_literal, Expr::Int) | 305 | map!(bool_literal, Expr::Bool) | 306 | map!(str_literal, Expr::Str) | 307 | map!(tag!("null"), |_| Expr::None) | 308 | func_call | 309 | dict_literal | 310 | list_literal | 311 | list_element | 312 | unary_op | 313 | map!(ident, Expr::Id) 314 | ) 315 | ); 316 | 317 | 318 | // --- Statements --- 319 | 320 | named!(break_statement, 321 | map!(ws!(tag!("break")), |_| Stmt::Break) 322 | ); 323 | 324 | named!(expr_statement, 325 | map!(expr, Stmt::Expr) 326 | ); 327 | 328 | named!(fndef_statement, 329 | do_parse!( 330 | ws!(tag!("fn")) >> 331 | id: ident >> 332 | args: delimited!( 333 | ws!(tag!("(")), 334 | separated_list!(ws!(tag!(",")), ident), 335 | ws!(tag!(")")) 336 | ) >> 337 | stmts: statement_block >> 338 | ( Stmt::FnDef(id, args, stmts) ) 339 | ) 340 | ); 341 | 342 | named!(if_statement, 343 | do_parse!( 344 | ws!(tag!("if")) >> 345 | cond: expr >> 346 | stmts: statement_block >> 347 | ( Stmt::If(cond, stmts) ) 348 | ) 349 | ); 350 | 351 | named!(if_else_statement, 352 | do_parse!( 353 | ws!(tag!("if")) >> 354 | cond: expr >> 355 | stmts_t: statement_block >> 356 | ws!(tag!("else")) >> 357 | stmts_f: statement_block >> 358 | ( Stmt::IfElse(cond, stmts_t, stmts_f) ) 359 | ) 360 | ); 361 | 362 | named!(let_statement, 363 | do_parse!( 364 | ws!(tag!("let")) >> 365 | id: ident >> 366 | ws!(tag!("=")) >> 367 | val: ws!(expr) >> 368 | ( Stmt::Let(id, val) ) 369 | ) 370 | ); 371 | 372 | named!(list_assignment_statement, 373 | do_parse!( 374 | id: ident >> 375 | idx: delimited!(ws!(tag!("[")), expr, ws!(tag!("]"))) >> 376 | ws!(tag!("=")) >> 377 | val: ws!(expr) >> 378 | ( Stmt::ListItemAssignment(id, idx, val) ) 379 | ) 380 | ); 381 | 382 | named!(loop_statement, 383 | do_parse!( 384 | ws!(tag!("loop")) >> 385 | stmts: statement_block >> 386 | ( Stmt::Loop(stmts) ) 387 | ) 388 | ); 389 | 390 | named!(return_statement, 391 | do_parse!( 392 | ws!(tag!("return")) >> 393 | val: ws!(expr) >> 394 | ( Stmt::Return(val) ) 395 | ) 396 | ); 397 | 398 | // Parser for a single supported statement of any type 399 | named!(statement, 400 | alt!( 401 | break_statement | 402 | fndef_statement | 403 | if_else_statement | 404 | if_statement | 405 | let_statement | 406 | list_assignment_statement | 407 | loop_statement | 408 | return_statement | 409 | expr_statement 410 | ) 411 | ); 412 | 413 | // Parser for a list of "statement" separated by ";" with an optional trailing ";" 414 | named!(statements>, 415 | do_parse!( 416 | list: separated_list!(ws!(tag!(";")), statement) >> 417 | opt!(tag!(";")) >> 418 | ( list ) 419 | ) 420 | ); 421 | 422 | // Parser for "statements" enclosed within braces 423 | named!(statement_block, 424 | delimited!(ws!(tag!("{")), statements, ws!(tag!("}"))) 425 | ); 426 | 427 | 428 | // Axiom rule: parses an entire program 429 | named!(program_parser, 430 | call!(statements) 431 | ); 432 | 433 | /** 434 | * Main parser function: takes source code and returns a Result containing either the AST or a 435 | * string error. 436 | */ 437 | pub fn parse<'s>(source: &'s str) -> Result { 438 | // TODO: obtain error from Nom 439 | match program_parser(CompleteStr(source)) { 440 | Ok((_, stmts)) => Ok(stmts), 441 | Err(_) => Err("Unable to parse source"), 442 | } 443 | } 444 | 445 | #[cfg(test)] 446 | mod tests { 447 | use super::*; 448 | 449 | #[test] 450 | fn number_sign_test_valid() { 451 | assert_eq!(Ok((CompleteStr(""), CompleteStr("+"))), number_sign(CompleteStr("+"))); 452 | assert_eq!(Ok((CompleteStr(""), CompleteStr("-"))), number_sign(CompleteStr("-"))); 453 | } 454 | 455 | #[test] 456 | #[should_panic] 457 | fn number_sign_test_invalid() { 458 | number_sign(CompleteStr("*")).unwrap(); 459 | } 460 | 461 | #[test] 462 | fn real_test_valid() { 463 | assert_eq!(Ok((CompleteStr(""), 123f64)), real(CompleteStr("123.0"))); 464 | assert_eq!(Ok((CompleteStr(""), 123.45f64)), real(CompleteStr("123.45"))); 465 | assert_eq!(Ok((CompleteStr(""), 123f64)), real(CompleteStr("+123.0"))); 466 | assert_eq!(Ok((CompleteStr(""), -123f64)), real(CompleteStr("-123.0"))); 467 | assert_eq!(Ok((CompleteStr(""), 0.5f64)), real(CompleteStr(".5"))); 468 | assert_eq!(Ok((CompleteStr(""), 0.5f64)), real(CompleteStr("+.5"))); 469 | assert_eq!(Ok((CompleteStr(""), - 0.5f64)), real(CompleteStr("-.5"))); 470 | } 471 | 472 | #[test] 473 | #[should_panic] 474 | fn real_test_invalid() { 475 | real(CompleteStr("123")).unwrap(); 476 | } 477 | 478 | #[test] 479 | fn int_test_valid() { 480 | assert_eq!(Ok((CompleteStr(""), 123)), int(CompleteStr("123"))); 481 | assert_eq!(Ok((CompleteStr(""), 123)), int(CompleteStr("+123"))); 482 | assert_eq!(Ok((CompleteStr(""), -123)), int(CompleteStr("-123"))); 483 | 484 | assert_eq!(Ok((CompleteStr(".45"), 123)), int(CompleteStr("123.45"))); 485 | } 486 | 487 | #[test] 488 | fn ident_test_valid() { 489 | assert_eq!(Ok((CompleteStr(""), "abc123")), ident(CompleteStr("abc123"))); 490 | assert_eq!(Ok((CompleteStr(""), "a")), ident(CompleteStr("a"))); 491 | assert_eq!(Ok((CompleteStr(""), "aa")), ident(CompleteStr("aa"))); 492 | assert_eq!(Ok((CompleteStr(" a"), "a")), ident(CompleteStr("a a"))); 493 | } 494 | 495 | #[test] 496 | #[should_panic] 497 | fn ident_test_invalid() { 498 | ident(CompleteStr("123abc")).unwrap(); 499 | } 500 | 501 | #[test] 502 | fn logical_opcode_test_valid() { 503 | assert_eq!(Ok((CompleteStr(""), Opcode::LogicalAnd)), logical_opcode(CompleteStr("&&"))); 504 | assert_eq!(Ok((CompleteStr(""), Opcode::LogicalOr)), logical_opcode(CompleteStr("||"))); 505 | assert_eq!(Ok((CompleteStr(""), Opcode::LogicalXor)), logical_opcode(CompleteStr("^"))); 506 | } 507 | 508 | #[test] 509 | fn product_opcode_test_valid() { 510 | assert_eq!(Ok((CompleteStr(""), Opcode::Div)), product_opcode(CompleteStr("/"))); 511 | assert_eq!(Ok((CompleteStr(""), Opcode::Mul)), product_opcode(CompleteStr("*"))); 512 | assert_eq!(Ok((CompleteStr(""), Opcode::Mod)), product_opcode(CompleteStr("%"))); 513 | } 514 | 515 | #[test] 516 | #[should_panic] 517 | fn product_opcode_test_invalid() { 518 | product_opcode(CompleteStr("+")).unwrap(); 519 | product_opcode(CompleteStr("-")).unwrap(); 520 | } 521 | 522 | #[test] 523 | fn relational_opcode_test_valid() { 524 | assert_eq!(Ok((CompleteStr(""), Opcode::LessThan)), relational_opcode(CompleteStr("<"))); 525 | assert_eq!(Ok((CompleteStr(""), Opcode::GreaterThan)), relational_opcode(CompleteStr(">"))); 526 | assert_eq!(Ok((CompleteStr(""), Opcode::LessThanOrEqual)), relational_opcode(CompleteStr("<="))); 527 | assert_eq!(Ok((CompleteStr(""), Opcode::GreaterThanOrEqual)), relational_opcode(CompleteStr(">="))); 528 | assert_eq!(Ok((CompleteStr(""), Opcode::Equal)), relational_opcode(CompleteStr("=="))); 529 | assert_eq!(Ok((CompleteStr(""), Opcode::NotEqual)), relational_opcode(CompleteStr("!="))); 530 | } 531 | 532 | #[test] 533 | fn sum_opcode_test_valid() { 534 | assert_eq!(Ok((CompleteStr(""), Opcode::Add)), sum_opcode(CompleteStr("+"))); 535 | assert_eq!(Ok((CompleteStr(""), Opcode::Sub)), sum_opcode(CompleteStr("-"))); 536 | } 537 | 538 | #[test] 539 | #[should_panic] 540 | fn sum_opcode_test_invalid() { 541 | sum_opcode(CompleteStr("*")).unwrap(); 542 | sum_opcode(CompleteStr("/")).unwrap(); 543 | } 544 | 545 | #[test] 546 | fn logical_expr_test_valid() { 547 | assert_eq!( 548 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::LogicalAnd, Box::new(Expr::Int(2))))), 549 | logical_expr(CompleteStr("1 && 2")) 550 | ); 551 | assert_eq!( 552 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::LogicalOr, Box::new(Expr::Int(2))))), 553 | logical_expr(CompleteStr("1 || 2")) 554 | ); 555 | assert_eq!( 556 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::LogicalXor, Box::new(Expr::Int(2))))), 557 | logical_expr(CompleteStr("1 ^ 2")) 558 | ); 559 | } 560 | 561 | #[test] 562 | fn relational_expr_test_valid() { 563 | assert_eq!( 564 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::LessThan, Box::new(Expr::Int(2))))), 565 | relational_expr(CompleteStr("1 < 2")) 566 | ); 567 | assert_eq!( 568 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::GreaterThan, Box::new(Expr::Int(2))))), 569 | relational_expr(CompleteStr("1 > 2")) 570 | ); 571 | assert_eq!( 572 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::LessThanOrEqual, Box::new(Expr::Int(2))))), 573 | relational_expr(CompleteStr("1 <= 2")) 574 | ); 575 | assert_eq!( 576 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::GreaterThanOrEqual, Box::new(Expr::Int(2))))), 577 | relational_expr(CompleteStr("1 >= 2")) 578 | ); 579 | assert_eq!( 580 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::Equal, Box::new(Expr::Int(2))))), 581 | relational_expr(CompleteStr("1 == 2")) 582 | ); 583 | assert_eq!( 584 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::NotEqual, Box::new(Expr::Int(2))))), 585 | relational_expr(CompleteStr("1 != 2")) 586 | ); 587 | } 588 | 589 | #[test] 590 | fn product_expr_test_valid() { 591 | assert_eq!( 592 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::Mul, Box::new(Expr::Int(2))))), 593 | product_expr(CompleteStr("1*2")) 594 | ); 595 | assert_eq!( 596 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::Mul, Box::new(Expr::Int(2))))), 597 | product_expr(CompleteStr("1 *2")) 598 | ); 599 | assert_eq!( 600 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::Mul, Box::new(Expr::Int(2))))), 601 | product_expr(CompleteStr("1* 2")) 602 | ); 603 | assert_eq!( 604 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::Mul, Box::new(Expr::Int(2))))), 605 | product_expr(CompleteStr("1 * 2")) 606 | ); 607 | assert_eq!( 608 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Real(1.23f64)), Opcode::Div, Box::new(Expr::Int(2))))), 609 | product_expr(CompleteStr("1.23 / 2")) 610 | ); 611 | assert_eq!( 612 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::Mod, Box::new(Expr::Int(2))))), 613 | product_expr(CompleteStr("1 % 2")) 614 | ); 615 | } 616 | 617 | #[test] 618 | fn sum_expr_test_valid() { 619 | assert_eq!( 620 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::Add, Box::new(Expr::Int(2))))), 621 | sum_expr(CompleteStr("1+2")) 622 | ); 623 | assert_eq!( 624 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::Add, Box::new(Expr::Int(2))))), 625 | sum_expr(CompleteStr("1 +2")) 626 | ); 627 | assert_eq!( 628 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::Add, Box::new(Expr::Int(2))))), 629 | sum_expr(CompleteStr("1+ 2")) 630 | ); 631 | assert_eq!( 632 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::Add, Box::new(Expr::Int(2))))), 633 | sum_expr(CompleteStr("1 + 2")) 634 | ); 635 | assert_eq!( 636 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Real(1.23f64)), Opcode::Sub, Box::new(Expr::Int(2))))), 637 | sum_expr(CompleteStr("1.23 - 2")) 638 | ); 639 | } 640 | 641 | #[test] 642 | fn expr_valid() { 643 | assert_eq!(Ok((CompleteStr(""), Expr::Real(1.23f64))), expr(CompleteStr("1.23"))); 644 | assert_eq!( 645 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::Add, Box::new(Expr::Int(2))))), 646 | expr(CompleteStr("1+2")) 647 | ); 648 | assert_eq!( 649 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::Mul, Box::new(Expr::Int(2))))), 650 | expr(CompleteStr("1*2")) 651 | ); 652 | assert_eq!( 653 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::LessThan, Box::new(Expr::Int(2))))), 654 | expr(CompleteStr("1<2")) 655 | ); 656 | assert_eq!( 657 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::LogicalOr, Box::new(Expr::Int(2))))), 658 | expr(CompleteStr("1 || 2")) 659 | ); 660 | assert_eq!( 661 | Ok(( 662 | CompleteStr(""), 663 | Expr::BinOp( 664 | Box::new(Expr::Real(1.23f64)), 665 | Opcode::Add, 666 | Box::new(Expr::BinOp( 667 | Box::new(Expr::Real(2.34f64)), 668 | Opcode::Mul, 669 | Box::new(Expr::Real(3.45f64)), 670 | )) 671 | ) 672 | )), 673 | expr(CompleteStr("1.23 + 2.34 * 3.45")) 674 | ); 675 | } 676 | 677 | #[test] 678 | fn term_valid() { 679 | assert_eq!( 680 | Ok((CompleteStr(""), Expr::BinOp(Box::new(Expr::Int(1)), Opcode::Add, Box::new(Expr::Int(2))))), 681 | term(CompleteStr("(1+2)")) 682 | ); 683 | assert_eq!(Ok((CompleteStr(""), Expr::Real(1.23f64))), term(CompleteStr("1.23"))); 684 | } 685 | 686 | #[test] 687 | fn bool_literal_valid() { 688 | assert_eq!(Ok((CompleteStr(""), true)), bool_literal(CompleteStr("true"))); 689 | assert_eq!(Ok((CompleteStr(""), true)), bool_literal(CompleteStr("True"))); 690 | assert_eq!(Ok((CompleteStr(""), true)), bool_literal(CompleteStr("TRUE"))); 691 | assert_eq!(Ok((CompleteStr(""), false)), bool_literal(CompleteStr("false"))); 692 | assert_eq!(Ok((CompleteStr(""), false)), bool_literal(CompleteStr("False"))); 693 | assert_eq!(Ok((CompleteStr(""), false)), bool_literal(CompleteStr("FALSE"))); 694 | } 695 | 696 | #[test] 697 | fn dict_literal_valid() { 698 | assert_eq!( 699 | Ok(( 700 | CompleteStr(""), 701 | Expr::Dict(vec![ 702 | ("a", Box::new(Expr::Int(1))), 703 | ("bcd", Box::new(Expr::Real(23.45f64))) 704 | ]) 705 | )), 706 | dict_literal(CompleteStr(r#"{"a":1,"bcd":23.45}"#)) 707 | ); 708 | } 709 | 710 | #[test] 711 | fn float_literal_valid() { 712 | assert_eq!(Ok((CompleteStr(""), 12.34f64)), float_literal(CompleteStr("12.34"))); 713 | } 714 | 715 | #[test] 716 | fn func_call_valid() { 717 | assert_eq!( 718 | Ok(( 719 | CompleteStr(""), 720 | Expr::FuncCall( 721 | "testFun", 722 | vec![ 723 | Box::new(Expr::Int(1)), 724 | Box::new(Expr::Int(2)), 725 | Box::new(Expr::Int(3)), 726 | ] 727 | ) 728 | )), 729 | func_call(CompleteStr("testFun(1, 2, 3)")) 730 | ); 731 | } 732 | 733 | #[test] 734 | fn int_literal_valid() { 735 | assert_eq!(Ok((CompleteStr(""), 123)), int_literal(CompleteStr("123"))); 736 | assert_eq!(Ok((CompleteStr(""), 123)), int_literal(CompleteStr("+123"))); 737 | assert_eq!(Ok((CompleteStr(""), -123)), int_literal(CompleteStr("-123"))); 738 | } 739 | 740 | #[test] 741 | fn key_val_pair_valid() { 742 | assert_eq!(Ok((CompleteStr(""), ("a", Expr::Int(1)))), key_val_pair(CompleteStr(r#""a":1"#))); 743 | assert_eq!(Ok((CompleteStr(""), ("a", Expr::Int(1)))), key_val_pair(CompleteStr(r#""a" :1"#))); 744 | assert_eq!(Ok((CompleteStr(""), ("a", Expr::Int(1)))), key_val_pair(CompleteStr(r#""a": 1"#))); 745 | assert_eq!(Ok((CompleteStr(""), ("a", Expr::Int(1)))), key_val_pair(CompleteStr(r#""a" : 1"#))); 746 | 747 | assert_eq!( 748 | Ok((CompleteStr(""), ("abc", Expr::Str("def")))), 749 | key_val_pair(CompleteStr(r#""abc":"def""#)) 750 | ); 751 | } 752 | 753 | #[test] 754 | fn list_element_valid() { 755 | assert_eq!( 756 | Ok((CompleteStr(""), Expr::ListElement("a", Box::new(Expr::Int(1))))), 757 | list_element(CompleteStr("a[1]")) 758 | ); 759 | } 760 | 761 | #[test] 762 | fn list_literal_valid() { 763 | assert_eq!( 764 | Ok(( 765 | CompleteStr(""), 766 | Expr::List(vec![ 767 | Box::new(Expr::Int(1)), 768 | Box::new(Expr::Str("two")), 769 | Box::new(Expr::Bool(true)), 770 | Box::new(Expr::Real(4.56f64)), 771 | ]) 772 | )), 773 | list_literal(CompleteStr(r#"[1, "two", true, 4.56]"#)) 774 | ); 775 | } 776 | 777 | #[test] 778 | fn str_literal_valid() { 779 | assert_eq!(Ok((CompleteStr(""), "")), str_literal(CompleteStr(r#""""#))); 780 | assert_eq!(Ok((CompleteStr(""), "a")), str_literal(CompleteStr(r#""a""#))); 781 | assert_eq!(Ok((CompleteStr(""), "abc")), str_literal(CompleteStr(r#""abc""#))); 782 | assert_eq!(Ok((CompleteStr(""), "abc 123")), str_literal(CompleteStr(r#""abc 123""#))); 783 | } 784 | 785 | #[test] 786 | fn unary_opcode_valid() { 787 | assert_eq!(Ok((CompleteStr(""), Opcode::Not)), unary_opcode(CompleteStr("!"))); 788 | } 789 | 790 | #[test] 791 | fn unary_op_valid() { 792 | assert_eq!( 793 | Ok((CompleteStr(""), Expr::UnaryOp(Opcode::Not, Box::new(Expr::Id("a"))))), 794 | unary_op(CompleteStr("!a")) 795 | ); 796 | assert_eq!( 797 | Ok((CompleteStr(""), Expr::UnaryOp(Opcode::Not, Box::new(Expr::Bool(true))))), 798 | unary_op(CompleteStr("!true")) 799 | ); 800 | } 801 | 802 | #[test] 803 | fn value_expr_valid() { 804 | assert_eq!(Ok((CompleteStr(""), Expr::Real(1.23f64))), value_expr(CompleteStr("1.23"))); 805 | assert_eq!(Ok((CompleteStr(""), Expr::Int(123))), value_expr(CompleteStr("123"))); 806 | assert_eq!(Ok((CompleteStr(""), Expr::Bool(true))), value_expr(CompleteStr("true"))); 807 | assert_eq!(Ok((CompleteStr(""), Expr::Str("abc"))), value_expr(CompleteStr(r#""abc""#))); 808 | assert_eq!(Ok((CompleteStr(""), Expr::None)), value_expr(CompleteStr("null"))); 809 | assert_eq!(Ok((CompleteStr(""), Expr::Id("abc"))), value_expr(CompleteStr("abc"))); 810 | 811 | assert_eq!( 812 | Ok(( 813 | CompleteStr(""), 814 | Expr::List(vec![ 815 | Box::new(Expr::Int(1)), 816 | Box::new(Expr::Str("two")), 817 | Box::new(Expr::Bool(true)), 818 | Box::new(Expr::Real(4.56f64)), 819 | ]) 820 | )), 821 | value_expr(CompleteStr(r#"[1, "two", true, 4.56]"#)) 822 | ); 823 | 824 | assert_eq!( 825 | Ok(( 826 | CompleteStr(""), 827 | Expr::Dict(vec![ 828 | ("a", Box::new(Expr::Int(1))), 829 | ("bcd", Box::new(Expr::Real(23.45f64))) 830 | ]) 831 | )), 832 | value_expr(CompleteStr(r#"{"a":1,"bcd":23.45}"#)) 833 | ); 834 | 835 | assert_eq!( 836 | Ok(( 837 | CompleteStr(""), 838 | Expr::FuncCall( 839 | "testFun", 840 | vec![ 841 | Box::new(Expr::Int(1)), 842 | Box::new(Expr::Int(2)), 843 | Box::new(Expr::Int(3)), 844 | ] 845 | ) 846 | )), 847 | value_expr(CompleteStr("testFun(1, 2, 3)")) 848 | ); 849 | 850 | assert_eq!( 851 | Ok((CompleteStr(""), Expr::ListElement("a", Box::new(Expr::Int(1))))), 852 | value_expr(CompleteStr("a[1]")) 853 | ); 854 | 855 | assert_eq!( 856 | Ok((CompleteStr(""), Expr::UnaryOp(Opcode::Not, Box::new(Expr::Id("a"))))), 857 | value_expr(CompleteStr("!a")) 858 | ); 859 | } 860 | 861 | #[test] 862 | fn break_statement_valid() { 863 | assert_eq!(Ok((CompleteStr(""), Stmt::Break)), break_statement(CompleteStr("break"))); 864 | assert_eq!(Ok((CompleteStr(""), Stmt::Break)), break_statement(CompleteStr(" break"))); 865 | assert_eq!(Ok((CompleteStr(""), Stmt::Break)), break_statement(CompleteStr("break "))); 866 | assert_eq!(Ok((CompleteStr(""), Stmt::Break)), break_statement(CompleteStr(" break "))); 867 | 868 | assert_eq!(Ok((CompleteStr(";"), Stmt::Break)), break_statement(CompleteStr("break;"))); 869 | } 870 | 871 | #[test] 872 | fn expr_statement_valid() { 873 | assert_eq!( 874 | Ok((CompleteStr(""), Stmt::Expr(Expr::Id("a")))), 875 | expr_statement(CompleteStr("a")) 876 | ); 877 | } 878 | 879 | #[test] 880 | fn fndef_statement_valid() { 881 | assert_eq!( 882 | Ok(( 883 | CompleteStr(""), 884 | Stmt::FnDef( 885 | "abc", 886 | vec![ 887 | "a", 888 | "b", 889 | "c", 890 | ], 891 | vec![ 892 | Stmt::Return(Expr::Id("a")), 893 | ] 894 | ) 895 | )), 896 | fndef_statement(CompleteStr("fn abc(a,b,c) { return a;}")) 897 | ); 898 | } 899 | 900 | #[test] 901 | fn if_statement_valid() { 902 | assert_eq!( 903 | Ok(( 904 | CompleteStr(""), 905 | Stmt::If( 906 | Expr::Bool(true), 907 | vec![ 908 | Stmt::Expr( 909 | Expr::FuncCall( 910 | "print", 911 | vec![Box::new(Expr::Int(1))], 912 | ), 913 | ), 914 | ] 915 | ) 916 | )), 917 | if_statement(CompleteStr(r#"if true { print(1); }"#)) 918 | ); 919 | } 920 | 921 | #[test] 922 | fn if_else_statement_valid() { 923 | assert_eq!( 924 | Ok(( 925 | CompleteStr(""), 926 | Stmt::IfElse( 927 | Expr::Bool(true), 928 | vec![ 929 | Stmt::Expr( 930 | Expr::FuncCall( 931 | "print", 932 | vec![Box::new(Expr::Int(1))], 933 | ), 934 | ), 935 | ], 936 | vec![ 937 | Stmt::Expr( 938 | Expr::FuncCall( 939 | "print", 940 | vec![Box::new(Expr::Int(0))], 941 | ), 942 | ), 943 | ] 944 | ) 945 | )), 946 | if_else_statement(CompleteStr(r#"if true { print(1); } else { print(0); }"#)) 947 | ); 948 | } 949 | 950 | #[test] 951 | fn let_statement_valid() { 952 | assert_eq!( 953 | Ok((CompleteStr(""), Stmt::Let("a", Expr::Int(123)))), 954 | let_statement(CompleteStr("let a = 123")) 955 | ); 956 | } 957 | 958 | #[test] 959 | fn list_assignment_statement_valid() { 960 | assert_eq!( 961 | Ok(( 962 | CompleteStr(""), 963 | Stmt::ListItemAssignment( 964 | "a", 965 | Expr::Int(1), 966 | Expr::Int(2) 967 | ) 968 | )), 969 | list_assignment_statement(CompleteStr("a[1] = 2")) 970 | ); 971 | assert_eq!( 972 | Ok(( 973 | CompleteStr(""), 974 | Stmt::ListItemAssignment( 975 | "a", 976 | Expr::Str("idx"), 977 | Expr::Int(2) 978 | ) 979 | )), 980 | list_assignment_statement(CompleteStr(r#"a["idx"] = 2"#)) 981 | ); 982 | } 983 | 984 | #[test] 985 | fn loop_statement_valid() { 986 | assert_eq!( 987 | Ok(( 988 | CompleteStr(""), 989 | Stmt::Loop( 990 | vec![ 991 | Stmt::Expr( 992 | Expr::FuncCall( 993 | "print", 994 | vec![Box::new(Expr::Int(1))] 995 | ) 996 | ), 997 | ] 998 | ) 999 | )), 1000 | loop_statement(CompleteStr("loop { print(1); }")) 1001 | ); 1002 | } 1003 | 1004 | #[test] 1005 | fn return_statement_valid() { 1006 | assert_eq!( 1007 | Ok((CompleteStr(""), Stmt::Return(Expr::Int(123)))), 1008 | return_statement(CompleteStr("return 123")) 1009 | ); 1010 | } 1011 | 1012 | #[test] 1013 | fn statement_valid() { 1014 | match statement(CompleteStr("break")) { 1015 | Err(_) => assert!(false, "statement(): Break: returned error"), 1016 | Ok(s) => match s.1 { 1017 | Stmt::Break => {}, 1018 | _ => assert!(false, "statement(): Break: not Stmt::Break"), 1019 | }, 1020 | } 1021 | match statement(CompleteStr("fn a(b) { return a; }")) { 1022 | Err(_) => assert!(false, "statement(): FnDef: returned error"), 1023 | Ok(s) => match s.1 { 1024 | Stmt::FnDef(_, _, _) => {}, 1025 | _ => assert!(false, "statement(): FnDef: not Stmt::FnDef"), 1026 | }, 1027 | } 1028 | match statement(CompleteStr("if true { print(1); }")) { 1029 | Err(_) => assert!(false, "statement(): If: returned error"), 1030 | Ok(s) => match s.1 { 1031 | Stmt::If(_, _) => {}, 1032 | _ => assert!(false, "statement(): If: not Stmt::If"), 1033 | }, 1034 | } 1035 | match statement(CompleteStr("if true { print(1); } else { print(0); }")) { 1036 | Err(_) => assert!(false, "statement(): IfElse: returned error"), 1037 | Ok(s) => match s.1 { 1038 | Stmt::IfElse(_, _, _) => {}, 1039 | _ => assert!(false, "statement(): IfElse: not Stmt::IfElse"), 1040 | }, 1041 | } 1042 | match statement(CompleteStr("let a = 1")) { 1043 | Err(_) => assert!(false, "statement(): Let: returned error"), 1044 | Ok(s) => match s.1 { 1045 | Stmt::Let(_, _) => {}, 1046 | _ => assert!(false, "statement(): Let: not Stmt::Let"), 1047 | }, 1048 | } 1049 | match statement(CompleteStr("a[1] = 2")) { 1050 | Err(_) => assert!(false, "statement(): ListItemAssignment: returned error"), 1051 | Ok(s) => match s.1 { 1052 | Stmt::ListItemAssignment(_, _, _) => {}, 1053 | _ => assert!(false, "statement(): ListItemAssignment: not Stmt::ListItemAssignment"), 1054 | }, 1055 | } 1056 | match statement(CompleteStr("loop { print(1); }")) { 1057 | Err(_) => assert!(false, "statement(): Loop: returned error"), 1058 | Ok(s) => match s.1 { 1059 | Stmt::Loop(_) => {}, 1060 | _ => assert!(false, "statement(): Loop: not Stmt::Loop"), 1061 | }, 1062 | } 1063 | match statement(CompleteStr("return 1")) { 1064 | Err(_) => assert!(false, "statement(): Return: returned error"), 1065 | Ok(s) => match s.1 { 1066 | Stmt::Return(_) => {}, 1067 | _ => assert!(false, "statement(): Return: not Stmt::Return"), 1068 | }, 1069 | } 1070 | match statement(CompleteStr("print(1)")) { 1071 | Err(_) => assert!(false, "statement(): Expr: returned error"), 1072 | Ok(s) => match s.1 { 1073 | Stmt::Expr(_) => {}, 1074 | _ => assert!(false, "statement(): Expr: not Stmt::Expr"), 1075 | }, 1076 | } 1077 | } 1078 | } 1079 | -------------------------------------------------------------------------------- /src/runtime.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(feature = "no_std"))] 2 | use std::any::Any; 3 | #[cfg(feature = "no_std")] 4 | use core::any::Any; 5 | 6 | #[cfg(not(feature = "no_std"))] 7 | use std::rc::Rc; 8 | #[cfg(feature = "no_std")] 9 | use alloc::rc::Rc; 10 | 11 | use ast::{NativeFunction, Value}; 12 | use interpreter::{Scope, ScopeChain}; 13 | 14 | /// Native "print" function 15 | pub struct NFPrint; 16 | 17 | /// Native "println" function 18 | pub struct NFPrintLn; 19 | 20 | impl NativeFunction for NFPrint { 21 | /// Execute the "print" NativeFunction 22 | /// 23 | /// Prints all arguments in turn to stdout. 24 | #[cfg(not(feature = "no_std"))] 25 | fn execute<'src>(&self, _scopes: &mut ScopeChain<'src>, args: &[Value<'src>]) -> Value<'src> { 26 | for arg in args { 27 | match arg { 28 | Value::Int(x) => print!("{}", x), 29 | Value::Real(x) => print!("{}", x), 30 | Value::Str(x) => print!("{}", x), 31 | _ => print!("{:?}", arg), 32 | }; 33 | } 34 | Value::None 35 | } 36 | 37 | #[cfg(feature = "no_std")] 38 | fn execute<'src>(&self, _scopes: &mut ScopeChain<'src>, _args: &[Value<'src>]) -> Value<'src> { 39 | Value::None 40 | } 41 | 42 | fn as_any(&self) -> &dyn Any { 43 | self 44 | } 45 | } 46 | 47 | impl NativeFunction for NFPrintLn { 48 | /// Execute the "println" NativeFunction 49 | /// 50 | /// Prints all arguments in turn to stdout, followed by a newline. 51 | #[cfg(not(feature = "no_std"))] 52 | fn execute<'src>(&self, _scopes: &mut ScopeChain<'src>, args: &[Value<'src>]) -> Value<'src> { 53 | for arg in args { 54 | match arg { 55 | Value::Int(x) => print!("{}", x), 56 | Value::Real(x) => print!("{}", x), 57 | Value::Str(x) => print!("{}", x), 58 | _ => println!("{:?}", arg), 59 | }; 60 | } 61 | println!(); 62 | Value::None 63 | } 64 | 65 | #[cfg(feature = "no_std")] 66 | fn execute<'src>(&self, _scopes: &mut ScopeChain<'src>, _args: &[Value<'src>]) -> Value<'src> { 67 | Value::None 68 | } 69 | 70 | fn as_any(&self) -> &dyn Any { 71 | self 72 | } 73 | } 74 | 75 | /// Takes a Scope and inserts the NativeFunctions defined in this runtime module for use within 76 | /// scripts. 77 | pub fn insert_native_functions(scope: &mut Scope) { 78 | scope 79 | .native_funcs 80 | .insert("print", Rc::new(NFPrint {})); 81 | scope 82 | .native_funcs 83 | .insert("println", Rc::new(NFPrintLn {})); 84 | } 85 | --------------------------------------------------------------------------------