initial commit
This commit is contained in:
commit
5a8ef55bb3
13 changed files with 3405 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/target
|
1886
Cargo.lock
generated
Normal file
1886
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
16
Cargo.toml
Normal file
16
Cargo.toml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
[package]
|
||||||
|
name = "wgpu_calc"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
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"
|
132
src/complex.rs
Normal file
132
src/complex.rs
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub struct Complex {
|
||||||
|
pub re: f64,
|
||||||
|
pub im: f64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Complex {
|
||||||
|
pub const fn new(re: f64, im: f64) -> Self {
|
||||||
|
Self { re, im }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<f64> for Complex {
|
||||||
|
fn from(value: f64) -> Self {
|
||||||
|
Self { re: value, im: 0.0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod cxfn {
|
||||||
|
use super::Complex;
|
||||||
|
|
||||||
|
pub type Function = fn(args: &[Complex]) -> Complex;
|
||||||
|
|
||||||
|
pub fn pos(a: &[Complex]) -> Complex {
|
||||||
|
a[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn neg(a: &[Complex]) -> Complex {
|
||||||
|
Complex::new(-a[0].re, -a[0].im)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn recip(a: &[Complex]) -> Complex {
|
||||||
|
let a = a[0];
|
||||||
|
let d = a.re*a.re + a.im*a.im;
|
||||||
|
Complex::new(a.re / d, -a.im / d)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn re(a: &[Complex]) -> Complex {
|
||||||
|
Complex::new(a[0].re, 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn im(a: &[Complex]) -> Complex {
|
||||||
|
Complex::new(a[0].im, 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn conj(a: &[Complex]) -> Complex {
|
||||||
|
Complex::new(a[0].re, -a[0].im)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn abs_sq(a: &[Complex]) -> Complex {
|
||||||
|
Complex::new(a[0].re*a[0].re + a[0].im*a[0].im, 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn abs(a: &[Complex]) -> Complex {
|
||||||
|
Complex::new((a[0].re*a[0].re + a[0].im*a[0].im).sqrt(), 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn arg(a: &[Complex]) -> Complex {
|
||||||
|
Complex::new(f64::atan2(a[0].im, a[0].re), 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(a: &[Complex]) -> Complex {
|
||||||
|
let (a, b) = (a[0], a[1]);
|
||||||
|
Complex::new(a.re + b.re, a.im + b.im)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sub(a: &[Complex]) -> Complex {
|
||||||
|
let (a, b) = (a[0], a[1]);
|
||||||
|
Complex::new(a.re - b.re, a.im - b.im)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mul(a: &[Complex]) -> Complex {
|
||||||
|
let (a, b) = (a[0], a[1]);
|
||||||
|
Complex::new(a.re*b.re - a.im*b.im, a.im*b.re + a.re*b.im)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn div(a: &[Complex]) -> Complex {
|
||||||
|
let (a, b) = (a[0], a[1]);
|
||||||
|
let d = b.re*b.re + b.im*b.im;
|
||||||
|
Complex::new((a.re*b.re + a.im*b.im)/d, (a.im*b.re - a.re*b.im)/d)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exp(a: &[Complex]) -> Complex {
|
||||||
|
let e = a[0].re.exp();
|
||||||
|
Complex::new(e * a[0].im.cos(), e * a[0].im.sin())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn log(a: &[Complex]) -> Complex {
|
||||||
|
let a = a[0];
|
||||||
|
Complex::new(0.5 * (a.re*a.re + a.im*a.im).ln(), f64::atan2(a.im, a.re))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pow(a: &[Complex]) -> Complex {
|
||||||
|
exp(&[mul(&[log(&[a[0]]), a[1]])])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sqrt(a: &[Complex]) -> Complex {
|
||||||
|
pow(&[a[0],Complex::new(0.5, 0.0)])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sin(a: &[Complex]) -> Complex {
|
||||||
|
let a = a[0];
|
||||||
|
Complex::new(a.re.sin()*a.im.cosh(), a.re.cos()*a.im.sinh())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cos(a: &[Complex]) -> Complex {
|
||||||
|
let a = a[0];
|
||||||
|
Complex::new(a.re.cos()*a.im.cosh(), -a.re.sin()*a.re.sinh())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tan(a: &[Complex]) -> Complex {
|
||||||
|
let a = a[0];
|
||||||
|
let d = (2.0*a.re).cos() + (2.0*a.im).cosh();
|
||||||
|
Complex::new((2.0*a.re).sin() / d, (2.0*a.im).sinh() / d)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sinh(a: &[Complex]) -> Complex {
|
||||||
|
let a = a[0];
|
||||||
|
Complex::new(a.re.sinh()*a.im.cos(), a.re.cosh()*a.re.sin())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cosh(a: &[Complex]) -> Complex {
|
||||||
|
let a = a[0];
|
||||||
|
Complex::new(a.re.cosh()*a.im.cos(), a.re.sinh()*a.re.sin())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tanh(a: &[Complex]) -> Complex {
|
||||||
|
let a = a[0];
|
||||||
|
let d = (2.0*a.re).cosh() + (2.0*a.im).cos();
|
||||||
|
Complex::new((2.0*a.re).sinh() / d, (2.0*a.im).sin() / d)
|
||||||
|
}
|
||||||
|
}
|
449
src/language/compiler.rs
Normal file
449
src/language/compiler.rs
Normal file
|
@ -0,0 +1,449 @@
|
||||||
|
use std::{collections::HashMap, fmt::Write};
|
||||||
|
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
use crate::complex::{Complex, cxfn};
|
||||||
|
|
||||||
|
use super::parser::{Expr, Stmt, UnaryOp, BinaryOp};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum CompileError<'s> {
|
||||||
|
FmtError,
|
||||||
|
TypeError(&'s str),
|
||||||
|
ArgCount(&'s str),
|
||||||
|
UndefinedVar(&'s str),
|
||||||
|
GlobalReassignment(&'s str),
|
||||||
|
BuiltinReassignment(&'s str),
|
||||||
|
StandaloneDerivative(&'s str),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <'s> From<std::fmt::Error> for CompileError<'s> {
|
||||||
|
fn from(_: std::fmt::Error) -> Self {
|
||||||
|
Self::FmtError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
pub static BUILTINS: HashMap<&'static str, (&'static str, Type, Option<cxfn::Function>)> = {
|
||||||
|
let mut m: HashMap<&'static str, (&'static str, Type, Option<cxfn::Function>)> = HashMap::new();
|
||||||
|
m.insert("i", ("CONST_I", Type::Number, Some(|_| Complex::new(0.0, 1.0))));
|
||||||
|
m.insert("e", ("CONST_E", Type::Number, Some(|_| Complex::new(std::f64::consts::E, 0.0))));
|
||||||
|
m.insert("tau", ("CONST_TAU",Type::Number, Some(|_| Complex::new(std::f64::consts::TAU, 0.0))));
|
||||||
|
m.insert("re", ("c_re", Type::Function(1), Some(cxfn::re)));
|
||||||
|
m.insert("im", ("c_im", Type::Function(1), Some(cxfn::im)));
|
||||||
|
m.insert("conj", ("c_conj", Type::Function(1), Some(cxfn::conj)));
|
||||||
|
m.insert("abs_sq", ("c_abs_sq", Type::Function(1), Some(cxfn::abs_sq)));
|
||||||
|
m.insert("abs", ("c_abs", Type::Function(1), Some(cxfn::abs)));
|
||||||
|
m.insert("arg", ("c_arg", Type::Function(1), Some(cxfn::arg)));
|
||||||
|
m.insert("pos", ("c_pos", Type::Function(1), Some(cxfn::pos)));
|
||||||
|
m.insert("neg", ("c_neg", Type::Function(1), Some(cxfn::neg)));
|
||||||
|
m.insert("recip", ("c_recip", Type::Function(1), Some(cxfn::recip)));
|
||||||
|
m.insert("add", ("c_add", Type::Function(2), Some(cxfn::add)));
|
||||||
|
m.insert("sub", ("c_sub", Type::Function(2), Some(cxfn::sub)));
|
||||||
|
m.insert("mul", ("c_mul", Type::Function(2), Some(cxfn::mul)));
|
||||||
|
m.insert("div", ("c_div", Type::Function(2), Some(cxfn::div)));
|
||||||
|
m.insert("recip", ("c_recip", Type::Function(1), Some(cxfn::recip)));
|
||||||
|
m.insert("exp", ("c_exp", Type::Function(1), Some(cxfn::exp)));
|
||||||
|
m.insert("log", ("c_log", Type::Function(1), Some(cxfn::log)));
|
||||||
|
m.insert("sqrt", ("c_sqrt", Type::Function(1), Some(cxfn::sqrt)));
|
||||||
|
m.insert("sin", ("c_sin", Type::Function(1), Some(cxfn::sin)));
|
||||||
|
m.insert("cos", ("c_cos", Type::Function(1), Some(cxfn::cos)));
|
||||||
|
m.insert("tan", ("c_tan", Type::Function(1), Some(cxfn::tan)));
|
||||||
|
m.insert("sinh", ("c_sinh", Type::Function(1), Some(cxfn::sinh)));
|
||||||
|
m.insert("cosh", ("c_cosh", Type::Function(1), Some(cxfn::cosh)));
|
||||||
|
m.insert("tanh", ("c_tanh", Type::Function(1), Some(cxfn::tanh)));
|
||||||
|
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)]
|
||||||
|
pub enum Type {
|
||||||
|
Number,
|
||||||
|
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 },
|
||||||
|
}
|
||||||
|
|
||||||
|
enum NameInfo {
|
||||||
|
Local { id: u32 },
|
||||||
|
Global { ty: Type, cname: String },
|
||||||
|
Builtin { ty: Type, bname: &'static str },
|
||||||
|
Generated { ty: Type, gname: String, gen: Generate }
|
||||||
|
}
|
||||||
|
|
||||||
|
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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type LocalTable<'s> = HashMap<&'s str, u32>;
|
||||||
|
|
||||||
|
type CompileResult<'s,T=()> = Result<T, CompileError<'s>>;
|
||||||
|
|
||||||
|
pub fn compile<'s>(buf: &mut impl Write, stmts: &[Stmt<'s>]) -> CompileResult<'s> {
|
||||||
|
let mut compiler = Compiler::new(buf);
|
||||||
|
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> {
|
||||||
|
fn new(buf: &'w mut W) -> Self {
|
||||||
|
Self {
|
||||||
|
buf,
|
||||||
|
globals: HashMap::new(),
|
||||||
|
generate: HashMap::new(),
|
||||||
|
next_fn_id: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////
|
||||||
|
// Statements //
|
||||||
|
//////////////////
|
||||||
|
|
||||||
|
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, ";}}")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeln!(self.buf)?;
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_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> {
|
||||||
|
let strings = unop_strings(op);
|
||||||
|
write!(self.buf, "{}", strings[0])?;
|
||||||
|
self.compile_expr(locals, arg)?;
|
||||||
|
write!(self.buf, "{}", strings[1])?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_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)?;
|
||||||
|
write!(self.buf, "{}", strings[1])?;
|
||||||
|
self.compile_expr(locals, rhs)?;
|
||||||
|
write!(self.buf, "{}", strings[2])?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_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)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_fncall(&mut self, locals: &LocalTable<'s>, name: &'s str, args: &Vec<Expr<'s>>, nderiv: u32) -> 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_")?;
|
||||||
|
}
|
||||||
|
self.compile_name_fncall(name, name_info, args.len() as u32)?;
|
||||||
|
write!(self.buf, "(")?;
|
||||||
|
for arg in args {
|
||||||
|
self.compile_expr(locals, arg)?;
|
||||||
|
write!(self.buf, ",")?;
|
||||||
|
}
|
||||||
|
write!(self.buf, ")")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////
|
||||||
|
// 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 let Some(ty) = self.globals.get(name) {
|
||||||
|
return Some(NameInfo::Global { ty: *ty, cname: format!("func_{name}") })
|
||||||
|
}
|
||||||
|
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 }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const fn unop_strings(op: UnaryOp) -> [&'static str; 2] {
|
||||||
|
match op {
|
||||||
|
UnaryOp::Pos => ["+(", ")"],
|
||||||
|
UnaryOp::Neg => ["-(", ")"],
|
||||||
|
UnaryOp::Recip => ["c_recip(", ")"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn binop_strings(op: BinaryOp) -> [&'static str; 3] {
|
||||||
|
match op {
|
||||||
|
BinaryOp::Add => ["(", ")+(", ")"],
|
||||||
|
BinaryOp::Sub => ["(", ")-(", ")"],
|
||||||
|
BinaryOp::Mul => ["c_mul(", ",", ")"],
|
||||||
|
BinaryOp::Div => ["c_div(", ",", ")"],
|
||||||
|
BinaryOp::Pow => ["c_pow(", ",", ")"],
|
||||||
|
}
|
||||||
|
}
|
42
src/language/mod.rs
Normal file
42
src/language/mod.rs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
mod scanner;
|
||||||
|
mod parser;
|
||||||
|
mod compiler;
|
||||||
|
mod optimizer;
|
||||||
|
|
||||||
|
pub use parser::ParseError;
|
||||||
|
pub use compiler::CompileError;
|
||||||
|
|
||||||
|
use self::optimizer::optimize;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Position {
|
||||||
|
pub pos: u32,
|
||||||
|
pub line: u32,
|
||||||
|
pub col: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Error<'s> {
|
||||||
|
Parse(ParseError<'s>),
|
||||||
|
Compile(CompileError<'s>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <'s> From<CompileError<'s>> for Error<'s> {
|
||||||
|
fn from(value: CompileError<'s>) -> Self {
|
||||||
|
Self::Compile(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <'s> From<ParseError<'s>> for Error<'s> {
|
||||||
|
fn from(value: ParseError<'s>) -> Self {
|
||||||
|
Self::Parse(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile(src: &str) -> Result<String, Error> {
|
||||||
|
let mut buf = String::new();
|
||||||
|
let stmts = parser::Parser::new(src).parse()?;
|
||||||
|
let stmts = optimize(stmts);
|
||||||
|
compiler::compile(&mut buf, &stmts)?;
|
||||||
|
Ok(buf)
|
||||||
|
}
|
65
src/language/optimizer.rs
Normal file
65
src/language/optimizer.rs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
use crate::complex::{Complex, cxfn};
|
||||||
|
|
||||||
|
use super::{parser::{Expr, UnaryOp, BinaryOp, Stmt}, compiler::{BUILTINS, Type}};
|
||||||
|
|
||||||
|
fn apply_unary(op: UnaryOp, arg: Complex) -> Complex {
|
||||||
|
match op {
|
||||||
|
UnaryOp::Pos => cxfn::pos(&[arg]),
|
||||||
|
UnaryOp::Neg => cxfn::neg(&[arg]),
|
||||||
|
UnaryOp::Recip => cxfn::recip(&[arg]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_binary(op: BinaryOp, u: Complex, v: Complex) -> Complex {
|
||||||
|
match op {
|
||||||
|
BinaryOp::Add => cxfn::add(&[u, v]),
|
||||||
|
BinaryOp::Sub => cxfn::sub(&[u, v]),
|
||||||
|
BinaryOp::Mul => cxfn::mul(&[u, v]),
|
||||||
|
BinaryOp::Div => cxfn::div(&[u, v]),
|
||||||
|
BinaryOp::Pow => cxfn::pow(&[u, v]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn optimize<'s>(stmts: Vec<Stmt<'s>>) -> Vec<Stmt<'s>> {
|
||||||
|
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)),
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn optimize_expr<'s>(e: Expr<'s>) -> Expr<'s> {
|
||||||
|
match e {
|
||||||
|
Expr::Unary(op, arg) => {
|
||||||
|
match optimize_expr(*arg) {
|
||||||
|
Expr::Number(z) => Expr::Number(apply_unary(op, z)),
|
||||||
|
e => Expr::Unary(op, Box::new(e)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Expr::Binary(op, lhs, rhs) => {
|
||||||
|
match (optimize_expr(*lhs), optimize_expr(*rhs)) {
|
||||||
|
(Expr::Number(u), Expr::Number(v)) => Expr::Number(apply_binary(op, u, v)),
|
||||||
|
(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();
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::FnCall { name, args, nderiv }
|
||||||
|
}
|
||||||
|
Expr::Name(name) => {
|
||||||
|
if let Some((_, Type::Number, Some(func))) = BUILTINS.with(|m| m.get(name).copied()) {
|
||||||
|
Expr::Number(func(&[]))
|
||||||
|
} else {
|
||||||
|
e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => e,
|
||||||
|
}
|
||||||
|
}
|
216
src/language/parser.rs
Normal file
216
src/language/parser.rs
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
use std::iter::Peekable;
|
||||||
|
|
||||||
|
use crate::complex::Complex;
|
||||||
|
|
||||||
|
use super::{scanner::{Scanner, Token}, Position};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum ParseError<'s> {
|
||||||
|
UnexpectedTokenPrefix(Position, Token<'s>),
|
||||||
|
Expected(Position, Token<'s>),
|
||||||
|
InvalidLValue(Position),
|
||||||
|
InvalidFunction(Expr<'s>),
|
||||||
|
UnexpectedEof,
|
||||||
|
}
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum BinaryOp {
|
||||||
|
Add, Sub, Mul, Div, Pow,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BinaryOp {
|
||||||
|
pub const fn from_token(tok: Token) -> Option<Self> {
|
||||||
|
match tok {
|
||||||
|
Token::Plus => Some(Self::Add),
|
||||||
|
Token::Minus => Some(Self::Sub),
|
||||||
|
Token::Star => Some(Self::Mul),
|
||||||
|
Token::Slash => Some(Self::Div),
|
||||||
|
Token::Caret => Some(Self::Pow),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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::Pow => (31, 30),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum UnaryOp {
|
||||||
|
Pos, Neg, Recip,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnaryOp {
|
||||||
|
pub const fn from_token(tok: Token) -> Option<Self> {
|
||||||
|
match tok {
|
||||||
|
Token::Plus => Some(Self::Pos),
|
||||||
|
Token::Minus => Some(Self::Neg),
|
||||||
|
Token::Slash => Some(Self::Recip),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn precedence(self) -> u32 {
|
||||||
|
40
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
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 },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Stmt<'s> {
|
||||||
|
AssignConst(&'s str, Expr<'s>),
|
||||||
|
AssignFunc(&'s str, Vec<&'s str>, Expr<'s>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Parser<'s> {
|
||||||
|
scanner: Peekable<Scanner<'s>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <'s> Parser<'s> {
|
||||||
|
pub fn new(src: &'s str) -> Self {
|
||||||
|
Self {
|
||||||
|
scanner: Scanner::new(src).peekable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expect(&mut self, tok: Token<'s>) -> Result<Position, ParseError<'s>> {
|
||||||
|
match self.scanner.peek() {
|
||||||
|
Some((p, t)) if *t == tok => {
|
||||||
|
let p = *p;
|
||||||
|
self.scanner.next();
|
||||||
|
Ok(p)
|
||||||
|
},
|
||||||
|
Some((p, _)) => Err(ParseError::Expected(*p, tok)),
|
||||||
|
None => Err(ParseError::UnexpectedEof),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expr(&mut self, min_prec: u32) -> Result<Expr<'s>, ParseError<'s>> {
|
||||||
|
let (pos, tok) = self.scanner.next().unwrap();
|
||||||
|
let mut expr = match tok {
|
||||||
|
Token::Number(n) => Expr::Number(Complex::from(n)),
|
||||||
|
Token::Name(n) => Expr::Name(n),
|
||||||
|
Token::LParen => {
|
||||||
|
let e = self.expr(0)?;
|
||||||
|
self.expect(Token::RParen)?;
|
||||||
|
e
|
||||||
|
}
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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::LParen => {
|
||||||
|
self.scanner.next();
|
||||||
|
let mut args = Vec::new();
|
||||||
|
while !matches!(self.scanner.peek(), None | Some((_, Token::RParen))) {
|
||||||
|
args.push(self.expr(0)?);
|
||||||
|
match self.scanner.peek() {
|
||||||
|
Some((_, Token::Comma)) => { self.scanner.next(); },
|
||||||
|
Some((_, Token::RParen)) => break,
|
||||||
|
Some((pos, _)) => return Err(ParseError::Expected(*pos, Token::RParen)),
|
||||||
|
None => return Err(ParseError::UnexpectedEof),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.expect(Token::RParen)?;
|
||||||
|
match expr {
|
||||||
|
Expr::Name(name) => Expr::FnCall { name, args, nderiv: 0 },
|
||||||
|
Expr::NameDeriv(name, nderiv) => Expr::FnCall { name, args, nderiv },
|
||||||
|
_ => return Err(ParseError::InvalidFunction(expr)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tok => if let Some(op) = BinaryOp::from_token(*tok) {
|
||||||
|
let (lp, rp) = op.precedence();
|
||||||
|
if lp < min_prec {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
self.scanner.next();
|
||||||
|
let rhs = self.expr(rp)?;
|
||||||
|
Expr::Binary(op, Box::new(expr), Box::new(rhs))
|
||||||
|
} else {
|
||||||
|
let (lp, rp) = BinaryOp::Mul.precedence();
|
||||||
|
if lp < min_prec {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let rhs = self.expr(rp)?;
|
||||||
|
Expr::Binary(BinaryOp::Mul, Box::new(expr), Box::new(rhs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(&mut self) -> Result<Vec<Stmt<'s>>, ParseError<'s>> {
|
||||||
|
let mut stmts = Vec::new();
|
||||||
|
while self.scanner.peek().is_some() {
|
||||||
|
while matches!(self.scanner.peek(), Some((_, Token::Newline))) {
|
||||||
|
self.scanner.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.scanner.peek().is_none() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
_ => return Err(ParseError::InvalidLValue(lhs_pos))
|
||||||
|
};
|
||||||
|
|
||||||
|
stmts.push(stmt);
|
||||||
|
}
|
||||||
|
Ok(stmts)
|
||||||
|
}
|
||||||
|
}
|
116
src/language/scanner.rs
Normal file
116
src/language/scanner.rs
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
use std::{iter::Peekable, str::Chars};
|
||||||
|
|
||||||
|
use super::Position;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum Token<'s> {
|
||||||
|
Error,
|
||||||
|
Name(&'s str),
|
||||||
|
Number(f64),
|
||||||
|
Plus,
|
||||||
|
Minus,
|
||||||
|
Star,
|
||||||
|
Slash,
|
||||||
|
Caret,
|
||||||
|
Equal,
|
||||||
|
Comma,
|
||||||
|
Quote,
|
||||||
|
LParen,
|
||||||
|
RParen,
|
||||||
|
Newline,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Scanner<'s> {
|
||||||
|
pub src: &'s str,
|
||||||
|
pub chars: Peekable<Chars<'s>>,
|
||||||
|
pub pos: Position,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <'s> Scanner<'s> {
|
||||||
|
pub fn new(src: &'s str) -> Self {
|
||||||
|
Self {
|
||||||
|
src,
|
||||||
|
chars: src.chars().peekable(),
|
||||||
|
pos: Position { pos: 0, line: 1, col: 1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<char> {
|
||||||
|
match self.chars.next() {
|
||||||
|
Some('\n') => {
|
||||||
|
self.pos.pos += 1;
|
||||||
|
self.pos.line += 1;
|
||||||
|
self.pos.col = 1;
|
||||||
|
Some('\n')
|
||||||
|
},
|
||||||
|
Some(c) => {
|
||||||
|
self.pos.pos += 1;
|
||||||
|
self.pos.col += 1;
|
||||||
|
Some(c)
|
||||||
|
},
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peek(&mut self) -> Option<char> {
|
||||||
|
self.chars.peek().copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_number(&mut self, pos: u32) -> Token<'s> {
|
||||||
|
while matches!(self.peek(), Some('0'..='9')) {
|
||||||
|
self.next();
|
||||||
|
}
|
||||||
|
if matches!(self.peek(), Some('.')) {
|
||||||
|
self.next();
|
||||||
|
while matches!(self.peek(), Some('0'..='9')) {
|
||||||
|
self.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let s = &self.src[pos as usize..self.pos.pos as usize];
|
||||||
|
match s.parse() {
|
||||||
|
Ok(x) => Token::Number(x),
|
||||||
|
Err(_) => Token::Error,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_name(&mut self, pos: u32) -> Token<'s> {
|
||||||
|
while matches!(self.peek(), Some('a'..='z' | 'A'..='Z' | '0'..='9' | '_')) {
|
||||||
|
self.next();
|
||||||
|
}
|
||||||
|
let s = &self.src[pos as usize..self.pos.pos as usize];
|
||||||
|
Token::Name(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_token(&mut self) -> Option<(Position, Token<'s>)> {
|
||||||
|
while matches!(self.peek(), Some(' ' | '\t')) {
|
||||||
|
self.next();
|
||||||
|
}
|
||||||
|
self.peek()?;
|
||||||
|
let pos = self.pos;
|
||||||
|
let tok = match self.next().unwrap() {
|
||||||
|
'\n' => Token::Newline,
|
||||||
|
'+' => Token::Plus,
|
||||||
|
'-' => Token::Minus,
|
||||||
|
'*' => Token::Star,
|
||||||
|
'/' => Token::Slash,
|
||||||
|
'^' => Token::Caret,
|
||||||
|
'\'' => Token::Quote,
|
||||||
|
'=' => Token::Equal,
|
||||||
|
',' => Token::Comma,
|
||||||
|
'(' => Token::LParen,
|
||||||
|
')' => Token::RParen,
|
||||||
|
'0'..='9' => self.next_number(pos.pos),
|
||||||
|
'a'..='z' | 'A'..='Z' => self.next_name(pos.pos),
|
||||||
|
_ => Token::Error,
|
||||||
|
};
|
||||||
|
Some((pos, tok))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <'s> Iterator for Scanner<'s> {
|
||||||
|
type Item = (Position, Token<'s>);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.next_token()
|
||||||
|
}
|
||||||
|
}
|
26
src/main.rs
Normal file
26
src/main.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use language::compile;
|
||||||
|
use renderer::run;
|
||||||
|
use winit::{event_loop::EventLoop, window::Window};
|
||||||
|
|
||||||
|
mod language;
|
||||||
|
mod renderer;
|
||||||
|
mod complex;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
env_logger::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)
|
||||||
|
"#;
|
||||||
|
let wgsl = compile(src).unwrap();
|
||||||
|
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));
|
||||||
|
|
||||||
|
}
|
236
src/renderer/mod.rs
Normal file
236
src/renderer/mod.rs
Normal file
|
@ -0,0 +1,236 @@
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
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_buffer: wgpu::Buffer,
|
||||||
|
queue: wgpu::Queue,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
async fn new(window: &Window) -> Self {
|
||||||
|
let size = window.inner_size();
|
||||||
|
|
||||||
|
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::default());
|
||||||
|
let surface = unsafe { instance.create_surface(&window) }.unwrap();
|
||||||
|
|
||||||
|
let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions {
|
||||||
|
power_preference: wgpu::PowerPreference::default(),
|
||||||
|
compatible_surface: Some(&surface),
|
||||||
|
force_fallback_adapter: false,
|
||||||
|
}).await.unwrap();
|
||||||
|
|
||||||
|
let (device, queue) = adapter.request_device(
|
||||||
|
&wgpu::DeviceDescriptor {
|
||||||
|
label: None,
|
||||||
|
features: wgpu::Features::empty(),
|
||||||
|
limits: wgpu::Limits::default(),
|
||||||
|
},
|
||||||
|
None
|
||||||
|
).await.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,
|
||||||
|
alpha_mode: wgpu::CompositeAlphaMode::Auto,
|
||||||
|
view_formats: vec![format],
|
||||||
|
};
|
||||||
|
surface.configure(&device, &config);
|
||||||
|
|
||||||
|
// Uniforms //
|
||||||
|
|
||||||
|
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: None,
|
||||||
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
|
contents: &[0; Uniforms::SHADER_SIZE.get() as usize],
|
||||||
|
});
|
||||||
|
|
||||||
|
let uniform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
label: None,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Buffer {
|
||||||
|
ty: wgpu::BufferBindingType::Uniform,
|
||||||
|
has_dynamic_offset: false,
|
||||||
|
min_binding_size: None,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
binding: 1,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
label: None,
|
||||||
|
layout: &uniform_bind_group_layout,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
|
||||||
|
buffer: &uniform_buffer,
|
||||||
|
offset: 0,
|
||||||
|
size: Some(NonZeroU64::new(Uniforms::SHADER_SIZE.get()).unwrap()),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Done //
|
||||||
|
|
||||||
|
Self {
|
||||||
|
surface,
|
||||||
|
device,
|
||||||
|
config,
|
||||||
|
render_pipeline: None,
|
||||||
|
queue,
|
||||||
|
uniform_bind_group,
|
||||||
|
uniform_bind_group_layout,
|
||||||
|
uniform_buffer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_shaders(&mut self, userdefs: &str) {
|
||||||
|
// Shaders //
|
||||||
|
let src = include_str!("shader.wgsl");
|
||||||
|
let src = src.replace("//INCLUDE//\n", userdefs);
|
||||||
|
|
||||||
|
let shader = self.device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
|
label: None,
|
||||||
|
source: wgpu::ShaderSource::Wgsl(src.into())
|
||||||
|
});
|
||||||
|
|
||||||
|
let vertex = wgpu::VertexState {
|
||||||
|
module: &shader,
|
||||||
|
entry_point: "vs_main",
|
||||||
|
buffers: &[]
|
||||||
|
};
|
||||||
|
|
||||||
|
let fragment = wgpu::FragmentState {
|
||||||
|
module: &shader,
|
||||||
|
entry_point: "fs_main",
|
||||||
|
targets: &[Some(wgpu::ColorTargetState {
|
||||||
|
format: self.config.format,
|
||||||
|
blend: Some(wgpu::BlendState {
|
||||||
|
color: wgpu::BlendComponent::REPLACE,
|
||||||
|
alpha: wgpu::BlendComponent::REPLACE,
|
||||||
|
}),
|
||||||
|
write_mask: wgpu::ColorWrites::ALL,
|
||||||
|
})],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Pipeline //
|
||||||
|
|
||||||
|
let pipeline_layout = self.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
label: None,
|
||||||
|
bind_group_layouts: &[&self.uniform_bind_group_layout],
|
||||||
|
push_constant_ranges: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
let render_pipeline = self.device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
|
label: None,
|
||||||
|
layout: Some(&pipeline_layout),
|
||||||
|
vertex,
|
||||||
|
fragment: Some(fragment),
|
||||||
|
primitive: wgpu::PrimitiveState::default(),
|
||||||
|
depth_stencil: None,
|
||||||
|
multisample: wgpu::MultisampleState::default(),
|
||||||
|
multiview: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.render_pipeline = Some(render_pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn redraw(&self, uniforms: &Uniforms) {
|
||||||
|
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 });
|
||||||
|
{
|
||||||
|
let color_attachment = wgpu::RenderPassColorAttachment {
|
||||||
|
view: &view,
|
||||||
|
resolve_target: None,
|
||||||
|
ops: wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
|
||||||
|
store: true,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
label: None,
|
||||||
|
color_attachments: &[Some(color_attachment)],
|
||||||
|
depth_stencil_attachment: None,
|
||||||
|
});
|
||||||
|
if let Some(pipeline) = &self.render_pipeline {
|
||||||
|
rpass.set_pipeline(pipeline);
|
||||||
|
rpass.set_bind_group(0, &self.uniform_bind_group, &[]);
|
||||||
|
rpass.draw(0..3, 0..1);
|
||||||
|
rpass.draw(1..4, 0..1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut uniform_buffer = UniformBuffer::new([0; Uniforms::SHADER_SIZE.get() as usize]);
|
||||||
|
uniform_buffer.write(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;
|
||||||
|
self.surface.configure(&self.device, &self.config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
205
src/renderer/shader.wgsl
Normal file
205
src/renderer/shader.wgsl
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
////////////////
|
||||||
|
// uniforms //
|
||||||
|
////////////////
|
||||||
|
|
||||||
|
struct Uniforms {
|
||||||
|
resolution: vec2u,
|
||||||
|
bounds_min: vec2f,
|
||||||
|
bounds_max: vec2f,
|
||||||
|
shading_intensity: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(1) var<uniform> uniforms: Uniforms;
|
||||||
|
|
||||||
|
///////////////
|
||||||
|
// utility //
|
||||||
|
///////////////
|
||||||
|
|
||||||
|
fn remap(val: vec2f, a1: vec2f, b1: vec2f, a2: vec2f, b2: vec2f) -> vec2f {
|
||||||
|
return a2 + (b2 - a2) * ((val - a1) / (b1 - a1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////
|
||||||
|
// constants //
|
||||||
|
/////////////////
|
||||||
|
|
||||||
|
const TAU = 6.283185307179586;
|
||||||
|
const E = 2.718281828459045;
|
||||||
|
|
||||||
|
/////////////////////////
|
||||||
|
// complex functions //
|
||||||
|
/////////////////////////
|
||||||
|
|
||||||
|
fn c_re(z: vec2f) -> vec2f {
|
||||||
|
return vec2(z.x, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_im(z: vec2f) -> vec2f {
|
||||||
|
return vec2(z.y, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_conj(z: vec2f) -> vec2f {
|
||||||
|
return z * vec2(1.0, -1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_abs_sq(z: vec2f) -> vec2f {
|
||||||
|
return vec2(dot(z, z), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_abs(z: vec2f) -> vec2f {
|
||||||
|
return vec2(sqrt(dot(z, z)), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_arg(z: vec2f) -> vec2f {
|
||||||
|
return vec2(atan2(z.y, z.x), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_add(u: vec2f, v: vec2f) -> vec2f {
|
||||||
|
return u + v;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_sub(u: vec2f, v: vec2f) -> vec2f {
|
||||||
|
return u - v;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_mul(u: vec2f, v: vec2f) -> vec2f {
|
||||||
|
return vec2(u.x*v.x - u.y*v.y, u.y*v.x + u.x*v.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_div(u: vec2f, v: vec2f) -> vec2f {
|
||||||
|
return vec2(u.x*v.x + u.y*v.y, u.y*v.x - u.x*v.y) / dot(v, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_pos(v: vec2f) -> vec2f {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_neg(v: vec2f) -> vec2f {
|
||||||
|
return -v;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_recip(v: vec2f) -> vec2f {
|
||||||
|
return vec2(v.x, -v.y) / dot(v, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_exp(z: vec2f) -> vec2f {
|
||||||
|
return exp(z.x) * vec2(cos(z.y), sin(z.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_log(z: vec2f) -> vec2f {
|
||||||
|
return vec2(0.5 * log(dot(z, z)), atan2(z.y, z.x));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_pow(u: vec2f, v: vec2f) -> vec2f {
|
||||||
|
return c_exp(c_mul(c_log(u), v));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_sqrt(z: vec2f) -> vec2f {
|
||||||
|
return c_pow(z, vec2(0.5, 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_sin(z: vec2f) -> vec2f {
|
||||||
|
return vec2(sin(z.x)*cosh(z.y), cos(z.x)*sinh(z.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_cos(z: vec2f) -> vec2f {
|
||||||
|
return vec2(cos(z.x)*cosh(z.y), -sin(z.x)*sinh(z.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_tan(z: vec2f) -> vec2f {
|
||||||
|
return vec2(sin(2.0*z.x), sinh(2.0*z.y)) / (cos(2.0*z.x) + cosh(2.0*z.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_sinh(z: vec2f) -> vec2f {
|
||||||
|
return vec2(sinh(z.x)*cos(z.y), cosh(z.x)*sin(z.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_cosh(z: vec2f) -> vec2f {
|
||||||
|
return vec2(cosh(z.x)*cos(z.y), sinh(z.x)*sin(z.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_tanh(z: vec2f) -> vec2f {
|
||||||
|
return vec2(sinh(2.0*z.x), sin(2.0*z.y)) / (cosh(2.0*z.x) + cos(2.0*z.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_gamma(z: vec2f) -> vec2f {
|
||||||
|
let reflect = z.x < 0.5;
|
||||||
|
var zp = z;
|
||||||
|
if reflect {
|
||||||
|
zp = vec2(1.0, 0.0) - z;
|
||||||
|
}
|
||||||
|
var w = c_gamma_inner2(zp);
|
||||||
|
if reflect {
|
||||||
|
w = TAU * 0.5 * c_recip(c_mul(c_sin(TAU * 0.5 * z), w));
|
||||||
|
}
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Yang, ZH., Tian, JF. An accurate approximation formula for gamma function. J Inequal Appl 2018, 56 (2018).
|
||||||
|
// https://doi.org/10.1186/s13660-018-1646-6
|
||||||
|
fn c_gamma_inner(z: vec2f) -> vec2f {
|
||||||
|
let z2 = c_mul(z, z);
|
||||||
|
let z3 = c_mul(z2, z);
|
||||||
|
|
||||||
|
let a = c_sqrt(TAU * z);
|
||||||
|
let b = c_pow(1.0 / (E * E) * c_mul(z3, c_sinh(c_recip(z))), 0.5 * z);
|
||||||
|
let c = c_exp(7.0/324.0 * c_recip(c_mul(z3, 35.0 * z2 + 33.0)));
|
||||||
|
|
||||||
|
return c_mul(c_mul(a, b), c);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_gamma_inner2(z: vec2f) -> vec2f {
|
||||||
|
let w = c_gamma_inner(z + vec2(3.0, 0.0));
|
||||||
|
return c_div(w, c_mul(c_mul(z, z + vec2(1.0, 0.0)), c_mul(z + vec2(2.0, 0.0), z + vec2(3.0, 0.0))));
|
||||||
|
}
|
||||||
|
|
||||||
|
const D_EPS = 0.01;
|
||||||
|
|
||||||
|
/////////////////
|
||||||
|
// user code //
|
||||||
|
/////////////////
|
||||||
|
|
||||||
|
//INCLUDE//
|
||||||
|
|
||||||
|
//////////////
|
||||||
|
// vertex //
|
||||||
|
//////////////
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4f {
|
||||||
|
var pos = array<vec2f, 4>(
|
||||||
|
vec2(-1.0,-1.0),
|
||||||
|
vec2( 1.0,-1.0),
|
||||||
|
vec2(-1.0, 1.0),
|
||||||
|
vec2( 1.0, 1.0),
|
||||||
|
);
|
||||||
|
|
||||||
|
return vec4f(pos[in_vertex_index], 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////
|
||||||
|
// fragment //
|
||||||
|
////////////////
|
||||||
|
|
||||||
|
fn hsv2rgb(c: vec3f) -> vec3f {
|
||||||
|
let p = abs(fract(c.xxx + vec3f(1.0, 2.0/3.0, 1.0/3.0)) * 6.0 - vec3f(3.0));
|
||||||
|
return c.z * mix(vec3f(1.0), clamp(p - vec3f(1.0), vec3f(0.0), vec3f(1.0)), c.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shademap(r: f32) -> f32 {
|
||||||
|
return r*inverseSqrt(r * r + 0.0625 * uniforms.shading_intensity)*0.9875 + 0.0125;
|
||||||
|
}
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fs_main(@builtin(position) in: vec4f) -> @location(0) vec4f {
|
||||||
|
let pos = vec2(in.x, f32(uniforms.resolution.y) - in.y);
|
||||||
|
var z = remap(pos, vec2(0.0), vec2f(uniforms.resolution), uniforms.bounds_min, uniforms.bounds_max);
|
||||||
|
|
||||||
|
z = func_plot(z);
|
||||||
|
|
||||||
|
let r = sqrt(z.x*z.x + z.y*z.y);
|
||||||
|
let arg = atan2(z.y, z.x);
|
||||||
|
let hsv = vec3f(arg / TAU + 1.0, shademap(1.0/r), shademap(r));
|
||||||
|
let col = pow(hsv2rgb(hsv), vec3(2.0));
|
||||||
|
return vec4f(col, 1.0);
|
||||||
|
}
|
15
syntax.md
Normal file
15
syntax.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
```
|
||||||
|
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