diff --git a/fine/src/parser.rs b/fine/src/parser.rs index 8a906de0..cfc40545 100644 --- a/fine/src/parser.rs +++ b/fine/src/parser.rs @@ -155,6 +155,9 @@ pub enum TreeKind { TypeParameter, TypeParameterList, UnaryExpression, + + IsExpression, + VariableBinding, } pub struct Tree<'a> { @@ -318,6 +321,7 @@ struct MarkClosed { struct CParser<'a> { tokens: Tokens<'a>, current: Token<'a>, + next: Token<'a>, fuel: Cell, events: Vec>, panic: bool, @@ -328,11 +332,13 @@ impl<'a> CParser<'a> { let mut parser = CParser { tokens, current: Token::new(TokenKind::EOF, 0, ""), + next: Token::new(TokenKind::EOF, 0, ""), fuel: Cell::new(256), events: Vec::new(), panic: false, }; parser.current = parser.tokens.next(); + parser.next = parser.tokens.next(); parser.skip_ephemera(); parser } @@ -371,14 +377,18 @@ impl<'a> CParser<'a> { self.events.push(ParseEvent::Advance { token: self.current.clone(), }); - self.current = self.tokens.next(); + // Move next into current (and current into next but who cares, thanks rust.) + std::mem::swap(&mut self.current, &mut self.next); + self.next = self.tokens.next(); self.skip_ephemera(); } fn skip_ephemera(&mut self) { while self.current.kind == TokenKind::Whitespace || self.current.kind == TokenKind::Comment { - self.current = self.tokens.next(); + // Move next into current (and current into next but who cares, thanks rust.) + std::mem::swap(&mut self.current, &mut self.next); + self.next = self.tokens.next(); } } @@ -397,6 +407,17 @@ impl<'a> CParser<'a> { self.current.kind } + fn peek_next(&self) -> TokenKind { + if self.fuel.get() == 0 { + panic!( + "parser is stuck at '{}' ({})!", + self.current, self.current.start + ); + } + self.fuel.set(self.fuel.get() - 1); + self.next.kind + } + // fn trace(&self, msg: &str) { // eprintln!("{}: {}: {}", self.current.start, self.current, msg); // } @@ -887,7 +908,7 @@ fn expression(p: &mut CParser) { expression_with_power(p, 0) } -const UNARY_POWER: u8 = 14; +const UNARY_POWER: u8 = 16; fn infix_power(token: TokenKind) -> Option<(u8, u8)> { // A dumb thing: the pair controls associativity. @@ -897,17 +918,18 @@ fn infix_power(token: TokenKind) -> Option<(u8, u8)> { match token { TokenKind::Equal => Some((1, 0)), TokenKind::Or => Some((2, 3)), - TokenKind::And => Some((4, 5)), - TokenKind::EqualEqual | TokenKind::BangEqual => Some((6, 7)), + TokenKind::Is => Some((4, 5)), + TokenKind::And => Some((6, 7)), + TokenKind::EqualEqual | TokenKind::BangEqual => Some((8, 9)), TokenKind::Less | TokenKind::Greater | TokenKind::GreaterEqual | TokenKind::LessEqual => { - Some((8, 9)) + Some((10, 11)) } - TokenKind::Plus | TokenKind::Minus => Some((10, 11)), - TokenKind::Star | TokenKind::Slash => Some((12, 13)), + TokenKind::Plus | TokenKind::Minus => Some((12, 13)), + TokenKind::Star | TokenKind::Slash => Some((14, 15)), // // UNARY_POWER goes here. // - TokenKind::Dot => Some((16, 17)), + TokenKind::Dot => Some((18, 19)), _ => None, } } @@ -931,22 +953,58 @@ fn expression_with_power(p: &mut CParser, minimum_power: u8) { break; } - // TODO: I don't think this works for other "infix" types, but we'll - // see won't we. - let m = p.start_before(expr); - p.advance(); // Consume the operator - expression_with_power(p, rp); - expr = p.end( - m, - if token == TokenKind::Dot { - TreeKind::MemberAccess - } else { - TreeKind::BinaryExpression - }, - ); + expr = match token { + TokenKind::Dot => member_access(p, expr, rp), + TokenKind::Is => is_expression(p, expr, rp), + _ => binary_expression(p, expr, rp), + }; } } +fn member_access(p: &mut CParser, left: MarkClosed, right_power: u8) -> MarkClosed { + let m = p.start_before(left); + p.advance(); // Consume the operator + expression_with_power(p, right_power); + p.end(m, TreeKind::MemberAccess) +} + +fn binary_expression(p: &mut CParser, left: MarkClosed, right_power: u8) -> MarkClosed { + let m = p.start_before(left); + p.advance(); // Consume the operator + expression_with_power(p, right_power); + p.end(m, TreeKind::BinaryExpression) +} + +fn is_expression(p: &mut CParser, left: MarkClosed, right_power: u8) -> MarkClosed { + let m = p.start_before(left); + p.advance(); // Consume the operator + + // This is hard to do with just, like, no lookahead. + if p.peek() == TokenKind::Identifier && p.peek_next() == TokenKind::Colon { + // This is a variable binding. + variable_binding(p); + } else { + type_expr(p); + } + + // Additional predicates go into the right-hand-side. + if p.eat(TokenKind::And) { + expression_with_power(p, right_power); + } + + p.end(m, TreeKind::IsExpression) +} + +fn variable_binding(p: &mut CParser) { + let m = p.start(); + + p.expect_start(TokenKind::Identifier); + p.expect_start(TokenKind::Colon); + type_expr(p); + + p.end(m, TreeKind::VariableBinding); +} + fn argument_list(p: &mut CParser) { let m = p.start(); diff --git a/fine/src/semantics.rs b/fine/src/semantics.rs index f09d1521..536715b3 100644 --- a/fine/src/semantics.rs +++ b/fine/src/semantics.rs @@ -1854,6 +1854,9 @@ pub fn check(s: &Semantics) { TreeKind::FieldValue => {} TreeKind::SelfParameter => {} TreeKind::SelfReference => {} + + TreeKind::IsExpression => check_is_expression(s, tree), + TreeKind::VariableBinding => check_variable_binding(s, tree), } } } @@ -2029,6 +2032,14 @@ fn check_class_declaration(s: &Semantics, tree: &Tree) { } } +fn check_is_expression(_s: &Semantics, _tree: &Tree) { + // TODO +} + +fn check_variable_binding(_s: &Semantics, _tree: &Tree) { + // TODO +} + #[cfg(test)] mod tests { use super::*; diff --git a/fine/src/tokens.rs b/fine/src/tokens.rs index 67140c88..36a9ec65 100644 --- a/fine/src/tokens.rs +++ b/fine/src/tokens.rs @@ -48,6 +48,7 @@ pub enum TokenKind { If, Import, In, + Is, Let, New, Or, @@ -297,6 +298,9 @@ impl<'a> Tokens<'a> { if ident == "in" { return TokenKind::In; } + if ident == "is" { + return TokenKind::Is; + } } 'l' => { if ident == "let" { @@ -593,6 +597,8 @@ mod tests { (58, New, "new") ); + test_tokens!(more_more_keywords, "in is", (0, In, "in"), (3, Is, "is")); + test_tokens!( strings, r#"'this is a string that\'s great!\r\n' "foo's" 'bar"s' "#, diff --git a/fine/tests/expression/alternates.fine b/fine/tests/expression/alternates.fine index 45dcb868..97bb3007 100644 --- a/fine/tests/expression/alternates.fine +++ b/fine/tests/expression/alternates.fine @@ -33,7 +33,7 @@ class Monster { fun print(x:string) {} fun in_range(weapon: MeleeWeapon or RangedWeapon, distance: f64) { - weapon match { + match weapon { w:RangedWeapon -> distance >= w.minRange and distance <= w.maxRange, _ -> distance == 1 }