diff --git a/simple-css-nesting.sln b/simple-css-nesting.sln
index 0446a06..4ca0aa6 100644
--- a/simple-css-nesting.sln
+++ b/simple-css-nesting.sln
@@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cascadium", "src\Cascadium.csproj", "{ACD2DBA9-BF07-4F18-95ED-BC69DCC59A3C}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cascadium-Utility", "tool\Cascadium-Utility.csproj", "{CB7E8644-3094-44DB-94C9-068CB12C4993}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{650184C6-E955-4D15-B478-15BB312B79AF}"
@@ -13,16 +11,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{EF0FD356
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CssTests", "tests\build\CssTests\CssTests.csproj", "{9C391FB9-77C4-420D-88DC-2863B759AA64}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CascadiumLexer", "src\CascadiumLexer.csproj", "{7043E166-FDAC-4ACB-B44E-1EE018CADAE8}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {ACD2DBA9-BF07-4F18-95ED-BC69DCC59A3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {ACD2DBA9-BF07-4F18-95ED-BC69DCC59A3C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {ACD2DBA9-BF07-4F18-95ED-BC69DCC59A3C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {ACD2DBA9-BF07-4F18-95ED-BC69DCC59A3C}.Release|Any CPU.Build.0 = Release|Any CPU
{CB7E8644-3094-44DB-94C9-068CB12C4993}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CB7E8644-3094-44DB-94C9-068CB12C4993}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CB7E8644-3094-44DB-94C9-068CB12C4993}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -31,6 +27,10 @@ Global
{9C391FB9-77C4-420D-88DC-2863B759AA64}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9C391FB9-77C4-420D-88DC-2863B759AA64}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9C391FB9-77C4-420D-88DC-2863B759AA64}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7043E166-FDAC-4ACB-B44E-1EE018CADAE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7043E166-FDAC-4ACB-B44E-1EE018CADAE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7043E166-FDAC-4ACB-B44E-1EE018CADAE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7043E166-FDAC-4ACB-B44E-1EE018CADAE8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/CascadiumCompiler.cs b/src/CascadiumCompiler.cs
index a96d233..87f307e 100644
--- a/src/CascadiumCompiler.cs
+++ b/src/CascadiumCompiler.cs
@@ -11,29 +11,17 @@
namespace Cascadium;
///
-/// Provides a CSS compiler that compiles higher-level code with single-line comments, nesting into a legacy CSS file.
+/// Provides a compiler that compiles Cascadium code into an legacy CSS file.
///
-public sealed partial class CascadiumCompiler
+public static class CascadiumCompiler
{
///
- /// Compiles a top-module CSS stylesheet to legacy CSS, with the code already minified.
+ /// Compiles an Cascadium stylesheet to legacy CSS.
///
- /// The byte array including the top module CSS code.
- /// The encoder which will be used to decode the CSS output.
- /// Optional options and parameters to the compilation.
- /// The compiled and minified UTF-8 CSS.
- public static string Compile(ReadOnlySpan xcss, Encoding encoder, CascadiumOptions? options = null)
- {
- return Compile(encoder.GetString(xcss), options);
- }
-
- ///
- /// Compiles a top-module CSS stylesheet to legacy CSS, with the code already minified.
- ///
- /// The top module CSS code.
- /// Optional options and parameters to the compilation.
+ /// The Cascadium source code.
+ /// Optional. Options and parameters to the compiler.
/// The compiled and minified CSS.
- public static string Compile(string xcss, CascadiumOptions? options = null)
+ public static CssStylesheet Parse(string xcss, CascadiumOptions? options = null)
{
CascadiumOptions _options = options ?? new CascadiumOptions();
@@ -55,13 +43,48 @@ public static string Compile(string xcss, CascadiumOptions? options = null)
//// build the css body, assembling the flat rules into an valid
//// css string
CssStylesheet css = new Assembler(_options).AssemblyCss(flattenStylesheet, _options);
+ css.Options = _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);
+ return css;
+ }
+
+ ///
+ /// Compiles an Cascadium stylesheet to legacy CSS.
+ ///
+ /// The Cascadium source code.
+ /// Optional. Options and parameters to the compiler.
+ /// The compiled and minified CSS.
+ public static string Compile(string xcss, CascadiumOptions? options = null)
+ {
+ CascadiumOptions _options = options ?? new CascadiumOptions();
+ return Parse(xcss, _options).Export();
+ }
+
+ ///
+ /// Compiles an Cascadium stylesheet to legacy CSS.
+ ///
+ /// The byte array including the Cascadium source code.
+ /// Optional. Options and parameters to the compiler.
+ /// The compiled UTF-8 CSS.
+ public static string Compile(ReadOnlySpan xcss, CascadiumOptions? options = null)
+ {
+ return Compile(xcss, Encoding.UTF8, options);
+ }
+
+ ///
+ /// Compiles an Cascadium stylesheet to legacy CSS.
+ ///
+ /// The byte array including the Cascadium source code.
+ /// The encoder which will be used to decode the CSS output.
+ /// Optional. Options and parameters to the compiler.
+ /// The compiled CSS.
+ public static string Compile(ReadOnlySpan xcss, Encoding encoder, CascadiumOptions? options = null)
+ {
+ return Compile(encoder.GetString(xcss), options);
}
}
\ No newline at end of file
diff --git a/src/CascadiumLexer.csproj b/src/CascadiumLexer.csproj
index 391a1d2..0fe2a0c 100644
--- a/src/CascadiumLexer.csproj
+++ b/src/CascadiumLexer.csproj
@@ -1,44 +1,10 @@
-
- net6.0
- Cascadium
- Cascadium.Core
- disable
- enable
- True
- True
-
- Cascadium Compiler
- Cascadium.Compiler
-
- CypherPotato
- Project Principium
- Cascadium
- Provides a CSS compiler that compiles higher-level code with single-line comments, nesting and more into a legacy CSS file.
- README.md
- https://github.com/CypherPotato/cascadium
- css,scss,sass,less
- git
-
- 0.2.0
- 0.2.0
- 0.2.0
-
- en
- True
- LICENSE.txt
-
-
-
-
- True
- \
-
-
- True
- \
-
-
-
-
\ No newline at end of file
+
+ net6.0
+ enable
+ enable
+ Cascadium
+
+
+
diff --git a/src/Compiler/Assembler.cs b/src/Compiler/Assembler.cs
index 7192faf..1079042 100644
--- a/src/Compiler/Assembler.cs
+++ b/src/Compiler/Assembler.cs
@@ -36,11 +36,11 @@ public CssStylesheet AssemblyCss(FlatStylesheet flatStylesheet, CascadiumOptions
{
cssRule = new CssRule()
{
- Declarations = rule.Declarations,
- Selector = rule.Selectors[0][0],
- Order = ++ruleIndex
+ _declarations = rule.Declarations,
+ Selector = BuildCssSelector(rule.Selectors),
+ _order = ++ruleIndex
};
- result.Rules.Add(cssRule);
+ result._rules.Add(cssRule);
}
else
{
@@ -49,29 +49,29 @@ public CssStylesheet AssemblyCss(FlatStylesheet flatStylesheet, CascadiumOptions
cssRule = new CssRule()
{
- Declarations = rule.Declarations,
+ _declarations = rule.Declarations,
Selector = selector,
- Order = ++ruleIndex
+ _order = ++ruleIndex
};
if (atRule != null)
{
CssStylesheet atRuleStylesheet = result.GetOrCreateStylesheet(atRule, options.Merge.HasFlag(MergeOption.AtRules));
- atRuleStylesheet.Rules.Add(cssRule);
+ atRuleStylesheet._rules.Add(cssRule);
}
else
{
- result.Rules.Add(cssRule);
+ result._rules.Add(cssRule);
}
}
}
- result.Statements.AddRange(flatStylesheet.Statements);
+ result._statements.AddRange(flatStylesheet.Statements);
if (options.Merge != MergeOption.None)
{
Merge(result, options);
- foreach(CssStylesheet subCss in result.Stylesheets)
+ foreach(CssStylesheet subCss in result._stylesheets)
{
Merge(subCss, options);
}
@@ -166,7 +166,7 @@ public void Merge(CssStylesheet stylesheet, CascadiumOptions options)
{
List newRules = new List();
- foreach (CssRule rule in stylesheet.Rules)
+ foreach (CssRule rule in stylesheet._rules)
{
CssRule? existingRule = newRules
.FirstOrDefault(r => Helper.IsSelectorsEqual(r.Selector, rule.Selector));
@@ -177,20 +177,20 @@ public void Merge(CssStylesheet stylesheet, CascadiumOptions options)
}
else
{
- foreach (var prop in rule.Declarations)
+ foreach (var prop in rule._declarations)
{
- existingRule.Declarations[prop.Key] = prop.Value;
+ existingRule._declarations[prop.Key] = prop.Value;
if (options.MergeOrderPriority == MergeOrderPriority.PreserveLast)
{
- if (rule.Order > existingRule.Order)
- existingRule.Order = rule.Order;
+ if (rule._order > existingRule._order)
+ existingRule._order = rule._order;
}
}
}
}
- stylesheet.Rules = newRules;
+ stylesheet._rules = newRules;
}
if (options.Merge.HasFlag(MergeOption.Declarations))
@@ -198,7 +198,7 @@ public void Merge(CssStylesheet stylesheet, CascadiumOptions options)
// merge top-level only
List newRules = new List();
- foreach (CssRule rule in stylesheet.Rules)
+ foreach (CssRule rule in stylesheet._rules)
{
CssRule? existingRule = newRules
.FirstOrDefault(r => r.GetHashCode() == rule.GetHashCode());
@@ -213,7 +213,7 @@ public void Merge(CssStylesheet stylesheet, CascadiumOptions options)
}
}
- stylesheet.Rules = newRules;
+ stylesheet._rules = newRules;
}
}
}
diff --git a/src/Compiler/Helper.cs b/src/Compiler/Helper.cs
index 5c00763..e0250de 100644
--- a/src/Compiler/Helper.cs
+++ b/src/Compiler/Helper.cs
@@ -9,6 +9,55 @@ namespace Cascadium.Compiler;
internal static class Helper
{
+ public static int SafeCountIncidences(string value, char op)
+ {
+ int incidences = 0;
+ bool inSingleString = false;
+ bool inDoubleString = false;
+ int expressionIndex = 0, groupIndex = 0;
+
+ for (int i = 0; i < value.Length; i++)
+ {
+ char c = value[i];
+ char b = i > 0 ? value[i - 1] : '\0';
+
+ if (c == '\'' && b != '\\' && !inDoubleString)
+ {
+ inSingleString = !inSingleString;
+ }
+ else if (c == '"' && b != '\\' && !inSingleString)
+ {
+ inDoubleString = !inDoubleString;
+ }
+ else if (c == '(' && !(inDoubleString || inSingleString))
+ {
+ expressionIndex++;
+ }
+ else if (c == ')' && !(inDoubleString || inSingleString))
+ {
+ expressionIndex--;
+ }
+ else if (c == '[' && !(inDoubleString || inSingleString))
+ {
+ groupIndex++;
+ }
+ else if (c == ']' && !(inDoubleString || inSingleString))
+ {
+ groupIndex--;
+ }
+
+ if ((inDoubleString || inSingleString) == false && expressionIndex == 0 && groupIndex == 0)
+ {
+ if (c == op)
+ {
+ incidences++;
+ }
+ }
+ }
+
+ return incidences;
+ }
+
public static string[] SafeSplit(string? value, char op)
{
if (value == null) return Array.Empty();
diff --git a/src/Compiler/TextInterpreter.cs b/src/Compiler/TextInterpreter.cs
index a49ca46..20c8940 100644
--- a/src/Compiler/TextInterpreter.cs
+++ b/src/Compiler/TextInterpreter.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Text;
+using System.Text;
using Cascadium.Object;
namespace Cascadium.Compiler;
diff --git a/src/Compiler/Tokenizer.cs b/src/Compiler/Tokenizer.cs
index debcf18..901bafa 100644
--- a/src/Compiler/Tokenizer.cs
+++ b/src/Compiler/Tokenizer.cs
@@ -93,9 +93,13 @@ IEnumerable ReadCurrentDeclaration(string declaration)
string property = declaration.Substring(0, dotPos).Trim();
string value = declaration.Substring(dotPos + 1).Trim();
- if (property == "")
+ if (!Token.IsValidPropertyName(property))
{
- throw new CascadiumException(Interpreter.TakeSnapshot(declaration), "syntax error: property name cannot be empty");
+ throw new CascadiumException(Interpreter.TakeSnapshot(declaration), "syntax error: invalid property name");
+ }
+ else if (Token.IsPropertyValueUnescapedDoubleDots(value))
+ {
+ throw new CascadiumException(Interpreter.TakeSnapshot(declaration), "syntax error: unclosed declaration");
}
else
{
diff --git a/src/Entity/CssRule.cs b/src/Entity/CssRule.cs
index 1a15c32..82e9c18 100644
--- a/src/Entity/CssRule.cs
+++ b/src/Entity/CssRule.cs
@@ -6,12 +6,28 @@
namespace Cascadium.Entity;
-internal class CssRule
+///
+/// Represents an CSS rule.
+///
+public class CssRule
{
- public string Selector { get; set; } = "";
- public Dictionary Declarations { get; set; } = new Dictionary();
- public int Order { get; set; } = 0;
+ internal Dictionary _declarations { get; set; } = new Dictionary();
+ internal int _order = 0;
+ ///
+ /// Gets the rule selector.
+ ///
+ public string Selector { get; internal set; } = "";
+
+ ///
+ /// Gets the declarations defined in this .
+ ///
+ public IDictionary Declarations { get => _declarations; }
+
+ ///
+ /// Gets the hash code for this .
+ ///
+ /// A 32-bit signed integer hash code.
public override int GetHashCode()
{
int n = 0, j = 1;
@@ -26,12 +42,26 @@ public override int GetHashCode()
// bar: foo
// foo: bar
- foreach (var kp in Declarations)
+ foreach (var kp in _declarations)
{
n += (kp.Key.GetHashCode() + kp.Value.GetHashCode()) / 2;
n *= j;
j++;
}
- return n / Declarations.Count;
+ return n / _declarations.Count;
+ }
+
+ ///
+ /// Determines if the specified objects are and are equals.
+ ///
+ /// The another .
+ /// A boolean indicating if this object is equals to the other one.
+ public override bool Equals(object? obj)
+ {
+ if (obj is CssRule r)
+ {
+ return this.GetHashCode() == r.GetHashCode();
+ }
+ return false;
}
}
diff --git a/src/Entity/CssStylesheet.cs b/src/Entity/CssStylesheet.cs
index 373697a..5e8bd5a 100644
--- a/src/Entity/CssStylesheet.cs
+++ b/src/Entity/CssStylesheet.cs
@@ -8,19 +8,57 @@
namespace Cascadium.Entity;
-class CssStylesheet
+///
+/// Represents an CSS stylesheet.
+///
+public class CssStylesheet
{
- public string? AtRuleDeclaration { get; set; }
- public List Statements { get; set; } = new List();
- public List Stylesheets { get; set; } = new List();
- public List Rules { get; set; } = new List();
+ internal List _statements { get; set; } = new List();
+ internal List _stylesheets { get; set; } = new List();
+ internal List _rules { get; set; } = new List();
- public CssStylesheet GetOrCreateStylesheet(string atRuleDeclaration, bool canMerge)
+ ///
+ /// Gets the @-rule declaration that holds this stylesheet.
+ ///
+ public string? AtRuleDeclaration { get; internal set; }
+
+ ///
+ /// Gets the individual statements of this stylesheet.
+ ///
+ public string[] Statements { get => _statements.ToArray(); }
+
+ ///
+ /// Gets the children of this stylesheet.
+ ///
+ public CssStylesheet[] Stylesheets { get => _stylesheets.ToArray(); }
+
+ ///
+ /// Gets an array of of this stylesheet.
+ ///
+ public CssRule[] Rules { get => _rules.ToArray(); }
+
+ ///
+ /// Gets the used used to compile this CSS stylesheet.
+ ///
+ public CascadiumOptions Options { get; internal set; } = null!;
+
+ ///
+ /// Exports this to an CSS representation string, using the
+ /// parameter.
+ ///
+ /// An CSS string.
+ public string Export()
+ {
+ return Export(Options);
+ }
+
+
+ internal CssStylesheet GetOrCreateStylesheet(string atRuleDeclaration, bool canMerge)
{
if (canMerge)
{
string sanitized = Helper.RemoveSpaces(atRuleDeclaration);
- foreach (CssStylesheet subStylesheet in Stylesheets)
+ foreach (CssStylesheet subStylesheet in _stylesheets)
{
if (Helper.RemoveSpaces(subStylesheet.AtRuleDeclaration ?? "") == sanitized)
{
@@ -32,7 +70,7 @@ public CssStylesheet GetOrCreateStylesheet(string atRuleDeclaration, bool canMer
{
AtRuleDeclaration = atRuleDeclaration
};
- Stylesheets.Add(newStylesheet);
+ _stylesheets.Add(newStylesheet);
return newStylesheet;
}
else
@@ -41,12 +79,12 @@ public CssStylesheet GetOrCreateStylesheet(string atRuleDeclaration, bool canMer
{
AtRuleDeclaration = atRuleDeclaration
};
- Stylesheets.Add(newStylesheet);
+ _stylesheets.Add(newStylesheet);
return newStylesheet;
}
}
- public string Export(CascadiumOptions options)
+ string Export(CascadiumOptions options)
{
StringBuilder sb = new StringBuilder();
@@ -76,7 +114,7 @@ void ExportStylesheet(CssStylesheet css, int indentLevel)
void ExportRules(CssStylesheet css, int indentLevel)
{
- foreach (var rule in css.Rules.OrderBy(r => r.Order))
+ foreach (var rule in css._rules.OrderBy(r => r._order))
{
if (options.Pretty) sb.Append(new string(' ', indentLevel * 4));
sb.Append(rule.Selector);
@@ -84,7 +122,7 @@ void ExportRules(CssStylesheet css, int indentLevel)
sb.Append('{');
if (options.Pretty) sb.Append('\n');
- foreach (KeyValuePair property in rule.Declarations)
+ foreach (KeyValuePair property in rule._declarations)
{
if (options.Pretty) sb.Append(new string(' ', (indentLevel + 1) * 4));
sb.Append(property.Key);
@@ -103,17 +141,17 @@ void ExportRules(CssStylesheet css, int indentLevel)
}
}
- foreach (string decl in Statements)
+ foreach (string decl in _statements)
{
sb.Append(decl);
sb.Append(';');
if (options.Pretty) sb.AppendLine();
}
- if (options.Pretty && Statements.Count > 0) sb.AppendLine();
+ if (options.Pretty && _statements.Count > 0) sb.AppendLine();
ExportRules(this, 0);
- foreach (CssStylesheet stylesheet in Stylesheets)
+ foreach (CssStylesheet stylesheet in _stylesheets)
{
ExportStylesheet(stylesheet, 0);
}
diff --git a/src/Extensions/Converter.cs b/src/Extensions/Converter.cs
index e2b6e50..4a82b6e 100644
--- a/src/Extensions/Converter.cs
+++ b/src/Extensions/Converter.cs
@@ -13,9 +13,9 @@ internal class Converter
{
public static void ConvertAll(CssStylesheet css, CascadiumOptions options)
{
- foreach (var rule in css.Rules)
+ foreach (var rule in css._rules)
{
- foreach (KeyValuePair declaration in rule.Declarations.ToArray())
+ foreach (KeyValuePair declaration in rule._declarations.ToArray())
{
foreach (CSSConverter converter in options.Converters)
{
@@ -23,19 +23,19 @@ public static void ConvertAll(CssStylesheet css, CascadiumOptions options)
{
NameValueCollection output = new NameValueCollection();
converter.Convert(declaration.Value, output);
- rule.Declarations.Remove(declaration.Key);
+ rule._declarations.Remove(declaration.Key);
foreach (string nprop in output)
{
string? value = output[nprop];
if (string.IsNullOrEmpty(value)) continue;
- rule.Declarations[nprop] = value;
+ rule._declarations[nprop] = value;
}
}
}
}
}
- foreach (var subcss in css.Stylesheets)
+ foreach (var subcss in css._stylesheets)
{
ConvertAll(subcss, options);
}
diff --git a/src/Extensions/MediaRewriter.cs b/src/Extensions/MediaRewriter.cs
index 25c5506..4540fb3 100644
--- a/src/Extensions/MediaRewriter.cs
+++ b/src/Extensions/MediaRewriter.cs
@@ -12,7 +12,7 @@ internal class MediaRewriter
{
public static void ApplyRewrites(CssStylesheet cssStylesheet, CascadiumOptions options)
{
- foreach (var subcss in cssStylesheet.Stylesheets)
+ foreach (var subcss in cssStylesheet._stylesheets)
{
if (subcss.AtRuleDeclaration == null) continue;
foreach (string rewrite in options.AtRulesRewrites)
diff --git a/src/Extensions/ValueHandler.cs b/src/Extensions/ValueHandler.cs
index 42240fa..2fdc984 100644
--- a/src/Extensions/ValueHandler.cs
+++ b/src/Extensions/ValueHandler.cs
@@ -12,20 +12,20 @@ internal class ValueHandler
{
public static void TransformVarShortcuts(CssStylesheet css)
{
- foreach (var rule in css.Rules)
+ foreach (var rule in css._rules)
{
- foreach(string key in rule.Declarations.Keys)
+ foreach (string key in rule._declarations.Keys)
{
- rule.Declarations[key] = ApplyVarShortcuts(rule.Declarations[key]);
+ rule._declarations[key] = ApplyVarShortcuts(rule._declarations[key], css);
}
}
- foreach (var subcss in css.Stylesheets)
+ foreach (var subcss in css._stylesheets)
{
TransformVarShortcuts(subcss);
}
}
- static string ApplyVarShortcuts(string value)
+ static string ApplyVarShortcuts(string value, CssStylesheet stylesheet)
{
StringBuilder output = new StringBuilder();
char[] chars = value.ToCharArray();
@@ -37,6 +37,7 @@ static string ApplyVarShortcuts(string value)
{
char c = chars[i];
char b = i > 0 ? chars[i - 1] : '\0';
+ char n = i < chars.Length - 1 ? chars[i + 1] : '\0';
output.Append(c);
@@ -51,7 +52,8 @@ static string ApplyVarShortcuts(string value)
if ((inSingleString || inDoubleString) == false)
{
- if (c == '-' && b == '-' && output.Length >= 2 && !output.ToString().EndsWith("var(--"))
+ string tmpOut = output.ToString();
+ if (c == '-' && b == '-' && n != '-' && output.Length >= 2 && !tmpOut.EndsWith("var(--") && !tmpOut.EndsWith("---"))
{
isParsingVarname = true;
output.Length -= 2;
diff --git a/src/Object/Token.cs b/src/Object/Token.cs
index d609e62..9c85d42 100644
--- a/src/Object/Token.cs
+++ b/src/Object/Token.cs
@@ -32,6 +32,17 @@ struct Token
public static readonly char Ch_Semicolon = ';';
public static readonly char Ch_DoubleDots = ':';
+ public static bool IsPropertyValueUnescapedDoubleDots(string propertyValue)
+ {
+ return Helper.SafeCountIncidences(propertyValue, ':') > 0;
+ }
+
+ public static bool IsValidPropertyName(string propertyName)
+ {
+ if (propertyName.Length == 0) return false;
+ return propertyName.All(c => char.IsLetterOrDigit(c) || c == '-' || c == '$' || c == '%'); // $ and % is an special token for converters
+ }
+
public static bool IsIdentifierChr(char c)
{
return Char.IsLetter(c) || Char.IsDigit(c) || c == '_' || c == '-';
diff --git a/tool/Cascadium-Utility.csproj b/tool/Cascadium-Utility.csproj
index af593ad..433475e 100644
--- a/tool/Cascadium-Utility.csproj
+++ b/tool/Cascadium-Utility.csproj
@@ -7,7 +7,7 @@
enable
cascadium
true
-
+
0.1.2.4
0.1.2.4
cascadiumtool
@@ -18,7 +18,7 @@
-
+
diff --git a/tool/Compiler.cs b/tool/Compiler.cs
index e0ad437..4d4919e 100644
--- a/tool/Compiler.cs
+++ b/tool/Compiler.cs
@@ -1,6 +1,7 @@
using Cascadium;
using Microsoft.VisualBasic;
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
@@ -9,14 +10,17 @@
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using System.Text.RegularExpressions;
+using System.Threading;
using System.Threading.Tasks;
namespace cascadiumtool;
internal class Compiler
{
- public static int RunCompiler(CommandLineArguments args)
+ public static async Task RunCompiler(CommandLineArguments args)
{
+ Stopwatch sw = new Stopwatch();
+ sw.Start();
string? stdin = null;
bool anyCompiled = false;
@@ -99,47 +103,55 @@ public static int RunCompiler(CommandLineArguments args)
if (inputFiles.Count > 0)
{
anyCompiled = true;
- StringBuilder resultCss = new StringBuilder();
-
- long compiledLength = 0, totalLength = 0;
+ long compiledLength = 0;
int smallInputLength = inputFiles.Select(Path.GetDirectoryName).Min(i => i?.Length ?? 0);
+ ConcurrentBag resultCss = new ConcurrentBag();
- foreach (string file in inputFiles)
- {
- string contents = ReadFile(file);
- string result;
- totalLength += contents.Length;
-
- try
- {
- result = CascadiumCompiler.Compile(contents, options);
- compiledLength += result.Length;
+ CancellationTokenSource errorCanceller = new CancellationTokenSource();
- if (options.Pretty)
+ try
+ {
+ await Parallel.ForEachAsync(inputFiles,
+ new ParallelOptions()
{
- resultCss.AppendLine(result + "\n");
- }
- else
+ MaxDegreeOfParallelism = 4,
+ CancellationToken = errorCanceller.Token
+ }, (file, ct) =>
{
- resultCss.Append(result);
- }
- }
- catch (CascadiumException cex)
- {
- Console.WriteLine($"error at file {file.Substring(smallInputLength + 1)}, line {cex.Line}, col. {cex.Column}:");
- Console.WriteLine();
- Console.WriteLine($"\t{cex.LineText}");
- Console.WriteLine($"\t{new string(' ', cex.Column - 1)}^");
- Console.WriteLine($"\t{cex.Message}");
- return 5;
- }
+ if (ct.IsCancellationRequested)
+ return ValueTask.FromCanceled(ct);
+
+ try
+ {
+ string result = CompileFile(file, options);
+ Interlocked.Add(ref compiledLength, result.Length);
+ resultCss.Add(result);
+ }
+ catch (CascadiumException cex)
+ {
+ Console.WriteLine($"error at file {file.Substring(smallInputLength + 1)}, line {cex.Line}, col. {cex.Column}:");
+ Console.WriteLine();
+ Console.WriteLine($"\t{cex.LineText}");
+ Console.WriteLine($"\t{new string(' ', cex.Column - 1)}^");
+ Console.WriteLine($"\t{cex.Message}");
+ errorCanceller.Cancel();
+ }
+
+ return ValueTask.CompletedTask;
+ });
+ }
+ catch (TaskCanceledException)
+ {
+ ;
}
if (outputFile != null)
{
- File.WriteAllText(outputFile, resultCss.ToString());
+ string css = string.Join(options.Pretty ? "\n" : "", resultCss);
+ File.WriteAllText(outputFile, css);
+
compiledLength = new FileInfo(outputFile).Length;
- Log.Info($"{inputFiles.Count} file(s) -> {Path.GetFileName(args.OutputFile)} [{PathUtils.FileSize(totalLength)} -> {PathUtils.FileSize(compiledLength)}]");
+ Log.Info($"{inputFiles.Count} file(s) -> {Path.GetFileName(args.OutputFile)} ({PathUtils.FileSize(compiledLength)}) in {sw.ElapsedMilliseconds:N0}ms");
}
else
{
@@ -156,8 +168,17 @@ public static int RunCompiler(CommandLineArguments args)
return 0;
}
- static string ReadFile(string file)
+ static string CompileFile(string file, CascadiumOptions options)
{
- return System.IO.File.ReadAllText(file);
+ string result;
+
+ lock (Program.CompilerCache)
+ if (!Program.CompilerCache.TryGetValue(file, out result!))
+ {
+ result = CascadiumCompiler.Compile(File.ReadAllText(file), options);
+ Program.CompilerCache.Add(file, result);
+ }
+
+ return result;
}
}
diff --git a/tool/Log.cs b/tool/Log.cs
index a829c26..c515983 100644
--- a/tool/Log.cs
+++ b/tool/Log.cs
@@ -14,7 +14,7 @@ public static int ErrorKill(string message)
Write("error", message);
return 1;
}
- public static int Info(string message, bool force=false)
+ public static int Info(string message, bool force = false)
{
if (!force && !LoggingEnabled) return 0;
Write("info", message);
diff --git a/tool/Program.cs b/tool/Program.cs
index 1d60b95..b89a14c 100644
--- a/tool/Program.cs
+++ b/tool/Program.cs
@@ -2,18 +2,20 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
+using System.Threading.Tasks;
namespace cascadiumtool;
internal class Program
{
- public const string VersionLabel = "v.0.2-alpha-4";
- public static string CurrentDirectory { get; } = Directory.GetCurrentDirectory();
+ public const string VersionLabel = "v.0.3";
+ public static string CurrentDirectory { get; set; } = Directory.GetCurrentDirectory();
public static bool HasRootConfiguration { get; private set; }
public static JsonCssCompilerOptions? CompilerOptions { get; set; }
+ public static Dictionary CompilerCache { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase);
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(CommandLineArguments))]
- static int Main(string[] args)
+ static async Task Main(string[] args)
{
CommandLineParser.TryParse(args, out var result, out var errors);
@@ -21,29 +23,31 @@ static int Main(string[] args)
{
CommandLineParser.PrintHelp($"Cascadium [{VersionLabel}]", "Distributed under MIT License", errors);
return 0;
- } else
+ }
+ else
{
- RunParsed(result, out int errorcode);
- return errorcode;
+ return await RunParsed(result);
}
}
- public static void RunParsed(CommandLineArguments args, out int errorcode)
+ public static async Task RunParsed(CommandLineArguments args)
{
if (args.ConfigFile != null)
{
- CompilerOptions = JsonCssCompilerOptions.Create(args.ConfigFile);
+ string fullPath = PathUtils.ResolvePath(args.ConfigFile);
+ CompilerOptions = JsonCssCompilerOptions.Create(fullPath);
+ Program.CurrentDirectory = Path.GetDirectoryName(fullPath)!;
}
args.Import(CompilerOptions);
if (args.Watch)
{
- errorcode = Watcher.Watch(args);
+ return await Watcher.Watch(args);
}
else
{
- errorcode = Compiler.RunCompiler(args);
+ return await Compiler.RunCompiler(args);
}
}
}
diff --git a/tool/Watcher.cs b/tool/Watcher.cs
index cbf6e07..8be61ee 100644
--- a/tool/Watcher.cs
+++ b/tool/Watcher.cs
@@ -14,8 +14,9 @@ internal static class Watcher
private static FileSystemWatcher fsWatcher = new FileSystemWatcher();
private static CommandLineArguments watchArgs = null!;
private static string[] watchingDirectories = Array.Empty();
+ private static bool IsCompilingFile = false;
- public static int Watch(CommandLineArguments args)
+ public static async Task Watch(CommandLineArguments args)
{
watchArgs = args;
HashSet paths = new HashSet();
@@ -40,7 +41,7 @@ public static int Watch(CommandLineArguments args)
watchingDirectories = paths.ToArray();
foreach (string p in paths)
{
- if(!Directory.Exists(p))
+ if (!Directory.Exists(p))
{
return Log.ErrorKill("the detected directory path at " + p + " does not exists.");
}
@@ -58,20 +59,25 @@ public static int Watch(CommandLineArguments args)
fsWatcher.IncludeSubdirectories = true;
fsWatcher.EnableRaisingEvents = true;
+ await Compiler.RunCompiler(args);
Log.Info("cascadium is watching for file changes");
- Log.LoggingEnabled = false;
-
- Compiler.RunCompiler(args);
+ //Log.LoggingEnabled = false;
Thread.Sleep(-1);
return 0;
}
- [MethodImpl(MethodImplOptions.Synchronized)]
- private static void FsWatcher_Changed(Object sender, FileSystemEventArgs e)
+ private static async void FsWatcher_Changed(Object sender, FileSystemEventArgs e)
{
+ if (IsCompilingFile)
+ {
+ return;
+ }
+
+ IsCompilingFile = true;
+
string outFile = PathUtils.ResolvePath(watchArgs.OutputFile);
- if(outFile == e.FullPath)
+ if (outFile == e.FullPath)
{
// avoid compiling the out file
return;
@@ -87,12 +93,17 @@ private static void FsWatcher_Changed(Object sender, FileSystemEventArgs e)
foreach (string includedDir in watchingDirectories)
isDirIncluded |= file.StartsWith(includedDir);
+
if (!isDirIncluded)
return;
+ Program.CompilerCache.Remove(file);
+
try
{
- Compiler.RunCompiler(watchArgs);
+ Thread.Sleep(300); // prevent the below error giving time to the time to write
+ await Compiler.RunCompiler(watchArgs);
+ ;
}
catch (System.IO.IOException)
{
@@ -101,7 +112,8 @@ private static void FsWatcher_Changed(Object sender, FileSystemEventArgs e)
}
finally
{
- Thread.Sleep(500);
+ IsCompilingFile = false;
+ Thread.Sleep(150);
}
}
}
diff --git a/tool/etc/build.bat b/tool/etc/build.bat
index 8f3283b..da45f34 100644
--- a/tool/etc/build.bat
+++ b/tool/etc/build.bat
@@ -12,4 +12,4 @@ dotnet publish "%~dp0/../Cascadium-Utility.csproj" --nologo ^
-p:PublishReadyToRun=true ^
-p:PublishTrimmed=true ^
-p:PublishSingleFile=true ^
- -o "%~dp0bin/Build/%NAME%/"
\ No newline at end of file
+ -o "%~dp0bin/Build/cascadium-v0.3-%NAME%/"
\ No newline at end of file