starting on scene expansion

This commit is contained in:
Rudis Muiznieks 2014-06-30 11:59:56 -05:00
parent e9c7d4c391
commit 4538edd9a3
11 changed files with 159 additions and 21 deletions

View File

@ -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>

View File

@ -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
{
}
}

View File

@ -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;

View File

@ -0,0 +1,9 @@
namespace Ficdown.Parser.Engine
{
using Model.Story;
public interface ISceneLinker
{
void ExpandScenes(Story story);
}
}

View File

@ -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;

View File

@ -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);
}
}
}
}
}
}

View File

@ -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));
}
}
}

View File

@ -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);

View File

@ -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" />

View File

@ -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)
};
}
}
}

View File

@ -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; }
}
}