Add else case and tests

This commit is contained in:
Alexander Zakharenko 2018-05-11 19:26:55 +03:00
parent f5cf7d8179
commit d257bbd52f
17 changed files with 881 additions and 699 deletions

View file

@ -2,7 +2,7 @@
// This CSharp output file generated by Gardens Point LEX
// Version: 1.1.3.301
// Machine: ALEXANDER-PC
// DateTime: 11.05.2018 13:59:31
// DateTime: 11.05.2018 18:06:46
// UserName: Alexander
// GPLEX input file <ABCPascal.lex>
// GPLEX frame file <embedded resource>

View file

@ -2537,9 +2537,9 @@ if_stmt
;
match_with
: tkMatch expr_l1 tkWith pattern_cases tkEnd
: tkMatch expr_l1 tkWith pattern_cases else_case tkEnd
{
$$ = new match_with($2, $4 as pattern_cases);
$$ = new match_with($2, $4 as pattern_cases, $5 as statement, @$);
}
;
@ -2559,11 +2559,11 @@ pattern_case
{ $$ = new empty_statement(); }
| pattern tkWhen expr_l1 tkColon unlabelled_stmt
{
$$ = new pattern_case($1 as pattern_node, $5 as statement, $3);
$$ = new pattern_case($1 as pattern_node, $5 as statement, $3, @$);
}
| pattern tkColon unlabelled_stmt
{
$$ = new pattern_case($1 as pattern_node, $3 as statement, null);
$$ = new pattern_case($1 as pattern_node, $3 as statement, null, @$);
}
;

File diff suppressed because it is too large Load diff

View file

@ -3787,6 +3787,7 @@ namespace PascalABCCompiler.SyntaxTree
pre_do_visit(_match_with);
visit(match_with.expr);
visit(match_with.case_list);
visit(match_with.defaultAction);
post_do_visit(_match_with);
}

View file

@ -3986,6 +3986,7 @@ namespace PascalABCCompiler.SyntaxTree
read_statement(_match_with);
_match_with.expr = _read_node() as expression;
_match_with.case_list = _read_node() as pattern_cases;
_match_with.defaultAction = _read_node() as statement;
}

View file

@ -6239,6 +6239,15 @@ namespace PascalABCCompiler.SyntaxTree
bw.Write((byte)1);
_match_with.case_list.visit(this);
}
if (_match_with.defaultAction == null)
{
bw.Write((byte)0);
}
else
{
bw.Write((byte)1);
_match_with.defaultAction.visit(this);
}
}

View file

@ -47878,25 +47878,28 @@ namespace PascalABCCompiler.SyntaxTree
///<summary>
///Конструктор с параметрами.
///</summary>
public match_with(expression _expr,pattern_cases _case_list)
public match_with(expression _expr,pattern_cases _case_list,statement _defaultAction)
{
this._expr=_expr;
this._case_list=_case_list;
this._defaultAction=_defaultAction;
FillParentsInDirectChilds();
}
///<summary>
///Конструктор с параметрами.
///</summary>
public match_with(expression _expr,pattern_cases _case_list,SourceContext sc)
public match_with(expression _expr,pattern_cases _case_list,statement _defaultAction,SourceContext sc)
{
this._expr=_expr;
this._case_list=_case_list;
this._defaultAction=_defaultAction;
source_context = sc;
FillParentsInDirectChilds();
}
protected expression _expr;
protected pattern_cases _case_list;
protected statement _defaultAction;
///<summary>
///
@ -47928,6 +47931,21 @@ namespace PascalABCCompiler.SyntaxTree
}
}
///<summary>
///
///</summary>
public statement defaultAction
{
get
{
return _defaultAction;
}
set
{
_defaultAction=value;
}
}
/// <summary> Создает копию узла </summary>
public override syntax_tree_node Clone()
@ -47951,6 +47969,11 @@ namespace PascalABCCompiler.SyntaxTree
copy.case_list = (pattern_cases)case_list.Clone();
copy.case_list.Parent = copy;
}
if (defaultAction != null)
{
copy.defaultAction = (statement)defaultAction.Clone();
copy.defaultAction.Parent = copy;
}
return copy;
}
@ -47969,6 +47992,8 @@ namespace PascalABCCompiler.SyntaxTree
expr.Parent = this;
if (case_list != null)
case_list.Parent = this;
if (defaultAction != null)
defaultAction.Parent = this;
}
///<summary> Заполняет поля Parent во всем поддереве </summary>
@ -47978,6 +48003,7 @@ namespace PascalABCCompiler.SyntaxTree
attributes?.FillParentsInAllChilds();
expr?.FillParentsInAllChilds();
case_list?.FillParentsInAllChilds();
defaultAction?.FillParentsInAllChilds();
}
///<summary>
@ -47987,7 +48013,7 @@ namespace PascalABCCompiler.SyntaxTree
{
get
{
return 2;
return 3;
}
}
///<summary>
@ -47997,7 +48023,7 @@ namespace PascalABCCompiler.SyntaxTree
{
get
{
return 2;
return 3;
}
}
///<summary>
@ -48015,6 +48041,8 @@ namespace PascalABCCompiler.SyntaxTree
return expr;
case 1:
return case_list;
case 2:
return defaultAction;
}
return null;
}
@ -48030,6 +48058,9 @@ namespace PascalABCCompiler.SyntaxTree
case 1:
case_list = (pattern_cases)value;
break;
case 2:
defaultAction = (statement)value;
break;
}
}
}

View file

@ -382,6 +382,11 @@ namespace PascalABCCompiler.SyntaxTree
return new bin_expr(left, right, Operators.Less);
}
public static bin_expr LogicalAnd(expression left, expression right)
{
return new bin_expr(left, right, Operators.LogicalAND);
}
public override string ToString()
{
return string.Format("{0} {2} {1}", left, right, OperatorServices.ToString(operation_type, LanguageId.PascalABCNET));

View file

@ -2901,6 +2901,7 @@
<Fields>
<SyntaxField Name="expr" SyntaxType="expression" />
<SyntaxField Name="case_list" SyntaxType="pattern_cases" />
<SyntaxField Name="defaultAction" SyntaxType="statement" />
</Fields>
<Methods />
<Tags>
@ -3680,6 +3681,7 @@
<HelpData Key="loop_stmt.stmt" Value="" />
<HelpData Key="match_with" Value="" />
<HelpData Key="match_with.case_list" Value="" />
<HelpData Key="match_with.defaultAction" Value="" />
<HelpData Key="match_with.expr" Value="" />
<HelpData Key="matching_expression" Value="" />
<HelpData Key="matching_expression.left" Value="" />
@ -3841,6 +3843,7 @@
<HelpData Key="pattern_case" Value="" />
<HelpData Key="pattern_case.case_action" Value="" />
<HelpData Key="pattern_case.condition" Value="" />
<HelpData Key="pattern_case.isDefault" Value="" />
<HelpData Key="pattern_case.pattern" Value="" />
<HelpData Key="pattern_cases" Value="" />
<HelpData Key="pattern_cases.elements" Value="" />

View file

@ -47,7 +47,7 @@ namespace SyntaxVisitors.SugarVisitors
private int _variableCounter = 0;
private if_node _previousIf;
private statement_list desugaredMatchWith;
private statement desugaredMatchWith;
public static MatchWithVisitor New => new MatchWithVisitor();
@ -67,8 +67,11 @@ namespace SyntaxVisitors.SugarVisitors
DesugarDeconstructorPatternCase(matchWith.expr, patternCase);
}
if (matchWith.defaultAction != null)
AddDefaultCase(matchWith.defaultAction as statement_list);
if (desugaredMatchWith == null)
return;
desugaredMatchWith = new empty_statement();
// Замена выражения match with на новое несахарное поддерево и его обход
ReplaceUsingParent(matchWith, desugaredMatchWith);
@ -116,18 +119,23 @@ namespace SyntaxVisitors.SugarVisitors
var desugaredPattern = DesugarPattern(pattern, matchingExpression);
result.Add(desugaredPattern.CastVariableDefinition);
if (patternCase.condition != null)
// Если есть секция when - добавляем ее в условие
{
// TODO: generate check for 'when'
}
// Создание тела if из объявлений переменных, вызова Deconstruct и соответствующего тела case
var ifBody = new statement_list();
ifBody.Add(new desugared_deconstruction(desugaredPattern.DeconstructionVariables, desugaredPattern.CastVariable, patternCase.pattern.source_context));
ifBody.Add(desugaredPattern.DeconstructCall);
ifBody.Add(patternCase.case_action);
var ifCheck = SubtreeCreator.CreateIf(desugaredPattern.TypeCastCheck, ifBody);
if (patternCase.condition != null)
{
// Выполняем тело, если выполняется условие when
ifBody.Add(SubtreeCreator.CreateIf(patternCase.condition, patternCase.case_action));
}
else
ifBody.Add(patternCase.case_action);
var ifCondition = desugaredPattern.TypeCastCheck;
var ifCheck = SubtreeCreator.CreateIf(ifCondition, ifBody);
result.Add(ifCheck);
@ -140,13 +148,21 @@ namespace SyntaxVisitors.SugarVisitors
return new ident("<>patternGenVar" + _variableCounter++);
}
private void AddDefaultCase(statement_list statements)
{
AddDesugaredCaseToResult(statements, _previousIf);
}
private void AddDesugaredCaseToResult(statement_list statements, if_node newIf)
{
// Если результат пустой, значит это первый case
if (desugaredMatchWith == null)
desugaredMatchWith = statements;
else
{
_previousIf.else_body = statements;
_previousIf.FillParentsInDirectChilds();
}
// Запоминаем только что сгенерированный if
_previousIf = newIf;

View file

@ -0,0 +1,15 @@
begin
match 'asd' with
string(s):
match s with
integer(i): Assert(false);
string(s1) when s.Length > 1: ;
else Assert(false);
end;
end;
match 'asd' with
integer(i): Assert(false);
string(s1) when s1.Length > 3: Assert(false);
end;
end.

View file

@ -0,0 +1,34 @@

procedure Deconstruct<T>(self: List<T>; var f: T); extensionmethod;
begin
if self.Count > 0 then f := self[0];
end;
procedure Deconstruct<T>(self: Dictionary<integer,List<T>>; var f: T); extensionmethod;
begin
var fst := self.First;
var lst := fst.Value;
if not (lst = nil) and (lst.Count > 0) then
f := lst[0];
end;
procedure Deconstruct<K,V>(self: Dictionary<K,V>; var kk: Dictionary<List<K>, V>; var vv: V); extensionmethod;
begin
kk := Dict((self.Keys.ToList, self.Values.First));
end;
begin
var pair := ('asd', Arr(1,2,3).ToList);
var lst := Arr(4,5,6).ToList as object;
var d := Dict(Arr(pair)) as object;
var d2 := Dict((7, 8.0)) as object;
var collection := Arr(lst, d, d2);
foreach var x in collection do
match x with
Dictionary<integer,real>(s; k): Assert((s is Dictionary<List<integer>, real>) and (k is real));
Dictionary<string,List<integer>>(first): Assert(first['asd'][0] = 1);
List<real>(l: real): Assert(false);
List<integer>(first: integer): Assert(first = 4);
else Assert(false)
end;
end.

View file

@ -0,0 +1,38 @@
uses System.Linq;
begin
match 'a b c'.Any(x -> x = 'a') with
string(s): Assert(false);
boolean(b): Assert(b);
integer(i): Assert(false);
else Assert(false)
end;
var count := 0;
match Arr('aa', 'bb').Select(x -> x = 'aa' ? x : nil).ToList with
List<string>(l):
foreach var x in l do
if not (x = nil) then
loop 5 do count += 1;
end;
Assert(count = 5);
match 'a b c'.Any(x -> x = 'a') with
string(s): Assert(false);
boolean(b):
begin
var r := Enumerable
.Repeat(b, 3)
.Select(
x ->
begin
match x with
boolean(b): Result := not b;
end;
end);
Assert(Enumerable.Repeat(false, 3).SequenceEqual(r));
end;
integer(i): Assert(false);
else Assert(false);
end;
end.

View file

@ -0,0 +1,6 @@
begin
match nil with
List<integer>(s): Assert(false);
integer(i): Assert(false);
end;
end.

View file

@ -0,0 +1,28 @@
type
My<T> = class
public
_a: T;
private
procedure Deconstruct(var a: T);
begin
Println('Instance');
a := _a;
end;
end;
procedure Deconstruct<T>(self: My<T>; var a: T);
begin
Println('Ext');
a := self._a;
end;
begin
var l := new My<List<integer>>;
l._a := Arr(1, 2, 3).ToList;
match l with
My<real>(a): Assert(false);
My<integer>(a): Assert(false);
My<List<integer>>(a): Assert(a[0] = 1);
end;
end.

View file

@ -69,6 +69,14 @@ namespace TreeConverter.LambdaExpressions
syntaxTreeVisitor.visit(varStmt);
}
// Patterns
public override void visit(desugared_deconstruction _desugared_deconstruction)
{
// позволяем вывести типы объявленных переменных
syntaxTreeVisitor.visit(_desugared_deconstruction);
}
// !Patterns
public override void visit(assign assignment)
{
var to = assignment.to as ident;

View file

@ -11,16 +11,6 @@ namespace PascalABCCompiler.TreeConverter
// Patterns
public partial class syntax_tree_visitor
{
private void CheckWhenExpression(SyntaxTree.expression when)
{
if (when == null)
return;
var convertedWhen = convert_strong(when);
if (!convertion_data_and_alghoritms.can_convert_type(convertedWhen, SystemLibrary.SystemLibrary.bool_type))
AddError(get_location(when), "WHEN_EXPRESSION_SHOULD_HAVE_BOOL_TYPE");
}
public override void visit(desugared_deconstruction deconstruction)
{
var invokationTarget = convert_strong(deconstruction.deconstruction_target as expression);
@ -94,7 +84,7 @@ namespace PascalABCCompiler.TreeConverter
{
parameterTypes = new type_node[givenParameterTypes.Length];
var selfParameter = candidate.is_extension_method ? candidate.parameters.First(IsSelfParameter) : null;
Debug.Assert(!candidate.is_extension_method || selfParameter != null, "Couldn't find self in extension method");
Debug.Assert(!candidate.is_extension_method || selfParameter != null, "Couldn't find self parameter in extension method");
var candidateParameterTypes =
candidate.is_extension_method ?
candidate.parameters.Where(x => !IsSelfParameter(x)).ToArray() :
@ -111,10 +101,11 @@ namespace PascalABCCompiler.TreeConverter
var nils = new List<int>();
var deduceSucceded = generic_convertions.DeduceInstanceTypes(selfParameter.type, patternInstance.type, deducedGenerics, nils);
if (!deduceSucceded || deducedGenerics.Contains(null))
{
AddError(deconstructionLocation, "COULDNT_DEDUCE_DECONSTRUCT_GENERIC_TYPE");
// Проверка на то, что в Deconstruct все дженерики выводятся по self делается в другом месте
// TODO: сделать проверку
// TODO: запретить дженерик методы в классах. Можно использовать только дженерик-типы самого класса в качестве параметров
//AddError(deconstructionLocation, "COULDNT_DEDUCE_DECONSTRUCT_GENERIC_TYPE");
return false;
}
}
for (int i = 0; i < givenParameterTypes.Length; i++)
@ -122,7 +113,7 @@ namespace PascalABCCompiler.TreeConverter
var givenParameter = givenParameterTypes[i];
var candidateParameter = candidateParameterTypes[i].type;
if (genericDeduceNeeded && (candidateParameter.is_generic_parameter || candidateParameter.is_generic_type_instance))
candidateParameter = InstantiateParameter(candidateParameter, deducedGenerics);//candidateParameter.get_instance(deducedGenerics.ToList());
candidateParameter = InstantiateParameter(candidateParameter, deducedGenerics);
if (givenParameter != null && !AreTheSameTypes(candidateParameter, givenParameter))
return false;
@ -183,11 +174,6 @@ namespace PascalABCCompiler.TreeConverter
var genericNode = (genericType as generic_instance_type_node);
if (genericNode != null)
{
//var genericParameters = genericNode.instance_params;
//for (int i = 0; i < genericParameters.Count; i++)
// genericParameters[i] = InstantiateParameter(genericParameters[i], instances);
var parameters = genericNode.instance_params.Select(x => InstantiateParameter(x, instances)).ToList();
genericNode.instance_params = parameters;
}