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