fix a few bugs with the new precedence rules

This commit is contained in:
りき萌 2025-09-02 20:02:48 +02:00
parent 29a80854a4
commit 9808d3227f
3 changed files with 113 additions and 59 deletions

View file

@ -432,22 +432,23 @@ 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(),
tags_string: instance.defs.serialize_tags(),

View file

@ -329,7 +329,7 @@ fn tighter(left: (TokenKind, Spaces), right: (TokenKind, Spaces)) -> Tighter {
| TokenKind::GreaterEqual => 2,
TokenKind::Plus | TokenKind::Minus | TokenKind::Star | TokenKind::Slash => 3,
// 4: reserve for `.`
_ if PREFIX_TOKENS.contains(kind) => 5,
_ if is_prefix_token((kind, spaces)) => 5,
_ => return None, // not an infix operator
};
Some(match kind {
@ -341,11 +341,11 @@ fn tighter(left: (TokenKind, Spaces), right: (TokenKind, Spaces)) -> Tighter {
// 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),
TokenKind::Minus if !spaces.are_balanced() => 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,
_ if is_prefix_token((kind, spaces)) => Tightness::Call,
// For everything else, the usual rules apply.
_ => match spaces.pair() {
@ -366,7 +366,7 @@ fn tighter(left: (TokenKind, Spaces), right: (TokenKind, Spaces)) -> Tighter {
// 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 {
if left_tightness == Tightness::Call && right.0 == TokenKind::Minus && !right.1.are_balanced() {
return Tighter::Left;
}
@ -579,13 +579,15 @@ fn if_expr(p: &mut Parser) -> Closed {
p.close(o, NodeKind::If)
}
// TODO: There is a lot of special casing around `-` being both a prefix and an infix token.
// Maybe there's a way to simplify it?
// NOTE: This must be synchronised with the match expression in prefix().
const PREFIX_TOKENS: TokenKindSet = TokenKindSet::new(&[
TokenKind::Ident,
TokenKind::Tag,
TokenKind::Number,
TokenKind::Color,
TokenKind::Minus,
TokenKind::Not,
TokenKind::LParen,
TokenKind::Backslash,
@ -593,6 +595,10 @@ const PREFIX_TOKENS: TokenKindSet = TokenKindSet::new(&[
TokenKind::LBrack,
]);
fn is_prefix_token((kind, spaces): (TokenKind, Spaces)) -> bool {
PREFIX_TOKENS.contains(kind) || (kind == TokenKind::Minus && !spaces.are_balanced())
}
fn prefix(p: &mut Parser) -> Closed {
let (kind, spaces) = p.peek_with_spaces();
match kind {
@ -602,7 +608,7 @@ fn prefix(p: &mut Parser) -> Closed {
TokenKind::Color => one(p, NodeKind::Color),
TokenKind::LBrack => list(p),
TokenKind::Minus if !spaces.right() => unary(p),
TokenKind::Minus if spaces.pair() == (true, false) => unary(p),
TokenKind::Not => unary(p),
TokenKind::LParen => paren(p),
TokenKind::Backslash => lambda(p),
@ -619,9 +625,9 @@ fn prefix(p: &mut Parser) -> Closed {
_ => {
assert!(
!PREFIX_TOKENS.contains(p.peek()),
"{:?} found in PREFIX_TOKENS",
p.peek()
!is_prefix_token(p.peek_with_spaces()),
"{:?} is not a prefix token",
p.peek_with_spaces()
);
let span = p.span();
@ -651,7 +657,7 @@ fn infix(p: &mut Parser, op: (TokenKind, Spaces)) -> NodeKind {
TokenKind::Equal => infix_let(p, op),
_ if PREFIX_TOKENS.contains(op.0) => infix_call(p, op),
_ if is_prefix_token(op) => infix_call(p, op),
_ => panic!("unhandled infix operator {op:?}"),
}
@ -671,7 +677,7 @@ fn infix_binary(p: &mut Parser, op: (TokenKind, Spaces)) -> NodeKind {
}
fn infix_call(p: &mut Parser, mut arg: (TokenKind, Spaces)) -> NodeKind {
while PREFIX_TOKENS.contains(p.peek()) {
while is_prefix_token(p.peek_with_spaces()) {
precedence_parse(p, arg);
arg = p.peek_with_spaces();
}