fix parsing prefix operators in calls
This commit is contained in:
parent
b52c1b26c9
commit
29a80854a4
4 changed files with 69 additions and 43 deletions
|
@ -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(),
|
||||
|
|
|
@ -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),
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue