Skip to content

Commit

Permalink
Implement aryptn
Browse files Browse the repository at this point in the history
  • Loading branch information
reese authored Jan 7, 2024
1 parent e00d2ab commit 31bcbed
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 10 deletions.
26 changes: 26 additions & 0 deletions fixtures/small/aryptn_actual.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
case ["I will arise", "and go now", "and go to Innisfree"]
in String, String
"and a small cabin build there, of clay and wattles made"
in SmallCabin["clay", "wattles"]
"Nine bean-rows will I have there, a hive for the honey-bee,"
in BeeLoudGlade[String, *, String]
"And live alone in the bee-loud glade"
end

case ["And I shall have some peace there", "for peace", "comes dropping slow"]
in String,;
"Dropping from the veils of the morning to where the cricket sings;"
in Midnight[*]
"There midnight's all a glimmer, and noon a purple glow,"
in 1, *, 2
"And evening full of the linnet's wings."
end

case []
in [*]
0
in []
1
else
2
end
26 changes: 26 additions & 0 deletions fixtures/small/aryptn_expected.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
case ["I will arise", "and go now", "and go to Innisfree"]
in [String, String]
"and a small cabin build there, of clay and wattles made"
in SmallCabin["clay", "wattles"]
"Nine bean-rows will I have there, a hive for the honey-bee,"
in BeeLoudGlade[String, *, String]
"And live alone in the bee-loud glade"
end

case ["And I shall have some peace there", "for peace", "comes dropping slow"]
in [String, *]
"Dropping from the veils of the morning to where the cricket sings;"
in Midnight[*]
"There midnight's all a glimmer, and noon a purple glow,"
in [1, *, 2]
"And evening full of the linnet's wings."
end

case []
in [*]
0
in []
1
else
2
end
9 changes: 9 additions & 0 deletions librubyfmt/rubyfmt_lib.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def initialize(file_data)
"next" => [],
"return" => [],
"when" => [],
"in" => [],
"case" => [],
"yield" => [],
"break" => [],
Expand Down Expand Up @@ -374,6 +375,10 @@ def on_when(cond, body, tail)
[:when, cond, body, tail, start_end_for_keyword('when')]
end

def on_in(cond, body, tail)
[:in, cond, body, tail, start_end_for_keyword('in')]
end

def on_case(cond, body)
[:case, cond, body, start_end_for_keyword('case')]
end
Expand All @@ -386,6 +391,10 @@ def on_break(arg)
[:break, arg, start_end_for_keyword('break')]
end

def on_var_field(*args)
with_lineno { super }
end

def on_tlambda(*args)
@tlambda_stack << lineno
super
Expand Down
90 changes: 87 additions & 3 deletions librubyfmt/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,10 @@ pub fn format_mlhs(ps: &mut dyn ConcreteParserState, mlhs: MLhs) {
}

fn bind_var_field(ps: &mut dyn ConcreteParserState, vf: &VarField) {
ps.bind_variable((vf.1).clone().to_local_string())
ps.bind_variable((vf.1).clone().expect(
"Var ref fields are only nilable in pattern matching, and we don't do var binding there",
)
.to_local_string())
}

fn bind_ident(ps: &mut dyn ConcreteParserState, id: &Ident) {
Expand Down Expand Up @@ -1732,7 +1735,12 @@ pub fn format_top_const_field(ps: &mut dyn ConcreteParserState, tcf: TopConstFie

pub fn format_var_field(ps: &mut dyn ConcreteParserState, vf: VarField) {
let left = vf.1;
format_var_ref_type(ps, left);
if let Some(var_ref_type) = left {
format_var_ref_type(ps, var_ref_type);
} else {
// Nil var fields are used for "*" matchers in patterns
ps.emit_ident("*".to_string());
}
}

pub fn format_aref_field(ps: &mut dyn ConcreteParserState, af: ArefField) {
Expand Down Expand Up @@ -3337,7 +3345,10 @@ pub fn format_case(ps: &mut dyn ConcreteParserState, case: Case) {
ps.with_start_of_line(
true,
Box::new(|ps| {
format_when_or_else(ps, WhenOrElse::When(tail));
match tail {
WhenOrIn::When(when) => format_when_or_else(ps, WhenOrElse::When(when)),
WhenOrIn::In(in_node) => format_in_or_else(ps, InOrElse::In(in_node)),
}
ps.emit_end();
}),
);
Expand All @@ -3349,6 +3360,78 @@ pub fn format_case(ps: &mut dyn ConcreteParserState, case: Case) {
ps.on_line(case.3 .1);
}

fn format_in_or_else(ps: &mut dyn ConcreteParserState, in_or_else: InOrElse) {
match in_or_else {
InOrElse::In(in_node) => {
let In(_, pattern, body, next_in_or_else, start_end) = in_node;
ps.on_line(start_end.start_line());
ps.emit_indent();
ps.emit_in_keyword();
ps.emit_space();

ps.with_start_of_line(
false,
Box::new(|ps| {
format_pattern(ps, pattern);
}),
);

ps.new_block(Box::new(|ps| {
ps.with_start_of_line(
true,
Box::new(|ps| {
ps.emit_newline();
for expr in body {
format_expression(ps, expr);
}
}),
);
}));

if let Some(in_or_else) = next_in_or_else {
format_in_or_else(ps, *in_or_else);
}
}
InOrElse::Else(else_node) => {
// `else` blocks are the same for `when` and `in` case statements
format_when_or_else(ps, WhenOrElse::Else(else_node));
}
}
}

fn format_pattern(ps: &mut dyn ConcreteParserState, pattern_node: PatternNode) {
match pattern_node {
PatternNode::Aryptn(aryptn) => format_aryptn(ps, aryptn),
}
}

fn format_aryptn(ps: &mut dyn ConcreteParserState, aryptn: Aryptn) {
let Aryptn(_, maybe_collection_name, maybe_pre_star_list, maybe_star, maybe_post_star_list) =
aryptn;
if let Some(collection_name) = maybe_collection_name {
format_var_ref(ps, collection_name);
}
ps.breakable_of(
BreakableDelims::for_array(),
Box::new(|ps| {
let mut vals = Vec::new();
if let Some(pre_star_list) = maybe_pre_star_list {
vals.append(&mut pre_star_list.clone());
}
if let Some(star) = maybe_star {
vals.push(Expression::Ident(Ident::new(
"*".to_string(),
LineCol(star.2.start_line(), 0),
)));
}
if let Some(post_star_list) = maybe_post_star_list {
vals.append(&mut post_star_list.clone());
}
format_list_like_thing_items(ps, vals, None, false);
}),
);
}

pub fn format_retry(ps: &mut dyn ConcreteParserState, r: Retry) {
format_keyword(
ps,
Expand Down Expand Up @@ -3772,6 +3855,7 @@ pub fn format_expression(ps: &mut dyn ConcreteParserState, expression: Expressio
Expression::IfMod(wm) => format_multilinable_mod(ps, wm.1, wm.2, "if".to_string()),
Expression::UnlessMod(um) => format_multilinable_mod(ps, um.1, um.2, "unless".to_string()),
Expression::Case(c) => format_case(ps, c),
Expression::Aryptn(arrayptn) => format_aryptn(ps, arrayptn),
Expression::Retry(r) => format_retry(ps, r),
Expression::Redo(r) => format_redo(ps, r),
Expression::SClass(sc) => format_sclass(ps, sc),
Expand Down
7 changes: 7 additions & 0 deletions librubyfmt/src/parser_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ where
fn emit_class_keyword(&mut self);
fn emit_do_keyword(&mut self);
fn emit_when_keyword(&mut self);
fn emit_in_keyword(&mut self);
fn emit_case_keyword(&mut self);
fn emit_rescue(&mut self);
fn emit_open_square_bracket(&mut self);
Expand Down Expand Up @@ -710,6 +711,12 @@ impl ConcreteParserState for BaseParserState {
});
}

fn emit_in_keyword(&mut self) {
self.push_concrete_token(ConcreteLineToken::Keyword {
keyword: "in".to_string(),
});
}

fn emit_do_keyword(&mut self) {
self.push_concrete_token(ConcreteLineToken::DoKeyword);
}
Expand Down
62 changes: 55 additions & 7 deletions librubyfmt/src/ripper_tree_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ pub enum Expression {
IfMod(IfMod),
UnlessMod(UnlessMod),
Case(Case),
Aryptn(Aryptn),
Retry(Retry),
Redo(Redo),
SClass(SClass),
Expand Down Expand Up @@ -266,6 +267,18 @@ impl Expression {
| Expression::EndBlock(EndBlock(_, exprs)) => {
exprs.first().and_then(|expr| expr.start_line())
}
Expression::Aryptn(Aryptn(_, _, exprs, star, ..)) => exprs
.as_ref()
.map(|exprs| exprs.first().map(|expr| expr.start_line()).flatten())
.flatten()
.or_else(|| {
Some(
star.as_ref()
.expect("If first exprs list is empty, there must be a * pattern")
.2
.start_line(),
)
}),
// Pick the first of either expression, since these can be e.g. `foo..bar` or `foo..` or `..bar`
Expression::Dot2(Dot2(_, maybe_first_expr, maybe_second_expr))
| Expression::Dot3(Dot3(_, maybe_first_expr, maybe_second_expr)) => maybe_first_expr
Expand Down Expand Up @@ -316,7 +329,7 @@ pub enum MLhsInner {
impl MLhsInner {
pub fn start_line(&self) -> Option<u64> {
match self {
MLhsInner::VarField(VarField(_, var_ref_type)) => Some(var_ref_type.start_line()),
MLhsInner::VarField(VarField(.., start_end)) => Some(start_end.start_line()),
MLhsInner::Field(Field(_, expr, ..)) => expr.start_line(),
MLhsInner::RestParam(RestParam(.., rest_param_assignable)) => rest_param_assignable
.as_ref()
Expand Down Expand Up @@ -599,9 +612,7 @@ impl RestParamAssignable {
match self {
RestParamAssignable::ArefField(ArefField(.., linecol))
| RestParamAssignable::Ident(Ident(.., linecol)) => Some(linecol.0),
RestParamAssignable::VarField(VarField(.., var_ref_type)) => {
Some(var_ref_type.start_line())
}
RestParamAssignable::VarField(VarField(.., start_end)) => Some(start_end.start_line()),
}
}
}
Expand All @@ -622,7 +633,7 @@ pub enum Assignable {
impl Assignable {
pub fn start_line(&self) -> Option<u64> {
match self {
Assignable::VarField(VarField(.., var_ref_type)) => Some(var_ref_type.start_line()),
Assignable::VarField(VarField(.., start_end)) => Some(start_end.start_line()),
Assignable::RestParam(RestParam(.., rest_param_assignable)) => rest_param_assignable
.as_ref()
.and_then(|rpa| rpa.start_line()),
Expand Down Expand Up @@ -661,7 +672,7 @@ pub struct ConstPathField(pub const_path_field_tag, pub Box<Expression>, pub Con

def_tag!(var_field_tag, "var_field");
#[derive(Deserialize, Debug, Clone)]
pub struct VarField(pub var_field_tag, pub VarRefType);
pub struct VarField(pub var_field_tag, pub Option<VarRefType>, pub StartEnd);

def_tag!(field_tag, "field");
#[derive(Deserialize, Debug, Clone)]
Expand Down Expand Up @@ -2340,7 +2351,7 @@ def_tag!(case_tag, "case");
pub struct Case(
case_tag,
pub Option<Box<Expression>>,
pub When,
pub WhenOrIn,
pub StartEnd,
);

Expand All @@ -2354,12 +2365,49 @@ pub struct When(
pub StartEnd,
);

#[derive(RipperDeserialize, Debug, Clone)]
pub enum WhenOrIn {
When(When),
In(In),
}

def_tag!(in_tag, "in");
#[derive(Deserialize, Debug, Clone)]
pub struct In(
pub in_tag,
pub PatternNode, // current pattern
pub Vec<Expression>, // body
pub Option<Box<InOrElse>>, // next in/else
pub StartEnd,
);

#[derive(RipperDeserialize, Debug, Clone)]
pub enum PatternNode {
Aryptn(Aryptn),
}

def_tag!(arrayptn_tag, "aryptn");
#[derive(Deserialize, Debug, Clone)]
pub struct Aryptn(
pub arrayptn_tag,
pub Option<VarRef>, // Container type, e.g. `in Foo["a", "b"]`
pub Option<Vec<Expression>>, // list of values before the first *
pub Option<VarField>, // "*" pattern
pub Option<Vec<Expression>>, // list of values the first *
);

#[derive(RipperDeserialize, Debug, Clone)]
pub enum WhenOrElse {
When(When),
Else(CaseElse),
}

#[derive(RipperDeserialize, Debug, Clone)]
pub enum InOrElse {
In(In),
Else(CaseElse),
}

def_tag!(case_else_tag, "else");
#[derive(Deserialize, Debug, Clone)]
pub struct CaseElse(case_else_tag, pub Vec<Expression>, pub StartEnd);
Expand Down

0 comments on commit 31bcbed

Please sign in to comment.