feat: parse content text (base case).
This commit is contained in:
34
InkBlot.Tests/ContentTextTest.cs
Normal file
34
InkBlot.Tests/ContentTextTest.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using InkBlot.ParseHierarchy;
|
||||||
|
using Shouldly;
|
||||||
|
|
||||||
|
namespace InkBlot.Tests;
|
||||||
|
|
||||||
|
public class ContentTextTest
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData("Hello!", "Hello!", 0)]
|
||||||
|
[InlineData("Hel-lo!", "Hel-lo!", 0)]
|
||||||
|
[InlineData("Hel\\lo!", "Hello!", 0)]
|
||||||
|
[InlineData("Hel<lo!", "Hel<lo!", 0)]
|
||||||
|
// TODO: check error situations better when diverts (->), threads (<-) and tags (#) are supported
|
||||||
|
[InlineData("Hel->lo!", "", 1)]
|
||||||
|
[InlineData("Hel<-lo!", "", 1)]
|
||||||
|
[InlineData("Hel#lo!", "", 1)]
|
||||||
|
public void TestBaseContentSuccess(string inkInput, string result, int numErrors)
|
||||||
|
{
|
||||||
|
// parse the story
|
||||||
|
var fileReader = new PreMadeFileReader([
|
||||||
|
("main.ink", inkInput)
|
||||||
|
]);
|
||||||
|
var parser = new InkBlotParser();
|
||||||
|
var (story, diagnostics) = parser.Parse(fileReader, "main.ink");
|
||||||
|
|
||||||
|
// check the diagnostic counts match
|
||||||
|
diagnostics.Count().ShouldBe(numErrors);
|
||||||
|
|
||||||
|
// check the contents match (only if there was no diagnostic)
|
||||||
|
if (numErrors != 0) return;
|
||||||
|
var contents = story.StoryNodes.ToArray();
|
||||||
|
contents.ShouldBe([new Content(result)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
InkBlot.Tests/Helpers.cs
Normal file
15
InkBlot.Tests/Helpers.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace InkBlot.Tests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A file reader where the contents are directly provided as strings.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filesToContents">A map between file names and their contents.</param>
|
||||||
|
internal class PreMadeFileReader((string, string)[] filesToContents) : IFileReader
|
||||||
|
{
|
||||||
|
public Stream GetContents(string filename)
|
||||||
|
{
|
||||||
|
return new MemoryStream(Encoding.UTF8.GetBytes(filesToContents.First(e => e.Item1 == filename).Item2));
|
||||||
|
}
|
||||||
|
}
|
||||||
26
InkBlot.Tests/InkBlot.Tests.csproj
Normal file
26
InkBlot.Tests/InkBlot.Tests.csproj
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="coverlet.collector" Version="6.0.2"/>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0"/>
|
||||||
|
<PackageReference Include="Shouldly" Version="4.3.0"/>
|
||||||
|
<PackageReference Include="xunit" Version="2.9.2"/>
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Using Include="Xunit"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\InkBlot\InkBlot.csproj"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
#
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InkBlot", "InkBlot\InkBlot.csproj", "{EDFCA854-0AF5-4C8F-8820-C328915B0FFE}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InkBlot", "InkBlot\InkBlot.csproj", "{EDFCA854-0AF5-4C8F-8820-C328915B0FFE}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InkBlotTestConsoleApp", "InkBlotTestConsoleApp\InkBlotTestConsoleApp.csproj", "{1BD86CDC-A93C-44EE-AA6B-E87E57FBC8AE}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InkBlotTestConsoleApp", "InkBlotTestConsoleApp\InkBlotTestConsoleApp.csproj", "{1BD86CDC-A93C-44EE-AA6B-E87E57FBC8AE}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InkBlot.Tests", "InkBlot.Tests\InkBlot.Tests.csproj", "{1B83421E-8A2D-4862-9E84-D8E5EDE13264}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -18,5 +21,9 @@ Global
|
|||||||
{1BD86CDC-A93C-44EE-AA6B-E87E57FBC8AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{1BD86CDC-A93C-44EE-AA6B-E87E57FBC8AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{1BD86CDC-A93C-44EE-AA6B-E87E57FBC8AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{1BD86CDC-A93C-44EE-AA6B-E87E57FBC8AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{1BD86CDC-A93C-44EE-AA6B-E87E57FBC8AE}.Release|Any CPU.Build.0 = Release|Any CPU
|
{1BD86CDC-A93C-44EE-AA6B-E87E57FBC8AE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{1B83421E-8A2D-4862-9E84-D8E5EDE13264}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1B83421E-8A2D-4862-9E84-D8E5EDE13264}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1B83421E-8A2D-4862-9E84-D8E5EDE13264}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1B83421E-8A2D-4862-9E84-D8E5EDE13264}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
3
InkBlot/Assembly.cs
Normal file
3
InkBlot/Assembly.cs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("InkBlot.Tests")]
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
namespace InkBlot;
|
|
||||||
|
|
||||||
public class Class1
|
|
||||||
{
|
|
||||||
}
|
|
||||||
5
InkBlot/Error.cs
Normal file
5
InkBlot/Error.cs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
namespace InkBlot;
|
||||||
|
|
||||||
|
public record Range(int StartLine, int StartColumn, int EndLine, int EndColumn);
|
||||||
|
|
||||||
|
public record Diagnostic(string FileName, Range Range, string Message, string Context);
|
||||||
17
InkBlot/Generated/InkBlotAntlrGrammar.interp
Normal file
17
InkBlot/Generated/InkBlotAntlrGrammar.interp
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
token literal names:
|
||||||
|
null
|
||||||
|
null
|
||||||
|
null
|
||||||
|
|
||||||
|
token symbolic names:
|
||||||
|
null
|
||||||
|
Whitespace
|
||||||
|
CONTENT_TEXT_NO_ESCAPE_SIMPLE
|
||||||
|
|
||||||
|
rule names:
|
||||||
|
story
|
||||||
|
contentText
|
||||||
|
|
||||||
|
|
||||||
|
atn:
|
||||||
|
[4, 1, 2, 12, 2, 0, 7, 0, 2, 1, 7, 1, 1, 0, 4, 0, 6, 8, 0, 11, 0, 12, 0, 7, 1, 1, 1, 1, 1, 1, 0, 0, 2, 0, 2, 0, 0, 10, 0, 5, 1, 0, 0, 0, 2, 9, 1, 0, 0, 0, 4, 6, 3, 2, 1, 0, 5, 4, 1, 0, 0, 0, 6, 7, 1, 0, 0, 0, 7, 5, 1, 0, 0, 0, 7, 8, 1, 0, 0, 0, 8, 1, 1, 0, 0, 0, 9, 10, 5, 2, 0, 0, 10, 3, 1, 0, 0, 0, 1, 7]
|
||||||
2
InkBlot/Generated/InkBlotAntlrGrammar.tokens
Normal file
2
InkBlot/Generated/InkBlotAntlrGrammar.tokens
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Whitespace=1
|
||||||
|
CONTENT_TEXT_NO_ESCAPE_SIMPLE=2
|
||||||
75
InkBlot/Generated/InkBlotAntlrGrammarBaseListener.cs
Normal file
75
InkBlot/Generated/InkBlotAntlrGrammarBaseListener.cs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// ANTLR Version: 4.13.2
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Generated from E:/ProgettiUnity/InkAntlr/InkBlot/InkBlot/InkBlotAntlrGrammar.g4 by ANTLR 4.13.2
|
||||||
|
|
||||||
|
// Unreachable code detected
|
||||||
|
#pragma warning disable 0162
|
||||||
|
// The variable '...' is assigned but its value is never used
|
||||||
|
#pragma warning disable 0219
|
||||||
|
// Missing XML comment for publicly visible type or member '...'
|
||||||
|
#pragma warning disable 1591
|
||||||
|
// Ambiguous reference in cref attribute
|
||||||
|
#pragma warning disable 419
|
||||||
|
|
||||||
|
|
||||||
|
using Antlr4.Runtime.Misc;
|
||||||
|
using IErrorNode = Antlr4.Runtime.Tree.IErrorNode;
|
||||||
|
using ITerminalNode = Antlr4.Runtime.Tree.ITerminalNode;
|
||||||
|
using IToken = Antlr4.Runtime.IToken;
|
||||||
|
using ParserRuleContext = Antlr4.Runtime.ParserRuleContext;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This class provides an empty implementation of <see cref="IInkBlotAntlrGrammarListener"/>,
|
||||||
|
/// which can be extended to create a listener which only needs to handle a subset
|
||||||
|
/// of the available methods.
|
||||||
|
/// </summary>
|
||||||
|
[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.13.2")]
|
||||||
|
[System.Diagnostics.DebuggerNonUserCode]
|
||||||
|
[System.CLSCompliant(false)]
|
||||||
|
public partial class InkBlotAntlrGrammarBaseListener : IInkBlotAntlrGrammarListener {
|
||||||
|
/// <summary>
|
||||||
|
/// Enter a parse tree produced by <see cref="InkBlotAntlrGrammarParser.story"/>.
|
||||||
|
/// <para>The default implementation does nothing.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The parse tree.</param>
|
||||||
|
public virtual void EnterStory([NotNull] InkBlotAntlrGrammarParser.StoryContext context) { }
|
||||||
|
/// <summary>
|
||||||
|
/// Exit a parse tree produced by <see cref="InkBlotAntlrGrammarParser.story"/>.
|
||||||
|
/// <para>The default implementation does nothing.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The parse tree.</param>
|
||||||
|
public virtual void ExitStory([NotNull] InkBlotAntlrGrammarParser.StoryContext context) { }
|
||||||
|
/// <summary>
|
||||||
|
/// Enter a parse tree produced by <see cref="InkBlotAntlrGrammarParser.contentText"/>.
|
||||||
|
/// <para>The default implementation does nothing.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The parse tree.</param>
|
||||||
|
public virtual void EnterContentText([NotNull] InkBlotAntlrGrammarParser.ContentTextContext context) { }
|
||||||
|
/// <summary>
|
||||||
|
/// Exit a parse tree produced by <see cref="InkBlotAntlrGrammarParser.contentText"/>.
|
||||||
|
/// <para>The default implementation does nothing.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The parse tree.</param>
|
||||||
|
public virtual void ExitContentText([NotNull] InkBlotAntlrGrammarParser.ContentTextContext context) { }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// <remarks>The default implementation does nothing.</remarks>
|
||||||
|
public virtual void EnterEveryRule([NotNull] ParserRuleContext context) { }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// <remarks>The default implementation does nothing.</remarks>
|
||||||
|
public virtual void ExitEveryRule([NotNull] ParserRuleContext context) { }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// <remarks>The default implementation does nothing.</remarks>
|
||||||
|
public virtual void VisitTerminal([NotNull] ITerminalNode node) { }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// <remarks>The default implementation does nothing.</remarks>
|
||||||
|
public virtual void VisitErrorNode([NotNull] IErrorNode node) { }
|
||||||
|
}
|
||||||
57
InkBlot/Generated/InkBlotAntlrGrammarBaseVisitor.cs
Normal file
57
InkBlot/Generated/InkBlotAntlrGrammarBaseVisitor.cs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// ANTLR Version: 4.13.2
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Generated from E:/ProgettiUnity/InkAntlr/InkBlot/InkBlot/InkBlotAntlrGrammar.g4 by ANTLR 4.13.2
|
||||||
|
|
||||||
|
// Unreachable code detected
|
||||||
|
#pragma warning disable 0162
|
||||||
|
// The variable '...' is assigned but its value is never used
|
||||||
|
#pragma warning disable 0219
|
||||||
|
// Missing XML comment for publicly visible type or member '...'
|
||||||
|
#pragma warning disable 1591
|
||||||
|
// Ambiguous reference in cref attribute
|
||||||
|
#pragma warning disable 419
|
||||||
|
|
||||||
|
using Antlr4.Runtime.Misc;
|
||||||
|
using Antlr4.Runtime.Tree;
|
||||||
|
using IToken = Antlr4.Runtime.IToken;
|
||||||
|
using ParserRuleContext = Antlr4.Runtime.ParserRuleContext;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This class provides an empty implementation of <see cref="IInkBlotAntlrGrammarVisitor{Result}"/>,
|
||||||
|
/// which can be extended to create a visitor which only needs to handle a subset
|
||||||
|
/// of the available methods.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="Result">The return type of the visit operation.</typeparam>
|
||||||
|
[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.13.2")]
|
||||||
|
[System.Diagnostics.DebuggerNonUserCode]
|
||||||
|
[System.CLSCompliant(false)]
|
||||||
|
public partial class InkBlotAntlrGrammarBaseVisitor<Result> : AbstractParseTreeVisitor<Result>, IInkBlotAntlrGrammarVisitor<Result> {
|
||||||
|
/// <summary>
|
||||||
|
/// Visit a parse tree produced by <see cref="InkBlotAntlrGrammarParser.story"/>.
|
||||||
|
/// <para>
|
||||||
|
/// The default implementation returns the result of calling <see cref="AbstractParseTreeVisitor{Result}.VisitChildren(IRuleNode)"/>
|
||||||
|
/// on <paramref name="context"/>.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The parse tree.</param>
|
||||||
|
/// <return>The visitor result.</return>
|
||||||
|
public virtual Result VisitStory([NotNull] InkBlotAntlrGrammarParser.StoryContext context) { return VisitChildren(context); }
|
||||||
|
/// <summary>
|
||||||
|
/// Visit a parse tree produced by <see cref="InkBlotAntlrGrammarParser.contentText"/>.
|
||||||
|
/// <para>
|
||||||
|
/// The default implementation returns the result of calling <see cref="AbstractParseTreeVisitor{Result}.VisitChildren(IRuleNode)"/>
|
||||||
|
/// on <paramref name="context"/>.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The parse tree.</param>
|
||||||
|
/// <return>The visitor result.</return>
|
||||||
|
public virtual Result VisitContentText([NotNull] InkBlotAntlrGrammarParser.ContentTextContext context) { return VisitChildren(context); }
|
||||||
|
}
|
||||||
120
InkBlot/Generated/InkBlotAntlrGrammarLexer.cs
Normal file
120
InkBlot/Generated/InkBlotAntlrGrammarLexer.cs
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// ANTLR Version: 4.13.2
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Generated from E:/ProgettiUnity/InkAntlr/InkBlot/InkBlot/InkBlotAntlrGrammar.g4 by ANTLR 4.13.2
|
||||||
|
|
||||||
|
// Unreachable code detected
|
||||||
|
#pragma warning disable 0162
|
||||||
|
// The variable '...' is assigned but its value is never used
|
||||||
|
#pragma warning disable 0219
|
||||||
|
// Missing XML comment for publicly visible type or member '...'
|
||||||
|
#pragma warning disable 1591
|
||||||
|
// Ambiguous reference in cref attribute
|
||||||
|
#pragma warning disable 419
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using Antlr4.Runtime;
|
||||||
|
using Antlr4.Runtime.Atn;
|
||||||
|
using Antlr4.Runtime.Misc;
|
||||||
|
using DFA = Antlr4.Runtime.Dfa.DFA;
|
||||||
|
|
||||||
|
[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.13.2")]
|
||||||
|
[System.CLSCompliant(false)]
|
||||||
|
public partial class InkBlotAntlrGrammarLexer : Lexer {
|
||||||
|
protected static DFA[] decisionToDFA;
|
||||||
|
protected static PredictionContextCache sharedContextCache = new PredictionContextCache();
|
||||||
|
public const int
|
||||||
|
Whitespace=1, CONTENT_TEXT_NO_ESCAPE_SIMPLE=2;
|
||||||
|
public static string[] channelNames = {
|
||||||
|
"DEFAULT_TOKEN_CHANNEL", "HIDDEN"
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string[] modeNames = {
|
||||||
|
"DEFAULT_MODE"
|
||||||
|
};
|
||||||
|
|
||||||
|
public static readonly string[] ruleNames = {
|
||||||
|
"Whitespace", "CONTENT_TEXT_NO_ESCAPE_SIMPLE"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public InkBlotAntlrGrammarLexer(ICharStream input)
|
||||||
|
: this(input, Console.Out, Console.Error) { }
|
||||||
|
|
||||||
|
public InkBlotAntlrGrammarLexer(ICharStream input, TextWriter output, TextWriter errorOutput)
|
||||||
|
: base(input, output, errorOutput)
|
||||||
|
{
|
||||||
|
Interpreter = new LexerATNSimulator(this, _ATN, decisionToDFA, sharedContextCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly string[] _LiteralNames = {
|
||||||
|
};
|
||||||
|
private static readonly string[] _SymbolicNames = {
|
||||||
|
null, "Whitespace", "CONTENT_TEXT_NO_ESCAPE_SIMPLE"
|
||||||
|
};
|
||||||
|
public static readonly IVocabulary DefaultVocabulary = new Vocabulary(_LiteralNames, _SymbolicNames);
|
||||||
|
|
||||||
|
[NotNull]
|
||||||
|
public override IVocabulary Vocabulary
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return DefaultVocabulary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GrammarFileName { get { return "InkBlotAntlrGrammar.g4"; } }
|
||||||
|
|
||||||
|
public override string[] RuleNames { get { return ruleNames; } }
|
||||||
|
|
||||||
|
public override string[] ChannelNames { get { return channelNames; } }
|
||||||
|
|
||||||
|
public override string[] ModeNames { get { return modeNames; } }
|
||||||
|
|
||||||
|
public override int[] SerializedAtn { get { return _serializedATN; } }
|
||||||
|
|
||||||
|
static InkBlotAntlrGrammarLexer() {
|
||||||
|
decisionToDFA = new DFA[_ATN.NumberOfDecisions];
|
||||||
|
for (int i = 0; i < _ATN.NumberOfDecisions; i++) {
|
||||||
|
decisionToDFA[i] = new DFA(_ATN.GetDecisionState(i), i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override bool Sempred(RuleContext _localctx, int ruleIndex, int predIndex) {
|
||||||
|
switch (ruleIndex) {
|
||||||
|
case 1 : return CONTENT_TEXT_NO_ESCAPE_SIMPLE_sempred(_localctx, predIndex);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
private bool CONTENT_TEXT_NO_ESCAPE_SIMPLE_sempred(RuleContext _localctx, int predIndex) {
|
||||||
|
switch (predIndex) {
|
||||||
|
case 0: return InputStream.LA(1) != '>' ;
|
||||||
|
case 1: return InputStream.LA(1) != '-' && InputStream.LA(1) != '>' ;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] _serializedATN = {
|
||||||
|
4,0,2,21,6,-1,2,0,7,0,2,1,7,1,1,0,4,0,7,8,0,11,0,12,0,8,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,4,1,18,8,1,11,1,12,1,19,0,0,2,1,1,3,2,1,0,3,2,0,9,9,32,32,
|
||||||
|
5,0,10,10,13,13,35,60,92,92,123,125,1,0,0,65535,25,0,1,1,0,0,0,0,3,1,0,
|
||||||
|
0,0,1,6,1,0,0,0,3,17,1,0,0,0,5,7,7,0,0,0,6,5,1,0,0,0,7,8,1,0,0,0,8,6,1,
|
||||||
|
0,0,0,8,9,1,0,0,0,9,2,1,0,0,0,10,18,8,1,0,0,11,12,5,92,0,0,12,18,7,2,0,
|
||||||
|
0,13,14,5,45,0,0,14,18,4,1,0,0,15,16,5,60,0,0,16,18,4,1,1,0,17,10,1,0,
|
||||||
|
0,0,17,11,1,0,0,0,17,13,1,0,0,0,17,15,1,0,0,0,18,19,1,0,0,0,19,17,1,0,
|
||||||
|
0,0,19,20,1,0,0,0,20,4,1,0,0,0,4,0,8,17,19,0
|
||||||
|
};
|
||||||
|
|
||||||
|
public static readonly ATN _ATN =
|
||||||
|
new ATNDeserializer().Deserialize(_serializedATN);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
23
InkBlot/Generated/InkBlotAntlrGrammarLexer.interp
Normal file
23
InkBlot/Generated/InkBlotAntlrGrammarLexer.interp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
token literal names:
|
||||||
|
null
|
||||||
|
null
|
||||||
|
null
|
||||||
|
|
||||||
|
token symbolic names:
|
||||||
|
null
|
||||||
|
Whitespace
|
||||||
|
CONTENT_TEXT_NO_ESCAPE_SIMPLE
|
||||||
|
|
||||||
|
rule names:
|
||||||
|
Whitespace
|
||||||
|
CONTENT_TEXT_NO_ESCAPE_SIMPLE
|
||||||
|
|
||||||
|
channel names:
|
||||||
|
DEFAULT_TOKEN_CHANNEL
|
||||||
|
HIDDEN
|
||||||
|
|
||||||
|
mode names:
|
||||||
|
DEFAULT_MODE
|
||||||
|
|
||||||
|
atn:
|
||||||
|
[4, 0, 2, 21, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 1, 0, 4, 0, 7, 8, 0, 11, 0, 12, 0, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 1, 18, 8, 1, 11, 1, 12, 1, 19, 0, 0, 2, 1, 1, 3, 2, 1, 0, 3, 2, 0, 9, 9, 32, 32, 5, 0, 10, 10, 13, 13, 35, 60, 92, 92, 123, 125, 1, 0, 0, 65535, 25, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 1, 6, 1, 0, 0, 0, 3, 17, 1, 0, 0, 0, 5, 7, 7, 0, 0, 0, 6, 5, 1, 0, 0, 0, 7, 8, 1, 0, 0, 0, 8, 6, 1, 0, 0, 0, 8, 9, 1, 0, 0, 0, 9, 2, 1, 0, 0, 0, 10, 18, 8, 1, 0, 0, 11, 12, 5, 92, 0, 0, 12, 18, 7, 2, 0, 0, 13, 14, 5, 45, 0, 0, 14, 18, 4, 1, 0, 0, 15, 16, 5, 60, 0, 0, 16, 18, 4, 1, 1, 0, 17, 10, 1, 0, 0, 0, 17, 11, 1, 0, 0, 0, 17, 13, 1, 0, 0, 0, 17, 15, 1, 0, 0, 0, 18, 19, 1, 0, 0, 0, 19, 17, 1, 0, 0, 0, 19, 20, 1, 0, 0, 0, 20, 4, 1, 0, 0, 0, 4, 0, 8, 17, 19, 0]
|
||||||
2
InkBlot/Generated/InkBlotAntlrGrammarLexer.tokens
Normal file
2
InkBlot/Generated/InkBlotAntlrGrammarLexer.tokens
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Whitespace=1
|
||||||
|
CONTENT_TEXT_NO_ESCAPE_SIMPLE=2
|
||||||
53
InkBlot/Generated/InkBlotAntlrGrammarListener.cs
Normal file
53
InkBlot/Generated/InkBlotAntlrGrammarListener.cs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// ANTLR Version: 4.13.2
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Generated from E:/ProgettiUnity/InkAntlr/InkBlot/InkBlot/InkBlotAntlrGrammar.g4 by ANTLR 4.13.2
|
||||||
|
|
||||||
|
// Unreachable code detected
|
||||||
|
#pragma warning disable 0162
|
||||||
|
// The variable '...' is assigned but its value is never used
|
||||||
|
#pragma warning disable 0219
|
||||||
|
// Missing XML comment for publicly visible type or member '...'
|
||||||
|
#pragma warning disable 1591
|
||||||
|
// Ambiguous reference in cref attribute
|
||||||
|
#pragma warning disable 419
|
||||||
|
|
||||||
|
using Antlr4.Runtime.Misc;
|
||||||
|
using IParseTreeListener = Antlr4.Runtime.Tree.IParseTreeListener;
|
||||||
|
using IToken = Antlr4.Runtime.IToken;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This interface defines a complete listener for a parse tree produced by
|
||||||
|
/// <see cref="InkBlotAntlrGrammarParser"/>.
|
||||||
|
/// </summary>
|
||||||
|
[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.13.2")]
|
||||||
|
[System.CLSCompliant(false)]
|
||||||
|
public interface IInkBlotAntlrGrammarListener : IParseTreeListener {
|
||||||
|
/// <summary>
|
||||||
|
/// Enter a parse tree produced by <see cref="InkBlotAntlrGrammarParser.story"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The parse tree.</param>
|
||||||
|
void EnterStory([NotNull] InkBlotAntlrGrammarParser.StoryContext context);
|
||||||
|
/// <summary>
|
||||||
|
/// Exit a parse tree produced by <see cref="InkBlotAntlrGrammarParser.story"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The parse tree.</param>
|
||||||
|
void ExitStory([NotNull] InkBlotAntlrGrammarParser.StoryContext context);
|
||||||
|
/// <summary>
|
||||||
|
/// Enter a parse tree produced by <see cref="InkBlotAntlrGrammarParser.contentText"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The parse tree.</param>
|
||||||
|
void EnterContentText([NotNull] InkBlotAntlrGrammarParser.ContentTextContext context);
|
||||||
|
/// <summary>
|
||||||
|
/// Exit a parse tree produced by <see cref="InkBlotAntlrGrammarParser.contentText"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The parse tree.</param>
|
||||||
|
void ExitContentText([NotNull] InkBlotAntlrGrammarParser.ContentTextContext context);
|
||||||
|
}
|
||||||
205
InkBlot/Generated/InkBlotAntlrGrammarParser.cs
Normal file
205
InkBlot/Generated/InkBlotAntlrGrammarParser.cs
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// ANTLR Version: 4.13.2
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Generated from E:/ProgettiUnity/InkAntlr/InkBlot/InkBlot/InkBlotAntlrGrammar.g4 by ANTLR 4.13.2
|
||||||
|
|
||||||
|
// Unreachable code detected
|
||||||
|
#pragma warning disable 0162
|
||||||
|
// The variable '...' is assigned but its value is never used
|
||||||
|
#pragma warning disable 0219
|
||||||
|
// Missing XML comment for publicly visible type or member '...'
|
||||||
|
#pragma warning disable 1591
|
||||||
|
// Ambiguous reference in cref attribute
|
||||||
|
#pragma warning disable 419
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Antlr4.Runtime;
|
||||||
|
using Antlr4.Runtime.Atn;
|
||||||
|
using Antlr4.Runtime.Misc;
|
||||||
|
using Antlr4.Runtime.Tree;
|
||||||
|
using DFA = Antlr4.Runtime.Dfa.DFA;
|
||||||
|
|
||||||
|
[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.13.2")]
|
||||||
|
[System.CLSCompliant(false)]
|
||||||
|
public partial class InkBlotAntlrGrammarParser : Parser {
|
||||||
|
protected static DFA[] decisionToDFA;
|
||||||
|
protected static PredictionContextCache sharedContextCache = new PredictionContextCache();
|
||||||
|
public const int
|
||||||
|
Whitespace=1, CONTENT_TEXT_NO_ESCAPE_SIMPLE=2;
|
||||||
|
public const int
|
||||||
|
RULE_story = 0, RULE_contentText = 1;
|
||||||
|
public static readonly string[] ruleNames = {
|
||||||
|
"story", "contentText"
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly string[] _LiteralNames = {
|
||||||
|
};
|
||||||
|
private static readonly string[] _SymbolicNames = {
|
||||||
|
null, "Whitespace", "CONTENT_TEXT_NO_ESCAPE_SIMPLE"
|
||||||
|
};
|
||||||
|
public static readonly IVocabulary DefaultVocabulary = new Vocabulary(_LiteralNames, _SymbolicNames);
|
||||||
|
|
||||||
|
[NotNull]
|
||||||
|
public override IVocabulary Vocabulary
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return DefaultVocabulary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GrammarFileName { get { return "InkBlotAntlrGrammar.g4"; } }
|
||||||
|
|
||||||
|
public override string[] RuleNames { get { return ruleNames; } }
|
||||||
|
|
||||||
|
public override int[] SerializedAtn { get { return _serializedATN; } }
|
||||||
|
|
||||||
|
static InkBlotAntlrGrammarParser() {
|
||||||
|
decisionToDFA = new DFA[_ATN.NumberOfDecisions];
|
||||||
|
for (int i = 0; i < _ATN.NumberOfDecisions; i++) {
|
||||||
|
decisionToDFA[i] = new DFA(_ATN.GetDecisionState(i), i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public InkBlotAntlrGrammarParser(ITokenStream input) : this(input, Console.Out, Console.Error) { }
|
||||||
|
|
||||||
|
public InkBlotAntlrGrammarParser(ITokenStream input, TextWriter output, TextWriter errorOutput)
|
||||||
|
: base(input, output, errorOutput)
|
||||||
|
{
|
||||||
|
Interpreter = new ParserATNSimulator(this, _ATN, decisionToDFA, sharedContextCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class StoryContext : ParserRuleContext {
|
||||||
|
[System.Diagnostics.DebuggerNonUserCode] public ContentTextContext[] contentText() {
|
||||||
|
return GetRuleContexts<ContentTextContext>();
|
||||||
|
}
|
||||||
|
[System.Diagnostics.DebuggerNonUserCode] public ContentTextContext contentText(int i) {
|
||||||
|
return GetRuleContext<ContentTextContext>(i);
|
||||||
|
}
|
||||||
|
public StoryContext(ParserRuleContext parent, int invokingState)
|
||||||
|
: base(parent, invokingState)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public override int RuleIndex { get { return RULE_story; } }
|
||||||
|
[System.Diagnostics.DebuggerNonUserCode]
|
||||||
|
public override void EnterRule(IParseTreeListener listener) {
|
||||||
|
IInkBlotAntlrGrammarListener typedListener = listener as IInkBlotAntlrGrammarListener;
|
||||||
|
if (typedListener != null) typedListener.EnterStory(this);
|
||||||
|
}
|
||||||
|
[System.Diagnostics.DebuggerNonUserCode]
|
||||||
|
public override void ExitRule(IParseTreeListener listener) {
|
||||||
|
IInkBlotAntlrGrammarListener typedListener = listener as IInkBlotAntlrGrammarListener;
|
||||||
|
if (typedListener != null) typedListener.ExitStory(this);
|
||||||
|
}
|
||||||
|
[System.Diagnostics.DebuggerNonUserCode]
|
||||||
|
public override TResult Accept<TResult>(IParseTreeVisitor<TResult> visitor) {
|
||||||
|
IInkBlotAntlrGrammarVisitor<TResult> typedVisitor = visitor as IInkBlotAntlrGrammarVisitor<TResult>;
|
||||||
|
if (typedVisitor != null) return typedVisitor.VisitStory(this);
|
||||||
|
else return visitor.VisitChildren(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[RuleVersion(0)]
|
||||||
|
public StoryContext story() {
|
||||||
|
StoryContext _localctx = new StoryContext(Context, State);
|
||||||
|
EnterRule(_localctx, 0, RULE_story);
|
||||||
|
int _la;
|
||||||
|
try {
|
||||||
|
EnterOuterAlt(_localctx, 1);
|
||||||
|
{
|
||||||
|
State = 5;
|
||||||
|
ErrorHandler.Sync(this);
|
||||||
|
_la = TokenStream.LA(1);
|
||||||
|
do {
|
||||||
|
{
|
||||||
|
{
|
||||||
|
State = 4;
|
||||||
|
contentText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
State = 7;
|
||||||
|
ErrorHandler.Sync(this);
|
||||||
|
_la = TokenStream.LA(1);
|
||||||
|
} while ( _la==CONTENT_TEXT_NO_ESCAPE_SIMPLE );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (RecognitionException re) {
|
||||||
|
_localctx.exception = re;
|
||||||
|
ErrorHandler.ReportError(this, re);
|
||||||
|
ErrorHandler.Recover(this, re);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
ExitRule();
|
||||||
|
}
|
||||||
|
return _localctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class ContentTextContext : ParserRuleContext {
|
||||||
|
[System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CONTENT_TEXT_NO_ESCAPE_SIMPLE() { return GetToken(InkBlotAntlrGrammarParser.CONTENT_TEXT_NO_ESCAPE_SIMPLE, 0); }
|
||||||
|
public ContentTextContext(ParserRuleContext parent, int invokingState)
|
||||||
|
: base(parent, invokingState)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public override int RuleIndex { get { return RULE_contentText; } }
|
||||||
|
[System.Diagnostics.DebuggerNonUserCode]
|
||||||
|
public override void EnterRule(IParseTreeListener listener) {
|
||||||
|
IInkBlotAntlrGrammarListener typedListener = listener as IInkBlotAntlrGrammarListener;
|
||||||
|
if (typedListener != null) typedListener.EnterContentText(this);
|
||||||
|
}
|
||||||
|
[System.Diagnostics.DebuggerNonUserCode]
|
||||||
|
public override void ExitRule(IParseTreeListener listener) {
|
||||||
|
IInkBlotAntlrGrammarListener typedListener = listener as IInkBlotAntlrGrammarListener;
|
||||||
|
if (typedListener != null) typedListener.ExitContentText(this);
|
||||||
|
}
|
||||||
|
[System.Diagnostics.DebuggerNonUserCode]
|
||||||
|
public override TResult Accept<TResult>(IParseTreeVisitor<TResult> visitor) {
|
||||||
|
IInkBlotAntlrGrammarVisitor<TResult> typedVisitor = visitor as IInkBlotAntlrGrammarVisitor<TResult>;
|
||||||
|
if (typedVisitor != null) return typedVisitor.VisitContentText(this);
|
||||||
|
else return visitor.VisitChildren(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[RuleVersion(0)]
|
||||||
|
public ContentTextContext contentText() {
|
||||||
|
ContentTextContext _localctx = new ContentTextContext(Context, State);
|
||||||
|
EnterRule(_localctx, 2, RULE_contentText);
|
||||||
|
try {
|
||||||
|
EnterOuterAlt(_localctx, 1);
|
||||||
|
{
|
||||||
|
State = 9;
|
||||||
|
Match(CONTENT_TEXT_NO_ESCAPE_SIMPLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (RecognitionException re) {
|
||||||
|
_localctx.exception = re;
|
||||||
|
ErrorHandler.ReportError(this, re);
|
||||||
|
ErrorHandler.Recover(this, re);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
ExitRule();
|
||||||
|
}
|
||||||
|
return _localctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] _serializedATN = {
|
||||||
|
4,1,2,12,2,0,7,0,2,1,7,1,1,0,4,0,6,8,0,11,0,12,0,7,1,1,1,1,1,1,0,0,2,0,
|
||||||
|
2,0,0,10,0,5,1,0,0,0,2,9,1,0,0,0,4,6,3,2,1,0,5,4,1,0,0,0,6,7,1,0,0,0,7,
|
||||||
|
5,1,0,0,0,7,8,1,0,0,0,8,1,1,0,0,0,9,10,5,2,0,0,10,3,1,0,0,0,1,7
|
||||||
|
};
|
||||||
|
|
||||||
|
public static readonly ATN _ATN =
|
||||||
|
new ATNDeserializer().Deserialize(_serializedATN);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
46
InkBlot/Generated/InkBlotAntlrGrammarVisitor.cs
Normal file
46
InkBlot/Generated/InkBlotAntlrGrammarVisitor.cs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// ANTLR Version: 4.13.2
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Generated from E:/ProgettiUnity/InkAntlr/InkBlot/InkBlot/InkBlotAntlrGrammar.g4 by ANTLR 4.13.2
|
||||||
|
|
||||||
|
// Unreachable code detected
|
||||||
|
#pragma warning disable 0162
|
||||||
|
// The variable '...' is assigned but its value is never used
|
||||||
|
#pragma warning disable 0219
|
||||||
|
// Missing XML comment for publicly visible type or member '...'
|
||||||
|
#pragma warning disable 1591
|
||||||
|
// Ambiguous reference in cref attribute
|
||||||
|
#pragma warning disable 419
|
||||||
|
|
||||||
|
using Antlr4.Runtime.Misc;
|
||||||
|
using Antlr4.Runtime.Tree;
|
||||||
|
using IToken = Antlr4.Runtime.IToken;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This interface defines a complete generic visitor for a parse tree produced
|
||||||
|
/// by <see cref="InkBlotAntlrGrammarParser"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="Result">The return type of the visit operation.</typeparam>
|
||||||
|
[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.13.2")]
|
||||||
|
[System.CLSCompliant(false)]
|
||||||
|
public interface IInkBlotAntlrGrammarVisitor<Result> : IParseTreeVisitor<Result> {
|
||||||
|
/// <summary>
|
||||||
|
/// Visit a parse tree produced by <see cref="InkBlotAntlrGrammarParser.story"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The parse tree.</param>
|
||||||
|
/// <return>The visitor result.</return>
|
||||||
|
Result VisitStory([NotNull] InkBlotAntlrGrammarParser.StoryContext context);
|
||||||
|
/// <summary>
|
||||||
|
/// Visit a parse tree produced by <see cref="InkBlotAntlrGrammarParser.contentText"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The parse tree.</param>
|
||||||
|
/// <return>The visitor result.</return>
|
||||||
|
Result VisitContentText([NotNull] InkBlotAntlrGrammarParser.ContentTextContext context);
|
||||||
|
}
|
||||||
17
InkBlot/IFileReader.cs
Normal file
17
InkBlot/IFileReader.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace InkBlot;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An object that can read files.
|
||||||
|
/// </summary>
|
||||||
|
public interface IFileReader
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Get the contents of a file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filename">
|
||||||
|
/// Name of the file to read. The name is relative to the main ink file. The name is normalized
|
||||||
|
/// (uses only single forward slashes between path elements).
|
||||||
|
/// </param>
|
||||||
|
/// <returns>A stream with the contents of the file.</returns>
|
||||||
|
Stream GetContents(string filename);
|
||||||
|
}
|
||||||
@@ -6,4 +6,8 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Antlr4.Runtime.Standard" Version="4.13.1"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
7
InkBlot/InkBlotAntlrGrammar.g4
Normal file
7
InkBlot/InkBlotAntlrGrammar.g4
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
grammar InkBlotAntlrGrammar;
|
||||||
|
|
||||||
|
import InkBlotAntlrLexer;
|
||||||
|
|
||||||
|
story: contentText+ ;
|
||||||
|
|
||||||
|
contentText: CONTENT_TEXT_NO_ESCAPE_SIMPLE ;
|
||||||
22
InkBlot/InkBlotAntlrLexer.g4
Normal file
22
InkBlot/InkBlotAntlrLexer.g4
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
lexer grammar InkBlotAntlrLexer;
|
||||||
|
|
||||||
|
Whitespace: [ \t]+ ;
|
||||||
|
|
||||||
|
// see InkParser_Content.cs, ContentTextNoEscape and ContentTextAllowingEcapeChar for the escape case
|
||||||
|
// this works for the base case where we're not parsing a string, nor a choice
|
||||||
|
CONTENT_TEXT_NO_ESCAPE_SIMPLE:
|
||||||
|
(
|
||||||
|
// any character is valid, except for:
|
||||||
|
// - {} ==> identifies embedded logic
|
||||||
|
// - | ==> text alternatives, is forbidden even in non-logic text for some reason
|
||||||
|
// - \n\r ==> a new line of content
|
||||||
|
// - # ==> a tag
|
||||||
|
// - \, < and - with exceptions (see below)
|
||||||
|
~[{}|\n\r\\#-<]
|
||||||
|
// any character can be escaped
|
||||||
|
| '\\' [\u0000-\uFFFF] // TODO: is there a better way to say "any character"?
|
||||||
|
// accept a - only if not followed by a > (->, a divert)
|
||||||
|
| '-' { InputStream.LA(1) != '>' }?
|
||||||
|
// same for threads (<-) and glue (<>)
|
||||||
|
| '<' { InputStream.LA(1) != '-' && InputStream.LA(1) != '>' }?
|
||||||
|
)+ ;
|
||||||
34
InkBlot/InkBlotParser.cs
Normal file
34
InkBlot/InkBlotParser.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using Antlr4.Runtime;
|
||||||
|
using Antlr4.Runtime.Atn;
|
||||||
|
using Antlr4.Runtime.Tree;
|
||||||
|
using InkBlot.ParseHierarchy;
|
||||||
|
using InkBlot.Visitor;
|
||||||
|
|
||||||
|
namespace InkBlot;
|
||||||
|
|
||||||
|
public class InkBlotParser
|
||||||
|
{
|
||||||
|
public (Story, IEnumerable<Diagnostic>) Parse(IFileReader fileReader, string mainFileName)
|
||||||
|
{
|
||||||
|
var stream = fileReader.GetContents(mainFileName);
|
||||||
|
var inputStream = new AntlrInputStream(stream);
|
||||||
|
var lexer = new InkBlotAntlrGrammarLexer(inputStream);
|
||||||
|
var lexerErrorListener = new LexerErrorListener();
|
||||||
|
lexer.RemoveErrorListeners();
|
||||||
|
lexer.AddErrorListener(lexerErrorListener);
|
||||||
|
var tokens = new CommonTokenStream(lexer);
|
||||||
|
var parser = new InkBlotAntlrGrammarParser(tokens);
|
||||||
|
var parserErrorListener = new ParserErrorListener();
|
||||||
|
parser.RemoveErrorListeners();
|
||||||
|
parser.AddErrorListener(parserErrorListener);
|
||||||
|
#if DEBUG
|
||||||
|
parser.AddErrorListener(new DiagnosticErrorListener());
|
||||||
|
parser.Interpreter.PredictionMode = PredictionMode.LL_EXACT_AMBIG_DETECTION;
|
||||||
|
#endif
|
||||||
|
var tree = parser.story();
|
||||||
|
var listener = new Listener();
|
||||||
|
var walker = new ParseTreeWalker();
|
||||||
|
walker.Walk(listener, tree);
|
||||||
|
return (listener.Story, lexerErrorListener.Diagnostics.Concat(parserErrorListener.Diagnostics));
|
||||||
|
}
|
||||||
|
}
|
||||||
16
InkBlot/LexerErrorListener.cs
Normal file
16
InkBlot/LexerErrorListener.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using Antlr4.Runtime;
|
||||||
|
|
||||||
|
namespace InkBlot;
|
||||||
|
|
||||||
|
// from https://github.com/YarnSpinnerTool/YarnSpinner/blob/main/YarnSpinner.Compiler/ErrorListener.cs#L247
|
||||||
|
internal sealed class LexerErrorListener : IAntlrErrorListener<int>
|
||||||
|
{
|
||||||
|
public readonly List<Diagnostic> Diagnostics = [];
|
||||||
|
|
||||||
|
public void SyntaxError(TextWriter output, IRecognizer recognizer, int offendingSymbol, int line,
|
||||||
|
int charPositionInLine, string msg, RecognitionException e)
|
||||||
|
{
|
||||||
|
var range = new Range(line - 1, charPositionInLine, line - 1, charPositionInLine + 1);
|
||||||
|
Diagnostics.Add(new Diagnostic("", range, msg, ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
3
InkBlot/ParseHierarchy/Content.cs
Normal file
3
InkBlot/ParseHierarchy/Content.cs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
namespace InkBlot.ParseHierarchy;
|
||||||
|
|
||||||
|
public record Content(string Text) : StoryNode;
|
||||||
3
InkBlot/ParseHierarchy/Story.cs
Normal file
3
InkBlot/ParseHierarchy/Story.cs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
namespace InkBlot.ParseHierarchy;
|
||||||
|
|
||||||
|
public record Story(IEnumerable<StoryNode> StoryNodes) : StoryNode;
|
||||||
6
InkBlot/ParseHierarchy/StoryNode.cs
Normal file
6
InkBlot/ParseHierarchy/StoryNode.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace InkBlot.ParseHierarchy;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Any node in the parsed story
|
||||||
|
/// </summary>
|
||||||
|
public record StoryNode;
|
||||||
58
InkBlot/ParserErrorListener.cs
Normal file
58
InkBlot/ParserErrorListener.cs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Antlr4.Runtime;
|
||||||
|
|
||||||
|
namespace InkBlot;
|
||||||
|
|
||||||
|
internal sealed class ParserErrorListener : BaseErrorListener
|
||||||
|
{
|
||||||
|
public readonly List<Diagnostic> Diagnostics = [];
|
||||||
|
|
||||||
|
public override void SyntaxError(TextWriter output, IRecognizer recognizer, IToken offendingSymbol, int line,
|
||||||
|
int charPositionInLine,
|
||||||
|
string msg, RecognitionException e)
|
||||||
|
{
|
||||||
|
// adapted from https://github.com/YarnSpinnerTool/YarnSpinner/blob/84e17d04fdf4ac2f824ed1d4d277bb3a1dde73f6/YarnSpinner.Compiler/ErrorListener.cs#L266
|
||||||
|
var range = new Range(line - 1, charPositionInLine, line - 1, charPositionInLine + 1);
|
||||||
|
var context = "";
|
||||||
|
|
||||||
|
if (offendingSymbol.TokenSource != null)
|
||||||
|
{
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
|
||||||
|
// the line with the error on it
|
||||||
|
var input = offendingSymbol.TokenSource.InputStream.ToString();
|
||||||
|
if (input != null)
|
||||||
|
{
|
||||||
|
var lines = input.Split('\n');
|
||||||
|
var errorLine = lines[line - 1];
|
||||||
|
builder.AppendLine(errorLine);
|
||||||
|
|
||||||
|
// adding indicator symbols pointing out where the error is
|
||||||
|
// on the line
|
||||||
|
var start = offendingSymbol.StartIndex;
|
||||||
|
var stop = offendingSymbol.StopIndex;
|
||||||
|
if (start >= 0 && stop >= 0)
|
||||||
|
{
|
||||||
|
// the end point of the error in "line space"
|
||||||
|
var end = stop - start + charPositionInLine + 1;
|
||||||
|
for (var i = 0; i < end; i++)
|
||||||
|
// move over until we are at the point we need to
|
||||||
|
// be
|
||||||
|
if (i >= charPositionInLine && i < end)
|
||||||
|
builder.Append('^');
|
||||||
|
else
|
||||||
|
builder.Append(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
context = builder.ToString();
|
||||||
|
|
||||||
|
range = new Range(offendingSymbol.Line - 1, offendingSymbol.Column, offendingSymbol.Line - 1,
|
||||||
|
offendingSymbol.Column + offendingSymbol.Text.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var diagnostic = new Diagnostic("", range, msg, context);
|
||||||
|
|
||||||
|
Diagnostics.Add(diagnostic);
|
||||||
|
}
|
||||||
|
}
|
||||||
5
InkBlot/Visitor/Listener.cs
Normal file
5
InkBlot/Visitor/Listener.cs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
namespace InkBlot.Visitor;
|
||||||
|
|
||||||
|
public partial class Listener : InkBlotAntlrGrammarBaseListener
|
||||||
|
{
|
||||||
|
}
|
||||||
34
InkBlot/Visitor/ListenerContentText.cs
Normal file
34
InkBlot/Visitor/ListenerContentText.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Antlr4.Runtime.Tree;
|
||||||
|
|
||||||
|
namespace InkBlot.Visitor;
|
||||||
|
|
||||||
|
public partial class Listener
|
||||||
|
{
|
||||||
|
private readonly ParseTreeProperty<string> _contentTextValue = new();
|
||||||
|
|
||||||
|
private readonly Regex _escapeRegex = MyRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex(@"\\(.)")]
|
||||||
|
private static partial Regex MyRegex();
|
||||||
|
|
||||||
|
private string GetContentText(InkBlotAntlrGrammarParser.ContentTextContext context)
|
||||||
|
{
|
||||||
|
return _contentTextValue.Get(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ExitContentText(InkBlotAntlrGrammarParser.ContentTextContext context)
|
||||||
|
{
|
||||||
|
// escape sequences are captured by this node, but not interpreted
|
||||||
|
var contentWithEscapes = context.CONTENT_TEXT_NO_ESCAPE_SIMPLE().ToString();
|
||||||
|
if (contentWithEscapes == null)
|
||||||
|
// when does this happen? :?
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
|
||||||
|
// replace \X with just X
|
||||||
|
var content = _escapeRegex.Replace(contentWithEscapes, match => match.Groups[1].Value);
|
||||||
|
|
||||||
|
// save the result
|
||||||
|
_contentTextValue.Put(context, content);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
InkBlot/Visitor/ListenerStory.cs
Normal file
20
InkBlot/Visitor/ListenerStory.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using InkBlot.ParseHierarchy;
|
||||||
|
|
||||||
|
namespace InkBlot.Visitor;
|
||||||
|
|
||||||
|
public partial class Listener
|
||||||
|
{
|
||||||
|
private Story? _story;
|
||||||
|
|
||||||
|
public Story Story => _story ?? throw new InvalidOperationException("No story found yet.");
|
||||||
|
|
||||||
|
public override void ExitStory(InkBlotAntlrGrammarParser.StoryContext context)
|
||||||
|
{
|
||||||
|
var storyNodes = context.children.Select(child => child switch
|
||||||
|
{
|
||||||
|
InkBlotAntlrGrammarParser.ContentTextContext contentText => new Content(GetContentText(contentText)),
|
||||||
|
_ => throw new InvalidOperationException($"unknown context of type {child.GetType()}")
|
||||||
|
});
|
||||||
|
_story = new Story(storyNodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,4 +7,8 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\InkBlot\InkBlot.csproj"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
// See https://aka.ms/new-console-template for more information
|
// See https://aka.ms/new-console-template for more information
|
||||||
|
|
||||||
Console.WriteLine("Hello, World!");
|
using System.Text;
|
||||||
|
using InkBlot;
|
||||||
|
|
||||||
|
var parser = new InkBlotParser();
|
||||||
|
parser.Parse(new PreMadeFileReader(new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "main.ink", "Hel-\\lo!" }
|
||||||
|
}), "main.ink");
|
||||||
|
|
||||||
|
|
||||||
|
internal class PreMadeFileReader(Dictionary<string, string> filesToContents) : IFileReader
|
||||||
|
{
|
||||||
|
public Stream GetContents(string filename)
|
||||||
|
{
|
||||||
|
return new MemoryStream(Encoding.UTF8.GetBytes(filesToContents[filename]));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user