Compare commits
No commits in common. "b52c1b26c9aa6c77d901196d2eee7125d5955d6f" and "bc2df73487ea13d588313023fa3b979a8df73d03" have entirely different histories.
b52c1b26c9
...
bc2df73487
8 changed files with 123 additions and 163 deletions
|
|
@ -6,7 +6,7 @@ use core::{alloc::Layout, mem, ptr, slice};
|
||||||
|
|
||||||
use alloc::{boxed::Box, string::String, vec::Vec};
|
use alloc::{boxed::Box, string::String, vec::Vec};
|
||||||
use haku::{
|
use haku::{
|
||||||
ast::{self, Ast},
|
ast::Ast,
|
||||||
bytecode::{Chunk, Defs, DefsImage, DefsLimits},
|
bytecode::{Chunk, Defs, DefsImage, DefsLimits},
|
||||||
compiler::{compile_expr, ClosureSpec, CompileError, Compiler, Source},
|
compiler::{compile_expr, ClosureSpec, CompileError, Compiler, Source},
|
||||||
diagnostic::Diagnostic,
|
diagnostic::Diagnostic,
|
||||||
|
|
@ -48,6 +48,12 @@ struct Limits {
|
||||||
max_parser_events: usize,
|
max_parser_events: usize,
|
||||||
ast_capacity: usize,
|
ast_capacity: usize,
|
||||||
chunk_capacity: usize,
|
chunk_capacity: usize,
|
||||||
|
stack_capacity: usize,
|
||||||
|
call_stack_capacity: usize,
|
||||||
|
ref_capacity: usize,
|
||||||
|
fuel: usize,
|
||||||
|
memory: usize,
|
||||||
|
render_max_depth: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Limits {
|
impl Default for Limits {
|
||||||
|
|
@ -61,6 +67,12 @@ impl Default for Limits {
|
||||||
max_parser_events: 1024,
|
max_parser_events: 1024,
|
||||||
ast_capacity: 1024,
|
ast_capacity: 1024,
|
||||||
chunk_capacity: 65536,
|
chunk_capacity: 65536,
|
||||||
|
stack_capacity: 1024,
|
||||||
|
call_stack_capacity: 256,
|
||||||
|
ref_capacity: 2048,
|
||||||
|
fuel: 65536,
|
||||||
|
memory: 1024 * 1024,
|
||||||
|
render_max_depth: 256,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -100,6 +112,12 @@ limit_setter!(max_tokens);
|
||||||
limit_setter!(max_parser_events);
|
limit_setter!(max_parser_events);
|
||||||
limit_setter!(ast_capacity);
|
limit_setter!(ast_capacity);
|
||||||
limit_setter!(chunk_capacity);
|
limit_setter!(chunk_capacity);
|
||||||
|
limit_setter!(stack_capacity);
|
||||||
|
limit_setter!(call_stack_capacity);
|
||||||
|
limit_setter!(ref_capacity);
|
||||||
|
limit_setter!(fuel);
|
||||||
|
limit_setter!(memory);
|
||||||
|
limit_setter!(render_max_depth);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Instance {
|
struct Instance {
|
||||||
|
|
@ -478,24 +496,22 @@ unsafe extern "C" fn haku_num_diagnostics(instance: *const Instance) -> u32 {
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn haku_diagnostic_start(instance: *const Instance, index: u32) -> u32 {
|
unsafe extern "C" fn haku_diagnostic_start(instance: *const Instance, index: u32) -> u32 {
|
||||||
(&(*instance).diagnostics2)[index as usize].span().start
|
(*instance).diagnostics2[index as usize].span().start
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn haku_diagnostic_end(instance: *const Instance, index: u32) -> u32 {
|
unsafe extern "C" fn haku_diagnostic_end(instance: *const Instance, index: u32) -> u32 {
|
||||||
(&(*instance).diagnostics2)[index as usize].span().end
|
(*instance).diagnostics2[index as usize].span().end
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn haku_diagnostic_message(instance: *const Instance, index: u32) -> *const u8 {
|
unsafe extern "C" fn haku_diagnostic_message(instance: *const Instance, index: u32) -> *const u8 {
|
||||||
(&(*instance).diagnostics2)[index as usize]
|
(*instance).diagnostics2[index as usize].message().as_ptr()
|
||||||
.message()
|
|
||||||
.as_ptr()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn haku_diagnostic_message_len(instance: *const Instance, index: u32) -> u32 {
|
unsafe extern "C" fn haku_diagnostic_message_len(instance: *const Instance, index: u32) -> u32 {
|
||||||
(&(*instance).diagnostics2)[index as usize].message().len() as u32
|
(*instance).diagnostics2[index as usize].message().len() as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use alloc::vec::Vec;
|
||||||
use crate::{
|
use crate::{
|
||||||
diagnostic::Diagnostic,
|
diagnostic::Diagnostic,
|
||||||
source::{SourceCode, Span},
|
source::{SourceCode, Span},
|
||||||
token::{Lexis, Spaces, TokenAllocError, TokenKind},
|
token::{Lexis, TokenAllocError, TokenKind},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Lexer<'a> {
|
pub struct Lexer<'a> {
|
||||||
|
|
@ -57,7 +57,7 @@ fn one_or_two(l: &mut Lexer<'_>, kind1: TokenKind, c2: char, kind2: TokenKind) -
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_ident_char(c: char) -> bool {
|
fn is_ident_char(c: char) -> bool {
|
||||||
matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '\'' | '?')
|
matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ident(l: &mut Lexer<'_>) -> TokenKind {
|
fn ident(l: &mut Lexer<'_>) -> TokenKind {
|
||||||
|
|
@ -132,8 +132,7 @@ fn color(l: &mut Lexer<'_>) -> TokenKind {
|
||||||
TokenKind::Color
|
TokenKind::Color
|
||||||
}
|
}
|
||||||
|
|
||||||
fn whitespace_and_comments(l: &mut Lexer<'_>) -> bool {
|
fn whitespace_and_comments(l: &mut Lexer<'_>) {
|
||||||
let mut matched = false;
|
|
||||||
loop {
|
loop {
|
||||||
match l.current() {
|
match l.current() {
|
||||||
'-' => {
|
'-' => {
|
||||||
|
|
@ -143,7 +142,6 @@ fn whitespace_and_comments(l: &mut Lexer<'_>) -> bool {
|
||||||
while l.current() != '\n' && l.current() != '\0' {
|
while l.current() != '\n' && l.current() != '\0' {
|
||||||
l.advance();
|
l.advance();
|
||||||
}
|
}
|
||||||
matched = true;
|
|
||||||
} else {
|
} else {
|
||||||
// An unfortunate little bit of backtracking here;
|
// An unfortunate little bit of backtracking here;
|
||||||
// This seems like the simplest possible solution though.
|
// This seems like the simplest possible solution though.
|
||||||
|
|
@ -155,18 +153,14 @@ fn whitespace_and_comments(l: &mut Lexer<'_>) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
' ' | '\r' | '\t' => {
|
' ' | '\r' | '\t' => l.advance(),
|
||||||
l.advance();
|
|
||||||
matched = true
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => break,
|
_ => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
matched
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn newline(l: &mut Lexer<'_>, has_left_space: bool) -> (TokenKind, Span, bool) {
|
fn newline(l: &mut Lexer<'_>) -> (TokenKind, Span) {
|
||||||
let start = l.position;
|
let start = l.position;
|
||||||
l.advance(); // skip the initial newline
|
l.advance(); // skip the initial newline
|
||||||
let end = l.position;
|
let end = l.position;
|
||||||
|
|
@ -183,11 +177,11 @@ fn newline(l: &mut Lexer<'_>, has_left_space: bool) -> (TokenKind, Span, bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(TokenKind::Newline, Span::new(start, end), has_left_space)
|
(TokenKind::Newline, Span::new(start, end))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn token(l: &mut Lexer<'_>) -> (TokenKind, Span, bool) {
|
fn token(l: &mut Lexer<'_>) -> (TokenKind, Span) {
|
||||||
let has_left_space = whitespace_and_comments(l);
|
whitespace_and_comments(l);
|
||||||
|
|
||||||
let start = l.position;
|
let start = l.position;
|
||||||
let kind = match l.current() {
|
let kind = match l.current() {
|
||||||
|
|
@ -209,7 +203,7 @@ fn token(l: &mut Lexer<'_>) -> (TokenKind, Span, bool) {
|
||||||
'<' => one_or_two(l, TokenKind::Less, '=', TokenKind::LessEqual),
|
'<' => one_or_two(l, TokenKind::Less, '=', TokenKind::LessEqual),
|
||||||
'>' => one_or_two(l, TokenKind::Greater, '=', TokenKind::GreaterEqual),
|
'>' => one_or_two(l, TokenKind::Greater, '=', TokenKind::GreaterEqual),
|
||||||
|
|
||||||
'\n' => return newline(l, has_left_space),
|
'\n' => return newline(l),
|
||||||
'(' => one(l, TokenKind::LParen),
|
'(' => one(l, TokenKind::LParen),
|
||||||
')' => one(l, TokenKind::RParen),
|
')' => one(l, TokenKind::RParen),
|
||||||
'[' => one(l, TokenKind::LBrack),
|
'[' => one(l, TokenKind::LBrack),
|
||||||
|
|
@ -228,22 +222,13 @@ fn token(l: &mut Lexer<'_>) -> (TokenKind, Span, bool) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let end = l.position;
|
let end = l.position;
|
||||||
(kind, Span::new(start, end), has_left_space)
|
(kind, Span::new(start, end))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lex(l: &mut Lexer<'_>) -> Result<(), TokenAllocError> {
|
pub fn lex(l: &mut Lexer<'_>) -> Result<(), TokenAllocError> {
|
||||||
loop {
|
loop {
|
||||||
let (kind, span, has_left_space) = token(l);
|
let (kind, span) = token(l);
|
||||||
|
l.lexis.push(kind, span)?;
|
||||||
if !l.lexis.is_empty() {
|
|
||||||
let prev = l.lexis.len() - 1;
|
|
||||||
let spaces = l.lexis.spaces(prev);
|
|
||||||
l.lexis
|
|
||||||
.set_spaces(prev, Spaces::new(spaces.left(), has_left_space));
|
|
||||||
}
|
|
||||||
let spaces = Spaces::new(has_left_space, false);
|
|
||||||
l.lexis.push(kind, spaces, span)?;
|
|
||||||
|
|
||||||
if kind == TokenKind::Eof {
|
if kind == TokenKind::Eof {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use crate::{
|
||||||
ast::{Ast, NodeAllocError, NodeId, NodeKind},
|
ast::{Ast, NodeAllocError, NodeId, NodeKind},
|
||||||
diagnostic::Diagnostic,
|
diagnostic::Diagnostic,
|
||||||
source::Span,
|
source::Span,
|
||||||
token::{Lexis, Spaces, TokenKind, TokenKindSet},
|
token::{Lexis, TokenKind, TokenKindSet},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
@ -132,11 +132,6 @@ impl<'a> Parser<'a> {
|
||||||
self.tokens.kind(self.position)
|
self.tokens.kind(self.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn peek_with_spaces(&self) -> (TokenKind, Spaces) {
|
|
||||||
(self.peek(), self.tokens.spaces(self.position))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn span(&self) -> Span {
|
fn span(&self) -> Span {
|
||||||
self.tokens.span(self.position)
|
self.tokens.span(self.position)
|
||||||
}
|
}
|
||||||
|
|
@ -303,61 +298,33 @@ impl fmt::Display for IntoAstError {
|
||||||
|
|
||||||
impl Error for IntoAstError {}
|
impl Error for IntoAstError {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
enum Tighter {
|
enum Tighter {
|
||||||
Left,
|
Left,
|
||||||
Right,
|
Right,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tighter(left: (TokenKind, Spaces), right: (TokenKind, Spaces)) -> Tighter {
|
fn tighter(left: TokenKind, right: TokenKind) -> Tighter {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
fn tightness(kind: TokenKind) -> Option<usize> {
|
||||||
enum Spacing {
|
match kind {
|
||||||
Loose,
|
TokenKind::Equal | TokenKind::Colon => Some(0),
|
||||||
Call,
|
|
||||||
Tight,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tightness((kind, spaces): (TokenKind, Spaces)) -> Option<(Spacing, usize)> {
|
|
||||||
let spacing = match kind {
|
|
||||||
// There are a few types of operators which are independent of tightness.
|
|
||||||
|
|
||||||
// For : and =, it does not matter if they're spelled one way or the other, because
|
|
||||||
// there is only one way to use them (at the beginning of the expression).
|
|
||||||
TokenKind::Colon | TokenKind::Equal => Spacing::Loose,
|
|
||||||
|
|
||||||
// For calls, there is a special intermediate level, such that they can sit between
|
|
||||||
// loose operators and tight operators.
|
|
||||||
_ if PREFIX_TOKENS.contains(kind) => Spacing::Call,
|
|
||||||
|
|
||||||
// For everything else, the usual rules apply.
|
|
||||||
_ => match spaces.pair() {
|
|
||||||
(false, false) => Spacing::Tight,
|
|
||||||
(true, true) => Spacing::Loose,
|
|
||||||
_ => return None, // not a valid infix operator
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let index = match kind {
|
|
||||||
TokenKind::Equal | TokenKind::Colon => 0,
|
|
||||||
// 1: reserved for `and` and `or`
|
|
||||||
TokenKind::EqualEqual
|
TokenKind::EqualEqual
|
||||||
| TokenKind::NotEqual
|
| TokenKind::NotEqual
|
||||||
| TokenKind::Less
|
| TokenKind::Less
|
||||||
| TokenKind::LessEqual
|
| TokenKind::LessEqual
|
||||||
| TokenKind::Greater
|
| TokenKind::Greater
|
||||||
| TokenKind::GreaterEqual => 2,
|
| TokenKind::GreaterEqual => Some(1),
|
||||||
TokenKind::Plus | TokenKind::Minus | TokenKind::Star | TokenKind::Slash => 3,
|
TokenKind::Plus | TokenKind::Minus => Some(2),
|
||||||
// 4: reserve for `.`
|
TokenKind::Star | TokenKind::Slash => Some(3),
|
||||||
_ if PREFIX_TOKENS.contains(kind) => 5,
|
_ if PREFIX_TOKENS.contains(kind) => Some(4),
|
||||||
_ => return None, // not an infix operator
|
_ => None,
|
||||||
};
|
}
|
||||||
Some((spacing, index))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(right_tightness) = tightness(right) else {
|
let Some(right_tightness) = tightness(right) else {
|
||||||
return Tighter::Left;
|
return Tighter::Left;
|
||||||
};
|
};
|
||||||
let Some(left_tightness) = tightness(left) else {
|
let Some(left_tightness) = tightness(left) else {
|
||||||
assert!(left.0 == TokenKind::Eof);
|
assert!(left == TokenKind::Eof);
|
||||||
return Tighter::Right;
|
return Tighter::Right;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -368,13 +335,12 @@ fn tighter(left: (TokenKind, Spaces), right: (TokenKind, Spaces)) -> Tighter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn precedence_parse(p: &mut Parser, left: (TokenKind, Spaces)) {
|
fn precedence_parse(p: &mut Parser, left: TokenKind) {
|
||||||
let mut lhs = prefix(p);
|
let mut lhs = prefix(p);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let right = p.peek_with_spaces();
|
let right = p.peek();
|
||||||
let tighter = tighter(left, right);
|
match tighter(left, right) {
|
||||||
match tighter {
|
|
||||||
Tighter::Left => break,
|
Tighter::Left => break,
|
||||||
Tighter::Right => {
|
Tighter::Right => {
|
||||||
let o = p.open_before(lhs);
|
let o = p.open_before(lhs);
|
||||||
|
|
@ -570,12 +536,55 @@ fn if_expr(p: &mut Parser) -> Closed {
|
||||||
p.close(o, NodeKind::If)
|
p.close(o, NodeKind::If)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: This must be synchronised with the match expression in prefix().
|
fn let_expr(p: &mut Parser) -> Closed {
|
||||||
|
let o = p.open();
|
||||||
|
|
||||||
|
p.advance(); // let
|
||||||
|
|
||||||
|
if p.peek() == TokenKind::Ident {
|
||||||
|
let ident = p.open();
|
||||||
|
p.advance();
|
||||||
|
p.close(ident, NodeKind::Ident);
|
||||||
|
} else {
|
||||||
|
let span = p.span();
|
||||||
|
p.emit(Diagnostic::error(span, "`let` variable name expected"));
|
||||||
|
p.advance_with_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.peek() == TokenKind::Equal {
|
||||||
|
p.advance();
|
||||||
|
} else {
|
||||||
|
let span = p.span();
|
||||||
|
p.emit(Diagnostic::error(span, "`=` expected after variable name"));
|
||||||
|
p.advance_with_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
expr(p);
|
||||||
|
|
||||||
|
if p.peek() == TokenKind::Newline {
|
||||||
|
p.advance();
|
||||||
|
} else {
|
||||||
|
let span = p.span();
|
||||||
|
p.emit(Diagnostic::error(
|
||||||
|
span,
|
||||||
|
"new line expected after `let` expression",
|
||||||
|
));
|
||||||
|
p.advance_with_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
expr(p);
|
||||||
|
|
||||||
|
p.close(o, NodeKind::Let)
|
||||||
|
}
|
||||||
|
|
||||||
const PREFIX_TOKENS: TokenKindSet = TokenKindSet::new(&[
|
const PREFIX_TOKENS: TokenKindSet = TokenKindSet::new(&[
|
||||||
TokenKind::Ident,
|
TokenKind::Ident,
|
||||||
TokenKind::Tag,
|
TokenKind::Tag,
|
||||||
TokenKind::Number,
|
TokenKind::Number,
|
||||||
TokenKind::Color,
|
TokenKind::Color,
|
||||||
|
// NOTE: This is ambiguous in function calls.
|
||||||
|
// In that case, the infix operator takes precedence (because the `match` arms for the infix op
|
||||||
|
// come first.)
|
||||||
TokenKind::Minus,
|
TokenKind::Minus,
|
||||||
TokenKind::Not,
|
TokenKind::Not,
|
||||||
TokenKind::LParen,
|
TokenKind::LParen,
|
||||||
|
|
@ -614,8 +623,8 @@ fn prefix(p: &mut Parser) -> Closed {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infix(p: &mut Parser, op: (TokenKind, Spaces)) -> NodeKind {
|
fn infix(p: &mut Parser, op: TokenKind) -> NodeKind {
|
||||||
match op.0 {
|
match op {
|
||||||
TokenKind::Plus
|
TokenKind::Plus
|
||||||
| TokenKind::Minus
|
| TokenKind::Minus
|
||||||
| TokenKind::Star
|
| TokenKind::Star
|
||||||
|
|
@ -630,13 +639,13 @@ fn infix(p: &mut Parser, op: (TokenKind, Spaces)) -> NodeKind {
|
||||||
|
|
||||||
TokenKind::Equal => infix_let(p, op),
|
TokenKind::Equal => infix_let(p, op),
|
||||||
|
|
||||||
_ if PREFIX_TOKENS.contains(op.0) => infix_call(p, op),
|
_ if PREFIX_TOKENS.contains(op) => infix_call(p),
|
||||||
|
|
||||||
_ => panic!("unhandled infix operator {op:?}"),
|
_ => panic!("unhandled infix operator {op:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infix_binary(p: &mut Parser, op: (TokenKind, Spaces)) -> NodeKind {
|
fn infix_binary(p: &mut Parser, op: TokenKind) -> NodeKind {
|
||||||
let o = p.open();
|
let o = p.open();
|
||||||
p.advance();
|
p.advance();
|
||||||
p.close(o, NodeKind::Op);
|
p.close(o, NodeKind::Op);
|
||||||
|
|
@ -649,16 +658,15 @@ fn infix_binary(p: &mut Parser, op: (TokenKind, Spaces)) -> NodeKind {
|
||||||
NodeKind::Binary
|
NodeKind::Binary
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infix_call(p: &mut Parser, mut arg: (TokenKind, Spaces)) -> NodeKind {
|
fn infix_call(p: &mut Parser) -> NodeKind {
|
||||||
while PREFIX_TOKENS.contains(p.peek()) {
|
while PREFIX_TOKENS.contains(p.peek()) {
|
||||||
precedence_parse(p, arg);
|
prefix(p);
|
||||||
arg = p.peek_with_spaces();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeKind::Call
|
NodeKind::Call
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infix_let(p: &mut Parser, op: (TokenKind, Spaces)) -> NodeKind {
|
fn infix_let(p: &mut Parser, op: TokenKind) -> NodeKind {
|
||||||
p.advance();
|
p.advance();
|
||||||
|
|
||||||
if p.peek() == TokenKind::Newline {
|
if p.peek() == TokenKind::Newline {
|
||||||
|
|
@ -684,7 +692,7 @@ fn infix_let(p: &mut Parser, op: (TokenKind, Spaces)) -> NodeKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expr(p: &mut Parser) {
|
pub fn expr(p: &mut Parser) {
|
||||||
precedence_parse(p, (TokenKind::Eof, Spaces::new(true, false)))
|
precedence_parse(p, TokenKind::Eof)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toplevel(p: &mut Parser) {
|
pub fn toplevel(p: &mut Parser) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,4 @@
|
||||||
use core::{
|
use core::{error::Error, fmt::Display};
|
||||||
error::Error,
|
|
||||||
fmt::{self, Display},
|
|
||||||
};
|
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
|
|
@ -52,16 +49,10 @@ pub enum TokenKind {
|
||||||
Error,
|
Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub struct Spaces {
|
|
||||||
value: u8, // 0b10 = left, 0b01 = right
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Lexis {
|
pub struct Lexis {
|
||||||
kinds: Vec<TokenKind>,
|
pub kinds: Vec<TokenKind>,
|
||||||
spaces: Vec<Spaces>,
|
pub spans: Vec<Span>,
|
||||||
spans: Vec<Span>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Lexis {
|
impl Lexis {
|
||||||
|
|
@ -70,7 +61,6 @@ impl Lexis {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
kinds: Vec::with_capacity(capacity),
|
kinds: Vec::with_capacity(capacity),
|
||||||
spaces: Vec::with_capacity(capacity),
|
|
||||||
spans: Vec::with_capacity(capacity),
|
spans: Vec::with_capacity(capacity),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -83,18 +73,12 @@ impl Lexis {
|
||||||
self.len() == 0
|
self.len() == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(
|
pub fn push(&mut self, kind: TokenKind, span: Span) -> Result<(), TokenAllocError> {
|
||||||
&mut self,
|
|
||||||
kind: TokenKind,
|
|
||||||
spaces: Spaces,
|
|
||||||
span: Span,
|
|
||||||
) -> Result<(), TokenAllocError> {
|
|
||||||
if self.kinds.len() >= self.kinds.capacity() {
|
if self.kinds.len() >= self.kinds.capacity() {
|
||||||
return Err(TokenAllocError);
|
return Err(TokenAllocError);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.kinds.push(kind);
|
self.kinds.push(kind);
|
||||||
self.spaces.push(spaces);
|
|
||||||
self.spans.push(span);
|
self.spans.push(span);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -104,48 +88,11 @@ impl Lexis {
|
||||||
self.kinds[position as usize]
|
self.kinds[position as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spaces(&self, position: u32) -> Spaces {
|
|
||||||
self.spaces[position as usize]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_spaces(&mut self, position: u32, spaces: Spaces) {
|
|
||||||
self.spaces[position as usize] = spaces;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn span(&self, position: u32) -> Span {
|
pub fn span(&self, position: u32) -> Span {
|
||||||
self.spans[position as usize]
|
self.spans[position as usize]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Spaces {
|
|
||||||
pub fn new(left: bool, right: bool) -> Self {
|
|
||||||
Self {
|
|
||||||
value: (left as u8) << 1 | right as u8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn left(self) -> bool {
|
|
||||||
(self.value & 0b10) == 0b10
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn right(self) -> bool {
|
|
||||||
(self.value & 0b01) == 0b01
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pair(self) -> (bool, bool) {
|
|
||||||
(self.left(), self.right())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Spaces {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_tuple("Spaces")
|
|
||||||
.field(&self.left())
|
|
||||||
.field(&self.right())
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct TokenAllocError;
|
pub struct TokenAllocError;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,11 @@ impl Database {
|
||||||
pub fn start(settings: Settings) -> eyre::Result<Database> {
|
pub fn start(settings: Settings) -> eyre::Result<Database> {
|
||||||
let db = Connection::open(settings.path).context("cannot open wall database")?;
|
let db = Connection::open(settings.path).context("cannot open wall database")?;
|
||||||
|
|
||||||
|
let major: u32 = env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap();
|
||||||
|
let minor: u32 = env!("CARGO_PKG_VERSION_MINOR").parse().unwrap();
|
||||||
|
let patch: u32 = env!("CARGO_PKG_VERSION_PATCH").parse().unwrap();
|
||||||
|
let version = major * 1_000_000 + minor * 1_000 + patch;
|
||||||
|
|
||||||
info!("initial setup");
|
info!("initial setup");
|
||||||
|
|
||||||
let version: u32 = db.pragma_query_value(None, "user_version", |x| x.get(0))?;
|
let version: u32 = db.pragma_query_value(None, "user_version", |x| x.get(0))?;
|
||||||
|
|
@ -105,7 +110,7 @@ pub fn start(settings: Settings) -> eyre::Result<Database> {
|
||||||
paint_area INTEGER NOT NULL,
|
paint_area INTEGER NOT NULL,
|
||||||
chunk_size INTEGER NOT NULL
|
chunk_size INTEGER NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS
|
CREATE TABLE IF NOT EXISTS
|
||||||
t_wall_info (
|
t_wall_info (
|
||||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,8 @@ or_: \\a, b ->
|
||||||
else b
|
else b
|
||||||
|
|
||||||
withDotter \\d ->
|
withDotter \\d ->
|
||||||
visible? = mod (d Num) length < length * duty
|
visible = mod (d Num) length < length * duty
|
||||||
if (visible?)
|
if (visible)
|
||||||
stroke thickness color (line (d From) (d To))
|
stroke thickness color (line (d From) (d To))
|
||||||
else
|
else
|
||||||
()
|
()
|
||||||
|
|
@ -76,9 +76,9 @@ wavelength: 1
|
||||||
|
|
||||||
withDotter \\d ->
|
withDotter \\d ->
|
||||||
pi = 3.14159265
|
pi = 3.14159265
|
||||||
a = sin (d Num * wavelength / pi) + 1 / 2
|
a = (sin (d Num * wavelength / pi) + 1) / 2
|
||||||
range = maxThickness - minThickness
|
range = maxThickness - minThickness
|
||||||
thickness = a * range + minThickness
|
thickness = minThickness + a * range
|
||||||
stroke thickness color (line (d From) (d To))
|
stroke thickness color (line (d From) (d To))
|
||||||
`.trim(),
|
`.trim(),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -217,14 +217,9 @@ export class Haku {
|
||||||
console.groupCollapsed("construct Haku");
|
console.groupCollapsed("construct Haku");
|
||||||
{
|
{
|
||||||
let pLimits = allocCheck(w.haku_limits_new());
|
let pLimits = allocCheck(w.haku_limits_new());
|
||||||
w.haku_limits_set_max_source_code_len(pLimits, limits.max_source_code_len);
|
for (let name of Object.keys(limits)) {
|
||||||
w.haku_limits_set_max_chunks(pLimits, limits.max_chunks);
|
w[`haku_limits_set_${name}`](pLimits, limits[name]);
|
||||||
w.haku_limits_set_max_defs(pLimits, limits.max_defs);
|
}
|
||||||
w.haku_limits_set_max_tags(pLimits, limits.max_tags);
|
|
||||||
w.haku_limits_set_max_tokens(pLimits, limits.max_tokens);
|
|
||||||
w.haku_limits_set_max_parser_events(pLimits, limits.max_parser_events);
|
|
||||||
w.haku_limits_set_ast_capacity(pLimits, limits.ast_capacity);
|
|
||||||
w.haku_limits_set_chunk_capacity(pLimits, limits.chunk_capacity);
|
|
||||||
|
|
||||||
this.#pInstance = allocCheck(w.haku_instance_new(pLimits));
|
this.#pInstance = allocCheck(w.haku_instance_new(pLimits));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,10 @@ export function selfController(interactionQueue, wall, layer, event) {
|
||||||
if (renderArea != null) {
|
if (renderArea != null) {
|
||||||
let numChunksToRender = numChunksInRectangle(renderArea, layer.chunkSize);
|
let numChunksToRender = numChunksInRectangle(renderArea, layer.chunkSize);
|
||||||
let result = renderToChunksInArea(layer, renderArea, renderToPixmap);
|
let result = renderToChunksInArea(layer, renderArea, renderToPixmap);
|
||||||
|
if (!layer.canFitNewChunks(numChunksToRender)) {
|
||||||
|
console.debug("too many chunks rendered; committing interaction early");
|
||||||
|
event.earlyCommitInteraction();
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
} else {
|
} else {
|
||||||
console.debug("render area is empty, nothing will be rendered");
|
console.debug("render area is empty, nothing will be rendered");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue