diff --git a/Ficdown.Parser.Tests/BlockHandlerTests.cs b/Ficdown.Parser.Tests/BlockHandlerTests.cs index f30a930..40424c8 100644 --- a/Ficdown.Parser.Tests/BlockHandlerTests.cs +++ b/Ficdown.Parser.Tests/BlockHandlerTests.cs @@ -1,29 +1,6 @@ namespace Ficdown.Parser.Tests { - using System; - using System.Collections.Generic; - using System.Text; - using Engine; - using Model.Parser; - using Model.Story; - using ServiceStack.Text; - using TestStories; - using Xunit; - public class BlockHandlerTests { - [Fact] - public void GoodTestStoryShouldParse() - { - var lines = Encoding.UTF8.GetString(Resources.the_robot_king).Split(new[] {'\r', '\n'}); - var handler = new BlockHandler(); - IEnumerable blocks = null; - Story story = null; - Assert.DoesNotThrow(() => blocks = handler.ExtractBlocks(lines)); - Assert.DoesNotThrow(() => story = handler.ParseBlocks(blocks)); - Assert.NotNull(story); - - Console.WriteLine(story.Dump()); - } } } diff --git a/Ficdown.Parser.Tests/IntegrationTests.cs b/Ficdown.Parser.Tests/IntegrationTests.cs index 500355f..c91f81b 100644 --- a/Ficdown.Parser.Tests/IntegrationTests.cs +++ b/Ficdown.Parser.Tests/IntegrationTests.cs @@ -1,12 +1,21 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Ficdown.Parser.Tests +namespace Ficdown.Parser.Tests { - class IntegrationTests + using System; + using System.Text; + using ServiceStack.Text; + using TestStories; + using Xunit; + + public class IntegrationTests { + [Fact] + public void CanParseValidStoryFile() + { + var parser = new FicdownParser(); + var storyText = Encoding.UTF8.GetString(Resources.the_robot_king); + var story = parser.ParseStory(storyText); + Assert.NotNull(story); + Console.WriteLine(story.Dump()); + } } } diff --git a/Ficdown.Parser/Engine/BlockHandler.cs b/Ficdown.Parser/Engine/BlockHandler.cs index bba148a..60e5ede 100644 --- a/Ficdown.Parser/Engine/BlockHandler.cs +++ b/Ficdown.Parser/Engine/BlockHandler.cs @@ -74,12 +74,7 @@ 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)); + story.FirstScene = storyTarget; return story; } diff --git a/Ficdown.Parser/Engine/SceneLinker.cs b/Ficdown.Parser/Engine/SceneLinker.cs index 3747716..e07a02d 100644 --- a/Ficdown.Parser/Engine/SceneLinker.cs +++ b/Ficdown.Parser/Engine/SceneLinker.cs @@ -1,13 +1,18 @@ namespace Ficdown.Parser.Engine { using System.Collections.Generic; + using System.Linq; using System.Text.RegularExpressions; using Model.Story; + using Model.Story.Extensions; public class SceneLinker : ISceneLinker { + public void ExpandScenes(Story story) { + VerifySanity(story); + var newScenes = new Dictionary>(); foreach(var key in story.Scenes.Keys) { @@ -15,14 +20,72 @@ foreach (var scene in story.Scenes[key]) { var anchors = RegexLib.Anchors.Matches(scene.Description); + // get a list of all unique condition combinations from the anchors + var uniques = new List>(); foreach (Match anchor in anchors) { string target; IList conditions, toggles; Utilities.ParseHref(anchor.Groups["href"].Value, out target, out conditions, out toggles); + if (conditions != null) + { + // union with the conditions required to reach this scene, if any + if (scene.Conditions != null) + { + conditions = conditions.Union(scene.Conditions).ToList(); + if (conditions.Count == scene.Conditions.Count) + continue; //WARN this anchor will never resolve false + } + + AddUnique(uniques, conditions); + } + } + // resolve the current scene + newScenes[key].Add(ResolveScene(scene, anchors)); + // resolve the uniques + foreach (var unique in uniques) + { + var uscene = scene.Clone(); + uscene.Conditions = unique; + newScenes[key].Add(ResolveScene(uscene, anchors)); } } } + story.Scenes = newScenes; + } + + private void AddUnique(IList> uniques, IList conditions) + { + // ignore this combo if there's a contradiction + if (conditions.Where(c => !c.StartsWith("!")) + .Any(c => conditions.Contains(string.Format("!{0}", c)))) + return; // WARN this anchor will never resolve true + + // make sure this is actually unique + if (uniques.Any(u => u.Intersect(conditions).Count() == conditions.Count)) return; + + uniques.Add(conditions); + + // we need to treat this unioned with all other existing uniques as another potential unique + var existing = new List>(uniques); + foreach (var old in existing) + { + AddUnique(uniques, old.Union(conditions).ToList()); + } + } + + private Scene ResolveScene(Scene scene, MatchCollection anchors) + { + foreach (Match anchor in anchors) + { + var satisfied = Utilities.ConditionsSatisfied(anchor.Groups["conditions"].Value, scene.Conditions); + } + return scene; + } + + private void VerifySanity(Story story) + { + } } } diff --git a/Ficdown.Parser/Engine/Utilities.cs b/Ficdown.Parser/Engine/Utilities.cs index da7e58b..7fc766e 100644 --- a/Ficdown.Parser/Engine/Utilities.cs +++ b/Ficdown.Parser/Engine/Utilities.cs @@ -47,5 +47,10 @@ } else throw new FormatException(string.Format("Invalid href: {0}", href)); } + + public static bool ConditionsSatisfied(string cquery, IList conditions) + { + return false; + } } } diff --git a/Ficdown.Parser/FicDownParser.cs b/Ficdown.Parser/FicDownParser.cs index 44f37a1..8616a8c 100644 --- a/Ficdown.Parser/FicDownParser.cs +++ b/Ficdown.Parser/FicDownParser.cs @@ -1,6 +1,5 @@ namespace Ficdown.Parser { - using System.IO; using Engine; using Model.Story; @@ -21,11 +20,13 @@ set { _sceneLinker = value; } } - public Story ParseStory(string storyFilePath) + public Story ParseStory(string storyText) { - var lines = File.ReadAllLines(storyFilePath); + var lines = storyText.Split('\n'); var blocks = BlockHandler.ExtractBlocks(lines); - return BlockHandler.ParseBlocks(blocks); + var story = BlockHandler.ParseBlocks(blocks); + SceneLinker.ExpandScenes(story); + return story; } } } diff --git a/Ficdown.Parser/Model/Story/Extensions/SceneExtensions.cs b/Ficdown.Parser/Model/Story/Extensions/SceneExtensions.cs index cdb960d..3e0838d 100644 --- a/Ficdown.Parser/Model/Story/Extensions/SceneExtensions.cs +++ b/Ficdown.Parser/Model/Story/Extensions/SceneExtensions.cs @@ -10,7 +10,7 @@ { Name = scene.Name, Description = scene.Description, - Conditions = new List(scene.Conditions) + Conditions = scene.Conditions == null ? null : new List(scene.Conditions) }; } } diff --git a/Ficdown.Parser/Model/Story/Story.cs b/Ficdown.Parser/Model/Story/Story.cs index b775ad7..3932302 100644 --- a/Ficdown.Parser/Model/Story/Story.cs +++ b/Ficdown.Parser/Model/Story/Story.cs @@ -6,7 +6,7 @@ { public string Name { get; set; } public string Description { get; set; } - public Scene FirstScene { get; set; } + public string FirstScene { get; set; } public IDictionary> States { get; set; } public IDictionary> Scenes { get; set; } }