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",
|
"compiling: parsed successfully into {} AST nodes",
|
||||||
ast.len()
|
ast.len()
|
||||||
);
|
);
|
||||||
|
// debug!("ast: {}", ast::dump::dump(&ast, root, Some(code)));
|
||||||
|
|
||||||
let src = Source { code, ast: &ast };
|
let src = Source { code, ast: &ast };
|
||||||
|
|
||||||
|
@ -431,21 +432,21 @@ unsafe extern "C" fn haku_compile_brush(
|
||||||
);
|
);
|
||||||
debug!("compiling: {closure_spec:?}");
|
debug!("compiling: {closure_spec:?}");
|
||||||
|
|
||||||
debug!("bytecode: {:?}", chunk.bytecode);
|
// debug!("bytecode: {:?}", chunk.bytecode);
|
||||||
{
|
// {
|
||||||
let mut cursor = 0_usize;
|
// let mut cursor = 0_usize;
|
||||||
for info in &chunk.span_info {
|
// for info in &chunk.span_info {
|
||||||
let slice = &chunk.bytecode[cursor..cursor + info.len as usize];
|
// let slice = &chunk.bytecode[cursor..cursor + info.len as usize];
|
||||||
debug!(
|
// debug!(
|
||||||
"{:?} | 0x{:x} {:?} | {:?}",
|
// "{:?} | 0x{:x} {:?} | {:?}",
|
||||||
info.span,
|
// info.span,
|
||||||
cursor,
|
// cursor,
|
||||||
slice,
|
// slice,
|
||||||
info.span.slice(src.code),
|
// info.span.slice(src.code),
|
||||||
);
|
// );
|
||||||
cursor += info.len as usize;
|
// cursor += info.len as usize;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
instance.compile_result2 = Some(CompileResult {
|
instance.compile_result2 = Some(CompileResult {
|
||||||
defs_string: instance.defs.serialize_defs(),
|
defs_string: instance.defs.serialize_defs(),
|
||||||
|
|
|
@ -311,31 +311,13 @@ enum Tighter {
|
||||||
|
|
||||||
fn tighter(left: (TokenKind, Spaces), right: (TokenKind, Spaces)) -> Tighter {
|
fn tighter(left: (TokenKind, Spaces), right: (TokenKind, Spaces)) -> Tighter {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
enum Spacing {
|
enum Tightness {
|
||||||
Loose,
|
Loose(usize),
|
||||||
Call,
|
Call,
|
||||||
Tight,
|
Tight(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tightness((kind, spaces): (TokenKind, Spaces)) -> Option<(Spacing, usize)> {
|
fn tightness((kind, spaces): (TokenKind, Spaces)) -> Option<Tightness> {
|
||||||
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 {
|
let index = match kind {
|
||||||
TokenKind::Equal | TokenKind::Colon => 0,
|
TokenKind::Equal | TokenKind::Colon => 0,
|
||||||
// 1: reserved for `and` and `or`
|
// 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,
|
_ if PREFIX_TOKENS.contains(kind) => 5,
|
||||||
_ => return None, // not an infix operator
|
_ => 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 {
|
let Some(right_tightness) = tightness(right) else {
|
||||||
|
@ -361,6 +364,12 @@ fn tighter(left: (TokenKind, Spaces), right: (TokenKind, Spaces)) -> Tighter {
|
||||||
return Tighter::Right;
|
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 {
|
if right_tightness > left_tightness {
|
||||||
Tighter::Right
|
Tighter::Right
|
||||||
} else {
|
} else {
|
||||||
|
@ -585,18 +594,29 @@ const PREFIX_TOKENS: TokenKindSet = TokenKindSet::new(&[
|
||||||
]);
|
]);
|
||||||
|
|
||||||
fn prefix(p: &mut Parser) -> Closed {
|
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::Ident => one(p, NodeKind::Ident),
|
||||||
TokenKind::Tag => one(p, NodeKind::Tag),
|
TokenKind::Tag => one(p, NodeKind::Tag),
|
||||||
TokenKind::Number => one(p, NodeKind::Number),
|
TokenKind::Number => one(p, NodeKind::Number),
|
||||||
TokenKind::Color => one(p, NodeKind::Color),
|
TokenKind::Color => one(p, NodeKind::Color),
|
||||||
TokenKind::LBrack => list(p),
|
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::LParen => paren(p),
|
||||||
TokenKind::Backslash => lambda(p),
|
TokenKind::Backslash => lambda(p),
|
||||||
TokenKind::If => if_expr(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!(
|
assert!(
|
||||||
!PREFIX_TOKENS.contains(p.peek()),
|
!PREFIX_TOKENS.contains(p.peek()),
|
||||||
|
@ -615,9 +635,9 @@ fn prefix(p: &mut Parser) -> Closed {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infix(p: &mut Parser, op: (TokenKind, Spaces)) -> NodeKind {
|
fn infix(p: &mut Parser, op: (TokenKind, Spaces)) -> NodeKind {
|
||||||
match op.0 {
|
let (kind, spaces) = op;
|
||||||
|
match kind {
|
||||||
TokenKind::Plus
|
TokenKind::Plus
|
||||||
| TokenKind::Minus
|
|
||||||
| TokenKind::Star
|
| TokenKind::Star
|
||||||
| TokenKind::Slash
|
| TokenKind::Slash
|
||||||
| TokenKind::EqualEqual
|
| TokenKind::EqualEqual
|
||||||
|
@ -627,6 +647,7 @@ fn infix(p: &mut Parser, op: (TokenKind, Spaces)) -> NodeKind {
|
||||||
| TokenKind::Greater
|
| TokenKind::Greater
|
||||||
| TokenKind::GreaterEqual
|
| TokenKind::GreaterEqual
|
||||||
| TokenKind::Colon => infix_binary(p, op),
|
| TokenKind::Colon => infix_binary(p, op),
|
||||||
|
TokenKind::Minus if spaces.are_balanced() => infix_binary(p, op),
|
||||||
|
|
||||||
TokenKind::Equal => infix_let(p, op),
|
TokenKind::Equal => infix_let(p, op),
|
||||||
|
|
||||||
|
|
|
@ -135,6 +135,10 @@ impl Spaces {
|
||||||
pub fn pair(self) -> (bool, bool) {
|
pub fn pair(self) -> (bool, bool) {
|
||||||
(self.left(), self.right())
|
(self.left(), self.right())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn are_balanced(self) -> bool {
|
||||||
|
matches!(self.pair(), (true, true) | (false, false))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Spaces {
|
impl fmt::Debug for Spaces {
|
||||||
|
|
|
@ -100,7 +100,7 @@ norm: \\u ->
|
||||||
u / vec l l
|
u / vec l l
|
||||||
|
|
||||||
perpClockwise: \\v ->
|
perpClockwise: \\v ->
|
||||||
vec (vecY v) (-(vecX v))
|
vec (vecY v) -(vecX v)
|
||||||
|
|
||||||
withDotter \\d ->
|
withDotter \\d ->
|
||||||
pi = 3.14159265
|
pi = 3.14159265
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue