mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 15:21:48 +03:00
Init AST impl
This commit is contained in:
parent
bfe81a731f
commit
bc410b6d52
12
ide/gui/.gitignore
vendored
Normal file
12
ide/gui/.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
# Rust
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
bin/
|
||||
pkg/
|
||||
wasm-pack.log
|
||||
|
||||
# IntelliJ
|
||||
.idea/
|
||||
.vscode/
|
||||
*.iml
|
44
ide/gui/.rustfmt.toml
Normal file
44
ide/gui/.rustfmt.toml
Normal file
@ -0,0 +1,44 @@
|
||||
unstable_features = true
|
||||
required_version = "1.4.8"
|
||||
|
||||
# Basic Configuration
|
||||
edition = "2018"
|
||||
newline_style = "Unix"
|
||||
format_code_in_doc_comments = true
|
||||
format_macro_matchers = true
|
||||
use_field_init_shorthand = true
|
||||
use_small_heuristics = "Max"
|
||||
use_try_shorthand = true
|
||||
where_single_line = true
|
||||
condense_wildcard_suffixes = true # `let (a,b,_,_) = x` -> `let (a,b,..) = x`
|
||||
|
||||
# Max Width
|
||||
max_width = 80
|
||||
error_on_line_overflow = true
|
||||
error_on_unformatted = true
|
||||
format_strings = true
|
||||
wrap_comments = true
|
||||
|
||||
# Comments
|
||||
normalize_comments = true
|
||||
normalize_doc_attributes = true
|
||||
|
||||
# Imports
|
||||
#merge_imports = true
|
||||
|
||||
|
||||
# Newlines
|
||||
overflow_delimited_expr = true
|
||||
|
||||
# Ordering
|
||||
reorder_impl_items = true
|
||||
|
||||
# Vertical Alignment
|
||||
struct_field_align_threshold = 80
|
||||
enum_discrim_align_threshold = 80
|
||||
spaces_around_ranges = true
|
||||
brace_style = "PreferSameLine"
|
||||
|
||||
# Reporting
|
||||
report_fixme = "Unnumbered"
|
||||
report_todo = "Unnumbered"
|
10
ide/gui/Cargo.toml
Normal file
10
ide/gui/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
||||
[workspace]
|
||||
|
||||
members = [
|
||||
"lib/core",
|
||||
"lib/structology/core",
|
||||
"lib/structology/macros",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
21
ide/gui/LICENSE
Normal file
21
ide/gui/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2019 Luna Team, Inc. http://luna-lang.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
75
ide/gui/README.md
Normal file
75
ide/gui/README.md
Normal file
@ -0,0 +1,75 @@
|
||||
[![License](https://img.shields.io/static/v1?label=License&message=MIT&color=2ec352&labelColor=2c3239)](https://github.com/luna/basegl/blob/master/LICENSE) [![Actions Status](https://github.com/luna/basegl/workflows/Build%20%28MacOS%2C%20Linux%2C%20Windows%29/badge.svg)](https://github.com/luna/basegl/actions) [![Coverage](https://img.shields.io/codecov/c/github/luna/basegl?label=Coverage&labelColor=2c3239)](https://codecov.io/gh/luna/basegl/branch/master)
|
||||
![Stability](https://img.shields.io/static/v1?label=Stability&message=Unstable&color=d52229&labelColor=2c3239)
|
||||
|
||||
# BaseGL
|
||||
|
||||
BaseGL is a blazing fast 2D drawing API. This repository is a work in progress
|
||||
of BaseGL 2.0. Please refer to BaseGL 1.0 repository for more information:
|
||||
https://github.com/luna/basegl-old.
|
||||
|
||||
## Working with the code
|
||||
|
||||
### The Rust toolchain
|
||||
|
||||
In order to use some of the WASM pipeline features we need to use a nightly Rust
|
||||
channel. The same applies to the code auto-formatter and it's advanced
|
||||
configuration options used here. You would neither be able to compile not format
|
||||
the code using the stable branch.
|
||||
|
||||
To setup the toolchain, please use the [the Rust toolchain installer
|
||||
](https://rustup.rs/):
|
||||
|
||||
```bash
|
||||
rustup toolchain install nightly-2019-10-03 # Install the nightly channel.
|
||||
rustup default nightly # Set it as the default one.
|
||||
rustup component add rustfmt # Install the code auto-formatter.
|
||||
rustup component add clippy # Install the linter.
|
||||
```
|
||||
|
||||
### Building and testing the project
|
||||
|
||||
Please use the `script/build.sh`, `script/watch.sh`, and `script/lint.sh`
|
||||
scripts to build, watch, and lint the project respectively. We need to use a
|
||||
simple custom wrappers here because of the several Rust toolchain issues:
|
||||
|
||||
- [No direct support for Cargo Workspaces in
|
||||
wasm-pack.](https://github.com/rustwasm/wasm-pack/issues/642). Fixed in
|
||||
`build.sh`.
|
||||
- There is no watch utility in wasm-pack, which makes using it harder than it
|
||||
should be. Fixed in `watch.sh`.
|
||||
- [The commands cargo-check and cargo-clippy do not clean local cache if used
|
||||
several times.](https://github.com/rust-lang/cargo/issues/6986). Fixed in
|
||||
`lint.sh`.
|
||||
|
||||
In order to build an example demo scene, please use the following commands:
|
||||
|
||||
```bash
|
||||
./script/watch.sh # Build and watch for changes.
|
||||
|
||||
# Wait till the project finishes building.
|
||||
# Run the following lines from other cmd:
|
||||
|
||||
cd examples/01-scene
|
||||
npm install
|
||||
npm run start
|
||||
```
|
||||
|
||||
You can now open the following address in your browser: http://localhost:8080.
|
||||
**Please remember to disable the cache in your browser!**
|
||||
|
||||
### Working with the source code
|
||||
|
||||
#### Formatting
|
||||
|
||||
All codebase should be auto-formatted using `rustfmt`. It is highly recommended
|
||||
that you use an IDE which takes care of formatting the code as you type. Please
|
||||
remember that auto-formatting does not mean you should not care of the way your
|
||||
code looks and feels! Be sure to carefully read the [Rust style
|
||||
guide](https://github.com/luna/enso/blob/master/doc/rust-style-guide.md) and
|
||||
apply it everywhere in your codebase.
|
||||
|
||||
|
||||
#### Linting
|
||||
|
||||
Please be sure to fix all errors reported by `cargo clippy` before creating a
|
||||
pull request to this repository.
|
2
ide/gui/examples/01-scene/.gitignore
vendored
Normal file
2
ide/gui/examples/01-scene/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
dist
|
5
ide/gui/examples/01-scene/bootstrap.js
vendored
Normal file
5
ide/gui/examples/01-scene/bootstrap.js
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
// A dependency graph that contains any wasm must all be imported
|
||||
// asynchronously. This `bootstrap.js` file does the single async import, so
|
||||
// that no one else needs to worry about it again.
|
||||
import("./index.js")
|
||||
.catch(e => console.error("Error importing `index.js`:", e));
|
19
ide/gui/examples/01-scene/index.html
Normal file
19
ide/gui/examples/01-scene/index.html
Normal file
@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
||||
<title>BaseGL Example</title>
|
||||
<style>
|
||||
html, body { width:100vw; height:100vh; }
|
||||
body { margin:0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>This page contains webassembly and javascript content, please enable javascript in your browser.</noscript>
|
||||
<div id="app" style="height:100vh; width:100vw; display:block">
|
||||
<canvas id="canvas" style="height:100vh; width:100vw; display:block"></canvas>
|
||||
</div>
|
||||
</body>
|
||||
<script src="./bootstrap.js"></script>
|
||||
</html>
|
1
ide/gui/examples/01-scene/index.js
Normal file
1
ide/gui/examples/01-scene/index.js
Normal file
@ -0,0 +1 @@
|
||||
import * as wasm from "basegl";
|
6058
ide/gui/examples/01-scene/package-lock.json
generated
Normal file
6058
ide/gui/examples/01-scene/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
38
ide/gui/examples/01-scene/package.json
Normal file
38
ide/gui/examples/01-scene/package.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "example-scene",
|
||||
"version": "0.1.0",
|
||||
"description": "Example Scene",
|
||||
"main": "index.js",
|
||||
"bin": {
|
||||
"create-wasm-app": ".bin/create-wasm-app.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "webpack --config webpack.config.js",
|
||||
"start": "webpack-dev-server"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/rustwasm/create-wasm-app.git"
|
||||
},
|
||||
"keywords": [
|
||||
"webassembly",
|
||||
"wasm",
|
||||
"rust",
|
||||
"webpack"
|
||||
],
|
||||
"author": "Ashley Williams <ashley666ashley@gmail.com>",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"bugs": {
|
||||
"url": "https://github.com/rustwasm/create-wasm-app/issues"
|
||||
},
|
||||
"homepage": "https://github.com/rustwasm/create-wasm-app#readme",
|
||||
"devDependencies": {
|
||||
"basegl": "file:../../target/web",
|
||||
"hello-wasm-pack": "^0.1.0",
|
||||
"webpack": "^4.29.3",
|
||||
"webpack-cli": "^3.1.0",
|
||||
"webpack-dev-server": "^3.1.5",
|
||||
"copy-webpack-plugin": "^5.0.0",
|
||||
"html-webpack-plugin": "^3.2.0"
|
||||
}
|
||||
}
|
16
ide/gui/examples/01-scene/webpack.config.js
Normal file
16
ide/gui/examples/01-scene/webpack.config.js
Normal file
@ -0,0 +1,16 @@
|
||||
const CopyWebpackPlugin = require("copy-webpack-plugin");
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: "./bootstrap.js",
|
||||
output: {
|
||||
path: path.resolve(__dirname, "dist"),
|
||||
filename: "bootstrap.js",
|
||||
},
|
||||
mode: "development",
|
||||
plugins: [
|
||||
new CopyWebpackPlugin(['index.html']),
|
||||
// new HtmlWebpackPlugin({template: 'index.html'}),
|
||||
],
|
||||
};
|
47
ide/gui/lib/core/Cargo.toml
Normal file
47
ide/gui/lib/core/Cargo.toml
Normal file
@ -0,0 +1,47 @@
|
||||
[package]
|
||||
name = "basegl"
|
||||
version = "0.1.0"
|
||||
authors = ["Wojciech Danilo <wojciech.danilo@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
basegl-prelude = { version = "0.1.0" , path = "../prelude" }
|
||||
structology = { version = "0.1.0" , path = "../structology/core" }
|
||||
wasm-bindgen = { version = "^0.2" , features = ["nightly"] }
|
||||
js-sys = { version = "0.3.28" }
|
||||
failure = { version = "0.1.5" }
|
||||
derive_more = { version = "0.15.0" }
|
||||
shrinkwraprs = { version = "0.2.1" }
|
||||
itertools = { version = "0.8" }
|
||||
nalgebra = { version = "0.18.1" }
|
||||
bit_field = { version = "0.10.0" }
|
||||
paste = { version = "0.1.6" }
|
||||
enum_dispatch = { version = "= 0.1.3" } # https://gitlab.com/antonok/enum_dispatch/issues/10
|
||||
typenum = { version = "1.11.2" }
|
||||
rustc-hash = { version = "1.0.1" }
|
||||
console_error_panic_hook = { version = "0.1.6" }
|
||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
||||
serde_json = { version = "1.0" }
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3.4"
|
||||
features = [
|
||||
'Document',
|
||||
'Element',
|
||||
'HtmlCanvasElement',
|
||||
'WebGlBuffer',
|
||||
'WebGlRenderingContext',
|
||||
'WebGlProgram',
|
||||
'WebGlShader',
|
||||
'Window',
|
||||
'console'
|
||||
]
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.2"
|
136
ide/gui/lib/core/src/data/function/callback.rs
Normal file
136
ide/gui/lib/core/src/data/function/callback.rs
Normal file
@ -0,0 +1,136 @@
|
||||
use crate::data::function::nop::NOP;
|
||||
use crate::prelude::*;
|
||||
use std::fmt;
|
||||
|
||||
// =====================
|
||||
// === Callback Type ===
|
||||
// =====================
|
||||
|
||||
pub type NoCallback = ();
|
||||
|
||||
#[derive(Shrinkwrap)]
|
||||
#[shrinkwrap(mutable)]
|
||||
pub struct Callback<Func>(pub Func);
|
||||
|
||||
impl<Func> Default for Callback<Func> {
|
||||
fn default() -> Self {
|
||||
// Callback(NOP::nop())
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Func> fmt::Debug for Callback<Func> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Callback")
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================
|
||||
// === Callback Interface ===
|
||||
// ==========================
|
||||
|
||||
pub trait Callback0: 'static {
|
||||
fn call(&mut self);
|
||||
}
|
||||
|
||||
pub trait Callback1<Arg1> {
|
||||
fn call(&mut self, arg1: Arg1);
|
||||
}
|
||||
|
||||
pub trait Callback2<Arg1, Arg2> {
|
||||
fn call(&mut self, arg1: Arg1, arg2: Arg2);
|
||||
}
|
||||
|
||||
pub trait Callback3<Arg1, Arg2, Arg3> {
|
||||
fn call(&mut self, arg1: Arg1, arg2: Arg2, arg3: Arg3);
|
||||
}
|
||||
|
||||
pub trait Callback4<Arg1, Arg2, Arg3, Arg4> {
|
||||
fn call(&mut self, arg1: Arg1, arg2: Arg2, arg3: Arg3, arg4: Arg4);
|
||||
}
|
||||
|
||||
pub trait Callback5<Arg1, Arg2, Arg3, Arg4, Arg5> {
|
||||
fn call(&mut self, arg1: Arg1, arg2: Arg2, arg3: Arg3, arg4: Arg4, arg5: Arg5);
|
||||
}
|
||||
|
||||
// === Unit Implementations ===
|
||||
|
||||
impl Callback0 for () {
|
||||
fn call(&mut self) {}
|
||||
}
|
||||
|
||||
impl<Arg1> Callback1<Arg1> for () {
|
||||
fn call(&mut self, _arg1: Arg1) {}
|
||||
}
|
||||
|
||||
impl<Arg1, Arg2> Callback2<Arg1, Arg2> for () {
|
||||
fn call(&mut self, _arg1: Arg1, _arg2: Arg2) {}
|
||||
}
|
||||
|
||||
impl<Arg1, Arg2, Arg3> Callback3<Arg1, Arg2, Arg3> for () {
|
||||
fn call(&mut self, _arg1: Arg1, _arg2: Arg2, _arg3: Arg3) {}
|
||||
}
|
||||
|
||||
impl<Arg1, Arg2, Arg3, Arg4> Callback4<Arg1, Arg2, Arg3, Arg4> for () {
|
||||
fn call(&mut self, _arg1: Arg1, _arg2: Arg2, _arg3: Arg3, _arg4: Arg4) {}
|
||||
}
|
||||
|
||||
impl<Arg1, Arg2, Arg3, Arg4, Arg5> Callback5<Arg1, Arg2, Arg3, Arg4, Arg5> for () {
|
||||
fn call(&mut self, _arg1: Arg1, _arg2: Arg2, _arg3: Arg3, _arg4: Arg4, _arg5: Arg5) {}
|
||||
}
|
||||
|
||||
// === FnMut Implementations ===
|
||||
|
||||
// FIXME: How to make it more generic?
|
||||
impl<T: 'static, P: 'static> Callback0 for WithPhantomType<Rc<Fn() -> T>, P> {
|
||||
fn call(&mut self) {
|
||||
(self.t)();
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: How to make it more generic?
|
||||
impl<Arg1, T: 'static, P: 'static> Callback1<Arg1> for WithPhantomType<Rc<Fn(Arg1) -> T>, P> {
|
||||
fn call(&mut self, arg1: Arg1) {
|
||||
(self.t)(arg1);
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FnMut() -> T + 'static, T> Callback0 for F {
|
||||
fn call(&mut self) {
|
||||
self();
|
||||
}
|
||||
}
|
||||
|
||||
impl<Arg1, F: FnMut(Arg1) -> T, T> Callback1<Arg1> for F {
|
||||
fn call(&mut self, arg1: Arg1) {
|
||||
self(arg1);
|
||||
}
|
||||
}
|
||||
|
||||
impl<Arg1, Arg2, F: FnMut(Arg1, Arg2) -> T, T> Callback2<Arg1, Arg2> for F {
|
||||
fn call(&mut self, arg1: Arg1, arg2: Arg2) {
|
||||
self(arg1, arg2);
|
||||
}
|
||||
}
|
||||
|
||||
impl<Arg1, Arg2, Arg3, F: FnMut(Arg1, Arg2, Arg3) -> T, T> Callback3<Arg1, Arg2, Arg3> for F {
|
||||
fn call(&mut self, arg1: Arg1, arg2: Arg2, arg3: Arg3) {
|
||||
self(arg1, arg2, arg3);
|
||||
}
|
||||
}
|
||||
|
||||
impl<Arg1, Arg2, Arg3, Arg4, F: FnMut(Arg1, Arg2, Arg3, Arg4) -> T, T>
|
||||
Callback4<Arg1, Arg2, Arg3, Arg4> for F
|
||||
{
|
||||
fn call(&mut self, arg1: Arg1, arg2: Arg2, arg3: Arg3, arg4: Arg4) {
|
||||
self(arg1, arg2, arg3, arg4);
|
||||
}
|
||||
}
|
||||
|
||||
impl<Arg1, Arg2, Arg3, Arg4, Arg5, F: FnMut(Arg1, Arg2, Arg3, Arg4, Arg5) -> T, T>
|
||||
Callback5<Arg1, Arg2, Arg3, Arg4, Arg5> for F
|
||||
{
|
||||
fn call(&mut self, arg1: Arg1, arg2: Arg2, arg3: Arg3, arg4: Arg4, arg5: Arg5) {
|
||||
self(arg1, arg2, arg3, arg4, arg5);
|
||||
}
|
||||
}
|
83
ide/gui/lib/core/src/data/function/closure.rs
Normal file
83
ide/gui/lib/core/src/data/function/closure.rs
Normal file
@ -0,0 +1,83 @@
|
||||
// #[macro_export]
|
||||
// macro_rules! closure {
|
||||
// ($name:ident
|
||||
// <$($param:ident : $param_type:ty),*>
|
||||
// ($($arg:ident : $arg_type:ty),*)
|
||||
// |$($larg:ident : $larg_type:ty),*|
|
||||
// $body:tt
|
||||
// ) => {
|
||||
// closure!( $name<$($param:$param_type),*>
|
||||
// ($($arg:$arg_type),*)
|
||||
// ($($larg:$larg_type)*)
|
||||
// $body
|
||||
// );
|
||||
// };
|
||||
// ($name:ident
|
||||
// <$($param:ident : $param_type:ty),*>
|
||||
// ($($arg:ident : $arg_type:ty),*)
|
||||
// || $body:tt) => {
|
||||
// closure!($name<$($param:$param_type),*>($($arg:$arg_type),*)()$body);
|
||||
// };
|
||||
// ($name:ident
|
||||
// <$($param:ident : $param_type:ty),*>
|
||||
// ($($arg:ident : $arg_type:ty),*)
|
||||
// ($($larg:ident : $larg_type:ty),*)
|
||||
// $body:tt
|
||||
// ) => { paste::item! {
|
||||
// pub type [<Closure_ $name>]<$($param),*> =
|
||||
// impl Fn($($larg_type),*) + Clone;
|
||||
// pub fn $name<$($param:$param_type),*>
|
||||
// ($($arg:$arg_type),*) -> [<Closure_ $name>]<$($param),*> {
|
||||
// move |$($larg),*| $body
|
||||
// }
|
||||
// }};
|
||||
// }
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! closure {
|
||||
($name:ident
|
||||
<$($param:ident : $param_type:ty),*>
|
||||
($($arg:ident : $arg_type:ty),*)
|
||||
|$($larg:ident : $larg_type:ty),*|
|
||||
$body:tt
|
||||
) => {
|
||||
closure!( $name<$($param:$param_type),*>
|
||||
($($arg:$arg_type),*)
|
||||
($($larg:$larg_type)*)
|
||||
$body
|
||||
);
|
||||
};
|
||||
($name:ident
|
||||
<$($param:ident : $param_type:ty),*>
|
||||
($($arg:ident : $arg_type:ty),*)
|
||||
|| $body:tt) => {
|
||||
closure!($name<$($param:$param_type),*>($($arg:$arg_type),*)()$body);
|
||||
};
|
||||
($name:ident
|
||||
<$($param:ident : $param_type:ty),*>
|
||||
($($arg:ident : $arg_type:ty),*)
|
||||
($($larg:ident : $larg_type:ty),*)
|
||||
$body:tt
|
||||
) => { paste::item! {
|
||||
#[cfg(not(feature = "no_unboxed_callbacks"))]
|
||||
pub type [<Closure_ $name>]<$($param),*> =
|
||||
impl Fn($($larg_type),*) + Clone;
|
||||
|
||||
#[cfg(not(feature = "no_unboxed_callbacks"))]
|
||||
pub fn $name<$($param:$param_type),*>
|
||||
($($arg:$arg_type),*) -> [<Closure_ $name>]<$($param),*> {
|
||||
move |$($larg),*| $body
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_unboxed_callbacks")]
|
||||
pub type [<Closure_ $name>]<$($param),*> =
|
||||
WithPhantomType<Rc<dyn Fn($($larg_type),*)>, $($param),*>;
|
||||
|
||||
#[cfg(feature = "no_unboxed_callbacks")]
|
||||
pub fn $name<$($param:$param_type),*>
|
||||
($($arg:$arg_type),*)
|
||||
-> WithPhantomType<Rc<dyn Fn($($larg_type),*)>, $($param),*> {
|
||||
WithPhantomType::new(Rc::new(move |$($larg),*| $body))
|
||||
}
|
||||
}};
|
||||
}
|
3
ide/gui/lib/core/src/data/function/mod.rs
Normal file
3
ide/gui/lib/core/src/data/function/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub mod callback;
|
||||
pub mod closure;
|
||||
pub mod nop;
|
39
ide/gui/lib/core/src/data/function/nop.rs
Normal file
39
ide/gui/lib/core/src/data/function/nop.rs
Normal file
@ -0,0 +1,39 @@
|
||||
// ===========
|
||||
// === NOP ===
|
||||
// ===========
|
||||
|
||||
pub trait NOP {
|
||||
fn nop() -> Self;
|
||||
}
|
||||
|
||||
impl NOP for () {
|
||||
fn nop() -> Self {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T1> NOP for fn(T1) {
|
||||
fn nop() -> Self {
|
||||
|_| {}
|
||||
}
|
||||
}
|
||||
impl<T1, T2> NOP for fn(T1, T2) {
|
||||
fn nop() -> Self {
|
||||
|_, _| {}
|
||||
}
|
||||
}
|
||||
impl<T1, T2, T3> NOP for fn(T1, T2, T3) {
|
||||
fn nop() -> Self {
|
||||
|_, _, _| {}
|
||||
}
|
||||
}
|
||||
impl<T1, T2, T3, T4> NOP for fn(T1, T2, T3, T4) {
|
||||
fn nop() -> Self {
|
||||
|_, _, _, _| {}
|
||||
}
|
||||
}
|
||||
impl<T1, T2, T3, T4, T5> NOP for fn(T1, T2, T3, T4, T5) {
|
||||
fn nop() -> Self {
|
||||
|_, _, _, _, _| {}
|
||||
}
|
||||
}
|
3
ide/gui/lib/core/src/data/mod.rs
Normal file
3
ide/gui/lib/core/src/data/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub mod function;
|
||||
pub mod opt_vec;
|
||||
pub mod shared;
|
104
ide/gui/lib/core/src/data/opt_vec.rs
Normal file
104
ide/gui/lib/core/src/data/opt_vec.rs
Normal file
@ -0,0 +1,104 @@
|
||||
use crate::prelude::*;
|
||||
use std::iter::FilterMap;
|
||||
use std::slice;
|
||||
|
||||
// ==============
|
||||
// === OptVec ===
|
||||
// ==============
|
||||
|
||||
/// A contiguous growable sparse array type. Similar to `Vec<T>`, but allowing
|
||||
/// missing values. After a value is removed, it remembers the index for reuse
|
||||
/// in the future.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Default(bound = ""))]
|
||||
#[derive(Clone, Debug, Shrinkwrap)]
|
||||
pub struct OptVec<T> {
|
||||
#[shrinkwrap(main_field)]
|
||||
pub vec : Vec<Option<T>>,
|
||||
pub free : Vec<Ix>,
|
||||
}
|
||||
|
||||
pub type Ix = usize;
|
||||
pub type Iter<'t, T> = FilterMap<slice::Iter<'t, Option<T>>, OptionAsRef<T>>;
|
||||
pub type IterMut<'t, T> = FilterMap<slice::IterMut<'t, Option<T>>, OptionAsRefMut<T>>;
|
||||
pub type OptionAsRef<T> = for<'r> fn(&'r Option<T>) -> Option<&'r T>;
|
||||
pub type OptionAsRefMut<T> = for<'r> fn(&'r mut Option<T>) -> Option<&'r mut T>;
|
||||
|
||||
impl<T> OptVec<T> {
|
||||
/// Constructs a new, empty `Vec<T>`. It will not allocate until elements
|
||||
/// are pushed onto it.
|
||||
pub fn new() -> Self {
|
||||
let vec = default();
|
||||
let free = default();
|
||||
Self { vec, free }
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, item: T) -> Ix {
|
||||
self.insert_with_ix(|_| item)
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> Iter<T> {
|
||||
self.vec.iter().filter_map(Option::as_ref)
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> IterMut<T> {
|
||||
self.vec.iter_mut().filter_map(Option::as_mut)
|
||||
}
|
||||
|
||||
/// Finds a free index and inserts the element. The index is re-used in case
|
||||
/// the array is sparse or is added in case of no free places.
|
||||
pub fn insert_with_ix<F: FnOnce(Ix) -> T>(&mut self, f: F) -> Ix {
|
||||
match self.free.pop() {
|
||||
None => {
|
||||
let ix = self.vec.len();
|
||||
self.vec.push(Some(f(ix)));
|
||||
ix
|
||||
}
|
||||
Some(ix) => {
|
||||
self.vec[ix] = Some(f(ix));
|
||||
ix
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the element at provided index and marks the index to be reused.
|
||||
/// Does nothing if the index was already empty. Panics if the index was out
|
||||
/// of bounds.
|
||||
pub fn remove(&mut self, ix: Ix) -> Option<T> {
|
||||
let item = self.vec[ix].take();
|
||||
item.iter().for_each(|_| self.free.push(ix));
|
||||
item
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Index<usize> for OptVec<T> {
|
||||
type Output = T;
|
||||
fn index(&self, ix: usize) -> &Self::Output {
|
||||
self.vec.index(ix).as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IndexMut<usize> for OptVec<T> {
|
||||
fn index_mut(&mut self, ix: usize) -> &mut Self::Output {
|
||||
self.vec.index_mut(ix).as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_add() {
|
||||
let mut v = OptVec::new();
|
||||
let ix1 = v.insert(1);
|
||||
let ix2 = v.insert(2);
|
||||
v.remove(ix1);
|
||||
let ix3 = v.insert(3);
|
||||
let ix4 = v.insert(4);
|
||||
assert_eq!(ix1, 0);
|
||||
assert_eq!(ix2, 1);
|
||||
assert_eq!(ix3, 0);
|
||||
assert_eq!(ix4, 2);
|
||||
}
|
||||
}
|
126
ide/gui/lib/core/src/data/shared.rs
Normal file
126
ide/gui/lib/core/src/data/shared.rs
Normal file
@ -0,0 +1,126 @@
|
||||
use std::cell::Ref;
|
||||
use std::cell::RefCell;
|
||||
use std::cell::RefMut;
|
||||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
use std::ops::DerefMut;
|
||||
use std::rc::Rc;
|
||||
use std::rc::Weak;
|
||||
|
||||
// ==============
|
||||
// === Shared ===
|
||||
// ==============
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Shared<T> {
|
||||
v: Rc<RefCell<T>>
|
||||
}
|
||||
|
||||
impl <T> Shared<T> {
|
||||
pub fn new(t: T)-> Shared<T> {
|
||||
Shared{v: Rc::new(RefCell::new(t))}
|
||||
}
|
||||
}
|
||||
|
||||
impl <T> Shared<T> {
|
||||
pub fn clone_ref(&self) -> Self {
|
||||
Self { v: self.v.clone() }
|
||||
}
|
||||
|
||||
pub fn borrow(&self) -> Ref<T> {
|
||||
self.v.borrow()
|
||||
}
|
||||
|
||||
pub fn borrow_mut(&self) -> RefMut<T> {
|
||||
self.v.borrow_mut()
|
||||
}
|
||||
|
||||
pub fn as_ptr(&self) -> *mut T {
|
||||
self.v.as_ptr()
|
||||
}
|
||||
|
||||
pub fn downgrade(&self) -> WeakShared<T> {
|
||||
WeakShared { raw: Rc::downgrade(&self.v) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl <T: fmt::Display> fmt::Display for Shared<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.deref())
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: fmt::Debug> fmt::Debug for Shared<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self.deref())
|
||||
}
|
||||
}
|
||||
|
||||
impl <T> Deref for Shared<T>{
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
unsafe {self.as_ptr().as_ref().unwrap()}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl <T> DerefMut for Shared<T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe {self.as_ptr().as_mut().unwrap()}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ==================
|
||||
// === WeakShared ===
|
||||
// ==================
|
||||
|
||||
pub struct WeakShared<T> {
|
||||
raw: Weak<RefCell<T>>
|
||||
}
|
||||
|
||||
impl<T> WeakShared<T> {
|
||||
pub fn upgrade(&self) -> Option<Shared<T>> {
|
||||
let opt_raw = self.raw.upgrade();
|
||||
opt_raw.map(|v| Shared { v })
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: fmt::Debug> fmt::Debug for WeakShared<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self.deref())
|
||||
}
|
||||
}
|
||||
|
||||
// fn split (s: Shared<String>) -> Vec<String> {
|
||||
// s.split_whitespace().map(|s| s.to_string()).collect()
|
||||
// }
|
||||
|
||||
// pub fn main() {
|
||||
// let s = Shared::new("hello".to_string());
|
||||
// let s2 = s.clone();
|
||||
// s2.borrow_mut().push('!');
|
||||
// println!("{:?}",s2);
|
||||
|
||||
// // Deref kicking in...
|
||||
// let n = s2.len();
|
||||
|
||||
// println!("{:?}", n);
|
||||
|
||||
// // mutation has to be explicit
|
||||
// s2.borrow_mut().push_str(" dolly");
|
||||
|
||||
// println!("{:?} {}",s2.borrow(), s);
|
||||
|
||||
// println!("{:?}", split(s2.clone()));
|
||||
|
||||
|
||||
|
||||
// }
|
||||
|
||||
|
608
ide/gui/lib/core/src/lib.rs
Normal file
608
ide/gui/lib/core/src/lib.rs
Normal file
@ -0,0 +1,608 @@
|
||||
#![feature(type_ascription)]
|
||||
#![feature(unboxed_closures)]
|
||||
#![cfg_attr(test, allow(dead_code))]
|
||||
#![feature(trait_alias)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![feature(proc_macro_hygiene)]
|
||||
#![feature(specialization)]
|
||||
#![feature(weak_into_raw)]
|
||||
#![feature(associated_type_defaults)]
|
||||
#![feature(set_stdio)]
|
||||
#![feature(overlapping_marker_traits)]
|
||||
//#![warn(missing_docs)]
|
||||
|
||||
// Lints. To be refactored after this gets resolved: https://github.com/rust-lang/cargo/issues/5034
|
||||
#![allow(clippy::option_map_unit_fn)]
|
||||
|
||||
#![feature(generators, generator_trait)]
|
||||
|
||||
|
||||
use std::ops::{Generator, GeneratorState};
|
||||
use std::pin::Pin;
|
||||
|
||||
pub use basegl_prelude as prelude;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use prelude::*;
|
||||
use structology::*;
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde_json;
|
||||
|
||||
#[derive(Iterator)]
|
||||
pub struct Demo<T> (T, u32, T);
|
||||
// pub struct Demo<T> { t: T, x:u32, z:T }
|
||||
|
||||
// struct Demo<'a, T: ?Sized> {
|
||||
// a: Box<T>,
|
||||
// b: u8,
|
||||
// c: &'a str,
|
||||
// d: String,
|
||||
// }
|
||||
|
||||
pub type Stream<T> = Vec<T>;
|
||||
|
||||
|
||||
// ============
|
||||
// === Tree ===
|
||||
// ============
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub struct Tree<K,V> where K: Eq + Hash {
|
||||
pub value : Option<V>,
|
||||
pub branches : HashMap<K, Tree<K,V>>,
|
||||
}
|
||||
|
||||
// ===============
|
||||
// === Shifted ===
|
||||
// ===============
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Serialize, Deserialize, Shrinkwrap)]
|
||||
#[shrinkwrap(mutable)]
|
||||
pub struct Shifted<T> {
|
||||
#[shrinkwrap(main_field)]
|
||||
pub wrapped : T,
|
||||
pub off : usize,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub struct ShiftedVec1<T> {
|
||||
pub head: T,
|
||||
pub tail: Vec<Shifted<T>>
|
||||
}
|
||||
|
||||
|
||||
// ===============
|
||||
// === Layered ===
|
||||
// ===============
|
||||
|
||||
pub trait Layer<T> {
|
||||
fn layered(t: T) -> Self;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Shrinkwrap)]
|
||||
#[shrinkwrap(mutable)]
|
||||
pub struct Layered<T>(pub T);
|
||||
|
||||
impl<T> Layer<T> for Layered<T> {
|
||||
fn layered(t: T) -> Self { Layered(t) }
|
||||
}
|
||||
|
||||
|
||||
// ===========
|
||||
// === AST ===
|
||||
// ===========
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Shrinkwrap, Serialize, Deserialize)]
|
||||
#[shrinkwrap(mutable)]
|
||||
pub struct Ast {
|
||||
#[serde(flatten)]
|
||||
pub wrapped: Rc<WithID<WithSpan<Shape<Ast>>>>
|
||||
}
|
||||
|
||||
|
||||
impl Ast {
|
||||
fn iter(&self) -> Rc<dyn Iterator<Item = &'_ Shape<Ast>> + '_> {
|
||||
Rc::new(IterGen(move || {
|
||||
let curr = &self.wrapped.wrapped.wrapped;
|
||||
yield curr;
|
||||
|
||||
if let Shape::App(t) = curr {
|
||||
for elem in (*t.func).iter() { yield elem }
|
||||
for elem in (*t.arg).iter() { yield elem }
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Spanned + Into<Shape<Ast>>>
|
||||
From<T> for Ast {
|
||||
fn from(t: T) -> Self {
|
||||
let span = t.span();
|
||||
let shape = t.into();
|
||||
let with_span = WithSpan { wrapped: shape, span };
|
||||
let with_id = WithID { wrapped: with_span, id: None };
|
||||
Ast { wrapped: Rc::new(with_id) }
|
||||
}
|
||||
}
|
||||
|
||||
// =============
|
||||
// === Shape ===
|
||||
// =============
|
||||
|
||||
#[ast(flat)] pub enum Shape<T> {
|
||||
|
||||
// === Identifiers ===
|
||||
Blank { },
|
||||
Var { name : String },
|
||||
Cons { name : String },
|
||||
Opr { name : String },
|
||||
Mod { name : String },
|
||||
|
||||
// === Literals ===
|
||||
Number { base: Option<String>, int: String },
|
||||
Text (Text<T>),
|
||||
|
||||
// === Expressions ===
|
||||
App { func : T , off : usize , arg: T },
|
||||
Infix { larg : T , loff : usize , opr: Opr , roff: usize , rarg: T },
|
||||
SectLeft { arg : T , off : usize , opr: Opr },
|
||||
SectRight { opr : Opr , off : usize , arg: T },
|
||||
SectSides { opr : Opr },
|
||||
|
||||
Invalid (Invalid<T>),
|
||||
Block (Block<T>),
|
||||
Module (Module<T>),
|
||||
Macro (Macro<T>),
|
||||
Comment (Comment),
|
||||
Import (Import<T>),
|
||||
Mixfix (Mixfix<T>),
|
||||
Group (Group<T>),
|
||||
Def (Def<T>),
|
||||
Foreign (Foreign),
|
||||
}
|
||||
|
||||
// ===============
|
||||
// === Invalid ===
|
||||
// ===============
|
||||
|
||||
#[ast] pub enum Invalid<T> {
|
||||
Unrecognized { input : String },
|
||||
Unexpected { msg : String, stream: Stream<T> },
|
||||
InvalidSuffex { elem : T, suffix: String },
|
||||
// DanglingBase
|
||||
}
|
||||
|
||||
|
||||
// ============
|
||||
// === Text ===
|
||||
// ============
|
||||
|
||||
#[ast] pub enum Text<T> {
|
||||
Raw { body: TextBody<TextRawSegment> },
|
||||
Fmt { body: TextBody<TextFmtSegment<T>> }
|
||||
}
|
||||
|
||||
#[ast] pub struct TextRawSegment {
|
||||
pub value: String
|
||||
}
|
||||
|
||||
#[ast] pub enum TextFmtSegment<T> {
|
||||
Plain { value : String },
|
||||
Expr { value : Option<T> },
|
||||
Escape { escape : TextEscape },
|
||||
}
|
||||
|
||||
pub type TextBlock<T> = Vec<TextLine<T>>;
|
||||
#[ast] pub struct TextLine<T> { off: usize, elems: Vec<T> }
|
||||
#[ast] pub struct TextBody<T> { quote: TextQuote, block: TextBlock<T> }
|
||||
#[ast] pub enum TextQuote { Single, Triple }
|
||||
#[ast] pub struct TextEscape {} // TODO
|
||||
|
||||
|
||||
// =============
|
||||
// === Block ===
|
||||
// =============
|
||||
|
||||
#[ast] pub struct Block<T> {
|
||||
ty : BlockType,
|
||||
ident : usize,
|
||||
empty_lines : usize,
|
||||
first_line : BlockLine<T>,
|
||||
lines : Vec<BlockLine<Option<T>>>,
|
||||
is_orphan : bool,
|
||||
}
|
||||
|
||||
#[ast] pub enum BlockType { Continuous, Discontinuous }
|
||||
#[ast] pub struct BlockLine <T> { elem: T, off: usize }
|
||||
|
||||
// ==============
|
||||
// === Module ===
|
||||
// ==============
|
||||
|
||||
#[ast] pub struct Module<T> { lines: Vec<BlockLine<Option<T>>> }
|
||||
|
||||
// =============
|
||||
// === Macro ===
|
||||
// =============
|
||||
|
||||
#[ast] pub enum Macro<T> {
|
||||
Match (Match<T>),
|
||||
Ambiguous (Ambiguous),
|
||||
}
|
||||
|
||||
#[ast] pub struct MacroMatch<T> {
|
||||
pub pfx : Option<MacroPatternMatch<Ast>>,
|
||||
pub segs : ShiftedVec1<MacroMatchSegment<T>>,
|
||||
pub resolved : Ast
|
||||
}
|
||||
|
||||
#[ast] pub struct MacroAmbiguous {
|
||||
pub segs : ShiftedVec1<MacroAmbiguousSegment>,
|
||||
// pub paths : Tree<Ast, ()>, // FIXME
|
||||
}
|
||||
|
||||
#[ast] pub struct MacroMatchSegment<T> {
|
||||
pub head : Ast,
|
||||
pub body : MacroPatternMatch<Shifted<T>>
|
||||
}
|
||||
|
||||
#[ast] pub struct MacroAmbiguousSegment {
|
||||
pub head: Ast,
|
||||
pub body: Option<Shifted<Ast>>
|
||||
}
|
||||
|
||||
pub type MacroPattern = Rc<MacroPatternRaw>;
|
||||
#[ast] pub enum MacroPatternRaw {
|
||||
|
||||
// === Boundary Patterns ===
|
||||
Begin ,
|
||||
End ,
|
||||
|
||||
// === Structural Patterns ===
|
||||
Nothing ,
|
||||
Seq { pat1 : MacroPattern , pat2 : MacroPattern },
|
||||
Or { pat1 : MacroPattern , pat2 : MacroPattern },
|
||||
Many { pat : MacroPattern },
|
||||
Except { not : MacroPattern, pat : MacroPattern },
|
||||
|
||||
// === Meta Patterns ===
|
||||
Build { pat : MacroPattern },
|
||||
Err { msg : String , pat : MacroPattern },
|
||||
Tag { tag : String , pat : MacroPattern },
|
||||
Cls { cls : PatternClass , pat : MacroPattern },
|
||||
|
||||
// === Token Patterns ===
|
||||
Tok { spaced : Spaced , ast : Ast },
|
||||
Blank { spaced : Spaced },
|
||||
Var { spaced : Spaced },
|
||||
Cons { spaced : Spaced },
|
||||
Opr { spaced : Spaced , max_prec : Option<usize> },
|
||||
Mod { spaced : Spaced },
|
||||
Num { spaced : Spaced },
|
||||
Text { spaced : Spaced },
|
||||
Block { spaced : Spaced },
|
||||
Macro { spaced : Spaced },
|
||||
Invalid { spaced : Spaced },
|
||||
}
|
||||
|
||||
#[ast] pub enum PatternClass { Normal, Pattern }
|
||||
pub type Spaced = Option<bool>;
|
||||
|
||||
#[derive(Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
|
||||
pub enum Either<L,R> { Left(L), Right(R) }
|
||||
pub type Switch<T> = Either<T,T>;
|
||||
|
||||
pub type MacroPatternMatch<T> = Rc<FooRaw<T>>;
|
||||
#[ast] pub enum FooRaw<T> {
|
||||
|
||||
// === Boundary Matches ===
|
||||
Begin { pat: MacroPatternRawBegin },
|
||||
End { pat: MacroPatternRawEnd },
|
||||
|
||||
// === Structural Matches ===
|
||||
Nothing { pat: MacroPatternRawNothing },
|
||||
Seq { pat: MacroPatternRawSeq , elem: (MacroPatternMatch<T>, MacroPatternMatch<T>) },
|
||||
Or { pat: MacroPatternRawOr , elem: Switch<MacroPatternMatch<T>>},
|
||||
Many { pat: MacroPatternRawMany , elem: Vec<MacroPatternMatch<T>> },
|
||||
Except { pat: MacroPatternRawExcept , elem: MacroPatternMatch<T> },
|
||||
|
||||
// === Meta Matches ===
|
||||
Build { pat: MacroPatternRawBuild , elem: T },
|
||||
Err { pat: MacroPatternRawErr , elem: T },
|
||||
Tag { pat: MacroPatternRawTag , elem: MacroPatternMatch<T> },
|
||||
Cls { pat: MacroPatternRawCls , elem: MacroPatternMatch<T> },
|
||||
|
||||
// === Token Matches ===
|
||||
Tok { pat: MacroPatternRawTok , elem: T },
|
||||
Blank { pat: MacroPatternRawBlank , elem: T },
|
||||
Var { pat: MacroPatternRawVar , elem: T },
|
||||
Cons { pat: MacroPatternRawCons , elem: T },
|
||||
Opr { pat: MacroPatternRawOpr , elem: T },
|
||||
Mod { pat: MacroPatternRawMod , elem: T },
|
||||
Num { pat: MacroPatternRawNum , elem: T },
|
||||
Text { pat: MacroPatternRawText , elem: T },
|
||||
Block { pat: MacroPatternRawBlock , elem: T },
|
||||
Macro { pat: MacroPatternRawMacro , elem: T },
|
||||
Invalid { pat: MacroPatternRawInvalid , elem: T },
|
||||
|
||||
}
|
||||
|
||||
|
||||
// =============================================================================
|
||||
// === Spaceless AST ===========================================================
|
||||
// =============================================================================
|
||||
|
||||
#[ast] pub struct Comment {
|
||||
pub lines: Vec<String>
|
||||
}
|
||||
|
||||
#[ast] pub struct Import<T> {
|
||||
pub lines: Vec<T>
|
||||
}
|
||||
|
||||
#[ast] pub struct Mixfix<T> {
|
||||
pub name: Vec<T>,
|
||||
pub args: Vec<T>,
|
||||
}
|
||||
|
||||
#[ast] pub struct Group<T> {
|
||||
pub body: Option<T>,
|
||||
}
|
||||
|
||||
#[ast] pub struct Def<T> {
|
||||
pub name: Cons,
|
||||
pub args: Vec<T>,
|
||||
pub body: Option<T>
|
||||
}
|
||||
|
||||
#[ast] pub struct Foreign {
|
||||
pub indent : usize,
|
||||
pub lang : String,
|
||||
pub code : Vec<String>
|
||||
}
|
||||
|
||||
|
||||
// =============================================================================
|
||||
// === Spaceless AST ===========================================================
|
||||
// =============================================================================
|
||||
|
||||
|
||||
|
||||
impl Var {
|
||||
pub fn new(name: String) -> Ast {
|
||||
Ast::from (Var { name })
|
||||
}
|
||||
}
|
||||
|
||||
impl Spanned for Var {}
|
||||
|
||||
impl<T> Shape<T> {
|
||||
pub fn iter(&self) -> Box<dyn Iterator<Item = &'_ T> + '_> {
|
||||
Box::new(IterGen(move || {
|
||||
match self {
|
||||
Shape::App(t) => { yield &t.func; yield &t.arg; }
|
||||
_ => {}
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Visitor<'t> {
|
||||
pub to_be_visited: Vec<&'t mut Ast>,
|
||||
}
|
||||
|
||||
impl<'t> Visitor<'t> {
|
||||
pub fn visit(&mut self, t: &'t mut Ast) {
|
||||
self.to_be_visited.push(t);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InstancedVisitor<'t, Instance> {
|
||||
pub visitor : Visitor<'t>,
|
||||
pub instance : Instance
|
||||
}
|
||||
|
||||
impl<'t, Instance> InstancedVisitor<'t, Instance>
|
||||
where Instance: VisitorFor<'t, App<Ast>> {
|
||||
|
||||
pub fn run(&mut self) {
|
||||
// loop {
|
||||
// match self.visitor.to_be_visited.pop() {
|
||||
// None => break,
|
||||
// Some(t) => self.step(t)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
pub fn step(&mut self, t: &'t mut Shape<Ast>) {
|
||||
match t {
|
||||
Shape::App(t) => self.instance.visit(&mut self.visitor, t),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub trait VisitorFor<'t, T: 't>
|
||||
where &'t mut T: IntoIterator<Item = &'t mut Ast> {
|
||||
fn visit(&mut self, visitor: &mut Visitor<'t>, t: &'t mut T) {
|
||||
t.into_iter().for_each(|s| visitor.visit(s))
|
||||
}
|
||||
}
|
||||
|
||||
// ===========
|
||||
// === AST ===
|
||||
// ===========
|
||||
|
||||
pub trait Spanned {
|
||||
fn span(&self) -> usize { 0 }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// === WithID ===
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Shrinkwrap, Serialize, Deserialize)]
|
||||
#[shrinkwrap(mutable)]
|
||||
pub struct WithID<T> {
|
||||
#[shrinkwrap(main_field)]
|
||||
#[serde(flatten)]
|
||||
pub wrapped: T,
|
||||
pub id: Option<i32>
|
||||
}
|
||||
|
||||
impl<T, S:Layer<T>>
|
||||
Layer<T> for WithID<S> {
|
||||
fn layered(t: T) -> Self {
|
||||
WithID { wrapped: Layer::layered(t), id: None }
|
||||
}
|
||||
}
|
||||
|
||||
// === WithSpan ===
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Shrinkwrap, Serialize, Deserialize)]
|
||||
#[shrinkwrap(mutable)]
|
||||
pub struct WithSpan<T> {
|
||||
#[shrinkwrap(main_field)]
|
||||
#[serde(flatten)]
|
||||
pub wrapped: T,
|
||||
pub span: usize
|
||||
}
|
||||
|
||||
impl<T> Spanned for WithSpan<T> {
|
||||
fn span(&self) -> usize { self.span }
|
||||
}
|
||||
|
||||
impl<T:Spanned, S:Layer<T>>
|
||||
Layer<T> for WithSpan<S> {
|
||||
fn layered(t: T) -> Self {
|
||||
let span = t.span();
|
||||
WithSpan { wrapped: Layer::layered(t), span }
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================
|
||||
|
||||
|
||||
|
||||
|
||||
impl<T> Spanned for Shape<T>{}
|
||||
|
||||
// ==== boilerplate
|
||||
|
||||
|
||||
|
||||
pub struct IterGen<G: Generator>(pub G);
|
||||
|
||||
impl<G> Iterator for IterGen<G>
|
||||
where G: Generator<Return = ()> + Unpin {
|
||||
type Item = G::Yield;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match { Pin::new(&mut self.0).resume() } {
|
||||
GeneratorState::Yielded(element) => Some(element),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === Utils ===
|
||||
// =============
|
||||
|
||||
use console_error_panic_hook;
|
||||
|
||||
fn main() {
|
||||
let a = Var { name: "foo".to_string() };
|
||||
let b: Shape<Ast> = Shape::from(a);
|
||||
let c: Ast = Ast::from(b);
|
||||
let serialized = serde_json::to_string(&c).unwrap();
|
||||
let d: Ast = serde_json::from_str(&serialized).unwrap();
|
||||
println!("serialized = {}", serialized);
|
||||
println!("deserialized = {:?}", d);
|
||||
}
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn start() {
|
||||
console_error_panic_hook::set_once();
|
||||
set_stdout();
|
||||
main();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////
|
||||
|
||||
type PrintFn = fn(&str) -> std::io::Result<()>;
|
||||
|
||||
struct Printer {
|
||||
printfn: PrintFn,
|
||||
buffer: String,
|
||||
is_buffered: bool,
|
||||
}
|
||||
|
||||
impl Printer {
|
||||
fn new(printfn: PrintFn, is_buffered: bool) -> Printer {
|
||||
Printer {
|
||||
buffer: String::new(),
|
||||
printfn,
|
||||
is_buffered,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::io::Write for Printer {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
self.buffer.push_str(&String::from_utf8_lossy(buf));
|
||||
|
||||
if !self.is_buffered {
|
||||
(self.printfn)(&self.buffer)?;
|
||||
self.buffer.clear();
|
||||
|
||||
return Ok(buf.len());
|
||||
}
|
||||
|
||||
if let Some(i) = self.buffer.rfind('\n') {
|
||||
let buffered = {
|
||||
let (first, last) = self.buffer.split_at(i);
|
||||
(self.printfn)(first)?;
|
||||
|
||||
String::from(&last[1..])
|
||||
};
|
||||
|
||||
self.buffer.clear();
|
||||
self.buffer.push_str(&buffered);
|
||||
}
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
(self.printfn)(&self.buffer)?;
|
||||
self.buffer.clear();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn _print(msg: &str) -> std::io::Result<()> {
|
||||
web_sys::console::info_1(&msg.to_string().into());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
pub fn set_stdout() {
|
||||
let printer = Printer::new(_print, true);
|
||||
std::io::set_print(Some(Box::new(printer)));
|
||||
}
|
||||
|
||||
pub fn set_stdout_unbuffered() {
|
||||
let printer = Printer::new(_print, false);
|
||||
std::io::set_print(Some(Box::new(printer)));
|
||||
}
|
358
ide/gui/lib/core/src/lib2.rs
Normal file
358
ide/gui/lib/core/src/lib2.rs
Normal file
@ -0,0 +1,358 @@
|
||||
#![feature(type_ascription)]
|
||||
#![feature(unboxed_closures)]
|
||||
#![cfg_attr(test, allow(dead_code))]
|
||||
#![feature(trait_alias)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![feature(proc_macro_hygiene)]
|
||||
#![feature(specialization)]
|
||||
#![feature(weak_into_raw)]
|
||||
#![feature(associated_type_defaults)]
|
||||
#![feature(set_stdio)]
|
||||
#![feature(overlapping_marker_traits)]
|
||||
//#![warn(missing_docs)]
|
||||
|
||||
// Lints. To be refactored after this gets resolved: https://github.com/rust-lang/cargo/issues/5034
|
||||
#![allow(clippy::option_map_unit_fn)]
|
||||
|
||||
#![feature(generators, generator_trait)]
|
||||
|
||||
|
||||
use std::ops::{Generator, GeneratorState};
|
||||
use std::pin::Pin;
|
||||
|
||||
pub use basegl_prelude as prelude;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use prelude::*;
|
||||
|
||||
// =============
|
||||
// === Shape ===
|
||||
// =============
|
||||
|
||||
#[derive(Debug)] pub struct Var { name: String }
|
||||
#[derive(Debug)] pub struct Opr { name: String }
|
||||
#[derive(Debug)] pub struct App <T> { func: T, off: i32, arg: T }
|
||||
#[derive(Debug)] pub struct SectLeft <T> { arg: T, off: i32, opr: Opr }
|
||||
#[derive(Debug)] pub struct SectRight <T> { opr: Opr, off: i32, arg: T }
|
||||
#[derive(Debug)] pub struct SectSides { opr: Opr }
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Shape<T> {
|
||||
Var(Var),
|
||||
App(App<T>),
|
||||
}
|
||||
|
||||
|
||||
type AppIterator<'t, T> = impl Iterator<Item = &'t T>;
|
||||
pub fn app_iterator<'t, T>(t: &'t App<T>) -> AppIterator<'t, T> {
|
||||
IterGen(move || {
|
||||
yield &t.func;
|
||||
yield &t.arg;
|
||||
})
|
||||
}
|
||||
|
||||
type AppIteratorMut<'t, T> = impl Iterator<Item = &'t mut T>;
|
||||
pub fn app_iterator_mut<'t, T>(t: &'t mut App<T>) -> AppIteratorMut<'t, T> {
|
||||
IterGen(move || {
|
||||
yield &mut t.func;
|
||||
yield &mut t.arg;
|
||||
})
|
||||
}
|
||||
|
||||
impl<T> App<T> {
|
||||
pub fn iter(&self) -> Box<dyn Iterator<Item = &'_ T> + '_> {
|
||||
Box::new(IterGen(move || {
|
||||
yield &self.func;
|
||||
yield &self.arg;
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'t, T> IntoIterator for &'t App<T> {
|
||||
type Item = &'t T;
|
||||
type IntoIter = AppIterator<'t, T>;
|
||||
fn into_iter(self) -> AppIterator<'t, T> { app_iterator(self) }
|
||||
}
|
||||
|
||||
impl<'t, T> IntoIterator for &'t mut App<T> {
|
||||
type Item = &'t mut T;
|
||||
type IntoIter = AppIteratorMut<'t, T>;
|
||||
fn into_iter(self) -> AppIteratorMut<'t, T> { app_iterator_mut(self) }
|
||||
}
|
||||
|
||||
|
||||
impl<T> Shape<T> {
|
||||
pub fn iter(&self) -> Box<dyn Iterator<Item = &'_ T> + '_> {
|
||||
Box::new(IterGen(move || {
|
||||
match self {
|
||||
Shape::App(t) => { yield &t.func; yield &t.arg; }
|
||||
_ => {}
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Visitor<'t> {
|
||||
pub to_be_visited: Vec<&'t mut Ast>,
|
||||
}
|
||||
|
||||
impl<'t> Visitor<'t> {
|
||||
pub fn visit(&mut self, t: &'t mut Ast) {
|
||||
self.to_be_visited.push(t);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InstancedVisitor<'t, Instance> {
|
||||
pub visitor : Visitor<'t>,
|
||||
pub instance : Instance
|
||||
}
|
||||
|
||||
impl<'t, Instance> InstancedVisitor<'t, Instance>
|
||||
where Instance: VisitorFor<'t, App<Ast>> {
|
||||
|
||||
pub fn run(&mut self) {
|
||||
loop {
|
||||
match self.visitor.to_be_visited.pop() {
|
||||
None => break,
|
||||
Some(t) => self.step(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn step(&mut self, t: &'t mut Shape<Ast>) {
|
||||
match t {
|
||||
Shape::App(t) => self.instance.visit(&mut self.visitor, t),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub trait VisitorFor<'t, T: 't>
|
||||
where &'t mut T: IntoIterator<Item = &'t mut Ast> {
|
||||
fn visit(&mut self, visitor: &mut Visitor<'t>, t: &'t mut T) {
|
||||
t.into_iter().for_each(|s| visitor.visit(s))
|
||||
}
|
||||
}
|
||||
|
||||
// ===========
|
||||
// === AST ===
|
||||
// ===========
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Shrinkwrap)]
|
||||
#[shrinkwrap(mutable)]
|
||||
pub struct WithID<T> {
|
||||
#[shrinkwrap(main_field)]
|
||||
pub wrapped: T,
|
||||
pub id: Option<i32>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Shrinkwrap)]
|
||||
#[shrinkwrap(mutable)]
|
||||
pub struct WithSpan<T> {
|
||||
#[shrinkwrap(main_field)]
|
||||
pub wrapped: T,
|
||||
pub span: i32
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Shrinkwrap)]
|
||||
#[shrinkwrap(mutable)]
|
||||
pub struct Ast {
|
||||
pub wrapped: Box<WithID<WithSpan<Shape<Ast>>>>
|
||||
}
|
||||
|
||||
|
||||
impl Ast {
|
||||
fn iter(&self) -> Box<dyn Iterator<Item = &'_ Shape<Ast>> + '_> {
|
||||
Box::new(IterGen(move || {
|
||||
let curr = &self.wrapped.wrapped.wrapped;
|
||||
yield curr;
|
||||
|
||||
if let Shape::App(t) = curr {
|
||||
for elem in t.func.iter() { yield elem }
|
||||
for elem in t.arg.iter() { yield elem }
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ==== boilerplate
|
||||
|
||||
|
||||
|
||||
pub struct IterGen<G: Generator>(pub G);
|
||||
|
||||
impl<G> Iterator for IterGen<G>
|
||||
where G: Generator<Return = ()> + Unpin {
|
||||
type Item = G::Yield;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match { Pin::new(&mut self.0).resume() } {
|
||||
GeneratorState::Yielded(element) => Some(element),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === Utils ===
|
||||
// =============
|
||||
|
||||
|
||||
fn main() {
|
||||
let node = |ast_of| Ast { wrapped: Box::new(WithID { wrapped: ast_of, id: None })};
|
||||
|
||||
// let ast = node(
|
||||
// Shape::App {
|
||||
// func: node(
|
||||
// Shape::App {
|
||||
// func: node(Shape::Var { name: "foo".to_owned() }),
|
||||
// off: 0,
|
||||
// arg: node(Shape::Var { name: "bar".to_owned() })
|
||||
// }
|
||||
// ),
|
||||
// off: 0,
|
||||
// arg: node(Shape::Var { name: "baz".to_owned() })
|
||||
// }
|
||||
// );
|
||||
|
||||
// let ast = mk_nodes(100000);
|
||||
|
||||
// let mut i: i32 = 0;
|
||||
// for node in ast.iter() {
|
||||
// // dbg!(node);
|
||||
// i += 1;
|
||||
// }
|
||||
// dbg!(i);
|
||||
}
|
||||
|
||||
// fn mk_nodes(depth: i32) -> Ast {
|
||||
// let node = |ast_of| Ast { wrapped: Box::new(WithID { elem: ast_of, id: None })};
|
||||
|
||||
// let mut ast = node(Shape::App {
|
||||
// func: node(Shape::Var { name: "foo".to_owned() }),
|
||||
// off: 0,
|
||||
// arg: node(Shape::Var { name: "bar".to_owned() })
|
||||
// });
|
||||
|
||||
// for x in 0..depth {
|
||||
// ast = node( Shape::App {
|
||||
// func: node(Shape::Var { name: "foo".to_owned() }),
|
||||
// off: 0,
|
||||
// arg: ast
|
||||
// })
|
||||
// };
|
||||
|
||||
// ast
|
||||
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ==== boilerplate
|
||||
|
||||
|
||||
|
||||
|
||||
// =================================
|
||||
// === Module Structure Reexport ===
|
||||
// =================================
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
use console_error_panic_hook;
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn start() {
|
||||
console_error_panic_hook::set_once();
|
||||
set_stdout();
|
||||
println!("----");
|
||||
|
||||
main();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////
|
||||
|
||||
type PrintFn = fn(&str) -> std::io::Result<()>;
|
||||
|
||||
struct Printer {
|
||||
printfn: PrintFn,
|
||||
buffer: String,
|
||||
is_buffered: bool,
|
||||
}
|
||||
|
||||
impl Printer {
|
||||
fn new(printfn: PrintFn, is_buffered: bool) -> Printer {
|
||||
Printer {
|
||||
buffer: String::new(),
|
||||
printfn,
|
||||
is_buffered,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::io::Write for Printer {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
self.buffer.push_str(&String::from_utf8_lossy(buf));
|
||||
|
||||
if !self.is_buffered {
|
||||
(self.printfn)(&self.buffer)?;
|
||||
self.buffer.clear();
|
||||
|
||||
return Ok(buf.len());
|
||||
}
|
||||
|
||||
if let Some(i) = self.buffer.rfind('\n') {
|
||||
let buffered = {
|
||||
let (first, last) = self.buffer.split_at(i);
|
||||
(self.printfn)(first)?;
|
||||
|
||||
String::from(&last[1..])
|
||||
};
|
||||
|
||||
self.buffer.clear();
|
||||
self.buffer.push_str(&buffered);
|
||||
}
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
(self.printfn)(&self.buffer)?;
|
||||
self.buffer.clear();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn _print(msg: &str) -> std::io::Result<()> {
|
||||
web_sys::console::info_1(&msg.to_string().into());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
pub fn set_stdout() {
|
||||
let printer = Printer::new(_print, true);
|
||||
std::io::set_print(Some(Box::new(printer)));
|
||||
}
|
||||
|
||||
pub fn set_stdout_unbuffered() {
|
||||
let printer = Printer::new(_print, false);
|
||||
std::io::set_print(Some(Box::new(printer)));
|
||||
}
|
16
ide/gui/lib/prelude/Cargo.toml
Normal file
16
ide/gui/lib/prelude/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "basegl-prelude"
|
||||
version = "0.1.0"
|
||||
authors = ["Wojciech Danilo <wojciech.danilo@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
|
||||
[dependencies]
|
||||
failure = "0.1.5"
|
||||
derive_more = "0.15.0"
|
||||
shrinkwraprs = "0.2.1"
|
||||
itertools = "0.8"
|
||||
derivative = "1.0.3"
|
||||
num = "0.2.0"
|
||||
boolinator = "2.4.0"
|
58
ide/gui/lib/prelude/src/lib.rs
Normal file
58
ide/gui/lib/prelude/src/lib.rs
Normal file
@ -0,0 +1,58 @@
|
||||
#![feature(trait_alias)]
|
||||
|
||||
pub use boolinator::Boolinator;
|
||||
pub use core::any::type_name;
|
||||
pub use core::fmt::Debug;
|
||||
pub use derivative::Derivative;
|
||||
pub use derive_more::*;
|
||||
pub use failure::Fail;
|
||||
pub use itertools::Itertools;
|
||||
pub use num::Num;
|
||||
pub use shrinkwraprs::Shrinkwrap;
|
||||
pub use std::cell::Ref;
|
||||
pub use std::cell::RefCell;
|
||||
pub use std::collections::HashMap;
|
||||
pub use std::collections::HashSet;
|
||||
pub use std::convert::identity;
|
||||
pub use std::convert::TryFrom;
|
||||
pub use std::convert::TryInto;
|
||||
pub use std::fmt::Display;
|
||||
pub use std::hash::Hash;
|
||||
pub use std::iter;
|
||||
pub use std::iter::FromIterator;
|
||||
pub use std::marker::PhantomData;
|
||||
pub use std::ops::Deref;
|
||||
pub use std::ops::DerefMut;
|
||||
pub use std::ops::Index;
|
||||
pub use std::ops::IndexMut;
|
||||
pub use std::rc::Rc;
|
||||
pub use std::rc::Weak;
|
||||
pub use std::slice;
|
||||
pub use std::slice::SliceIndex;
|
||||
|
||||
pub trait Str = AsRef<str>;
|
||||
|
||||
pub fn default<T: Default>() -> T {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derive(Shrinkwrap)]
|
||||
#[shrinkwrap(mutable)]
|
||||
#[derivative(Clone(bound="T: Clone"))]
|
||||
pub struct WithPhantomType<T, P=()> {
|
||||
#[shrinkwrap(main_field)]
|
||||
pub t: T,
|
||||
phantom: PhantomData<P>
|
||||
}
|
||||
|
||||
impl<T, P> WithPhantomType<T, P> {
|
||||
pub fn new(t: T) -> Self {
|
||||
let phantom = PhantomData;
|
||||
Self { t, phantom }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with<T, F: FnOnce(T) -> Out, Out>(t: T, f: F) -> Out {
|
||||
f(t)
|
||||
}
|
15
ide/gui/lib/structology/core/Cargo.toml
Normal file
15
ide/gui/lib/structology/core/Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "structology"
|
||||
version = "0.1.0"
|
||||
authors = ["Wojciech Danilo <wojciech.danilo@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
structology-macros = { version = "0.1.0" , path = "../macros" }
|
||||
basegl-prelude = { version = "0.1.0" , path = "../../prelude" }
|
||||
|
45
ide/gui/lib/structology/core/src/lib.rs
Normal file
45
ide/gui/lib/structology/core/src/lib.rs
Normal file
@ -0,0 +1,45 @@
|
||||
#![feature(generators, generator_trait)]
|
||||
|
||||
pub use structology_macros::*;
|
||||
|
||||
use std::ops::Generator;
|
||||
use std::ops::GeneratorState;
|
||||
use std::pin::Pin;
|
||||
use basegl_prelude::PhantomData;
|
||||
|
||||
// ========================
|
||||
// === IterForGenerator ===
|
||||
// ========================
|
||||
|
||||
pub struct IterForGenerator<G: Generator>(pub G);
|
||||
|
||||
impl<G> Iterator for IterForGenerator<G>
|
||||
where G: Generator<Return = ()> + Unpin {
|
||||
type Item = G::Yield;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match { Pin::new(&mut self.0).resume() } {
|
||||
GeneratorState::Yielded(element) => Some(element),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ======================
|
||||
// === EmptyGenerator ===
|
||||
// ======================
|
||||
|
||||
pub struct EmptyGenerator<T>(PhantomData<T>);
|
||||
|
||||
impl<T> EmptyGenerator<T> {
|
||||
pub fn new() -> Self {
|
||||
Self(PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Iterator for EmptyGenerator<T> {
|
||||
type Item = T;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
None
|
||||
}
|
||||
}
|
24
ide/gui/lib/structology/macros/Cargo.toml
Normal file
24
ide/gui/lib/structology/macros/Cargo.toml
Normal file
@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "structology-macros"
|
||||
version = "0.1.0"
|
||||
authors = ["Wojciech Danilo <wojciech.danilo@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
basegl-prelude = { version = "0.1.0" , path = "../../prelude" }
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
Inflector = "0.11.4"
|
||||
itertools = "0.8.1"
|
||||
|
||||
[dependencies.syn]
|
||||
version = "1.0"
|
||||
features = [
|
||||
'extra-traits'
|
||||
]
|
350
ide/gui/lib/structology/macros/src/lib.rs
Normal file
350
ide/gui/lib/structology/macros/src/lib.rs
Normal file
@ -0,0 +1,350 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
use basegl_prelude::*;
|
||||
|
||||
use inflector::Inflector;
|
||||
use proc_macro2::{TokenStream, Ident, Span};
|
||||
use quote::quote;
|
||||
use syn;
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
|
||||
/// In order to make the definition easier to read, an example expansion of the
|
||||
/// following definition was provided for each quotation:
|
||||
///
|
||||
/// #[derive(Iterator)]
|
||||
/// pub struct Foo<S, T> { foo: T }
|
||||
#[proc_macro_derive(Iterator)]
|
||||
pub fn derive_iterator
|
||||
(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let decl = syn::parse_macro_input!(input as syn::DeriveInput);
|
||||
let data = &decl.data;
|
||||
let params = &decl.generics.params.iter().collect::<Vec<_>>();
|
||||
match params.last() {
|
||||
Some(last_param) => derive_iterator_for(&decl, &last_param),
|
||||
None => proc_macro::TokenStream::from(quote! {})
|
||||
}
|
||||
}
|
||||
|
||||
fn derive_iterator_for
|
||||
( decl : &syn::DeriveInput
|
||||
, target_param : &syn::GenericParam
|
||||
) -> proc_macro::TokenStream {
|
||||
let data = &decl.data;
|
||||
let params = &decl.generics.params.iter().collect::<Vec<_>>();
|
||||
let target_param_str = repr(&target_param);
|
||||
let matched_fields: Vec<TokenStream> = match *data {
|
||||
syn::Data::Struct(ref data) => {
|
||||
fields_list(&data.fields).iter().enumerate().filter_map(|(i, f)| {
|
||||
let type_matched = repr(&f.ty) == target_param_str;
|
||||
type_matched.as_some_from(|| {
|
||||
match &f.ident {
|
||||
Some(ident) => quote!(#ident),
|
||||
None => {
|
||||
let ix = syn::Index::from(i);
|
||||
quote!(#ix)
|
||||
}
|
||||
}
|
||||
})
|
||||
}).collect()
|
||||
}
|
||||
syn::Data::Enum(_) | syn::Data::Union(_) => unimplemented!(),
|
||||
};
|
||||
let data = &decl.ident;
|
||||
let t_iterator = format!("{}Iterator" , data);
|
||||
let t_iterator_mut = format!("{}IteratorMut" , data);
|
||||
let iterator = t_iterator.to_snake_case();
|
||||
let iterator_mut = t_iterator_mut.to_snake_case();
|
||||
let t_iterator = Ident::new(&t_iterator , Span::call_site());
|
||||
let t_iterator_mut = Ident::new(&t_iterator_mut , Span::call_site());
|
||||
let iterator = Ident::new(&iterator , Span::call_site());
|
||||
let iterator_mut = Ident::new(&iterator_mut , Span::call_site());
|
||||
let iter_body_ref = quote! {
|
||||
structology::IterForGenerator
|
||||
(move || { #(yield &t.#matched_fields;)* })
|
||||
};
|
||||
let iter_body_mut = quote! {
|
||||
structology::IterForGenerator
|
||||
(move || { #(yield &mut t.#matched_fields;)* })
|
||||
};
|
||||
let iter_body_dummy = quote! { structology::EmptyGenerator::new() };
|
||||
let empty = matched_fields.is_empty();
|
||||
let iter_body = if empty { &iter_body_dummy } else { &iter_body_ref };
|
||||
let iter_body_mut = if empty { &iter_body_dummy } else { &iter_body_mut };
|
||||
let expanded = quote! {
|
||||
// type FooIterator<'t, T> = impl Iterator<Item = &'t T>;
|
||||
type #t_iterator<'t, #(#params),*> =
|
||||
impl Iterator<Item = &'t #target_param>;
|
||||
|
||||
// pub fn foo_iterator<'t, T>
|
||||
// (t: &'t Foo<T>) -> FooIterator<'t, T> {
|
||||
// structology::IterForGenerator(move || {
|
||||
// yield &t.foo;
|
||||
// })
|
||||
// }
|
||||
pub fn #iterator<'t, #(#params),*>
|
||||
(t: &'t #data<#(#params),*>) -> #t_iterator<'t, #(#params),*> {
|
||||
#iter_body
|
||||
}
|
||||
|
||||
// type FooIteratorMut<'t, T> = impl Iterator<Item = &'t mut T>;
|
||||
type #t_iterator_mut<'t, #(#params),*> =
|
||||
impl Iterator<Item = &'t mut #target_param>;
|
||||
|
||||
// pub fn foo_iterator_mut<'t, T>
|
||||
// (t: &'t mut Foo<T>) -> FooIteratorMut<'t, T> {
|
||||
// structology::IterForGenerator(move || {
|
||||
// yield &t.foo;
|
||||
// })
|
||||
// }
|
||||
pub fn #iterator_mut<'t, #(#params),*>
|
||||
(t: &'t mut #data<#(#params),*>) -> #t_iterator_mut<'t, #(#params),*> {
|
||||
#iter_body_mut
|
||||
}
|
||||
|
||||
// impl<'t, T> IntoIterator for &'t Foo<T> {
|
||||
// type Item = &'t T;
|
||||
// type IntoIter = FooIterator<'t, T>;
|
||||
// fn into_iter(self) -> FooIterator<'t, T> {
|
||||
// foo_iterator(self)
|
||||
// }
|
||||
// }
|
||||
impl<'t, #(#params),*> IntoIterator for &'t #data<#(#params),*> {
|
||||
type Item = &'t #target_param;
|
||||
type IntoIter = #t_iterator<'t, #(#params),*>;
|
||||
fn into_iter(self) -> #t_iterator<'t, #(#params),*> {
|
||||
#iterator(self)
|
||||
}
|
||||
}
|
||||
|
||||
// impl<'t, T> IntoIterator for &'t mut Foo<T> {
|
||||
// type Item = &'t mut T;
|
||||
// type IntoIter = FooIteratorMut<'t, T>;
|
||||
// fn into_iter(self) -> FooIteratorMut<'t, T> {
|
||||
// foo_iterator_mut(self)
|
||||
// }
|
||||
// }
|
||||
impl<'t, #(#params),*> IntoIterator for &'t mut #data<#(#params),*> {
|
||||
type Item = &'t mut #target_param;
|
||||
type IntoIter = #t_iterator_mut<'t, #(#params),*>;
|
||||
fn into_iter(self) -> #t_iterator_mut<'t, #(#params),*> {
|
||||
#iterator_mut(self)
|
||||
}
|
||||
}
|
||||
|
||||
// impl Foo<T> {
|
||||
// pub fn iter(&self) -> FooIterator<'_, T> {
|
||||
// #foo_iterator(self)
|
||||
// }
|
||||
// pub fn iter_mut(&mut self) -> FooIteratorMut<'_, T> {
|
||||
// #foo_iterator_mut (self)
|
||||
// }
|
||||
// }
|
||||
impl<#(#params),*> #data<#(#params),*> {
|
||||
pub fn iter(&self) -> #t_iterator<'_, #(#params),*> {
|
||||
#iterator(self)
|
||||
}
|
||||
pub fn iter_mut(&mut self) -> #t_iterator_mut<'_, #(#params),*> {
|
||||
#iterator_mut(self)
|
||||
}
|
||||
}
|
||||
};
|
||||
proc_macro::TokenStream::from(expanded)
|
||||
}
|
||||
|
||||
fn fields_list(fields: &syn::Fields) -> Vec<&syn::Field> {
|
||||
match fields {
|
||||
syn::Fields::Named (ref f) => { f.named.iter().collect() }
|
||||
syn::Fields::Unnamed (ref f) => { f.unnamed.iter().collect() }
|
||||
syn::Fields::Unit => default()
|
||||
}
|
||||
}
|
||||
|
||||
fn field_type(field: &syn::Field) -> String {
|
||||
let tp = &field.ty;
|
||||
quote!(#tp).to_string()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(AstNode)]
|
||||
pub fn derive_ast_node
|
||||
(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let expanded = quote! {
|
||||
#[derive(Debug)]
|
||||
};
|
||||
proc_macro::TokenStream::from(expanded)
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn ast_node
|
||||
(_meta: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input: TokenStream = input.into();
|
||||
let output = quote! {
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
#[derive(Iterator)]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#input
|
||||
};
|
||||
output.into()
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn ast
|
||||
( attrs : proc_macro::TokenStream
|
||||
, input : proc_macro::TokenStream
|
||||
) -> proc_macro::TokenStream {
|
||||
let attrs: TokenStream = attrs.into();
|
||||
let decl = syn::parse_macro_input!(input as syn::DeriveInput);
|
||||
let output = match &decl.data {
|
||||
syn::Data::Enum { .. } => quote! {
|
||||
#[to_variant_types(#attrs)]
|
||||
#[ast_node]
|
||||
#decl
|
||||
},
|
||||
_ => quote! {
|
||||
#[ast_node]
|
||||
#decl
|
||||
}
|
||||
};
|
||||
output.into()
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn repr<T: quote::ToTokens>(t: &T) -> String {
|
||||
quote!(#t).to_string()
|
||||
}
|
||||
|
||||
|
||||
use syn::visit::{self, Visit};
|
||||
use syn::{File, ItemFn};
|
||||
|
||||
struct TypeGather {
|
||||
pub types: Vec<String>
|
||||
}
|
||||
|
||||
impl TypeGather {
|
||||
pub fn new() -> Self {
|
||||
let types = default();
|
||||
Self { types }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> Visit<'ast> for TypeGather {
|
||||
fn visit_type_path(&mut self, node: &'ast syn::TypePath) {
|
||||
self.types.push(repr(node));
|
||||
visit::visit_type_path(self, node);
|
||||
}
|
||||
}
|
||||
|
||||
fn gather_all_types(node: &syn::Type) -> Vec<String> {
|
||||
let mut type_gather = TypeGather::new();
|
||||
type_gather.visit_type(node);
|
||||
type_gather.types
|
||||
}
|
||||
|
||||
|
||||
fn mk_product_type
|
||||
( is_flat : bool
|
||||
, decl : &syn::DeriveInput
|
||||
, variant : &syn::Variant
|
||||
) -> syn::ItemStruct {
|
||||
use syn::ItemStruct;
|
||||
let fields = &variant.fields;
|
||||
let types = fields.iter().flat_map(|f| {gather_all_types(&f.ty) });
|
||||
let types = types.collect::<HashSet<_>>();
|
||||
let ty_vars = decl.generics.params.iter().cloned();
|
||||
let params = ty_vars.filter(|v| types.contains(&repr(&v))).collect();
|
||||
let attrs = decl.attrs.clone();
|
||||
let vis = decl.vis.clone();
|
||||
let struct_token = syn::token::Struct { span: Span::call_site() };
|
||||
let ident_flat = variant.ident.clone();
|
||||
let ident_nested = format!("{}{}", decl.ident, variant.ident);
|
||||
let ident_nested = Ident::new(&ident_nested, Span::call_site());
|
||||
let ident = if is_flat { ident_flat } else { ident_nested };
|
||||
let generics = syn::Generics { params, .. default() };
|
||||
let mut fields = variant.fields.clone();
|
||||
let semi_token = None;
|
||||
fields.iter_mut().for_each(|f| f.vis = vis.clone());
|
||||
ItemStruct { attrs, vis, struct_token, ident, generics, fields, semi_token }
|
||||
}
|
||||
|
||||
fn gen_variant_decl
|
||||
(ident: &syn::Ident, variant: &syn::ItemStruct) -> TokenStream {
|
||||
let variant_ident = &variant.ident;
|
||||
let params = variant.generics.params.iter();
|
||||
quote! {
|
||||
// App(ShapeApp<T>),
|
||||
// Var(ShapeVar),
|
||||
#ident(#variant_ident<#(#params),*>)
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_from_impls
|
||||
(ident: &syn::Ident, decl: &syn::DeriveInput, variant: &syn::ItemStruct) -> TokenStream {
|
||||
let sum_label = &decl.ident;
|
||||
let variant_label = &variant.ident;
|
||||
let sum_params = &decl.generics.params.iter().cloned().collect::<Vec<_>>();
|
||||
let variant_params = &variant.generics.params.iter().cloned().collect::<Vec<_>>();
|
||||
;
|
||||
quote! {
|
||||
// impl<T> From<App<T>> for Shape<T> {
|
||||
// fn from(t: App<T>) -> Self { Shape::App(t) }
|
||||
// }
|
||||
// ...
|
||||
impl<#(#sum_params),*> From<#variant_label<#(#variant_params),*>>
|
||||
for #sum_label<#(#sum_params),*> {
|
||||
fn from(t: #variant_label<#(#variant_params),*>) -> Self {
|
||||
#sum_label::#ident(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// In order to make the definition easier to read, an example expansion of the
|
||||
/// following definition was provided for each quotation:
|
||||
///
|
||||
/// #[to_variant_types]
|
||||
/// pub enum Shape<T> {
|
||||
/// Var(Var),
|
||||
/// App(App<T>),
|
||||
/// }
|
||||
#[proc_macro_attribute]
|
||||
pub fn to_variant_types
|
||||
( attrs: proc_macro::TokenStream
|
||||
, input: proc_macro::TokenStream
|
||||
) -> proc_macro::TokenStream {
|
||||
let attrs: TokenStream = attrs.into();
|
||||
let decl = syn::parse_macro_input!(input as syn::DeriveInput);
|
||||
let ident = &decl.ident;
|
||||
let ty_vars = &decl.generics.params;
|
||||
let variants = match &decl.data {
|
||||
syn::Data::Enum(ref data) => data.variants.iter(),
|
||||
_ => unimplemented!()
|
||||
}.collect::<Vec<_>>();
|
||||
|
||||
let is_flat = repr(&attrs) == "flat";
|
||||
let variant_idents = variants.iter().map(|v| &v.ident).collect::<Vec<_>>();
|
||||
let structs = variants.iter().map(|v| mk_product_type(is_flat, &decl, v));
|
||||
let structs = structs.collect::<Vec<_>>();
|
||||
let variant_decls = variant_idents.iter().zip(structs.iter()).map(|(i,v)| gen_variant_decl(i,&v));
|
||||
let variant_froms = variant_idents.iter().zip(structs.iter()).map(|(i,v)| gen_from_impls(i, &decl, &v));
|
||||
|
||||
// Handle single value, unnamed params as created by user.
|
||||
let structs = structs.iter().filter(|v| match &v.fields {
|
||||
syn::Fields::Unnamed(f) => f.unnamed.len() != 1,
|
||||
_ => true
|
||||
});
|
||||
|
||||
let output = quote! {
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum #ident <#ty_vars> {
|
||||
#(#variant_decls),*
|
||||
}
|
||||
#(#structs)*
|
||||
#(#variant_froms)*
|
||||
};
|
||||
output.into()
|
||||
}
|
||||
|
35
ide/gui/lib/system/web/Cargo.toml
Normal file
35
ide/gui/lib/system/web/Cargo.toml
Normal file
@ -0,0 +1,35 @@
|
||||
[package]
|
||||
name = "basegl-system-web"
|
||||
version = "0.1.0"
|
||||
authors = ["Wojciech Danilo <wojciech.danilo@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
|
||||
[features]
|
||||
default = ["console_error_panic_hook"]
|
||||
|
||||
[dependencies]
|
||||
basegl-prelude = { version = "0.1.0" , path = "../../prelude" }
|
||||
js-sys = { version = "0.3.28" }
|
||||
wasm-bindgen = { version = "^0.2" , features = ["nightly"] }
|
||||
failure = { version = "0.1.5" }
|
||||
|
||||
console_error_panic_hook = { version = "0.1.1", optional = true }
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3.4"
|
||||
features = [
|
||||
'Document',
|
||||
'Element',
|
||||
'HtmlCanvasElement',
|
||||
'WebGlBuffer',
|
||||
'WebGlRenderingContext',
|
||||
'WebGlProgram',
|
||||
'WebGlShader',
|
||||
'Window',
|
||||
'console'
|
||||
]
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.2"
|
72
ide/gui/lib/system/web/js/resize_observer.js
Normal file
72
ide/gui/lib/system/web/js/resize_observer.js
Normal file
@ -0,0 +1,72 @@
|
||||
// ==============
|
||||
// === IxPool ===
|
||||
// ==============
|
||||
|
||||
class IxPool {
|
||||
constructor() {
|
||||
this.next = 0
|
||||
this.free = []
|
||||
}
|
||||
|
||||
reserve() {
|
||||
let ix
|
||||
if(this.free.length == 0) {
|
||||
ix = this.next
|
||||
this.next += 1
|
||||
} else {
|
||||
ix = this.free.shift()
|
||||
}
|
||||
return ix
|
||||
}
|
||||
|
||||
drop(ix) {
|
||||
this.free.unshift(ix)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============
|
||||
// === Pool ===
|
||||
// ============
|
||||
|
||||
class Pool {
|
||||
constructor(cons) {
|
||||
this.cons = cons
|
||||
this.ixs = new IxPool
|
||||
}
|
||||
|
||||
reserve(...args) {
|
||||
let ix = this.ixs.reserve()
|
||||
this[ix] = this.cons(...args)
|
||||
return ix
|
||||
}
|
||||
|
||||
drop(ix) {
|
||||
this.ixs.drop(ix)
|
||||
this[ix] = null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ======================
|
||||
// === ResizeObserver ===
|
||||
// ======================
|
||||
|
||||
let resizeObserverPool = new Pool((...args) => new ResizeObserver(...args))
|
||||
|
||||
export function resize_observe(target, f) {
|
||||
let id = resizeObserverPool.reserve(resize_observer_update(f))
|
||||
resizeObserverPool[id].observe(target)
|
||||
}
|
||||
|
||||
export function resize_unobserve(id) {
|
||||
resizeObserverPool[id].disconnect()
|
||||
resizeObserverPool.drop(id)
|
||||
}
|
||||
|
||||
function resize_observer_update(f) {
|
||||
return entries => {
|
||||
let rect = entries[0].contentRect;
|
||||
f(rect.width, rect.height);
|
||||
}
|
||||
}
|
196
ide/gui/lib/system/web/src/lib.rs
Normal file
196
ide/gui/lib/system/web/src/lib.rs
Normal file
@ -0,0 +1,196 @@
|
||||
#![feature(trait_alias)]
|
||||
|
||||
pub mod resize_observer;
|
||||
|
||||
use basegl_prelude::*;
|
||||
|
||||
use wasm_bindgen::prelude::Closure;
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::HtmlCanvasElement;
|
||||
use web_sys::WebGlRenderingContext;
|
||||
|
||||
pub use web_sys::console;
|
||||
|
||||
// =============
|
||||
// === Error ===
|
||||
// =============
|
||||
|
||||
type Result<A> = std::result::Result<A, Error>;
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum Error {
|
||||
#[fail(display = "Missing `{}`.", name)]
|
||||
Missing { name: String },
|
||||
#[fail(display = "Type mismatch. Expected `{}`, got `{}`.", expected, got)]
|
||||
TypeMismatch { expected: String, got: String },
|
||||
#[fail(display = "WebGL {} is not available.", version)]
|
||||
NoWebGL { version: u32 },
|
||||
}
|
||||
impl Error {
|
||||
pub fn missing(name: &str) -> Error {
|
||||
let name = name.to_string();
|
||||
Error::Missing { name }
|
||||
}
|
||||
|
||||
pub fn type_mismatch(expected: &str, got: &str) -> Error {
|
||||
let expected = expected.to_string();
|
||||
let got = got.to_string();
|
||||
Error::TypeMismatch { expected, got }
|
||||
}
|
||||
}
|
||||
|
||||
// ==============
|
||||
// === LogMsg ===
|
||||
// ==============
|
||||
|
||||
pub trait LogMsg {
|
||||
fn with_log_msg<F: FnOnce(&str) -> T, T>(&self, f: F) -> T;
|
||||
}
|
||||
|
||||
impl LogMsg for &str {
|
||||
fn with_log_msg<F: FnOnce(&str) -> T, T>(&self, f: F) -> T {
|
||||
f(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Fn() -> S, S: AsRef<str>> LogMsg for F {
|
||||
fn with_log_msg<G: FnOnce(&str) -> T, T>(&self, f: G) -> T {
|
||||
f(self().as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
// ==============
|
||||
// === Logger ===
|
||||
// ==============
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Logger {
|
||||
pub path: String,
|
||||
}
|
||||
impl Logger {
|
||||
pub fn new<T: AsRef<str>>(path: T) -> Self {
|
||||
let path = path.as_ref().to_string();
|
||||
Self { path }
|
||||
}
|
||||
|
||||
// FIXME: Default
|
||||
pub fn new_() -> Self {
|
||||
Self::new("")
|
||||
}
|
||||
|
||||
pub fn sub<T: AsRef<str>>(&self, path: T) -> Self {
|
||||
Self::new(format!("{}.{}", self.path, path.as_ref()))
|
||||
}
|
||||
|
||||
pub fn trace<M: LogMsg>(&self, msg: M) {
|
||||
console::debug_1(&self.format(msg));
|
||||
}
|
||||
|
||||
pub fn info<M: LogMsg>(&self, msg: M) {
|
||||
// console::info_1(&self.format(msg));
|
||||
console::group_1(&self.format(msg));
|
||||
console::group_end();
|
||||
}
|
||||
|
||||
pub fn warning<M: LogMsg>(&self, msg: M) {
|
||||
console::warn_1(&self.format(msg));
|
||||
}
|
||||
|
||||
pub fn error<M: LogMsg>(&self, msg: M) {
|
||||
console::error_1(&self.format(msg));
|
||||
}
|
||||
|
||||
pub fn group_begin<M: LogMsg>(&self, msg: M) {
|
||||
console::group_1(&self.format(msg));
|
||||
}
|
||||
|
||||
pub fn group_end(&self) {
|
||||
console::group_end();
|
||||
}
|
||||
|
||||
pub fn group<M: LogMsg, T, F: FnOnce() -> T>(&self, msg: M, f: F) -> T {
|
||||
self.group_begin(msg);
|
||||
let out = f();
|
||||
self.group_end();
|
||||
out
|
||||
}
|
||||
|
||||
fn format<M: LogMsg>(&self, msg: M) -> JsValue {
|
||||
msg.with_log_msg(|s| format!("[{}] {}", self.path, s)).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Logger {
|
||||
fn default() -> Self {
|
||||
Self::new("")
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
// === Logger Utils ===
|
||||
// ====================
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! fmt {
|
||||
($($arg:tt)*) => (||(format!($($arg)*)))
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! group {
|
||||
($logger:expr, $message:expr, $body:tt) => {{
|
||||
$logger.group_begin(|| $message);
|
||||
let out = $body;
|
||||
$logger.group_end();
|
||||
out
|
||||
}};
|
||||
}
|
||||
|
||||
// ===================
|
||||
// === DOM Helpers ===
|
||||
// ===================
|
||||
|
||||
pub fn window() -> Result<web_sys::Window> {
|
||||
web_sys::window().ok_or_else(|| Error::missing("window"))
|
||||
}
|
||||
|
||||
pub fn document() -> Result<web_sys::Document> {
|
||||
window()?.document().ok_or_else(|| Error::missing("document"))
|
||||
}
|
||||
|
||||
pub fn get_element_by_id(id: &str) -> Result<web_sys::Element> {
|
||||
document()?.get_element_by_id(id).ok_or_else(|| Error::missing(id))
|
||||
}
|
||||
|
||||
pub fn get_element_by_id_as<T: wasm_bindgen::JsCast>(id: &str) -> Result<T> {
|
||||
let elem = get_element_by_id(id)?;
|
||||
let expected = type_name::<T>(); // fixme
|
||||
let got = format!("{:?}", elem); // fixme
|
||||
elem.dyn_into().map_err(|_| Error::type_mismatch(&expected, &got))
|
||||
}
|
||||
|
||||
pub fn get_canvas(id: &str) -> Result<web_sys::HtmlCanvasElement> {
|
||||
get_element_by_id_as(id)
|
||||
}
|
||||
|
||||
pub fn get_webgl_context(
|
||||
canvas: &HtmlCanvasElement,
|
||||
version: u32,
|
||||
) -> Result<WebGlRenderingContext>
|
||||
{
|
||||
let no_webgl = || Error::NoWebGL { version };
|
||||
let name_sfx = if version == 1 { "".to_string() } else { version.to_string() };
|
||||
let name = &format!("webgl{}", &name_sfx);
|
||||
let context = canvas.get_context(name).map_err(|_| no_webgl())?.ok_or_else(no_webgl)?;
|
||||
context.dyn_into().map_err(|_| no_webgl())
|
||||
}
|
||||
|
||||
pub fn request_animation_frame(f: &Closure<dyn FnMut()>) -> Result<i32> {
|
||||
let req = window()?.request_animation_frame(f.as_ref().unchecked_ref());
|
||||
req.map_err(|_| Error::missing("requestAnimationFrame"))
|
||||
}
|
||||
|
||||
pub fn cancel_animation_frame(id: i32) -> Result<()> {
|
||||
let req = window()?.cancel_animation_frame(id);
|
||||
req.map_err(|_| Error::missing("cancel_animation_frame"))
|
||||
}
|
51
ide/gui/lib/system/web/src/resize_observer.rs
Normal file
51
ide/gui/lib/system/web/src/resize_observer.rs
Normal file
@ -0,0 +1,51 @@
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
use wasm_bindgen::prelude::Closure;
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
// =============
|
||||
// === Types ===
|
||||
// =============
|
||||
|
||||
pub type Listener = Closure<dyn FnMut(i32, i32)>;
|
||||
|
||||
// ===================
|
||||
// === JS Bindings ===
|
||||
// ===================
|
||||
|
||||
#[wasm_bindgen(module = "/js/resize_observer.js")]
|
||||
extern "C" {
|
||||
fn resize_observe(target: &JsValue, closure: &Listener) -> usize;
|
||||
fn resize_unobserve(id: usize);
|
||||
}
|
||||
|
||||
// ======================
|
||||
// === ResizeObserver ===
|
||||
// ======================
|
||||
|
||||
/// The ResizeObserver interface reports changes to the dimensions of an
|
||||
/// DOM Element's content or border box. ResizeObserver avoids infinite callback
|
||||
/// loops and cyclic dependencies that are often created when resizing via a
|
||||
/// callback function. It does this by only processing elements deeper in the
|
||||
/// DOM in subsequent frames.
|
||||
///
|
||||
/// See also https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
|
||||
#[derive(Debug)]
|
||||
pub struct ResizeObserver {
|
||||
pub target: JsValue,
|
||||
pub listener: Listener,
|
||||
pub observer_id: usize,
|
||||
}
|
||||
|
||||
impl ResizeObserver {
|
||||
pub fn new(target: &JsValue, listener: Listener) -> Self {
|
||||
let target = target.clone();
|
||||
let observer_id = resize_observe(&target, &listener);
|
||||
Self { target, listener, observer_id }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ResizeObserver {
|
||||
fn drop(&mut self) {
|
||||
resize_unobserve(self.observer_id);
|
||||
}
|
||||
}
|
2
ide/gui/script/build.sh
Executable file
2
ide/gui/script/build.sh
Executable file
@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
wasm-pack build --out-dir '../../target/web' lib/core
|
3
ide/gui/script/lint.sh
Executable file
3
ide/gui/script/lint.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
cargo clean -p basegl -p basegl-backend-webgl -p basegl-prelude -p basegl-system-web
|
||||
cargo clippy -- -D warnings
|
2
ide/gui/script/watch.sh
Executable file
2
ide/gui/script/watch.sh
Executable file
@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
cargo watch -i .gitignore -i "pkg/*" -s "script/build.sh"
|
13
ide/gui/tests/web.rs
Normal file
13
ide/gui/tests/web.rs
Normal file
@ -0,0 +1,13 @@
|
||||
//! Test suite for the Web and headless browsers.
|
||||
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
|
||||
extern crate wasm_bindgen_test;
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn pass() {
|
||||
assert_eq!(1 + 1, 2);
|
||||
}
|
Loading…
Reference in New Issue
Block a user