Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion OpenDreamClient/Interface/Controls/ControlMap.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
using System.Diagnostics.CodeAnalysis;
using OpenDreamClient.Input;
using OpenDreamClient.Interface.Controls.UI;
using OpenDreamClient.Interface.Css;
using OpenDreamShared.Interface.Descriptors;
using OpenDreamShared.Interface.DMF;
using OpenDreamClient.Rendering;
using OpenDreamShared.Dream;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input;
Expand Down Expand Up @@ -39,6 +39,9 @@ protected override void UpdateElementDescriptor() {
};

UpdateViewRange(InterfaceManager.View);

var cssRulesets = new CssParser(new CssLexer(MapDescriptor.Style.AsRaw())).Stylesheet();
Logger.Info($"{cssRulesets.Count} rulesets parsed");
}

public void UpdateViewRange(ViewRange view) {
Expand Down
94 changes: 94 additions & 0 deletions OpenDreamClient/Interface/Css/CssAst.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using System.Text;

namespace OpenDreamClient.Interface.Css;

public interface ICssAst;
public interface ICssExpression;

public sealed class CssRuleset(List<CssSelector> selectors, List<CssDeclaration> declarations) : ICssAst {
public readonly List<CssSelector> Selectors = selectors;
public readonly List<CssDeclaration> Declarations = declarations;
}

#region Selectors

public abstract class CssSelector : ICssAst {
public CssSelector? SubSelector { get; set; }
}

public sealed class CssNameSelector(ReadOnlyMemory<char> name) : CssSelector {
public readonly ReadOnlyMemory<char> Name = name;

public override string ToString() => Name.ToString();
}

public sealed class CssIdSelector(ReadOnlyMemory<char> id) : CssSelector {
public readonly ReadOnlyMemory<char> Id = id;

public override string ToString() => $"#{Id}";
}

public sealed class CssClassSelector(ReadOnlyMemory<char> @class) : CssSelector {
public readonly ReadOnlyMemory<char> Class = @class;

public override string ToString() => $".{Class}";
}

#endregion

public sealed class CssDeclaration(ReadOnlyMemory<char> property, ICssExpression value) : ICssAst {
public readonly ReadOnlyMemory<char> Property = property;
public readonly ICssExpression Value = value;

public override string ToString() {
return $"{Property}: {Value}";
}
}

#region Expressions

public sealed class CssString(ReadOnlyMemory<char> value) : ICssExpression {
public readonly ReadOnlyMemory<char> Value = value;

public override string ToString() => $"\"{Value}\"";
}

public sealed class CssIdentifier(ReadOnlyMemory<char> value) : ICssExpression {
public readonly ReadOnlyMemory<char> Value = value;

public override string ToString() => Value.ToString();
}

public sealed class CssHexColor(ReadOnlyMemory<char> value) : ICssExpression {
public readonly ReadOnlyMemory<char> Value = value;

public override string ToString() => Value.ToString();
}

public sealed class CssNumber(ReadOnlyMemory<char> value) : ICssExpression {
public readonly ReadOnlyMemory<char> Value = value;

public override string ToString() => Value.ToString();
}

public sealed class CssDimension(ReadOnlyMemory<char> value) : ICssExpression {
public readonly ReadOnlyMemory<char> Value = value;

public override string ToString() => Value.ToString();
}

public sealed class CssExpressionGroup(List<ICssExpression> expressions) : ICssExpression {
public readonly List<ICssExpression> Expressions = expressions;

public override string ToString() {
var strBuilder = new StringBuilder();
foreach (var expr in Expressions) {
strBuilder.Append(expr);
strBuilder.Append(' ');
}

return strBuilder.ToString();
}
}

#endregion
129 changes: 129 additions & 0 deletions OpenDreamClient/Interface/Css/CssLexer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
namespace OpenDreamClient.Interface.Css;

public sealed class CssLexer(string source) {
private int _currentIndex;

public enum TokenType {
EndOfSource,
Unknown,
Whitespace,
Semicolon,
Colon,
Comma,
Asterisk,
Period,
OpeningBrace,
ClosingBrace,
Identifier,
String,
Hash,
Number,
Dimension
}

public readonly struct Token(TokenType type, ReadOnlyMemory<char> text) {
public readonly TokenType Type = type;
public readonly ReadOnlyMemory<char> Text = text;
}

public Token GetNextToken() {
var tokenStart = _currentIndex;
var tokenType = AdvanceNextToken();
var tokenText = source.AsMemory(tokenStart, _currentIndex - tokenStart);

if (tokenType is TokenType.Identifier) {
tokenType = tokenText switch {
_ => TokenType.Identifier
};
}

return new Token(tokenType, tokenText);
}

private TokenType AdvanceNextToken() {
if (_currentIndex >= source.Length)
return TokenType.EndOfSource;

var c = source[_currentIndex];
switch (c) {
case ';': Advance(); return TokenType.Semicolon;
case ':': Advance(); return TokenType.Colon;
case ',': Advance(); return TokenType.Comma;
case '*': Advance(); return TokenType.Asterisk;
case '.': Advance(); return TokenType.Period;
case '{': Advance(); return TokenType.OpeningBrace;
case '}': Advance(); return TokenType.ClosingBrace;
case '#':
do {
c = Advance();
} while (IsIdentifierChar(c));

return TokenType.Hash;
case '"':
case '\'':
var terminator = c;
do {
c = Advance();
} while (!IsNewline(c) && c != terminator);

Advance();
return TokenType.String;
default:
if (char.IsWhiteSpace(c) || IsNewline(c)) {
do {
c = Advance();
} while (char.IsWhiteSpace(c) || IsNewline(c));

return TokenType.Whitespace;
}

if (AdvanceIdentifier(c)) {
return TokenType.Identifier;
} else if (char.IsAsciiDigit(c) || c is '.') {
do {
c = Advance();
} while (char.IsAsciiDigit(c));

if (c is '.') {
do {
c = Advance();
} while (char.IsAsciiDigit(c));
}

return AdvanceIdentifier(c) ? TokenType.Dimension : TokenType.Number;
}

return TokenType.Unknown;
}
}

private char Advance() {
_currentIndex++;

return _currentIndex < source.Length ? source[_currentIndex] : '\0';
}

private bool AdvanceText(string text) {
foreach (var c in text) {
if (Advance() != c)
return false;
}

return true;
}

private bool AdvanceIdentifier(char c) {
if (char.IsAsciiLetter(c) || c is '-' or '_') {
do {
c = Advance();
} while (IsIdentifierChar(c));

return true;
}

return false;
}

private static bool IsNewline(char c) => c is '\n' or '\r' or '\f';
private static bool IsIdentifierChar(char c) => char.IsAsciiLetterOrDigit(c) || c is '-' or '_';
}
Loading
Loading