commit
This commit is contained in:
parent
ee26b6133a
commit
3e9f05bbfd
11 changed files with 222 additions and 174 deletions
17
index.html
17
index.html
|
@ -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>
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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 })
|
||||
}
|
||||
_ => 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))
|
||||
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))
|
||||
};
|
||||
if self.scanner.peek().is_none() {
|
||||
return Err(ParseError::UnexpectedEof)
|
||||
Ok(Stmt::Store(name, expr))
|
||||
}
|
||||
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)
|
||||
_ => Ok(Stmt::Expr(expr))
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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}");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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| {
|
||||
|
|
28
style.css
28
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 {
|
||||
|
|
Loading…
Reference in a new issue