Init AST impl

This commit is contained in:
Wojciech Danilo 2019-11-12 13:47:02 +01:00
parent bfe81a731f
commit bc410b6d52
37 changed files with 8690 additions and 0 deletions

12
ide/gui/.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,2 @@
node_modules
dist

View 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));

View 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>

View File

@ -0,0 +1 @@
import * as wasm from "basegl";

6058
ide/gui/examples/01-scene/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View 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"
}
}

View 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'}),
],
};

View 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"

View 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);
}
}

View 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))
}
}};
}

View File

@ -0,0 +1,3 @@
pub mod callback;
pub mod closure;
pub mod nop;

View 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 {
|_, _, _, _, _| {}
}
}

View File

@ -0,0 +1,3 @@
pub mod function;
pub mod opt_vec;
pub mod shared;

View 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);
}
}

View 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
View 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)));
}

View 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)));
}

View 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"

View 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)
}

View 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" }

View 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
}
}

View 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'
]

View 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()
}

View 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"

View 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);
}
}

View 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"))
}

View 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
View File

@ -0,0 +1,2 @@
#!/bin/sh
wasm-pack build --out-dir '../../target/web' lib/core

3
ide/gui/script/lint.sh Executable file
View 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
View 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
View 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);
}