starting on scene expansion
This commit is contained in:
parent
e9c7d4c391
commit
4538edd9a3
|
@ -49,6 +49,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="BlockHandlerTests.cs" />
|
<Compile Include="BlockHandlerTests.cs" />
|
||||||
|
<Compile Include="IntegrationTests.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="TestStories\Resources.Designer.cs">
|
<Compile Include="TestStories\Resources.Designer.cs">
|
||||||
<AutoGen>True</AutoGen>
|
<AutoGen>True</AutoGen>
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ficdown.Parser.Tests
|
||||||
|
{
|
||||||
|
class IntegrationTests
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -42,8 +42,19 @@
|
||||||
var storyBlock = blocks.Single(b => b.Type == BlockType.Story);
|
var storyBlock = blocks.Single(b => b.Type == BlockType.Story);
|
||||||
var storyName = RegexLib.Anchors.Match(storyBlock.Name);
|
var storyName = RegexLib.Anchors.Match(storyBlock.Name);
|
||||||
|
|
||||||
|
string storyTarget;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Utilities.ParseHref(storyName.Groups["href"].Value, out storyTarget);
|
||||||
|
}
|
||||||
|
catch (FormatException)
|
||||||
|
{
|
||||||
|
throw new FormatException(string.Format("Story href should only have target: {0}",
|
||||||
|
storyName.Groups["href"].Value));
|
||||||
|
}
|
||||||
|
|
||||||
if (!storyName.Success)
|
if (!storyName.Success)
|
||||||
throw new FormatException("Story name must be a link to the first scene.");
|
throw new FormatException("Story name must link to the first scene.");
|
||||||
|
|
||||||
var story = new Story
|
var story = new Story
|
||||||
{
|
{
|
||||||
|
@ -53,7 +64,7 @@
|
||||||
States = new Dictionary<string, IList<Action>>()
|
States = new Dictionary<string, IList<Action>>()
|
||||||
};
|
};
|
||||||
|
|
||||||
var scenes = blocks.Where(b => b.Type == BlockType.Scene).Select(b => BlockToScene(b));
|
var scenes = blocks.Where(b => b.Type == BlockType.Scene).Select(BlockToScene);
|
||||||
foreach (var scene in scenes)
|
foreach (var scene in scenes)
|
||||||
{
|
{
|
||||||
var key = Utilities.NormalizeString(scene.Name);
|
var key = Utilities.NormalizeString(scene.Name);
|
||||||
|
@ -61,21 +72,18 @@
|
||||||
story.Scenes[key].Add(scene);
|
story.Scenes[key].Add(scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!story.Scenes.ContainsKey(storyTarget))
|
||||||
|
throw new FormatException(string.Format("Story targets non-existent scene: {0}", storyTarget));
|
||||||
|
story.FirstScene =
|
||||||
|
story.Scenes[storyTarget].SingleOrDefault(
|
||||||
|
s => s.Conditions == null || s.Conditions.All(c => c.StartsWith("!")));
|
||||||
|
if (story.FirstScene == null)
|
||||||
|
throw new FormatException(string.Format("Story targets scene with no unconditional definition: {0}",
|
||||||
|
storyTarget));
|
||||||
|
|
||||||
return story;
|
return story;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ParseHref(string href, out IList<string> conditions, out IList<string> toggles)
|
|
||||||
{
|
|
||||||
var match = RegexLib.Href.Match(href);
|
|
||||||
if (match.Success)
|
|
||||||
{
|
|
||||||
var cstr = match.Groups["conditions"].Value;
|
|
||||||
var tstr = match.Groups["toggles"].Value;
|
|
||||||
}
|
|
||||||
else throw new FormatException(string.Format("Invalid href: {0}", href));
|
|
||||||
conditions = null;
|
|
||||||
toggles = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Scene BlockToScene(Block block)
|
private Scene BlockToScene(Block block)
|
||||||
{
|
{
|
||||||
|
@ -88,10 +96,16 @@
|
||||||
if (sceneName.Success)
|
if (sceneName.Success)
|
||||||
{
|
{
|
||||||
scene.Name = sceneName.Groups["text"].Value;
|
scene.Name = sceneName.Groups["text"].Value;
|
||||||
IList<string> conditions, toggles;
|
IList<string> conditions;
|
||||||
ParseHref(sceneName.Groups["href"].Value, out conditions, out toggles);
|
try
|
||||||
|
{
|
||||||
|
Utilities.ParseHref(sceneName.Groups["href"].Value, out conditions);
|
||||||
|
}
|
||||||
|
catch (FormatException)
|
||||||
|
{
|
||||||
|
throw new FormatException(string.Format("Scene href should only have conditions: {0}", block.Name));
|
||||||
|
}
|
||||||
scene.Conditions = conditions;
|
scene.Conditions = conditions;
|
||||||
scene.Toggles = toggles;
|
|
||||||
}
|
}
|
||||||
else scene.Name = block.Name;
|
else scene.Name = block.Name;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Ficdown.Parser.Engine
|
||||||
|
{
|
||||||
|
using Model.Story;
|
||||||
|
|
||||||
|
public interface ISceneLinker
|
||||||
|
{
|
||||||
|
void ExpandScenes(Story story);
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,16 @@
|
||||||
string.Format(@"(?<anchor>\[(?<text>{0})\]\([ ]*(?<href>{1})[ ]*\))", GetNestedBracketsPattern(),
|
string.Format(@"(?<anchor>\[(?<text>{0})\]\([ ]*(?<href>{1})[ ]*\))", GetNestedBracketsPattern(),
|
||||||
GetNestedParensPattern()), RegexOptions.Singleline | RegexOptions.Compiled);
|
GetNestedParensPattern()), RegexOptions.Singleline | RegexOptions.Compiled);
|
||||||
|
|
||||||
public static Regex Href = new Regex(@"^(\?(?<conditions>[^#]+))?(#(?<toggles>.+))?$", RegexOptions.Compiled);
|
private const string RegexValidName = @"[a-zA-Z](-?[a-zA-Z0-9])*";
|
||||||
|
private static readonly string RegexHrefTarget = string.Format(@"\/({0})", RegexValidName);
|
||||||
|
private static readonly string RegexHrefConditions = string.Format(@"\?((!?{0})(&!?{0})*)?", RegexValidName);
|
||||||
|
private static readonly string RegexHrefToggles = string.Format(@"#({0})(\+{0})*", RegexValidName);
|
||||||
|
|
||||||
|
public static Regex Href =
|
||||||
|
new Regex(
|
||||||
|
string.Format(@"^(?<target>{0})?(?<conditions>{1})?(?<toggles>{2})?$", RegexHrefTarget,
|
||||||
|
RegexHrefConditions, RegexHrefToggles), RegexOptions.Compiled);
|
||||||
|
|
||||||
|
|
||||||
private const int _nestDepth = 6;
|
private const int _nestDepth = 6;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
namespace Ficdown.Parser.Engine
|
||||||
|
{
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Model.Story;
|
||||||
|
|
||||||
|
public class SceneLinker : ISceneLinker
|
||||||
|
{
|
||||||
|
public void ExpandScenes(Story story)
|
||||||
|
{
|
||||||
|
var newScenes = new Dictionary<string, IList<Scene>>();
|
||||||
|
foreach(var key in story.Scenes.Keys)
|
||||||
|
{
|
||||||
|
newScenes.Add(key, new List<Scene>());
|
||||||
|
foreach (var scene in story.Scenes[key])
|
||||||
|
{
|
||||||
|
var anchors = RegexLib.Anchors.Matches(scene.Description);
|
||||||
|
foreach (Match anchor in anchors)
|
||||||
|
{
|
||||||
|
string target;
|
||||||
|
IList<string> conditions, toggles;
|
||||||
|
Utilities.ParseHref(anchor.Groups["href"].Value, out target, out conditions, out toggles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,8 @@
|
||||||
namespace Ficdown.Parser.Engine
|
namespace Ficdown.Parser.Engine
|
||||||
{
|
{
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
internal static class Utilities
|
internal static class Utilities
|
||||||
|
@ -8,5 +11,41 @@
|
||||||
{
|
{
|
||||||
return Regex.Replace(Regex.Replace(raw.ToLower(), @"^\W+|\W+$", string.Empty), @"\W+", "-");
|
return Regex.Replace(Regex.Replace(raw.ToLower(), @"^\W+|\W+$", string.Empty), @"\W+", "-");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ParseHref(string href, out string target)
|
||||||
|
{
|
||||||
|
IList<string> conditions, toggles;
|
||||||
|
ParseHref(href, out target, out conditions, out toggles);
|
||||||
|
if(conditions != null || toggles != null) throw new FormatException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ParseHref(string href, out IList<string> conditions)
|
||||||
|
{
|
||||||
|
string target;
|
||||||
|
IList<string> toggles;
|
||||||
|
ParseHref(href, out target, out conditions, out toggles);
|
||||||
|
if(target != null || toggles != null) throw new FormatException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ParseHref(string href, out string target, out IList<string> conditions, out IList<string> toggles)
|
||||||
|
{
|
||||||
|
target = null;
|
||||||
|
conditions = null;
|
||||||
|
toggles = null;
|
||||||
|
var match = RegexLib.Href.Match(href);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
var ttstr = match.Groups["target"].Value;
|
||||||
|
var cstr = match.Groups["conditions"].Value;
|
||||||
|
var tstr = match.Groups["toggles"].Value;
|
||||||
|
if (!string.IsNullOrEmpty(ttstr))
|
||||||
|
target = ttstr.TrimStart('/');
|
||||||
|
if (!string.IsNullOrEmpty(cstr))
|
||||||
|
conditions = new List<string>(cstr.TrimStart('?').Split('&').Select(c => c.Trim().ToLower()));
|
||||||
|
if (!string.IsNullOrEmpty(tstr))
|
||||||
|
toggles = new List<string>(tstr.TrimStart('#').Split('+').Select(t => t.Trim().ToLower()));
|
||||||
|
}
|
||||||
|
else throw new FormatException(string.Format("Invalid href: {0}", href));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,13 +7,20 @@
|
||||||
public class FicdownParser
|
public class FicdownParser
|
||||||
{
|
{
|
||||||
private IBlockHandler _blockHandler;
|
private IBlockHandler _blockHandler;
|
||||||
|
|
||||||
public IBlockHandler BlockHandler
|
public IBlockHandler BlockHandler
|
||||||
{
|
{
|
||||||
get { return _blockHandler ?? (_blockHandler = new BlockHandler()); }
|
get { return _blockHandler ?? (_blockHandler = new BlockHandler()); }
|
||||||
set { _blockHandler = value; }
|
set { _blockHandler = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ISceneLinker _sceneLinker;
|
||||||
|
|
||||||
|
public ISceneLinker SceneLinker
|
||||||
|
{
|
||||||
|
get { return _sceneLinker ?? (_sceneLinker = new SceneLinker()); }
|
||||||
|
set { _sceneLinker = value; }
|
||||||
|
}
|
||||||
|
|
||||||
public Story ParseStory(string storyFilePath)
|
public Story ParseStory(string storyFilePath)
|
||||||
{
|
{
|
||||||
var lines = File.ReadAllLines(storyFilePath);
|
var lines = File.ReadAllLines(storyFilePath);
|
||||||
|
|
|
@ -42,10 +42,13 @@
|
||||||
<Compile Include="Engine\BlockHandler.cs" />
|
<Compile Include="Engine\BlockHandler.cs" />
|
||||||
<Compile Include="Engine\IBlockHandler.cs" />
|
<Compile Include="Engine\IBlockHandler.cs" />
|
||||||
<Compile Include="Engine\RegexLib.cs" />
|
<Compile Include="Engine\RegexLib.cs" />
|
||||||
|
<Compile Include="Engine\SceneLinker.cs" />
|
||||||
<Compile Include="Engine\Utilities.cs" />
|
<Compile Include="Engine\Utilities.cs" />
|
||||||
<Compile Include="FicDownParser.cs" />
|
<Compile Include="FicDownParser.cs" />
|
||||||
<Compile Include="Model\Parser\Block.cs" />
|
<Compile Include="Model\Parser\Block.cs" />
|
||||||
<Compile Include="Model\Parser\BlockType.cs" />
|
<Compile Include="Model\Parser\BlockType.cs" />
|
||||||
|
<Compile Include="Engine\ISceneLinker.cs" />
|
||||||
|
<Compile Include="Model\Story\Extensions\SceneExtensions.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Model\Story\Action.cs" />
|
<Compile Include="Model\Story\Action.cs" />
|
||||||
<Compile Include="Model\Story\Scene.cs" />
|
<Compile Include="Model\Story\Scene.cs" />
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace Ficdown.Parser.Model.Story.Extensions
|
||||||
|
{
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
public static class SceneExtensions
|
||||||
|
{
|
||||||
|
public static Scene Clone(this Scene scene)
|
||||||
|
{
|
||||||
|
return new Scene
|
||||||
|
{
|
||||||
|
Name = scene.Name,
|
||||||
|
Description = scene.Description,
|
||||||
|
Conditions = new List<string>(scene.Conditions)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,5 @@
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public IList<string> Conditions { get; set; }
|
public IList<string> Conditions { get; set; }
|
||||||
public IList<string> Toggles { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue