This commit is contained in:
TriMill 2023-05-30 21:46:04 -04:00
parent ee26b6133a
commit 3e9f05bbfd
11 changed files with 222 additions and 174 deletions

View file

@ -16,24 +16,25 @@
<textarea id="srcinput"></textarea>
</div>
<div id="canvas_container" class="split right">
<!-- canvas -->
</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%";
await cxgraph.load_shader("f(z) = z^8 + 15z^4 - 16\nfp(z) = 8z^7 + 60z^3\nn(z) = z - f(z)/fp(z)\nni: iter n, 60\nplot(z) = f(z)");
await cxgraph.redraw();
let canvas = document.getElementsByTagName("canvas")[0];
new ResizeObserver(async () => {
//console.log("resize");
//let width = canvas.clientWidth;
//let height = canvas.clientHeight;
//await cxgraph.resize(width, height);
//await cxgraph.redraw();
let width = canvas.clientWidth;
let height = canvas.clientHeight;
await cxgraph.resize(width*2, height*2);
await cxgraph.set_bounds(-5*width/height, -5, 5*width/height, 5);
await cxgraph.redraw();
}).observe(canvas);
</script>
</body>

View file

@ -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<T, CompileError<'s>>;
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<Stmt<'s>>, 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(())
}

View file

@ -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<CompileError<'s>> for Error<'s> {
}
}
impl <'s> From<ParseError<'s>> for Error<'s> {
fn from(value: ParseError<'s>) -> Self {
impl <'s> From<ParseError> for Error<'s> {
fn from(value: ParseError) -> 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)?;
println!("parsing");
let defns = parser::Parser::new(src).parse()?;
println!("optimizing");
let defns = optimize(defns);
println!("compiling");
compiler::compile(&mut buf, &defns)?;
Ok(buf)
}

View file

@ -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<Stmt>) -> Vec<Stmt> {
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<Defn>) -> Vec<Defn> {
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) => {

View file

@ -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<Position>,
}
#[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<Stmt<'s>>),
}
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<Stmt<'s>>, Expr<'s>) },
}
pub struct Parser<'s> {
@ -90,20 +91,46 @@ impl <'s> Parser<'s> {
}
}
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 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<Expr<'s>, 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<Position, ParseError> {
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<S>(&mut self, msg: S) -> ParseError
where S: Into<String> {
ParseError { msg: msg.into(), pos: self.peek().map(|(p,_)| p).ok() }
}
fn err_at<S>(&mut self, msg: S, pos: Position) -> ParseError
where S: Into<String> {
ParseError { msg: msg.into(), pos: Some(pos) }
}
fn expr(&mut self, min_prec: u32) -> Result<Expr<'s>, 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<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 })
fn stmt(&mut self) -> Result<Stmt<'s>, 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<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>> {
fn stmts(&mut self) -> Result<Vec<Stmt<'s>>, 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<Vec<Defn<'s>>, 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::<Result<Vec<&str>, 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)
}

View file

@ -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,

View file

@ -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}");

View file

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

View file

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

View file

@ -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| {

View file

@ -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 {