diff --git a/index.html b/index.html index 521da46..ec3e401 100644 --- a/index.html +++ b/index.html @@ -16,24 +16,25 @@
+
diff --git a/src/language/compiler.rs b/src/language/compiler.rs index c854fff..fc5e349 100644 --- a/src/language/compiler.rs +++ b/src/language/compiler.rs @@ -2,7 +2,7 @@ use std::{collections::{HashMap, HashSet}, fmt::Write}; use crate::complex::{Complex, cxfn}; -use super::parser::{Expr, Stmt, UnaryOp, BinaryOp}; +use super::parser::{Expr, Stmt, UnaryOp, BinaryOp, Defn}; #[derive(Clone, Debug)] pub enum CompileError<'s> { @@ -85,10 +85,10 @@ type LocalTable<'s> = HashSet<&'s str>; type CompileResult<'s,T=()> = Result>; -pub fn compile<'s>(buf: &mut impl Write, stmts: &[Stmt<'s>]) -> CompileResult<'s> { +pub fn compile<'s>(buf: &mut impl Write, defns: &[Defn<'s>]) -> CompileResult<'s> { let mut compiler = Compiler::new(buf); - for stmt in stmts { - compiler.compile_stmt(stmt)?; + for defn in defns { + compiler.compile_defn(defn)?; } Ok(()) } @@ -110,18 +110,16 @@ impl <'s, 'w, W: Write> Compiler<'s, 'w, W> { // Statements // ////////////////// - fn compile_stmt(&mut self, stmt: &Stmt<'s>) -> CompileResult<'s> { - 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), + fn compile_defn(&mut self, defn: &Defn<'s>) -> CompileResult<'s> { + let res = match defn { + Defn::Const { name, body } => self.defn_const(name, body), + Defn::Func { name, args, body } => self.defn_func(name, args, body), }; writeln!(self.buf)?; res } - fn stmt_const(&mut self, name: &'s str, body: &Expr<'s>) -> CompileResult<'s> { + fn defn_const(&mut self, name: &'s str, body: &Expr<'s>) -> CompileResult<'s> { if self.name_info(name, None).is_some() { return Err(CompileError::Reassignment(name)) } @@ -136,7 +134,7 @@ impl <'s, 'w, W: Write> Compiler<'s, 'w, W> { Ok(()) } - fn stmt_func(&mut self, name: &'s str, args: &[&'s str], body: &Expr<'s>) -> CompileResult<'s> { + fn defn_func(&mut self, name: &'s str, args: &[&'s str], body: &(Vec>, Expr<'s>)) -> CompileResult<'s> { if self.name_info(name, None).is_some() { return Err(CompileError::Reassignment(name)) } @@ -150,7 +148,7 @@ impl <'s, 'w, W: Write> Compiler<'s, 'w, W> { locals.insert(arg); } write!(self.buf, ")->vec2f{{return ")?; - self.compile_expr(&locals, body)?; + self.compile_expr(&locals, &body.1)?; write!(self.buf, ";}}")?; Ok(()) } diff --git a/src/language/mod.rs b/src/language/mod.rs index 66f36bb..781f8e1 100644 --- a/src/language/mod.rs +++ b/src/language/mod.rs @@ -17,7 +17,7 @@ pub struct Position { #[derive(Clone, Debug)] pub enum Error<'s> { - Parse(ParseError<'s>), + Parse(ParseError), Compile(CompileError<'s>), } @@ -27,16 +27,19 @@ impl <'s> From> for Error<'s> { } } -impl <'s> From> for Error<'s> { - fn from(value: ParseError<'s>) -> Self { +impl <'s> From for Error<'s> { + fn from(value: ParseError) -> Self { Self::Parse(value) } } pub fn compile(src: &str) -> Result { let mut buf = String::new(); - let stmts = parser::Parser::new(src).parse()?; - let stmts = optimize(stmts); - compiler::compile(&mut buf, &stmts)?; + println!("parsing"); + let defns = parser::Parser::new(src).parse()?; + println!("optimizing"); + let defns = optimize(defns); + println!("compiling"); + compiler::compile(&mut buf, &defns)?; Ok(buf) } diff --git a/src/language/optimizer.rs b/src/language/optimizer.rs index cf40aa4..0c88cba 100644 --- a/src/language/optimizer.rs +++ b/src/language/optimizer.rs @@ -1,6 +1,6 @@ use crate::complex::{Complex, cxfn}; -use super::{parser::{Expr, UnaryOp, BinaryOp, Stmt}, compiler::{BUILTINS, Type}}; +use super::{parser::{Expr, UnaryOp, BinaryOp, Stmt, Defn}, compiler::{BUILTINS, Type}}; fn apply_unary(op: UnaryOp, arg: Complex) -> Complex { match op { @@ -20,14 +20,29 @@ fn apply_binary(op: BinaryOp, u: Complex, v: Complex) -> Complex { } } -pub fn optimize(stmts: Vec) -> Vec { - stmts.into_iter().map(|s| match s { - Stmt::Const { name, body } => Stmt::Const { name, body: optimize_expr(body) }, - Stmt::Func { name, args, body } => Stmt::Func { name, args, body: optimize_expr(body) }, +pub fn optimize(defns: Vec) -> Vec { + defns.into_iter().map(|s| match s { + Defn::Const { name, body } => Defn::Const { name, body: optimize_expr(body) }, + Defn::Func { name, args, body } => { + let stmts = body.0.into_iter() + .map(optimize_stmt).collect(); + let expr = optimize_expr(body.1); + Defn::Func { name, args, body: (stmts, expr) } + } _ => s, }).collect() } +fn optimize_stmt(stmt: Stmt) -> Stmt { + match stmt { + Stmt::Expr(e) => Stmt::Expr(optimize_expr(e)), + Stmt::Store(v, e) => Stmt::Store(v, optimize_expr(e)), + Stmt::Iter(v, min, max, stmts) => Stmt::Iter(v, min, max, + stmts.into_iter().map(optimize_stmt).collect() + ) + } +} + fn optimize_expr<'s>(e: Expr<'s>) -> Expr<'s> { match e { Expr::Unary(op, arg) => { diff --git a/src/language/parser.rs b/src/language/parser.rs index f507ea5..4594f7e 100644 --- a/src/language/parser.rs +++ b/src/language/parser.rs @@ -5,14 +5,11 @@ use crate::complex::Complex; use super::{scanner::{Scanner, Token}, Position}; #[derive(Clone, Debug)] -pub enum ParseError<'s> { - UnexpectedExpr(Position, Expr<'s>), - Unexpected(Position, Token<'s>), - Expected(Position, Token<'s>), - InvalidLValue(Position), - InvalidFunction(Expr<'s>), - UnexpectedEof, +pub struct ParseError { + msg: String, + pos: Option, } + #[derive(Debug, Clone, Copy)] pub enum BinaryOp { Add, Sub, Mul, Div, Pow, @@ -73,10 +70,14 @@ pub enum Expr<'s> { #[derive(Debug)] pub enum Stmt<'s> { + Expr(Expr<'s>), + Store(&'s str, Expr<'s>), + Iter(&'s str, u32, u32, Vec>), +} + +pub enum Defn<'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 } + Func { name: &'s str, args: Vec<&'s str>, body: (Vec>, Expr<'s>) }, } pub struct Parser<'s> { @@ -90,20 +91,46 @@ impl <'s> Parser<'s> { } } - fn expect(&mut self, tok: Token<'s>) -> Result> { - 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 next(&mut self) -> Result<(Position, Token<'s>), ParseError> { + match self.scanner.next() { + Some(r) => Ok(r), + None => Err(self.err_here("Unexpected EOF")), } } - fn expr(&mut self, min_prec: u32) -> Result, ParseError<'s>> { - let (pos, tok) = self.scanner.next().unwrap(); + fn peek(&mut self) -> Result<(Position, Token<'s>), ParseError> { + match self.scanner.peek() { + Some(r) => Ok(*r), + None => Err(self.err_here("Unexpected EOF")), + } + } + + fn at_end(&mut self) -> bool { + self.scanner.peek().is_none() + } + + fn expect(&mut self, tok: Token<'s>) -> Result { + match self.peek()? { + (p, t) if t == tok => { + self.next()?; + Ok(p) + }, + (p, t) => Err(self.err_at(format!("Unexpected token {t:?}, expected {tok:?}"), p)), + } + } + + fn err_here(&mut self, msg: S) -> ParseError + where S: Into { + ParseError { msg: msg.into(), pos: self.peek().map(|(p,_)| p).ok() } + } + + fn err_at(&mut self, msg: S, pos: Position) -> ParseError + where S: Into { + ParseError { msg: msg.into(), pos: Some(pos) } + } + + fn expr(&mut self, min_prec: u32) -> Result, ParseError> { + let (pos, tok) = self.next()?; let mut expr = match tok { Token::Number(n) => Expr::Number(Complex::from(n)), Token::Name(n) => Expr::Name(n), @@ -115,37 +142,38 @@ 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::Unexpected(pos, tok)) + return Err(self.err_at(format!("Unexpected token {:?}", tok), pos)) } }; - while let Some((_, tok)) = self.scanner.peek() { + while let Ok((_, tok)) = self.peek() { + if is_closing(&tok) { + break; + } expr = match tok { - Token::Equal | Token::Colon | Token::RParen | Token::Newline | Token::Comma => break, Token::LParen => { - self.scanner.next(); + self.next()?; let mut args = Vec::new(); - while !matches!(self.scanner.peek(), None | Some((_, Token::RParen))) { + while !matches!(self.peek(), Err(_) | Ok((_, 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), + match self.peek()?.1 { + Token::Comma => { self.next()?; }, + Token::RParen => break, + _ => return Err(self.err_here(format!("Unexpected token {:?}, expected ',' or ')'", tok))) } } self.expect(Token::RParen)?; match expr { Expr::Name(name) => Expr::FnCall(name, args), - _ => return Err(ParseError::InvalidFunction(expr)), + _ => return Err(self.err_here("Cannot call this expression")) } }, - tok => if let Some(op) = BinaryOp::from_token(*tok) { + tok => if let Some(op) = BinaryOp::from_token(tok) { let (lp, rp) = op.precedence(); if lp < min_prec { break; } - self.scanner.next(); + self.next()?; let rhs = self.expr(rp)?; Expr::Binary(op, Box::new(expr), Box::new(rhs)) } else { @@ -162,98 +190,83 @@ impl <'s> Parser<'s> { Ok(expr) } - pub fn parse_stmt_equals(&mut self, lhs_pos: Position, lhs: Expr<'s>) -> Result, 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 }) + fn stmt(&mut self) -> Result, ParseError> { + let expr = self.expr(0)?; + match self.peek()?.1 { + Token::Arrow => { + self.next()?; + let name = match self.next()? { + (_, Token::Name(name)) => name, + (p, t) => return Err(self.err_at(format!("Unexpected token {t:?}, expected a name"), p)) + }; + Ok(Stmt::Store(name, expr)) } - _ => return Err(ParseError::InvalidLValue(lhs_pos)) + _ => Ok(Stmt::Expr(expr)) } } - pub fn parse_stmt_deriv(&mut self, lhs_pos: Position, lhs: Expr<'s>) -> Result, 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, 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>, ParseError<'s>> { + fn stmts(&mut self) -> Result>, ParseError> { let mut stmts = Vec::new(); - while self.scanner.peek().is_some() { - while matches!(self.scanner.peek(), Some((_, Token::Newline))) { - self.scanner.next(); + loop { + stmts.push(self.stmt()?); + if !matches!(self.peek(), Ok((_, Token::Comma))) { + break } - - if self.scanner.peek().is_none() { - break; - } - - let lhs_pos = self.scanner.peek().unwrap().0; - let lhs = self.expr(0)?; - - 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), - } - Some((pos, tok)) => return Err(ParseError::Unexpected(pos, tok)), - None => return Err(ParseError::UnexpectedEof), - }; - - stmts.push(stmt); + self.next()?; } Ok(stmts) } + + pub fn parse(&mut self) -> Result>, ParseError> { + println!("parse"); + let mut defns = Vec::new(); + while self.peek().is_ok() { + println!("parse loop"); + while matches!(self.peek(), Ok((_, Token::Newline))) { + self.next()?; + } + + if self.peek().is_err() { + break; + } + + let lhspos = self.peek()?.0; + let lhs = self.expr(0)?; + + self.expect(Token::Equal)?; + + let defn = match lhs { + Expr::Name(name) => { + let rhs = self.expr(0)?; + Defn::Const { name, body: rhs } + }, + Expr::FnCall(name, args) => { + let mut rhs = self.stmts()?; + let last = rhs.pop().ok_or(self.err_here("Empty function body"))?; + let Stmt::Expr(last) = last else { + return Err(self.err_here("Last statement in function body must be a plain expression")) + }; + let args = args.iter() + .map(|a| match a { + Expr::Name(n) => Ok(*n), + _ => Err(self.err_at("Invalid function declaration", lhspos)) + }).collect::, ParseError>>()?; + Defn::Func { name, args, body: (rhs, last) } + }, + _ => return Err(self.err_at("Invalid lvalue, expected a name or function call", lhspos)), + }; + + defns.push(defn); + + if self.at_end() { + break + } + self.expect(Token::Newline)?; + } + Ok(defns) + } +} + +fn is_closing(tok: &Token) -> bool { + matches!(tok, Token::Equal | Token::RParen | Token::Newline | Token::Comma | Token::Arrow) } diff --git a/src/language/scanner.rs b/src/language/scanner.rs index 907e3d7..819907e 100644 --- a/src/language/scanner.rs +++ b/src/language/scanner.rs @@ -14,7 +14,7 @@ pub enum Token<'s> { Caret, Equal, Comma, - Colon, + Arrow, LParen, RParen, Newline, @@ -90,11 +90,13 @@ impl <'s> Scanner<'s> { let tok = match self.next().unwrap() { '\n' => Token::Newline, '+' => Token::Plus, - '-' => Token::Minus, + '-' => match self.peek().unwrap() { + '>' => { self.next(); Token::Arrow }, + _ => Token::Minus, + } '*' => Token::Star, '/' => Token::Slash, '^' => Token::Caret, - ':' => Token::Colon, '=' => Token::Equal, ',' => Token::Comma, '(' => Token::LParen, diff --git a/src/main.rs b/src/main.rs index f555acd..8ff41fd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,9 +8,7 @@ fn main() { .init(); let src = r#" - f(z, c) = z^2 + c - g: iter f, 100 - plot(z) = g(z, z) + plot(z) = z^2 -> w, z*w "#; let wgsl = compile(src).unwrap(); println!("{wgsl}"); diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index ce9970a..755fbe8 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -207,7 +207,7 @@ impl WgpuState { } pub fn resize(&mut self, size: (u32, u32)) { - let size = (size.0.max(1).min(2048), size.1.max(1).min(2048)); + let size = (size.0.max(1).min(8192), size.1.max(1).min(8192)); self.config.width = size.0; self.config.height = size.1; self.surface.configure(&self.device, &self.config); diff --git a/src/renderer/shader.wgsl b/src/renderer/shader.wgsl index a07e5c1..06229fd 100644 --- a/src/renderer/shader.wgsl +++ b/src/renderer/shader.wgsl @@ -190,16 +190,22 @@ 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); +fn colorfor(w: vec2f) -> vec3f { + let z = func_plot(w); 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 pow(hsv2rgb(hsv), vec3(2.0)); +} + +@fragment +fn fs_main(@builtin(position) in: vec4f) -> @location(0) vec4f { + let pos = vec2(in.x, f32(uniforms.resolution.y) - in.y); + let z = remap(pos, vec2(0.0), vec2f(uniforms.resolution), uniforms.bounds_min, uniforms.bounds_max); + //let dz = z - remap(pos + vec2f(1.0), vec2(0.0), vec2f(uniforms.resolution), uniforms.bounds_min, uniforms.bounds_max); + + let col = colorfor(z); + return vec4f(col, 1.0); } diff --git a/src/wasm.rs b/src/wasm.rs index da498f5..7392763 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -26,7 +26,7 @@ pub async fn start() { let event_loop = EventLoop::new(); let window = Window::new(&event_loop).unwrap(); - window.set_inner_size(PhysicalSize::new(1200, 1200)); + window.set_inner_size(PhysicalSize::new(100, 100)); web_sys::window() .and_then(|win| win.document()) .and_then(|doc| { diff --git a/style.css b/style.css index ae0e2cb..42953c9 100644 --- a/style.css +++ b/style.css @@ -4,32 +4,44 @@ } body { - height: 100%; + min-height: 100vh; + max-height: 100vh; overflow: hidden; + --header-height: 50px; + --sidebar-width: 500px; } #main { min-height: 100vh; - display: flex; - flex-direction: column; + max-height: 100vh; } #header { - height: 50px; + height: var(--header-height); } #content { - display: flex; - flex: 1; width: 100vw; + height: calc(100vh - var(--header-height)); + display: flex; } #sidebar { - flex: 1; + width: var(--sidebar-width); + height: inherit; } #canvas_container { - flex: 3; + width: calc(100vw - var(--sidebar-width)); + height: inherit; +} + +#sidebar textarea { + width: calc(100% - 24px); + margin-left: 10px; + height: 99%; + resize: none; + font-size: 16px; } canvas {