diff --git a/Cargo.lock b/Cargo.lock index a7b7a1b..280c4cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -339,9 +339,12 @@ name = "conq-er" version = "0.1.0" dependencies = [ "gloo 0.11.0", + "gloo-net 0.5.0", + "js-sys", "monaco", "stylist", "wasm-bindgen", + "wasm-bindgen-futures", "web-sys", "yew", "yew-autoprops", @@ -354,6 +357,7 @@ dependencies = [ "chumsky", "melior", "rand", + "tempfile", ] [[package]] @@ -839,7 +843,7 @@ dependencies = [ "futures-core", "futures-sink", "gloo-utils 0.1.7", - "http", + "http 0.2.11", "js-sys", "pin-project", "serde", @@ -860,7 +864,7 @@ dependencies = [ "futures-core", "futures-sink", "gloo-utils 0.2.0", - "http", + "http 0.2.11", "js-sys", "pin-project", "serde", @@ -881,7 +885,7 @@ dependencies = [ "futures-core", "futures-sink", "gloo-utils 0.2.0", - "http", + "http 0.2.11", "js-sys", "pin-project", "serde", @@ -1055,6 +1059,25 @@ dependencies = [ "syn 2.0.50", ] +[[package]] +name = "h2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 1.1.0", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.14.3" @@ -1097,6 +1120,93 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +dependencies = [ + "bytes", + "futures-core", + "http 1.1.0", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http 1.1.0", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1293,6 +1403,17 @@ dependencies = [ "adler", ] +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + [[package]] name = "mlir-sys" version = "0.2.1" @@ -1377,6 +1498,16 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + [[package]] name = "parking_lot_core" version = "0.9.9" @@ -1783,6 +1914,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.9" @@ -1808,6 +1948,16 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +[[package]] +name = "socket2" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "stacker" version = "0.1.15" @@ -1938,6 +2088,18 @@ dependencies = [ "thiserror", ] +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "terminal_size" version = "0.3.0" @@ -1948,6 +2110,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "test-server" +version = "0.1.0" +dependencies = [ + "conqiler", + "http-body-util", + "hyper", + "hyper-util", + "tokio", +] + [[package]] name = "thiserror" version = "1.0.57" @@ -2006,7 +2179,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", ] [[package]] @@ -2020,6 +2213,20 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + [[package]] name = "toml_datetime" version = "0.6.5" @@ -2037,12 +2244,41 @@ dependencies = [ "winnow", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2068,6 +2304,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "typed-arena" version = "2.0.2" @@ -2120,6 +2362,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 9e264eb..6355aeb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,5 +2,6 @@ resolver="2" members=[ "conqiler", - "conq-er" + "conq-er", + "test-server", ] diff --git a/README.md b/README.md index a21b1e4..b7f91a0 100644 --- a/README.md +++ b/README.md @@ -18,4 +18,9 @@ sudo ln /usr/bin/llvm-config-17 /usr/bin/llvm-config sudo apt install libmlir-17-dev ``` -## conq-er \ No newline at end of file +## conq-er + +```bash +cargo build +trunk serve --open conq-er/index.html +``` \ No newline at end of file diff --git a/Trunk.toml b/Trunk.toml new file mode 100644 index 0000000..812a73e --- /dev/null +++ b/Trunk.toml @@ -0,0 +1,17 @@ +[serve] +# The address to serve on LAN. +address = "127.0.0.1" +# The address to serve on WAN. +# address = "0.0.0.0" +# The port to serve on. +port = 3000 + + +[[proxy]] +backend = "http://localhost:7878/api/echo" + +[[proxy]] +backend = "http://localhost:7878/api/sample" + +[[proxy]] +backend = "http://localhost:7878/api/compile" diff --git a/conq-er/Cargo.toml b/conq-er/Cargo.toml index 9fb02c6..2feab21 100644 --- a/conq-er/Cargo.toml +++ b/conq-er/Cargo.toml @@ -12,5 +12,18 @@ wasm-bindgen = "0.2.91" monaco = { git = "https://github.com/siku2/rust-monaco.git", features = ["yew-components"] } # ^ swap this once 0.5.0 is released stylist = { features = ["yew_integration"] } -web-sys = { version = "0.3", features = ["HtmlElement", "CssStyleDeclaration"] } gloo = "0.11.0" +gloo-net = "0.5" +wasm-bindgen-futures = "0.4" +js-sys = "0.3" + +[dependencies.web-sys] +version = "0.3" +features = [ + 'CanvasRenderingContext2d', + 'Document', + 'Element', + 'CssStyleDeclaration', + 'HtmlCanvasElement', + 'Window', +] \ No newline at end of file diff --git a/conq-er/README.md b/conq-er/README.md index 370c8f4..be557e7 100644 --- a/conq-er/README.md +++ b/conq-er/README.md @@ -17,10 +17,10 @@ Requires [Rust](https://www.rust-lang.org/) and [Trunk](https://trunkrs.dev/). To get started with Conq-er, follow these steps: -1. Clone this repository: `git clone https://github.com/Enigmatrix/conq/` -2. Navigate to the top-level project directory: `cd conq` -3. Install the necessary dependencies: `cargo build` -4. Run the server: `trunk serve --open conq-er/index.html` +1. Clone the parent repository: `git clone https://github.com/Enigmatrix/conq/ && cd conq` +2. Install the necessary dependencies: `cargo build` +3. Run the server: `cargo run --bin test-server` +4. Run the client: `trunk serve --open conq-er/index.html`. ## License diff --git a/conq-er/Trunk.toml b/conq-er/Trunk.toml deleted file mode 100644 index 43c5c31..0000000 --- a/conq-er/Trunk.toml +++ /dev/null @@ -1,7 +0,0 @@ -[serve] -# The address to serve on LAN. -address = "127.0.0.1" -# The address to serve on WAN. -# address = "0.0.0.0" -# The port to serve on. -port = 3000 \ No newline at end of file diff --git a/conq-er/js/conqMonarchTokensProvider.js b/conq-er/js/conqMonarchTokensProvider.js new file mode 100644 index 0000000..a0f82ca --- /dev/null +++ b/conq-er/js/conqMonarchTokensProvider.js @@ -0,0 +1,85 @@ +export const makeTokensProvider = () => { + return { + keywords: [ + "break", + "continue", + "else", + "fn", + "for", + "fun", + "if", + "let", + "return", + "true", + "while", + ], + + operators: [ + "-", + "+", + "/", + "*", + "%", + "!", + "!=", + "=", + "==", + ">", + ">=", + "<", + "<=", + "&&", + "||", + ",", + ], + + symbols: /[=> CodeEditorOptions { + CodeEditorOptions::default() + .with_language(monaco_conq::ID.to_string()) + .with_value(CONTENT.to_owned()) + .with_builtin_theme(BuiltinTheme::VsDark) + .with_automatic_layout(true) +} + +#[derive(PartialEq, Properties)] +pub struct EditorProps { + pub on_editor_created: Callback, + pub text_model: TextModel, +} + +#[function_component] +pub fn Editor(props: &EditorProps) -> Html { + let EditorProps { + on_editor_created, + text_model, + } = props; + + html! { + + } +} diff --git a/conq-er/src/engine.rs b/conq-er/src/engine.rs new file mode 100644 index 0000000..d9db5c1 --- /dev/null +++ b/conq-er/src/engine.rs @@ -0,0 +1,219 @@ +use std::sync::Mutex; + +use js_sys::{self, BigInt}; +use js_sys::{Function, Map, Object, Reflect, WebAssembly, WebAssembly::Memory, JSON::stringify}; +use wasm_bindgen::prelude::*; +use wasm_bindgen::{JsCast, JsValue}; +use wasm_bindgen_futures::JsFuture; +use web_sys::CanvasRenderingContext2d; + +static STD_OUT: Mutex = Mutex::new(String::new()); + +async fn execute(binary: Vec) -> Result { + let result = JsFuture::from(WebAssembly::instantiate_buffer( + &binary.as_slice(), + &make_imports().unwrap(), + )) + .await + .unwrap(); + let instance: WebAssembly::Instance = Reflect::get(&result, &"instance".into()) + .unwrap() + .dyn_into() + .unwrap(); + // let memory = Reflect::get(&instance.exports(), &"memory".into()).unwrap().dyn_into::().expect("memory export wasn't a `WebAssembly.Memory"); + // memory.grow(2); + // log!("Instance", &instance); + let start = Reflect::get(&instance.exports(), &"_start".into()) // TODO: Export Start + .unwrap() + .dyn_into::() + .expect("entrypoint function start not found"); + start.call1(&JsValue::undefined(), &0.into()) +} + +pub async fn exec_n_get_output(binary: Vec) -> String { + let mut result = b_stringify(&execute(binary).await.unwrap()); + result.insert_str(0, &STD_OUT.lock().unwrap()); + STD_OUT.lock().unwrap().clear(); + result +} + +fn b_stringify(a: &JsValue) -> String { + if a.is_undefined() { + "undefined".to_string() + } else if a.is_null() { + "null".to_string() + } else if let Some(s) = a.as_string() { + s + } else if let Some(b) = a.dyn_ref::() { + b.to_string(10).unwrap().into() + } else { + stringify(&a).unwrap_throw().as_string().unwrap() + } +} + +#[wasm_bindgen] +pub struct Env { + _canvas: web_sys::HtmlCanvasElement, + _context: CanvasRenderingContext2d, + _memory: Memory, + _mem_current: i64, +} + +#[wasm_bindgen] +impl Env { + + pub fn new() -> Env { + let mem_descriptor = Object::new(); + Reflect::set( + &mem_descriptor, + &JsValue::from("initial"), + &JsValue::from(10), + ) + .unwrap(); + Reflect::set( + &mem_descriptor, + &JsValue::from("maximum"), + &JsValue::from(256), + ) + .unwrap(); + Env { + _canvas: JsValue::undefined().into(), + _context: JsValue::undefined().into(), + _memory: Memory::new(&mem_descriptor).unwrap(), + _mem_current: 0, + } + } + + fn mem_size(&self) -> i64 { + self._memory + .buffer() + .dyn_into::() + .unwrap() + .byte_length() as i64 + } + + pub fn malloc(&mut self, size: i64) -> i32 { + let mem_current = self._mem_current; + self._mem_current += size; + if self._mem_current > self.mem_size() { + self._memory.grow(1); + }; + mem_current as i32 + } + + pub fn print(&self, a: JsValue) { + let str = b_stringify(&a); + STD_OUT.lock().unwrap().push_str(&str); + } + + pub fn init_canvas(&mut self) { + let document = web_sys::window() + .unwrap() + .open() + .unwrap() + .unwrap() + .document() + .unwrap(); + self._canvas = document + .create_element("canvas") + .unwrap() + .unchecked_into::(); + self._context = self + ._canvas + .get_context("2d") + .unwrap() + .unwrap() + .unchecked_into::(); + self._context.begin_path(); + } + + pub fn clear_canvas(&self) { + self._context.clear_rect( + 0.0, + 0.0, + self._canvas.width() as f64, + self._canvas.height() as f64, + ); + self._context.begin_path(); + } + + pub fn line_to(&self, x: f64, y: f64) { + self._context.line_to(x, y); + } + + pub fn move_to(&self, x: f64, y: f64) { + self._context.move_to(x, y); + } + + pub fn close_path(&self) { + self._context.close_path(); + } + + pub fn stroke_style(&self, style: &str) { + self._context.set_stroke_style(&JsValue::from_str(style)); + } + + pub fn stroke(&self) { + self._context.stroke(); + } + + pub fn fill_style(&self, style: &str) { + self._context.set_fill_style(&JsValue::from_str(style)); + } + + pub fn fill(&self) { + self._context.fill(); + } + + pub fn arc(&self, x: f64, y: f64, radius: f64, start_angle: f64, end_angle: f64) { + self._context + .arc(x, y, radius, start_angle, end_angle) + .unwrap(); + } + + pub fn quadratic_curve_to(&self, cpx: f64, cpy: f64, x: f64, y: f64) { + self._context.quadratic_curve_to(cpx, cpy, x, y); + } + + pub fn bezier_curve_to(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) { + self._context.bezier_curve_to(cp1x, cp1y, cp2x, cp2y, x, y); + } +} + +fn bind(this: &JsValue, func_name: &str) -> Result<(), JsValue> { + let property_key = JsValue::from(func_name); + let orig_func = Reflect::get(this, &property_key)?.dyn_into::()?; + let func = orig_func.bind(this); + if !Reflect::set(this, &property_key, &func)? { + return Err(JsValue::from("failed to set property")); + } + Ok(()) +} + +pub fn make_imports() -> Result { + let map = Map::new(); + let env: JsValue = Env::new().into(); + + // Reflect::set(&imports, &JsValue::from("__linear_memory"), &memory).unwrap(); + + // let canvas: JsValue = Canvas::new().into(); + // Reflect::set(&imports, &JsValue::from("canvas"), &canvas).unwrap(); + + bind(&env, "malloc")?; + bind(&env, "print")?; + bind(&env, "init_canvas")?; + bind(&env, "clear_canvas")?; + bind(&env, "line_to")?; + bind(&env, "move_to")?; + bind(&env, "close_path")?; + bind(&env, "stroke_style")?; + bind(&env, "stroke")?; + bind(&env, "fill_style")?; + bind(&env, "fill")?; + bind(&env, "arc")?; + bind(&env, "quadratic_curve_to")?; + bind(&env, "bezier_curve_to")?; + + map.set(&JsValue::from("env"), &env); + Object::from_entries(&map.into()) +} diff --git a/conq-er/src/main.rs b/conq-er/src/main.rs index c8dec28..96ad6d1 100644 --- a/conq-er/src/main.rs +++ b/conq-er/src/main.rs @@ -1,15 +1,26 @@ // https://blog.theodo.com/2020/11/react-resizeable-split-panels/ -use std::rc::Rc; - use gloo::utils::window; -use gloo::{/* console::log, */events::EventListener}; +use gloo::{console::log, events::EventListener}; +use gloo_net::http::Request; use stylist::yew::{styled_component, Global}; +use wasm_bindgen::{closure::Closure, JsCast, JsValue}; use web_sys::HtmlElement; use yew::prelude::*; use yew_autoprops::autoprops; -use monaco::{api::CodeEditorOptions, sys::editor::BuiltinTheme, yew::CodeEditor}; +use monaco::{api::TextModel, sys::editor::IStandaloneCodeEditor, yew::CodeEditorLink}; + +mod editor; +use editor::Editor; +mod output; +use output::Output; +mod engine; +use engine::exec_n_get_output; + +mod monaco_conq; + +const CONTENT: &str = include_str!("../static/init.cq"); #[derive(Clone, PartialEq)] enum SplitAxis { @@ -19,40 +30,6 @@ enum SplitAxis { const MIN_AXIS: i32 = 200; -const CONTENT: &str = include_str!("main.rs"); - -fn get_options() -> CodeEditorOptions { - CodeEditorOptions::default() - .with_language("rust".to_owned()) - .with_value(CONTENT.to_owned()) - .with_builtin_theme(BuiltinTheme::VsDark) - .with_automatic_layout(true) -} - -#[styled_component] -pub fn Editor() -> Html { - let options = Rc::new(get_options()); - html! { - - } -} - -#[styled_component] -pub fn Output() -> Html { - html! { -
-
{"test out please ignore"}
-
- } -} - #[autoprops] #[styled_component] pub fn Divider(on_mouse_down: Callback) -> Html { @@ -84,7 +61,7 @@ pub fn Divider(on_mouse_down: Callback) -> Html { #[function_component] pub fn LeftPane( left_width: Option, - set_left_width: Callback, + init_left_width: Callback, children: &Children, ) -> Html { let left_ref = use_node_ref(); @@ -92,14 +69,13 @@ pub fn LeftPane( { let left_width = left_width.clone(); - let set_left_width = set_left_width.clone(); + let init_left_width = init_left_width.clone(); let left_ref = left_ref.clone(); - use_effect_with(left_width.clone(), move |_| { + use_effect_with((left_width.clone(), axis.clone()), move |_| { if let Some(left_div) = left_ref.cast::() { if left_width.is_none() { - left_div.style().set_property("flex", "1").unwrap(); - set_left_width.emit(match axis { + init_left_width.emit(match axis { SplitAxis::Horizontal => left_div.client_width(), SplitAxis::Vertical => left_div.client_height(), }); @@ -107,18 +83,24 @@ pub fn LeftPane( } else { left_div .style() - .set_property( match axis { - SplitAxis::Horizontal => "width", - SplitAxis::Vertical => "height", - }, &format!("{}px", left_width.unwrap())) + .set_property( + match axis { + SplitAxis::Horizontal => "width", + SplitAxis::Vertical => "height", + }, + &format!("{}px", left_width.unwrap()), + ) + .unwrap(); + left_div + .style() + .set_property( + match axis { + SplitAxis::Horizontal => "height", + SplitAxis::Vertical => "width", + }, + "unset", + ) .unwrap(); - left_div.style().set_property( - match axis { - SplitAxis::Horizontal => "height", - SplitAxis::Vertical => "width", - }, - "unset", - ).unwrap(); } } || {} @@ -126,7 +108,7 @@ pub fn LeftPane( } html! { -
+
{children.clone()}
} @@ -136,16 +118,35 @@ pub fn LeftPane( #[styled_component] pub fn SplitPane(left: &Html, right: &Html) -> Html { let left_width = use_state_eq(|| None); + let left_frac = use_state_eq(|| 0.5); let divider_x = use_state_eq(|| None); let is_dragging = use_state_eq(|| false); let axis_ctx = use_state_eq(|| SplitAxis::Horizontal); let split_pane_ref = use_node_ref(); - let set_left_width = { + let sync_split_axis = { + let axis_ctx = axis_ctx.clone(); + move || { + let new_axis; + if window().inner_width().unwrap().as_f64().unwrap() + < window().inner_height().unwrap().as_f64().unwrap() + { + new_axis = SplitAxis::Vertical; + } else { + new_axis = SplitAxis::Horizontal; + } + axis_ctx.set(new_axis.clone()); + new_axis + } + }; + + let init_left_width = { let left_width = left_width.clone(); + let sync_split_axis = sync_split_axis.clone(); Callback::from(move |value: i32| { left_width.set(Some(value)); + sync_split_axis(); }) }; @@ -165,23 +166,25 @@ pub fn SplitPane(left: &Html, right: &Html) -> Html { let set_width_bounded = { let left_width = left_width.clone(); + let left_frac = left_frac.clone(); let split_pane_ref = split_pane_ref.clone(); let axis_ctx = axis_ctx.clone(); Callback::from(move |value: i32| { - if value < MIN_AXIS { - left_width.set(Some(MIN_AXIS)); - return; - } - if let Some(left_div) = split_pane_ref.cast::() { + if let Some(split_pane_div) = split_pane_ref.cast::() { + let new_width; let parent_len = match *axis_ctx { - SplitAxis::Horizontal => left_div.client_width(), - SplitAxis::Vertical => left_div.client_height(), + SplitAxis::Horizontal => split_pane_div.client_width(), + SplitAxis::Vertical => split_pane_div.client_height(), }; - if value > parent_len - MIN_AXIS { - left_width.set(Some(parent_len - MIN_AXIS)); + if value < MIN_AXIS { + new_width = MIN_AXIS; + } else if value > parent_len - MIN_AXIS { + new_width = parent_len - MIN_AXIS; } else { - left_width.set(Some(value)); + new_width = value; } + left_width.set(Some(new_width)); + left_frac.set(new_width as f64 / parent_len as f64); } }) }; @@ -190,7 +193,7 @@ pub fn SplitPane(left: &Html, right: &Html) -> Html { let left_width = left_width.clone(); let divider_x = divider_x.clone(); let is_dragging = is_dragging.clone(); - let set_with_bounded = set_width_bounded.clone(); + let set_width_bounded = set_width_bounded.clone(); let axis_ctx = axis_ctx.clone(); Callback::from(move |event: PointerEvent| { if *is_dragging { @@ -199,10 +202,9 @@ pub fn SplitPane(left: &Html, right: &Html) -> Html { SplitAxis::Horizontal => event.client_x(), SplitAxis::Vertical => event.client_y(), }; - let new_width = - left_width.unwrap() + (drag_len - divider_x.unwrap()); + let new_width = left_width.unwrap() + (drag_len - divider_x.unwrap()); divider_x.set(Some(drag_len)); - set_with_bounded.emit(new_width); + set_width_bounded.emit(new_width); } } }) @@ -217,20 +219,25 @@ pub fn SplitPane(left: &Html, right: &Html) -> Html { use_effect_with((), { let axis_ctx = axis_ctx.clone(); + let left_frac = left_frac.clone(); let left_width = left_width.clone(); + let split_pane_ref = split_pane_ref.clone(); + let sync_split_axis = sync_split_axis.clone(); move |_| { EventListener::new(&window(), "resize", move |_| { - let new_axis; - if window().inner_width().unwrap().as_f64().unwrap() - < window().inner_height().unwrap().as_f64().unwrap() - { - new_axis = SplitAxis::Vertical; - } else { - new_axis = SplitAxis::Horizontal; + let new_axis = sync_split_axis(); + axis_ctx.set(new_axis.clone()); + + if let Some(split_pane_div) = split_pane_ref.cast::() { + let parent_len = match new_axis { + SplitAxis::Horizontal => split_pane_div.client_width(), + SplitAxis::Vertical => split_pane_div.client_height(), + }; + let new_width = (*left_frac * parent_len as f64) as i32; + left_width.set(Some(new_width)); } - left_width.set(None); - axis_ctx.set(new_axis); - }).forget(); + }) + .forget(); || {} } }); @@ -254,7 +261,7 @@ pub fn SplitPane(left: &Html, right: &Html) -> Html { > {left.clone()} @@ -269,6 +276,69 @@ pub fn SplitPane(left: &Html, right: &Html) -> Html { #[styled_component] pub fn App() -> Html { + let text_model = use_state_eq(|| TextModel::create(CONTENT, Some("rust"), None).unwrap()); + let out = use_state_eq(|| String::from(CONTENT)); + + let on_run = { + let text_model = text_model.clone(); + let out = out.clone(); + Callback::from(move |_| { + let text_model = text_model.clone(); + let out = out.clone(); + wasm_bindgen_futures::spawn_local(async move { + let res = Request::post("/api/compile") + .header("Content-Type", "text/plain") + .body(text_model.get_value()) + .unwrap() + .send() + .await + .unwrap(); + match res.status() { + 200 => { + let binary = res.binary().await.unwrap(); + let result = exec_n_get_output(binary).await; + out.set(result); + } + _ => { + let err = format!("Error: {}", res.text().await.unwrap()); + log!("Error", &err); + out.set(err); + } + } + }); + }) + }; + + // https://github.com/siku2/rust-monaco/blob/main/examples/yew_events_keymapping/src/main.rs + let on_editor_created = { + let text_model = text_model.clone(); + let on_run = on_run.clone(); + + let js_closure: Closure = Closure::wrap(Box::new(move || { + on_run.emit(web_sys::MouseEvent::from(JsValue::NULL)); + })); + + // Here we define our callback, we use use_callback as we want to re-render when dependencies change. + // See https://yew.rs/docs/concepts/function-components/state#general-view-of-how-to-store-state + use_callback( + text_model, + move |editor_link: CodeEditorLink, _text_model| { + editor_link.with_editor(|editor| { + // Registers Ctrl/Cmd + Enter hotkey + let keycode = monaco::sys::KeyCode::Enter.to_value() + | (monaco::sys::KeyMod::ctrl_cmd() as u32); + let raw_editor: &IStandaloneCodeEditor = editor.as_ref(); + + raw_editor.add_command( + keycode.into(), + js_closure.as_ref().unchecked_ref(), + None, + ); + }); + }, + ) + }; + html! { <> // Global Styles can be applied with component. @@ -281,13 +351,18 @@ pub fn App() -> Html { } "#)} /> } } - right={html!{}} + left={html!{} } + right={html!{ + + {out.to_string()} + + }} /> } } fn main() { + monaco_conq::register_conq(); yew::Renderer::::new().render(); } diff --git a/conq-er/src/monaco_conq.rs b/conq-er/src/monaco_conq.rs new file mode 100644 index 0000000..c4cc1f2 --- /dev/null +++ b/conq-er/src/monaco_conq.rs @@ -0,0 +1,43 @@ +use js_sys::{Array, Object}; +use monaco::sys::languages::{ILanguageExtensionPoint, LanguageConfiguration}; +use wasm_bindgen::{prelude::*, JsCast, JsValue}; + +pub const ID: &str = "conq"; + +pub fn register_conq() { + monaco::sys::languages::register(&language()); + monaco::sys::languages::set_monarch_tokens_provider(ID, &make_tokens_provider().into()); + monaco::sys::languages::set_language_configuration(ID, &language_configuration()); +} + +fn language() -> ILanguageExtensionPoint { + let lang: ILanguageExtensionPoint = Object::new().unchecked_into(); + lang.set_id(ID); + lang +} + +#[wasm_bindgen(module = "/js/conqMonarchTokensProvider.js")] +extern "C" { + #[wasm_bindgen(js_name = "makeTokensProvider")] + fn make_tokens_provider() -> Object; +} + +fn language_configuration() -> LanguageConfiguration { + let brackets = Array::new_with_length(2); + { + let pair = Array::new_with_length(2); + pair.set(0, JsValue::from_str("(")); + pair.set(1, JsValue::from_str(")")); + brackets.set(0, pair.into()); + } + { + let pair = Array::new_with_length(2); + pair.set(0, JsValue::from_str("{")); + pair.set(1, JsValue::from_str("}")); + brackets.set(1, pair.into()); + } + + let cfg: LanguageConfiguration = Object::new().unchecked_into(); + cfg.set_colorized_bracket_pairs(Some(&brackets)); + cfg +} diff --git a/conq-er/src/output.rs b/conq-er/src/output.rs new file mode 100644 index 0000000..e79a2a9 --- /dev/null +++ b/conq-er/src/output.rs @@ -0,0 +1,50 @@ +use stylist::yew::styled_component; +use yew::prelude::*; +use yew_autoprops::autoprops; + +#[autoprops] +#[styled_component] +pub fn Output(on_run: Callback, children: &Children) -> Html { + html! { +
+
+ +

{"or Press CTRL+Enter / Command+Enter"}

+
+
+
{children.clone()}
+
+
+ } +} diff --git a/conq-er/static/init.cq b/conq-er/static/init.cq new file mode 100644 index 0000000..90c8a41 --- /dev/null +++ b/conq-er/static/init.cq @@ -0,0 +1,9 @@ +fn _start() { + let a = 2; + let b = 3; + b = 5; + let c = a == 2; + if a == 2 { b = 45; } else { b = 67; }; + init_canvas(); + return b; +} \ No newline at end of file diff --git a/conqiler/Cargo.toml b/conqiler/Cargo.toml index 87a4595..74a1a19 100644 --- a/conqiler/Cargo.toml +++ b/conqiler/Cargo.toml @@ -9,3 +9,4 @@ edition = "2021" chumsky = "0.9.3" melior = "0.17.0" rand = "0.8.5" +tempfile = "3.2.0" \ No newline at end of file diff --git a/conqiler/src/ast.rs b/conqiler/src/ast.rs index ce39599..468c5cc 100644 --- a/conqiler/src/ast.rs +++ b/conqiler/src/ast.rs @@ -1,4 +1,3 @@ - #[derive(Debug, PartialEq, Clone)] pub enum Ast { Stmt(Stmt), @@ -45,14 +44,14 @@ pub enum UnaryOp { #[derive(Debug, PartialEq, Clone)] pub enum Value { - Int(i64), - /*Float(f64),*/ + Int(i32), + Float(f64), Bool(bool), String(String), - Void + Void, } -#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Hash, Ord)] +#[derive(Debug, PartialEq, Clone)] pub struct Ident(pub String); #[derive(Debug, PartialEq, Clone)] diff --git a/conqiler/src/compile.rs b/conqiler/src/compile.rs new file mode 100644 index 0000000..3910d6c --- /dev/null +++ b/conqiler/src/compile.rs @@ -0,0 +1,464 @@ +use core::panic; +use std::{collections::HashMap, ops::DerefMut, process::Output, rc::Rc}; + +use crate::type_decl::Type; +use melior::{ + dialect::*, + ir::{self}, + utility::{register_all_dialects, register_all_llvm_translations}, + Context, +}; + +use crate::{ + ast::{Ast, Expr, Ident, Stmt}, + compile_expr::val, +}; + +pub enum NameValue<'c, 'a> { + Value(ir::Value<'c, 'a>, Type), + Function { + name: String, + inputs: Vec>, + outputs: Vec>, + typ: Type, + }, +} + +pub struct Frame<'c, 'a> { + inner: HashMap>, + block: &'a ir::Block<'c>, +} + +impl<'c, 'a> Frame<'c, 'a> { + pub fn new(block: &'a ir::Block<'c>) -> Self { + Self { + inner: HashMap::new(), + block, + } + } + + pub fn decl(&mut self, name: String, val: NameValue<'c, 'a>) -> bool { + self.inner.insert(name, val).is_none() + } +} + +pub struct Environment<'c, 'a> { + frames: Vec>>, +} + +impl<'c, 'a> Environment<'c, 'a> { + pub fn new(block: &'a ir::Block<'c>) -> Self { + Self { + frames: vec![Rc::new(Frame::new(block))], // global frame + } + } + + pub fn extend<'a2: 'a>(&self, block: &'a2 ir::Block<'c>) -> Self { + let mut frames = self.frames.clone(); + frames.push(Rc::new(Frame::new(block))); + Self { frames } + } + + pub fn block(&self) -> &'a ir::Block<'c> { + self.frames.last().unwrap().block + } + + pub fn decl(&mut self, name: String, val: NameValue<'c, 'a>) -> bool { + let rc = self.frames.last_mut().unwrap(); + // this is safe since only the last block (which has no other owners) is being mutated + Rc::get_mut(rc).unwrap().deref_mut().decl(name, val) + } + + pub fn get(&self, name: String) -> Option<&NameValue<'c, 'a>> { + for frame in self.frames.iter().rev() { + if let Some(val) = frame.inner.get(&name) { + return Some(val); + } + } + return None; + } +} + +pub struct Compiler<'a> { + pub ctx: &'a Context, + pub loc: ir::Location<'a>, + pub module: ir::Module<'a>, +} + +impl<'c> Compiler<'c> { + pub fn new(ctx: &'c Context) -> Self { + let loc = ir::Location::unknown(&ctx); + let module = ir::Module::new(loc); + + Self { ctx, loc, module } + } + + pub fn compile_stmt<'a>(&self, env: &mut Environment<'c, 'a>, stmt: Stmt) { + match stmt { + Stmt::Expr(expr) => { + let _ = self.compile_expr(env, expr); + } + Stmt::Let { + name: Ident(name), + expr, + } => { + let (v, vt) = self.compile_expr_val(env, *expr); + + if !env.decl(name.clone(), NameValue::Value(v, vt)) { + panic!("redeclared variable {name}"); + } + } + Stmt::Break => todo!(), + Stmt::Continue => todo!(), + Stmt::Return(expr) => { + let v = self.compile_expr(env, *expr); + if let Some(v) = v { + env.block() + .append_operation(func::r#return(&[v.0], self.loc)); + } else { + env.block().append_operation(func::r#return(&[], self.loc)); + } + } + Stmt::While { pred, body } => { + let before_region = { + let loc = self.loc.clone(); + let region = ir::Region::new(); + let block = region.append_block(ir::Block::new(&[])); + let mut env = env.extend(&block); + let val = self.compile_expr_block_optim(&mut env, *pred); + let val = self.load(&mut env, val.expect("a value is returned").0); + block.append_operation(scf::condition(val, &[], loc)); + region + }; + let after_region = { + let region = ir::Region::new(); + let block = region.append_block(ir::Block::new(&[])); + let mut env = env.extend(&block); + let _val = self.compile_expr_block_optim(&mut env, *body); + block.append_operation(scf::r#yield(&[], self.loc)); + region + }; + + let whl = scf::r#while(&[], &[], before_region, after_region, self.loc); + env.block().append_operation(whl); + } + Stmt::Fn { + name: Ident(name), + params, + expr, + } => { + let arg_types = params + .iter() + .map(|_| self.int_ref_type()) + .collect::>(); + let arg_types_with_loc = arg_types + .iter() + .cloned() + .map(|t| (t, self.loc)) + .collect::>(); + let ret_type = self.int_ref_type(); + + // TODO currently only int params + // TODO currently int return (not even void) + let fn_type = ir::attribute::TypeAttribute::new( + ir::r#type::FunctionType::new(&self.ctx, &arg_types, &[ret_type]).into(), + ); + let fn_attr = ir::attribute::StringAttribute::new(&self.ctx, &name.clone()); + let fn_op = func::func( + &self.ctx, + fn_attr, + fn_type, + { + let region = ir::Region::new(); + let block = ir::Block::new(&arg_types_with_loc); + let mut env = env.extend(&block); + let mut i = 0; + for Ident(p) in params { + let v = block.argument(i).unwrap().into(); + // TODO args + if !env.decl(p.clone(), NameValue::Value(v, Type::Int)) { + panic!("redeclared argument {p}"); + } + i += 1; + } + let _val = self.compile_expr_block_optim(&mut env, *expr); + let block = region.append_block(block); + region + }, + &[], + self.loc, + ); + env.block().append_operation(fn_op); + if !env.decl( + name.clone(), + NameValue::Function { + name: name.clone(), + outputs: vec![ret_type], + // TODO this type ;-; + typ: Type::Function { + args: arg_types.iter().map(|_| Type::Int).collect(), + ret: Some(Box::new(Type::Int)), + }, + inputs: arg_types, + }, + ) { + panic!("redeclared function {name}"); + } + } + } + } + + pub fn define_import( + &self, + env: &mut Environment<'c, 'c>, + name: &str, + args: Vec, + out: Vec, + ) { + let attrs = vec![( + ir::Identifier::new(&self.ctx, "sym_visibility").into(), + ir::attribute::StringAttribute::new(&self.ctx, "private").into(), + )]; + let typed_args = args.iter().map(|x| self.into_type(x)).collect::>(); + let typed_out = out.iter().map(|x| self.into_type(x)).collect::>(); + let fn_type = ir::attribute::TypeAttribute::new( + ir::r#type::FunctionType::new(&self.ctx, &typed_args, &typed_out).into(), + ); + let fn_attr = ir::attribute::StringAttribute::new(&self.ctx, &name.clone()); + let fn_op = func::func( + &self.ctx, + fn_attr, + fn_type, + { + let region = ir::Region::new(); + region + }, + &attrs, + self.loc, + ); + if !env.decl( + name.to_string(), + NameValue::Function { + name: name.to_string(), + inputs: typed_args, + outputs: typed_out, + typ: Type::Function { + args, + ret: out.first().cloned().map(Box::new), + }, + }, + ) { + panic!("redeclared imported function {name}"); + } + self.module.body().append_operation(fn_op); + } + + pub fn define_imported(&self, env: &mut Environment<'c, 'c>) { + self.define_import(env, "init_canvas", vec![], vec![]); + self.define_import(env, "clear_canvas", vec![], vec![]); + self.define_import(env, "line_to", vec![Type::Int, Type::Int], vec![]); + self.define_import(env, "move_to", vec![Type::Int, Type::Int], vec![]); + self.define_import(env, "close_path", vec![], vec![]); + // self.define_import(env, "stroke_style", vec![Type::String], vec![]); + self.define_import(env, "fill", vec![], vec![]); + self.define_import( + env, + "arc", + vec![Type::Int, Type::Int, Type::Int, Type::Int, Type::Int], + vec![], + ); + self.define_import( + env, + "quadratic_curve_to", + vec![Type::Int, Type::Int, Type::Int, Type::Int], + vec![], + ); + self.define_import( + env, + "bezier_curve_to", + vec![ + Type::Int, + Type::Int, + Type::Int, + Type::Int, + Type::Int, + Type::Int, + ], + vec![], + ); + } + + pub fn compile(&self, ast: Expr) { + let gbody = self.module.body(); + let mut env = Environment::new(&gbody); + self.define_imported(&mut env); + + // match ast { + // Ast::Stmt(stmt) => self.compile_stmt(&mut env, stmt), + // Ast::Expr(expr) => drop(self.compile_expr(&mut env, expr)), + // } + drop(self.compile_expr(&mut env, ast)); + } +} + +pub fn setup_ctx() -> Context { + let registry = DialectRegistry::new(); + register_all_dialects(®istry); + let ctx = Context::new(); + ctx.append_dialect_registry(®istry); + ctx.load_all_available_dialects(); + register_all_llvm_translations(&ctx); + ctx +} + +#[cfg(test)] +pub mod tests { + use std::{io::Read, ops::Deref}; + + use melior::{ + dialect::DialectRegistry, + ir::{Block, Operation}, + }; + + use crate::{ast::Value, parser::parse, translate}; + + use super::*; + + #[test] + fn test_name() { + let ctx = setup_ctx(); + + let mut compiler = Compiler::new(&ctx); + let blkref = compiler.module.body(); + let mut env = Environment::new(&blkref); + compiler.compile_stmt( + &mut env, + Stmt::While { + pred: Box::new(Expr::Unary { + op: crate::ast::UnaryOp::Not, + expr: Box::new(Expr::Literal(Value::Bool(true))), + }), + body: Box::new(Expr::Body { + stmts: vec![], + val: Box::new(Expr::Literal(Value::Int(1))), + }), + }, + ); + assert!(compiler.module.as_operation().verify()); + compiler.module.as_operation().dump(); + + let mut wasm_file = translate::compile_program_text(&ctx, &mut compiler.module).unwrap(); + let mut wasm = String::new(); + wasm_file.read_to_string(&mut wasm).unwrap(); + + eprintln!("{}", wasm); + } + + #[test] + fn test_decl_int() { + let ctx = setup_ctx(); + + let mut compiler = Compiler::new(&ctx); + let blkref = compiler.module.body(); + let mut env = Environment::new(&blkref); + let let_a = Stmt::Let { + name: Ident("a".to_string()), + expr: Box::new(Expr::Literal(Value::Int(1))), + }; + let ret_32 = Stmt::Return(Box::new(Expr::Literal(Value::Int(32)))); + compiler.compile_stmt( + &mut env, + Stmt::Fn { + name: Ident("_start".to_string()), + params: vec![Ident("b".to_string())], + expr: Box::new(Expr::Body { + stmts: vec![let_a, ret_32], + val: Box::new(Expr::Literal(Value::Void)), + }), + }, + ); + assert!(compiler.module.as_operation().verify()); + compiler.module.as_operation().dump(); + + let mut output = translate::compile_program_text(&ctx, &mut compiler.module).unwrap(); + let mut o = String::new(); + output.read_to_string(&mut o).unwrap(); + + eprintln!("{}", o); + let mut output = translate::compile_and_run_program(&ctx, &mut compiler.module).unwrap(); + eprintln!("{:?}", output); + } + + #[test] + fn test_decl_int2() { + let ctx = setup_ctx(); + + let mut compiler = Compiler::new(&ctx); + let blkref = compiler.module.body(); + let mut env = Environment::new(&blkref); + let ast = parse( + r#" + fn add(a, b) { + let c = 1; + return c + a + b; + } + fn _start() { + return add(2, 3); + } + "#, + ) + .unwrap(); + eprintln!("{:?}", ast); + // if let crate::ast::Ast::Stmt(stmt) = ast { + // compiler.compile_stmt(&mut env, stmt); + // } else if let crate::ast::Ast::Expr(expr) = ast { + // compiler.compile_expr(&mut env, expr); + // } + compiler.compile_expr(&mut env, ast); + assert!(compiler.module.as_operation().verify()); + compiler.module.as_operation().dump(); + + let mut output = translate::compile_program_text(&ctx, &mut compiler.module).unwrap(); + let mut o = String::new(); + output.read_to_string(&mut o).unwrap(); + + eprintln!("{}", o); + let mut output = translate::compile_and_run_program(&ctx, &mut compiler.module).unwrap(); + eprintln!("{:?}", output); + } + + #[test] + fn test_if() { + let ctx = setup_ctx(); + + let mut compiler = Compiler::new(&ctx); + let blkref = compiler.module.body(); + let mut env = Environment::new(&blkref); + let ast = parse( + r#" + fn _start() { + let a = 2; + let b = 3; + b = 5; + let c = a == 2; + if a == 2 { b = 45; } else { b = 67; }; + init_canvas(); + return b; + } + "#, + ) + .unwrap(); + eprintln!("{:?}", ast); + compiler.compile(ast); + compiler.module.as_operation().dump(); + assert!(compiler.module.as_operation().verify()); + + let mut output = translate::compile_program_text(&ctx, &mut compiler.module).unwrap(); + let mut o = String::new(); + output.read_to_string(&mut o).unwrap(); + + eprintln!("{}", o); + let mut output = translate::compile_and_run_program(&ctx, &mut compiler.module).unwrap(); + eprintln!("{:?}", output); + } +} diff --git a/conqiler/src/compile_expr.rs b/conqiler/src/compile_expr.rs new file mode 100644 index 0000000..76ed448 --- /dev/null +++ b/conqiler/src/compile_expr.rs @@ -0,0 +1,504 @@ +use std::borrow::Borrow; + +use crate::compile::{Compiler, Environment, NameValue}; +use crate::type_decl::Type; +use melior::ir::{block, Block, ValueLike}; +use melior::{dialect::*, ir}; + +use crate::ast::{ArithBinaryOp, CompareBinaryOp, Expr, Ident, LogicalBinaryOp, Stmt, Value}; + +pub fn val<'a, 'i>(x: ir::OperationRef<'a, 'i>) -> ir::Value<'a, 'i> { + x.result(0) + .unwrap_or_else(|_| panic!("this operation has no value: {x}")) + .into() +} + +impl<'c> Compiler<'c> { + pub fn int_type(&self) -> ir::Type<'c> { + ir::r#type::IntegerType::new(&self.ctx, 32).into() + } + + fn bool_type(&self) -> ir::Type<'c> { + ir::r#type::IntegerType::new(&self.ctx, 1).into() + } + + pub fn int_ref_type(&self) -> ir::Type<'c> { + ir::r#type::MemRefType::new(self.int_type(), &[], None, None).into() + } + + pub fn bool_ref_type(&self) -> ir::Type<'c> { + ir::r#type::MemRefType::new(self.bool_type(), &[], None, None).into() + } + + pub fn into_type(&self, t: &Type) -> ir::Type<'c> { + match t { + Type::Int => self.int_ref_type(), + Type::Bool => self.bool_ref_type(), + Type::Function { args, ret } => todo!(), + } + } + + fn assert_int_ref_type(&self, v: ir::Value<'c, '_>) { + if !v.r#type().eq(&self.int_ref_type()) { + panic!("expected i32, got {v}"); + } + } + + fn assert_bool_ref_type(&self, v: ir::Value<'c, '_>) { + if !v.r#type().eq(&self.bool_ref_type()) { + panic!("expected bool, got {v}"); + } + } + + pub fn alloc<'a>( + &self, + env: &mut Environment<'c, 'a>, + inner_typ: ir::Type<'c>, + ) -> ir::Value<'c, 'a> { + let alloca = memref::alloc( + &self.ctx, + ir::r#type::MemRefType::new(inner_typ, &[], None, None), + &[], + &[], + None, + self.loc, + ); + val(env.block().append_operation(alloca)) + } + + pub fn store<'a>( + &self, + env: &mut Environment<'c, 'a>, + memref: ir::Value<'c, 'a>, + v: ir::Value<'c, '_>, + ) { + env.block() + .append_operation(memref::store(v, memref, &[], self.loc)); + } + + pub fn alloc_and_store<'a>( + &self, + env: &mut Environment<'c, 'a>, + v: ir::Value<'c, 'a>, + inner_typ: ir::Type<'c>, + ) -> ir::Value<'c, 'a> { + let alloc_ref = self.alloc(env, inner_typ); + self.store(env, alloc_ref, v); + alloc_ref + } + + pub fn load<'a>( + &self, + env: &mut Environment<'c, 'a>, + memref: ir::Value<'c, 'a>, + ) -> ir::Value<'c, 'a> { + let load = memref::load(memref, &[], self.loc); + let load_ref = val(env.block().append_operation(load)); + load_ref + } + + pub fn compile_literal_int<'a>( + &self, + env: &mut Environment<'c, 'a>, + i: i32, + ) -> (ir::Value<'c, 'a>, Type) { + let cnst = val(env.block().append_operation(arith::constant( + &self.ctx, + ir::attribute::IntegerAttribute::new(self.int_type(), i as i64).into(), + self.loc, + ))); + (self.alloc_and_store(env, cnst, self.int_type()), Type::Int) + } + + pub fn compile_literal_bool<'a>( + &self, + env: &mut Environment<'c, 'a>, + b: bool, + ) -> (ir::Value<'c, 'a>, Type) { + let cnst = val(env.block().append_operation(arith::constant( + &self.ctx, + ir::attribute::IntegerAttribute::new(self.bool_type(), if b { 1 } else { 0 }).into(), + self.loc, + ))); + (self.alloc_and_store(env, cnst, self.bool_type()), Type::Bool) + } + + pub fn compile_unary_not<'a>( + &self, + env: &mut Environment<'c, 'a>, + expr: Expr, + ) -> (ir::Value<'c, 'a>, Type) { + let btrue = self.compile_literal_bool(env, true).0; + let v = self.compile_expr_val(env, expr).0; + self.assert_bool_ref_type(v); + let v = val(env.block().append_operation(arith::xori( + self.load(env, v), + self.load(env, btrue), + self.loc, + ))); + (self.alloc_and_store(env, v, self.bool_type()), Type::Bool) + } + + pub fn compile_unary_neg<'a>( + &self, + env: &mut Environment<'c, 'a>, + expr: Expr, + ) -> (ir::Value<'c, 'a>, Type) { + let ineg1 = self.compile_literal_int(env, -1).0; + let v = self.compile_expr_val(env, expr).0; + self.assert_int_ref_type(v); + let v = val(env.block().append_operation(arith::muli( + self.load(env, v), + self.load(env, ineg1), + self.loc, + ))); + (self.alloc_and_store(env, v, self.int_type()), Type::Int) + } + + pub fn compile_block<'a>( + &self, + env: &mut Environment<'c, 'a>, + new_block: &'a ir::Block<'c>, + stmts: Vec, + expr: Expr, + ) -> Option<(ir::Value<'c, 'a>, Type)> { + let mut env = env.extend(new_block); + for stmt in stmts { + self.compile_stmt(&mut env, stmt); + } + if let Expr::Literal(Value::Void) = expr { + return None; + } + let val = self.compile_expr(&mut env, expr); + val + } + + pub fn compile_expr_block_optim<'a>( + &self, + env: &mut Environment<'c, 'a>, + expr: Expr, + ) -> Option<(ir::Value<'c, 'a>, Type)> { + match expr { + Expr::Body { stmts, val } => { + // we aren't creating a new env here + for stmt in stmts { + self.compile_stmt(env, stmt); + } + if let Expr::Literal(Value::Void) = *val { + return None; + } + self.compile_expr(env, *val) + } + expr => self.compile_expr(env, expr), + } + } + + pub fn compile_arith_binary<'a>( + &self, + env: &mut Environment<'c, 'a>, + op: ArithBinaryOp, + lhs: Expr, + rhs: Expr, + ) -> (ir::Value<'c, 'a>, Type) { + let lhs = self.compile_expr_val(env, lhs).0; + let rhs = self.compile_expr_val(env, rhs).0; + self.assert_int_ref_type(lhs); + self.assert_int_ref_type(rhs); + let lhs = self.load(env, lhs); + let rhs = self.load(env, rhs); + let v = val(env.block().append_operation(match op { + ArithBinaryOp::Add => arith::addi(lhs, rhs, self.loc), + ArithBinaryOp::Sub => arith::subi(lhs, rhs, self.loc), + ArithBinaryOp::Mul => arith::muli(lhs, rhs, self.loc), + ArithBinaryOp::Div => arith::divsi(lhs, rhs, self.loc), + ArithBinaryOp::Mod => arith::remsi(lhs, rhs, self.loc), + })); + (self.alloc_and_store(env, v, self.int_type()), Type::Int) + } + + pub fn compile_compare_binary<'a>( + &self, + env: &mut Environment<'c, 'a>, + op: CompareBinaryOp, + lhs: Expr, + rhs: Expr, + ) -> (ir::Value<'c, 'a>, Type) { + let lhs = self.compile_expr_val(env, lhs).0; + let rhs = self.compile_expr_val(env, rhs).0; + self.assert_int_ref_type(lhs); + self.assert_int_ref_type(rhs); + let lhs = self.load(env, lhs); + let rhs = self.load(env, rhs); + let v = val(env.block().append_operation(match op { + CompareBinaryOp::Gt => { + arith::cmpi(&self.ctx, arith::CmpiPredicate::Sgt, lhs, rhs, self.loc) + } + CompareBinaryOp::Lt => { + arith::cmpi(&self.ctx, arith::CmpiPredicate::Slt, lhs, rhs, self.loc) + } + CompareBinaryOp::Gte => { + arith::cmpi(&self.ctx, arith::CmpiPredicate::Sge, lhs, rhs, self.loc) + } + CompareBinaryOp::Lte => { + arith::cmpi(&self.ctx, arith::CmpiPredicate::Sle, lhs, rhs, self.loc) + } + CompareBinaryOp::Eq => { + arith::cmpi(&self.ctx, arith::CmpiPredicate::Eq, lhs, rhs, self.loc) + } + CompareBinaryOp::Neq => { + arith::cmpi(&self.ctx, arith::CmpiPredicate::Ne, lhs, rhs, self.loc) + } + })); + (self.alloc_and_store(env, v, self.bool_type()), Type::Bool) + } + + pub fn compile_logical_binary<'a>( + &self, + env: &mut Environment<'c, 'a>, + op: LogicalBinaryOp, + lhs: Expr, + rhs: Expr, + ) -> (ir::Value<'c, 'a>, Type) { + let lhs = self.compile_expr_val(env, lhs).0; + let rhs = self.compile_expr_val(env, rhs).0; + self.assert_bool_ref_type(lhs); + self.assert_bool_ref_type(rhs); + let lhs = self.load(env, lhs); + let rhs = self.load(env, rhs); + let v = val(env.block().append_operation(match op { + LogicalBinaryOp::Or => todo!(), // need cond for tihs + LogicalBinaryOp::And => todo!(), + // TODO Xor + })); + (self.alloc_and_store(env, v, self.bool_type()), Type::Bool) + } + + pub fn compile_cond<'a>( + &self, + env: &mut Environment<'c, 'a>, + pred: Expr, + conseq: Expr, + alt: Expr, + ) -> Option<(ir::Value<'c, 'a>, Type)> { + let cond = self.compile_expr_val(env, pred).0; + let cond = self.load(env, cond); + let mut typ = None; + let mut a = None; + + let (then_region, else_region) = { + let then_region = ir::Region::new(); + let then_block = then_region.append_block(ir::Block::new(&[])); + let mut then_env = env.extend(&then_block); + let then_val = self.compile_expr_block_optim(&mut then_env, conseq); + let then_type = then_val.clone().map(|v| v.0.r#type()); + if let Some(then_val) = then_val { + then_block.append_operation(scf::r#yield(&[then_val.0], self.loc)); + a = Some(then_val.1) + } else { + then_block.append_operation(scf::r#yield(&[], self.loc)); + } + + let else_region = ir::Region::new(); + let else_block = else_region.append_block(ir::Block::new(&[])); + let mut else_env = env.extend(&else_block); + let else_val = self.compile_expr_block_optim(&mut else_env, alt); + let else_type = else_val.clone().map(|v| v.0.r#type()); + if let Some(else_val) = else_val { + else_block.append_operation(scf::r#yield(&[else_val.0], self.loc)); + } else { + else_block.append_operation(scf::r#yield(&[], self.loc)); + } + + + if then_type != else_type { + panic!("conditional branches have different type") + } + typ = then_type.map(|a| a.to_string()); + (then_region, else_region) + }; + + let ret_types = typ.map(|s| ir::Type::parse(&self.ctx, &s).unwrap()).into_iter().collect::>(); + let ifop = env.block().append_operation(scf::r#if(cond, &ret_types, then_region, else_region, self.loc)); + if ret_types.is_empty() { + return None; + } + Some((val(ifop), a.unwrap())) + } + + pub fn compile_apply<'a>( + &self, + env: &mut Environment<'c, 'a>, + r#fn: Expr, + mut args: Vec, + ) -> Option<(ir::Value<'c, 'a>, Type)> { + let (fn_val, fn_ty) = self.compile_expr_val(env, r#fn); + eprintln!("fn_ty: {fn_ty:?}"); + + // eprintln!("fn_val: {fn_val}"); + if let Type::Function { args: inp, ret } = fn_ty { + + // let fn_type = ir::r#type::FunctionType::new(ctx, &[self.int_ref_type(), self.int_ref_type()], &[self.int_ref_type()]); + // let add = ir::attribute::FlatSymbolRefAttribute::new(ctx, "add"); + // eprintln!(", opadd: {add}"); + // eprintln!(", fntype: {fn_type}"); + // let op = func::constant(ctx, add, fn_type, self.loc); + // //eprintln!(", op: {op}"); + // let fn_val = val(env.block().append_operation(op.clone())); + //eprintln!(", op: {op}"); + //eprintln!("fn_val: {fn_val}, op: {op}"); + if let Some(Expr::Literal(Value::Void)) = args.first() { + args.remove(0); + } + let args = args + .into_iter() + .map(|arg| self.compile_expr_val(env, arg).0) + .collect::>(); + // TODO add assert + // TODO check number of args + let v = env.block().append_operation(func::call_indirect( + fn_val, + &args, + &ret.iter().map(|t| self.into_type(t)).collect::>(), + self.loc, + )); + + if let Some(ret) = ret { + Some((val(v), *ret)) + } else { + None + } + } else { + panic!("not a func") + } + + } + + pub fn compile_expr_val<'a>( + &self, + env: &mut Environment<'c, 'a>, + expr: Expr, + ) -> (ir::Value<'c, 'a>, Type) { + return self.compile_expr(env, expr).expect("expr value expected"); + } + + pub fn compile_expr<'a>( + &self, + env: &mut Environment<'c, 'a>, + expr: Expr, + ) -> Option<(ir::Value<'c, 'a>, Type)> { + match expr { + Expr::Binary { op, lhs, rhs } => match op { + crate::ast::BinaryOp::Arith(op) => { + Some(self.compile_arith_binary(env, op, *lhs, *rhs)) + } + crate::ast::BinaryOp::Compare(op) => { + Some(self.compile_compare_binary(env, op, *lhs, *rhs)) + } + crate::ast::BinaryOp::Logical(op) => { + Some(self.compile_logical_binary(env, op, *lhs, *rhs)) + } + }, + Expr::Unary { op, expr } => match op { + crate::ast::UnaryOp::Neg => Some(self.compile_unary_neg(env, *expr)), + crate::ast::UnaryOp::Not => Some(self.compile_unary_not(env, *expr)), + }, + Expr::Literal(v) => match v { + crate::ast::Value::Int(i) => Some(self.compile_literal_int(env, i)), + crate::ast::Value::Bool(b) => Some(self.compile_literal_bool(env, b)), + crate::ast::Value::Float(_) => todo!(), + crate::ast::Value::String(_) => todo!(), + crate::ast::Value::Void => todo!(), + }, + Expr::Ident(Ident(name)) => { + + if name.starts_with("host_") { + + } + + let v = env.get(name.clone()); + if let Some(v) = v { + match v { + NameValue::Value(v, vt) => Some((v.clone(), vt.clone())), + NameValue::Function { + name, + inputs, + outputs, + typ, + } => { + let fn_type = + ir::r#type::FunctionType::new(&self.ctx, &inputs, &outputs); + let fn_ref = + ir::attribute::FlatSymbolRefAttribute::new(&self.ctx, name); + let op = func::constant(&self.ctx, fn_ref, fn_type, self.loc); + let fn_val = val(env.block().append_operation(op)); + let body = env.block(); + let gbody = self.module.body(); + eprintln!("body: {body}, gbody: {gbody} name: {name}, inp: {inputs:?}, out: {outputs:?}, typ: {typ:?}"); + Some((fn_val, typ.clone())) + } + } + } else { + panic!("unknown variable {name}"); + } + } + Expr::Assign { + name: Ident(name), + expr, + } => { + let (expr_value, ty) = self.compile_expr_val(env, *expr); + let v = env.get(name.clone()); + if let Some(v) = v { + if let NameValue::Value(v, vt) = v { + if ty != *vt { + panic!("type mismatch"); + } + let v = v.clone(); + let vt = vt.clone(); + let expr_value = self.load(env, expr_value); + self.store(env, v, expr_value); + Some((v.clone(), vt.clone())) + } else { + panic!("not a variable {name}"); + } + } else { + panic!("unknown variable {name}"); + } + } + // TODO create a completely new block for this -_- + Expr::Body { stmts, val } => self.compile_block(env, env.block(), stmts, *val), + Expr::Cond { pred, conseq, alt } => self.compile_cond(env, *pred, *conseq, *alt), + Expr::Apply { r#fn, args } => self.compile_apply(env, *r#fn, args), + } + } +} + +#[cfg(test)] +mod tests { + + use crate::{ + ast::Expr, + compile::{setup_ctx, Compiler, Environment}, + }; + + #[test] + fn test_literal_int() { + let ctx = setup_ctx(); + let compiler = Compiler::new(&ctx); + let global_body = compiler.module.body(); + let mut env = Environment::new(&global_body); + compiler.compile_expr(&mut env, Expr::Literal(crate::ast::Value::Int(1))); + assert!(compiler.module.as_operation().verify()); + compiler.module.as_operation().dump(); + } + + #[test] + fn test_literal_bool() { + let ctx = setup_ctx(); + let compiler = Compiler::new(&ctx); + let global_body = compiler.module.body(); + let mut env = Environment::new(&global_body); + compiler.compile_expr(&mut env, Expr::Literal(crate::ast::Value::Bool(true))); + assert!(compiler.module.as_operation().verify()); + compiler.module.as_operation().dump(); + } +} diff --git a/conqiler/src/lib.rs b/conqiler/src/lib.rs new file mode 100644 index 0000000..2374d2e --- /dev/null +++ b/conqiler/src/lib.rs @@ -0,0 +1,7 @@ +mod ast; +mod compile_expr; +mod type_decl; + +pub mod parser; +pub mod compile; +pub mod translate; \ No newline at end of file diff --git a/conqiler/src/main.rs b/conqiler/src/main.rs index 9c825e0..3926fe6 100644 --- a/conqiler/src/main.rs +++ b/conqiler/src/main.rs @@ -1,3 +1,5 @@ +use std::{error::Error, io::Read}; + use melior::{ dialect::{arith, func, DialectRegistry}, ir::{ @@ -5,23 +7,28 @@ use melior::{ r#type::FunctionType, *, }, - utility::register_all_dialects, + utility::{register_all_dialects, register_all_llvm_translations}, Context, }; mod ast; +mod compile; +mod compile_expr; mod parser; +mod translate; +mod type_decl; -fn main() { +fn main() -> Result<(), Box> { let registry = DialectRegistry::new(); register_all_dialects(®istry); let context = Context::new(); context.append_dialect_registry(®istry); context.load_all_available_dialects(); + register_all_llvm_translations(&context); let location = Location::unknown(&context); - let module = Module::new(location); + let mut module = Module::new(location); let index_type = Type::index(&context); @@ -50,7 +57,10 @@ fn main() { location, )); - assert!(module.as_operation().verify()); - let llir = module.as_operation().to_string(); - println!("{}", llir); -} + let mut wasm_file = translate::compile_program_text(&context, &mut module)?; + let mut wasm = String::new(); + wasm_file.read_to_string(&mut wasm)?; + + println!("{}", wasm); + Ok(()) +} \ No newline at end of file diff --git a/conqiler/src/parser.rs b/conqiler/src/parser.rs index 92a3ee9..b9b43e3 100644 --- a/conqiler/src/parser.rs +++ b/conqiler/src/parser.rs @@ -1,739 +1,739 @@ -use chumsky::prelude::*; - -use crate::ast::*; - -#[derive(Clone, Debug, PartialEq, Hash, Eq)] -pub enum Token { - Bool(bool), - Num(i64), - Str(String), - Op(String), - Ctrl(char), - Ident(String), - Fn, - Let, - If, - Else, - While, - Return, - Break, - Continue, -} - -// One iterative pass to prevent keyword capture -fn lexer() -> impl Parser, Error = Simple> { - let num = text::int(10).map(|s: String| Token::Num(s.parse().unwrap())); - let str_ = none_of('"') - .repeated() - .delimited_by(just('"'), just('"')) - .map(|v: Vec| Token::Str(v.into_iter().collect())); - - // gotta be a better way to do this - let op = choice(( - just("=="), - just("!="), - just(">="), - just("<="), - just("&&"), - just("||"), - just("-"), - just("+"), - just("*"), - just("/"), - just("%"), - just("="), - just(">"), - just("<"), - just("!"), - )) - .map(|s: &str| Token::Op(s.to_string())); - - let ctrl = one_of("(){};,").map(Token::Ctrl); - let ident = text::ident().map(|ident: String| match ident.as_str() { - "true" => Token::Bool(true), - "false" => Token::Bool(false), - "fn" => Token::Fn, - "let" => Token::Let, - "if" => Token::If, - "else" => Token::Else, - "while" => Token::While, - "return" => Token::Return, - "break" => Token::Break, - "continue" => Token::Continue, - _ => Token::Ident(ident), - }); - - let token = num.or(str_).or(op).or(ctrl).or(ident).padded(); - - let comment = just("//").then(none_of('\n').repeated()).padded(); - - token.padded_by(comment.repeated()).padded().repeated() -} - -pub fn parser() -> impl Parser> { - let ident = select! { - Token::Ident(s) => Ident(s), - }; - - let val = select! { - Token::Bool(b) => Value::Bool(b), - Token::Num(n) => Value::Int(n), - Token::Str(s) => Value::String(s), - } - .map(Expr::Literal); - - recursive(|expr| { - let scoped_block = expr - .clone() - .delimited_by(just(Token::Ctrl('{')), just(Token::Ctrl('}'))); - - let atom = val - .or(expr - .clone() - .delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')')))) - .or(scoped_block.clone()) - .or(ident.clone().map(Expr::Ident)); - - let apply = atom - .clone() - .then( - expr.clone() - .separated_by(just(Token::Ctrl(','))) - .allow_trailing() - .delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')'))) - .collect::>() - .repeated(), - ) - .foldl(|r#fn, args| Expr::Apply { - r#fn: Box::new(r#fn), - args, - }); - - // http://en.wikipedia.org/wiki/Order_of_operations#Programming_languages - let op = choice(( - just(Token::Op("-".to_string())).to(UnaryOp::Neg), - just(Token::Op("!".to_string())).to(UnaryOp::Not), - )); - - let unary = op.repeated().then(apply).foldr(|op, rhs| Expr::Unary { - op, - expr: Box::new(rhs), - }); - - let op = choice(( - just(Token::Op("%".to_string())).to(ArithBinaryOp::Mod), - just(Token::Op("*".to_string())).to(ArithBinaryOp::Mul), - just(Token::Op("/".to_string())).to(ArithBinaryOp::Div), - )); - - let product = unary - .clone() - .then(op.then(unary).repeated()) - .foldl(|lhs, (op, rhs)| Expr::Binary { - op: BinaryOp::Arith(op), - lhs: Box::new(lhs), - rhs: Box::new(rhs), - }); - - let op = choice(( - just(Token::Op("+".to_string())).to(ArithBinaryOp::Add), - just(Token::Op("-".to_string())).to(ArithBinaryOp::Sub), - )); - let sum = product - .clone() - .then(op.then(product).repeated()) - .foldl(|lhs, (op, rhs)| Expr::Binary { - op: BinaryOp::Arith(op), - lhs: Box::new(lhs), - rhs: Box::new(rhs), - }); - - let op = choice(( - just(Token::Op(">".to_string())).to(CompareBinaryOp::Gt), - just(Token::Op("<".to_string())).to(CompareBinaryOp::Lt), - just(Token::Op(">=".to_string())).to(CompareBinaryOp::Gte), - just(Token::Op("<=".to_string())).to(CompareBinaryOp::Lte), - just(Token::Op("==".to_string())).to(CompareBinaryOp::Eq), - just(Token::Op("!=".to_string())).to(CompareBinaryOp::Neq), - )); - let comp = sum - .clone() - .then(op.then(sum).repeated()) - .foldl(|lhs, (op, rhs)| Expr::Binary { - op: BinaryOp::Compare(op), - lhs: Box::new(lhs), - rhs: Box::new(rhs), - }); - - let op = choice(( - just(Token::Op("&&".to_string())).to(LogicalBinaryOp::And), - just(Token::Op("||".to_string())).to(LogicalBinaryOp::Or), - )); - - let logical = comp - .clone() - .then(op.then(comp).repeated()) - .foldl(|lhs, (op, rhs)| Expr::Binary { - op: BinaryOp::Logical(op), - lhs: Box::new(lhs), - rhs: Box::new(rhs), - }); - - let inline_expr = logical; - - let decl = just(Token::Let) - .ignore_then(ident.clone()) - .then_ignore(just(Token::Op("=".to_string()))) - .then(inline_expr.clone()) - .then_ignore(just(Token::Ctrl(';'))) - .map(|(name, expr)| Stmt::Let { - name, - expr: Box::new(expr), - }); - - let assign = ident - .clone() - .then_ignore(just(Token::Op("=".to_string()))) - .then(inline_expr.clone()) - .map(|(name, expr)| Expr::Assign { - name, - expr: Box::new(expr), - }); - - let cond = just(Token::If) - .ignore_then(expr.clone()) - .then(scoped_block.clone()) - .then(just(Token::Else).ignore_then(scoped_block.clone()).or_not()) - .map(|((pred, conseq), alt)| Expr::Cond { - pred: Box::new(pred), - conseq: Box::new(conseq), - alt: Box::new(alt.unwrap_or(Expr::Literal(Value::Void))), - }); - - let while_ = just(Token::While) - .ignore_then(expr.clone()) - .then(scoped_block.clone()) - .map(|(pred, body)| Stmt::While { - pred: Box::new(pred), - body: Box::new(body), - }); - - let fn_decl = just(Token::Fn) - .ignore_then(ident.clone()) - .then( - ident - .clone() - .separated_by(just(Token::Ctrl(','))) - .allow_trailing() - .collect() - .delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')'))), - ) - .then(scoped_block.clone()) - .map(|((name, params), body)| Stmt::Fn { - name, - params, - expr: Box::new(body), - }); - - let jump = choice(( - just(Token::Return) - .ignore_then(inline_expr.clone().or_not()) - .map(|expr| Stmt::Return(Box::new(expr.unwrap_or(Expr::Literal(Value::Void))))), - just(Token::Break).to(Stmt::Break), - just(Token::Continue).to(Stmt::Continue), - )) - .then_ignore(just(Token::Ctrl(';'))); - - // Note: order matters: put less specific parsers lower down the chain - let expr_ = cond.or(assign).or(inline_expr); - - // Same here - let stmt = jump.or(decl).or(while_).or(fn_decl).or(expr_ - .clone() - .then_ignore(just(Token::Ctrl(';'))) - .map(Stmt::Expr)); - - let block = recursive(|block| { - let block = block.delimited_by(just(Token::Ctrl('{')), just(Token::Ctrl('}'))); - stmt.or(block.clone().map(Stmt::Expr)) - .repeated() - .then((expr_.or(block)).or_not()) - .map(|(stmts, val)| { - if stmts.is_empty() { - val.unwrap_or(Expr::Literal(Value::Void)) - } else { - Expr::Body { - stmts, - val: Box::new(val.unwrap_or(Expr::Literal(Value::Void))), - } - } - }) - }); - block - }) - .then_ignore(end()) -} - -pub fn parse(s: &str) -> Ast { - Ast::Expr(parser().parse(lexer().parse(s).unwrap()).unwrap()) -} - -const EXAMPLE: &str = r#" - canvas_init(); - - let rad_delta = 1; - let rad_angle = 0; - let radius = 20; - - while rad_angle <= 2 * math_pi() { - let x = radius * math_cos(rad_angle); - let y = radius * math_sin(rad_angle); - canvas_draw_point(x, y); - rad_angle = rad_angle + rad_delta; - } -"#; - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn lex() { - assert_eq!( - lexer().parse(EXAMPLE).unwrap(), - [ - Token::Ident("canvas_init".to_string()), - Token::Ctrl('('), - Token::Ctrl(')'), - Token::Ctrl(';'), - Token::Let, - Token::Ident("rad_delta".to_string()), - Token::Op("=".to_string()), - Token::Num(1), - Token::Ctrl(';'), - Token::Let, - Token::Ident("rad_angle".to_string()), - Token::Op("=".to_string()), - Token::Num(0), - Token::Ctrl(';'), - Token::Let, - Token::Ident("radius".to_string()), - Token::Op("=".to_string()), - Token::Num(20), - Token::Ctrl(';'), - Token::While, - Token::Ident("rad_angle".to_string()), - Token::Op("<=".to_string()), - Token::Num(2), - Token::Op("*".to_string()), - Token::Ident("math_pi".to_string()), - Token::Ctrl('('), - Token::Ctrl(')'), - Token::Ctrl('{'), - Token::Let, - Token::Ident("x".to_string()), - Token::Op("=".to_string()), - Token::Ident("radius".to_string()), - Token::Op("*".to_string()), - Token::Ident("math_cos".to_string()), - Token::Ctrl('('), - Token::Ident("rad_angle".to_string()), - Token::Ctrl(')'), - Token::Ctrl(';'), - Token::Let, - Token::Ident("y".to_string()), - Token::Op("=".to_string()), - Token::Ident("radius".to_string()), - Token::Op("*".to_string()), - Token::Ident("math_sin".to_string()), - Token::Ctrl('('), - Token::Ident("rad_angle".to_string()), - Token::Ctrl(')'), - Token::Ctrl(';'), - Token::Ident("canvas_draw_point".to_string()), - Token::Ctrl('('), - Token::Ident("x".to_string()), - Token::Ctrl(','), - Token::Ident("y".to_string()), - Token::Ctrl(')'), - Token::Ctrl(';'), - Token::Ident("rad_angle".to_string()), - Token::Op("=".to_string()), - Token::Ident("rad_angle".to_string()), - Token::Op("+".to_string()), - Token::Ident("rad_delta".to_string()), - Token::Ctrl(';'), - Token::Ctrl('}'), - ] - ); - } - - #[test] - fn parse_atom() { - assert_eq!( - parse(" 505 "), - Ast::Expr(Expr::Literal(Value::Int(505))) - ); - assert_eq!( - parse(" true "), - Ast::Expr(Expr::Literal(Value::Bool(true))) - ); - assert_eq!( - parse(" false "), - Ast::Expr(Expr::Literal(Value::Bool(false))) - ); - assert_eq!( - parse(" ! true "), - Ast::Expr(Expr::Unary { - op: UnaryOp::Not, - expr: Box::new(Expr::Literal(Value::Bool(true))) - }) - ); - assert_eq!( - parse(" \"hello\" "), - Ast::Expr(Expr::Literal(Value::String("hello".to_string()))) - ); - } - - #[test] - fn parse_neg() { - assert_eq!( - parse(" ---505 "), - Ast::Expr(Expr::Unary { - op: UnaryOp::Neg, - expr: Box::new(Expr::Unary { - op: UnaryOp::Neg, - expr: Box::new(Expr::Unary { - op: UnaryOp::Neg, - expr: Box::new(Expr::Literal(Value::Int(505))), - }), - }) - }) - ); - } - - #[test] - fn parse_arith() { - assert_eq!( - parse(" 1 + 2 * ( (3 - 4) / 5 ) % 6 "), - // sub -> div -> mul -> mod -> add - Ast::Expr(Expr::Binary { - op: BinaryOp::Arith(ArithBinaryOp::Add), - lhs: Box::new(Expr::Literal(Value::Int(1))), - rhs: Box::new(Expr::Binary { - op: BinaryOp::Arith(ArithBinaryOp::Mod), - lhs: Box::new(Expr::Binary { - op: BinaryOp::Arith(ArithBinaryOp::Mul), - lhs: Box::new(Expr::Literal(Value::Int(2))), - rhs: Box::new(Expr::Binary { - op: BinaryOp::Arith(ArithBinaryOp::Div), - lhs: Box::new(Expr::Binary { - op: BinaryOp::Arith(ArithBinaryOp::Sub), - lhs: Box::new(Expr::Literal(Value::Int(3))), - rhs: Box::new(Expr::Literal(Value::Int(4))), - }), - rhs: Box::new(Expr::Literal(Value::Int(5))), - }), - }), - rhs: Box::new(Expr::Literal(Value::Int(6))), - }), - }) - ); - } - - #[test] - fn parse_logical_compare() { - assert_eq!( - parse(" 1 < 2 && 3 > 4 || false != true"), - Ast::Expr(Expr::Binary { - op: BinaryOp::Logical(LogicalBinaryOp::Or), - lhs: Box::new(Expr::Binary { - op: BinaryOp::Logical(LogicalBinaryOp::And), - lhs: Box::new(Expr::Binary { - op: BinaryOp::Compare(CompareBinaryOp::Lt), - lhs: Box::new(Expr::Literal(Value::Int(1))), - rhs: Box::new(Expr::Literal(Value::Int(2))), - }), - rhs: Box::new(Expr::Binary { - op: BinaryOp::Compare(CompareBinaryOp::Gt), - lhs: Box::new(Expr::Literal(Value::Int(3))), - rhs: Box::new(Expr::Literal(Value::Int(4))), - }), - }), - rhs: Box::new(Expr::Binary { - op: BinaryOp::Compare(CompareBinaryOp::Neq), - lhs: Box::new(Expr::Literal(Value::Bool(false))), - rhs: Box::new(Expr::Literal(Value::Bool(true))), - }), - }) - ); - } - - #[test] - fn parse_ident_let() { - assert_eq!( - parse("let a = 0; a = 1 ; a + 4"), - Ast::Expr(Expr::Body { - stmts: vec![ - Stmt::Let { - name: Ident("a".to_string()), - expr: Box::new(Expr::Literal(Value::Int(0))), - }, - Stmt::Expr(Expr::Assign { - name: Ident("a".to_string()), - expr: Box::new(Expr::Literal(Value::Int(1))), - }), - ], - val: Box::new(Expr::Binary { - op: BinaryOp::Arith(ArithBinaryOp::Add), - lhs: Box::new(Expr::Ident(Ident("a".to_string()))), - rhs: Box::new(Expr::Literal(Value::Int(4))), - }), - }) - ); - } - - #[test] - fn parse_block() { - assert_eq!( - parse( - r#" - { - 1 + 2 - } - let a = { - let b = 0; - a = 1; - a + b - }; - a - "# - ), - Ast::Expr(Expr::Body { - stmts: vec![ - Stmt::Expr(Expr::Binary { - op: BinaryOp::Arith(ArithBinaryOp::Add), - lhs: Box::new(Expr::Literal(Value::Int(1))), - rhs: Box::new(Expr::Literal(Value::Int(2))), - }), - Stmt::Let { - name: Ident("a".to_string()), - expr: Box::new(Expr::Body { - stmts: vec![ - Stmt::Let { - name: Ident("b".to_string()), - expr: Box::new(Expr::Literal(Value::Int(0))), - }, - Stmt::Expr(Expr::Assign { - name: Ident("a".to_string()), - expr: Box::new(Expr::Literal(Value::Int(1))), - }), - ], - val: Box::new(Expr::Binary { - op: BinaryOp::Arith(ArithBinaryOp::Add), - lhs: Box::new(Expr::Ident(Ident("a".to_string()))), - rhs: Box::new(Expr::Ident(Ident("b".to_string()))), - }), - }), - }, - ], - val: Box::new(Expr::Ident(Ident("a".to_string()))), - }) - ); - } - - #[test] - fn parse_cond() { - assert_eq!( - parse( - r#" - if true { - 1 - } else { - 0 - } - "# - ), - Ast::Expr(Expr::Cond { - pred: Box::new(Expr::Literal(Value::Bool(true))), - conseq: Box::new(Expr::Literal(Value::Int(1))), - alt: Box::new(Expr::Literal(Value::Int(0))), - }) - ); - } - - #[test] - fn parse_while() { - assert_eq!( - parse( - r#" - while test(true) == 1 { - 1 - } - "# - ), - Ast::Expr(Expr::Body { - stmts: vec![Stmt::While { - pred: Box::new(Expr::Binary { - op: BinaryOp::Compare(CompareBinaryOp::Eq), - lhs: Box::new(Expr::Apply { - r#fn: Box::new(Expr::Ident(Ident("test".to_string()))), - args: vec![Expr::Literal(Value::Bool(true))], - }), - rhs: Box::new(Expr::Literal(Value::Int(1))), - }), - body: Box::new(Expr::Literal(Value::Int(1))), - },], - val: Box::new(Expr::Literal(Value::Void)), - }) - ); - } - - #[test] - fn parse_fn_apply() { - assert_eq!( - parse( - r#" - fn add(a, b) { - fn add2 (a, b) { - let c = 1; - a + b - } - return add2(a, b); - } - add(1, 2)() - "# - ), - Ast::Expr(Expr::Body { - stmts: vec![Stmt::Fn { - name: Ident("add".to_string()), - params: vec![Ident("a".to_string()), Ident("b".to_string())], - expr: Box::new(Expr::Body { - stmts: vec![ - Stmt::Fn { - name: Ident("add2".to_string()), - params: vec![Ident("a".to_string()), Ident("b".to_string())], - expr: Box::new(Expr::Body { - stmts: vec![Stmt::Let { - name: Ident("c".to_string()), - expr: Box::new(Expr::Literal(Value::Int(1))), - },], - val: Box::new(Expr::Binary { - op: BinaryOp::Arith(ArithBinaryOp::Add), - lhs: Box::new(Expr::Ident(Ident("a".to_string()))), - rhs: Box::new(Expr::Ident(Ident("b".to_string()))), - }), - }), - }, - Stmt::Return(Box::new(Expr::Apply { - r#fn: Box::new(Expr::Ident(Ident("add2".to_string()))), - args: vec![ - Expr::Ident(Ident("a".to_string())), - Expr::Ident(Ident("b".to_string())), - ], - })), - ], - val: Box::new(Expr::Literal(Value::Void)), - }), - }], - val: Box::new(Expr::Apply { - r#fn: Box::new(Expr::Apply { - r#fn: Box::new(Expr::Ident(Ident("add".to_string()))), - args: vec![Expr::Literal(Value::Int(1)), Expr::Literal(Value::Int(2))], - }), - args: vec![Expr::Literal(Value::Void)], - }), - }) - ); - } - - #[test] - fn parse_example() { - assert_eq!( - parse(EXAMPLE), - Ast::Expr(Expr::Body { - stmts: vec![ - Stmt::Expr(Expr::Apply { - r#fn: Box::new(Expr::Ident(Ident("canvas_init".to_string()))), - args: vec![Expr::Literal(Value::Void)], - }), - Stmt::Let { - name: Ident("rad_delta".to_string()), - expr: Box::new(Expr::Literal(Value::Int(1))), - }, - Stmt::Let { - name: Ident("rad_angle".to_string()), - expr: Box::new(Expr::Literal(Value::Int(0))), - }, - Stmt::Let { - name: Ident("radius".to_string()), - expr: Box::new(Expr::Literal(Value::Int(20))), - }, - Stmt::While { - pred: Box::new(Expr::Binary { - op: BinaryOp::Compare(CompareBinaryOp::Lte), - lhs: Box::new(Expr::Ident(Ident("rad_angle".to_string()))), - rhs: Box::new(Expr::Binary { - op: BinaryOp::Arith(ArithBinaryOp::Mul), - lhs: Box::new(Expr::Literal(Value::Int(2))), - rhs: Box::new(Expr::Apply { - r#fn: Box::new(Expr::Ident(Ident("math_pi".to_string()))), - args: vec![Expr::Literal(Value::Void)], - }), - }), - }), - body: Box::new(Expr::Body { - stmts: vec![ - Stmt::Let { - name: Ident("x".to_string()), - expr: Box::new(Expr::Binary { - op: BinaryOp::Arith(ArithBinaryOp::Mul), - lhs: Box::new(Expr::Ident(Ident("radius".to_string()))), - rhs: Box::new(Expr::Apply { - r#fn: Box::new(Expr::Ident(Ident( - "math_cos".to_string() - ))), - args: vec![Expr::Ident(Ident("rad_angle".to_string()))], - }), - }), - }, - Stmt::Let { - name: Ident("y".to_string()), - expr: Box::new(Expr::Binary { - op: BinaryOp::Arith(ArithBinaryOp::Mul), - lhs: Box::new(Expr::Ident(Ident("radius".to_string()))), - rhs: Box::new(Expr::Apply { - r#fn: Box::new(Expr::Ident(Ident( - "math_sin".to_string() - ))), - args: vec![Expr::Ident(Ident("rad_angle".to_string()))], - }), - }), - }, - Stmt::Expr(Expr::Apply { - r#fn: Box::new(Expr::Ident(Ident( - "canvas_draw_point".to_string() - ))), - args: vec![ - Expr::Ident(Ident("x".to_string())), - Expr::Ident(Ident("y".to_string())), - ], - }), - Stmt::Expr(Expr::Assign { - name: Ident("rad_angle".to_string()), - expr: Box::new(Expr::Binary { - op: BinaryOp::Arith(ArithBinaryOp::Add), - lhs: Box::new(Expr::Ident(Ident("rad_angle".to_string()))), - rhs: Box::new(Expr::Ident(Ident("rad_delta".to_string()))), - }), - }), - ], - val: Box::new(Expr::Literal(Value::Void)), - }), - }, - ], - val: Box::new(Expr::Literal(Value::Void)), - }) - ); - } -} +use chumsky::{error::Cheap, prelude::*}; + +use crate::ast::*; + +#[derive(Clone, Debug, PartialEq)] +pub enum Token { + Bool(bool), + Num(i32), + Float(f64), + Str(String), + Op(String), + Ctrl(char), + Ident(String), + Fn, + Let, + If, + Else, + While, + Return, + Break, + Continue, +} + +// One iterative pass to prevent keyword capture +fn lexer() -> impl Parser, Error = Cheap> { + let num = text::int(10) + .then(just('.').ignore_then(text::digits(10)).or_not()) + .map(|(int, frac)| { + match frac { + Some(frac) => Token::Float(format!("{}.{}", int, frac).parse().unwrap()), + None => Token::Num(int.parse().unwrap()), + } + }); + let str_ = none_of('"') + .repeated() + .delimited_by(just('"'), just('"')) + .map(|v: Vec| Token::Str(v.into_iter().collect())); + + // gotta be a better way to do this + let op = choice(( + just("=="), + just("!="), + just(">="), + just("<="), + just("&&"), + just("||"), + just("-"), + just("+"), + just("*"), + just("/"), + just("%"), + just("="), + just(">"), + just("<"), + just("!"), + )) + .map(|s: &str| Token::Op(s.to_string())); + + let ctrl = one_of("(){};,").map(Token::Ctrl); + let ident = text::ident().map(|ident: String| match ident.as_str() { + "true" => Token::Bool(true), + "false" => Token::Bool(false), + "fn" => Token::Fn, + "let" => Token::Let, + "if" => Token::If, + "else" => Token::Else, + "while" => Token::While, + "return" => Token::Return, + "break" => Token::Break, + "continue" => Token::Continue, + _ => Token::Ident(ident), + }); + + let token = num.or(str_).or(op).or(ctrl).or(ident).padded(); + + let comment = just("//").then(none_of('\n').repeated()).padded(); + + token.padded_by(comment.repeated()).padded().repeated() +} + +pub fn parser() -> impl Parser> { + let ident = select! { + Token::Ident(s) => Ident(s), + }; + + let val = select! { + Token::Bool(b) => Value::Bool(b), + Token::Num(n) => Value::Int(n), + Token::Float(f) => Value::Float(f), + Token::Str(s) => Value::String(s), + } + .map(Expr::Literal); + + recursive(|expr| { + let scoped_block = expr + .clone() + .delimited_by(just(Token::Ctrl('{')), just(Token::Ctrl('}'))); + + let atom = val + .or(expr + .clone() + .delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')')))) + .or(scoped_block.clone()) + .or(ident.clone().map(Expr::Ident)); + + let apply = atom + .clone() + .then( + expr.clone() + .separated_by(just(Token::Ctrl(','))) + .allow_trailing() + .delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')'))) + .collect::>() + .repeated(), + ) + .foldl(|r#fn, args| Expr::Apply { + r#fn: Box::new(r#fn), + args, + }); + + // http://en.wikipedia.org/wiki/Order_of_operations#Programming_languages + let op = choice(( + just(Token::Op("-".to_string())).to(UnaryOp::Neg), + just(Token::Op("!".to_string())).to(UnaryOp::Not), + )); + + let unary = op.repeated().then(apply).foldr(|op, rhs| Expr::Unary { + op, + expr: Box::new(rhs), + }); + + let op = choice(( + just(Token::Op("%".to_string())).to(ArithBinaryOp::Mod), + just(Token::Op("*".to_string())).to(ArithBinaryOp::Mul), + just(Token::Op("/".to_string())).to(ArithBinaryOp::Div), + )); + + let product = unary + .clone() + .then(op.then(unary).repeated()) + .foldl(|lhs, (op, rhs)| Expr::Binary { + op: BinaryOp::Arith(op), + lhs: Box::new(lhs), + rhs: Box::new(rhs), + }); + + let op = choice(( + just(Token::Op("+".to_string())).to(ArithBinaryOp::Add), + just(Token::Op("-".to_string())).to(ArithBinaryOp::Sub), + )); + let sum = product + .clone() + .then(op.then(product).repeated()) + .foldl(|lhs, (op, rhs)| Expr::Binary { + op: BinaryOp::Arith(op), + lhs: Box::new(lhs), + rhs: Box::new(rhs), + }); + + let op = choice(( + just(Token::Op(">".to_string())).to(CompareBinaryOp::Gt), + just(Token::Op("<".to_string())).to(CompareBinaryOp::Lt), + just(Token::Op(">=".to_string())).to(CompareBinaryOp::Gte), + just(Token::Op("<=".to_string())).to(CompareBinaryOp::Lte), + just(Token::Op("==".to_string())).to(CompareBinaryOp::Eq), + just(Token::Op("!=".to_string())).to(CompareBinaryOp::Neq), + )); + let comp = sum + .clone() + .then(op.then(sum).repeated()) + .foldl(|lhs, (op, rhs)| Expr::Binary { + op: BinaryOp::Compare(op), + lhs: Box::new(lhs), + rhs: Box::new(rhs), + }); + + let op = choice(( + just(Token::Op("&&".to_string())).to(LogicalBinaryOp::And), + just(Token::Op("||".to_string())).to(LogicalBinaryOp::Or), + )); + + let logical = comp + .clone() + .then(op.then(comp).repeated()) + .foldl(|lhs, (op, rhs)| Expr::Binary { + op: BinaryOp::Logical(op), + lhs: Box::new(lhs), + rhs: Box::new(rhs), + }); + + let inline_expr = logical; + + let decl = just(Token::Let) + .ignore_then(ident.clone()) + .then_ignore(just(Token::Op("=".to_string()))) + .then(inline_expr.clone()) + .then_ignore(just(Token::Ctrl(';'))) + .map(|(name, expr)| Stmt::Let { + name, + expr: Box::new(expr), + }); + + let assign = ident + .clone() + .then_ignore(just(Token::Op("=".to_string()))) + .then(inline_expr.clone()) + .map(|(name, expr)| Expr::Assign { + name, + expr: Box::new(expr), + }); + + let cond = just(Token::If) + .ignore_then(expr.clone()) + .then(scoped_block.clone()) + .then(just(Token::Else).ignore_then(scoped_block.clone()).or_not()) + .map(|((pred, conseq), alt)| Expr::Cond { + pred: Box::new(pred), + conseq: Box::new(conseq), + alt: Box::new(alt.unwrap_or(Expr::Literal(Value::Void))), + }); + + let while_ = just(Token::While) + .ignore_then(expr.clone()) + .then(scoped_block.clone()) + .map(|(pred, body)| Stmt::While { + pred: Box::new(pred), + body: Box::new(body), + }); + + let fn_decl = just(Token::Fn) + .ignore_then(ident.clone()) + .then( + ident + .clone() + .separated_by(just(Token::Ctrl(','))) + .allow_trailing() + .collect() + .delimited_by(just(Token::Ctrl('(')), just(Token::Ctrl(')'))), + ) + .then(scoped_block.clone()) + .map(|((name, params), body)| Stmt::Fn { + name, + params, + expr: Box::new(body), + }); + + let jump = choice(( + just(Token::Return) + .ignore_then(inline_expr.clone().or_not()) + .map(|expr| Stmt::Return(Box::new(expr.unwrap_or(Expr::Literal(Value::Void))))), + just(Token::Break).to(Stmt::Break), + just(Token::Continue).to(Stmt::Continue), + )) + .then_ignore(just(Token::Ctrl(';'))); + + // Note: order matters: put less specific parsers lower down the chain + let expr_ = cond.or(assign).or(inline_expr); + + // Same here + let stmt = jump.or(decl).or(while_).or(fn_decl).or(expr_ + .clone() + .then_ignore(just(Token::Ctrl(';'))) + .map(Stmt::Expr)); + + let block = recursive(|block| { + let block = block.delimited_by(just(Token::Ctrl('{')), just(Token::Ctrl('}'))); + stmt.or(block.clone().map(Stmt::Expr)) + .repeated() + .then((expr_.or(block)).or_not()) + .map(|(stmts, val)| { + if stmts.is_empty() { + val.unwrap_or(Expr::Literal(Value::Void)) + } else { + Expr::Body { + stmts, + val: Box::new(val.unwrap_or(Expr::Literal(Value::Void))), + } + } + }) + }); + block + }) + .then_ignore(end()) +} + +pub fn parse(s: &str) -> Result>> { + parser().parse(lexer().parse(s).unwrap()) +} + +const EXAMPLE: &str = r#" + canvas_init(); + + let rad_delta = 1; + let rad_angle = 0; + let radius = 20; + + while rad_angle <= 2 * math_pi() { + let x = radius * math_cos(rad_angle); + let y = radius * math_sin(rad_angle); + canvas_draw_point(x, y); + rad_angle = rad_angle + rad_delta; + } +"#; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn lex() { + assert_eq!( + lexer().parse(EXAMPLE).unwrap(), + [ + Token::Ident("canvas_init".to_string()), + Token::Ctrl('('), + Token::Ctrl(')'), + Token::Ctrl(';'), + Token::Let, + Token::Ident("rad_delta".to_string()), + Token::Op("=".to_string()), + Token::Num(1), + Token::Ctrl(';'), + Token::Let, + Token::Ident("rad_angle".to_string()), + Token::Op("=".to_string()), + Token::Num(0), + Token::Ctrl(';'), + Token::Let, + Token::Ident("radius".to_string()), + Token::Op("=".to_string()), + Token::Num(20), + Token::Ctrl(';'), + Token::While, + Token::Ident("rad_angle".to_string()), + Token::Op("<=".to_string()), + Token::Num(2), + Token::Op("*".to_string()), + Token::Ident("math_pi".to_string()), + Token::Ctrl('('), + Token::Ctrl(')'), + Token::Ctrl('{'), + Token::Let, + Token::Ident("x".to_string()), + Token::Op("=".to_string()), + Token::Ident("radius".to_string()), + Token::Op("*".to_string()), + Token::Ident("math_cos".to_string()), + Token::Ctrl('('), + Token::Ident("rad_angle".to_string()), + Token::Ctrl(')'), + Token::Ctrl(';'), + Token::Let, + Token::Ident("y".to_string()), + Token::Op("=".to_string()), + Token::Ident("radius".to_string()), + Token::Op("*".to_string()), + Token::Ident("math_sin".to_string()), + Token::Ctrl('('), + Token::Ident("rad_angle".to_string()), + Token::Ctrl(')'), + Token::Ctrl(';'), + Token::Ident("canvas_draw_point".to_string()), + Token::Ctrl('('), + Token::Ident("x".to_string()), + Token::Ctrl(','), + Token::Ident("y".to_string()), + Token::Ctrl(')'), + Token::Ctrl(';'), + Token::Ident("rad_angle".to_string()), + Token::Op("=".to_string()), + Token::Ident("rad_angle".to_string()), + Token::Op("+".to_string()), + Token::Ident("rad_delta".to_string()), + Token::Ctrl(';'), + Token::Ctrl('}'), + ] + ); + } + + #[test] + fn parse_atom() { + assert_eq!(parse(" 505 "), Ok(Expr::Literal(Value::Int(505)))); + assert_eq!(parse(" true "), Ok(Expr::Literal(Value::Bool(true)))); + assert_eq!(parse(" false "), Ok(Expr::Literal(Value::Bool(false)))); + assert_eq!( + parse(" ! true "), + Ok(Expr::Unary { + op: UnaryOp::Not, + expr: Box::new(Expr::Literal(Value::Bool(true))) + }) + ); + assert_eq!( + parse(" \"hello\" "), + Ok(Expr::Literal(Value::String("hello".to_string()))) + ); + } + + #[test] + fn parse_neg() { + assert_eq!( + parse(" ---505 "), + Ok(Expr::Unary { + op: UnaryOp::Neg, + expr: Box::new(Expr::Unary { + op: UnaryOp::Neg, + expr: Box::new(Expr::Unary { + op: UnaryOp::Neg, + expr: Box::new(Expr::Literal(Value::Int(505))), + }), + }) + }) + ); + } + + #[test] + fn parse_arith() { + assert_eq!( + parse(" 1 + 2 * ( (3 - 4) / 5 ) % 6 "), + // sub -> div -> mul -> mod -> add + Ok(Expr::Binary { + op: BinaryOp::Arith(ArithBinaryOp::Add), + lhs: Box::new(Expr::Literal(Value::Int(1))), + rhs: Box::new(Expr::Binary { + op: BinaryOp::Arith(ArithBinaryOp::Mod), + lhs: Box::new(Expr::Binary { + op: BinaryOp::Arith(ArithBinaryOp::Mul), + lhs: Box::new(Expr::Literal(Value::Int(2))), + rhs: Box::new(Expr::Binary { + op: BinaryOp::Arith(ArithBinaryOp::Div), + lhs: Box::new(Expr::Binary { + op: BinaryOp::Arith(ArithBinaryOp::Sub), + lhs: Box::new(Expr::Literal(Value::Int(3))), + rhs: Box::new(Expr::Literal(Value::Int(4))), + }), + rhs: Box::new(Expr::Literal(Value::Int(5))), + }), + }), + rhs: Box::new(Expr::Literal(Value::Int(6))), + }), + }) + ); + } + + #[test] + fn parse_logical_compare() { + assert_eq!( + parse(" 1 < 2 && 3 > 4 || false != true"), + Ok(Expr::Binary { + op: BinaryOp::Logical(LogicalBinaryOp::Or), + lhs: Box::new(Expr::Binary { + op: BinaryOp::Logical(LogicalBinaryOp::And), + lhs: Box::new(Expr::Binary { + op: BinaryOp::Compare(CompareBinaryOp::Lt), + lhs: Box::new(Expr::Literal(Value::Int(1))), + rhs: Box::new(Expr::Literal(Value::Int(2))), + }), + rhs: Box::new(Expr::Binary { + op: BinaryOp::Compare(CompareBinaryOp::Gt), + lhs: Box::new(Expr::Literal(Value::Int(3))), + rhs: Box::new(Expr::Literal(Value::Int(4))), + }), + }), + rhs: Box::new(Expr::Binary { + op: BinaryOp::Compare(CompareBinaryOp::Neq), + lhs: Box::new(Expr::Literal(Value::Bool(false))), + rhs: Box::new(Expr::Literal(Value::Bool(true))), + }), + }) + ); + } + + #[test] + fn parse_ident_let() { + assert_eq!( + parse("let a = 0; a = 1 ; a + 4"), + Ok(Expr::Body { + stmts: vec![ + Stmt::Let { + name: Ident("a".to_string()), + expr: Box::new(Expr::Literal(Value::Int(0))), + }, + Stmt::Expr(Expr::Assign { + name: Ident("a".to_string()), + expr: Box::new(Expr::Literal(Value::Int(1))), + }), + ], + val: Box::new(Expr::Binary { + op: BinaryOp::Arith(ArithBinaryOp::Add), + lhs: Box::new(Expr::Ident(Ident("a".to_string()))), + rhs: Box::new(Expr::Literal(Value::Int(4))), + }), + }) + ); + } + + #[test] + fn parse_block() { + assert_eq!( + parse( + r#" + { + 1 + 2 + } + let a = { + let b = 0; + a = 1; + a + b + }; + a + "# + ), + Ok(Expr::Body { + stmts: vec![ + Stmt::Expr(Expr::Binary { + op: BinaryOp::Arith(ArithBinaryOp::Add), + lhs: Box::new(Expr::Literal(Value::Int(1))), + rhs: Box::new(Expr::Literal(Value::Int(2))), + }), + Stmt::Let { + name: Ident("a".to_string()), + expr: Box::new(Expr::Body { + stmts: vec![ + Stmt::Let { + name: Ident("b".to_string()), + expr: Box::new(Expr::Literal(Value::Int(0))), + }, + Stmt::Expr(Expr::Assign { + name: Ident("a".to_string()), + expr: Box::new(Expr::Literal(Value::Int(1))), + }), + ], + val: Box::new(Expr::Binary { + op: BinaryOp::Arith(ArithBinaryOp::Add), + lhs: Box::new(Expr::Ident(Ident("a".to_string()))), + rhs: Box::new(Expr::Ident(Ident("b".to_string()))), + }), + }), + }, + ], + val: Box::new(Expr::Ident(Ident("a".to_string()))), + }) + ); + } + + #[test] + fn parse_cond() { + assert_eq!( + parse( + r#" + if true { + 1 + } else { + 0 + } + "# + ), + Ok(Expr::Cond { + pred: Box::new(Expr::Literal(Value::Bool(true))), + conseq: Box::new(Expr::Literal(Value::Int(1))), + alt: Box::new(Expr::Literal(Value::Int(0))), + }) + ); + } + + #[test] + fn parse_while() { + assert_eq!( + parse( + r#" + while test(true) == 1 { + 1 + } + "# + ), + Ok(Expr::Body { + stmts: vec![Stmt::While { + pred: Box::new(Expr::Binary { + op: BinaryOp::Compare(CompareBinaryOp::Eq), + lhs: Box::new(Expr::Apply { + r#fn: Box::new(Expr::Ident(Ident("test".to_string()))), + args: vec![Expr::Literal(Value::Bool(true))], + }), + rhs: Box::new(Expr::Literal(Value::Int(1))), + }), + body: Box::new(Expr::Literal(Value::Int(1))), + },], + val: Box::new(Expr::Literal(Value::Void)), + }) + ); + } + + #[test] + fn parse_fn_apply() { + assert_eq!( + parse( + r#" + fn add(a, b) { + fn add2 (a, b) { + let c = 1; + a + b + } + return add2(a, b); + } + add(1, 2)() + "# + ), + Ok(Expr::Body { + stmts: vec![Stmt::Fn { + name: Ident("add".to_string()), + params: vec![Ident("a".to_string()), Ident("b".to_string())], + expr: Box::new(Expr::Body { + stmts: vec![ + Stmt::Fn { + name: Ident("add2".to_string()), + params: vec![Ident("a".to_string()), Ident("b".to_string())], + expr: Box::new(Expr::Body { + stmts: vec![Stmt::Let { + name: Ident("c".to_string()), + expr: Box::new(Expr::Literal(Value::Int(1))), + },], + val: Box::new(Expr::Binary { + op: BinaryOp::Arith(ArithBinaryOp::Add), + lhs: Box::new(Expr::Ident(Ident("a".to_string()))), + rhs: Box::new(Expr::Ident(Ident("b".to_string()))), + }), + }), + }, + Stmt::Return(Box::new(Expr::Apply { + r#fn: Box::new(Expr::Ident(Ident("add2".to_string()))), + args: vec![ + Expr::Ident(Ident("a".to_string())), + Expr::Ident(Ident("b".to_string())), + ], + })), + ], + val: Box::new(Expr::Literal(Value::Void)), + }), + }], + val: Box::new(Expr::Apply { + r#fn: Box::new(Expr::Apply { + r#fn: Box::new(Expr::Ident(Ident("add".to_string()))), + args: vec![Expr::Literal(Value::Int(1)), Expr::Literal(Value::Int(2))], + }), + args: vec![Expr::Literal(Value::Void)], + }), + }) + ); + } + + #[test] + fn parse_example() { + assert_eq!( + parse(EXAMPLE), + Ok(Expr::Body { + stmts: vec![ + Stmt::Expr(Expr::Apply { + r#fn: Box::new(Expr::Ident(Ident("canvas_init".to_string()))), + args: vec![Expr::Literal(Value::Void)], + }), + Stmt::Let { + name: Ident("rad_delta".to_string()), + expr: Box::new(Expr::Literal(Value::Int(1))), + }, + Stmt::Let { + name: Ident("rad_angle".to_string()), + expr: Box::new(Expr::Literal(Value::Int(0))), + }, + Stmt::Let { + name: Ident("radius".to_string()), + expr: Box::new(Expr::Literal(Value::Int(20))), + }, + Stmt::While { + pred: Box::new(Expr::Binary { + op: BinaryOp::Compare(CompareBinaryOp::Lte), + lhs: Box::new(Expr::Ident(Ident("rad_angle".to_string()))), + rhs: Box::new(Expr::Binary { + op: BinaryOp::Arith(ArithBinaryOp::Mul), + lhs: Box::new(Expr::Literal(Value::Int(2))), + rhs: Box::new(Expr::Apply { + r#fn: Box::new(Expr::Ident(Ident("math_pi".to_string()))), + args: vec![Expr::Literal(Value::Void)], + }), + }), + }), + body: Box::new(Expr::Body { + stmts: vec![ + Stmt::Let { + name: Ident("x".to_string()), + expr: Box::new(Expr::Binary { + op: BinaryOp::Arith(ArithBinaryOp::Mul), + lhs: Box::new(Expr::Ident(Ident("radius".to_string()))), + rhs: Box::new(Expr::Apply { + r#fn: Box::new(Expr::Ident(Ident( + "math_cos".to_string() + ))), + args: vec![Expr::Ident(Ident("rad_angle".to_string()))], + }), + }), + }, + Stmt::Let { + name: Ident("y".to_string()), + expr: Box::new(Expr::Binary { + op: BinaryOp::Arith(ArithBinaryOp::Mul), + lhs: Box::new(Expr::Ident(Ident("radius".to_string()))), + rhs: Box::new(Expr::Apply { + r#fn: Box::new(Expr::Ident(Ident( + "math_sin".to_string() + ))), + args: vec![Expr::Ident(Ident("rad_angle".to_string()))], + }), + }), + }, + Stmt::Expr(Expr::Apply { + r#fn: Box::new(Expr::Ident(Ident( + "canvas_draw_point".to_string() + ))), + args: vec![ + Expr::Ident(Ident("x".to_string())), + Expr::Ident(Ident("y".to_string())), + ], + }), + Stmt::Expr(Expr::Assign { + name: Ident("rad_angle".to_string()), + expr: Box::new(Expr::Binary { + op: BinaryOp::Arith(ArithBinaryOp::Add), + lhs: Box::new(Expr::Ident(Ident("rad_angle".to_string()))), + rhs: Box::new(Expr::Ident(Ident("rad_delta".to_string()))), + }), + }), + ], + val: Box::new(Expr::Literal(Value::Void)), + }), + }, + ], + val: Box::new(Expr::Literal(Value::Void)), + }) + ); + } +} diff --git a/conqiler/src/translate.rs b/conqiler/src/translate.rs new file mode 100644 index 0000000..fe547e8 --- /dev/null +++ b/conqiler/src/translate.rs @@ -0,0 +1,176 @@ +use std::{ + error::Error, + fs::File, + io::{Read, Seek, Write}, + process::{Command, Output}, +}; + +use melior::{ + pass::{self, PassManager}, + Context, +}; +use tempfile::NamedTempFile; + +pub fn compile_program_text( + context: &melior::Context, + module: &mut melior::ir::Module, +) -> Result> { + lower_to_llvm_dialect(&context, module)?; + + let llir = module.as_operation().to_string(); + let llvmir_file = convert_mlir_to_llvmir(&llir)?; + let wasm_file = convert_llvmir_to_wasm_text(llvmir_file)?; + Ok(wasm_file.into_file()) +} + +pub fn compile_program( + context: &melior::Context, + module: &mut melior::ir::Module, +) -> Result> { + lower_to_llvm_dialect(&context, module)?; + + let llir = module.as_operation().to_string(); + let llvmir_file = convert_mlir_to_llvmir(&llir)?; + let wasm_file = convert_llvmir_to_wasm(llvmir_file)?; + Ok(wasm_file.into_file()) +} + +pub fn compile_and_get_bytes( + context: &melior::Context, + module: &mut melior::ir::Module, +) -> Result, Box> { + lower_to_llvm_dialect(&context, module)?; + + let llir = module.as_operation().to_string(); + let llvmir_file = convert_mlir_to_llvmir(&llir)?; + let wasm_file = convert_llvmir_to_wasm(llvmir_file)?; + let mut linked_wasm_file = NamedTempFile::new()?; + let path = linked_wasm_file.path(); + + let output = Command::new("wasm-ld-17") + .arg("--allow-undefined") + .arg("--export-all") + .arg(wasm_file.path()) + .arg("-o") + .arg(path) + .output()?; + + linked_wasm_file.persist("wat.wasm")?; + let mut buf = Vec::new(); + let mut f = File::open("wat.wasm")?; + f.read_to_end(&mut buf)?; + eprintln!("Buffer: {:?}", buf); + Ok(buf) +} + +pub fn compile_and_run_program( + context: &melior::Context, + module: &mut melior::ir::Module, +) -> Result> { + lower_to_llvm_dialect(&context, module)?; + + let llir = module.as_operation().to_string(); + let llvmir_file = convert_mlir_to_llvmir(&llir)?; + let wasm_file = convert_llvmir_to_wasm(llvmir_file)?; + let linked_wasm_file = NamedTempFile::new()?; + let path = linked_wasm_file.path(); + + let output = Command::new("wasm-ld-17") + .arg("--allow-undefined") + .arg("--export-all") + .arg(wasm_file.path()) + .arg("-o") + .arg(path) + .output()?; + + linked_wasm_file.persist("wat.wasm")?; + let path = "wat.wasm"; + + eprintln!("{:?}", output); + let output = Command::new("wasmer").arg("run").arg(path).output()?; + Ok(output) +} + +pub fn lower_to_llvm_dialect( + context: &melior::Context, + module: &mut melior::ir::Module, +) -> Result<(), Box> { + //let module = melior::ir::Module::new(self.unknown_loc()); + //let block = module.body(); + + // for e in prog.externs { + // block.append_operation(self.compile_extern(e)?); + // } + // for f in prog.funcs { + // block.append_operation(self.compile_func(f)?); + // } + + //module.as_operation().dump(); + //println!("--"); + //assert!(module.as_operation().verify()); + assert!(module.as_operation().verify()); + + // Convert to LLVM Dialect + let pass_manager = PassManager::new(context); + // pass_manager.add_pass(pass::r#async::create_async_func_to_async_runtime()); + // pass_manager.add_pass(pass::r#async::create_async_to_async_runtime()); + // pass_manager.add_pass(pass::conversion::create_async_to_llvm()); + pass_manager + .nested_under("func.func") + .add_pass(pass::conversion::create_arith_to_llvm()); + pass_manager + .nested_under("func.func") + .add_pass(pass::conversion::create_index_to_llvm()); + pass_manager.enable_verifier(true); + pass_manager.add_pass(pass::transform::create_canonicalizer()); + pass_manager.add_pass(pass::conversion::create_scf_to_control_flow()); + pass_manager.add_pass(pass::conversion::create_arith_to_llvm()); + pass_manager.add_pass(pass::conversion::create_control_flow_to_llvm()); + pass_manager.add_pass(pass::conversion::create_index_to_llvm()); + pass_manager.add_pass(pass::conversion::create_finalize_mem_ref_to_llvm()); + pass_manager.add_pass(pass::conversion::create_func_to_llvm()); + pass_manager.add_pass(pass::conversion::create_reconcile_unrealized_casts()); + pass_manager.run(module)?; + + //module.as_operation().dump(); + assert!(module.as_operation().verify()); + Ok(()) +} + +pub fn convert_mlir_to_llvmir(s: &str) -> Result> { + let mut inp = NamedTempFile::new()?; + let out = NamedTempFile::new()?; + inp.write(s.as_bytes())?; + let _output = Command::new("mlir-translate-17") + .arg("--target-abi=wasm32") + .arg("--mlir-to-llvmir") + .arg(inp.path()) + .arg("-o") + .arg(out.path()) + .output()?; + Ok(out) +} + +pub fn convert_llvmir_to_wasm_text(inp: NamedTempFile) -> Result> { + let out = NamedTempFile::new()?; + let _output = Command::new("llc-17") + .arg("-filetype=asm") + .arg("-march=wasm32") + .arg(inp.path()) + .arg("-o") + .arg(out.path()) + .output()?; + Ok(out) +} + +pub fn convert_llvmir_to_wasm(inp: NamedTempFile) -> Result> { + let out = NamedTempFile::new()?; + let _output = Command::new("llc-17") + .arg("-filetype=obj") + .arg("-march=wasm32") + .arg(inp.path()) + .arg("-o") + .arg(out.path()) + .output()?; + Ok(out) +} diff --git a/conqiler/src/type_decl.rs b/conqiler/src/type_decl.rs new file mode 100644 index 0000000..acfa691 --- /dev/null +++ b/conqiler/src/type_decl.rs @@ -0,0 +1,11 @@ +#[derive(Debug, Clone, PartialEq)] +pub enum Type { + Int, + Bool, + // String, + // Void, + Function { + args: Vec, + ret: Option>, + }, +} \ No newline at end of file diff --git a/runtest.js b/runtest.js new file mode 100644 index 0000000..c800a71 --- /dev/null +++ b/runtest.js @@ -0,0 +1,39 @@ +// Load the Wasm file +const fs = require('fs'); +const wasmFile = './conqiler/wat.wasm'; +//const wasmFile = './program.wasm'; +const buffer = fs.readFileSync(wasmFile); + +// Create imports object with implementation for `malloc` +let wasmInstance; +let last = 0; +const imports = { + env: { + // Implementation for `malloc` + malloc: size => { + const memory = wasmInstance.exports.memory; + const currentMemory = last; + const memoryStart = currentMemory + 8; + const memoryEnd = memoryStart + Number(size); + + if (memoryEnd > memory.buffer.byteLength) { + throw new Error('Out of memory'); + } + + last = memoryEnd; + return memoryStart; + } + } +}; + +// Instantiate the Wasm module with the provided imports +WebAssembly.instantiate(buffer, imports).then(result => { + wasmInstance = result.instance; + + // Call a function from the Wasm module + const wasmFunctionResult = wasmInstance.exports._start(200); + console.log(wasmInstance.exports.memory.buffer) + + // Do something with the result + console.log(wasmFunctionResult); +}); \ No newline at end of file diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml new file mode 100644 index 0000000..df68057 --- /dev/null +++ b/test-server/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "test-server" +version = "0.1.0" +edition = "2021" + +[dependencies] +hyper = { version = "1", features = ["full"] } +tokio = { version = "1", features = ["full"] } +http-body-util = "0.1" +hyper-util = { version = "0.1", features = ["full"] } + +conqiler = { path = "../conqiler" } \ No newline at end of file diff --git a/test-server/src/main.rs b/test-server/src/main.rs new file mode 100644 index 0000000..aec1210 --- /dev/null +++ b/test-server/src/main.rs @@ -0,0 +1,105 @@ +use std::net::SocketAddr; +use conqiler::translate; +use http_body_util::Full; +use http_body_util::{combinators::BoxBody, BodyExt, Empty}; +use hyper::body::Bytes; +use hyper::server::conn::http1; +use hyper::service::service_fn; +use hyper::{Method, Request, Response, StatusCode}; +use hyper_util::rt::TokioIo; +use tokio::fs::File; +use tokio::io::AsyncReadExt; +use tokio::net::TcpListener; + +use conqiler::{ + parser::parse, + compile::{Compiler, Environment, setup_ctx}, +}; + +/// This is our service handler. It receives a Request, routes on its +/// path, and returns a Future of a Response. +async fn echo( + req: Request, +) -> Result>, hyper::Error> { + match (req.method(), req.uri().path()) { + // Simply echo the body back to the client. + (&Method::POST, "/api/echo") => Ok(Response::new(req.into_body().boxed())), + + (&Method::POST, "/api/sample") => { + let mut f = File::open("test-server/static/wat.wasm").await.unwrap(); + if f.metadata().await.unwrap().len() > 1024 * 16 { + return Ok(Response::builder() + .status(StatusCode::PAYLOAD_TOO_LARGE) + .body(full("File too large")) + .unwrap()); + } + let mut buf = Vec::new(); + f.read_to_end(&mut buf).await.unwrap(); + Ok(Response::builder() + .status(StatusCode::OK) + .body(full(buf)) + .unwrap()) + } + + (&Method::POST, "/api/compile") => { + let bytes = req.collect().await.unwrap().to_bytes(); + let code = String::from_utf8(bytes.to_vec()).unwrap(); + + let ctx = setup_ctx(); + let mut compiler = Compiler::new(&ctx); + let blkref = compiler.module.body(); + let env = Environment::new(&blkref); + + let ast = parse(&code).unwrap(); + compiler.compile(ast); + + let wasm = translate::compile_and_get_bytes(&ctx, &mut compiler.module).unwrap(); + + // TODO + Ok(Response::builder() + .status(StatusCode::OK) + .body(full(wasm)) + .unwrap()) + } + + // Return the 404 Not Found for other routes. + _ => { + let mut not_found = Response::new(empty()); + *not_found.status_mut() = StatusCode::NOT_FOUND; + Ok(not_found) + } + } +} + +fn empty() -> BoxBody { + Empty::::new() + .map_err(|never| match never {}) + .boxed() +} + +fn full>(chunk: T) -> BoxBody { + Full::new(chunk.into()) + .map_err(|never| match never {}) + .boxed() +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let addr = SocketAddr::from(([127, 0, 0, 1], 7878)); + + let listener = TcpListener::bind(addr).await?; + println!("Listening on http://{}", addr); + loop { + let (stream, _) = listener.accept().await?; + let io = TokioIo::new(stream); + + tokio::task::spawn(async move { + if let Err(err) = http1::Builder::new() + .serve_connection(io, service_fn(echo)) + .await + { + println!("Error serving connection: {:?}", err); + } + }); + } +} diff --git a/test-server/static/hello.wasm b/test-server/static/hello.wasm new file mode 100755 index 0000000..5168843 Binary files /dev/null and b/test-server/static/hello.wasm differ diff --git a/test-server/static/hello.wat b/test-server/static/hello.wat new file mode 100644 index 0000000..6cad6d2 --- /dev/null +++ b/test-server/static/hello.wat @@ -0,0 +1,1035 @@ +(module + (type (;0;) (func (param i32))) + (type (;1;) (func (param i32 i32 i32) (result i32))) + (type (;2;) (func)) + (type (;3;) (func (param i32) (result i32))) + (type (;4;) (func (result i32))) + (type (;5;) (func (param i32 i32 i32 i32) (result i32))) + (type (;6;) (func (param i32 i64 i32) (result i64))) + (type (;7;) (func (param i32 i32) (result i32))) + (import "wasi_snapshot_preview1" "proc_exit" (func (;0;) (type 0))) + (import "wasi_snapshot_preview1" "fd_write" (func (;1;) (type 5))) + (func (;2;) (type 2)) + (func (;3;) (type 4) (result i32) + i32.const 1024 + call 22 + drop + i32.const 0) + (func (;4;) (type 2) + block ;; label = @1 + i32.const 1 + i32.eqz + br_if 0 (;@1;) + call 2 + end + call 3 + call 7 + unreachable) + (func (;5;) (type 2)) + (func (;6;) (type 2) + (local i32) + i32.const 0 + local.set 0 + block ;; label = @1 + i32.const 0 + i32.const 0 + i32.le_u + br_if 0 (;@1;) + loop ;; label = @2 + local.get 0 + i32.const -4 + i32.add + local.tee 0 + i32.load + call_indirect (type 2) + local.get 0 + i32.const 0 + i32.gt_u + br_if 0 (;@2;) + end + end + call 5) + (func (;7;) (type 0) (param i32) + call 5 + call 6 + call 14 + local.get 0 + call 8 + unreachable) + (func (;8;) (type 0) (param i32) + local.get 0 + call 0 + unreachable) + (func (;9;) (type 3) (param i32) (result i32) + i32.const 1) + (func (;10;) (type 0) (param i32)) + (func (;11;) (type 4) (result i32) + i32.const 1200) + (func (;12;) (type 0) (param i32)) + (func (;13;) (type 4) (result i32) + i32.const 1204 + call 12 + i32.const 1208) + (func (;14;) (type 2) + (local i32) + block ;; label = @1 + call 13 + i32.load + local.tee 0 + i32.eqz + br_if 0 (;@1;) + loop ;; label = @2 + local.get 0 + call 15 + local.get 0 + i32.load offset=56 + local.tee 0 + br_if 0 (;@2;) + end + end + i32.const 0 + i32.load offset=1212 + call 15 + i32.const 0 + i32.load offset=1184 + call 15 + i32.const 0 + i32.load offset=1212 + call 15) + (func (;15;) (type 0) (param i32) + (local i32 i32) + block ;; label = @1 + local.get 0 + i32.eqz + br_if 0 (;@1;) + block ;; label = @2 + local.get 0 + i32.load offset=76 + i32.const 0 + i32.lt_s + br_if 0 (;@2;) + local.get 0 + call 9 + drop + end + block ;; label = @2 + local.get 0 + i32.load offset=20 + local.get 0 + i32.load offset=28 + i32.eq + br_if 0 (;@2;) + local.get 0 + i32.const 0 + i32.const 0 + local.get 0 + i32.load offset=36 + call_indirect (type 1) + drop + end + local.get 0 + i32.load offset=4 + local.tee 1 + local.get 0 + i32.load offset=8 + local.tee 2 + i32.eq + br_if 0 (;@1;) + local.get 0 + local.get 1 + local.get 2 + i32.sub + i64.extend_i32_s + i32.const 1 + local.get 0 + i32.load offset=40 + call_indirect (type 6) + drop + end) + (func (;16;) (type 3) (param i32) (result i32) + (local i32) + local.get 0 + local.get 0 + i32.load offset=72 + local.tee 1 + i32.const -1 + i32.add + local.get 1 + i32.or + i32.store offset=72 + block ;; label = @1 + local.get 0 + i32.load + local.tee 1 + i32.const 8 + i32.and + i32.eqz + br_if 0 (;@1;) + local.get 0 + local.get 1 + i32.const 32 + i32.or + i32.store + i32.const -1 + return + end + local.get 0 + i64.const 0 + i64.store offset=4 align=4 + local.get 0 + local.get 0 + i32.load offset=44 + local.tee 1 + i32.store offset=28 + local.get 0 + local.get 1 + i32.store offset=20 + local.get 0 + local.get 1 + local.get 0 + i32.load offset=48 + i32.add + i32.store offset=16 + i32.const 0) + (func (;17;) (type 1) (param i32 i32 i32) (result i32) + (local i32 i32 i32) + local.get 0 + local.get 2 + i32.add + local.set 3 + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + block ;; label = @4 + local.get 1 + local.get 0 + i32.xor + i32.const 3 + i32.and + br_if 0 (;@4;) + local.get 0 + i32.const 3 + i32.and + i32.eqz + br_if 1 (;@3;) + local.get 2 + i32.const 1 + i32.lt_s + br_if 1 (;@3;) + local.get 0 + local.set 2 + loop ;; label = @5 + local.get 2 + local.get 1 + i32.load8_u + i32.store8 + local.get 1 + i32.const 1 + i32.add + local.set 1 + local.get 2 + i32.const 1 + i32.add + local.tee 2 + i32.const 3 + i32.and + i32.eqz + br_if 3 (;@2;) + local.get 2 + local.get 3 + i32.lt_u + br_if 0 (;@5;) + br 3 (;@2;) + end + unreachable + end + block ;; label = @4 + local.get 3 + i32.const 4 + i32.lt_u + br_if 0 (;@4;) + local.get 3 + i32.const -4 + i32.add + local.tee 4 + local.get 0 + i32.lt_u + br_if 0 (;@4;) + local.get 0 + local.set 2 + loop ;; label = @5 + local.get 2 + local.get 1 + i32.load8_u + i32.store8 + local.get 2 + local.get 1 + i32.load8_u offset=1 + i32.store8 offset=1 + local.get 2 + local.get 1 + i32.load8_u offset=2 + i32.store8 offset=2 + local.get 2 + local.get 1 + i32.load8_u offset=3 + i32.store8 offset=3 + local.get 1 + i32.const 4 + i32.add + local.set 1 + local.get 2 + i32.const 4 + i32.add + local.tee 2 + local.get 4 + i32.le_u + br_if 0 (;@5;) + br 4 (;@1;) + end + unreachable + end + local.get 0 + local.set 2 + br 2 (;@1;) + end + local.get 0 + local.set 2 + end + block ;; label = @2 + local.get 3 + i32.const -4 + i32.and + local.tee 4 + i32.const 64 + i32.lt_u + br_if 0 (;@2;) + local.get 2 + local.get 4 + i32.const -64 + i32.add + local.tee 5 + i32.gt_u + br_if 0 (;@2;) + loop ;; label = @3 + local.get 2 + local.get 1 + i32.load + i32.store + local.get 2 + local.get 1 + i32.load offset=4 + i32.store offset=4 + local.get 2 + local.get 1 + i32.load offset=8 + i32.store offset=8 + local.get 2 + local.get 1 + i32.load offset=12 + i32.store offset=12 + local.get 2 + local.get 1 + i32.load offset=16 + i32.store offset=16 + local.get 2 + local.get 1 + i32.load offset=20 + i32.store offset=20 + local.get 2 + local.get 1 + i32.load offset=24 + i32.store offset=24 + local.get 2 + local.get 1 + i32.load offset=28 + i32.store offset=28 + local.get 2 + local.get 1 + i32.load offset=32 + i32.store offset=32 + local.get 2 + local.get 1 + i32.load offset=36 + i32.store offset=36 + local.get 2 + local.get 1 + i32.load offset=40 + i32.store offset=40 + local.get 2 + local.get 1 + i32.load offset=44 + i32.store offset=44 + local.get 2 + local.get 1 + i32.load offset=48 + i32.store offset=48 + local.get 2 + local.get 1 + i32.load offset=52 + i32.store offset=52 + local.get 2 + local.get 1 + i32.load offset=56 + i32.store offset=56 + local.get 2 + local.get 1 + i32.load offset=60 + i32.store offset=60 + local.get 1 + i32.const 64 + i32.add + local.set 1 + local.get 2 + i32.const 64 + i32.add + local.tee 2 + local.get 5 + i32.le_u + br_if 0 (;@3;) + end + end + local.get 2 + local.get 4 + i32.ge_u + br_if 0 (;@1;) + loop ;; label = @2 + local.get 2 + local.get 1 + i32.load + i32.store + local.get 1 + i32.const 4 + i32.add + local.set 1 + local.get 2 + i32.const 4 + i32.add + local.tee 2 + local.get 4 + i32.lt_u + br_if 0 (;@2;) + end + end + block ;; label = @1 + local.get 2 + local.get 3 + i32.ge_u + br_if 0 (;@1;) + loop ;; label = @2 + local.get 2 + local.get 1 + i32.load8_u + i32.store8 + local.get 1 + i32.const 1 + i32.add + local.set 1 + local.get 2 + i32.const 1 + i32.add + local.tee 2 + local.get 3 + i32.ne + br_if 0 (;@2;) + end + end + local.get 0) + (func (;18;) (type 1) (param i32 i32 i32) (result i32) + (local i32 i32 i32) + block ;; label = @1 + block ;; label = @2 + local.get 2 + i32.load offset=16 + local.tee 3 + br_if 0 (;@2;) + i32.const 0 + local.set 4 + local.get 2 + call 16 + br_if 1 (;@1;) + local.get 2 + i32.load offset=16 + local.set 3 + end + block ;; label = @2 + local.get 3 + local.get 2 + i32.load offset=20 + local.tee 4 + i32.sub + local.get 1 + i32.ge_u + br_if 0 (;@2;) + local.get 2 + local.get 0 + local.get 1 + local.get 2 + i32.load offset=36 + call_indirect (type 1) + return + end + block ;; label = @2 + block ;; label = @3 + local.get 2 + i32.load offset=80 + i32.const 0 + i32.lt_s + br_if 0 (;@3;) + local.get 1 + i32.eqz + br_if 0 (;@3;) + local.get 1 + local.set 3 + block ;; label = @4 + loop ;; label = @5 + local.get 0 + local.get 3 + i32.add + local.tee 5 + i32.const -1 + i32.add + i32.load8_u + i32.const 10 + i32.eq + br_if 1 (;@4;) + local.get 3 + i32.const -1 + i32.add + local.tee 3 + i32.eqz + br_if 2 (;@3;) + br 0 (;@5;) + end + unreachable + end + local.get 2 + local.get 0 + local.get 3 + local.get 2 + i32.load offset=36 + call_indirect (type 1) + local.tee 4 + local.get 3 + i32.lt_u + br_if 2 (;@1;) + local.get 1 + local.get 3 + i32.sub + local.set 1 + local.get 2 + i32.load offset=20 + local.set 4 + br 1 (;@2;) + end + local.get 0 + local.set 5 + i32.const 0 + local.set 3 + end + local.get 4 + local.get 5 + local.get 1 + call 17 + drop + local.get 2 + local.get 2 + i32.load offset=20 + local.get 1 + i32.add + i32.store offset=20 + local.get 3 + local.get 1 + i32.add + local.set 4 + end + local.get 4) + (func (;19;) (type 5) (param i32 i32 i32 i32) (result i32) + (local i32 i32) + local.get 2 + local.get 1 + i32.mul + local.set 4 + block ;; label = @1 + block ;; label = @2 + local.get 3 + i32.load offset=76 + i32.const -1 + i32.gt_s + br_if 0 (;@2;) + local.get 0 + local.get 4 + local.get 3 + call 18 + local.set 0 + br 1 (;@1;) + end + local.get 3 + call 9 + local.set 5 + local.get 0 + local.get 4 + local.get 3 + call 18 + local.set 0 + local.get 5 + i32.eqz + br_if 0 (;@1;) + local.get 3 + call 10 + end + block ;; label = @1 + local.get 0 + local.get 4 + i32.ne + br_if 0 (;@1;) + local.get 2 + i32.const 0 + local.get 1 + select + return + end + local.get 0 + local.get 1 + i32.div_u) + (func (;20;) (type 7) (param i32 i32) (result i32) + (local i32) + local.get 0 + call 26 + local.set 2 + i32.const -1 + i32.const 0 + local.get 2 + local.get 0 + i32.const 1 + local.get 2 + local.get 1 + call 19 + i32.ne + select) + (func (;21;) (type 7) (param i32 i32) (result i32) + (local i32 i32 i32) + global.get 0 + i32.const 16 + i32.sub + local.tee 2 + global.set 0 + local.get 2 + local.get 1 + i32.store8 offset=15 + block ;; label = @1 + block ;; label = @2 + local.get 0 + i32.load offset=16 + local.tee 3 + br_if 0 (;@2;) + i32.const -1 + local.set 3 + local.get 0 + call 16 + br_if 1 (;@1;) + local.get 0 + i32.load offset=16 + local.set 3 + end + block ;; label = @2 + local.get 0 + i32.load offset=20 + local.tee 4 + local.get 3 + i32.eq + br_if 0 (;@2;) + local.get 0 + i32.load offset=80 + local.get 1 + i32.const 255 + i32.and + local.tee 3 + i32.eq + br_if 0 (;@2;) + local.get 0 + local.get 4 + i32.const 1 + i32.add + i32.store offset=20 + local.get 4 + local.get 1 + i32.store8 + br 1 (;@1;) + end + i32.const -1 + local.set 3 + local.get 0 + local.get 2 + i32.const 15 + i32.add + i32.const 1 + local.get 0 + i32.load offset=36 + call_indirect (type 1) + i32.const 1 + i32.ne + br_if 0 (;@1;) + local.get 2 + i32.load8_u offset=15 + local.set 3 + end + local.get 2 + i32.const 16 + i32.add + global.set 0 + local.get 3) + (func (;22;) (type 3) (param i32) (result i32) + (local i32 i32) + block ;; label = @1 + block ;; label = @2 + i32.const 0 + i32.load offset=1116 + i32.const 0 + i32.ge_s + br_if 0 (;@2;) + i32.const 1 + local.set 1 + br 1 (;@1;) + end + i32.const 1040 + call 9 + i32.eqz + local.set 1 + end + block ;; label = @1 + block ;; label = @2 + local.get 0 + i32.const 1040 + call 20 + i32.const 0 + i32.ge_s + br_if 0 (;@2;) + i32.const -1 + local.set 0 + br 1 (;@1;) + end + block ;; label = @2 + i32.const 0 + i32.load offset=1120 + i32.const 10 + i32.eq + br_if 0 (;@2;) + i32.const 0 + i32.load offset=1060 + local.tee 2 + i32.const 0 + i32.load offset=1056 + i32.eq + br_if 0 (;@2;) + i32.const 0 + local.set 0 + i32.const 0 + local.get 2 + i32.const 1 + i32.add + i32.store offset=1060 + local.get 2 + i32.const 10 + i32.store8 + br 1 (;@1;) + end + i32.const 1040 + i32.const 10 + call 21 + i32.const 31 + i32.shr_s + local.set 0 + end + block ;; label = @1 + local.get 1 + br_if 0 (;@1;) + i32.const 1040 + call 10 + end + local.get 0) + (func (;23;) (type 1) (param i32 i32 i32) (result i32) + (local i32 i32 i32 i32 i32 i32 i32) + global.get 0 + i32.const 32 + i32.sub + local.tee 3 + global.set 0 + local.get 3 + local.get 0 + i32.load offset=28 + local.tee 4 + i32.store offset=16 + local.get 0 + i32.load offset=20 + local.set 5 + local.get 3 + local.get 2 + i32.store offset=28 + local.get 3 + local.get 1 + i32.store offset=24 + local.get 3 + local.get 5 + local.get 4 + i32.sub + local.tee 1 + i32.store offset=20 + local.get 1 + local.get 2 + i32.add + local.set 6 + local.get 3 + i32.const 16 + i32.add + local.set 4 + i32.const 2 + local.set 7 + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + block ;; label = @4 + block ;; label = @5 + local.get 0 + i32.load offset=60 + local.get 3 + i32.const 16 + i32.add + i32.const 2 + local.get 3 + i32.const 12 + i32.add + call 1 + call 27 + i32.eqz + br_if 0 (;@5;) + local.get 4 + local.set 5 + br 1 (;@4;) + end + loop ;; label = @5 + local.get 6 + local.get 3 + i32.load offset=12 + local.tee 1 + i32.eq + br_if 2 (;@3;) + block ;; label = @6 + local.get 1 + i32.const -1 + i32.gt_s + br_if 0 (;@6;) + local.get 4 + local.set 5 + br 4 (;@2;) + end + local.get 4 + local.get 1 + local.get 4 + i32.load offset=4 + local.tee 8 + i32.gt_u + local.tee 9 + i32.const 3 + i32.shl + i32.add + local.tee 5 + local.get 5 + i32.load + local.get 1 + local.get 8 + i32.const 0 + local.get 9 + select + i32.sub + local.tee 8 + i32.add + i32.store + local.get 4 + i32.const 12 + i32.const 4 + local.get 9 + select + i32.add + local.tee 4 + local.get 4 + i32.load + local.get 8 + i32.sub + i32.store + local.get 6 + local.get 1 + i32.sub + local.set 6 + local.get 5 + local.set 4 + local.get 0 + i32.load offset=60 + local.get 5 + local.get 7 + local.get 9 + i32.sub + local.tee 7 + local.get 3 + i32.const 12 + i32.add + call 1 + call 27 + i32.eqz + br_if 0 (;@5;) + end + end + local.get 6 + i32.const -1 + i32.ne + br_if 1 (;@2;) + end + local.get 0 + local.get 0 + i32.load offset=44 + local.tee 1 + i32.store offset=28 + local.get 0 + local.get 1 + i32.store offset=20 + local.get 0 + local.get 1 + local.get 0 + i32.load offset=48 + i32.add + i32.store offset=16 + local.get 2 + local.set 1 + br 1 (;@1;) + end + i32.const 0 + local.set 1 + local.get 0 + i32.const 0 + i32.store offset=28 + local.get 0 + i64.const 0 + i64.store offset=16 + local.get 0 + local.get 0 + i32.load + i32.const 32 + i32.or + i32.store + local.get 7 + i32.const 2 + i32.eq + br_if 0 (;@1;) + local.get 2 + local.get 5 + i32.load offset=4 + i32.sub + local.set 1 + end + local.get 3 + i32.const 32 + i32.add + global.set 0 + local.get 1) + (func (;24;) (type 3) (param i32) (result i32) + i32.const 0) + (func (;25;) (type 6) (param i32 i64 i32) (result i64) + i64.const 0) + (func (;26;) (type 3) (param i32) (result i32) + (local i32 i32 i32) + local.get 0 + local.set 1 + block ;; label = @1 + block ;; label = @2 + local.get 0 + i32.const 3 + i32.and + i32.eqz + br_if 0 (;@2;) + block ;; label = @3 + local.get 0 + i32.load8_u + br_if 0 (;@3;) + local.get 0 + local.get 0 + i32.sub + return + end + local.get 0 + local.set 1 + loop ;; label = @3 + local.get 1 + i32.const 1 + i32.add + local.tee 1 + i32.const 3 + i32.and + i32.eqz + br_if 1 (;@2;) + local.get 1 + i32.load8_u + br_if 0 (;@3;) + br 2 (;@1;) + end + unreachable + end + loop ;; label = @2 + local.get 1 + local.tee 2 + i32.const 4 + i32.add + local.set 1 + i32.const 16843008 + local.get 2 + i32.load + local.tee 3 + i32.sub + local.get 3 + i32.or + i32.const -2139062144 + i32.and + i32.const -2139062144 + i32.eq + br_if 0 (;@2;) + end + loop ;; label = @2 + local.get 2 + local.tee 1 + i32.const 1 + i32.add + local.set 2 + local.get 1 + i32.load8_u + br_if 0 (;@2;) + end + end + local.get 1 + local.get 0 + i32.sub) + (func (;27;) (type 3) (param i32) (result i32) + block ;; label = @1 + local.get 0 + br_if 0 (;@1;) + i32.const 0 + return + end + call 11 + local.get 0 + i32.store + i32.const -1) + (func (;28;) (type 0) (param i32) + local.get 0 + global.set 0) + (func (;29;) (type 4) (result i32) + global.get 0) + (table (;0;) 5 5 funcref) + (memory (;0;) 258 258) + (global (;0;) (mut i32) (i32.const 67792)) + (export "memory" (memory 0)) + (export "__indirect_function_table" (table 0)) + (export "_start" (func 4)) + (export "_emscripten_stack_restore" (func 28)) + (export "emscripten_stack_get_current" (func 29)) + (elem (;0;) (i32.const 1) func 2 24 23 25) + (data (;0;) (i32.const 1024) "hello, world!\00") + (data (;1;) (i32.const 1040) "\05\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\03\00\00\00\04\00\00\00\c8\04\00\00\00\04\00\00\00\00\00\00\00\00\00\00\01\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\ff\ff\ff\ff\0a\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\10\04\00\00")) diff --git a/test-server/static/wat.wasm b/test-server/static/wat.wasm new file mode 100644 index 0000000..193d32f Binary files /dev/null and b/test-server/static/wat.wasm differ diff --git a/test-server/static/wat.wat b/test-server/static/wat.wat new file mode 100644 index 0000000..c6ec3d4 --- /dev/null +++ b/test-server/static/wat.wat @@ -0,0 +1,87 @@ +(module + (type (;0;) (func (param i64) (result i32))) + (type (;1;) (func)) + (type (;2;) (func (param i32))) + (import "env" "malloc" (func $malloc (type 0))) + (func $__wasm_call_ctors (type 1)) + (func $_start (type 2) (param i32) + (local i32 i32 i32) + i64.const 4 + call $malloc + local.tee 1 + i32.const 2 + i32.store + i64.const 4 + call $malloc + local.tee 2 + i32.const 3 + i32.store + i64.const 4 + call $malloc + i32.const 5 + i32.store + local.get 2 + i32.const 5 + i32.store + i64.const 4 + call $malloc + i32.const 2 + i32.store + local.get 1 + i32.load + local.set 3 + i64.const 1 + call $malloc + local.get 3 + i32.const 2 + i32.eq + i32.store8 + i32.const 45 + local.set 1 + block ;; label = @1 + local.get 3 + i32.const 2 + i32.eq + br_if 0 (;@1;) + i32.const 67 + local.set 1 + end + i64.const 4 + call $malloc + local.get 1 + i32.store + local.get 2 + local.get 1 + i32.store + local.get 0 + i64.const 0 + i64.store offset=8 + local.get 0 + local.get 2 + i32.store offset=4 + local.get 0 + local.get 2 + i32.store) + (memory (;0;) 2) + (global $__stack_pointer (mut i32) (i32.const 66560)) + (global (;1;) i32 (i32.const 1024)) + (global (;2;) i32 (i32.const 1024)) + (global (;3;) i32 (i32.const 1024)) + (global (;4;) i32 (i32.const 66560)) + (global (;5;) i32 (i32.const 1024)) + (global (;6;) i32 (i32.const 66560)) + (global (;7;) i32 (i32.const 131072)) + (global (;8;) i32 (i32.const 0)) + (global (;9;) i32 (i32.const 1)) + (export "memory" (memory 0)) + (export "__wasm_call_ctors" (func $__wasm_call_ctors)) + (export "_start" (func $_start)) + (export "__dso_handle" (global 1)) + (export "__data_end" (global 2)) + (export "__stack_low" (global 3)) + (export "__stack_high" (global 4)) + (export "__global_base" (global 5)) + (export "__heap_base" (global 6)) + (export "__heap_end" (global 7)) + (export "__memory_base" (global 8)) + (export "__table_base" (global 9))) diff --git a/test-server/static/wtf.wasm b/test-server/static/wtf.wasm new file mode 100755 index 0000000..8f91d46 Binary files /dev/null and b/test-server/static/wtf.wasm differ diff --git a/test-server/static/wtf2.wasm b/test-server/static/wtf2.wasm new file mode 100644 index 0000000..c2cf9d9 Binary files /dev/null and b/test-server/static/wtf2.wasm differ diff --git a/test-server/static/wtf2.wat b/test-server/static/wtf2.wat new file mode 100644 index 0000000..9ddefba --- /dev/null +++ b/test-server/static/wtf2.wat @@ -0,0 +1,9 @@ +(module + (type (;0;) (func (param i64) (result i64))) + (import "env" "__linear_memory" (memory (;0;) 0)) + (import "env" "print" (func $print (param i64))) + (func $_start (type 0) (param i64) (result i64) + i64.const 69 + call $print + i64.const 32) + (export "_start" (func $_start))) diff --git a/test-server/static/wtf3.c b/test-server/static/wtf3.c new file mode 100644 index 0000000..b93f1b4 --- /dev/null +++ b/test-server/static/wtf3.c @@ -0,0 +1,4 @@ +int add (int first, int second) +{ + return first + second; +} \ No newline at end of file diff --git a/test-server/static/wtf3.wasm b/test-server/static/wtf3.wasm new file mode 100755 index 0000000..6a19931 Binary files /dev/null and b/test-server/static/wtf3.wasm differ diff --git a/test-server/static/wtf3.wat b/test-server/static/wtf3.wat new file mode 100644 index 0000000..a535ebd --- /dev/null +++ b/test-server/static/wtf3.wat @@ -0,0 +1,55 @@ +(module + (type (;0;) (func)) + (type (;1;) (func (param i32 i32) (result i32))) + (func $__wasm_call_ctors (type 0)) + (func $add (type 1) (param i32 i32) (result i32) + (local i32 i32 i32 i32 i32 i32) + global.get $__stack_pointer + local.set 2 + i32.const 16 + local.set 3 + local.get 2 + local.get 3 + i32.sub + local.set 4 + local.get 4 + local.get 0 + i32.store offset=12 + local.get 4 + local.get 1 + i32.store offset=8 + local.get 4 + i32.load offset=12 + local.set 5 + local.get 4 + i32.load offset=8 + local.set 6 + local.get 5 + local.get 6 + i32.add + local.set 7 + local.get 7 + return) + (memory (;0;) 2) + (global $__stack_pointer (mut i32) (i32.const 66560)) + (global (;1;) i32 (i32.const 1024)) + (global (;2;) i32 (i32.const 1024)) + (global (;3;) i32 (i32.const 1024)) + (global (;4;) i32 (i32.const 66560)) + (global (;5;) i32 (i32.const 1024)) + (global (;6;) i32 (i32.const 66560)) + (global (;7;) i32 (i32.const 131072)) + (global (;8;) i32 (i32.const 0)) + (global (;9;) i32 (i32.const 1)) + (export "memory" (memory 0)) + (export "__wasm_call_ctors" (func $__wasm_call_ctors)) + (export "add" (func $add)) + (export "__dso_handle" (global 1)) + (export "__data_end" (global 2)) + (export "__stack_low" (global 3)) + (export "__stack_high" (global 4)) + (export "__global_base" (global 5)) + (export "__heap_base" (global 6)) + (export "__heap_end" (global 7)) + (export "__memory_base" (global 8)) + (export "__table_base" (global 9))) diff --git a/tet.rs b/tet.rs deleted file mode 100644 index c73d6d2..0000000 --- a/tet.rs +++ /dev/null @@ -1,8 +0,0 @@ - - -let a = 0; -let b = { - let c = 1; - let d = 2; - c + d -}; \ No newline at end of file diff --git a/wat.wasm b/wat.wasm new file mode 100755 index 0000000..7b8e5c1 Binary files /dev/null and b/wat.wasm differ