From 19a247703cd2814a669fda0a5c5d73d18ee8abfb Mon Sep 17 00:00:00 2001 From: teggotic Date: Tue, 5 May 2020 03:03:38 +0300 Subject: [PATCH 1/3] Add Inner enum destructoring --- src/ast/enum_destructuring.cr | 2 +- src/compilers/case_branch.cr | 42 ++++++++++--- ...tructuring_expected_closing_parentheses.cr | 11 ++++ src/parsers/enum_destructuring.cr | 12 +++- src/type_checkers/case_branch.cr | 59 ++++++++++++------- 5 files changed, 94 insertions(+), 32 deletions(-) create mode 100644 src/messages/enum_destructuring_expected_closing_parentheses.cr diff --git a/src/ast/enum_destructuring.cr b/src/ast/enum_destructuring.cr index 19942e99a..511f1b250 100644 --- a/src/ast/enum_destructuring.cr +++ b/src/ast/enum_destructuring.cr @@ -3,7 +3,7 @@ module Mint class EnumDestructuring < Node getter name, option, parameters - def initialize(@parameters : Array(TypeVariable), + def initialize(@parameters : Array(Node), @option : String, @name : String, @input : Data, diff --git a/src/compilers/case_branch.cr b/src/compilers/case_branch.cr index 03bc02f6f..2c2887609 100644 --- a/src/compilers/case_branch.cr +++ b/src/compilers/case_branch.cr @@ -52,16 +52,10 @@ module Mint ]), } when Ast::EnumDestructuring - variables = - match.parameters.map_with_index do |param, index1| - "const #{js.variable_of(param)} = #{variable}._#{index1}" - end + variables = get_enum_destruct_vars match, variable - name = - js.class_of(lookups[match]) - - { - "#{variable} instanceof #{name}", + res = { + get_enum_destruct_condition(match, variable), js.statements(variables + [expression]), } else @@ -77,5 +71,35 @@ module Mint {nil, expression} end end + + def get_enum_destruct_condition(match, variable) + pp match + name = + js.class_of(lookups[match]? || match) + + condition = "#{variable} instanceof #{name}" + match.parameters.each_with_index do |param, index| + case param + when Ast::EnumDestructuring + condition += " && (" + get_enum_destruct_condition(param, "#{variable}._#{index}") + ")" + else + end + end + condition + end + + def get_enum_destruct_vars(match, variable) + variables = + match.parameters.map_with_index do |param, index1| + vars = ["const #{js.variable_of(param)} = #{variable}._#{index1}"] + case param + when Ast::EnumDestructuring + vars += get_enum_destruct_vars param, variable + else + end + vars + end + variables.flatten + end end end diff --git a/src/messages/enum_destructuring_expected_closing_parentheses.cr b/src/messages/enum_destructuring_expected_closing_parentheses.cr new file mode 100644 index 000000000..09e26a181 --- /dev/null +++ b/src/messages/enum_destructuring_expected_closing_parentheses.cr @@ -0,0 +1,11 @@ +message EnumDestructuringExpectedClosingParentheses do + title "Syntax Error" + + block do + text "I was looing for the" + code ")" + text "after paranthesed parameter in enum destructuring, but found" + code got + text "instead." + end +end diff --git a/src/parsers/enum_destructuring.cr b/src/parsers/enum_destructuring.cr index aaaab5128..396987590 100644 --- a/src/parsers/enum_destructuring.cr +++ b/src/parsers/enum_destructuring.cr @@ -2,6 +2,7 @@ module Mint class Parser syntax_error EnumDestructuringExpectedDoubleColon syntax_error EnumDestructuringExpectedOption + syntax_error EnumDestructuringExpectedClosingParentheses def enum_destructuring start do |start_position| @@ -11,7 +12,16 @@ module Mint option = type_id! EnumDestructuringExpectedOption - parameters = many { type_variable }.compact + parameters = (many do + if char == '(' + parended = char! '(' + param = enum_destructuring || tuple_destructuring || array_destructuring || type_variable + char(')', EnumDestructuringExpectedClosingParentheses) if parended + else + param = type_variable.try(&.as Ast::Node) + end + param + end).compact Ast::EnumDestructuring.new( parameters: parameters, diff --git a/src/type_checkers/case_branch.cr b/src/type_checkers/case_branch.cr index dbc028578..ece3d5aa9 100644 --- a/src/type_checkers/case_branch.cr +++ b/src/type_checkers/case_branch.cr @@ -29,7 +29,7 @@ module Mint } unless Comparer.compare(match, condition) } - node.match.try do |item| + destruct_node = ->(item : Ast::Node) { case item when Ast::ArrayDestructuring raise CaseBranchNotArray, { @@ -83,36 +83,53 @@ module Mint when Ast::EnumDestructuring check_match.call(item) - variables = - item.parameters.map_with_index do |param, index| - entity = - ast.enums.find(&.name.==(item.name)).not_nil! + variables = get_enum_variables item, condition - option = - entity.options.find(&.value.==(item.option)).not_nil! + scope(variables) do + resolve_expression.call + end + else + check_match.call(item) + end + } - option_type = - resolve(option.parameters[index]).not_nil! + node.match.try do |item| + destruct_node.call item + end || resolve_expression.call + end - mapping = {} of String => Checkable + def get_enum_variables(item : Ast::EnumDestructuring, condition) + result = item.parameters.map_with_index do |param, index| + entity = + ast.enums.find(&.name.==(item.name)).not_nil! - entity.parameters.each_with_index do |param2, index2| - mapping[param2.value] = condition.parameters[index2] - end + option = + entity.options.find(&.value.==(item.option)).not_nil! - resolved_type = - Comparer.fill(option_type, mapping) + option_type = + resolve(option.parameters[index]).not_nil! - {param.value, resolved_type.not_nil!, param} - end + case param + when Ast::TypeVariable + mapping = {} of String => Checkable - scope(variables) do - resolve_expression.call + entity.parameters.each_with_index do |param2, index2| + mapping[param2.value] = condition.parameters[index2] end + + resolved_type = + Comparer.fill(option_type, mapping) + + [{param.value, resolved_type.not_nil!, param}] + when Ast::EnumDestructuring + get_enum_variables param, option_type else - check_match.call(item) + puts param.class + raise CaseBranchTupleMismatch end - end || resolve_expression.call + end + + result.flatten end end end From 12a6da07d21e28fdc47cc56ee9ad14f055a1e4a9 Mon Sep 17 00:00:00 2001 From: teggotic Date: Wed, 6 May 2020 08:29:27 +0300 Subject: [PATCH 2/3] Add Array and Tuple inner destructuring --- src/ast.cr | 2 + src/ast/array_destructuring.cr | 4 +- src/ast/enum_destructuring.cr | 4 +- src/ast/tuple_destructuring.cr | 4 +- src/compilers/array_destructuring.cr | 100 +++++++++++++++++------ src/compilers/case_branch.cr | 74 +++-------------- src/compilers/enum_destructuring.cr | 37 +++++++++ src/compilers/tuple_destructuring.cr | 33 ++++++++ src/parsers/array_destructuring.cr | 9 ++- src/parsers/tuple_destructuring.cr | 6 +- src/type_checkers/case_branch.cr | 103 ++++++++++++++---------- src/type_checkers/enum_destructuring.cr | 6 ++ src/type_checkers/scope.cr | 4 +- 13 files changed, 246 insertions(+), 140 deletions(-) create mode 100644 src/compilers/enum_destructuring.cr create mode 100644 src/compilers/tuple_destructuring.cr diff --git a/src/ast.cr b/src/ast.cr index 31559a519..82ce373af 100644 --- a/src/ast.cr +++ b/src/ast.cr @@ -4,6 +4,8 @@ module Mint alias TypeOrVariable = Type | TypeVariable + alias DestructuringType = ArrayDestructuring | EnumDestructuring | TupleDestructuring + alias Expression = ParenthesizedExpression | NegatedExpression | InlineFunction | diff --git a/src/ast/array_destructuring.cr b/src/ast/array_destructuring.cr index dc2598e86..e5454c977 100644 --- a/src/ast/array_destructuring.cr +++ b/src/ast/array_destructuring.cr @@ -1,9 +1,11 @@ module Mint class Ast class ArrayDestructuring < Node + alias ArrayDestructuringItem = DestructuringType | Variable | Spread + getter items - def initialize(@items : Array(Node), + def initialize(@items : Array(ArrayDestructuringItem), @input : Data, @from : Int32, @to : Int32) diff --git a/src/ast/enum_destructuring.cr b/src/ast/enum_destructuring.cr index 511f1b250..dec18e542 100644 --- a/src/ast/enum_destructuring.cr +++ b/src/ast/enum_destructuring.cr @@ -1,9 +1,11 @@ module Mint class Ast class EnumDestructuring < Node + alias EnumDestructuringParameter = DestructuringType | TypeVariable + getter name, option, parameters - def initialize(@parameters : Array(Node), + def initialize(@parameters : Array(EnumDestructuringParameter), @option : String, @name : String, @input : Data, diff --git a/src/ast/tuple_destructuring.cr b/src/ast/tuple_destructuring.cr index 42e29b17a..6e2205402 100644 --- a/src/ast/tuple_destructuring.cr +++ b/src/ast/tuple_destructuring.cr @@ -1,9 +1,11 @@ module Mint class Ast class TupleDestructuring < Node + alias TupleDestructuringParameter = DestructuringType | Variable + getter parameters - def initialize(@parameters : Array(Variable), + def initialize(@parameters : Array(TupleDestructuringParameter), @input : Data, @from : Int32, @to : Int32) diff --git a/src/compilers/array_destructuring.cr b/src/compilers/array_destructuring.cr index fbefa1562..0e98138cc 100644 --- a/src/compilers/array_destructuring.cr +++ b/src/compilers/array_destructuring.cr @@ -1,36 +1,84 @@ module Mint class Compiler - def _compile(node : Ast::ArrayDestructuring, value) + def _compile(node : Ast::ArrayDestructuring, start_variable, index : Int32? = nil, prev_cond_var_name : String? = nil) : {Array(String), String} + variable = "__" + + condition_var_name = prev_cond_var_name || start_variable + condition_var_name += "[#{index}]" if index if node.spread? - statements = [ - "const __ = Array.from(#{value})", - ] - - node - .items - .take_while(&.is_a?(Ast::Variable)) - .each do |var| - statements << "const #{js.variable_of(var)} = __.shift()" - end - - node - .items - .reverse - .take_while(&.is_a?(Ast::Variable)) - .each do |var| - statements << "const #{js.variable_of(var)} = __.pop()" - end - - statements << "const #{js.variable_of(node.items.select(Ast::Spread).first.variable)} = __" - statements + condition = "Array.isArray(#{condition_var_name}) && #{condition_var_name}.length >== #{node.items.size - 1}" else - variables = + condition = "Array.isArray(#{condition_var_name}) && #{condition_var_name}.length === #{node.items.size}" + end + + _compile_destruction_item = ->(item : Ast::Node, var_name : String, index : Int32, vars : Array(String)) { + if item.is_a? Ast::DestructuringType + res = case item + when Ast::ArrayDestructuring + _compile item, var_name, index, condition_var_name + when Ast::TupleDestructuring + _compile item, var_name, index + when Ast::EnumDestructuring + _compile item, var_name, index + else + # ignore + end.not_nil! + condition += " && " + res[1] + vars.concat(res[0]) + end + } + + variables = [ + "const #{variable} = Array.from(#{start_variable})", + ] + ( + unless node.spread? + node.items.map_with_index do |item, index1| + var_name = js.variable_of(item) + vars = [ + "const #{var_name} = #{variable}[#{index1}]", + ] + + _compile_destruction_item.call item, var_name, index1, vars + + vars + end.flatten + else + tmp = [] of Array(String) + node .items - .join(',') { |param| js.variable_of(param) } + .take_while { |item| !item.is_a?(Ast::Spread) } + .each_with_index do |item, index1| + var_name = js.variable_of(item) + vars = [ + "const #{var_name} = #{variable}.shift()", + ] - ["const [#{variables}] = #{value}"] - end + _compile_destruction_item.call item, var_name, index1, vars + + vars + end.try { |vars| tmp.concat(vars) } + + node + .items + .reverse + .take_while { |item| !item.is_a?(Ast::Spread) } + .each_with_index do |item, index1| + var_name = js.variable_of(item) + vars = [ + "const #{var_name} = #{variable}.pop()", + ] + + _compile_destruction_item.call item, var_name, index1, vars + + vars + end.try { |vars| tmp.concat(vars) } + tmp << ["const #{js.variable_of(node.items.select(Ast::Spread).first.variable)} = #{variable}"] + tmp.flatten + end + ) + + {variables, condition} end end end diff --git a/src/compilers/case_branch.cr b/src/compilers/case_branch.cr index 2c2887609..7bbe36afb 100644 --- a/src/compilers/case_branch.cr +++ b/src/compilers/case_branch.cr @@ -21,42 +21,22 @@ module Mint if match = node.match case match - when Ast::ArrayDestructuring - statements = - _compile(match, variable) - - statements << expression - - if match.spread? - { - "Array.isArray(#{variable}) && #{variable}.length >= #{match.items.size - 1}", - js.statements(statements), - } + when Ast::DestructuringType + tmp = case match + when Ast::ArrayDestructuring + _compile match, variable + when Ast::TupleDestructuring + _compile match, variable + when Ast::EnumDestructuring + _compile match, variable else - { - "Array.isArray(#{variable}) && #{variable}.length === #{match.items.size}", - js.statements(statements), - } - end - when Ast::TupleDestructuring - variables = - match - .parameters - .join(',') { |param| js.variable_of(param) } + end.not_nil! { - "Array.isArray(#{variable})", - js.statements([ - "const [#{variables}] = #{variable}", + tmp[1], + js.statements(tmp[0].concat([ expression, - ]), - } - when Ast::EnumDestructuring - variables = get_enum_destruct_vars match, variable - - res = { - get_enum_destruct_condition(match, variable), - js.statements(variables + [expression]), + ])), } else compiled = @@ -71,35 +51,5 @@ module Mint {nil, expression} end end - - def get_enum_destruct_condition(match, variable) - pp match - name = - js.class_of(lookups[match]? || match) - - condition = "#{variable} instanceof #{name}" - match.parameters.each_with_index do |param, index| - case param - when Ast::EnumDestructuring - condition += " && (" + get_enum_destruct_condition(param, "#{variable}._#{index}") + ")" - else - end - end - condition - end - - def get_enum_destruct_vars(match, variable) - variables = - match.parameters.map_with_index do |param, index1| - vars = ["const #{js.variable_of(param)} = #{variable}._#{index1}"] - case param - when Ast::EnumDestructuring - vars += get_enum_destruct_vars param, variable - else - end - vars - end - variables.flatten - end end end diff --git a/src/compilers/enum_destructuring.cr b/src/compilers/enum_destructuring.cr new file mode 100644 index 000000000..837ce6af2 --- /dev/null +++ b/src/compilers/enum_destructuring.cr @@ -0,0 +1,37 @@ +module Mint + class Compiler + def _compile(node : Ast::EnumDestructuring, variable, index : Int32? = nil) : {Array(String), String} + name = + js.class_of(lookups[node]) + + condition_var_name = !index ? variable : "#{variable}._#{index}" + condition = "#{condition_var_name} instanceof #{name}" + + variables = node.parameters.map_with_index do |param, index1| + var_name = js.variable_of(param) + vars = ["const #{var_name} = #{variable}._#{index1}"] + + if param.is_a? Ast::DestructuringType + res = case param + when Ast::ArrayDestructuring + _compile param, var_name, index1 + when Ast::TupleDestructuring + _compile param, var_name, index1 + when Ast::EnumDestructuring + _compile param, var_name, index1 + else + puts param.class + # ignore + end.not_nil! + + condition += " && " + res[1] + vars.concat(res[0]) + end + + vars + end.flatten + + {variables, condition} + end + end +end diff --git a/src/compilers/tuple_destructuring.cr b/src/compilers/tuple_destructuring.cr new file mode 100644 index 000000000..2b2851366 --- /dev/null +++ b/src/compilers/tuple_destructuring.cr @@ -0,0 +1,33 @@ +module Mint + class Compiler + def _compile(node : Ast::TupleDestructuring, variable, index : Int32? = nil) : {Array(String), String} + condition_var_name = !index ? variable : "#{variable}[#{index}]" + condition = "Array.isArray(#{condition_var_name})" + + variables = + node.parameters.map_with_index do |param, index1| + var_name = js.variable_of(param) + vars = ["const #{var_name} = #{variable}[#{index1}]"] + + if param.is_a? Ast::DestructuringType + res = case param + when Ast::ArrayDestructuring + _compile param, var_name, index1 + when Ast::TupleDestructuring + _compile param, var_name, index1 + when Ast::EnumDestructuring + _compile param, var_name, index1 + else + # ignore + end.not_nil! + condition += " && " + res[1] + vars.concat(res[0]) + end + + vars + end.flatten + + {variables, condition} + end + end +end diff --git a/src/parsers/array_destructuring.cr b/src/parsers/array_destructuring.cr index b3644db82..9a9264480 100644 --- a/src/parsers/array_destructuring.cr +++ b/src/parsers/array_destructuring.cr @@ -6,7 +6,7 @@ module Mint start do |start_position| head = start do skip unless char! '[' - value = variable || spread + value = enum_destructuring || tuple_destructuring || array_destructuring || variable || spread whitespace keyword "," whitespace @@ -16,8 +16,8 @@ module Mint skip unless head items = - [head.as(Ast::Node)].concat(list(terminator: ']', separator: ',') do - variable || spread + [head.as(Ast::ArrayDestructuring::ArrayDestructuringItem)].concat(list(terminator: ']', separator: ',') do + value = enum_destructuring || tuple_destructuring || array_destructuring || variable || spread end.compact) whitespace @@ -31,5 +31,8 @@ module Mint input: data) end end + + def parse_destructuring_item + end end end diff --git a/src/parsers/tuple_destructuring.cr b/src/parsers/tuple_destructuring.cr index f4e66e5e8..efaa144fc 100644 --- a/src/parsers/tuple_destructuring.cr +++ b/src/parsers/tuple_destructuring.cr @@ -6,7 +6,7 @@ module Mint start do |start_position| head = start do skip unless char! '{' - value = variable + value = enum_destructuring || tuple_destructuring || array_destructuring || variable whitespace char! ',' whitespace @@ -16,7 +16,9 @@ module Mint skip unless head parameters = [head].concat( - list(terminator: '}', separator: ',') { variable }.compact) + list(terminator: '}', separator: ',') do + enum_destructuring || tuple_destructuring || array_destructuring || variable + end.compact) whitespace diff --git a/src/type_checkers/case_branch.cr b/src/type_checkers/case_branch.cr index ece3d5aa9..6822c0c0d 100644 --- a/src/type_checkers/case_branch.cr +++ b/src/type_checkers/case_branch.cr @@ -29,7 +29,7 @@ module Mint } unless Comparer.compare(match, condition) } - destruct_node = ->(item : Ast::Node) { + node.match.try do |item| case item when Ast::ArrayDestructuring raise CaseBranchNotArray, { @@ -45,19 +45,9 @@ module Mint "node" => node, } if spreads > 1 - variables = - item.items.compact_map do |variable| - case variable - when Ast::Variable - {variable.value, condition.parameters[0], variable} - when Ast::Spread - {variable.variable.value, condition, variable.variable} - else - # ignore - end - end + variables = get_destructuring_variables item, condition - scope(variables) do + scope(variables.not_nil!) do resolve_expression.call end when Ast::TupleDestructuring @@ -72,10 +62,7 @@ module Mint "node" => item, } if item.parameters.size > condition.parameters.size - variables = - item.parameters.map_with_index do |variable, index| - {variable.value, condition.parameters[index], variable} - end + variables = get_destructuring_variables item, condition scope(variables) do resolve_expression.call @@ -83,7 +70,7 @@ module Mint when Ast::EnumDestructuring check_match.call(item) - variables = get_enum_variables item, condition + variables = get_destructuring_variables item, condition scope(variables) do resolve_expression.call @@ -91,43 +78,75 @@ module Mint else check_match.call(item) end - } - - node.match.try do |item| - destruct_node.call item end || resolve_expression.call end - def get_enum_variables(item : Ast::EnumDestructuring, condition) - result = item.parameters.map_with_index do |param, index| + def get_destructuring_variables(item : Ast::DestructuringType, condition) + result = (case item + when Ast::EnumDestructuring + match = resolve item + + raise CaseBranchNotMatchCondition, { + "expected" => condition, + "got" => match, + "node" => item, + } unless Comparer.compare(match, condition) + entity = ast.enums.find(&.name.==(item.name)).not_nil! option = entity.options.find(&.value.==(item.option)).not_nil! - option_type = - resolve(option.parameters[index]).not_nil! + item.parameters.map_with_index do |param, index| + option_type = + resolve(option.parameters[index]).not_nil! - case param - when Ast::TypeVariable - mapping = {} of String => Checkable + case param + when Ast::TypeVariable + mapping = {} of String => Checkable - entity.parameters.each_with_index do |param2, index2| - mapping[param2.value] = condition.parameters[index2] - end + entity.parameters.each_with_index do |param2, index2| + mapping[param2.value] = condition.parameters[index2] + end - resolved_type = - Comparer.fill(option_type, mapping) + resolved_type = + Comparer.fill(option_type, mapping) - [{param.value, resolved_type.not_nil!, param}] - when Ast::EnumDestructuring - get_enum_variables param, option_type - else - puts param.class - raise CaseBranchTupleMismatch - end - end + [{param.value, resolved_type.not_nil!, param.as(Ast::TypeOrVariable)}] + when Ast::DestructuringType + get_destructuring_variables param, option_type + else + # ignore + end + end.compact + when Ast::ArrayDestructuring + item.items.compact_map do |item1| + case item1 + when Ast::Variable + [{item1.value, condition.parameters[0], item1.as(Ast::TypeOrVariable)}] + when Ast::Spread + [{item1.variable.value, condition, item1.variable.as(Ast::TypeOrVariable)}] + when Ast::DestructuringType + get_destructuring_variables item1, resolve(item1) + else + # ignore + end + end.compact + when Ast::TupleDestructuring + item.parameters.map_with_index do |variable, index| + case variable + when Ast::Variable + [{variable.value, condition.parameters[index], variable.as(Ast::TypeOrVariable)}] + when Ast::DestructuringType + get_destructuring_variables variable, resolve(variable) + else + # ignore + end + end.compact + else + # ignore + end) || [] of Array(Tuple(String, Checkable, Ast::TypeOrVariable)) result.flatten end diff --git a/src/type_checkers/enum_destructuring.cr b/src/type_checkers/enum_destructuring.cr index 98f37813f..f44a72eb4 100644 --- a/src/type_checkers/enum_destructuring.cr +++ b/src/type_checkers/enum_destructuring.cr @@ -33,6 +33,12 @@ module Mint "option" => option, "node" => param, } unless option.parameters[index]? + + case param + when Ast::DestructuringType + resolve param + else + end end resolve parent diff --git a/src/type_checkers/scope.cr b/src/type_checkers/scope.cr index c9ed036f6..a025e5ec7 100644 --- a/src/type_checkers/scope.cr +++ b/src/type_checkers/scope.cr @@ -110,7 +110,7 @@ module Mint when Ast::Variable node if target.value == variable when Ast::TupleDestructuring - target.parameters.find(&.value.==(variable)).try do |item| + target.parameters.find { |param| param.is_a?(Ast::Variable) && param.value.==(variable) }.try do |item| {node, target.parameters.index(item).not_nil!} end else @@ -123,7 +123,7 @@ module Mint when Ast::Variable node if target.value == variable when Ast::TupleDestructuring - target.parameters.find(&.value.==(variable)).try do |item| + target.parameters.find { |param| param.is_a?(Ast::Variable) && param.value.==(variable) }.try do |item| {node, target.parameters.index(item).not_nil!} end else From 70bfe03f47bdd7c4ab8841b6dc8c55ed1a029100 Mon Sep 17 00:00:00 2001 From: teggotic Date: Wed, 6 May 2020 11:30:35 +0300 Subject: [PATCH 3/3] Fix Array and Tuple destructuring compilers --- spec/compilers/case_with_array_destructuring | 17 +++-- spec/compilers/case_with_tuple_destructuring | 4 +- src/compilers/array_destructuring.cr | 78 ++++++++------------ src/compilers/case_branch.cr | 22 +++--- src/compilers/destructuring.cr | 16 ++++ src/compilers/enum_destructuring.cr | 18 +---- src/compilers/tuple_destructuring.cr | 18 ++--- src/type_checkers/case_branch.cr | 6 +- src/type_checkers/enum_destructuring.cr | 2 +- 9 files changed, 81 insertions(+), 100 deletions(-) create mode 100644 src/compilers/destructuring.cr diff --git a/spec/compilers/case_with_array_destructuring b/spec/compilers/case_with_array_destructuring index aafad4c33..203022231 100644 --- a/spec/compilers/case_with_array_destructuring +++ b/spec/compilers/case_with_array_destructuring @@ -20,16 +20,19 @@ class A extends _C { } else if (_compare(a, [`a`])) { return `` } else if (Array.isArray(a) && a.length === 1) { - const [b] = a; + const a__arr = Array.from(a); + const b = a__arr[0]; return ``; } else if (Array.isArray(a) && a.length === 2) { - const [c,d] = a; + const a__arr = Array.from(a); + const c = a__arr[0]; + const d = a__arr[1]; return ``; - } else if (Array.isArray(a) && a.length >= 2) { - const __ = Array.from(a); - const e = __.shift(); - const f = __.pop(); - const g = __; + } else if (Array.isArray(a) && a.length >== 2) { + const a__arr = Array.from(a); + const e = a__arr.shift(); + const f = a__arr.pop(); + const g = a__arr; return ``; }; })(); diff --git a/spec/compilers/case_with_tuple_destructuring b/spec/compilers/case_with_tuple_destructuring index 5cfc31e4a..b5f89335c 100644 --- a/spec/compilers/case_with_tuple_destructuring +++ b/spec/compilers/case_with_tuple_destructuring @@ -23,7 +23,9 @@ class A extends _C { } else if (_compare(a, [`A`, 0, true])) { return `A` } else if (Array.isArray(a)) { - const [b,c,d] = a; + const b = a[0]; + const c = a[1]; + const d = a[2]; return b; }; })(); diff --git a/src/compilers/array_destructuring.cr b/src/compilers/array_destructuring.cr index 0e98138cc..03119c2b3 100644 --- a/src/compilers/array_destructuring.cr +++ b/src/compilers/array_destructuring.cr @@ -1,78 +1,58 @@ module Mint class Compiler - def _compile(node : Ast::ArrayDestructuring, start_variable, index : Int32? = nil, prev_cond_var_name : String? = nil) : {Array(String), String} - variable = "__" + private macro compile_item!(initial_var, item = item, index = index1) + var_name = js.variable_of({{ item }}) + vars = [ + {{ initial_var }} + ] - condition_var_name = prev_cond_var_name || start_variable - condition_var_name += "[#{index}]" if index + res = _compile_destructuring {{ item }}, var_name, "#{condition_var_name}[#{{{ index }}}]" + if res + condition += " && " + res[1] + vars.concat(res[0]) + end + + vars + end + + def _compile(node : Ast::ArrayDestructuring, start_variable : String, condition_variable : String? = nil) + variable = "#{start_variable}__arr" + + condition_var_name = condition_variable || start_variable if node.spread? condition = "Array.isArray(#{condition_var_name}) && #{condition_var_name}.length >== #{node.items.size - 1}" else condition = "Array.isArray(#{condition_var_name}) && #{condition_var_name}.length === #{node.items.size}" end - _compile_destruction_item = ->(item : Ast::Node, var_name : String, index : Int32, vars : Array(String)) { - if item.is_a? Ast::DestructuringType - res = case item - when Ast::ArrayDestructuring - _compile item, var_name, index, condition_var_name - when Ast::TupleDestructuring - _compile item, var_name, index - when Ast::EnumDestructuring - _compile item, var_name, index - else - # ignore - end.not_nil! - condition += " && " + res[1] - vars.concat(res[0]) - end - } - variables = [ "const #{variable} = Array.from(#{start_variable})", ] + ( unless node.spread? node.items.map_with_index do |item, index1| - var_name = js.variable_of(item) - vars = [ - "const #{var_name} = #{variable}[#{index1}]", - ] - - _compile_destruction_item.call item, var_name, index1, vars - - vars + compile_item! "const #{var_name} = #{variable}[#{index1}]" end.flatten else tmp = [] of Array(String) - node + start_vars = node .items .take_while { |item| !item.is_a?(Ast::Spread) } - .each_with_index do |item, index1| - var_name = js.variable_of(item) - vars = [ - "const #{var_name} = #{variable}.shift()", - ] - - _compile_destruction_item.call item, var_name, index1, vars - - vars - end.try { |vars| tmp.concat(vars) } + .map_with_index do |item, index1| + compile_item! "const #{var_name} = #{variable}.shift()" + end - node + end_vars = node .items .reverse .take_while { |item| !item.is_a?(Ast::Spread) } - .each_with_index do |item, index1| - var_name = js.variable_of(item) - vars = [ - "const #{var_name} = #{variable}.pop()", - ] + .map_with_index do |item, index1| + compile_item! "const #{var_name} = #{variable}.pop()" + end - _compile_destruction_item.call item, var_name, index1, vars + tmp += start_vars if start_vars + tmp += end_vars if end_vars - vars - end.try { |vars| tmp.concat(vars) } tmp << ["const #{js.variable_of(node.items.select(Ast::Spread).first.variable)} = #{variable}"] tmp.flatten end diff --git a/src/compilers/case_branch.cr b/src/compilers/case_branch.cr index 7bbe36afb..67829cb87 100644 --- a/src/compilers/case_branch.cr +++ b/src/compilers/case_branch.cr @@ -20,18 +20,16 @@ module Mint end if match = node.match - case match - when Ast::DestructuringType - tmp = case match - when Ast::ArrayDestructuring - _compile match, variable - when Ast::TupleDestructuring - _compile match, variable - when Ast::EnumDestructuring - _compile match, variable - else - end.not_nil! - + tmp = case match + when Ast::ArrayDestructuring + _compile match, variable + when Ast::TupleDestructuring + _compile match, variable + when Ast::EnumDestructuring + _compile match, variable + else + end + if tmp { tmp[1], js.statements(tmp[0].concat([ diff --git a/src/compilers/destructuring.cr b/src/compilers/destructuring.cr new file mode 100644 index 000000000..807bf5748 --- /dev/null +++ b/src/compilers/destructuring.cr @@ -0,0 +1,16 @@ +module Mint + class Compiler + def _compile_destructuring(node : Ast::Node, variable : String, condition_variable : String? = nil) + case node + when Ast::ArrayDestructuring + _compile node, variable, condition_variable + when Ast::TupleDestructuring + _compile node, variable, condition_variable + when Ast::EnumDestructuring + _compile node, variable, condition_variable + else + # ignore + end + end + end +end diff --git a/src/compilers/enum_destructuring.cr b/src/compilers/enum_destructuring.cr index 837ce6af2..b84b67a43 100644 --- a/src/compilers/enum_destructuring.cr +++ b/src/compilers/enum_destructuring.cr @@ -1,29 +1,19 @@ module Mint class Compiler - def _compile(node : Ast::EnumDestructuring, variable, index : Int32? = nil) : {Array(String), String} + def _compile(node : Ast::EnumDestructuring, variable : String, condition_variable : String? = nil) name = js.class_of(lookups[node]) - condition_var_name = !index ? variable : "#{variable}._#{index}" + condition_var_name = condition_variable || variable condition = "#{condition_var_name} instanceof #{name}" variables = node.parameters.map_with_index do |param, index1| var_name = js.variable_of(param) vars = ["const #{var_name} = #{variable}._#{index1}"] - if param.is_a? Ast::DestructuringType - res = case param - when Ast::ArrayDestructuring - _compile param, var_name, index1 - when Ast::TupleDestructuring - _compile param, var_name, index1 - when Ast::EnumDestructuring - _compile param, var_name, index1 - else - puts param.class - # ignore - end.not_nil! + res = _compile_destructuring param, var_name, "#{condition_var_name}._#{index1}" + if res condition += " && " + res[1] vars.concat(res[0]) end diff --git a/src/compilers/tuple_destructuring.cr b/src/compilers/tuple_destructuring.cr index 2b2851366..0dda90ed7 100644 --- a/src/compilers/tuple_destructuring.cr +++ b/src/compilers/tuple_destructuring.cr @@ -1,7 +1,7 @@ module Mint class Compiler - def _compile(node : Ast::TupleDestructuring, variable, index : Int32? = nil) : {Array(String), String} - condition_var_name = !index ? variable : "#{variable}[#{index}]" + def _compile(node : Ast::TupleDestructuring, variable : String, condition_variable : String? = nil) : {Array(String), String} + condition_var_name = condition_variable || variable condition = "Array.isArray(#{condition_var_name})" variables = @@ -9,17 +9,9 @@ module Mint var_name = js.variable_of(param) vars = ["const #{var_name} = #{variable}[#{index1}]"] - if param.is_a? Ast::DestructuringType - res = case param - when Ast::ArrayDestructuring - _compile param, var_name, index1 - when Ast::TupleDestructuring - _compile param, var_name, index1 - when Ast::EnumDestructuring - _compile param, var_name, index1 - else - # ignore - end.not_nil! + res = _compile_destructuring param, var_name, "#{condition_var_name}[#{index1}]" + + if res condition += " && " + res[1] vars.concat(res[0]) end diff --git a/src/type_checkers/case_branch.cr b/src/type_checkers/case_branch.cr index 6822c0c0d..034c6071e 100644 --- a/src/type_checkers/case_branch.cr +++ b/src/type_checkers/case_branch.cr @@ -121,14 +121,14 @@ module Mint end end.compact when Ast::ArrayDestructuring - item.items.compact_map do |item1| + item.items.compact.map_with_index do |item1, index| case item1 when Ast::Variable [{item1.value, condition.parameters[0], item1.as(Ast::TypeOrVariable)}] when Ast::Spread [{item1.variable.value, condition, item1.variable.as(Ast::TypeOrVariable)}] when Ast::DestructuringType - get_destructuring_variables item1, resolve(item1) + get_destructuring_variables item1, condition.parameters[index] else # ignore end @@ -139,7 +139,7 @@ module Mint when Ast::Variable [{variable.value, condition.parameters[index], variable.as(Ast::TypeOrVariable)}] when Ast::DestructuringType - get_destructuring_variables variable, resolve(variable) + get_destructuring_variables variable, condition.parameters[index] else # ignore end diff --git a/src/type_checkers/enum_destructuring.cr b/src/type_checkers/enum_destructuring.cr index f44a72eb4..b901b14ad 100644 --- a/src/type_checkers/enum_destructuring.cr +++ b/src/type_checkers/enum_destructuring.cr @@ -35,7 +35,7 @@ module Mint } unless option.parameters[index]? case param - when Ast::DestructuringType + when Ast::EnumDestructuring resolve param else end