summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@digital-scurf.org>2016-07-30 10:38:51 +0100
committerDaniel Silverstone <dsilvers@digital-scurf.org>2016-07-30 10:38:51 +0100
commitf15f73b188dfed9713d2922ffe58d284dcd258ba (patch)
tree46abe36bb54f1bc4af240a5e82ea64201a06fca7
parent88e13e3bca052cefaa941e6a51d7ae2717b5b25a (diff)
downloadtongue-f15f73b188dfed9713d2922ffe58d284dcd258ba.tar.bz2
Basic expansion string support working. Possibly some later corner cases to chase
-rw-r--r--lib/tongue/langpack.lua154
-rw-r--r--test/test-tongue.langpack.lua71
2 files changed, 224 insertions, 1 deletions
diff --git a/lib/tongue/langpack.lua b/lib/tongue/langpack.lua
index b9a22d1..c4188fb 100644
--- a/lib/tongue/langpack.lua
+++ b/lib/tongue/langpack.lua
@@ -40,7 +40,159 @@ local fallback, langpack = nil, {}
-- @treturn[2] nil To indicate error
-- @treturn[2] string Error message
local function try_convert(expn)
- return nil, "No converter written"
+ local expn_t = {}
+ local pstate = "normal"
+ local acc = ""
+ local function transacc(repl)
+ if acc ~= "" then
+ expn_t[#expn_t+1] = acc
+ end
+ acc = repl or ""
+ end
+ local parser = {
+ normal = function(ch)
+ if ch == "$" then
+ pstate = "dollar"
+ else
+ acc = acc .. ch
+ end
+ end,
+ dollar = function(ch)
+ if ch == "{" then
+ pstate = "getname"
+ transacc({type="arg", name=""})
+ elseif ch == "(" then
+ pstate = "getexpn"
+ transacc({type="expn", name=""})
+ else
+ acc = acc .. "$" .. ch
+ pstate = "normal"
+ end
+ end,
+ getname = function(ch)
+ if ch == "}" then
+ transacc()
+ pstate = "normal"
+ else
+ acc.name = acc.name .. ch
+ end
+ end,
+ getexpn = function(ch)
+ if ch == ")" then
+ transacc()
+ pstate = "normal"
+ elseif ch == "," then
+ pstate = "getarg"
+ acc[#acc+1] = ""
+ else
+ acc.name = acc.name .. ch
+ end
+ end,
+ getarg = function(ch)
+ if ch == ")" then
+ transacc()
+ pstate = "normal"
+ elseif ch == "=" then
+ pstate = "getvalue"
+ acc[#acc] = {k=acc[#acc],v=""}
+ elseif ch == "," then
+ acc[#acc+1] = ""
+ else
+ acc[#acc] = acc[#acc] .. ch
+ end
+ end,
+ getvalue = function(ch)
+ if ch == ")" then
+ transacc()
+ pstate = "normal"
+ elseif ch == "," then
+ pstate = "getarg"
+ acc[#acc+1] = ""
+ else
+ acc[#acc].v = acc[#acc].v .. ch
+ end
+ end,
+ }
+ for ch in expn:gfind("(.)") do
+ local fn = parser[pstate]
+ if fn == nil then
+ return nil, "Unexpected parse state: " .. tostring(pstate)
+ end
+ fn(ch)
+ end
+ if pstate ~= "normal" then
+ return nil, "Unexpected end of string parsing expansion expression"
+ end
+ transacc()
+ -- Now we need to validate the expansion table
+ -- Simple rules on expansion expressions:
+ -- 1. must have a name
+ -- 2. every argument must have a name (str) or a key/value (tab)
+ -- 3. maximum of one ... as a name, not a key/value
+ -- 4. any key/value must have the key and value be non-empty
+ for _, t in ipairs(expn_t) do
+ if type(t) == "string" then
+ -- Nothing to check here
+ else
+ if t.name == "" then
+ return nil, "Empty name in expansion expression"
+ end
+ local found_dotdotdot = false
+ if t.type == "expn" then
+ for _, tt in ipairs(t) do
+ if tt == "..." then
+ if found_dotdotdot then
+ return nil, "Repeated ... in expansion expression"
+ end
+ found_dotdotdot = true
+ elseif type(tt) == "table" then
+ if tt.k == "" or tt.v == "" then
+ return nil, "Empty key or value in expansion expression"
+ end
+ if tt.k == "..." then
+ return nil, "Use of ... in key=value expansion expression"
+ end
+ end
+ end
+ else
+ -- Nothing to check here, arg expansions are simple
+ end
+ end
+ end
+ -- Okay, so superficially things look good, wrapper it up in a function
+ local function processed_expansion(args)
+ -- Note: this closes over expn_t from the outer function
+ local ret = {}
+ for _, t in ipairs(expn_t) do
+ if type(t) == "string" then
+ ret[#ret+1] = t
+ elseif t.type == "expn" then
+ -- We're "recursing"...
+ local token = t.name
+ local nargs = {}
+ for _, tt in ipairs(t) do
+ if type(tt) == "string" then
+ if tt == "..." then
+ for k, v in pairs(args) do
+ nargs[k] = v
+ end
+ else
+ nargs[tt] = args[tt]
+ end
+ else
+ nargs[tt.k] = tt.v
+ end
+ end
+ nargs._pack = args._pack
+ ret[#ret+1] = args._pack:expand(token, nargs)
+ else
+ -- Argument expansion
+ ret[#ret+1] = args[t.name] or ""
+ end
+ end
+ return table.concat(ret, "")
+ end
+ return processed_expansion
end
--- Generate a function to localise when a conversion failed.
diff --git a/test/test-tongue.langpack.lua b/test/test-tongue.langpack.lua
index 04f7050..083a23f 100644
--- a/test/test-tongue.langpack.lua
+++ b/test/test-tongue.langpack.lua
@@ -60,6 +60,77 @@ function suite.add_function_expn()
assert(lang:expand("TEST",{foo="bar"}) == "bar", "Function tokens don't work")
end
+function suite.add_plain_expn_good_lazy()
+ local lang = tongue.langpack.create("en")
+ assert(lang, "Could not create language pack")
+ assert(lang:add_token("TEST", "Hello World"),
+ "Unable to add a good plain token")
+ assert(lang:expand("TEST",{}) == "Hello World", "Good plain text tokens don't work")
+end
+
+function suite.add_plain_expn_good_strict()
+ local lang = tongue.langpack.create("en")
+ assert(lang, "Could not create language pack")
+ assert(lang:add_token("TEST", "Hello World", true),
+ "Unable to add a good plain token strictly")
+ assert(lang:expand("TEST",{}) == "Hello World", "Good plain text tokens don't work")
+end
+
+function suite.add_simple_expn_good_lazy()
+ local lang = tongue.langpack.create("en")
+ assert(lang, "Could not create language pack")
+ assert(lang:add_token("TEST", "Hello World"),
+ "Unable to add a good plain token")
+ assert(lang:add_token("TEST2", "I say $(TEST)"), "Unable to add a good simple token")
+ assert(lang:expand("TEST2",{}) == "I say Hello World", "Good simple text tokens don't work")
+end
+
+function suite.add_simple_expn_good_strict()
+ local lang = tongue.langpack.create("en")
+ assert(lang, "Could not create language pack")
+ assert(lang:add_token("TEST", "Hello World", true),
+ "Unable to add a good plain token")
+ assert(lang:add_token("TEST2", "I say $(TEST)", true), "Unable to add a good simple token")
+ assert(lang:expand("TEST2",{}) == "I say Hello World", "Good simple text tokens don't work")
+end
+
+function suite.add_simple_expn_good_pass()
+ local lang = tongue.langpack.create("en")
+ assert(lang, "Could not create language pack")
+ assert(lang:add_token("TEST", "Hello ${name}", true),
+ "Unable to add a good plain token")
+ assert(lang:add_token("TEST2", "I say $(TEST,...)", true), "Unable to add a good simple token")
+ assert(lang:expand("TEST2",{name="Jeff"}) == "I say Hello Jeff", "Good simple text tokens don't work")
+end
+
+function suite.add_simple_expn_good_pass_override()
+ local lang = tongue.langpack.create("en")
+ assert(lang, "Could not create language pack")
+ assert(lang:add_token("TEST", "Hello ${name}", true),
+ "Unable to add a good plain token")
+ assert(lang:add_token("TEST2", "I say $(TEST,...,name=Susan)", true), "Unable to add a good simple token")
+ assert(lang:expand("TEST2",{name="Jeff"}) == "I say Hello Susan", "Good simple text tokens don't work")
+end
+
+function suite.add_simple_expn_good_pass_onlyone()
+ local lang = tongue.langpack.create("en")
+ assert(lang, "Could not create language pack")
+ assert(lang:add_token("TEST", "Hello ${title} ${name}", true),
+ "Unable to add a good plain token")
+ assert(lang:add_token("TEST2", "I say $(TEST,name)", true), "Unable to add a good simple token")
+ assert(lang:expand("TEST2",{name="Jeff", title="Mr"}) == "I say Hello Jeff", "Good simple text tokens don't work")
+end
+
+function suite.add_simple_expn_good_pass_puredata()
+ local lang = tongue.langpack.create("en")
+ assert(lang, "Could not create language pack")
+ assert(lang:add_token("TEST", "Hello ${name}", true),
+ "Unable to add a good plain token")
+ assert(lang:add_token("TEST2", "I say $(TEST,name=Maria)", true), "Unable to add a good simple token")
+ assert(lang:expand("TEST2",{}) == "I say Hello Maria", "Good simple text tokens don't work")
+end
+
+
local count_ok = 0
for _, testname in ipairs(testnames) do
-- print("Run: " .. testname)