Skip to content

Commit 69a74a8

Browse files
committed
v0.2, new lexer and interpreter
1 parent 72363d7 commit 69a74a8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1569
-971
lines changed

src/CascadiumCompiler.cs

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1-
using System;
1+
using Cascadium.Compiler;
2+
using Cascadium.Entity;
3+
using Cascadium.Extensions;
4+
using Cascadium.Object;
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Linq;
28
using System.Text;
9+
using System.Threading.Tasks;
310

411
namespace Cascadium;
512

@@ -8,10 +15,6 @@ namespace Cascadium;
815
/// </summary>
916
public sealed partial class CascadiumCompiler
1017
{
11-
internal static char[] combinators = new[] { '>', '~', '+' };
12-
13-
private CascadiumCompiler() { }
14-
1518
/// <summary>
1619
/// Compiles a top-module CSS stylesheet to legacy CSS, with the code already minified.
1720
/// </summary>
@@ -32,12 +35,33 @@ public static string Compile(ReadOnlySpan<byte> xcss, Encoding encoder, Cascadiu
3235
/// <returns>The compiled and minified CSS.</returns>
3336
public static string Compile(string xcss, CascadiumOptions? options = null)
3437
{
35-
var context = new Compiler.CompilerContext()
36-
{
37-
Options = options
38-
};
39-
string prepared = context.Preparers.PrepareCssInput(xcss);
40-
context.Parser.ParseXCss(prepared);
41-
return context.Exporter.Export().Trim();
38+
CascadiumOptions _options = options ?? new CascadiumOptions();
39+
40+
//// strip comments and trim the input
41+
string sanitizedInput = Sanitizer.SanitizeInput(xcss);
42+
43+
//// tokenizes the sanitized input, which is the lexical analyzer
44+
//// and convert the code into tokens
45+
TokenCollection resultTokens = new Tokenizer(sanitizedInput).Tokenize();
46+
47+
//// parses the produced tokens and produce an nested stylesheet from
48+
//// the token. this also applies the semantic and syntax checking
49+
NestedStylesheet nestedStylesheet = Parser.ParseSpreadsheet(resultTokens);
50+
51+
//// flatten the stylesheet, which removes the nesting and places all
52+
//// rules in the top level of the stylesheet.
53+
FlatStylesheet flattenStylesheet = Flattener.FlattenStylesheet(nestedStylesheet);
54+
55+
//// build the css body, assembling the flat rules into an valid
56+
//// css string
57+
CssStylesheet css = new Assembler(_options).AssemblyCss(flattenStylesheet, _options);
58+
59+
//// apply cascadium extensions
60+
if (_options.UseVarShortcut) ValueHandler.TransformVarShortcuts(css);
61+
if (_options.AtRulesRewrites.Count > 0) MediaRewriter.ApplyRewrites(css, _options);
62+
if (_options.Converters.Count > 0) Converter.ConvertAll(css, _options);
63+
64+
//// export the css into an string
65+
return css.Export(_options);
4266
}
4367
}

src/CascadiumException.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using Cascadium.Object;
7+
8+
namespace Cascadium;
9+
10+
public class CascadiumException : Exception
11+
{
12+
public int Line { get; private set; }
13+
public int Column { get; private set; }
14+
public string LineText { get; private set; }
15+
16+
internal CascadiumException(TokenDebugInfo snapshot, string message) : base(message)
17+
{
18+
Line = snapshot.Line;
19+
Column = snapshot.Column;
20+
LineText = snapshot.LineText;
21+
}
22+
}

src/Cascadium.csproj renamed to src/CascadiumLexer.csproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFramework>net6.0</TargetFramework>
@@ -21,9 +21,9 @@
2121
<PackageTags>css,scss,sass,less</PackageTags>
2222
<RepositoryType>git</RepositoryType>
2323

24-
<Version>0.1.2</Version>
25-
<AssemblyVersion>0.1.2</AssemblyVersion>
26-
<FileVersion>0.1.2</FileVersion>
24+
<Version>0.2.0</Version>
25+
<AssemblyVersion>0.2.0</AssemblyVersion>
26+
<FileVersion>0.2.0</FileVersion>
2727

2828
<NeutralLanguage>en</NeutralLanguage>
2929
<IncludeSymbols>True</IncludeSymbols>
@@ -41,4 +41,4 @@
4141
</None>
4242
</ItemGroup>
4343

44-
</Project>
44+
</Project>

src/CascadiumOptions.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
using System;
33
using System.Collections.Generic;
44
using System.Collections.Specialized;
5+
using System.Linq;
6+
using System.Text;
7+
using System.Threading.Tasks;
58

69
namespace Cascadium;
710

@@ -47,6 +50,7 @@ public class CascadiumOptions
4750
public MergeOrderPriority MergeOrderPriority { get; set; } = MergeOrderPriority.PreserveLast;
4851
}
4952

53+
5054
/// <summary>
5155
/// Represents the order priority used at an <see cref="CascadiumOptions"/>, only appliable to <see cref="MergeOption.Selectors"/>.
5256
/// </summary>

src/Changelog.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/Compiler/Assembler.cs

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
using Cascadium.Entity;
2+
using Cascadium.Object;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Data;
6+
using System.Linq;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
10+
namespace Cascadium.Compiler;
11+
12+
class Assembler
13+
{
14+
public CascadiumOptions Options { get; set; }
15+
16+
public Assembler(CascadiumOptions options)
17+
{
18+
Options = options;
19+
}
20+
21+
public CssStylesheet AssemblyCss(FlatStylesheet flatStylesheet, CascadiumOptions options)
22+
{
23+
CssStylesheet result = new CssStylesheet();
24+
int ruleIndex = 0;
25+
26+
foreach (FlatRule rule in flatStylesheet.Rules)
27+
{
28+
if (rule.Declarations.Count == 0)
29+
{
30+
// skip empty rules
31+
continue;
32+
}
33+
34+
CssRule cssRule;
35+
if (rule.IsRuleAtRule()) // checks if the rule is an @ rule, like @font-face
36+
{
37+
cssRule = new CssRule()
38+
{
39+
Declarations = rule.Declarations,
40+
Selector = rule.Selectors[0][0],
41+
Order = ++ruleIndex
42+
};
43+
result.Rules.Add(cssRule);
44+
}
45+
else
46+
{
47+
string? atRule = rule.PopAtRule();
48+
string selector = BuildCssSelector(rule.Selectors);
49+
50+
cssRule = new CssRule()
51+
{
52+
Declarations = rule.Declarations,
53+
Selector = selector,
54+
Order = ++ruleIndex
55+
};
56+
57+
if (atRule != null)
58+
{
59+
CssStylesheet atRuleStylesheet = result.GetOrCreateStylesheet(atRule, options.Merge.HasFlag(MergeOption.AtRules));
60+
atRuleStylesheet.Rules.Add(cssRule);
61+
}
62+
else
63+
{
64+
result.Rules.Add(cssRule);
65+
}
66+
}
67+
}
68+
69+
result.Statements.AddRange(flatStylesheet.Statements);
70+
71+
if (options.Merge != MergeOption.None)
72+
{
73+
Merge(result, options);
74+
foreach(CssStylesheet subCss in result.Stylesheets)
75+
{
76+
Merge(subCss, options);
77+
}
78+
}
79+
80+
return result;
81+
}
82+
83+
string BuildCssSelector(IList<string[]> selectors)
84+
{
85+
int flatCount = selectors.Count;
86+
87+
if (flatCount == 0)
88+
{
89+
return "";
90+
}
91+
if (flatCount == 1)
92+
{
93+
return BuildCssSelector(selectors[0], Array.Empty<string>());
94+
}
95+
else
96+
{
97+
string carry = BuildCssSelector(selectors[0], Array.Empty<string>());
98+
for (int i = 1; i < flatCount; i++)
99+
{
100+
string[] current = selectors[i];
101+
if (current.Length == 0) continue;
102+
carry = BuildCssSelector(current, Helper.SafeSplit(carry, ','));
103+
}
104+
return carry;
105+
}
106+
}
107+
108+
string BuildCssSelector(string[] cSelectors, string[] bSelectors)
109+
{
110+
StringBuilder sb = new StringBuilder();
111+
if (bSelectors.Length == 0)
112+
{
113+
foreach (string cSelector in cSelectors)
114+
{
115+
string prepared = Helper.PrepareSelectorUnit(cSelector, Options.KeepNestingSpace, Options.Pretty);
116+
sb.Append(prepared);
117+
sb.Append(',');
118+
if (Options.Pretty)
119+
sb.Append(' ');
120+
}
121+
goto finish;
122+
}
123+
foreach (string C in cSelectors)
124+
{
125+
string c = C.Trim();
126+
foreach (string B in bSelectors)
127+
{
128+
string b = B.Trim();
129+
string s;
130+
if (c.StartsWith('&'))
131+
{
132+
sb.Append(b);
133+
s = c.Substring(1);
134+
if (!Options.KeepNestingSpace)
135+
{
136+
s = s.TrimStart();
137+
}
138+
}
139+
else
140+
{
141+
sb.Append(b);
142+
if (c.Length != 0 && !Token.Sel_Combinators.Contains(c[0]))
143+
{
144+
sb.Append(' ');
145+
}
146+
s = c;
147+
}
148+
149+
s = Helper.PrepareSelectorUnit(s, Options.KeepNestingSpace, Options.Pretty);
150+
sb.Append(s);
151+
sb.Append(',');
152+
if (Options.Pretty)
153+
sb.Append(' ');
154+
}
155+
}
156+
157+
finish:
158+
if (sb.Length > 0) sb.Length--;
159+
if (sb.Length > 0 && Options.Pretty) sb.Length--;
160+
return sb.ToString();
161+
}
162+
163+
public void Merge(CssStylesheet stylesheet, CascadiumOptions options)
164+
{
165+
if (options.Merge.HasFlag(MergeOption.Selectors))
166+
{
167+
List<CssRule> newRules = new List<CssRule>();
168+
169+
foreach (CssRule rule in stylesheet.Rules)
170+
{
171+
CssRule? existingRule = newRules
172+
.FirstOrDefault(r => Helper.IsSelectorsEqual(r.Selector, rule.Selector));
173+
174+
if (existingRule == null)
175+
{
176+
newRules.Add(rule);
177+
}
178+
else
179+
{
180+
foreach (var prop in rule.Declarations)
181+
{
182+
existingRule.Declarations[prop.Key] = prop.Value;
183+
184+
if (options.MergeOrderPriority == MergeOrderPriority.PreserveLast)
185+
{
186+
if (rule.Order > existingRule.Order)
187+
existingRule.Order = rule.Order;
188+
}
189+
}
190+
}
191+
}
192+
193+
stylesheet.Rules = newRules;
194+
}
195+
196+
if (options.Merge.HasFlag(MergeOption.Declarations))
197+
{
198+
// merge top-level only
199+
List<CssRule> newRules = new List<CssRule>();
200+
201+
foreach (CssRule rule in stylesheet.Rules)
202+
{
203+
CssRule? existingRule = newRules
204+
.FirstOrDefault(r => r.GetHashCode() == rule.GetHashCode());
205+
206+
if (existingRule == null)
207+
{
208+
newRules.Add(rule);
209+
}
210+
else
211+
{
212+
existingRule.Selector = Helper.CombineSelectors(existingRule.Selector, rule.Selector, options.Pretty);
213+
}
214+
}
215+
216+
stylesheet.Rules = newRules;
217+
}
218+
}
219+
}

0 commit comments

Comments
 (0)