Add UIXA string literals

This commit is contained in:
Yoshi Askharoun
2025-01-27 18:23:07 -06:00
parent 609325b62c
commit 4c4bedb492
4 changed files with 64 additions and 32 deletions
+44 -30
View File
@@ -87,45 +87,59 @@ partial class Lexer
if (!typeNameResult.WasSuccessful) if (!typeNameResult.WasSuccessful)
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected qualified name of type to construct"]); return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected qualified name of type to construct"]);
Markup.MarkupConstantPersistMode? persistMode = null;
string content = "";
var encodingMarkerResult = Parse.Char('.')(input); var encodingMarkerResult = Parse.Char('.')(input);
input = encodingMarkerResult.Remainder; input = encodingMarkerResult.Remainder;
if (!encodingMarkerResult.WasSuccessful) if (!encodingMarkerResult.WasSuccessful)
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected '.', followed by the persist mode."]);
var persistModeResult = Parse.CharExcept('(').AtLeastOnce().Text().Token()(input);
input = persistModeResult.Remainder;
if (!persistModeResult.WasSuccessful)
return Result.Failure<IDirective>(input, "Invalid constant directive", ["No persist mode was specified."]);
Markup.MarkupConstantPersistMode? persistMode = persistModeResult.Value.ToLowerInvariant() switch
{ {
"bin" => Markup.MarkupConstantPersistMode.Binary, // Support defining constants from string literals
"str" => Markup.MarkupConstantPersistMode.FromString, var stringLiteralResult = ExpressionInBraces(StringLiteral)(input);
"can" => Markup.MarkupConstantPersistMode.Canonical, input = stringLiteralResult.Remainder;
_ => null if (!stringLiteralResult.WasSuccessful)
}; return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected '.', followed by the persist mode."]);
var openBracketResult = Parse.Char('(').Token()(input); persistMode = Markup.MarkupConstantPersistMode.FromString;
input = openBracketResult.Remainder; content = stringLiteralResult.Value[1..^1].Unescape();
if (!openBracketResult.WasSuccessful) }
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected '('"]); else
{
var persistModeResult = Parse.CharExcept('(').AtLeastOnce().Text().Token()(input);
input = persistModeResult.Remainder;
if (!persistModeResult.WasSuccessful)
return Result.Failure<IDirective>(input, "Invalid constant directive", ["No persist mode was specified."]);
var contentResult = Parse.CharExcept(')').AtLeastOnce().Text().Token()(input); persistMode = persistModeResult.Value.ToLowerInvariant() switch
input = contentResult.Remainder; {
if (!contentResult.WasSuccessful) "bin" => Markup.MarkupConstantPersistMode.Binary,
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected constant value"]); "str" => Markup.MarkupConstantPersistMode.FromString,
"can" => Markup.MarkupConstantPersistMode.Canonical,
_ => null
};
var closeBracketResult = Parse.Char(')').Token()(input); var openBracketResult = Parse.Char('(').Token()(input);
input = closeBracketResult.Remainder; input = openBracketResult.Remainder;
if (!closeBracketResult.WasSuccessful) if (!openBracketResult.WasSuccessful)
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected ')'"]); return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected '('"]);
var contentResult = Parse.AnyChar.Until(StatementEnd).Text().Token()(input);
input = contentResult.Remainder;
if (!contentResult.WasSuccessful)
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected constant value"]);
content = contentResult.Value;
if (content.Length <= 1 || content[^1] != ')')
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected ')'"]);
content = content[..^1];
}
var constantName = constNameResult.Value; var constantName = constNameResult.Value;
var typeName = typeNameResult.Value; var typeName = typeNameResult.Value;
if (persistMode == Markup.MarkupConstantPersistMode.FromString) if (persistMode == Markup.MarkupConstantPersistMode.FromString)
{ {
directive = new StringEncodedConstantDirective(constantName, typeName, contentResult.Value) directive = new StringEncodedConstantDirective(constantName, typeName, content)
{ {
Line = line, Line = line,
Column = column, Column = column,
@@ -133,7 +147,7 @@ partial class Lexer
} }
else if (persistMode == Markup.MarkupConstantPersistMode.Canonical) else if (persistMode == Markup.MarkupConstantPersistMode.Canonical)
{ {
directive = new CanonicalInstanceConstantDirective(constantName, typeName, contentResult.Value) directive = new CanonicalInstanceConstantDirective(constantName, typeName, content)
{ {
Line = line, Line = line,
Column = column, Column = column,
@@ -142,7 +156,7 @@ partial class Lexer
else if (persistMode == Markup.MarkupConstantPersistMode.Binary) else if (persistMode == Markup.MarkupConstantPersistMode.Binary)
{ {
byte[] constantBytes; byte[] constantBytes;
var byteParts = contentResult.Value.Split(','); var byteParts = content.Split(',');
if (byteParts.Length == 1) if (byteParts.Length == 1)
{ {
var constantStr = byteParts[0].Trim(); var constantStr = byteParts[0].Trim();
@@ -167,7 +181,7 @@ partial class Lexer
} }
else else
{ {
return Result.Failure<IDirective>(input, "Invalid constant directive", [$"Expected a 1, 2, or 4 hex number, or a list of bytes."]); return Result.Failure<IDirective>(input, "Invalid constant directive", [$"Expected a 1-, 2-, or 4-digit hex number, or a list of bytes"]);
} }
} }
else else
@@ -185,7 +199,7 @@ partial class Lexer
} }
else else
{ {
return Result.Failure<IDirective>(input, "Invalid constant directive", [$"'{persistModeResult.Value}' is not a valid persist mode."]); return Result.Failure<IDirective>(input, "Invalid constant directive", [$"Invalid persist mode"]);
} }
} }
break; break;
+16
View File
@@ -16,6 +16,16 @@ public static partial class Lexer
public static readonly Parser<string> Uri = Parse.LetterOrDigit.Or(Parse.Chars(":/!._-")).AtLeastOnce().Text(); public static readonly Parser<string> Uri = Parse.LetterOrDigit.Or(Parse.Chars(":/!._-")).AtLeastOnce().Text();
// https://github.com/apexsharp/apexparser/blob/4eb5983c657b0e6c49ed47bc42a0346e80f9e26d/ApexSharp.ApexParser/Parser/ApexGrammar.cs#L360C9-L367C64
public static readonly Parser<string> StringLiteral =
from leading in Parse.WhiteSpace.Many()
from openQuote in Parse.Char('"')
from fragments in Parse.Char('\\').Then(_ => Parse.AnyChar.Select(c => $"\\{c}"))
.Or(Parse.CharExcept("\\\"").Many().Text()).Many()
from closeQuote in Parse.Char('"')
from trailing in Parse.WhiteSpace.Many()
select $"\"{string.Join(string.Empty, fragments)}\"";
public static readonly Parser<string> StatementEnd = Parse.Char(';').Return(";").Or(Parse.LineTerminator); public static readonly Parser<string> StatementEnd = Parse.Char(';').Return(";").Or(Parse.LineTerminator);
public static readonly Parser<QualifiedTypeName> QualifiedTypeName = ParseQualifiedTypeName; public static readonly Parser<QualifiedTypeName> QualifiedTypeName = ParseQualifiedTypeName;
@@ -70,4 +80,10 @@ public static partial class Lexer
return Result.Success(qualifiedName, input); return Result.Success(qualifiedName, input);
} }
private static Parser<string> ExpressionInBraces(Parser<string> parser, char open = '(', char close = ')') =>
from openBrace in Parse.Char(open).Token()
from expression in parser.Optional()
from closeBrace in Parse.Char(close).Token()
select expression.GetOrElse(string.Empty).Trim();
} }
+1 -1
View File
@@ -24,7 +24,7 @@ public record ConstantDirective : Directive
public record StringEncodedConstantDirective : ConstantDirective public record StringEncodedConstantDirective : ConstantDirective
{ {
public StringEncodedConstantDirective(string name, QualifiedTypeName typeName, string content) public StringEncodedConstantDirective(string name, QualifiedTypeName typeName, string content)
: base(name, typeName, $"{typeName}.str({content})") : base(name, typeName, $"{typeName}(\"{content.Escape()}\")")
{ {
Content = content; Content = content;
PersistMode = MarkupConstantPersistMode.FromString; PersistMode = MarkupConstantPersistMode.FromString;
+3 -1
View File
@@ -45,6 +45,8 @@ public class Assembly
.constant const3 = Color.str(255, 0, 0, 255) .constant const3 = Color.str(255, 0, 0, 255)
.constant const4 = Font.str(JetBrains Mono) .constant const4 = Font.str(JetBrains Mono)
.constant const5 = String.str(This is some blue text) .constant const5 = String.str(This is some blue text)
.constant const3255 = String.str(({0:X8}))
.constant const3256 = String("Content\nOn a new line")
.section object .section object
Default_cont: Default_cont:
@@ -77,7 +79,7 @@ Alt_locl:
_output.WriteLine(ast.ToString()); _output.WriteLine(ast.ToString());
Assert.NotNull(ast); Assert.NotNull(ast);
Assert.Equal(20, ast.Directives.Count()); Assert.Equal(22, ast.Directives.Count());
Assert.Equal(24, ast.Code.Count()); Assert.Equal(24, ast.Code.Count());
} }