make changes
This commit is contained in:
parent
5a8ef55bb3
commit
ee26b6133a
14 changed files with 585 additions and 449 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1 +1,3 @@
|
|||
/target
|
||||
/pkg
|
||||
/.vscode
|
||||
|
|
86
Cargo.lock
generated
86
Cargo.lock
generated
|
@ -191,9 +191,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.12.1"
|
||||
version = "3.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8"
|
||||
checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
|
@ -261,6 +261,26 @@ version = "0.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf43edc576402991846b093a7ca18a3477e0ef9c588cde84964b5d3e43016642"
|
||||
|
||||
[[package]]
|
||||
name = "console_error_panic_hook"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console_log"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be8aed40e4edbf4d3b4431ab260b63fdc40f5780a4766824329ea0f1eefe3c0f"
|
||||
dependencies = [
|
||||
"log",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const_panic"
|
||||
version = "0.2.8"
|
||||
|
@ -317,6 +337,26 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxgraph"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cgmath",
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
"encase",
|
||||
"env_logger",
|
||||
"log",
|
||||
"pollster",
|
||||
"raw-window-handle",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"wgpu",
|
||||
"winit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "d3d12"
|
||||
version = "0.6.0"
|
||||
|
@ -351,9 +391,9 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
|||
|
||||
[[package]]
|
||||
name = "encase"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76ca07b3a7863d1bc1ddea8647d67394a0236fc5582035a343c4ef3a962ea62e"
|
||||
checksum = "8fce2eeef77fd4a293a54b62aa00ac9daebfbcda4bf8998c5a815635b004aa1c"
|
||||
dependencies = [
|
||||
"cgmath",
|
||||
"const_panic",
|
||||
|
@ -363,18 +403,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "encase_derive"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54141cb8a5d4ed9308fec79fb1cb9cd272fcd38aa8d1b1165bef654b18671161"
|
||||
checksum = "0e520cde08cbf4f7cc097f61573ec06ce467019803de8ae82fb2823fa1554a0e"
|
||||
dependencies = [
|
||||
"encase_derive_impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encase_derive_impl"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dbc65b49ac8078eee25dd0a341e2763974031008518d17b9c448bffc1025541"
|
||||
checksum = "3fe2568f851fd6144a45fa91cfed8fe5ca8fc0b56ba6797bfc1ed2771b90e37c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -660,9 +700,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.143"
|
||||
version = "0.2.144"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edc207893e85c5d6be840e969b496b53d94cec8be2d501b214f50daa97fa8024"
|
||||
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
|
@ -1066,9 +1106,9 @@ checksum = "332cd62e95873ea4f41f3dfd6bbbfc5b52aec892d7e8d534197c4720a0bbbab2"
|
|||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.26"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
|
||||
checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
@ -1623,20 +1663,6 @@ dependencies = [
|
|||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wgpu_calc"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cgmath",
|
||||
"encase",
|
||||
"env_logger",
|
||||
"log",
|
||||
"pollster",
|
||||
"regex",
|
||||
"wgpu",
|
||||
"winit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "1.0.2"
|
||||
|
@ -1817,9 +1843,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
|||
|
||||
[[package]]
|
||||
name = "winit"
|
||||
version = "0.28.5"
|
||||
version = "0.28.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94c9651471cd576737671fbf7081edfea43de3e06846dd9bd4e49ea803c9f55f"
|
||||
checksum = "866db3f712fffba75d31bf0cdecf357c8aeafd158c5b7ab51dba2a2b2d47f196"
|
||||
dependencies = [
|
||||
"android-activity",
|
||||
"bitflags 1.3.2",
|
||||
|
@ -1881,6 +1907,6 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "xml-rs"
|
||||
version = "0.8.7"
|
||||
version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "699d0104bcdd7e7af6d093d6c6e2d0c479b8a129ee0d1023b31d2e0c71bfdda2"
|
||||
checksum = "dc95a04ea24f543cd9be5aab44f963fa35589c99e18415c38fb2b17e133bf8d2"
|
||||
|
|
26
Cargo.toml
26
Cargo.toml
|
@ -1,16 +1,30 @@
|
|||
[package]
|
||||
name = "wgpu_calc"
|
||||
name = "cxgraph"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cfg-if = "1"
|
||||
wgpu = "0.16"
|
||||
env_logger = "0.10"
|
||||
log = "0.4"
|
||||
pollster = "0.3"
|
||||
winit = "0.28"
|
||||
cgmath = "0.18"
|
||||
encase = { version = "0.6", features = ["cgmath"] }
|
||||
regex = "1.8"
|
||||
raw-window-handle = "0.5"
|
||||
winit = "0.28"
|
||||
log = "0.4"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
pollster = "0.3"
|
||||
env_logger = "0.10"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
console_log = "1.0"
|
||||
wgpu = { version = "0.16", features = ["webgl"]}
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
web-sys = { version = "0.3", features = ["Document", "Window", "Element"]}
|
||||
|
|
40
index.html
Normal file
40
index.html
Normal file
|
@ -0,0 +1,40 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>cxgraph</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="main">
|
||||
<div id="header">
|
||||
<h1>CXGraph</h1>
|
||||
</div>
|
||||
<div id="content">
|
||||
<div id="sidebar" class="split left">
|
||||
<textarea id="srcinput"></textarea>
|
||||
</div>
|
||||
<div id="canvas_container" class="split right">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module">
|
||||
import init, * as cxgraph from "./pkg/cxgraph.js";
|
||||
await init();
|
||||
await cxgraph.load_shader("plot(z) = z");
|
||||
await cxgraph.redraw();
|
||||
let canvas = document.getElementsByTagName("canvas")[0];
|
||||
canvas.style.width = "100%";
|
||||
canvas.style.height = "100%";
|
||||
|
||||
new ResizeObserver(async () => {
|
||||
//console.log("resize");
|
||||
//let width = canvas.clientWidth;
|
||||
//let height = canvas.clientHeight;
|
||||
//await cxgraph.resize(width, height);
|
||||
//await cxgraph.redraw();
|
||||
}).observe(canvas);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,6 +1,4 @@
|
|||
use std::{collections::HashMap, fmt::Write};
|
||||
|
||||
use regex::Regex;
|
||||
use std::{collections::{HashMap, HashSet}, fmt::Write};
|
||||
|
||||
use crate::complex::{Complex, cxfn};
|
||||
|
||||
|
@ -10,11 +8,8 @@ use super::parser::{Expr, Stmt, UnaryOp, BinaryOp};
|
|||
pub enum CompileError<'s> {
|
||||
FmtError,
|
||||
TypeError(&'s str),
|
||||
ArgCount(&'s str),
|
||||
UndefinedVar(&'s str),
|
||||
GlobalReassignment(&'s str),
|
||||
BuiltinReassignment(&'s str),
|
||||
StandaloneDerivative(&'s str),
|
||||
Reassignment(&'s str),
|
||||
}
|
||||
|
||||
impl <'s> From<std::fmt::Error> for CompileError<'s> {
|
||||
|
@ -55,9 +50,6 @@ thread_local! {
|
|||
m.insert("gamma", ("c_gamma", Type::Function(1), None));
|
||||
m
|
||||
};
|
||||
|
||||
static RE_INVOKE: Regex = Regex::new(r"^invoke(\d+)$").unwrap();
|
||||
static RE_ITER: Regex = Regex::new(r"^iter(\d+)$").unwrap();
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
|
@ -66,33 +58,30 @@ pub enum Type {
|
|||
Function(u32),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
enum Generate {
|
||||
FunctionId { argc: u32, id: u32 },
|
||||
Invoke { argc: u32 },
|
||||
Iter { argc: u32 },
|
||||
Derivative { func: String, nderiv: u32, argc: u32 },
|
||||
#[derive(Clone, Copy)]
|
||||
enum NameScope {
|
||||
Local, Global, Builtin
|
||||
}
|
||||
|
||||
enum NameInfo {
|
||||
Local { id: u32 },
|
||||
Global { ty: Type, cname: String },
|
||||
Builtin { ty: Type, bname: &'static str },
|
||||
Generated { ty: Type, gname: String, gen: Generate }
|
||||
struct NameInfo<'s> {
|
||||
scope: NameScope,
|
||||
name: &'s str,
|
||||
ty: Type
|
||||
}
|
||||
|
||||
impl NameInfo {
|
||||
pub fn get_compiled_name(&self) -> String {
|
||||
match self {
|
||||
NameInfo::Local { id } => format!("arg_{id}"),
|
||||
NameInfo::Global { cname, .. } => cname.to_owned(),
|
||||
NameInfo::Builtin { bname, .. } => (*bname).to_owned(),
|
||||
NameInfo::Generated { gname, .. } => gname.to_owned(),
|
||||
impl <'s> NameInfo<'s> {
|
||||
pub fn get_cname(&self) -> String {
|
||||
let name = self.name;
|
||||
match (self.scope, self.ty) {
|
||||
(NameScope::Local, _) => format!("arg_{name}"),
|
||||
(NameScope::Global, Type::Number) => format!("VAR_{name}"),
|
||||
(NameScope::Global, Type::Function(_)) => format!("func_{name}"),
|
||||
(NameScope::Builtin, _) => name.to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type LocalTable<'s> = HashMap<&'s str, u32>;
|
||||
type LocalTable<'s> = HashSet<&'s str>;
|
||||
|
||||
type CompileResult<'s,T=()> = Result<T, CompileError<'s>>;
|
||||
|
||||
|
@ -101,16 +90,12 @@ pub fn compile<'s>(buf: &mut impl Write, stmts: &[Stmt<'s>]) -> CompileResult<'s
|
|||
for stmt in stmts {
|
||||
compiler.compile_stmt(stmt)?;
|
||||
}
|
||||
compiler.generate()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
struct Compiler<'s, 'w, W> where W: Write {
|
||||
buf: &'w mut W,
|
||||
globals: HashMap<&'s str, Type>,
|
||||
generate: HashMap<String, Generate>,
|
||||
next_fn_id: u32,
|
||||
}
|
||||
|
||||
impl <'s, 'w, W: Write> Compiler<'s, 'w, W> {
|
||||
|
@ -118,8 +103,6 @@ impl <'s, 'w, W: Write> Compiler<'s, 'w, W> {
|
|||
Self {
|
||||
buf,
|
||||
globals: HashMap::new(),
|
||||
generate: HashMap::new(),
|
||||
next_fn_id: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,67 +111,136 @@ impl <'s, 'w, W: Write> Compiler<'s, 'w, W> {
|
|||
//////////////////
|
||||
|
||||
fn compile_stmt(&mut self, stmt: &Stmt<'s>) -> CompileResult<'s> {
|
||||
match stmt {
|
||||
Stmt::AssignConst(name, expr) => {
|
||||
if BUILTINS.with(|m| m.contains_key(name)) {
|
||||
return Err(CompileError::BuiltinReassignment(name))
|
||||
}
|
||||
if self.globals.contains_key(name) {
|
||||
return Err(CompileError::GlobalReassignment(name))
|
||||
}
|
||||
self.globals.insert(name, Type::Number);
|
||||
write!(self.buf, "const VAR_{name} = ")?;
|
||||
|
||||
let locals = LocalTable::with_capacity(0);
|
||||
self.compile_expr(&locals, expr)?;
|
||||
|
||||
write!(self.buf, ";")?;
|
||||
}
|
||||
Stmt::AssignFunc(name, args, expr) => {
|
||||
if BUILTINS.with(|m| m.contains_key(name)) {
|
||||
return Err(CompileError::BuiltinReassignment(name))
|
||||
}
|
||||
if self.globals.contains_key(name) {
|
||||
return Err(CompileError::GlobalReassignment(name))
|
||||
}
|
||||
self.globals.insert(name, Type::Function(args.len() as u32));
|
||||
write!(self.buf, "fn func_{name}(")?;
|
||||
|
||||
let mut locals = LocalTable::with_capacity(args.len());
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
write!(self.buf, "arg_{}:vec2f,", i)?;
|
||||
locals.insert(arg, i as u32);
|
||||
}
|
||||
write!(self.buf, ")->vec2f{{return ")?;
|
||||
self.compile_expr(&locals, expr)?;
|
||||
write!(self.buf, ";}}")?;
|
||||
}
|
||||
}
|
||||
let res = match stmt {
|
||||
Stmt::Const { name, body } => self.stmt_const(name, body),
|
||||
Stmt::Func { name, args, body } => self.stmt_func(name, args, body),
|
||||
Stmt::Deriv { name, func } => self.stmt_deriv(name, func),
|
||||
Stmt::Iter { name, func, count } => self.stmt_iter(name, func, *count),
|
||||
};
|
||||
writeln!(self.buf)?;
|
||||
Ok(())
|
||||
res
|
||||
}
|
||||
|
||||
fn stmt_const(&mut self, name: &'s str, body: &Expr<'s>) -> CompileResult<'s> {
|
||||
if self.name_info(name, None).is_some() {
|
||||
return Err(CompileError::Reassignment(name))
|
||||
}
|
||||
|
||||
self.globals.insert(name, Type::Number);
|
||||
write!(self.buf, "const VAR_{name} = ")?;
|
||||
|
||||
let locals = LocalTable::with_capacity(0);
|
||||
self.compile_expr(&locals, body)?;
|
||||
|
||||
write!(self.buf, ";")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn stmt_func(&mut self, name: &'s str, args: &[&'s str], body: &Expr<'s>) -> CompileResult<'s> {
|
||||
if self.name_info(name, None).is_some() {
|
||||
return Err(CompileError::Reassignment(name))
|
||||
}
|
||||
|
||||
self.globals.insert(name, Type::Function(args.len() as u32));
|
||||
write!(self.buf, "fn func_{name}(")?;
|
||||
|
||||
let mut locals = LocalTable::with_capacity(args.len());
|
||||
for arg in args {
|
||||
write!(self.buf, "arg_{arg}:vec2f,")?;
|
||||
locals.insert(arg);
|
||||
}
|
||||
write!(self.buf, ")->vec2f{{return ")?;
|
||||
self.compile_expr(&locals, body)?;
|
||||
write!(self.buf, ";}}")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn stmt_deriv(&mut self, name: &'s str, func: &'s str) -> CompileResult<'s> {
|
||||
if self.name_info(name, None).is_some() {
|
||||
return Err(CompileError::Reassignment(name))
|
||||
}
|
||||
|
||||
let Some(name_info) = self.name_info(func, None) else {
|
||||
return Err(CompileError::UndefinedVar(name))
|
||||
};
|
||||
let Type::Function(argc) = name_info.ty else {
|
||||
return Err(CompileError::TypeError(name))
|
||||
};
|
||||
|
||||
let func_name = name_info.get_cname();
|
||||
|
||||
self.globals.insert(name, Type::Function(argc));
|
||||
|
||||
write!(self.buf, "fn func_{name}(")?;
|
||||
|
||||
for i in 0..argc {
|
||||
write!(self.buf, "arg_{i}:vec2f,")?;
|
||||
}
|
||||
let args: String = (1..argc).map(|i| format!(",arg_{i}")).collect();
|
||||
|
||||
write!(self.buf, ")->vec2f{{\
|
||||
let a = c_mul({func_name}(arg_0 + vec2( D_EPS, 0.0){args}), vec2( 0.25/D_EPS, 0.0));\
|
||||
let b = c_mul({func_name}(arg_0 + vec2(-D_EPS, 0.0){args}), vec2(-0.25/D_EPS, 0.0));\
|
||||
let c = c_mul({func_name}(arg_0 + vec2(0.0, D_EPS){args}), vec2(0.0, -0.25/D_EPS));\
|
||||
let d = c_mul({func_name}(arg_0 + vec2(0.0, -D_EPS){args}), vec2(0.0, 0.25/D_EPS));\
|
||||
return a + b + c + d;}}\
|
||||
")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn stmt_iter(&mut self, name: &'s str, func: &'s str, count: u32) -> CompileResult<'s> {
|
||||
if self.name_info(name, None).is_some() {
|
||||
return Err(CompileError::Reassignment(name))
|
||||
}
|
||||
|
||||
let Some(name_info) = self.name_info(func, None) else {
|
||||
return Err(CompileError::UndefinedVar(name))
|
||||
};
|
||||
let Type::Function(argc) = name_info.ty else {
|
||||
return Err(CompileError::TypeError(name))
|
||||
};
|
||||
|
||||
let func_name = name_info.get_cname();
|
||||
|
||||
self.globals.insert(name, Type::Function(argc));
|
||||
|
||||
write!(self.buf, "fn func_{name}(")?;
|
||||
|
||||
for i in 0..argc {
|
||||
write!(self.buf, "arg_{i}:vec2f,")?;
|
||||
}
|
||||
let args: String = (1..argc).map(|i| format!(",arg_{i}")).collect();
|
||||
|
||||
write!(self.buf, ")->vec2f{{\
|
||||
var r=arg_0;\
|
||||
for(var i=0;i<{count};i++){{\
|
||||
r={func_name}(r{args});\
|
||||
}}\
|
||||
return r;}}\
|
||||
")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///////////////////
|
||||
// Expressions //
|
||||
///////////////////
|
||||
|
||||
fn compile_expr(&mut self, locals: &LocalTable<'s>, expr: &Expr<'s>) -> CompileResult<'s> {
|
||||
match expr {
|
||||
Expr::Number(z) => self.compile_number(*z),
|
||||
Expr::Name(name) => self.compile_var(locals, name),
|
||||
Expr::NameDeriv(name, _) => return Err(CompileError::StandaloneDerivative(name)),
|
||||
Expr::Unary(op, arg) => self.compile_unary(locals, *op, arg),
|
||||
Expr::Binary(op, lhs, rhs) => self.compile_binary(locals, *op, lhs, rhs),
|
||||
Expr::FnCall { name, args, nderiv } => self.compile_fncall(locals, name, args, *nderiv),
|
||||
Expr::Number(z) => self.expr_number(*z),
|
||||
Expr::Name(name) => self.expr_var(locals, name),
|
||||
Expr::Unary(op, arg) => self.expr_unary(locals, *op, arg),
|
||||
Expr::Binary(op, lhs, rhs) => self.expr_binary(locals, *op, lhs, rhs),
|
||||
Expr::FnCall(name, args) => self.expr_fncall(locals, name, args),
|
||||
}
|
||||
}
|
||||
|
||||
fn compile_number(&mut self, z: Complex) -> CompileResult<'s> {
|
||||
fn expr_number(&mut self, z: Complex) -> CompileResult<'s> {
|
||||
write!(self.buf, "vec2f({:?},{:?})", z.re, z.im)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compile_unary(&mut self, locals: &LocalTable<'s>, op: UnaryOp, arg: &Expr<'s>) -> CompileResult<'s> {
|
||||
fn expr_unary(&mut self, locals: &LocalTable<'s>, op: UnaryOp, arg: &Expr<'s>) -> CompileResult<'s> {
|
||||
let strings = unop_strings(op);
|
||||
write!(self.buf, "{}", strings[0])?;
|
||||
self.compile_expr(locals, arg)?;
|
||||
|
@ -196,7 +248,7 @@ impl <'s, 'w, W: Write> Compiler<'s, 'w, W> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn compile_binary(&mut self, locals: &LocalTable<'s>, op: BinaryOp, lhs: &Expr<'s>, rhs: &Expr<'s>) -> CompileResult<'s> {
|
||||
fn expr_binary(&mut self, locals: &LocalTable<'s>, op: BinaryOp, lhs: &Expr<'s>, rhs: &Expr<'s>) -> CompileResult<'s> {
|
||||
let strings = binop_strings(op);
|
||||
write!(self.buf, "{}", strings[0])?;
|
||||
self.compile_expr(locals, lhs)?;
|
||||
|
@ -206,24 +258,25 @@ impl <'s, 'w, W: Write> Compiler<'s, 'w, W> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn compile_var(&mut self, locals: &LocalTable<'s>, name: &'s str) -> CompileResult<'s> {
|
||||
fn expr_var(&mut self, locals: &LocalTable<'s>, name: &'s str) -> CompileResult<'s> {
|
||||
let Some(name_info) = self.name_info(name, Some(locals)) else {
|
||||
return Err(CompileError::UndefinedVar(name))
|
||||
};
|
||||
self.compile_name(name, name_info)?;
|
||||
if !matches!(name_info.ty, Type::Number) {
|
||||
return Err(CompileError::TypeError(name))
|
||||
}
|
||||
write!(self.buf, "{}", name_info.get_cname())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compile_fncall(&mut self, locals: &LocalTable<'s>, name: &'s str, args: &Vec<Expr<'s>>, nderiv: u32) -> CompileResult<'s> {
|
||||
fn expr_fncall(&mut self, locals: &LocalTable<'s>, name: &'s str, args: &Vec<Expr<'s>>) -> CompileResult<'s> {
|
||||
let Some(name_info) = self.name_info(name, Some(locals)) else {
|
||||
return Err(CompileError::UndefinedVar(name))
|
||||
};
|
||||
let compname = &name_info.get_compiled_name();
|
||||
for i in 0..nderiv {
|
||||
self.add_gen_deriv(&compname, i+1, args.len() as u32);
|
||||
write!(self.buf, "deriv_")?;
|
||||
if !matches!(name_info.ty, Type::Function(n) if n as usize == args.len()) {
|
||||
return Err(CompileError::TypeError(name))
|
||||
}
|
||||
self.compile_name_fncall(name, name_info, args.len() as u32)?;
|
||||
write!(self.buf, "{}", name_info.get_cname())?;
|
||||
write!(self.buf, "(")?;
|
||||
for arg in args {
|
||||
self.compile_expr(locals, arg)?;
|
||||
|
@ -237,196 +290,40 @@ impl <'s, 'w, W: Write> Compiler<'s, 'w, W> {
|
|||
// Names //
|
||||
/////////////
|
||||
|
||||
|
||||
fn compile_name_fncall(&mut self, name: &'s str, name_info: NameInfo, argc: u32) -> CompileResult<'s> {
|
||||
match name_info {
|
||||
NameInfo::Local { .. } => return Err(CompileError::TypeError(name)),
|
||||
NameInfo::Global { ty: Type::Function(c), cname } if c == argc => {
|
||||
write!(self.buf, "{cname}")?;
|
||||
}
|
||||
NameInfo::Global { ty: Type::Function(_), .. } => return Err(CompileError::ArgCount(name)),
|
||||
NameInfo::Global { .. } => return Err(CompileError::TypeError(name)),
|
||||
NameInfo::Builtin { ty: Type::Function(c), bname } if c == argc => {
|
||||
write!(self.buf, "{bname}")?;
|
||||
}
|
||||
NameInfo::Builtin { ty: Type::Function(_), .. } => return Err(CompileError::ArgCount(name)),
|
||||
NameInfo::Builtin { .. } => return Err(CompileError::TypeError(name)),
|
||||
NameInfo::Generated { ty: Type::Function(c), gname, gen } if c == argc => {
|
||||
write!(self.buf, "{gname}")?;
|
||||
self.generate.insert(gname, gen);
|
||||
}
|
||||
NameInfo::Generated { ty: Type::Function(_), .. } => return Err(CompileError::ArgCount(name)),
|
||||
NameInfo::Generated { .. } => return Err(CompileError::TypeError(name)),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compile_name(&mut self, name: &str, name_info: NameInfo) -> CompileResult<'s> {
|
||||
match name_info {
|
||||
NameInfo::Local { id } => {
|
||||
write!(self.buf, "arg_{id}")?;
|
||||
},
|
||||
NameInfo::Global { ty: Type::Function(c), cname } => {
|
||||
self.add_gen_fid(format!("{cname}"), c);
|
||||
write!(self.buf, "FID_{cname}")?;
|
||||
},
|
||||
NameInfo::Global { ty: Type::Number, .. } => {
|
||||
write!(self.buf, "VAR_{name}")?;
|
||||
},
|
||||
NameInfo::Builtin { ty: Type::Function(c), bname } => {
|
||||
self.add_gen_fid(bname.to_owned(), c);
|
||||
write!(self.buf, "FID_{bname}")?;
|
||||
},
|
||||
NameInfo::Builtin { ty: Type::Number, bname } => {
|
||||
write!(self.buf, "{bname}")?;
|
||||
},
|
||||
NameInfo::Generated { ty: Type::Function(c), gname, gen } => {
|
||||
write!(self.buf, "FID_{gname}")?;
|
||||
self.add_gen_fid(gname.to_owned(), c);
|
||||
self.generate.insert(gname, gen);
|
||||
},
|
||||
NameInfo::Generated { ty: Type::Number, gname, gen } => {
|
||||
write!(self.buf, "{gname}")?;
|
||||
self.generate.insert(gname, gen);
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name_info(&self, name: &'s str, locals: Option<&LocalTable<'s>>) -> Option<NameInfo> {
|
||||
if let Some(locals) = locals {
|
||||
if let Some(id) = locals.get(name) {
|
||||
return Some(NameInfo::Local { id: *id })
|
||||
if locals.contains(name) {
|
||||
return Some(NameInfo { scope: NameScope::Local, name, ty: Type::Number });
|
||||
}
|
||||
}
|
||||
if let Some(ty) = self.globals.get(name) {
|
||||
return Some(NameInfo::Global { ty: *ty, cname: format!("func_{name}") })
|
||||
if let Some(ty) = self.globals.get(name).copied() {
|
||||
return Some(NameInfo { scope: NameScope::Global, name, ty })
|
||||
}
|
||||
if let Some((bname, ty, _)) = BUILTINS.with(|m| m.get(name).copied()) {
|
||||
return Some(NameInfo::Builtin { ty, bname })
|
||||
}
|
||||
if let Some(caps) = RE_INVOKE.with(|re| re.captures(name)) {
|
||||
if let Ok(n) = caps[1].parse() {
|
||||
return Some(NameInfo::Generated {
|
||||
ty: Type::Function(n + 1),
|
||||
gname: format!("invoke{n}"),
|
||||
gen: Generate::Invoke { argc: n },
|
||||
})
|
||||
}
|
||||
}
|
||||
if let Some(caps) = RE_ITER.with(|re| re.captures(name)) {
|
||||
if let Ok(n) = caps[1].parse() {
|
||||
return Some(NameInfo::Generated {
|
||||
ty: Type::Function(n + 2),
|
||||
gname: format!("iter{n}"),
|
||||
gen: Generate::Iter { argc: n }
|
||||
})
|
||||
}
|
||||
return Some(NameInfo { scope: NameScope::Builtin, name: bname, ty })
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
//////////////////
|
||||
// Generation //
|
||||
//////////////////
|
||||
|
||||
fn add_gen_fid(&mut self, name: String, argc: u32) {
|
||||
self.generate.insert(name, Generate::FunctionId { argc, id: self.next_fn_id });
|
||||
self.next_fn_id += 1;
|
||||
}
|
||||
|
||||
fn add_gen_deriv(&mut self, name: &str, nderiv: u32, argc: u32) {
|
||||
let func_name = "deriv_".repeat(nderiv as usize - 1) + name;
|
||||
let deriv_name = "deriv_".repeat(nderiv as usize) + name;
|
||||
self.generate.insert(deriv_name, Generate::Derivative { func: func_name, nderiv, argc });
|
||||
}
|
||||
|
||||
fn generate(&mut self) -> CompileResult<'s> {
|
||||
for (name, g) in self.generate.clone() {
|
||||
match g {
|
||||
Generate::FunctionId { id, .. } => {
|
||||
write!(self.buf, "const FID_{name}=vec2f({id}.0,0.0);")?;
|
||||
}
|
||||
Generate::Invoke { argc } => self.generate_invoke(argc)?,
|
||||
Generate::Iter { argc } => self.generate_iter(argc)?,
|
||||
Generate::Derivative { func, nderiv, argc } => self.generate_derivative(func, nderiv, argc)?,
|
||||
}
|
||||
writeln!(self.buf)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_invoke(&mut self, argc: u32) -> CompileResult<'s> {
|
||||
write!(self.buf, "fn invoke{argc}(func:vec2f,")?;
|
||||
for i in 0..argc {
|
||||
write!(self.buf, "arg_{i}:vec2f")?;
|
||||
write!(self.buf, ",")?;
|
||||
}
|
||||
write!(self.buf, ")->vec2f{{switch i32(func.x){{")?;
|
||||
for (name, g) in &self.generate {
|
||||
if let Generate::FunctionId { argc: fargc, id } = g {
|
||||
write!(self.buf, "case {id}")?;
|
||||
if *id == 0 {
|
||||
write!(self.buf, ",default")?;
|
||||
}
|
||||
write!(self.buf, "{{return {name}(")?;
|
||||
let a = argc.min(*fargc);
|
||||
for i in 0..a {
|
||||
write!(self.buf, "arg_{i},")?;
|
||||
}
|
||||
for _ in a..*fargc {
|
||||
write!(self.buf, "vec2f(0.0),")?;
|
||||
}
|
||||
write!(self.buf, ");}}")?;
|
||||
}
|
||||
}
|
||||
write!(self.buf, "}};}}")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_iter(&mut self, argc: u32) -> CompileResult<'s> {
|
||||
if !self.generate.contains_key(&format!("invoke{argc}")) {
|
||||
self.generate.insert(format!("invoke{argc}"), Generate::Iter { argc });
|
||||
self.generate_invoke(argc)?;
|
||||
writeln!(self.buf)?;
|
||||
}
|
||||
write!(self.buf, "fn iter{argc}(func:vec2f,n:vec2f,")?;
|
||||
for i in 0..argc {
|
||||
write!(self.buf, "arg_{i}:vec2f")?;
|
||||
write!(self.buf, ",")?;
|
||||
}
|
||||
write!(self.buf, ")->vec2f{{var result=arg_0;")?;
|
||||
write!(self.buf, "for(var i=0;i<i32(n.x);i++){{result=invoke{argc}(func,result,")?;
|
||||
for i in 1..argc {
|
||||
write!(self.buf, "arg_{i},")?;
|
||||
}
|
||||
write!(self.buf, ");}}return result;}}")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_derivative(&mut self, func: String, nderiv: u32, argc: u32) -> CompileResult<'s> {
|
||||
if let Some(f) = func.strip_suffix("deriv_") {
|
||||
if !self.generate.contains_key(f) {
|
||||
self.generate.insert(func.clone(), Generate::Derivative { func: f.to_owned(), nderiv: nderiv - 1, argc });
|
||||
self.generate_derivative(f.to_owned(), nderiv - 1, argc)?;
|
||||
}
|
||||
}
|
||||
write!(self.buf, "fn deriv_{func}(z:vec2f")?;
|
||||
let mut args = String::new();
|
||||
for i in 1..argc {
|
||||
write!(self.buf, ",arg{i}:vec2f")?;
|
||||
args += &format!(",arg{i}");
|
||||
}
|
||||
write!(self.buf, ")->vec2f{{")?;
|
||||
write!(self.buf, "\
|
||||
let a = c_mul({func}(z + vec2( D_EPS, 0.0){args}), vec2( 0.25/D_EPS, 0.0));\
|
||||
let b = c_mul({func}(z + vec2(-D_EPS, 0.0){args}), vec2(-0.25/D_EPS, 0.0));\
|
||||
let c = c_mul({func}(z + vec2(0.0, D_EPS){args}), vec2(0.0, -0.25/D_EPS));\
|
||||
let d = c_mul({func}(z + vec2(0.0, -D_EPS){args}), vec2(0.0, 0.25/D_EPS));\
|
||||
return a + b + c + d;}}\
|
||||
")?;
|
||||
Ok(())
|
||||
}
|
||||
// fn generate_iter(&mut self, argc: u32) -> CompileResult<'s> {
|
||||
// if !self.generate.contains_key(&format!("invoke{argc}")) {
|
||||
// self.generate.insert(format!("invoke{argc}"), Generate::Iter { argc });
|
||||
// self.generate_invoke(argc)?;
|
||||
// writeln!(self.buf)?;
|
||||
// }
|
||||
// write!(self.buf, "fn iter{argc}(func:vec2f,n:vec2f,")?;
|
||||
// for i in 0..argc {
|
||||
// write!(self.buf, "arg_{i}:vec2f")?;
|
||||
// write!(self.buf, ",")?;
|
||||
// }
|
||||
// write!(self.buf, ")->vec2f{{var result=arg_0;")?;
|
||||
// write!(self.buf, "for(var i=0;i<i32(n.x);i++){{result=invoke{argc}(func,result,")?;
|
||||
// for i in 1..argc {
|
||||
// write!(self.buf, "arg_{i},")?;
|
||||
// }
|
||||
// write!(self.buf, ");}}return result;}}")?;
|
||||
// Ok(())
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -20,10 +20,11 @@ fn apply_binary(op: BinaryOp, u: Complex, v: Complex) -> Complex {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn optimize<'s>(stmts: Vec<Stmt<'s>>) -> Vec<Stmt<'s>> {
|
||||
pub fn optimize(stmts: Vec<Stmt>) -> Vec<Stmt> {
|
||||
stmts.into_iter().map(|s| match s {
|
||||
Stmt::AssignConst(a, expr) => Stmt::AssignConst(a, optimize_expr(expr)),
|
||||
Stmt::AssignFunc(a, b, expr) => Stmt::AssignFunc(a, b, optimize_expr(expr)),
|
||||
Stmt::Const { name, body } => Stmt::Const { name, body: optimize_expr(body) },
|
||||
Stmt::Func { name, args, body } => Stmt::Func { name, args, body: optimize_expr(body) },
|
||||
_ => s,
|
||||
}).collect()
|
||||
}
|
||||
|
||||
|
@ -41,17 +42,16 @@ fn optimize_expr<'s>(e: Expr<'s>) -> Expr<'s> {
|
|||
(u, v) => Expr::Binary(op, Box::new(u), Box::new(v))
|
||||
}
|
||||
},
|
||||
Expr::FnCall { name, args, nderiv } => {
|
||||
let args: Vec<Expr<'s>> = args.into_iter().map(|a| optimize_expr(a)).collect();
|
||||
Expr::FnCall(name, args) => {
|
||||
let args: Vec<Expr<'s>> = args.into_iter().map(optimize_expr).collect();
|
||||
if let Some((_, Type::Function(argc), Some(func))) = BUILTINS.with(|m| m.get(name).copied()) {
|
||||
if argc as usize == args.len() && nderiv == 0 {
|
||||
if args.iter().all(|e| matches!(e, Expr::Number(_))) {
|
||||
let ns: Vec<Complex> = args.into_iter().map(|a| match a { Expr::Number(n) => n, _ => unreachable!() }).collect();
|
||||
return Expr::Number(func(&ns))
|
||||
}
|
||||
if argc as usize == args.len()
|
||||
&& args.iter().all(|e| matches!(e, Expr::Number(_))) {
|
||||
let ns: Vec<Complex> = args.into_iter().map(|a| match a { Expr::Number(n) => n, _ => unreachable!() }).collect();
|
||||
return Expr::Number(func(&ns))
|
||||
}
|
||||
}
|
||||
Expr::FnCall { name, args, nderiv }
|
||||
Expr::FnCall(name, args)
|
||||
}
|
||||
Expr::Name(name) => {
|
||||
if let Some((_, Type::Number, Some(func))) = BUILTINS.with(|m| m.get(name).copied()) {
|
||||
|
@ -60,6 +60,6 @@ fn optimize_expr<'s>(e: Expr<'s>) -> Expr<'s> {
|
|||
e
|
||||
}
|
||||
}
|
||||
_ => e,
|
||||
Expr::Number(_) => e,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@ use super::{scanner::{Scanner, Token}, Position};
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ParseError<'s> {
|
||||
UnexpectedTokenPrefix(Position, Token<'s>),
|
||||
UnexpectedExpr(Position, Expr<'s>),
|
||||
Unexpected(Position, Token<'s>),
|
||||
Expected(Position, Token<'s>),
|
||||
InvalidLValue(Position),
|
||||
InvalidFunction(Expr<'s>),
|
||||
|
@ -31,10 +32,10 @@ impl BinaryOp {
|
|||
|
||||
pub const fn precedence(self) -> (u32, u32) {
|
||||
match self {
|
||||
BinaryOp::Add => (10, 11),
|
||||
BinaryOp::Sub => (10, 11),
|
||||
BinaryOp::Mul => (20, 21),
|
||||
BinaryOp::Div => (20, 21),
|
||||
BinaryOp::Add
|
||||
| BinaryOp::Sub => (10, 11),
|
||||
BinaryOp::Mul
|
||||
| BinaryOp::Div => (20, 21),
|
||||
BinaryOp::Pow => (31, 30),
|
||||
}
|
||||
}
|
||||
|
@ -65,16 +66,17 @@ impl UnaryOp {
|
|||
pub enum Expr<'s> {
|
||||
Number(Complex),
|
||||
Name(&'s str),
|
||||
NameDeriv(&'s str, u32),
|
||||
Unary(UnaryOp, Box<Expr<'s>>),
|
||||
Binary(BinaryOp, Box<Expr<'s>>, Box<Expr<'s>>),
|
||||
FnCall { name: &'s str, args: Vec<Expr<'s>>, nderiv: u32 },
|
||||
FnCall(&'s str, Vec<Expr<'s>>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Stmt<'s> {
|
||||
AssignConst(&'s str, Expr<'s>),
|
||||
AssignFunc(&'s str, Vec<&'s str>, Expr<'s>),
|
||||
Const { name: &'s str, body: Expr<'s> },
|
||||
Func { name: &'s str, args: Vec<&'s str>, body: Expr<'s> },
|
||||
Deriv { name: &'s str, func: &'s str },
|
||||
Iter { name: &'s str, func: &'s str, count: u32 }
|
||||
}
|
||||
|
||||
pub struct Parser<'s> {
|
||||
|
@ -113,21 +115,13 @@ impl <'s> Parser<'s> {
|
|||
tok => if let Some(op) = UnaryOp::from_token(tok) {
|
||||
Expr::Unary(op, Box::new(self.expr(op.precedence())?))
|
||||
} else {
|
||||
return Err(ParseError::UnexpectedTokenPrefix(pos, tok))
|
||||
return Err(ParseError::Unexpected(pos, tok))
|
||||
}
|
||||
};
|
||||
|
||||
while let Some((_, tok)) = self.scanner.peek() {
|
||||
expr = match tok {
|
||||
Token::Equal | Token::RParen | Token::Newline | Token::Comma => break,
|
||||
Token::Quote => {
|
||||
self.scanner.next();
|
||||
match expr {
|
||||
Expr::Name(name) => Expr::NameDeriv(name, 1),
|
||||
Expr::NameDeriv(name, nderiv) => Expr::NameDeriv(name, nderiv+1),
|
||||
_ => return Err(ParseError::InvalidFunction(expr))
|
||||
}
|
||||
},
|
||||
Token::Equal | Token::Colon | Token::RParen | Token::Newline | Token::Comma => break,
|
||||
Token::LParen => {
|
||||
self.scanner.next();
|
||||
let mut args = Vec::new();
|
||||
|
@ -142,8 +136,7 @@ impl <'s> Parser<'s> {
|
|||
}
|
||||
self.expect(Token::RParen)?;
|
||||
match expr {
|
||||
Expr::Name(name) => Expr::FnCall { name, args, nderiv: 0 },
|
||||
Expr::NameDeriv(name, nderiv) => Expr::FnCall { name, args, nderiv },
|
||||
Expr::Name(name) => Expr::FnCall(name, args),
|
||||
_ => return Err(ParseError::InvalidFunction(expr)),
|
||||
}
|
||||
},
|
||||
|
@ -169,6 +162,70 @@ impl <'s> Parser<'s> {
|
|||
Ok(expr)
|
||||
}
|
||||
|
||||
pub fn parse_stmt_equals(&mut self, lhs_pos: Position, lhs: Expr<'s>) -> Result<Stmt<'s>, ParseError<'s>> {
|
||||
if self.scanner.peek().is_none() {
|
||||
return Err(ParseError::UnexpectedEof)
|
||||
}
|
||||
|
||||
let rhs = self.expr(0)?;
|
||||
|
||||
if self.scanner.peek().is_some() {
|
||||
self.expect(Token::Newline)?;
|
||||
}
|
||||
|
||||
match lhs {
|
||||
Expr::Name(name) => Ok(Stmt::Const { name, body: rhs }),
|
||||
Expr::FnCall(name, args) => {
|
||||
let mut arg_names = Vec::with_capacity(args.len());
|
||||
for arg in args {
|
||||
if let Expr::Name(name) = arg {
|
||||
arg_names.push(name);
|
||||
}
|
||||
}
|
||||
Ok(Stmt::Func { name, args: arg_names, body: rhs })
|
||||
}
|
||||
_ => return Err(ParseError::InvalidLValue(lhs_pos))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_stmt_deriv(&mut self, lhs_pos: Position, lhs: Expr<'s>) -> Result<Stmt<'s>, ParseError<'s>> {
|
||||
if self.scanner.peek().is_none() {
|
||||
return Err(ParseError::UnexpectedEof)
|
||||
}
|
||||
let pos = self.scanner.peek().unwrap().0;
|
||||
let f = self.expr(0)?;
|
||||
match (lhs, f) {
|
||||
(Expr::Name(name), Expr::Name(func)) => Ok(Stmt::Deriv { name, func }),
|
||||
(Expr::Name(_), f) => Err(ParseError::UnexpectedExpr(pos, f)),
|
||||
(lhs, _) => Err(ParseError::UnexpectedExpr(lhs_pos, lhs)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_stmt_iter(&mut self, lhs_pos: Position, lhs: Expr<'s>) -> Result<Stmt<'s>, ParseError<'s>> {
|
||||
let Expr::Name(name) = lhs else {
|
||||
return Err(ParseError::UnexpectedExpr(lhs_pos, lhs))
|
||||
};
|
||||
if self.scanner.peek().is_none() {
|
||||
return Err(ParseError::UnexpectedEof)
|
||||
}
|
||||
let pos = self.scanner.peek().unwrap().0;
|
||||
let func = self.expr(0)?;
|
||||
let Expr::Name(func) = func else {
|
||||
return Err(ParseError::UnexpectedExpr(pos, func))
|
||||
};
|
||||
self.expect(Token::Comma)?;
|
||||
if self.scanner.peek().is_none() {
|
||||
return Err(ParseError::UnexpectedEof)
|
||||
}
|
||||
let pos = self.scanner.peek().unwrap().0;
|
||||
let count = self.expr(0)?;
|
||||
let Expr::Number(count) = count else {
|
||||
return Err(ParseError::UnexpectedExpr(pos, count))
|
||||
};
|
||||
Ok(Stmt::Iter { name, func, count: count.re as u32 })
|
||||
|
||||
}
|
||||
|
||||
pub fn parse(&mut self) -> Result<Vec<Stmt<'s>>, ParseError<'s>> {
|
||||
let mut stmts = Vec::new();
|
||||
while self.scanner.peek().is_some() {
|
||||
|
@ -183,30 +240,16 @@ impl <'s> Parser<'s> {
|
|||
let lhs_pos = self.scanner.peek().unwrap().0;
|
||||
let lhs = self.expr(0)?;
|
||||
|
||||
self.expect(Token::Equal)?;
|
||||
|
||||
if self.scanner.peek().is_none() {
|
||||
return Err(ParseError::UnexpectedEof)
|
||||
}
|
||||
|
||||
let rhs = self.expr(0)?;
|
||||
|
||||
if self.scanner.peek().is_some() {
|
||||
self.expect(Token::Newline)?;
|
||||
}
|
||||
|
||||
let stmt = match lhs {
|
||||
Expr::Name(name) => Stmt::AssignConst(name, rhs),
|
||||
Expr::FnCall { name, args, nderiv: 0 } => {
|
||||
let mut arg_names = Vec::with_capacity(args.len());
|
||||
for arg in args {
|
||||
if let Expr::Name(name) = arg {
|
||||
arg_names.push(name)
|
||||
}
|
||||
}
|
||||
Stmt::AssignFunc(name, arg_names, rhs)
|
||||
let stmt = match self.scanner.next() {
|
||||
Some((_, Token::Equal)) => self.parse_stmt_equals(lhs_pos, lhs)?,
|
||||
Some((_, Token::Colon)) => match self.scanner.next() {
|
||||
Some((_, Token::Name("deriv"))) => self.parse_stmt_deriv(lhs_pos, lhs)?,
|
||||
Some((_, Token::Name("iter"))) => self.parse_stmt_iter(lhs_pos, lhs)?,
|
||||
Some((pos, tok)) => return Err(ParseError::Unexpected(pos, tok)),
|
||||
None => return Err(ParseError::UnexpectedEof),
|
||||
}
|
||||
_ => return Err(ParseError::InvalidLValue(lhs_pos))
|
||||
Some((pos, tok)) => return Err(ParseError::Unexpected(pos, tok)),
|
||||
None => return Err(ParseError::UnexpectedEof),
|
||||
};
|
||||
|
||||
stmts.push(stmt);
|
||||
|
|
|
@ -14,7 +14,7 @@ pub enum Token<'s> {
|
|||
Caret,
|
||||
Equal,
|
||||
Comma,
|
||||
Quote,
|
||||
Colon,
|
||||
LParen,
|
||||
RParen,
|
||||
Newline,
|
||||
|
@ -94,7 +94,7 @@ impl <'s> Scanner<'s> {
|
|||
'*' => Token::Star,
|
||||
'/' => Token::Slash,
|
||||
'^' => Token::Caret,
|
||||
'\'' => Token::Quote,
|
||||
':' => Token::Colon,
|
||||
'=' => Token::Equal,
|
||||
',' => Token::Comma,
|
||||
'(' => Token::LParen,
|
||||
|
|
4
src/lib.rs
Normal file
4
src/lib.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
pub mod language;
|
||||
pub mod renderer;
|
||||
pub mod complex;
|
||||
pub mod wasm;
|
46
src/main.rs
46
src/main.rs
|
@ -1,26 +1,48 @@
|
|||
use language::compile;
|
||||
use renderer::run;
|
||||
use winit::{event_loop::EventLoop, window::Window};
|
||||
|
||||
mod language;
|
||||
mod renderer;
|
||||
mod complex;
|
||||
use cxgraph::{renderer::WgpuState, language::compile};
|
||||
use winit::{event_loop::EventLoop, window::Window, event::{Event, WindowEvent}};
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
env_logger::builder()
|
||||
.filter_level(log::LevelFilter::Info)
|
||||
.init();
|
||||
|
||||
let src = r#"
|
||||
f(z,a,b,c) = (z-a)*(z-b)*(z-c)
|
||||
g(z,a,b,c) = z - f(z,a,b,c)/f'(z,a,b,c)
|
||||
plot(z) = iter4(g, 50, z/3, z, 1, -1)
|
||||
f(z, c) = z^2 + c
|
||||
g: iter f, 100
|
||||
plot(z) = g(z, z)
|
||||
"#;
|
||||
let wgsl = compile(src).unwrap();
|
||||
println!("{}", wgsl);
|
||||
println!("{wgsl}");
|
||||
|
||||
let event_loop = EventLoop::new();
|
||||
let window = Window::new(&event_loop).unwrap();
|
||||
window.set_title("window");
|
||||
|
||||
pollster::block_on(run(event_loop, window, &wgsl));
|
||||
|
||||
}
|
||||
|
||||
async fn run(event_loop: EventLoop<()>, window: Window, code: &str) {
|
||||
let size = window.inner_size();
|
||||
let mut state = WgpuState::new(&window, size.into()).await;
|
||||
|
||||
state.load_shaders(code);
|
||||
|
||||
state.set_bounds((-5.0, -5.0), (5.0, 5.0));
|
||||
state.set_shading_intensity(0.01);
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
control_flow.set_wait();
|
||||
match event {
|
||||
Event::WindowEvent { event: WindowEvent::CloseRequested, .. }
|
||||
=> control_flow.set_exit(),
|
||||
Event::RedrawRequested(_)
|
||||
=> state.redraw(),
|
||||
Event::WindowEvent { event: WindowEvent::Resized(size), .. } => {
|
||||
state.resize(size.into());
|
||||
window.request_redraw();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,36 +2,36 @@ use std::num::NonZeroU64;
|
|||
|
||||
use encase::{ShaderType, ShaderSize, UniformBuffer};
|
||||
use wgpu::util::DeviceExt;
|
||||
use winit::{event_loop::EventLoop, window::Window, event::{Event, WindowEvent}, dpi::PhysicalSize};
|
||||
|
||||
type Vec2u = cgmath::Vector2<u32>;
|
||||
type Vec2f = cgmath::Vector2<f32>;
|
||||
|
||||
#[derive(ShaderType)]
|
||||
struct Uniforms {
|
||||
resolution: Vec2u,
|
||||
bounds_min: Vec2f,
|
||||
bounds_max: Vec2f,
|
||||
shading_intensity: f32,
|
||||
pub resolution: Vec2u,
|
||||
pub bounds_min: Vec2f,
|
||||
pub bounds_max: Vec2f,
|
||||
pub shading_intensity: f32,
|
||||
}
|
||||
|
||||
struct State {
|
||||
pub struct WgpuState {
|
||||
uniforms: Uniforms,
|
||||
surface: wgpu::Surface,
|
||||
device: wgpu::Device,
|
||||
config: wgpu::SurfaceConfiguration,
|
||||
render_pipeline: Option<wgpu::RenderPipeline>,
|
||||
uniform_bind_group: wgpu::BindGroup,
|
||||
uniform_bind_group_layout: wgpu::BindGroupLayout,
|
||||
uniform_layout: wgpu::BindGroupLayout,
|
||||
uniform_buffer: wgpu::Buffer,
|
||||
queue: wgpu::Queue,
|
||||
queue: wgpu::Queue
|
||||
}
|
||||
|
||||
impl State {
|
||||
async fn new(window: &Window) -> Self {
|
||||
let size = window.inner_size();
|
||||
impl WgpuState {
|
||||
pub async fn new<W>(window: &W, size: (u32, u32)) -> Self
|
||||
where W: raw_window_handle::HasRawWindowHandle + raw_window_handle::HasRawDisplayHandle {
|
||||
|
||||
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::default());
|
||||
let surface = unsafe { instance.create_surface(&window) }.unwrap();
|
||||
let surface = unsafe { instance.create_surface(&window) }.unwrap();
|
||||
|
||||
let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions {
|
||||
power_preference: wgpu::PowerPreference::default(),
|
||||
|
@ -43,19 +43,19 @@ impl State {
|
|||
&wgpu::DeviceDescriptor {
|
||||
label: None,
|
||||
features: wgpu::Features::empty(),
|
||||
limits: wgpu::Limits::default(),
|
||||
limits: wgpu::Limits::downlevel_webgl2_defaults(),
|
||||
},
|
||||
None
|
||||
).await.unwrap();
|
||||
).await.map_err(|e| e.to_string()).unwrap();
|
||||
|
||||
let format = surface.get_capabilities(&adapter).formats[0];
|
||||
|
||||
let config = wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
format,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
present_mode: wgpu::PresentMode::Mailbox,
|
||||
width: size.0,
|
||||
height: size.1,
|
||||
present_mode: wgpu::PresentMode::Fifo,
|
||||
alpha_mode: wgpu::CompositeAlphaMode::Auto,
|
||||
view_formats: vec![format],
|
||||
};
|
||||
|
@ -69,7 +69,7 @@ impl State {
|
|||
contents: &[0; Uniforms::SHADER_SIZE.get() as usize],
|
||||
});
|
||||
|
||||
let uniform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
let uniform_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
label: None,
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
|
@ -87,7 +87,7 @@ impl State {
|
|||
|
||||
let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: None,
|
||||
layout: &uniform_bind_group_layout,
|
||||
layout: &uniform_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
|
@ -102,19 +102,27 @@ impl State {
|
|||
|
||||
// Done //
|
||||
|
||||
let uniforms = Uniforms {
|
||||
resolution: size.into(),
|
||||
bounds_min: [-0.0, -0.0].into(),
|
||||
bounds_max: [ 0.0, 0.0].into(),
|
||||
shading_intensity: 0.0,
|
||||
};
|
||||
|
||||
Self {
|
||||
uniforms,
|
||||
surface,
|
||||
device,
|
||||
config,
|
||||
device,
|
||||
render_pipeline: None,
|
||||
queue,
|
||||
uniform_bind_group,
|
||||
uniform_bind_group_layout,
|
||||
uniform_layout,
|
||||
uniform_buffer,
|
||||
queue,
|
||||
}
|
||||
}
|
||||
|
||||
fn load_shaders(&mut self, userdefs: &str) {
|
||||
pub fn load_shaders(&mut self, userdefs: &str) {
|
||||
// Shaders //
|
||||
let src = include_str!("shader.wgsl");
|
||||
let src = src.replace("//INCLUDE//\n", userdefs);
|
||||
|
@ -147,7 +155,7 @@ impl State {
|
|||
|
||||
let pipeline_layout = self.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: None,
|
||||
bind_group_layouts: &[&self.uniform_bind_group_layout],
|
||||
bind_group_layouts: &[&self.uniform_layout],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
|
@ -165,7 +173,7 @@ impl State {
|
|||
self.render_pipeline = Some(render_pipeline);
|
||||
}
|
||||
|
||||
fn redraw(&self, uniforms: &Uniforms) {
|
||||
pub fn redraw(&self) {
|
||||
let frame = self.surface.get_current_texture().unwrap();
|
||||
let view = frame.texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||
|
@ -192,45 +200,26 @@ impl State {
|
|||
}
|
||||
}
|
||||
let mut uniform_buffer = UniformBuffer::new([0; Uniforms::SHADER_SIZE.get() as usize]);
|
||||
uniform_buffer.write(uniforms).unwrap();
|
||||
uniform_buffer.write(&self.uniforms).unwrap();
|
||||
self.queue.write_buffer(&self.uniform_buffer, 0, &uniform_buffer.into_inner());
|
||||
self.queue.submit(Some(encoder.finish()));
|
||||
frame.present();
|
||||
}
|
||||
|
||||
fn resize(&mut self, size: PhysicalSize<u32>) {
|
||||
self.config.width = size.width;
|
||||
self.config.height = size.height;
|
||||
pub fn resize(&mut self, size: (u32, u32)) {
|
||||
let size = (size.0.max(1).min(2048), size.1.max(1).min(2048));
|
||||
self.config.width = size.0;
|
||||
self.config.height = size.1;
|
||||
self.surface.configure(&self.device, &self.config);
|
||||
self.uniforms.resolution = size.into();
|
||||
}
|
||||
|
||||
pub fn set_bounds(&mut self, min: (f32, f32), max: (f32, f32)) {
|
||||
self.uniforms.bounds_min = min.into();
|
||||
self.uniforms.bounds_max = max.into();
|
||||
}
|
||||
|
||||
pub fn set_shading_intensity(&mut self, value: f32) {
|
||||
self.uniforms.shading_intensity = value;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run(event_loop: EventLoop<()>, window: Window, code: &str) {
|
||||
let mut state = State::new(&window).await;
|
||||
state.load_shaders(code);
|
||||
let mut uniforms = Uniforms {
|
||||
resolution: [window.inner_size().width, window.inner_size().height].into(),
|
||||
bounds_min: [0.73, 0.65].into(),
|
||||
bounds_max: [0.98, 0.9].into(),
|
||||
shading_intensity: 0.001,
|
||||
};
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
control_flow.set_wait();
|
||||
match event {
|
||||
Event::WindowEvent { event: WindowEvent::CloseRequested, .. }
|
||||
=> control_flow.set_exit(),
|
||||
Event::RedrawRequested(_)
|
||||
=> state.redraw(&uniforms),
|
||||
Event::WindowEvent { event: WindowEvent::Resized(size), .. } => {
|
||||
uniforms.resolution = [size.width, size.height].into();
|
||||
state.resize(size);
|
||||
window.request_redraw()
|
||||
}
|
||||
Event::MainEventsCleared => {
|
||||
//window.request_redraw();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
77
src/wasm.rs
Normal file
77
src/wasm.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
#![cfg(target_arch="wasm32")]
|
||||
|
||||
use crate::{renderer::WgpuState, language::compile};
|
||||
use winit::{event_loop::EventLoop, window::Window};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
// wasm is single-threaded so there's no possibility of Bad happening
|
||||
// due to mutable global state. this will be Some(..) once start runs
|
||||
static mut WGPU_STATE: Option<WgpuState> = None;
|
||||
|
||||
fn with_state<F>(f: F)
|
||||
where F: Fn(&mut WgpuState) {
|
||||
let mut state = unsafe { WGPU_STATE.take().unwrap() };
|
||||
f(&mut state);
|
||||
unsafe { WGPU_STATE = Some(state) };
|
||||
}
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub async fn start() {
|
||||
use winit::dpi::PhysicalSize;
|
||||
use winit::platform::web::WindowExtWebSys;
|
||||
|
||||
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||
console_log::init_with_level(log::Level::Info).expect("Couldn't initialize logger");
|
||||
|
||||
let event_loop = EventLoop::new();
|
||||
let window = Window::new(&event_loop).unwrap();
|
||||
|
||||
window.set_inner_size(PhysicalSize::new(1200, 1200));
|
||||
web_sys::window()
|
||||
.and_then(|win| win.document())
|
||||
.and_then(|doc| {
|
||||
let dst = doc.get_element_by_id("canvas_container")?;
|
||||
let canvas = web_sys::Element::from(window.canvas());
|
||||
dst.append_child(&canvas).ok()?;
|
||||
Some(())
|
||||
}).expect("Couldn't append canvas to document body.");
|
||||
|
||||
window.set_title("window");
|
||||
|
||||
let size = window.inner_size();
|
||||
let mut state = WgpuState::new(&window, size.into()).await;
|
||||
state.set_bounds((-5.0, -5.0), (5.0, 5.0));
|
||||
state.set_shading_intensity(0.01);
|
||||
unsafe { WGPU_STATE = Some(state) };
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn load_shader(src: &str) {
|
||||
let wgsl = compile(src).unwrap();
|
||||
with_state(|state| {
|
||||
state.load_shaders(&wgsl);
|
||||
state.redraw();
|
||||
});
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn redraw() {
|
||||
with_state(|state| {
|
||||
state.redraw();
|
||||
});
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn resize(width: u32, height: u32) {
|
||||
with_state(|state| state.resize((width, height)));
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn set_bounds(min_x: f32, min_y: f32, max_x: f32, max_y: f32) {
|
||||
with_state(|state| state.set_bounds((min_x, min_y), (max_x, max_y)));
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn set_shading_intensity(value: f32) {
|
||||
with_state(|state| state.set_shading_intensity(value));
|
||||
}
|
37
style.css
Normal file
37
style.css
Normal file
|
@ -0,0 +1,37 @@
|
|||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#main {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#header {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
#content {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#canvas_container {
|
||||
flex: 3;
|
||||
}
|
||||
|
||||
canvas {
|
||||
background-color: black;
|
||||
}
|
15
syntax.md
15
syntax.md
|
@ -1,15 +0,0 @@
|
|||
```
|
||||
f(z) = z^3 - 1
|
||||
fp(z): deriv f(z)
|
||||
n(z) = z - f(z)/fp(z)
|
||||
plot(z): iter w = z, w = f(w), w, 100
|
||||
```
|
||||
|
||||
unary ops: - / (neg recip)
|
||||
binary ops: + - * / ^ (add sub mul div pow)
|
||||
holomorphic fns: sqrt exp ln sin cos tan sinh cosh tanh asin acos atan asinh acosh atanh gamma
|
||||
bad fns: re im abs abssq arg conj
|
||||
higher order: iter deriv
|
||||
constants: i tau e
|
||||
|
||||
type sigils: complex, $function, @integer
|
Loading…
Reference in a new issue