diff --git a/Ficdown.Parser.Tests/Ficdown.Parser.Tests.csproj b/Ficdown.Parser.Tests/Ficdown.Parser.Tests.csproj index 8cc0931..f452ba8 100644 --- a/Ficdown.Parser.Tests/Ficdown.Parser.Tests.csproj +++ b/Ficdown.Parser.Tests/Ficdown.Parser.Tests.csproj @@ -61,7 +61,7 @@ - + diff --git a/Ficdown.Parser.Tests/IntegrationTests.cs b/Ficdown.Parser.Tests/IntegrationTests.cs index c91f81b..5ab902a 100644 --- a/Ficdown.Parser.Tests/IntegrationTests.cs +++ b/Ficdown.Parser.Tests/IntegrationTests.cs @@ -1,6 +1,7 @@ namespace Ficdown.Parser.Tests { using System; + using System.Linq; using System.Text; using ServiceStack.Text; using TestStories; @@ -12,9 +13,11 @@ public void CanParseValidStoryFile() { var parser = new FicdownParser(); - var storyText = Encoding.UTF8.GetString(Resources.the_robot_king); + var storyText = Encoding.UTF8.GetString(Resources.TheRobotKing); var story = parser.ParseStory(storyText); Assert.NotNull(story); + Assert.Equal("The Robot King", story.Name); + Assert.Equal("Robot Cave", story.Scenes[story.FirstScene].First().Name); Console.WriteLine(story.Dump()); } } diff --git a/Ficdown.Parser.Tests/SceneLinkerTests.cs b/Ficdown.Parser.Tests/SceneLinkerTests.cs index 0212e0d..18585c8 100644 --- a/Ficdown.Parser.Tests/SceneLinkerTests.cs +++ b/Ficdown.Parser.Tests/SceneLinkerTests.cs @@ -41,6 +41,7 @@ } }); sl.ExpandScenes(story); + Console.WriteLine(story.Dump()); Assert.Equal(2, story.Scenes["test-scene"].Count); Scene passed = null, failed = null; Assert.DoesNotThrow(() => @@ -57,22 +58,6 @@ Assert.NotNull(failed); } - [Fact] - public void NegativeConditionalAnchorGetsReplacedCorrectly() - { - var sl = new SceneLinker(); - var story = MockStoryWithScenes(new[] - { - new Scene - { - Name = "Test Scene", - Description = "Test [passed|failed](?!test-condition) text." - } - }); - sl.ExpandScenes(story); - Console.WriteLine(story.Dump()); - } - [Fact] public void MultipleConditionalAnchorsGetReplacedCorrectly() { @@ -88,6 +73,24 @@ }); sl.ExpandScenes(story); Console.WriteLine(story.Dump()); + Assert.Equal(4, story.Scenes["test-scene"].Count); + Assert.Equal( + story.Scenes["test-scene"].Select(s => s.Conditions == null ? null : s.Conditions.ToArray()).ToArray(), + new[] + { + null, new[] {"test1-condition"}, new[] {"test2-condition"}, + new[] {"test1-condition", "test2-condition"} + }); + Assert.False( + story.Scenes["test-scene"].Any( + s => + s.Conditions != null && s.Conditions.Contains("test1-condition") && + s.Description.Contains("Test1 failed1."))); + Assert.False( + story.Scenes["test-scene"].Any( + s => + s.Conditions != null && s.Conditions.Contains("test2-condition") && + s.Description.Contains("Test2 failed2."))); } } } diff --git a/Ficdown.Parser.Tests/TestStories/Resources.Designer.cs b/Ficdown.Parser.Tests/TestStories/Resources.Designer.cs index 95e5703..f8b1859 100644 --- a/Ficdown.Parser.Tests/TestStories/Resources.Designer.cs +++ b/Ficdown.Parser.Tests/TestStories/Resources.Designer.cs @@ -63,9 +63,9 @@ namespace Ficdown.Parser.Tests.TestStories { /// /// Looks up a localized resource of type System.Byte[]. /// - internal static byte[] the_robot_king { + internal static byte[] TheRobotKing { get { - object obj = ResourceManager.GetObject("the_robot_king", resourceCulture); + object obj = ResourceManager.GetObject("TheRobotKing", resourceCulture); return ((byte[])(obj)); } } diff --git a/Ficdown.Parser.Tests/TestStories/Resources.resx b/Ficdown.Parser.Tests/TestStories/Resources.resx index 4ed3f95..b979b7c 100644 --- a/Ficdown.Parser.Tests/TestStories/Resources.resx +++ b/Ficdown.Parser.Tests/TestStories/Resources.resx @@ -118,7 +118,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - the-robot-king.md;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + therobotking.md;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 \ No newline at end of file diff --git a/Ficdown.Parser.Tests/TestStories/the-robot-king.md b/Ficdown.Parser.Tests/TestStories/TheRobotKing.md similarity index 89% rename from Ficdown.Parser.Tests/TestStories/the-robot-king.md rename to Ficdown.Parser.Tests/TestStories/TheRobotKing.md index 6bb727a..6cc29a9 100644 --- a/Ficdown.Parser.Tests/TestStories/the-robot-king.md +++ b/Ficdown.Parser.Tests/TestStories/TheRobotKing.md @@ -15,8 +15,8 @@ Your cave only has one tiny window, and through it you can see [the sun shining **What do you want to do?** - [Go outside and start walking to the palace.](/outside) -- [Wait for it to stop raining.](?!stopped-raining#stopped-raining) -- [Put on your raincoat.](?!raincoat#raincoat) +- [|Wait for it to stop raining.](?stopped-raining#stopped-raining) +- [|Put on your raincoat.](?raincoat#raincoat) ### Raincoat @@ -26,7 +26,7 @@ You take your raincoat and put it on. It fits perfectly! It feels like hours, but it finally stops raining. You hope you won't be late for your new job! -## [Outside](?!raincoat&!stopped-raining) +## Outside > You step through the door and feel the water flowing over your metal body. Brrr! That's cold! You start to think that maybe getting your raincoat would be a good idea. This is just the kind of rain that might turn you into a rusty robot statue if you stay in it too long. @@ -37,7 +37,7 @@ You're standing on your front porch in the pouring rain. You need to get to the - [Continue walking to the palace.](/rusted) - [Go back into your cave.](/robot-cave) -## Outside +## [Outside](?stopped-raining) You step through the door and feel the early afternoon sun warming your metal body. It feels good, but you were supposed to start your new job early in the morning! @@ -57,7 +57,7 @@ You will have a long time to think about your mistake while you wait for another **You have turned into a rusty robot statue!** -## [Outside](?raincoat&!stopped-raining) +## [Outside](?raincoat) You head out the door and into the rain. It's a good thing you put on your raincoat, because it's just the kind of rain that would probably turn you into a rusty robot statue if you stayed in it for too long. @@ -67,10 +67,10 @@ The palace guard looks you up and down. "What do you want?" he asks. **What will you tell him?** -- ["I'm the new janitor-bot!"](/palace-entrance#new-job) -- ["I'd like a tour of the palace!"](/palace-entrance) +- ["I'm the new janitor-bot!"](/palace-gate#new-job) +- ["I'd like a tour of the palace!"](/palace-gate) -## Palace Entrance +## Palace Gate The robot guard looks at you and [nods|frowns](?new-job). "[Oh yeah, they told me to expect you. You're supposed to be starting today right?|We don't do tours on weekdays. Hey, aren't you the new janitor-bot who's starting today?](?new-job)" @@ -117,13 +117,13 @@ You walk into the hall that leads to the living quarters, and find a gate blocki You're standing in the basement where new employees can pick up their uniforms and learn what their jobs are for the day. -[The Master Janitor Robot is pacing back and forth here, muttering to himself.|There is a funny looking robot here pacing back and forth, muttering to himself. That must be the Master Janitor Robot. When he notices you, he stops muttering and stares at you with crazy eyes.](#talked-to-master) +[The Master Janitor Robot is pacing back and forth here, muttering to himself.|There is a funny looking robot here pacing back and forth, muttering to himself. That must be the Master Janitor Robot. When he notices you, he stops muttering and stares at you with crazy eyes.](?talked-to-master) **What will you do?** - [Go back upstairs.](/palace-entrance) - [Ask the Master Janitor Robot what he's muttering about.](#talked-to-master+muttering) -- [Ask the Master Janitor Robot about your uniform.](#talked-to-master+uniform) +- [|Ask the Master Janitor Robot about your uniform.](?uniform#talked-to-master+uniform) - [Ask the Master Janitor Robot about the gate upstairs.](?tried-gate#talked-to-master+about-the-gate) - [Ask the Master Janitor Robot about your job.](?uniform#started-job) @@ -141,11 +141,11 @@ He walks to a box in the corner and pulls out a blue janitor's uniform, then han ### About the Gate -"Ahh, yes, the gate," says the Master Janitor Robot. "Quite a clever contraption. There's a scanner attached that looks for a special device that's sewn into the [uniform I gave you|uniform that employees here wear](?uniform).[ As I said, you'll want to head up there now to start cleaning room 13.](?started-job)" +"Ahh, yes, the gate," says the Master Janitor Robot. "Quite a clever contraption. There's a scanner attached that looks for a special device that's sewn into the [uniform I gave you|uniform that employees here wear](?uniform). [As I said, you'll want to head up there now to start cleaning room 13.](?started-job)" ### Started Job -"Ready to get going?" says the Master Janitor Robot. He continues before you have a chance to answer. "Good, good. Your first job will be to clean room 13 in the living quarters. That's where the Robot King keeps all of his spare robes and crowns. There's a janitor's closet right next to that room where you can get a mop to clean the floors, and a duster to dust off the crowns." +["Like I said before, your|"Ready to get going?" says the Master Janitor Robot. He continues before you have a chance to answer. "Good, good. Your](?started-job) first job will be to clean room 13 in the living quarters. That's where the Robot King keeps all of his spare robes and crowns. There's a janitor's closet right next to that room where you can get a mop to clean the floors, and a duster to dust off the crowns." The Master Janitor Robot scratches his chin for a moment, then resumes pacing back and forth and muttering to himself. @@ -155,7 +155,7 @@ You head into the hallway that leads to the living quarters and come to a large You notice with some alarm that there's no scanner on the inside of the gate. You don't know how to get back out! -## [Living Quarters](?uniform&!job-started) +## [Living Quarters](?uniform) That's when you realize that you never asked the Master Janitor Bot what your job here was. You just took your uniform and left! @@ -167,4 +167,4 @@ That's no problem though, because you already know what your job is. You continu You open the closet and grab the mop and duster. You're so excited! Your first day as a janitor working for a Robot King that looks just like you, and you are about to enter a room containing all of his spare robes and crowns. What fun! -**You have reached the end of the intro to The Robot King.** +**You have reached the end of the intro to The Robot King.** \ No newline at end of file diff --git a/Ficdown.Parser.Tests/UtilityTests.cs b/Ficdown.Parser.Tests/UtilityTests.cs index 5766598..1990800 100644 --- a/Ficdown.Parser.Tests/UtilityTests.cs +++ b/Ficdown.Parser.Tests/UtilityTests.cs @@ -94,17 +94,11 @@ Assert.Contains("condition-state", conditions); Assert.Null(toggles); - Utilities.ParseHref("?!condition-state", out target, out conditions, out toggles); - Assert.Null(target); - Assert.Equal(1, conditions.Count); - Assert.Contains("!condition-state", conditions); - Assert.Null(toggles); - - Utilities.ParseHref("?condition-1&!condition-2", out target, out conditions, out toggles); + Utilities.ParseHref("?condition-1&condition-2", out target, out conditions, out toggles); Assert.Null(target); Assert.Equal(2, conditions.Count); Assert.Contains("condition-1", conditions); - Assert.Contains("!condition-2", conditions); + Assert.Contains("condition-2", conditions); Assert.Null(toggles); } @@ -151,13 +145,14 @@ Assert.Equal(1, toggles.Count); Assert.Contains("toggle-state", toggles); - Utilities.ParseHref("?!condition-one&condition-two#toggle-state", out target, out conditions, out toggles); + Utilities.ParseHref("?condition-one&condition-two#toggle-one+toggle-two", out target, out conditions, out toggles); Assert.Null(target); Assert.Equal(2, conditions.Count); - Assert.Contains("!condition-one", conditions); + Assert.Contains("condition-one", conditions); Assert.Contains("condition-two", conditions); - Assert.Equal(1, toggles.Count); - Assert.Contains("toggle-state", toggles); + Assert.Equal(2, toggles.Count); + Assert.Contains("toggle-one", toggles); + Assert.Contains("toggle-two", toggles); } } } diff --git a/Ficdown.Parser/Engine/BlockHandler.cs b/Ficdown.Parser/Engine/BlockHandler.cs index 60e5ede..e08d579 100644 --- a/Ficdown.Parser/Engine/BlockHandler.cs +++ b/Ficdown.Parser/Engine/BlockHandler.cs @@ -90,7 +90,7 @@ var sceneName = RegexLib.Anchors.Match(block.Name); if (sceneName.Success) { - scene.Name = sceneName.Groups["text"].Value; + scene.Name = sceneName.Groups["text"].Value.Trim(); IList conditions; try { diff --git a/Ficdown.Parser/Engine/RegexLib.cs b/Ficdown.Parser/Engine/RegexLib.cs index a995c6c..54caf89 100644 --- a/Ficdown.Parser/Engine/RegexLib.cs +++ b/Ficdown.Parser/Engine/RegexLib.cs @@ -17,7 +17,7 @@ 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 RegexHrefConditions = string.Format(@"\?(({0})(&{0})*)?", RegexValidName); private static readonly string RegexHrefToggles = string.Format(@"#({0})(\+{0})*", RegexValidName); public static Regex Href = diff --git a/Ficdown.Parser/Engine/SceneLinker.cs b/Ficdown.Parser/Engine/SceneLinker.cs index 30ae622..68e9787 100644 --- a/Ficdown.Parser/Engine/SceneLinker.cs +++ b/Ficdown.Parser/Engine/SceneLinker.cs @@ -66,7 +66,6 @@ // make sure this is actually unique if (uniques.Any(u => u.Intersect(conditions).Count() == conditions.Count)) return; - uniques.Add(conditions); @@ -87,11 +86,7 @@ Utilities.ParseHref(anchor.Groups["href"].Value, out target, out conditions, out toggles); if (conditions != null) { - var satisfied = scene.Conditions == null - ? conditions.All(c => c.StartsWith("!")) - : conditions.All( - c => scene.Conditions.Contains(c) || - (c.StartsWith("!") && !scene.Conditions.Contains(c))); + var satisfied = scene.Conditions != null && conditions.All(c => scene.Conditions.Contains(c)); var text = anchor.Groups["text"].Value; var alts = RegexLib.ConditionalText.Match(text); @@ -103,17 +98,18 @@ RegexLib.EscapeChar.Replace(satisfied ? alts.Groups["true"].Value : alts.Groups["false"].Value, string.Empty); - // if there's no target or toggles, replace the whole anchor - if (target == null && toggles == null) + // if there's no target or toggles, or the replace text is an empty string, replace the whole anchor + if (string.IsNullOrEmpty(replace) || (target == null && toggles == null)) { scene.Description = scene.Description.Replace(anchor.Groups["anchor"].Value, replace); } // if there's a target or toggles, replace the text and remove the conditions on the anchor else { + var parsedHref = RegexLib.Href.Match(anchor.Groups["href"].Value); scene.Description = scene.Description.Replace(anchor.Groups["anchor"].Value, - string.Format("[{0}]({1}{2})", replace, anchor.Groups["target"].Value, - anchor.Groups["toggles"].Value)); + string.Format("[{0}]({1}{2})", replace, parsedHref.Groups["target"].Value, + parsedHref.Groups["toggles"].Value)); } } diff --git a/Ficdown.Parser/FicDownParser.cs b/Ficdown.Parser/FicDownParser.cs index 8616a8c..b17247e 100644 --- a/Ficdown.Parser/FicDownParser.cs +++ b/Ficdown.Parser/FicDownParser.cs @@ -1,5 +1,6 @@ namespace Ficdown.Parser { + using System; using Engine; using Model.Story; @@ -22,7 +23,7 @@ public Story ParseStory(string storyText) { - var lines = storyText.Split('\n'); + var lines = storyText.Split(new[] {"\n", "\r\n"}, StringSplitOptions.None); var blocks = BlockHandler.ExtractBlocks(lines); var story = BlockHandler.ParseBlocks(blocks); SceneLinker.ExpandScenes(story);