[fine] Parse is expression, partially

This commit is contained in:
John Doty 2024-02-02 06:32:03 -08:00
parent afa4812074
commit ba5b37f5ff
4 changed files with 98 additions and 23 deletions

View file

@ -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<u32>,
events: Vec<ParseEvent<'a>>,
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();

View file

@ -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::*;

View file

@ -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' "#,

View file

@ -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
}