From 5a6afdb72f10452c1486d6712125a966d2fa13b7 Mon Sep 17 00:00:00 2001 From: premek Date: Tue, 8 Aug 2017 16:28:55 +0200 Subject: [PATCH 1/5] flatparse wip --- pink/parser.lua | 37 ++++++++----------------------------- test/parser/basic.lua | 16 ++++++++-------- test/parser/glue.lua | 26 +++++++++----------------- test/parser/knot.lua | 23 +++++++++++++---------- 4 files changed, 38 insertions(+), 64 deletions(-) diff --git a/pink/parser.lua b/pink/parser.lua index 72c0f97..4351e6a 100644 --- a/pink/parser.lua +++ b/pink/parser.lua @@ -4,10 +4,6 @@ local S,C,Ct,Cc,Cg,Cb,Cf,Cmt,P,V = lpeg.S, lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.Cg, lpeg.Cb, lpeg.Cf, lpeg.Cmt, lpeg.P, lpeg.V -local concat = function (p) - return Cf(p, function (a,b) return a..b end) -end - local parserLogger = print local eof = -1 local sp = S" \t" ^0 + eof @@ -21,27 +17,20 @@ local commOL = sp * '//' * sp * (1-nl)^0 * wh local commML = sp * '/*' * wh * (P(1)-'*/')^0 * '*/' * wh local comm = commOL + commML + todo -local glue = P'<>'/'glue' *wh -- FIXME do not consume spaces after glue +local glue = Ct(P'<>'/'glue') *wh -- FIXME do not consume spaces after glue local divertSym = '->' *wh -local divertEndSym = C('END') *wh +local divertEndSym = Ct(C('END')) *wh local divertEnd = divertSym * divertEndSym local divertJump = Ct(divertSym/'divert' * addr * wh) local divert = divertEnd + divertJump -local knotHead = P('=')^2/'knot' * wh * C(id) * wh * P('=')^0 * wh -local stitchHead = P('=')^1/'stitch' * wh * C(id) * wh * P('=')^0 * wh +local knot = Ct(P('=')^2/'knot' * wh * C(id) * wh * P('=')^0) * wh +local stitch = Ct(P('=')^1/'stitch' * wh * C(id) * wh * P('=')^0) * wh local optDiv = '[' * C((P(1) - ']')^0) * ']' -local optStars = concat(wh * C(P'*') * (sp * C'*')^0) -local optStarsSameIndent = Cmt(Cb("indent") * optStars, - function (s, i, a, b) return a == b end) -local optStarsLEIndent = Cmt(Cb("indent") * optStars, - function (s, i, backtrack, this) - return string.len(this) <= string.len(backtrack) - end) - +local optStars = wh * C(P'*') * (sp * C'*')^0 local tag = Ct(wh * P('#')/'tag' * wh * V'text' * wh) @@ -50,11 +39,7 @@ local tag = Ct(wh * P('#')/'tag' * wh * V'text' * wh) local ink = P({ "lines", - knotKnot = Ct(knotHead * (V'line'-knotHead)^0 * wh), - knotStitch = Ct(stitchHead * (V'line'-stitchHead)^0 * wh), - knot = V'knotKnot' + V'knotStitch', - - stmt = glue + divert + V'knot' + optDiv + comm + V'include' + tag, + stmt = glue + divert + knot + stitch + V'option' + optDiv + comm + V'include' + tag, text = C((1-nl-V'stmt')^1) *wh, textE = C((1-nl-V'stmt')^0) *wh, @@ -62,22 +47,16 @@ local ink = P({ optAnsWithoutDiv = V'textE' * Cc ''* Cc ''* wh, -- huh? optAns = V'optAnsWithDiv' + V'optAnsWithoutDiv', --- TODO clean this - opt = Cg(optStars,'indent') * - Ct(Cc'option' * sp * V'optAns' * (V'line'-V'optLEIndent'-V'knot')^0 * wh), --TODO which can by toplevel only? - optSameIndent = Ct(Cc'option' * optStarsSameIndent * sp * V'optAns' * (V'line'-V'optLEIndent'-V'knot')^0 * wh), - optLEIndent = Ct(Cc'option' * optStarsLEIndent * sp * V'optAns' * (V'line'-V'optLEIndent'-V'knot')^0 * wh), + option = Ct(Cc'option' * optStars * sp * V'optAns'), - opts = (V'opt'*V'optSameIndent'^0), - choice = Ct(Cc'choice' * V'opts')/function(t) t.indent=nil; return t end, include = Ct(P('INCLUDE')/'include' * wh * V'text' * wh), para = Ct(Cc'para' * V'text'), - line = V'stmt' + V'choice' + V'para', + line = V'stmt' + V'para', lines = Ct(V'line'^0) }) diff --git a/test/parser/basic.lua b/test/parser/basic.lua index 5034d45..dd6e941 100644 --- a/test/parser/basic.lua +++ b/test/parser/basic.lua @@ -33,14 +33,14 @@ expected= { {"para", '"What do you make of this?" she asked.'}, {"para", "\"I couldn't possibly comment,\" I replied."}, {"para", "we "}, - "glue", + {"glue"}, {"para", "hurr ied"}, {"divert", "to_savile_row"}, - { - "knot", - "to_savile_row", - {"para", "to Savile Row"}, - {"stitch", "st", {"para", "stiiii"}}, - {"stitch", "st2", {"para", "222stiiii "}, "END"} - } + {"knot", "to_savile_row"}, + {"para", "to Savile Row"}, + {"stitch", "st"}, + {"para", "stiiii"}, + {"stitch", "st2"}, + {"para", "222stiiii "}, + {"END"} }} diff --git a/test/parser/glue.lua b/test/parser/glue.lua index 0171984..624ac0e 100644 --- a/test/parser/glue.lua +++ b/test/parser/glue.lua @@ -9,22 +9,14 @@ ink=[[ ]], expected={ - { - "choice", - { - "option", - '"Monsieur, let us savour this moment!"', - "", - " I declared.", - { - "para", - "My master clouted me firmly around the head and dragged me out of the door. " - }, - "glue", - {"divert", "dragged_outside"} - }, - {"option", "", "We hurried home", " ", {"divert", "hurry_outside"}} - }, - {"knot", "as_fast_as_we_could", "glue", {"para", "as fast as we could."}} -- TODO should be space before 'as' + {"option", "*", '"Monsieur, let us savour this moment!"', "", " I declared."}, + {"para", "My master clouted me firmly around the head and dragged me out of the door. " }, + {"glue"}, + {"divert", "dragged_outside"}, + {"option", "*", "", "We hurried home", " "}, + {"divert", "hurry_outside"}, + {"knot", "as_fast_as_we_could"}, + {"glue"}, + {"para", "as fast as we could."} -- TODO should be space before 'as' } } diff --git a/test/parser/knot.lua b/test/parser/knot.lua index 872d6d9..e2ff435 100644 --- a/test/parser/knot.lua +++ b/test/parser/knot.lua @@ -16,15 +16,18 @@ ink=[[ ]], expected={ - { - "knot", - "the_orient_express", - {"stitch", "in_first_class", {"para", "..."}}, - {"stitch", "in_third_class", {"para", "..."}}, - {"stitch", "in_the_guards_van", {"para", "..."}, {"para", "..."}}, - {"stitch", "missed_the_train", {"para", "..."}} - }, - {"knot", "the_orient_express"}, - {"knot", "the_orient_express", {"stitch", "stitch"}} + { "knot", "the_orient_express"}, + {"stitch", "in_first_class"}, + {"para", "..."}, + {"stitch", "in_third_class"}, + {"para", "..."}, + {"stitch", "in_the_guards_van"}, + {"para", "..."}, + {"para", "..."}, + {"stitch", "missed_the_train"}, + {"para", "..."}, + {"knot", "the_orient_express"}, + {"knot", "the_orient_express"}, + {"stitch", "stitch"} } } From 8a8856db1d36fa6c891c8607b13298c9bedebc6c Mon Sep 17 00:00:00 2001 From: premek Date: Wed, 9 Aug 2017 11:36:14 +0200 Subject: [PATCH 2/5] flat parser --- pink/parser.lua | 9 +++--- test/parser/basic.lua | 15 ++-------- test/parser/branching.lua | 59 +++++++++++++-------------------------- test/parser/choices.lua | 34 +++++++--------------- test/parser/comments.lua | 34 ++++++++++++++++++++++ test/parser/glue.lua | 4 +-- test/parser/nested.lua | 18 +++++------- test/parser/nested2.lua | 23 ++++----------- test/parser/tags.lua | 13 ++++++++- test/test.lua | 7 ++--- 10 files changed, 99 insertions(+), 117 deletions(-) create mode 100644 test/parser/comments.lua diff --git a/pink/parser.lua b/pink/parser.lua index 4351e6a..3a9b3f9 100644 --- a/pink/parser.lua +++ b/pink/parser.lua @@ -4,7 +4,6 @@ local S,C,Ct,Cc,Cg,Cb,Cf,Cmt,P,V = lpeg.S, lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.Cg, lpeg.Cb, lpeg.Cf, lpeg.Cmt, lpeg.P, lpeg.V -local parserLogger = print local eof = -1 local sp = S" \t" ^0 + eof local wh = S" \t\r\n" ^0 + eof @@ -12,9 +11,9 @@ local nl = S"\r\n" ^1 + eof local id = (lpeg.alpha + '_') * (lpeg.alnum + '_')^0 local addr = C(id) * ('.' * C(id))^-1 -local todo = sp * 'TODO:' * sp * (1-nl)^0 / parserLogger * wh -- TODO log location -local commOL = sp * '//' * sp * (1-nl)^0 * wh -local commML = sp * '/*' * wh * (P(1)-'*/')^0 * '*/' * wh +local todo = Ct(sp * 'TODO:'/"todo" * sp * C((1-nl)^0)) * wh +local commOL = Ct(sp * '//'/"comment" * sp * C((1-nl)^0)) * wh +local commML = Ct(sp * '/*'/"comment" * wh * C((P(1)-'*/')^0)) * '*/' * wh local comm = commOL + commML + todo local glue = Ct(P'<>'/'glue') *wh -- FIXME do not consume spaces after glue @@ -30,7 +29,7 @@ local stitch = Ct(P('=')^1/'stitch' * wh * C(id) * wh * P('=')^0) * wh local optDiv = '[' * C((P(1) - ']')^0) * ']' -local optStars = wh * C(P'*') * (sp * C'*')^0 +local optStars = wh * Ct(C'*' * (sp * C'*')^0)/table.getn local tag = Ct(wh * P('#')/'tag' * wh * V'text' * wh) diff --git a/test/parser/basic.lua b/test/parser/basic.lua index dd6e941..77df0d6 100644 --- a/test/parser/basic.lua +++ b/test/parser/basic.lua @@ -2,21 +2,10 @@ return { ink=[[ Hello, world! Hello? -//TODO: this is a test todo-item - "What do you make of this?" she asked. - -// Something unprintable... - "I couldn't possibly comment," I replied. - -/* - ... or an unlimited block of text -*/ -we <> /* he -asdf -*/ hurr ied-> to_savile_row // comm - +we <> +hurr ied-> to_savile_row === to_savile_row === diff --git a/test/parser/branching.lua b/test/parser/branching.lua index 59f6429..bfcc0ab 100644 --- a/test/parser/branching.lua +++ b/test/parser/branching.lua @@ -27,46 +27,27 @@ He insisted that we hurried home to Savile Row <> as fast as we could. ]], expected= { + {"knot", "back_in_london"}, + {"para", "We arrived into London at 9.45pm exactly."}, + {"option", 1, '"There is not a moment to lose!"', "", " I declared."}, + {"divert", "hurry_outside"}, + {"option", 1, '"Monsieur, let us savour this moment!"', "", " I declared."}, { - "knot", - "back_in_london", - {"para", "We arrived into London at 9.45pm exactly."}, - { - "choice", - { - "option", - '"There is not a moment to lose!"', - "", - " I declared.", - {"divert", "hurry_outside"} - }, - { - "option", - '"Monsieur, let us savour this moment!"', - "", - " I declared.", - { - "para", - "My master clouted me firmly around the head and dragged me out of the door." - }, - 'glue', - {"divert", "dragged_outside"} - }, - {"option", "", "We hurried home", " ", {"divert", "hurry_outside"}} - } + "para", + "My master clouted me firmly around the head and dragged me out of the door." }, - { - "knot", - "hurry_outside", - {"para", "We hurried home to Savile Row "}, - {"divert", "as_fast_as_we_could"} - }, - { - "knot", - "dragged_outside", - {"para", "He insisted that we hurried home to Savile Row"}, - {"divert", "as_fast_as_we_could"} - }, - {"knot", "as_fast_as_we_could", "glue", {"para", "as fast as we could."}} + {"glue"}, + {"divert", "dragged_outside"}, + {"option", 1, "", "We hurried home", " "}, + {"divert", "hurry_outside"}, + {"knot", "hurry_outside"}, + {"para", "We hurried home to Savile Row "}, + {"divert", "as_fast_as_we_could"}, + {"knot", "dragged_outside"}, + {"para", "He insisted that we hurried home to Savile Row"}, + {"divert", "as_fast_as_we_could"}, + {"knot", "as_fast_as_we_could"}, + {"glue"}, + {"para", "as fast as we could."} } } diff --git a/test/parser/choices.lua b/test/parser/choices.lua index d11c1ae..dcd107f 100644 --- a/test/parser/choices.lua +++ b/test/parser/choices.lua @@ -12,30 +12,16 @@ ink=[[ == finale == ]], expected= { - { - "knot", - "start", - { - "choice", - {"option", "I dont know", "", ""}, - { - "option", - '"I am somewhat tired', - '."', - '," I repeated.', - {"para", '"Really," he responded.'}, - {"para", '"How deleterious."'} - }, - { - "option", - '"Nothing, Monsieur!"', - "", - " I replied.", - {"para", '"Very good, *then."'} - }, - {"option", "I said no more", "", "", {"para", '"Ah,". "I see you"'}} - } - }, + {"knot", "start"}, + {"option", 1, "I dont know", "", ""}, + {"option", 1, '"I am somewhat tired', '."', '," I repeated.'}, + {"para", '"Really," he responded.'}, + {"para", '"How deleterious."'}, + {"option", 1, '"Nothing, Monsieur!"', "", " I replied."}, + {"para", '"Very good,'}, + {"option", 1, 'then."', "", ""}, + {"option", 1, "I said no more", "", ""}, + {"para", '"Ah,". "I see you"'}, {"knot", "finale"} } } diff --git a/test/parser/comments.lua b/test/parser/comments.lua new file mode 100644 index 0000000..156a198 --- /dev/null +++ b/test/parser/comments.lua @@ -0,0 +1,34 @@ +return { +ink=[[ +Hello? +TODO: this is a test todo-item + +"What do you make of this?" she asked. + +// Something unprintable... + +"I couldn't possibly comment," I replied. + +/* + ... or an unlimited block of text +*/ +we <> /* he +asdf +*/ hurr ied-> to_savile_row // comm + + +]], +expected= { + {"para", "Hello?"}, + {"todo", "this is a test todo-item"}, + {"para", '"What do you make of this?" she asked.'}, + {"comment", "Something unprintable..."}, + {"para", "\"I couldn't possibly comment,\" I replied."}, + {"comment", "... or an unlimited block of text\n"}, + {"para", "we "}, + {"glue"}, + {"comment", "he\nasdf\n"}, + {"para", "hurr ied"}, + {"divert", "to_savile_row"}, + {"comment", "comm"}, +}} diff --git a/test/parser/glue.lua b/test/parser/glue.lua index 624ac0e..f8bbb58 100644 --- a/test/parser/glue.lua +++ b/test/parser/glue.lua @@ -9,11 +9,11 @@ ink=[[ ]], expected={ - {"option", "*", '"Monsieur, let us savour this moment!"', "", " I declared."}, + {"option", 1, '"Monsieur, let us savour this moment!"', "", " I declared."}, {"para", "My master clouted me firmly around the head and dragged me out of the door. " }, {"glue"}, {"divert", "dragged_outside"}, - {"option", "*", "", "We hurried home", " "}, + {"option", 1, "", "We hurried home", " "}, {"divert", "hurry_outside"}, {"knot", "as_fast_as_we_could"}, {"glue"}, diff --git a/test/parser/nested.lua b/test/parser/nested.lua index 76d9a53..d4e0489 100644 --- a/test/parser/nested.lua +++ b/test/parser/nested.lua @@ -2,18 +2,14 @@ return { ink=[[ * "Murder!" ** A + * * A * "Suicide!" ]], expected= { - { - "choice", - { - "option", - '"Murder!"', - "", - "", - {"choice", {"option", "A", "", ""}} - }, - {"option", '"Suicide!"', "", ""} - } + + {"option", 1, '"Murder!"', "", ""}, + {"option", 2, "A", "", ""}, + {"option", 2, "A", "", ""}, + {"option", 1, '"Suicide!"', "", ""} + } } diff --git a/test/parser/nested2.lua b/test/parser/nested2.lua index df89d2e..a90f390 100644 --- a/test/parser/nested2.lua +++ b/test/parser/nested2.lua @@ -10,22 +10,11 @@ ink=[[ ]], expected = { {"para", '"Well, Poirot? Murder or suicide?"'}, - { - "choice", - { - "option", - '"Murder"', - "", - "", - {"para", '"And who did it?"'}, - { - "choice", - {"option", '"Detective-Inspector Japp!"', "", ""}, - {"option", '"Captain Hastings!"', "", ""}, - {"option", '"Myself!"', "", ""} - } - }, - {"option", '"Suicide"', "", ""} - } + {"option", 1, '"Murder"', "", ""}, + {"para", '"And who did it?"'}, + {"option", 2, '"Detective-Inspector Japp!"', "", ""}, + {"option", 2, '"Captain Hastings!"', "", ""}, + {"option", 2, '"Myself!"', "", ""}, + {"option", 1, '"Suicide"', "", ""} } } diff --git a/test/parser/tags.lua b/test/parser/tags.lua index b348374..305a175 100644 --- a/test/parser/tags.lua +++ b/test/parser/tags.lua @@ -9,10 +9,21 @@ ink=[[ # require: Train ticket This is the line of content. # the third tag # really_monsieur.ogg - +#tag aaa ]], expected={ + {"tag", "author: Joseph Humfrey"}, + {"tag", "title: My Wonderful Ink Story"}, + {"knot", "content"}, + {"tag", "location: Germany"}, + {"tag", "overview: munich.ogg"}, + {"tag", "require: Train ticket"}, + {"para", "This is the line of content."}, + {"tag", "the third tag"}, + {"tag", "really_monsieur.ogg"}, + {"tag", "tag"}, + {"para", "aaa"} } } diff --git a/test/test.lua b/test/test.lua index 8ec7f53..ba33444 100644 --- a/test/test.lua +++ b/test/test.lua @@ -15,13 +15,10 @@ function testText() doTestS( {{"para", "Hello world"}} ) end -function testOpt1() doTestS( - '* "I am somewhat tired[."]," I repeated.', - {{'choice', {"option", '"I am somewhat tired', '."', '," I repeated.'}}} -) end function testBasic() doTest('basic') end +function testComments() doTest('comments') end function testChoices() doTest('choices') end function testNest() doTest('nested') end function testNest2() doTest('nested2') end @@ -29,7 +26,7 @@ function testKnot() doTest('knot') end function testBranching() doTest('branching') end function testGlue() doTest('glue') end function testInclude() doTest('include') end -function testInclude() doTest('tags') end +function testTagsP() doTest('tags') end --- runtime --- From 83aa300e2fe473c4f49e7c2928f30c05d3661d6c Mon Sep 17 00:00:00 2001 From: premek Date: Wed, 9 Aug 2017 11:46:43 +0200 Subject: [PATCH 3/5] end divert small caps --- pink/parser.lua | 3 +-- test/parser/basic.lua | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pink/parser.lua b/pink/parser.lua index 3a9b3f9..127a156 100644 --- a/pink/parser.lua +++ b/pink/parser.lua @@ -19,8 +19,7 @@ local comm = commOL + commML + todo local glue = Ct(P'<>'/'glue') *wh -- FIXME do not consume spaces after glue local divertSym = '->' *wh -local divertEndSym = Ct(C('END')) *wh -local divertEnd = divertSym * divertEndSym +local divertEnd = Ct(divertSym/'end' * 'END' * wh) local divertJump = Ct(divertSym/'divert' * addr * wh) local divert = divertEnd + divertJump diff --git a/test/parser/basic.lua b/test/parser/basic.lua index 77df0d6..29e5445 100644 --- a/test/parser/basic.lua +++ b/test/parser/basic.lua @@ -31,5 +31,5 @@ expected= { {"para", "stiiii"}, {"stitch", "st2"}, {"para", "222stiiii "}, - {"END"} + {"end"} }} From dcd79a06b4c9cbaa62fd49f0112c708d77b0b5a6 Mon Sep 17 00:00:00 2001 From: premek Date: Wed, 9 Aug 2017 14:07:49 +0200 Subject: [PATCH 4/5] basic runtime test --- test/test.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/test.lua b/test/test.lua index ba33444..4d59a2b 100644 --- a/test/test.lua +++ b/test/test.lua @@ -31,6 +31,13 @@ function testTagsP() doTest('tags') end --- runtime --- +function testBasicR() + local story = pink.getStory('test/runtime/hello.ink') + luaunit.assertEquals(story.continue(), 'hello world') + luaunit.assertFalse(story.canContinue) +end + + function testVisitCount() local story = pink.getStory('test/runtime/branching.ink') story.choosePathString('hurry_outside'); From 36b4e1309f07392cb5f28cace14c23abc65cb70e Mon Sep 17 00:00:00 2001 From: premek Date: Sat, 12 Aug 2017 23:36:07 +0200 Subject: [PATCH 5/5] finished flat parser refactor - choices, added tags support --- pink/parser.lua | 20 +++-- pink/runtime.lua | 177 +++++++++++++++++++++++++------------ test/parser/tags.lua | 18 ++-- test/runtime/branching.ink | 11 ++- test/runtime/tags.ink | 8 +- test/test.lua | 37 ++++++-- 6 files changed, 176 insertions(+), 95 deletions(-) diff --git a/pink/parser.lua b/pink/parser.lua index 127a156..5568bea 100644 --- a/pink/parser.lua +++ b/pink/parser.lua @@ -30,19 +30,21 @@ local optDiv = '[' * C((P(1) - ']')^0) * ']' local optStars = wh * Ct(C'*' * (sp * C'*')^0)/table.getn -local tag = Ct(wh * P('#')/'tag' * wh * V'text' * wh) - - +local hash = P('#') +local tag = hash * wh * V'text' +local tagGlobal = Ct(Cc'tag' * Cc'global' * tag * wh) +local tagAbove = Ct(Cc'tag' * Cc'above' * tag * wh) +local tagEnd = Ct(Cc'tag' * Cc'end' * tag * sp) local ink = P({ "lines", - stmt = glue + divert + knot + stitch + V'option' + optDiv + comm + V'include' + tag, - text = C((1-nl-V'stmt')^1) *wh, - textE = C((1-nl-V'stmt')^0) *wh, + stmt = glue + divert + knot + stitch + V'option' + optDiv + comm + V'include', + text = C((1-nl-V'stmt'-hash)^1), + textEmptyCapt = C((1-nl-V'stmt'-hash)^0), - optAnsWithDiv = V'textE' * optDiv * V'textE' * wh, - optAnsWithoutDiv = V'textE' * Cc ''* Cc ''* wh, -- huh? + optAnsWithDiv = V'textEmptyCapt' * sp * optDiv * V'text'^0 * wh, + optAnsWithoutDiv = V'textEmptyCapt' * sp * Cc '' * Cc '' * wh, -- huh? optAns = V'optAnsWithDiv' + V'optAnsWithoutDiv', option = Ct(Cc'option' * optStars * sp * V'optAns'), @@ -52,7 +54,7 @@ local ink = P({ include = Ct(P('INCLUDE')/'include' * wh * V'text' * wh), - para = Ct(Cc'para' * V'text'), + para = tagAbove^0 * Ct(Cc'para' * V'text') * tagEnd^0 * wh + tagGlobal, line = V'stmt' + V'para', lines = Ct(V'line'^0) diff --git a/pink/runtime.lua b/pink/runtime.lua index a4e1138..6414168 100644 --- a/pink/runtime.lua +++ b/pink/runtime.lua @@ -1,104 +1,158 @@ +local debug = function(x) print( require('test/luaunit').prettystr(x) ) end + local is = function (what, node) return node ~= nil and (type(node) == "table" and node[1] == what) - or (type(node) == "string" and node == what) -end - -local getPara = function (node) - if is('para', node) then return node[2] end end return function (tree) - local s = {} - - local pointer = nil - local tab = {} - - local knots = {} - - -- TODO state should contain tab/pointer to be able to save / load - s.state = { - visitCount = {} + local s = { + globalTags = {}, + state = { + visitCount = {}, + } } - local process = function () - for _, v in ipairs(tree) do - if is('knot', v) then - knots[v[2]] = v + local pointer = 1 + local knots = {} + local tags = {} -- maps (pointer to para) -> (list of tags) + local tagsForContentAtPath = {} + local currentChoicesPointers = {} + + -- TODO state should contain tree/pointer to be able to save / load + + local preProcess = function () + + local aboveTags = {} + local lastPara = {} + local lastKnotName + + for p, n in ipairs(tree) do + if is('knot', n) then -- FIXME stitches + knots[n[2]] = p + --print(v[2],k) end + + if is('tag', n) then + if n[2] == 'global' then + table.insert(s.globalTags, n[3]) + end + if n[2] == 'above' then + if lastKnotName then table.insert(tagsForContentAtPath[lastKnotName], n[3]) end + table.insert(aboveTags, n[3]) + end + if n[2] == 'end' then + table.insert(tags[lastPara], n[3]) + end + end + + if is('knot', n) or is('stitch', n) then + lastKnotName = n[2] + tagsForContentAtPath[lastKnotName] = {} + end + + if is('para', n) then + tags[p] = aboveTags + aboveTags = {} + lastPara = p + end + end end local goToKnot = function(knotName) if knots[knotName] then - s.state.visitCount[knotName] = (s.state.visitCount[knotName] or 0) + 1 - tab = knots[knotName] - pointer = 3 - return tab[pointer] - else + s.state.visitCount[knotName] = s.state.visitCountAtPathString(knotName) + 1 + pointer = knots[knotName] + 1 -- go to the line after the knot + else print('unknown knot', knotName) end end - local update = function () - local next = tab[pointer] + local isNext = function (what) + return is(what, tree[pointer]) + end - if is('knot', next) then + local getPara = function () + if isNext('para') then return tree[pointer][2] end + end + + local update + update = function () + + if isNext('knot') then -- FIXME: we shouldn't continue to next knot automatically probably - how about stitches? --next = goToKnot(next[2]) end - if is('divert', next) then next = goToKnot(next[2]) end + if isNext('divert') then + goToKnot(tree[pointer][2]) + update() + return + end - s.canContinue = is('para', next) + if isNext('tag') then + + pointer = pointer + 1 + update() + return + end + + s.canContinue = isNext('para') s.currentChoices = {} - if is('choice', next) then - for i=2, #next do - --print(to_string(next[i])) - table.insert(s.currentChoices, { - text = (next[i][2] or '') .. (next[i][3] or ''), - choiceText = next[i][2] .. (next[i][4] or ''), - }) + currentChoicesPointers = {} + + if isNext('option') then + local choiceDepth = tree[pointer][2] + -- find all choices on the same level in the same knot and in the same super-choice + for p=pointer, #tree do + local n = tree[p] + --print('looking for options', choiceDepth, n[1], n[2]) + if is('knot', n) or is('stitch', n) or (is('option', n) and n[2] < choiceDepth) then + --print('stop looking for options'); + break + end + + if is('option', n) and n[2] == choiceDepth then + -- print('adding', p, n[3]) + table.insert(currentChoicesPointers, p) + table.insert(s.currentChoices, { + text = (n[3] or '') .. (n[4] or ''), + choiceText = n[3] .. (n[5] or ''), + }) + end end end end - local step = function () - pointer = pointer + 1 - update() - return tab[pointer] - end - - local stepTo = function (table, pos) - tab = table - pointer = pos - update() - return tab[pointer] end s.canContinue = nil s.continue = function() - local res = getPara(tab[pointer]) - local next = step() - if is('glue', next) then - step() + local res = getPara() + s.currentTags = tags[pointer] or {} + + pointer = pointer + 1 + update() + + if isNext('glue') then + pointer = pointer + 1 + update() res = res .. s.continue() end return res; end - s.currentChoices = nil + s.currentChoices = {} s.chooseChoiceIndex = function(index) - s.currentChoices = {} - local choice = tab[pointer] - local option = choice[1 + index] - stepTo(option, 5) + pointer = currentChoicesPointers[index]+1 + update() end s.choosePathString = function(knotName) @@ -110,14 +164,21 @@ return function (tree) return s.state.visitCount[knotName] or 0 end + s.tagsForContentAtPath = function(knotName) + return tagsForContentAtPath[knotName] or {} + end + + s.currentTags = {} s.variablesState = {} -- s.state.ToJson();s.state.LoadJson(savedJson); - stepTo(tree, 1) - process() + preProcess() + --debug(tree) + --debug(tags) -- debug s._tree = tree + return s end diff --git a/test/parser/tags.lua b/test/parser/tags.lua index 305a175..c130095 100644 --- a/test/parser/tags.lua +++ b/test/parser/tags.lua @@ -13,16 +13,16 @@ This is the line of content. # the third tag # really_monsieur.ogg aaa ]], expected={ - {"tag", "author: Joseph Humfrey"}, - {"tag", "title: My Wonderful Ink Story"}, + {"tag", "global", "author: Joseph Humfrey"}, + {"tag", "global", "title: My Wonderful Ink Story"}, {"knot", "content"}, - {"tag", "location: Germany"}, - {"tag", "overview: munich.ogg"}, - {"tag", "require: Train ticket"}, - {"para", "This is the line of content."}, - {"tag", "the third tag"}, - {"tag", "really_monsieur.ogg"}, - {"tag", "tag"}, + {"tag", "above", "location: Germany"}, + {"tag", "above", "overview: munich.ogg"}, + {"tag", "above", "require: Train ticket"}, + {"para", "This is the line of content. "}, + {"tag", "end", "the third tag "}, + {"tag", "end", "really_monsieur.ogg"}, + {"tag", "above", "tag"}, {"para", "aaa"} } } diff --git a/test/runtime/branching.ink b/test/runtime/branching.ink index 9771c99..98d24a6 100644 --- a/test/runtime/branching.ink +++ b/test/runtime/branching.ink @@ -1,14 +1,17 @@ === back_in_london === -We arrived into London at 9.45pm exactly. +We arrived into London at 9.45pm +exactly * "There is not a moment to lose!"[] I declared. -> hurry_outside * "Monsieur, let us savour this moment!"[] I declared. - My master clouted me firmly around the head and dragged me out of the door.<> - -> dragged_outside - + My master clouted me firmly around the head + ** ugh? + no thanks + ** hug + huhu * [We hurried home] -> hurry_outside diff --git a/test/runtime/tags.ink b/test/runtime/tags.ink index 437f38b..10808c3 100644 --- a/test/runtime/tags.ink +++ b/test/runtime/tags.ink @@ -3,15 +3,11 @@ ->content === content - A line of normal game-text. # colour it blue - -Passepartout: Really, Monsieur. # surly - -Passepartout: Really, Monsieur. # surly # really_monsieur.ogg - # the first tag # the second tag This is the line of content. # the third tag +#not this one +that to this === Munich == # location: Germany diff --git a/test/test.lua b/test/test.lua index 4d59a2b..6c2a07a 100644 --- a/test/test.lua +++ b/test/test.lua @@ -26,19 +26,34 @@ function testKnot() doTest('knot') end function testBranching() doTest('branching') end function testGlue() doTest('glue') end function testInclude() doTest('include') end -function testTagsP() doTest('tags') end +function testTags() doTest('tags') end --- runtime --- -function testBasicR() +function testRBasic() local story = pink.getStory('test/runtime/hello.ink') luaunit.assertEquals(story.continue(), 'hello world') luaunit.assertFalse(story.canContinue) end -function testVisitCount() +function testRChoices() + local story = pink.getStory('test/runtime/branching.ink') + story.choosePathString('back_in_london'); + story.continue() + luaunit.assertEquals(story.continue(), 'exactly') + luaunit.assertFalse(story.canContinue) + luaunit.assertEquals(#story.currentChoices, 3) + story.chooseChoiceIndex(2) + luaunit.assertEquals(story.continue(), 'My master clouted me firmly around the head') + luaunit.assertEquals(#story.currentChoices, 2) + story.chooseChoiceIndex(2) + luaunit.assertEquals(story.continue(), 'huhu') +end + + +function testRVisitCount() local story = pink.getStory('test/runtime/branching.ink') story.choosePathString('hurry_outside'); luaunit.assertEquals(story.state.visitCountAtPathString('as_fast_as_we_could'), 0) @@ -50,22 +65,26 @@ function testVisitCount() luaunit.assertEquals(story.state.visitCountAtPathString('as_fast_as_we_could'), 2) end -function testIncludeR() +function testRInclude() local story = pink.getStory('test/runtime/include.ink') luaunit.assertEquals(story.continue(), 'hello world') luaunit.assertEquals(story.continue(), 'hello again') luaunit.assertFalse(story.canContinue) end -function testTags() +function testRTags() local story = pink.getStory('test/runtime/tags.ink') - luaunit.assertEquals(story.continue(), '') - luaunit.assertEquals(story.continue(), '') - luaunit.assertEquals(story.globalTags, {""}) + luaunit.assertEquals(story.globalTags, {"author: Joseph Humfrey", "title: My Wonderful Ink Story"}) + story.choosePathString('content'); + luaunit.assertEquals(story.continue(), 'This is the line of content. ') + luaunit.assertEquals(story.currentTags, {"the first tag", "the second tag", "the third tag"}) + story.continue() + luaunit.assertEquals(story.currentTags, {"not this one"}) luaunit.assertFalse(story.canContinue) + luaunit.assertEquals(story.tagsForContentAtPath('Munich'), {"location: Germany", "overview: munich.ogg", "require: Train ticket"}) end -function testInvisibleDiverts() +function testRInvisibleDiverts() local story = pink.getStory('test/runtime/branching.ink') story.choosePathString('hurry_outside') luaunit.assertEquals(story.continue(), "We hurried home to Savile Row as fast as we could.")