fix parsing prefix operators in calls

This commit is contained in:
りき萌 2025-09-02 18:44:38 +02:00
parent b52c1b26c9
commit 29a80854a4
4 changed files with 69 additions and 43 deletions

View file

@ -401,6 +401,7 @@ unsafe extern "C" fn haku_compile_brush(
"compiling: parsed successfully into {} AST nodes",
ast.len()
);
// debug!("ast: {}", ast::dump::dump(&ast, root, Some(code)));
let src = Source { code, ast: &ast };
@ -431,21 +432,21 @@ unsafe extern "C" fn haku_compile_brush(
);
debug!("compiling: {closure_spec:?}");
debug!("bytecode: {:?}", chunk.bytecode);
{
let mut cursor = 0_usize;
for info in &chunk.span_info {
let slice = &chunk.bytecode[cursor..cursor + info.len as usize];
debug!(
"{:?} | 0x{:x} {:?} | {:?}",
info.span,
cursor,
slice,
info.span.slice(src.code),
);
cursor += info.len as usize;
}
}
// debug!("bytecode: {:?}", chunk.bytecode);
// {
// let mut cursor = 0_usize;
// for info in &chunk.span_info {
// let slice = &chunk.bytecode[cursor..cursor + info.len as usize];
// debug!(
// "{:?} | 0x{:x} {:?} | {:?}",
// info.span,
// cursor,
// slice,
// info.span.slice(src.code),
// );
// cursor += info.len as usize;
// }
// }
instance.compile_result2 = Some(CompileResult {
defs_string: instance.defs.serialize_defs(),

View file

@ -311,31 +311,13 @@ enum Tighter {
fn tighter(left: (TokenKind, Spaces), right: (TokenKind, Spaces)) -> Tighter {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
enum Spacing {
Loose,
enum Tightness {
Loose(usize),
Call,
Tight,
Tight(usize),
}
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
},
};
fn tightness((kind, spaces): (TokenKind, Spaces)) -> Option<Tightness> {
let index = match kind {
TokenKind::Equal | TokenKind::Colon => 0,
// 1: reserved for `and` and `or`
@ -350,7 +332,28 @@ fn tighter(left: (TokenKind, Spaces), right: (TokenKind, Spaces)) -> Tighter {
_ if PREFIX_TOKENS.contains(kind) => 5,
_ => return None, // not an infix operator
};
Some((spacing, index))
Some(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 => Tightness::Loose(index),
// For unary -, we treat it as having Tight spacing rather than Call, else it would
// be allowed to begin function calls.
TokenKind::Minus if spaces.pair() == (true, false) => Tightness::Tight(index),
// For calls, there is a special intermediate level, such that they can sit between
// loose operators and tight operators.
_ if PREFIX_TOKENS.contains(kind) => Tightness::Call,
// For everything else, the usual rules apply.
_ => match spaces.pair() {
(false, false) => Tightness::Tight(index),
(true, true) => Tightness::Loose(index),
_ => return None, // not a valid infix operator
},
})
}
let Some(right_tightness) = tightness(right) else {
@ -361,6 +364,12 @@ fn tighter(left: (TokenKind, Spaces), right: (TokenKind, Spaces)) -> Tighter {
return Tighter::Right;
};
// When we're inside a call, subsequent arguments must not be slurped into the current
// expression, as it would result in calls being parsed as (vec (1 (-1))), which is not correct.
if left_tightness == Tightness::Call {
return Tighter::Left;
}
if right_tightness > left_tightness {
Tighter::Right
} else {
@ -585,18 +594,29 @@ const PREFIX_TOKENS: TokenKindSet = TokenKindSet::new(&[
]);
fn prefix(p: &mut Parser) -> Closed {
match p.peek() {
let (kind, spaces) = p.peek_with_spaces();
match kind {
TokenKind::Ident => one(p, NodeKind::Ident),
TokenKind::Tag => one(p, NodeKind::Tag),
TokenKind::Number => one(p, NodeKind::Number),
TokenKind::Color => one(p, NodeKind::Color),
TokenKind::LBrack => list(p),
TokenKind::Minus | TokenKind::Not => unary(p),
TokenKind::Minus if !spaces.right() => unary(p),
TokenKind::Not => unary(p),
TokenKind::LParen => paren(p),
TokenKind::Backslash => lambda(p),
TokenKind::If => if_expr(p),
TokenKind::Minus if spaces.pair() == (false, true) => {
let span = p.span();
p.emit(Diagnostic::error(
span,
"`-` operator must be surrounded by an equal amount of spaces",
));
p.advance_with_error()
}
_ => {
assert!(
!PREFIX_TOKENS.contains(p.peek()),
@ -615,9 +635,9 @@ fn prefix(p: &mut Parser) -> Closed {
}
fn infix(p: &mut Parser, op: (TokenKind, Spaces)) -> NodeKind {
match op.0 {
let (kind, spaces) = op;
match kind {
TokenKind::Plus
| TokenKind::Minus
| TokenKind::Star
| TokenKind::Slash
| TokenKind::EqualEqual
@ -627,6 +647,7 @@ fn infix(p: &mut Parser, op: (TokenKind, Spaces)) -> NodeKind {
| TokenKind::Greater
| TokenKind::GreaterEqual
| TokenKind::Colon => infix_binary(p, op),
TokenKind::Minus if spaces.are_balanced() => infix_binary(p, op),
TokenKind::Equal => infix_let(p, op),

View file

@ -135,6 +135,10 @@ impl Spaces {
pub fn pair(self) -> (bool, bool) {
(self.left(), self.right())
}
pub fn are_balanced(self) -> bool {
matches!(self.pair(), (true, true) | (false, false))
}
}
impl fmt::Debug for Spaces {

View file

@ -100,7 +100,7 @@ norm: \\u ->
u / vec l l
perpClockwise: \\v ->
vec (vecY v) (-(vecX v))
vec (vecY v) -(vecX v)
withDotter \\d ->
pi = 3.14159265