[fine] Implement the wildcard pattern
This commit is contained in:
parent
e0721d09fc
commit
65ec2d4cca
5 changed files with 104 additions and 25 deletions
|
|
@ -629,6 +629,18 @@ fn compile_is_expression(c: &mut Compiler, tree: &Tree) -> Option<()> {
|
|||
fn compile_pattern(c: &mut Compiler, t: TreeRef) -> Option<()> {
|
||||
let tree = &c.syntax[t];
|
||||
|
||||
// Let's *try* to generate good code in the presence of a wildcard pattern....
|
||||
let is_wildcard = tree
|
||||
.child_tree_of_kind(c.syntax, TreeKind::WildcardPattern)
|
||||
.is_some();
|
||||
|
||||
let type_expr = tree.child_tree_of_kind(c.syntax, TreeKind::TypeExpression);
|
||||
|
||||
let and_index = tree.children.iter().position(|c| match c {
|
||||
Child::Token(t) => t.kind == TokenKind::And,
|
||||
_ => false,
|
||||
});
|
||||
|
||||
// If you have a binding, dup and store now, it is in scope.
|
||||
if let Some(binding) = tree.child_tree_of_kind(c.syntax, TreeKind::VariableBinding) {
|
||||
if let Some(variable) = binding.nth_token(0) {
|
||||
|
|
@ -644,31 +656,51 @@ fn compile_pattern(c: &mut Compiler, t: TreeRef) -> Option<()> {
|
|||
ice!(c, t, "is cannot make a non-local, non-variable declaration")
|
||||
};
|
||||
|
||||
// If we aren't a wildcard or we have an attached predicate then
|
||||
// we will need the value on the stack, otherwise we can discard
|
||||
// it.
|
||||
if and_index.is_some() || !is_wildcard {
|
||||
c.push(Instruction::Dup);
|
||||
}
|
||||
c.push(Instruction::StoreLocal(*index));
|
||||
}
|
||||
}
|
||||
|
||||
let type_expr = tree.child_tree_of_kind(c.syntax, TreeKind::TypeExpression)?;
|
||||
if !is_wildcard {
|
||||
let type_expr = type_expr?;
|
||||
compile_type_expr_eq(c, type_expr.nth_tree(0)?);
|
||||
|
||||
let and_index = tree.children.iter().position(|c| match c {
|
||||
Child::Token(t) => t.kind == TokenKind::And,
|
||||
_ => false,
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(and_index) = and_index {
|
||||
// This is the tail end of an "AND" expression.
|
||||
let jump_end_index = if is_wildcard {
|
||||
// If the pattern was a wildcard then don't bother with this jump
|
||||
// nonsense; we know the pattern matched, all we need to do is
|
||||
// evaluate the predicate.
|
||||
None
|
||||
} else {
|
||||
// Otherwise test the pattern to see if it passed; if it did then
|
||||
// we need to run the predicate. (This is the back half of an AND
|
||||
// expression.)
|
||||
let jump_true_index = c.push(Instruction::JumpTrue(0));
|
||||
|
||||
c.push(Instruction::PushFalse);
|
||||
let jump_end_index = c.push(Instruction::Jump(0));
|
||||
|
||||
c.patch(jump_true_index, |i| Instruction::JumpTrue(i));
|
||||
Some(jump_end_index)
|
||||
};
|
||||
|
||||
compile_expression(c, tree.nth_tree(and_index + 1)?);
|
||||
|
||||
// If we wound up with a jump what needs patching, patch it.
|
||||
if let Some(jump_end_index) = jump_end_index {
|
||||
c.patch(jump_end_index, |i| Instruction::Jump(i));
|
||||
};
|
||||
}
|
||||
} else if is_wildcard {
|
||||
// If there was no predicate *and* the pattern was a wildcard then
|
||||
// I'll just need to push true here.
|
||||
c.push(Instruction::PushTrue);
|
||||
}
|
||||
|
||||
OK
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,6 +163,7 @@ pub enum TreeKind {
|
|||
MatchExpression,
|
||||
MatchBody,
|
||||
MatchArm,
|
||||
WildcardPattern,
|
||||
}
|
||||
|
||||
pub struct Tree<'a> {
|
||||
|
|
@ -998,7 +999,11 @@ fn pattern(p: &mut CParser, right_power: u8) {
|
|||
variable_binding(p);
|
||||
}
|
||||
|
||||
if p.peek() == TokenKind::Underscore {
|
||||
wildcard_pattern(p);
|
||||
} else {
|
||||
type_expr(p);
|
||||
}
|
||||
|
||||
if p.eat(TokenKind::And) {
|
||||
expression_with_power(p, right_power);
|
||||
|
|
@ -1016,6 +1021,12 @@ fn variable_binding(p: &mut CParser) {
|
|||
p.end(m, TreeKind::VariableBinding);
|
||||
}
|
||||
|
||||
fn wildcard_pattern(p: &mut CParser) {
|
||||
let m = p.start();
|
||||
p.expect_start(TokenKind::Underscore);
|
||||
p.end(m, TreeKind::WildcardPattern);
|
||||
}
|
||||
|
||||
fn argument_list(p: &mut CParser) {
|
||||
let m = p.start();
|
||||
|
||||
|
|
|
|||
|
|
@ -857,11 +857,21 @@ impl<'a> Semantics<'a> {
|
|||
// parse error, don't make more trouble.
|
||||
return Environment::error();
|
||||
};
|
||||
self.environment_of_pattern(parent, pattern)
|
||||
|
||||
// The left hand side of the `is` expression is used for wildcard types.
|
||||
let Some(lhs) = tree.nth_tree(0) else {
|
||||
return Environment::error();
|
||||
};
|
||||
self.environment_of_pattern(parent, pattern, lhs)
|
||||
}
|
||||
|
||||
// NOTE: THIS IS CALLED DIRECTLY, NOT VIA `environment_of` TO AVOID CYCLES.
|
||||
fn environment_of_pattern(&self, parent: EnvironmentRef, tree: &Tree) -> EnvironmentRef {
|
||||
fn environment_of_pattern(
|
||||
&self,
|
||||
parent: EnvironmentRef,
|
||||
tree: &Tree,
|
||||
value_expr: TreeRef,
|
||||
) -> EnvironmentRef {
|
||||
assert_eq!(tree.kind, TreeKind::Pattern);
|
||||
|
||||
let Some(binding) = tree.child_tree_of_kind(self.syntax_tree, TreeKind::VariableBinding)
|
||||
|
|
@ -872,15 +882,30 @@ impl<'a> Semantics<'a> {
|
|||
let Some(variable) = binding.nth_token(0) else {
|
||||
return Environment::error();
|
||||
};
|
||||
let Some(type_expr) = tree.child_of_kind(self.syntax_tree, TreeKind::TypeExpression) else {
|
||||
|
||||
let is_wildcard = tree
|
||||
.child_of_kind(self.syntax_tree, TreeKind::WildcardPattern)
|
||||
.is_some();
|
||||
|
||||
let variable_decl = if is_wildcard {
|
||||
// If the variable is bound to a wildcard then treat the value
|
||||
// expression as the declaration for the purpose of determining
|
||||
// type.
|
||||
value_expr
|
||||
} else {
|
||||
// Otherwise the binding is to the type expression which must
|
||||
// match for the variable to have a value.
|
||||
let Some(type_expr) = tree.child_of_kind(self.syntax_tree, TreeKind::TypeExpression)
|
||||
else {
|
||||
return Environment::error();
|
||||
};
|
||||
type_expr
|
||||
};
|
||||
|
||||
// TODO: This binding should be un-assignable! Don't assign to this!
|
||||
// (UNLESS VERY SPECIFIC CIRCUMSTANCES; WHICH ARE COMPLEX)
|
||||
|
||||
let mut env = Environment::new(Some(parent), Location::Local);
|
||||
env.insert(variable, type_expr);
|
||||
env.insert(variable, variable_decl);
|
||||
EnvironmentRef::new(env)
|
||||
}
|
||||
|
||||
|
|
@ -1927,6 +1952,7 @@ pub fn check(s: &Semantics) {
|
|||
TreeKind::IsExpression => check_is_expression(s, tree),
|
||||
TreeKind::VariableBinding => check_variable_binding(s, tree),
|
||||
TreeKind::Pattern => check_pattern(s, tree),
|
||||
TreeKind::WildcardPattern => {}
|
||||
|
||||
TreeKind::MatchArm => {}
|
||||
TreeKind::MatchBody => {}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ pub enum TokenKind {
|
|||
Select,
|
||||
Selff,
|
||||
True,
|
||||
Underscore,
|
||||
While,
|
||||
Yield,
|
||||
}
|
||||
|
|
@ -351,6 +352,11 @@ impl<'a> Tokens<'a> {
|
|||
return TokenKind::Yield;
|
||||
}
|
||||
}
|
||||
'_' => {
|
||||
if ident == "_" {
|
||||
return TokenKind::Underscore;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
|
|
@ -605,10 +611,11 @@ mod tests {
|
|||
|
||||
test_tokens!(
|
||||
more_more_keywords,
|
||||
"in is match",
|
||||
"in is match _",
|
||||
(0, In, "in"),
|
||||
(3, Is, "is"),
|
||||
(6, Match, "match")
|
||||
(6, Match, "match"),
|
||||
(12, Underscore, "_")
|
||||
);
|
||||
|
||||
test_tokens!(
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@ fun test() -> f64 {
|
|||
if b is c:Foo and c.a == 24 {
|
||||
result = result + 10;
|
||||
}
|
||||
if b is c:_ {
|
||||
result = result + 100; // underscore should always match!
|
||||
}
|
||||
if b is c:Foo {
|
||||
result = result + c.a; // c should still be in scope!
|
||||
}
|
||||
|
|
@ -19,4 +22,4 @@ fun test() -> f64 {
|
|||
}
|
||||
|
||||
// @no-errors
|
||||
// @eval: Float(1001.0)
|
||||
// @eval: Float(1101.0)
|
||||
Loading…
Add table
Add a link
Reference in a new issue