Skip to content

Commit

Permalink
Merge pull request #280 from candy-lang/or-patterns
Browse files Browse the repository at this point in the history
Or patterns
  • Loading branch information
JonasWanke committed Feb 6, 2023
2 parents 8030dde + 734316d commit e5ba2f1
Show file tree
Hide file tree
Showing 15 changed files with 591 additions and 74 deletions.
103 changes: 94 additions & 9 deletions compiler/src/compiler/ast.rs
@@ -1,9 +1,11 @@
use super::{cst_to_ast::CstToAst, error::CompilerError, utils::AdjustCasingOfFirstLetter};
use super::{cst, cst_to_ast::CstToAst, error::CompilerError, utils::AdjustCasingOfFirstLetter};
use crate::module::Module;
use itertools::Itertools;
use num_bigint::BigUint;
use rustc_hash::FxHashMap;
use std::{
fmt::{self, Display, Formatter},
num::NonZeroUsize,
ops::Deref,
};

Expand Down Expand Up @@ -41,6 +43,14 @@ pub struct Ast {
pub kind: AstKind,
}

impl Deref for Ast {
type Target = AstKind;

fn deref(&self) -> &Self::Target {
&self.kind
}
}

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum AstKind {
Int(Int),
Expand All @@ -56,6 +66,7 @@ pub enum AstKind {
Assignment(Assignment),
Match(Match),
MatchCase(MatchCase),
OrPattern(OrPattern),
Error {
/// The child may be set if it still makes sense to continue working
/// with the error-containing subtree.
Expand Down Expand Up @@ -126,6 +137,8 @@ pub struct MatchCase {
pub pattern: Box<Ast>,
pub body: Vec<Ast>,
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct OrPattern(pub Vec<Ast>);

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct AstString {
Expand All @@ -148,6 +161,11 @@ pub enum AstError {
ListItemWithoutComma,
ListWithNonListItem,
ListWithoutClosingParenthesis,
OrPatternIsMissingIdentifiers {
identifier: String,
number_of_missing_captures: NonZeroUsize,
all_captures: Vec<cst::Id>,
},
ParenthesizedInPattern,
ParenthesizedWithoutClosingParenthesis,
PatternContainsInvalidExpression,
Expand Down Expand Up @@ -186,6 +204,7 @@ impl FindAst for Ast {
AstKind::Assignment(assignment) => assignment.find(id),
AstKind::Match(match_) => match_.find(id),
AstKind::MatchCase(match_case) => match_case.find(id),
AstKind::OrPattern(or_pattern) => or_pattern.find(id),
AstKind::Error { child, .. } => child.as_ref().and_then(|child| child.find(id)),
}
}
Expand Down Expand Up @@ -242,12 +261,66 @@ impl FindAst for MatchCase {
self.pattern.find(id).or_else(|| self.body.find(id))
}
}
impl FindAst for OrPattern {
fn find(&self, id: &Id) -> Option<&Ast> {
self.0.find(id)
}
}
impl FindAst for Vec<Ast> {
fn find(&self, id: &Id) -> Option<&Ast> {
self.iter().find_map(|ast| ast.find(id))
}
}

impl AstKind {
pub fn captured_identifiers(&self) -> FxHashMap<String, Vec<Id>> {
let mut captured_identifiers = FxHashMap::default();
self.captured_identifiers_helper(&mut captured_identifiers);
captured_identifiers
}
fn captured_identifiers_helper(&self, captured_identifiers: &mut FxHashMap<String, Vec<Id>>) {
match self {
AstKind::Int(_) | AstKind::Text(_) | AstKind::TextPart(_) => {}
AstKind::Identifier(Identifier(identifier)) => {
let entry = captured_identifiers
.entry(identifier.value.clone())
.or_insert_with(Vec::new);
entry.push(identifier.id.to_owned())
}
AstKind::Symbol(_) => {}
AstKind::List(List(list)) => {
for item in list {
item.captured_identifiers_helper(captured_identifiers)
}
}
AstKind::Struct(Struct { fields }) => {
for (key, value) in fields {
if let Some(key) = key {
key.captured_identifiers_helper(captured_identifiers)
}
value.captured_identifiers_helper(captured_identifiers)
}
}
AstKind::StructAccess(_)
| AstKind::Lambda(_)
| AstKind::Call(_)
| AstKind::Assignment(_)
| AstKind::Match(_)
| AstKind::MatchCase(_) => {}
AstKind::OrPattern(OrPattern(patterns)) => {
for pattern in patterns {
pattern.captured_identifiers_helper(captured_identifiers);
}
}
AstKind::Error { child, .. } => {
if let Some(child) = child {
child.captured_identifiers_helper(captured_identifiers)
}
}
}
}
}

pub trait CollectErrors {
fn collect_errors(self, errors: &mut Vec<CompilerError>);
}
Expand All @@ -272,8 +345,8 @@ impl CollectErrors for Ast {
value.collect_errors(errors);
}
}
AstKind::StructAccess(struct_access) => {
struct_access.struct_.collect_errors(errors);
AstKind::StructAccess(StructAccess { struct_, key: _ }) => {
struct_.collect_errors(errors);
}
AstKind::Lambda(lambda) => lambda.body.collect_errors(errors),
AstKind::Call(call) => call.arguments.collect_errors(errors),
Expand All @@ -286,13 +359,18 @@ impl CollectErrors for Ast {
}
}
},
AstKind::Match(match_) => {
match_.expression.collect_errors(errors);
match_.cases.collect_errors(errors);
AstKind::Match(Match { expression, cases }) => {
expression.collect_errors(errors);
cases.collect_errors(errors);
}
AstKind::MatchCase(MatchCase { pattern, body }) => {
pattern.collect_errors(errors);
body.collect_errors(errors);
}
AstKind::MatchCase(match_case) => {
match_case.pattern.collect_errors(errors);
match_case.body.collect_errors(errors);
AstKind::OrPattern(OrPattern(patterns)) => {
for pattern in patterns {
pattern.collect_errors(errors);
}
}
AstKind::Error {
child,
Expand Down Expand Up @@ -412,6 +490,13 @@ impl Display for Ast {
.map(|line| line.to_string())
.join(" \n"),
),
AstKind::OrPattern(OrPattern(patterns)) => {
write!(
f,
"{}",
patterns.iter().map(|it| it.to_string()).join(" | "),
)
}
AstKind::Error { child, errors } => {
write!(
f,
Expand Down
16 changes: 12 additions & 4 deletions compiler/src/compiler/ast_to_hir.rs
@@ -1,7 +1,7 @@
use super::{
ast::{
self, Assignment, Ast, AstKind, AstString, Call, Identifier, Int, List, MatchCase, Struct,
StructAccess, Symbol, Text, TextPart,
self, Assignment, Ast, AstKind, AstString, Call, Identifier, Int, List, MatchCase,
OrPattern, Struct, StructAccess, Symbol, Text, TextPart,
},
cst::{self, CstDb},
cst_to_ast::CstToAst,
Expand Down Expand Up @@ -348,6 +348,9 @@ impl<'a> Context<'a> {
AstKind::MatchCase(_) => {
unreachable!("Match cases should be handled in match directly.")
}
AstKind::OrPattern(_) => {
unreachable!("Or patterns should be handled in `PatternContext`.")
}
AstKind::Error { child, errors } => {
let child = child.as_ref().map(|child| self.compile_single(child));
self.push(
Expand Down Expand Up @@ -657,9 +660,7 @@ impl<'a> Context<'a> {
}
unreachable!()
}
}

impl<'a> Context<'a> {
fn generate_sparkles(&mut self) {
let mut sparkles_map = FxHashMap::default();

Expand Down Expand Up @@ -823,6 +824,13 @@ impl PatternContext {
"AST pattern can't contain struct access, lambda, call, assignment, match, or match case."
)
}
AstKind::OrPattern(OrPattern(patterns)) => {
let patterns = patterns
.iter()
.map(|pattern| self.compile_pattern(pattern))
.collect();
Pattern::Or(patterns)
}
AstKind::Error { child, errors, .. } => {
let child = child
.as_ref()
Expand Down
40 changes: 40 additions & 0 deletions compiler/src/compiler/cst.rs
Expand Up @@ -141,6 +141,10 @@ pub enum CstKind {
arrow: Box<Cst>,
body: Vec<Cst>,
},
OrPattern {
left: Box<Cst>,
right: Vec<(Cst, Cst)>,
},
Lambda {
opening_curly_brace: Box<Cst>,
parameters_and_arrow: Option<(Vec<Cst>, Box<Cst>)>,
Expand Down Expand Up @@ -336,6 +340,14 @@ impl Display for Cst {
}
Ok(())
}
CstKind::OrPattern { left, right } => {
left.fmt(f)?;
for (bar, right) in right {
bar.fmt(f)?;
right.fmt(f)?;
}
Ok(())
}
CstKind::Lambda {
opening_curly_brace,
parameters_and_arrow,
Expand Down Expand Up @@ -559,6 +571,18 @@ impl UnwrapWhitespaceAndComment for Cst {
arrow: Box::new(arrow.unwrap_whitespace_and_comment()),
body: body.unwrap_whitespace_and_comment(),
},
CstKind::OrPattern { left, right } => CstKind::OrPattern {
left: Box::new(left.unwrap_whitespace_and_comment()),
right: right
.iter()
.map(|(bar, right)| {
(
bar.unwrap_whitespace_and_comment(),
right.unwrap_whitespace_and_comment(),
)
})
.collect(),
},
CstKind::Lambda {
opening_curly_brace,
parameters_and_arrow,
Expand Down Expand Up @@ -742,6 +766,12 @@ impl TreeWithIds for Cst {
.find(id)
.or_else(|| arrow.find(id))
.or_else(|| body.find(id)),
CstKind::OrPattern { left, right } => left.find(id).or_else(|| {
// TODO: use binary search
right
.iter()
.find_map(|(bar, right)| bar.find(id).or_else(|| right.find(id)))
}),
CstKind::Lambda {
opening_curly_brace,
parameters_and_arrow,
Expand Down Expand Up @@ -894,6 +924,16 @@ impl TreeWithIds for Cst {
.or_else(|| body.find_by_offset(offset)),
false,
),
CstKind::OrPattern { left, right } => (
left.find_by_offset(offset).or_else(|| {
// TODO: use binary search
right.iter().find_map(|(bar, right)| {
bar.find_by_offset(offset)
.or_else(|| right.find_by_offset(offset))
})
}),
false,
),
CstKind::Lambda { body, .. } => (body.find_by_offset(offset), false),
CstKind::Assignment {
name_or_pattern,
Expand Down

1 comment on commit e5ba2f1

@jwbot
Copy link
Collaborator

@jwbot jwbot commented on e5ba2f1 Feb 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Compiler

Benchmark suite Current: e5ba2f1 Previous: 8030dde Ratio
Time: Compiler/hello_world 19857238 ns/iter (± 286491) 24109145 ns/iter (± 349937) 0.82
Time: Compiler/fibonacci 562901787 ns/iter (± 4486740) 538104078 ns/iter (± 5033559) 1.05
Time: VM Runtime/hello_world 15286404 ns/iter (± 388966) 19013214 ns/iter (± 343731) 0.80
Time: VM Runtime/fibonacci/15 188924568 ns/iter (± 1935547) 202563219 ns/iter (± 1162003) 0.93

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.