Skip to content

Commit 265ea48

Browse files
committed
add RulesEngine for TypeInference
1 parent 957a6bb commit 265ea48

File tree

9 files changed

+167
-52
lines changed

9 files changed

+167
-52
lines changed

generators/CssInCSharp.CommandLine/Configuration.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Text.RegularExpressions;
22
using CssInCSharp.Generator;
3+
using RulesEngine.Models;
34

45
namespace CssInCSharp.CommandLine
56
{
@@ -13,6 +14,8 @@ internal class Configuration
1314

1415
public List<string> Exclude { get; set; } = [];
1516

17+
public List<Rule> TypeInferences { get; set; } = [];
18+
1619
public void AddIncludeItem(string src, string dest)
1720
{
1821
Include.Add(new IncludeItem

generators/CssInCSharp.CommandLine/ConvertCommand.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.CommandLine;
22
using CssInCSharp.Generator;
3+
using RulesEngine.Models;
34

45
namespace CssInCSharp.CommandLine
56
{
@@ -67,6 +68,9 @@ private async Task ExecAsync(string configFile, ConverterType type, string src,
6768
}.Update();
6869
});
6970

71+
// init rule engine
72+
InferenceEngine.Initialize(config.TypeInferences);
73+
7074
foreach (var item in items)
7175
{
7276
var converter = ConverterFactory.Create(config.Converter, item.CsOptions);

generators/CssInCSharp.CommandLine/Util.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@ public static class Util
1616
{
1717
opt.PreCondition((src, dest, srcMember) => src.Usings is { Count: > 0 });
1818
})
19-
.ForMember(x => x.TypeInferences, opt =>
20-
{
21-
opt.PreCondition((src, dest, srcMember) => src.TypeInferences is { Count: > 0 });
22-
})
2319
.ForMember(x => x.Replacements, opt =>
2420
{
2521
opt.PreCondition((src, dest, srcMember) => src.Replacements is { Count: > 0 });
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace CssInCSharp.Generator;
2+
3+
public class AstGenerateException: Exception
4+
{
5+
public int StartLine { get; }
6+
public int EndLine { get; }
7+
8+
public AstGenerateException(int start, int end) : base($"Generate ast failed, source file line: {start} {end}")
9+
{
10+
StartLine = start;
11+
EndLine = end;
12+
}
13+
}

generators/CssInCSharp.Generator/CSharpOptions.cs

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System.Text.RegularExpressions;
2-
using CssInCSharp.Generator.Extensions;
32

43
namespace CssInCSharp.Generator;
54

@@ -19,8 +18,6 @@ public class CSharpOptions
1918
public bool UseStaticMethod { get; set; } = false;
2019
public bool UsePascalCase { get; set; } = false;
2120
public bool UseAnonymousType { get; set; } = false;
22-
public bool UseTypeInference { get; set; } = false;
23-
public List<MatchItem> TypeInferences { get; set; } = [];
2421
public List<MatchItem> Replacements { get; set; } = [];
2522

2623
public void SetContextVariables()
@@ -35,20 +32,6 @@ public void SetContextVariables()
3532
_contextVariables[nameof(DefaultExportType)] = DefaultExportType;
3633
}
3734

38-
public string Infer(string token, string defaultValue = "object")
39-
{
40-
if (!UseTypeInference) return defaultValue;
41-
foreach (var item in TypeInferences)
42-
{
43-
if (Regex.IsMatch(token, item.Pattern))
44-
{
45-
return item.GetValue(_contextVariables).ToPascalCase();
46-
}
47-
}
48-
49-
return defaultValue;
50-
}
51-
5235
public string Replace(string input)
5336
{
5437
if (Replacements.Count <= 0)

generators/CssInCSharp.Generator/CssInCSharp.Generator.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
<ItemGroup>
1010
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" />
11+
<PackageReference Include="RulesEngine" Version="5.0.3" />
1112
</ItemGroup>
1213

1314
<ItemGroup>

generators/CssInCSharp.Generator/Extensions/StringExtensions.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,19 @@ public static string Purify(this string str)
1313
return str.Trim('\'');
1414
}
1515

16+
public static string ToType(this string type)
17+
{
18+
switch (type)
19+
{
20+
case "double":
21+
case "bool":
22+
case "string":
23+
case "object":
24+
return type;
25+
default: return type.ToPascalCase();
26+
}
27+
}
28+
1629
public static string ToPascalCase(this string input) =>
1730
input switch
1831
{
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
using System.Text.RegularExpressions;
2+
using CssInCSharp.Generator.Extensions;
3+
using RulesEngine.Actions;
4+
using RulesEngine.Models;
5+
6+
namespace CssInCSharp.Generator
7+
{
8+
public class Token
9+
{
10+
public string? Kind { get; set; }
11+
public string? Identifier { get; set; }
12+
public string? MethodName { get; set; }
13+
public string? NamePrefix { get; set; }
14+
public string? DefaultValue { get; set; }
15+
16+
public string ParseDefaultType()
17+
{
18+
if (double.TryParse(DefaultValue, out _))
19+
{
20+
return "double";
21+
}
22+
23+
if (bool.TryParse(DefaultValue, out _))
24+
{
25+
return "bool";
26+
}
27+
28+
if (Regex.IsMatch(DefaultValue, @"^""\w*""$"))
29+
{
30+
return "string";
31+
}
32+
33+
return "object";
34+
}
35+
}
36+
37+
public static class InferenceEngine
38+
{
39+
private static RulesEngine.RulesEngine? _engine;
40+
41+
public static void Initialize(List<Rule> rules)
42+
{
43+
if (rules.Count <= 0) return;
44+
var workflows = new Workflow[]
45+
{
46+
new Workflow()
47+
{
48+
WorkflowName = "TypeInference",
49+
Rules = rules
50+
}
51+
};
52+
var reSettings = new ReSettings();
53+
reSettings.CustomActions = new Dictionary<string, Func<ActionBase>>()
54+
{
55+
};
56+
57+
_engine = new RulesEngine.RulesEngine(workflows, reSettings);
58+
}
59+
60+
public static string Infer(Token token, string defaultValue = "object")
61+
{
62+
if (_engine == null) return defaultValue;
63+
var results = _engine.ExecuteAllRulesAsync("TypeInference", token).GetAwaiter().GetResult();
64+
if (results != null)
65+
{
66+
foreach (var result in results)
67+
{
68+
if (result.IsSuccess)
69+
{
70+
// If there are multiple matching results, the first one is used.
71+
return result.ActionResult.Output.ToString()!.ToType();
72+
}
73+
}
74+
}
75+
76+
return defaultValue;
77+
}
78+
}
79+
}

generators/CssInCSharp.Generator/TypeScriptConverter.cs

Lines changed: 54 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,44 @@ private SyntaxNodeOrList GenerateCSharpAst(Ts.TsTypes.INode node, NodeContext? c
8989
{
9090
var n = node.AsType<Ts.TsTypes.ArrowFunction>();
9191
var funcName = context?.FuncName ?? string.Empty;
92-
var returnType = n.Type?.GetText() ?? _options.Infer(funcName, _options.DefaultReturnType);
92+
var returnType = n.Type?.GetText() ?? InferenceEngine.Infer(new Token
93+
{
94+
Kind = Ts.TsTypes.SyntaxKind.TypeReference.ToString(),
95+
MethodName = funcName,
96+
NamePrefix = _options.NamePrefix
97+
}, _options.DefaultReturnType);
9398

9499
var parameters = n.Parameters.Select(x =>
95100
{
101+
LiteralExpressionSyntax? initializer = null;
102+
string defaultValue = null;
103+
if (x.Initializer != null)
104+
{
105+
// todo: csharp does not support variable as default value
106+
if (x.Initializer.Kind != Ts.TsTypes.SyntaxKind.Identifier)
107+
{
108+
initializer = GenerateCSharpAst(x.Initializer).AsType<LiteralExpressionSyntax>();
109+
defaultValue = initializer.GetText().ToString();
110+
}
111+
}
96112
var pName = x.Name.GetText();
97-
var pType = x.Type?.GetText() ?? _options.Infer(pName, _options.DefaultParameterType);
98-
return SyntaxFactory.Parameter(SyntaxFactory.Identifier(pName))
113+
var pType = x.Type?.GetText() ?? InferenceEngine.Infer(new Token
114+
{
115+
Kind = x.Kind.ToString(),
116+
Identifier = pName,
117+
MethodName = funcName,
118+
NamePrefix = _options.NamePrefix,
119+
DefaultValue = defaultValue,
120+
}, _options.DefaultParameterType);
121+
122+
var parameter = SyntaxFactory.Parameter(SyntaxFactory.Identifier(pName))
99123
.WithType(SyntaxFactory.ParseTypeName(pType));
124+
if (initializer != null)
125+
{
126+
parameter = parameter.WithDefault(SyntaxFactory.EqualsValueClause(initializer));
127+
}
128+
129+
return parameter;
100130
}).ToArray();
101131

102132
var funcBody = n.Body;
@@ -118,7 +148,7 @@ private SyntaxNodeOrList GenerateCSharpAst(Ts.TsTypes.INode node, NodeContext? c
118148
}
119149
default:
120150
{
121-
var statement = GenerateCSharpAst(funcBody).AsType<ExpressionSyntax>();
151+
var statement = GenerateCSharpAst(funcBody, new NodeContext(){ ReturnType = returnType}).AsType<ExpressionSyntax>();
122152
statements.Add(SyntaxFactory.ReturnStatement(statement));
123153
break;
124154
}
@@ -146,14 +176,14 @@ private SyntaxNodeOrList GenerateCSharpAst(Ts.TsTypes.INode node, NodeContext? c
146176
.Where(x => x.Kind != Ts.TsTypes.SyntaxKind.SpreadElement) // remove SpreadElement
147177
.Select(x => (SyntaxNodeOrToken)GenerateCSharpAst(x).AsT0)
148178
.Separate(SyntaxFactory.Token(SyntaxKind.CommaToken));
149-
var arrayType = SyntaxFactory.ArrayType(
150-
SyntaxFactory.PredefinedType(
151-
SyntaxFactory.Token(SyntaxKind.ObjectKeyword)))
152-
.WithRankSpecifiers(
153-
SyntaxFactory.SingletonList<ArrayRankSpecifierSyntax>(
154-
SyntaxFactory.ArrayRankSpecifier(
155-
SyntaxFactory.SingletonSeparatedList<ExpressionSyntax>(
156-
SyntaxFactory.OmittedArraySizeExpression()))));
179+
var arrayType = SyntaxFactory.ArrayType(context is { ReturnType: not null }
180+
? SyntaxFactory.IdentifierName(context.ReturnType)
181+
: SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ObjectKeyword)))
182+
.WithRankSpecifiers(
183+
SyntaxFactory.SingletonList<ArrayRankSpecifierSyntax>(
184+
SyntaxFactory.ArrayRankSpecifier(
185+
SyntaxFactory.SingletonSeparatedList<ExpressionSyntax>(
186+
SyntaxFactory.OmittedArraySizeExpression()))));
157187

158188
var arrayCreation = SyntaxFactory.ArrayCreationExpression(arrayType)
159189
.WithInitializer
@@ -258,7 +288,7 @@ private SyntaxNodeOrList GenerateCSharpAst(Ts.TsTypes.INode node, NodeContext? c
258288
{
259289
var n = node.AsType<Ts.TsTypes.CallExpression>();
260290
var args = n.Arguments
261-
.Select(x => (SyntaxNodeOrToken)SyntaxFactory.Argument(GenerateCSharpAst(x, new NodeContext() { UseLambda = true }).AsType<ExpressionSyntax>()))
291+
.Select(x => (SyntaxNodeOrToken)SyntaxFactory.Argument(GenerateCSharpAst(x, new NodeContext() { FuncName = n.IdentifierStr, UseLambda = true }).AsType<ExpressionSyntax>()))
262292
.Separate(SyntaxFactory.Token(SyntaxKind.CommaToken)).ToList();
263293
return SyntaxFactory.InvocationExpression
264294
(
@@ -307,7 +337,12 @@ private SyntaxNodeOrList GenerateCSharpAst(Ts.TsTypes.INode node, NodeContext? c
307337
? [SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)]
308338
: [SyntaxFactory.Token(SyntaxKind.PublicKeyword)];
309339
var funcName = _options.DefaultExportMethodName;
310-
var returnType = _options.Infer(funcName, _options.DefaultExportType);
340+
var returnType = InferenceEngine.Infer(new Token
341+
{
342+
Kind = Ts.TsTypes.SyntaxKind.TypeReference.ToString(),
343+
MethodName = funcName,
344+
NamePrefix = _options.NamePrefix
345+
}, _options.DefaultExportType);
311346
var methodDeclaration = SyntaxFactory
312347
.MethodDeclaration(SyntaxFactory.ParseTypeName(returnType), Format(funcName))
313348
.AddModifiers(tokens);
@@ -558,6 +593,11 @@ private SyntaxNodeOrList GenerateCSharpAst(Ts.TsTypes.INode node, NodeContext? c
558593
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
559594
);
560595
}
596+
case Ts.TsTypes.SyntaxKind.ReturnStatement:
597+
{
598+
var n = node.AsType<Ts.TsTypes.ReturnStatement>();
599+
return SyntaxFactory.ReturnStatement(GenerateCSharpAst(n.Expression, context).AsType<ExpressionSyntax>());
600+
}
561601
case Ts.TsTypes.SyntaxKind.SpreadAssignment:
562602
{
563603
var n = node.AsType<Ts.TsTypes.SpreadAssignment>();
@@ -581,11 +621,6 @@ private SyntaxNodeOrList GenerateCSharpAst(Ts.TsTypes.INode node, NodeContext? c
581621

582622
return expression;
583623
}
584-
case Ts.TsTypes.SyntaxKind.ReturnStatement:
585-
{
586-
var n = node.AsType<Ts.TsTypes.ReturnStatement>();
587-
return SyntaxFactory.ReturnStatement(GenerateCSharpAst(n.Expression, context).AsType<ExpressionSyntax>());
588-
}
589624
case Ts.TsTypes.SyntaxKind.SourceFile:
590625
{
591626
SyntaxToken[] tokens = _options.UsePartialClass
@@ -914,18 +949,6 @@ private static int GetLineNumber(string text, int index)
914949
}
915950
}
916951

917-
public class AstGenerateException: Exception
918-
{
919-
public int StartLine { get; }
920-
public int EndLine { get; }
921-
922-
public AstGenerateException(int start, int end) : base($"Generate ast failed, source file line: {start} {end}")
923-
{
924-
StartLine = start;
925-
EndLine = end;
926-
}
927-
}
928-
929952
public class NodeContext
930953
{
931954
public string? ReturnType { get; set; }

0 commit comments

Comments
 (0)