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>
|
<textarea id="srcinput"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div id="canvas_container" class="split right">
|
<div id="canvas_container" class="split right">
|
||||||
|
<!-- canvas -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import init, * as cxgraph from "./pkg/cxgraph.js";
|
import init, * as cxgraph from "./pkg/cxgraph.js";
|
||||||
await init();
|
await init();
|
||||||
await cxgraph.load_shader("plot(z) = z");
|
|
||||||
await cxgraph.redraw();
|
|
||||||
let canvas = document.getElementsByTagName("canvas")[0];
|
|
||||||
canvas.style.width = "100%";
|
canvas.style.width = "100%";
|
||||||
canvas.style.height = "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 () => {
|
new ResizeObserver(async () => {
|
||||||
//console.log("resize");
|
let width = canvas.clientWidth;
|
||||||
//let width = canvas.clientWidth;
|
let height = canvas.clientHeight;
|
||||||
//let height = canvas.clientHeight;
|
await cxgraph.resize(width*2, height*2);
|
||||||
//await cxgraph.resize(width, height);
|
await cxgraph.set_bounds(-5*width/height, -5, 5*width/height, 5);
|
||||||
//await cxgraph.redraw();
|
await cxgraph.redraw();
|
||||||
}).observe(canvas);
|
}).observe(canvas);
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::{collections::{HashMap, HashSet}, fmt::Write};
|
||||||
|
|
||||||
use crate::complex::{Complex, cxfn};
|
use crate::complex::{Complex, cxfn};
|
||||||
|
|
||||||
use super::parser::{Expr, Stmt, UnaryOp, BinaryOp};
|
use super::parser::{Expr, Stmt, UnaryOp, BinaryOp, Defn};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum CompileError<'s> {
|
pub enum CompileError<'s> {
|
||||||
|
@ -85,10 +85,10 @@ type LocalTable<'s> = HashSet<&'s str>;
|
||||||
|
|
||||||
type CompileResult<'s,T=()> = Result<T, CompileError<'s>>;
|
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);
|
let mut compiler = Compiler::new(buf);
|
||||||
for stmt in stmts {
|
for defn in defns {
|
||||||
compiler.compile_stmt(stmt)?;
|
compiler.compile_defn(defn)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -110,18 +110,16 @@ impl <'s, 'w, W: Write> Compiler<'s, 'w, W> {
|
||||||
// Statements //
|
// Statements //
|
||||||
//////////////////
|
//////////////////
|
||||||
|
|
||||||
fn compile_stmt(&mut self, stmt: &Stmt<'s>) -> CompileResult<'s> {
|
fn compile_defn(&mut self, defn: &Defn<'s>) -> CompileResult<'s> {
|
||||||
let res = match stmt {
|
let res = match defn {
|
||||||
Stmt::Const { name, body } => self.stmt_const(name, body),
|
Defn::Const { name, body } => self.defn_const(name, body),
|
||||||
Stmt::Func { name, args, body } => self.stmt_func(name, args, body),
|
Defn::Func { name, args, body } => self.defn_func(name, args, body),
|
||||||
Stmt::Deriv { name, func } => self.stmt_deriv(name, func),
|
|
||||||
Stmt::Iter { name, func, count } => self.stmt_iter(name, func, *count),
|
|
||||||
};
|
};
|
||||||
writeln!(self.buf)?;
|
writeln!(self.buf)?;
|
||||||
res
|
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() {
|
if self.name_info(name, None).is_some() {
|
||||||
return Err(CompileError::Reassignment(name))
|
return Err(CompileError::Reassignment(name))
|
||||||
}
|
}
|
||||||
|
@ -136,7 +134,7 @@ impl <'s, 'w, W: Write> Compiler<'s, 'w, W> {
|
||||||
Ok(())
|
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() {
|
if self.name_info(name, None).is_some() {
|
||||||
return Err(CompileError::Reassignment(name))
|
return Err(CompileError::Reassignment(name))
|
||||||
}
|
}
|
||||||
|
@ -150,7 +148,7 @@ impl <'s, 'w, W: Write> Compiler<'s, 'w, W> {
|
||||||
locals.insert(arg);
|
locals.insert(arg);
|
||||||
}
|
}
|
||||||
write!(self.buf, ")->vec2f{{return ")?;
|
write!(self.buf, ")->vec2f{{return ")?;
|
||||||
self.compile_expr(&locals, body)?;
|
self.compile_expr(&locals, &body.1)?;
|
||||||
write!(self.buf, ";}}")?;
|
write!(self.buf, ";}}")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ pub struct Position {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Error<'s> {
|
pub enum Error<'s> {
|
||||||
Parse(ParseError<'s>),
|
Parse(ParseError),
|
||||||
Compile(CompileError<'s>),
|
Compile(CompileError<'s>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,16 +27,19 @@ impl <'s> From<CompileError<'s>> for Error<'s> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'s> From<ParseError<'s>> for Error<'s> {
|
impl <'s> From<ParseError> for Error<'s> {
|
||||||
fn from(value: ParseError<'s>) -> Self {
|
fn from(value: ParseError) -> Self {
|
||||||
Self::Parse(value)
|
Self::Parse(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile(src: &str) -> Result<String, Error> {
|
pub fn compile(src: &str) -> Result<String, Error> {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
let stmts = parser::Parser::new(src).parse()?;
|
println!("parsing");
|
||||||
let stmts = optimize(stmts);
|
let defns = parser::Parser::new(src).parse()?;
|
||||||
compiler::compile(&mut buf, &stmts)?;
|
println!("optimizing");
|
||||||
|
let defns = optimize(defns);
|
||||||
|
println!("compiling");
|
||||||
|
compiler::compile(&mut buf, &defns)?;
|
||||||
Ok(buf)
|
Ok(buf)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::complex::{Complex, cxfn};
|
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 {
|
fn apply_unary(op: UnaryOp, arg: Complex) -> Complex {
|
||||||
match op {
|
match op {
|
||||||
|
@ -20,14 +20,29 @@ fn apply_binary(op: BinaryOp, u: Complex, v: Complex) -> Complex {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn optimize(stmts: Vec<Stmt>) -> Vec<Stmt> {
|
pub fn optimize(defns: Vec<Defn>) -> Vec<Defn> {
|
||||||
stmts.into_iter().map(|s| match s {
|
defns.into_iter().map(|s| match s {
|
||||||
Stmt::Const { name, body } => Stmt::Const { name, body: optimize_expr(body) },
|
Defn::Const { name, body } => Defn::Const { name, body: optimize_expr(body) },
|
||||||
Stmt::Func { name, args, body } => Stmt::Func { name, args, 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,
|
_ => s,
|
||||||
}).collect()
|
}).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> {
|
fn optimize_expr<'s>(e: Expr<'s>) -> Expr<'s> {
|
||||||
match e {
|
match e {
|
||||||
Expr::Unary(op, arg) => {
|
Expr::Unary(op, arg) => {
|
||||||
|
|
|
@ -5,14 +5,11 @@ use crate::complex::Complex;
|
||||||
use super::{scanner::{Scanner, Token}, Position};
|
use super::{scanner::{Scanner, Token}, Position};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum ParseError<'s> {
|
pub struct ParseError {
|
||||||
UnexpectedExpr(Position, Expr<'s>),
|
msg: String,
|
||||||
Unexpected(Position, Token<'s>),
|
pos: Option<Position>,
|
||||||
Expected(Position, Token<'s>),
|
|
||||||
InvalidLValue(Position),
|
|
||||||
InvalidFunction(Expr<'s>),
|
|
||||||
UnexpectedEof,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum BinaryOp {
|
pub enum BinaryOp {
|
||||||
Add, Sub, Mul, Div, Pow,
|
Add, Sub, Mul, Div, Pow,
|
||||||
|
@ -73,10 +70,14 @@ pub enum Expr<'s> {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Stmt<'s> {
|
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> },
|
Const { name: &'s str, body: Expr<'s> },
|
||||||
Func { name: &'s str, args: Vec<&'s str>, body: Expr<'s> },
|
Func { name: &'s str, args: Vec<&'s str>, body: (Vec<Stmt<'s>>, Expr<'s>) },
|
||||||
Deriv { name: &'s str, func: &'s str },
|
|
||||||
Iter { name: &'s str, func: &'s str, count: u32 }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Parser<'s> {
|
pub struct Parser<'s> {
|
||||||
|
@ -90,20 +91,46 @@ impl <'s> Parser<'s> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expect(&mut self, tok: Token<'s>) -> Result<Position, ParseError<'s>> {
|
fn next(&mut self) -> Result<(Position, Token<'s>), ParseError> {
|
||||||
match self.scanner.peek() {
|
match self.scanner.next() {
|
||||||
Some((p, t)) if *t == tok => {
|
Some(r) => Ok(r),
|
||||||
let p = *p;
|
None => Err(self.err_here("Unexpected EOF")),
|
||||||
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>> {
|
fn peek(&mut self) -> Result<(Position, Token<'s>), ParseError> {
|
||||||
let (pos, tok) = self.scanner.next().unwrap();
|
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 {
|
let mut expr = match tok {
|
||||||
Token::Number(n) => Expr::Number(Complex::from(n)),
|
Token::Number(n) => Expr::Number(Complex::from(n)),
|
||||||
Token::Name(n) => Expr::Name(n),
|
Token::Name(n) => Expr::Name(n),
|
||||||
|
@ -115,37 +142,38 @@ impl <'s> Parser<'s> {
|
||||||
tok => if let Some(op) = UnaryOp::from_token(tok) {
|
tok => if let Some(op) = UnaryOp::from_token(tok) {
|
||||||
Expr::Unary(op, Box::new(self.expr(op.precedence())?))
|
Expr::Unary(op, Box::new(self.expr(op.precedence())?))
|
||||||
} else {
|
} 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 {
|
expr = match tok {
|
||||||
Token::Equal | Token::Colon | Token::RParen | Token::Newline | Token::Comma => break,
|
|
||||||
Token::LParen => {
|
Token::LParen => {
|
||||||
self.scanner.next();
|
self.next()?;
|
||||||
let mut args = Vec::new();
|
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)?);
|
args.push(self.expr(0)?);
|
||||||
match self.scanner.peek() {
|
match self.peek()?.1 {
|
||||||
Some((_, Token::Comma)) => { self.scanner.next(); },
|
Token::Comma => { self.next()?; },
|
||||||
Some((_, Token::RParen)) => break,
|
Token::RParen => break,
|
||||||
Some((pos, _)) => return Err(ParseError::Expected(*pos, Token::RParen)),
|
_ => return Err(self.err_here(format!("Unexpected token {:?}, expected ',' or ')'", tok)))
|
||||||
None => return Err(ParseError::UnexpectedEof),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.expect(Token::RParen)?;
|
self.expect(Token::RParen)?;
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Name(name) => Expr::FnCall(name, args),
|
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();
|
let (lp, rp) = op.precedence();
|
||||||
if lp < min_prec {
|
if lp < min_prec {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
self.scanner.next();
|
self.next()?;
|
||||||
let rhs = self.expr(rp)?;
|
let rhs = self.expr(rp)?;
|
||||||
Expr::Binary(op, Box::new(expr), Box::new(rhs))
|
Expr::Binary(op, Box::new(expr), Box::new(rhs))
|
||||||
} else {
|
} else {
|
||||||
|
@ -162,98 +190,83 @@ impl <'s> Parser<'s> {
|
||||||
Ok(expr)
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_stmt_equals(&mut self, lhs_pos: Position, lhs: Expr<'s>) -> Result<Stmt<'s>, ParseError<'s>> {
|
fn stmt(&mut self) -> Result<Stmt<'s>, ParseError> {
|
||||||
if self.scanner.peek().is_none() {
|
let expr = self.expr(0)?;
|
||||||
return Err(ParseError::UnexpectedEof)
|
match self.peek()?.1 {
|
||||||
}
|
Token::Arrow => {
|
||||||
|
self.next()?;
|
||||||
let rhs = self.expr(0)?;
|
let name = match self.next()? {
|
||||||
|
(_, Token::Name(name)) => name,
|
||||||
if self.scanner.peek().is_some() {
|
(p, t) => return Err(self.err_at(format!("Unexpected token {t:?}, expected a name"), p))
|
||||||
self.expect(Token::Newline)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
match lhs {
|
|
||||||
Expr::Name(name) => Ok(Stmt::Const { name, body: rhs }),
|
|
||||||
Expr::FnCall(name, args) => {
|
|
||||||
let mut arg_names = Vec::with_capacity(args.len());
|
|
||||||
for arg in args {
|
|
||||||
if let Expr::Name(name) = arg {
|
|
||||||
arg_names.push(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Stmt::Func { name, args: arg_names, body: rhs })
|
|
||||||
}
|
|
||||||
_ => return Err(ParseError::InvalidLValue(lhs_pos))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_stmt_deriv(&mut self, lhs_pos: Position, lhs: Expr<'s>) -> Result<Stmt<'s>, ParseError<'s>> {
|
|
||||||
if self.scanner.peek().is_none() {
|
|
||||||
return Err(ParseError::UnexpectedEof)
|
|
||||||
}
|
|
||||||
let pos = self.scanner.peek().unwrap().0;
|
|
||||||
let f = self.expr(0)?;
|
|
||||||
match (lhs, f) {
|
|
||||||
(Expr::Name(name), Expr::Name(func)) => Ok(Stmt::Deriv { name, func }),
|
|
||||||
(Expr::Name(_), f) => Err(ParseError::UnexpectedExpr(pos, f)),
|
|
||||||
(lhs, _) => Err(ParseError::UnexpectedExpr(lhs_pos, lhs)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_stmt_iter(&mut self, lhs_pos: Position, lhs: Expr<'s>) -> Result<Stmt<'s>, ParseError<'s>> {
|
|
||||||
let Expr::Name(name) = lhs else {
|
|
||||||
return Err(ParseError::UnexpectedExpr(lhs_pos, lhs))
|
|
||||||
};
|
};
|
||||||
if self.scanner.peek().is_none() {
|
Ok(Stmt::Store(name, expr))
|
||||||
return Err(ParseError::UnexpectedEof)
|
|
||||||
}
|
}
|
||||||
let pos = self.scanner.peek().unwrap().0;
|
_ => Ok(Stmt::Expr(expr))
|
||||||
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();
|
let mut stmts = Vec::new();
|
||||||
while self.scanner.peek().is_some() {
|
loop {
|
||||||
while matches!(self.scanner.peek(), Some((_, Token::Newline))) {
|
stmts.push(self.stmt()?);
|
||||||
self.scanner.next();
|
if !matches!(self.peek(), Ok((_, Token::Comma))) {
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
self.next()?;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
Ok(stmts)
|
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,
|
Caret,
|
||||||
Equal,
|
Equal,
|
||||||
Comma,
|
Comma,
|
||||||
Colon,
|
Arrow,
|
||||||
LParen,
|
LParen,
|
||||||
RParen,
|
RParen,
|
||||||
Newline,
|
Newline,
|
||||||
|
@ -90,11 +90,13 @@ impl <'s> Scanner<'s> {
|
||||||
let tok = match self.next().unwrap() {
|
let tok = match self.next().unwrap() {
|
||||||
'\n' => Token::Newline,
|
'\n' => Token::Newline,
|
||||||
'+' => Token::Plus,
|
'+' => Token::Plus,
|
||||||
'-' => Token::Minus,
|
'-' => match self.peek().unwrap() {
|
||||||
|
'>' => { self.next(); Token::Arrow },
|
||||||
|
_ => Token::Minus,
|
||||||
|
}
|
||||||
'*' => Token::Star,
|
'*' => Token::Star,
|
||||||
'/' => Token::Slash,
|
'/' => Token::Slash,
|
||||||
'^' => Token::Caret,
|
'^' => Token::Caret,
|
||||||
':' => Token::Colon,
|
|
||||||
'=' => Token::Equal,
|
'=' => Token::Equal,
|
||||||
',' => Token::Comma,
|
',' => Token::Comma,
|
||||||
'(' => Token::LParen,
|
'(' => Token::LParen,
|
||||||
|
|
|
@ -8,9 +8,7 @@ fn main() {
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
let src = r#"
|
let src = r#"
|
||||||
f(z, c) = z^2 + c
|
plot(z) = z^2 -> w, z*w
|
||||||
g: iter f, 100
|
|
||||||
plot(z) = g(z, z)
|
|
||||||
"#;
|
"#;
|
||||||
let wgsl = compile(src).unwrap();
|
let wgsl = compile(src).unwrap();
|
||||||
println!("{wgsl}");
|
println!("{wgsl}");
|
||||||
|
|
|
@ -207,7 +207,7 @@ impl WgpuState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resize(&mut self, size: (u32, u32)) {
|
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.width = size.0;
|
||||||
self.config.height = size.1;
|
self.config.height = size.1;
|
||||||
self.surface.configure(&self.device, &self.config);
|
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;
|
return r*inverseSqrt(r * r + 0.0625 * uniforms.shading_intensity)*0.9875 + 0.0125;
|
||||||
}
|
}
|
||||||
|
|
||||||
@fragment
|
fn colorfor(w: vec2f) -> vec3f {
|
||||||
fn fs_main(@builtin(position) in: vec4f) -> @location(0) vec4f {
|
let z = func_plot(w);
|
||||||
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 r = sqrt(z.x*z.x + z.y*z.y);
|
||||||
let arg = atan2(z.y, z.x);
|
let arg = atan2(z.y, z.x);
|
||||||
let hsv = vec3f(arg / TAU + 1.0, shademap(1.0/r), shademap(r));
|
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);
|
return vec4f(col, 1.0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ pub async fn start() {
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new();
|
||||||
let window = Window::new(&event_loop).unwrap();
|
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()
|
web_sys::window()
|
||||||
.and_then(|win| win.document())
|
.and_then(|win| win.document())
|
||||||
.and_then(|doc| {
|
.and_then(|doc| {
|
||||||
|
|
28
style.css
28
style.css
|
@ -4,32 +4,44 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
height: 100%;
|
min-height: 100vh;
|
||||||
|
max-height: 100vh;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
--header-height: 50px;
|
||||||
|
--sidebar-width: 500px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#main {
|
#main {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
display: flex;
|
max-height: 100vh;
|
||||||
flex-direction: column;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#header {
|
#header {
|
||||||
height: 50px;
|
height: var(--header-height);
|
||||||
}
|
}
|
||||||
|
|
||||||
#content {
|
#content {
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
|
height: calc(100vh - var(--header-height));
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar {
|
#sidebar {
|
||||||
flex: 1;
|
width: var(--sidebar-width);
|
||||||
|
height: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
#canvas_container {
|
#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 {
|
canvas {
|
||||||
|
|
Loading…
Reference in a new issue