starting on scene expansion
This commit is contained in:
parent
e9c7d4c391
commit
4538edd9a3
|
@ -49,6 +49,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="BlockHandlerTests.cs" />
|
||||
<Compile Include="IntegrationTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="TestStories\Resources.Designer.cs">
|
||||
<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 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)
|
||||
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
|
||||
{
|
||||
|
@ -53,7 +64,7 @@
|
|||
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)
|
||||
{
|
||||
var key = Utilities.NormalizeString(scene.Name);
|
||||
|
@ -61,21 +72,18 @@
|
|||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -88,10 +96,16 @@
|
|||
if (sceneName.Success)
|
||||
{
|
||||
scene.Name = sceneName.Groups["text"].Value;
|
||||
IList<string> conditions, toggles;
|
||||
ParseHref(sceneName.Groups["href"].Value, out conditions, out toggles);
|
||||
IList<string> conditions;
|
||||
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.Toggles = toggles;
|
||||
}
|
||||
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(),
|
||||
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;
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
internal static class Utilities
|
||||
|
@ -8,5 +11,41 @@
|
|||
{
|
||||
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
|
||||
{
|
||||
private IBlockHandler _blockHandler;
|
||||
|
||||
public IBlockHandler BlockHandler
|
||||
{
|
||||
get { return _blockHandler ?? (_blockHandler = new BlockHandler()); }
|
||||
set { _blockHandler = value; }
|
||||
}
|
||||
|
||||
private ISceneLinker _sceneLinker;
|
||||
|
||||
public ISceneLinker SceneLinker
|
||||
{
|
||||
get { return _sceneLinker ?? (_sceneLinker = new SceneLinker()); }
|
||||
set { _sceneLinker = value; }
|
||||
}
|
||||
|
||||
public Story ParseStory(string storyFilePath)
|
||||
{
|
||||
var lines = File.ReadAllLines(storyFilePath);
|
||||
|
|
|
@ -42,10 +42,13 @@
|
|||
<Compile Include="Engine\BlockHandler.cs" />
|
||||
<Compile Include="Engine\IBlockHandler.cs" />
|
||||
<Compile Include="Engine\RegexLib.cs" />
|
||||
<Compile Include="Engine\SceneLinker.cs" />
|
||||
<Compile Include="Engine\Utilities.cs" />
|
||||
<Compile Include="FicDownParser.cs" />
|
||||
<Compile Include="Model\Parser\Block.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="Model\Story\Action.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 Description { get; set; }
|
||||
public IList<string> Conditions { get; set; }
|
||||
public IList<string> Toggles { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue