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)
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);
input = encodingMarkerResult.Remainder;
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,
"str" => Markup.MarkupConstantPersistMode.FromString,
"can" => Markup.MarkupConstantPersistMode.Canonical,
_ => null
};
// Support defining constants from string literals
var stringLiteralResult = ExpressionInBraces(StringLiteral)(input);
input = stringLiteralResult.Remainder;
if (!stringLiteralResult.WasSuccessful)
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected '.', followed by the persist mode."]);
var openBracketResult = Parse.Char('(').Token()(input);
input = openBracketResult.Remainder;
if (!openBracketResult.WasSuccessful)
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected '('"]);
persistMode = Markup.MarkupConstantPersistMode.FromString;
content = stringLiteralResult.Value[1..^1].Unescape();
}
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);
input = contentResult.Remainder;
if (!contentResult.WasSuccessful)
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected constant value"]);
persistMode = persistModeResult.Value.ToLowerInvariant() switch
{
"bin" => Markup.MarkupConstantPersistMode.Binary,
"str" => Markup.MarkupConstantPersistMode.FromString,
"can" => Markup.MarkupConstantPersistMode.Canonical,
_ => null
};
var closeBracketResult = Parse.Char(')').Token()(input);
input = closeBracketResult.Remainder;
if (!closeBracketResult.WasSuccessful)
return Result.Failure<IDirective>(input, "Invalid constant directive", ["Expected ')'"]);
var openBracketResult = Parse.Char('(').Token()(input);
input = openBracketResult.Remainder;
if (!openBracketResult.WasSuccessful)
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 typeName = typeNameResult.Value;
if (persistMode == Markup.MarkupConstantPersistMode.FromString)
{
directive = new StringEncodedConstantDirective(constantName, typeName, contentResult.Value)
directive = new StringEncodedConstantDirective(constantName, typeName, content)
{
Line = line,
Column = column,
@@ -133,7 +147,7 @@ partial class Lexer
}
else if (persistMode == Markup.MarkupConstantPersistMode.Canonical)
{
directive = new CanonicalInstanceConstantDirective(constantName, typeName, contentResult.Value)
directive = new CanonicalInstanceConstantDirective(constantName, typeName, content)
{
Line = line,
Column = column,
@@ -142,7 +156,7 @@ partial class Lexer
else if (persistMode == Markup.MarkupConstantPersistMode.Binary)
{
byte[] constantBytes;
var byteParts = contentResult.Value.Split(',');
var byteParts = content.Split(',');
if (byteParts.Length == 1)
{
var constantStr = byteParts[0].Trim();
@@ -167,7 +181,7 @@ partial class Lexer
}
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
@@ -185,7 +199,7 @@ partial class Lexer
}
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;
+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();
// 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<QualifiedTypeName> QualifiedTypeName = ParseQualifiedTypeName;
@@ -70,4 +80,10 @@ public static partial class Lexer
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 StringEncodedConstantDirective(string name, QualifiedTypeName typeName, string content)
: base(name, typeName, $"{typeName}.str({content})")
: base(name, typeName, $"{typeName}(\"{content.Escape()}\")")
{
Content = content;
PersistMode = MarkupConstantPersistMode.FromString;
+3 -1
View File
@@ -45,6 +45,8 @@ public class Assembly
.constant const3 = Color.str(255, 0, 0, 255)
.constant const4 = Font.str(JetBrains Mono)
.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
Default_cont:
@@ -77,7 +79,7 @@ Alt_locl:
_output.WriteLine(ast.ToString());
Assert.NotNull(ast);
Assert.Equal(20, ast.Directives.Count());
Assert.Equal(22, ast.Directives.Count());
Assert.Equal(24, ast.Code.Count());
}