Skip to content

Commit

Permalink
v0.2, new lexer and interpreter
Browse files Browse the repository at this point in the history
  • Loading branch information
CypherPotato committed Jan 25, 2024
1 parent 72363d7 commit 69a74a8
Show file tree
Hide file tree
Showing 41 changed files with 1,569 additions and 971 deletions.
48 changes: 36 additions & 12 deletions src/CascadiumCompiler.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
using System;
using Cascadium.Compiler;
using Cascadium.Entity;
using Cascadium.Extensions;
using Cascadium.Object;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Cascadium;

Expand All @@ -8,10 +15,6 @@ namespace Cascadium;
/// </summary>
public sealed partial class CascadiumCompiler
{
internal static char[] combinators = new[] { '>', '~', '+' };

private CascadiumCompiler() { }

/// <summary>
/// Compiles a top-module CSS stylesheet to legacy CSS, with the code already minified.
/// </summary>
Expand All @@ -32,12 +35,33 @@ public static string Compile(ReadOnlySpan<byte> xcss, Encoding encoder, Cascadiu
/// <returns>The compiled and minified CSS.</returns>
public static string Compile(string xcss, CascadiumOptions? options = null)
{
var context = new Compiler.CompilerContext()
{
Options = options
};
string prepared = context.Preparers.PrepareCssInput(xcss);
context.Parser.ParseXCss(prepared);
return context.Exporter.Export().Trim();
CascadiumOptions _options = options ?? new CascadiumOptions();

//// strip comments and trim the input
string sanitizedInput = Sanitizer.SanitizeInput(xcss);

//// tokenizes the sanitized input, which is the lexical analyzer
//// and convert the code into tokens
TokenCollection resultTokens = new Tokenizer(sanitizedInput).Tokenize();

//// parses the produced tokens and produce an nested stylesheet from
//// the token. this also applies the semantic and syntax checking
NestedStylesheet nestedStylesheet = Parser.ParseSpreadsheet(resultTokens);

//// flatten the stylesheet, which removes the nesting and places all
//// rules in the top level of the stylesheet.
FlatStylesheet flattenStylesheet = Flattener.FlattenStylesheet(nestedStylesheet);

//// build the css body, assembling the flat rules into an valid
//// css string
CssStylesheet css = new Assembler(_options).AssemblyCss(flattenStylesheet, _options);

//// apply cascadium extensions
if (_options.UseVarShortcut) ValueHandler.TransformVarShortcuts(css);
if (_options.AtRulesRewrites.Count > 0) MediaRewriter.ApplyRewrites(css, _options);
if (_options.Converters.Count > 0) Converter.ConvertAll(css, _options);

//// export the css into an string
return css.Export(_options);
}
}
22 changes: 22 additions & 0 deletions src/CascadiumException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cascadium.Object;

namespace Cascadium;

public class CascadiumException : Exception
{
public int Line { get; private set; }
public int Column { get; private set; }
public string LineText { get; private set; }

internal CascadiumException(TokenDebugInfo snapshot, string message) : base(message)
{
Line = snapshot.Line;
Column = snapshot.Column;
LineText = snapshot.LineText;
}
}
10 changes: 5 additions & 5 deletions src/Cascadium.csproj → src/CascadiumLexer.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
Expand All @@ -21,9 +21,9 @@
<PackageTags>css,scss,sass,less</PackageTags>
<RepositoryType>git</RepositoryType>

<Version>0.1.2</Version>
<AssemblyVersion>0.1.2</AssemblyVersion>
<FileVersion>0.1.2</FileVersion>
<Version>0.2.0</Version>
<AssemblyVersion>0.2.0</AssemblyVersion>
<FileVersion>0.2.0</FileVersion>

<NeutralLanguage>en</NeutralLanguage>
<IncludeSymbols>True</IncludeSymbols>
Expand All @@ -41,4 +41,4 @@
</None>
</ItemGroup>

</Project>
</Project>
4 changes: 4 additions & 0 deletions src/CascadiumOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Cascadium;

Expand Down Expand Up @@ -47,6 +50,7 @@ public class CascadiumOptions
public MergeOrderPriority MergeOrderPriority { get; set; } = MergeOrderPriority.PreserveLast;
}


/// <summary>
/// Represents the order priority used at an <see cref="CascadiumOptions"/>, only appliable to <see cref="MergeOption.Selectors"/>.
/// </summary>
Expand Down
5 changes: 0 additions & 5 deletions src/Changelog.md

This file was deleted.

219 changes: 219 additions & 0 deletions src/Compiler/Assembler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
using Cascadium.Entity;
using Cascadium.Object;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Cascadium.Compiler;

class Assembler
{
public CascadiumOptions Options { get; set; }

public Assembler(CascadiumOptions options)
{
Options = options;
}

public CssStylesheet AssemblyCss(FlatStylesheet flatStylesheet, CascadiumOptions options)
{
CssStylesheet result = new CssStylesheet();
int ruleIndex = 0;

foreach (FlatRule rule in flatStylesheet.Rules)
{
if (rule.Declarations.Count == 0)
{
// skip empty rules
continue;
}

CssRule cssRule;
if (rule.IsRuleAtRule()) // checks if the rule is an @ rule, like @font-face
{
cssRule = new CssRule()
{
Declarations = rule.Declarations,
Selector = rule.Selectors[0][0],
Order = ++ruleIndex
};
result.Rules.Add(cssRule);
}
else
{
string? atRule = rule.PopAtRule();
string selector = BuildCssSelector(rule.Selectors);

cssRule = new CssRule()
{
Declarations = rule.Declarations,
Selector = selector,
Order = ++ruleIndex
};

if (atRule != null)
{
CssStylesheet atRuleStylesheet = result.GetOrCreateStylesheet(atRule, options.Merge.HasFlag(MergeOption.AtRules));
atRuleStylesheet.Rules.Add(cssRule);
}
else
{
result.Rules.Add(cssRule);
}
}
}

result.Statements.AddRange(flatStylesheet.Statements);

if (options.Merge != MergeOption.None)
{
Merge(result, options);
foreach(CssStylesheet subCss in result.Stylesheets)
{
Merge(subCss, options);
}
}

return result;
}

string BuildCssSelector(IList<string[]> selectors)
{
int flatCount = selectors.Count;

if (flatCount == 0)
{
return "";
}
if (flatCount == 1)
{
return BuildCssSelector(selectors[0], Array.Empty<string>());
}
else
{
string carry = BuildCssSelector(selectors[0], Array.Empty<string>());
for (int i = 1; i < flatCount; i++)
{
string[] current = selectors[i];
if (current.Length == 0) continue;
carry = BuildCssSelector(current, Helper.SafeSplit(carry, ','));
}
return carry;
}
}

string BuildCssSelector(string[] cSelectors, string[] bSelectors)
{
StringBuilder sb = new StringBuilder();
if (bSelectors.Length == 0)
{
foreach (string cSelector in cSelectors)
{
string prepared = Helper.PrepareSelectorUnit(cSelector, Options.KeepNestingSpace, Options.Pretty);
sb.Append(prepared);
sb.Append(',');
if (Options.Pretty)
sb.Append(' ');
}
goto finish;
}
foreach (string C in cSelectors)
{
string c = C.Trim();
foreach (string B in bSelectors)
{
string b = B.Trim();
string s;
if (c.StartsWith('&'))
{
sb.Append(b);
s = c.Substring(1);
if (!Options.KeepNestingSpace)
{
s = s.TrimStart();
}
}
else
{
sb.Append(b);
if (c.Length != 0 && !Token.Sel_Combinators.Contains(c[0]))
{
sb.Append(' ');
}
s = c;
}

s = Helper.PrepareSelectorUnit(s, Options.KeepNestingSpace, Options.Pretty);
sb.Append(s);
sb.Append(',');
if (Options.Pretty)
sb.Append(' ');
}
}

finish:
if (sb.Length > 0) sb.Length--;
if (sb.Length > 0 && Options.Pretty) sb.Length--;
return sb.ToString();
}

public void Merge(CssStylesheet stylesheet, CascadiumOptions options)
{
if (options.Merge.HasFlag(MergeOption.Selectors))
{
List<CssRule> newRules = new List<CssRule>();

foreach (CssRule rule in stylesheet.Rules)
{
CssRule? existingRule = newRules
.FirstOrDefault(r => Helper.IsSelectorsEqual(r.Selector, rule.Selector));

if (existingRule == null)
{
newRules.Add(rule);
}
else
{
foreach (var prop in rule.Declarations)
{
existingRule.Declarations[prop.Key] = prop.Value;

if (options.MergeOrderPriority == MergeOrderPriority.PreserveLast)
{
if (rule.Order > existingRule.Order)
existingRule.Order = rule.Order;
}
}
}
}

stylesheet.Rules = newRules;
}

if (options.Merge.HasFlag(MergeOption.Declarations))
{
// merge top-level only
List<CssRule> newRules = new List<CssRule>();

foreach (CssRule rule in stylesheet.Rules)
{
CssRule? existingRule = newRules
.FirstOrDefault(r => r.GetHashCode() == rule.GetHashCode());

if (existingRule == null)
{
newRules.Add(rule);
}
else
{
existingRule.Selector = Helper.CombineSelectors(existingRule.Selector, rule.Selector, options.Pretty);
}
}

stylesheet.Rules = newRules;
}
}
}
Loading

0 comments on commit 69a74a8

Please sign in to comment.