[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<()> {
|
fn compile_pattern(c: &mut Compiler, t: TreeRef) -> Option<()> {
|
||||||
let tree = &c.syntax[t];
|
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 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(binding) = tree.child_tree_of_kind(c.syntax, TreeKind::VariableBinding) {
|
||||||
if let Some(variable) = binding.nth_token(0) {
|
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")
|
ice!(c, t, "is cannot make a non-local, non-variable declaration")
|
||||||
};
|
};
|
||||||
|
|
||||||
c.push(Instruction::Dup);
|
// 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));
|
c.push(Instruction::StoreLocal(*index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let type_expr = tree.child_tree_of_kind(c.syntax, TreeKind::TypeExpression)?;
|
if !is_wildcard {
|
||||||
compile_type_expr_eq(c, type_expr.nth_tree(0)?);
|
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 {
|
if let Some(and_index) = and_index {
|
||||||
// This is the tail end of an "AND" expression.
|
let jump_end_index = if is_wildcard {
|
||||||
let jump_true_index = c.push(Instruction::JumpTrue(0));
|
// 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);
|
c.push(Instruction::PushFalse);
|
||||||
let jump_end_index = c.push(Instruction::Jump(0));
|
let jump_end_index = c.push(Instruction::Jump(0));
|
||||||
|
|
||||||
c.patch(jump_true_index, |i| Instruction::JumpTrue(i));
|
c.patch(jump_true_index, |i| Instruction::JumpTrue(i));
|
||||||
|
Some(jump_end_index)
|
||||||
|
};
|
||||||
|
|
||||||
compile_expression(c, tree.nth_tree(and_index + 1)?);
|
compile_expression(c, tree.nth_tree(and_index + 1)?);
|
||||||
c.patch(jump_end_index, |i| Instruction::Jump(i));
|
|
||||||
};
|
// 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
|
OK
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,7 @@ pub enum TreeKind {
|
||||||
MatchExpression,
|
MatchExpression,
|
||||||
MatchBody,
|
MatchBody,
|
||||||
MatchArm,
|
MatchArm,
|
||||||
|
WildcardPattern,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Tree<'a> {
|
pub struct Tree<'a> {
|
||||||
|
|
@ -998,7 +999,11 @@ fn pattern(p: &mut CParser, right_power: u8) {
|
||||||
variable_binding(p);
|
variable_binding(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
type_expr(p);
|
if p.peek() == TokenKind::Underscore {
|
||||||
|
wildcard_pattern(p);
|
||||||
|
} else {
|
||||||
|
type_expr(p);
|
||||||
|
}
|
||||||
|
|
||||||
if p.eat(TokenKind::And) {
|
if p.eat(TokenKind::And) {
|
||||||
expression_with_power(p, right_power);
|
expression_with_power(p, right_power);
|
||||||
|
|
@ -1016,6 +1021,12 @@ fn variable_binding(p: &mut CParser) {
|
||||||
p.end(m, TreeKind::VariableBinding);
|
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) {
|
fn argument_list(p: &mut CParser) {
|
||||||
let m = p.start();
|
let m = p.start();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -857,11 +857,21 @@ impl<'a> Semantics<'a> {
|
||||||
// parse error, don't make more trouble.
|
// parse error, don't make more trouble.
|
||||||
return Environment::error();
|
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.
|
// 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);
|
assert_eq!(tree.kind, TreeKind::Pattern);
|
||||||
|
|
||||||
let Some(binding) = tree.child_tree_of_kind(self.syntax_tree, TreeKind::VariableBinding)
|
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 {
|
let Some(variable) = binding.nth_token(0) else {
|
||||||
return Environment::error();
|
return Environment::error();
|
||||||
};
|
};
|
||||||
let Some(type_expr) = tree.child_of_kind(self.syntax_tree, TreeKind::TypeExpression) else {
|
|
||||||
return Environment::error();
|
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!
|
// 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);
|
let mut env = Environment::new(Some(parent), Location::Local);
|
||||||
env.insert(variable, type_expr);
|
env.insert(variable, variable_decl);
|
||||||
EnvironmentRef::new(env)
|
EnvironmentRef::new(env)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1927,6 +1952,7 @@ pub fn check(s: &Semantics) {
|
||||||
TreeKind::IsExpression => check_is_expression(s, tree),
|
TreeKind::IsExpression => check_is_expression(s, tree),
|
||||||
TreeKind::VariableBinding => check_variable_binding(s, tree),
|
TreeKind::VariableBinding => check_variable_binding(s, tree),
|
||||||
TreeKind::Pattern => check_pattern(s, tree),
|
TreeKind::Pattern => check_pattern(s, tree),
|
||||||
|
TreeKind::WildcardPattern => {}
|
||||||
|
|
||||||
TreeKind::MatchArm => {}
|
TreeKind::MatchArm => {}
|
||||||
TreeKind::MatchBody => {}
|
TreeKind::MatchBody => {}
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ pub enum TokenKind {
|
||||||
Select,
|
Select,
|
||||||
Selff,
|
Selff,
|
||||||
True,
|
True,
|
||||||
|
Underscore,
|
||||||
While,
|
While,
|
||||||
Yield,
|
Yield,
|
||||||
}
|
}
|
||||||
|
|
@ -351,6 +352,11 @@ impl<'a> Tokens<'a> {
|
||||||
return TokenKind::Yield;
|
return TokenKind::Yield;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
'_' => {
|
||||||
|
if ident == "_" {
|
||||||
|
return TokenKind::Underscore;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -605,10 +611,11 @@ mod tests {
|
||||||
|
|
||||||
test_tokens!(
|
test_tokens!(
|
||||||
more_more_keywords,
|
more_more_keywords,
|
||||||
"in is match",
|
"in is match _",
|
||||||
(0, In, "in"),
|
(0, In, "in"),
|
||||||
(3, Is, "is"),
|
(3, Is, "is"),
|
||||||
(6, Match, "match")
|
(6, Match, "match"),
|
||||||
|
(12, Underscore, "_")
|
||||||
);
|
);
|
||||||
|
|
||||||
test_tokens!(
|
test_tokens!(
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,9 @@ fun test() -> f64 {
|
||||||
if b is c:Foo and c.a == 24 {
|
if b is c:Foo and c.a == 24 {
|
||||||
result = result + 10;
|
result = result + 10;
|
||||||
}
|
}
|
||||||
|
if b is c:_ {
|
||||||
|
result = result + 100; // underscore should always match!
|
||||||
|
}
|
||||||
if b is c:Foo {
|
if b is c:Foo {
|
||||||
result = result + c.a; // c should still be in scope!
|
result = result + c.a; // c should still be in scope!
|
||||||
}
|
}
|
||||||
|
|
@ -19,4 +22,4 @@ fun test() -> f64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// @no-errors
|
// @no-errors
|
||||||
// @eval: Float(1001.0)
|
// @eval: Float(1101.0)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue