Tue, 27 Oct 2015 23:14:19 +0000
Merge
main.lua | file | annotate | diff | comparison | revisions | |
scansion/objects/client.lua | file | annotate | diff | comparison | revisions |
--- a/main.lua Wed Sep 23 01:03:56 2015 +0200 +++ b/main.lua Tue Oct 27 23:14:19 2015 +0000 @@ -55,6 +55,9 @@ local object = script.objects[action.object_name]; local handler = object.handler; assert(handler[action.action], "Objects of type '"..object.type.."' do not support action '"..action.action.."'"); + if action.annotation then + print(action.annotation); + end print(object.name, action.action.."..."); local ok, err = pcall(handler[action.action], object, action.extra); if not ok then
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scansion/generator.lua Tue Oct 27 23:14:19 2015 +0000 @@ -0,0 +1,54 @@ +local it = require "scansion.iterators"; + +local script_methods = {}; + +local excluded_fields = { name = true, defined_line = true, type = true, annotation = true }; +function script_methods:client(data) + self:_append("["..data.name.."]"); + for k, v in pairs(data) do + if not excluded_fields[k] then + self:_append("", k..": "..v); + end + end + self:_append(""); +end + +function script_methods:action(name, verb, data) + if self.in_header then + self.in_header = false; + self:_append("----------"); + self:_append(""); + end + + self:_append(name.." "..verb..(data and ":" or "")); + if data then + for s in tostring(data):gmatch("[^\n]+") do + self:_append("", s); + end + end + self:_append(""); +end + +function script_methods:_append(...) + local fields = { ... }; + for i = 1, select("#", ...) do + fields[i] = tostring(fields[i]); + end + table.insert(self.lines, table.concat(fields, "\t")); +end + +function script_methods:lines() + return it.values(self.lines); +end + +function script_methods:render() + return table.concat(self.lines, "\n"); +end + +local script_mt = { __index = script_methods }; + +return { + new_script = function () + return setmetatable({ in_header = true, n_clients = 0, clients = {} }, script_mt); + end; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scansion/iterators.lua Tue Oct 27 23:14:19 2015 +0000 @@ -0,0 +1,201 @@ +-- Prosody IM +-- Copyright (C) 2008-2010 Matthew Wild +-- Copyright (C) 2008-2010 Waqas Hussain +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +--[[ Iterators ]]-- + +local it = {}; + +local t_insert = table.insert; +local select, unpack, next = select, unpack, next; +local function pack(...) return { n = select("#", ...), ... }; end + +-- Reverse an iterator +function it.reverse(f, s, var) + local results = {}; + + -- First call the normal iterator + while true do + local ret = { f(s, var) }; + var = ret[1]; + if var == nil then break; end + t_insert(results, 1, ret); + end + + -- Then return our reverse one + local i,max = 0, #results; + return function (results) + if i<max then + i = i + 1; + return unpack(results[i]); + end + end, results; +end + +-- Iterate only over keys in a table +local function _keys_it(t, key) + return (next(t, key)); +end +function it.keys(t) + return _keys_it, t; +end + +-- Iterate only over values in a table +function it.values(t) + local key, val; + return function (t) + key, val = next(t, key); + return val; + end, t; +end + +-- Given an iterator, iterate only over unique items +function it.unique(f, s, var) + local set = {}; + + return function () + while true do + local ret = pack(f(s, var)); + var = ret[1]; + if var == nil then break; end + if not set[var] then + set[var] = true; + return unpack(ret, 1, ret.n); + end + end + end; +end + +--[[ Return the number of items an iterator returns ]]-- +function it.count(f, s, var) + local x = 0; + + while true do + var = f(s, var); + if var == nil then break; end + x = x + 1; + end + + return x; +end + +-- Return the first n items an iterator returns +function it.head(n, f, s, var) + local c = 0; + return function (s, var) + if c >= n then + return nil; + end + c = c + 1; + return f(s, var); + end, s; +end + +-- Skip the first n items an iterator returns +function it.skip(n, f, s, var) + for i=1,n do + var = f(s, var); + end + return f, s, var; +end + +-- Return the last n items an iterator returns +function it.tail(n, f, s, var) + local results, count = {}, 0; + while true do + local ret = pack(f(s, var)); + var = ret[1]; + if var == nil then break; end + results[(count%n)+1] = ret; + count = count + 1; + end + + if n > count then n = count; end + + local pos = 0; + return function () + pos = pos + 1; + if pos > n then return nil; end + local ret = results[((count-1+pos)%n)+1]; + return unpack(ret, 1, ret.n); + end + --return reverse(head(n, reverse(f, s, var))); -- ! +end + +function it.filter(filter, f, s, var) + if type(filter) ~= "function" then + local filter_value = filter; + function filter(x) return x ~= filter_value; end + end + return function (s, var) + local ret; + repeat ret = pack(f(s, var)); + var = ret[1]; + until var == nil or filter(unpack(ret, 1, ret.n)); + return unpack(ret, 1, ret.n); + end, s, var; +end + +local function _ripairs_iter(t, key) if key > 1 then return key-1, t[key-1]; end end +function it.ripairs(t) + return _ripairs_iter, t, #t+1; +end + +local function _range_iter(max, curr) if curr < max then return curr + 1; end end +function it.range(x, y) + if not y then x, y = 1, x; end -- Default to 1..x if y not given + return _range_iter, y, x-1; +end + +-- Convert the values returned by an iterator to an array +function it.to_array(f, s, var) + local t, var = {}; + while true do + var = f(s, var); + if var == nil then break; end + t_insert(t, var); + end + return t; +end + +-- Treat the return of an iterator as key,value pairs, +-- and build a table +function it.to_table(f, s, var) + local t, var2 = {}; + while true do + var, var2 = f(s, var); + if var == nil then break; end + t[var] = var2; + end + return t; +end + +function combinations(iterator, ...) + local n_inputs = select("#", ...); + local inputs = { ... }; + local f, s, var = {}, {}, {}; + for i = 1, n_inputs do + f[i], s[i], var[i] = iterator(inputs[i]); + end + + local level = 1; + return function () + while level > 0 do + var[level] = f[level](s[level], var[level]); + if level == n_inputs and var[level] ~= nil then + return unpack(var, 1, n_inputs); + elseif var[level] then + level = level + 1; + f[level], s[level], var[level] = iterator(inputs[level]); + else + level = level - 1; + end + end + end +end + +return it;
--- a/scansion/objects/client.lua Wed Sep 23 01:03:56 2015 +0200 +++ b/scansion/objects/client.lua Tue Oct 27 23:14:19 2015 +0000 @@ -10,7 +10,7 @@ local function filter_expression(script, s) local expr = s:match("^%$%{(.+)%}$"); if not expr then return s end - local name, value_name = expr:match("^(.-)'s (.+)$"); + local name, value_name = expr:match("^(.+)'s (.+)$"); assert(name, "Unable to parse expression: "..expr); local key = value_name:lower():gsub(" ", "_"); assert(script.objects[name], "Unknown object called "..name);
--- a/scansion/parser.lua Wed Sep 23 01:03:56 2015 +0200 +++ b/scansion/parser.lua Tue Oct 27 23:14:19 2015 +0000 @@ -6,8 +6,9 @@ local line_number = 0; local last_object; + local annotation; - for line in data:gmatch("([^\r\n]+)\r?\n") do + for line in data:gmatch("([^\r\n]*)\r?\n") do line_number = line_number + 1; if line:sub(1,1) == "[" then local obj_type, name, extra = line:match("^%[(%a+)%] (.+)$"); @@ -30,10 +31,21 @@ elseif #parsed.actions > 0 and line:sub(1,1) == "\t" then table.insert(parsed.actions[#parsed.actions].extra, line:sub(2)); elseif line:match("^%s*$") or line:match("^#") or line:match("^([/-])%1") then - -- Blank line or comment + -- Blank line or comment, save as annotation + if #line == 0 then + if annotation then + annotation.closed = true; + end + else + if (not annotation) or annotation.closed then + annotation = { line }; + else + table.insert(annotation, line); + end + end else last_object = nil; - local name, action, extra = line:match("^(%a+) (%a+):?%s?(.*)$"); + local name, action, extra = line:match("^([^:]+) (%a+):?%s?(.*)$"); if not name then return nil, "Unable to parse action on line "..line_number; end @@ -44,7 +56,9 @@ object_name = name; action = action:lower(); extra = {#extra>0 and extra or nil}; + annotation = annotation and table.concat(annotation, "\n") or nil; }); + annotation = nil; end end return parsed;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scansion/queue.lua Tue Oct 27 23:14:19 2015 +0000 @@ -0,0 +1,72 @@ +-- Prosody IM +-- Copyright (C) 2008-2015 Matthew Wild +-- Copyright (C) 2008-2015 Waqas Hussain +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +-- Small ringbuffer library (i.e. an efficient FIFO queue with a size limit) +-- (because unbounded dynamically-growing queues are a bad thing...) + +local have_utable, utable = pcall(require, "util.table"); -- For pre-allocation of table + +local function new(size, allow_wrapping) + -- Head is next insert, tail is next read + local head, tail = 1, 1; + local items = 0; -- Number of stored items + local t = have_utable and utable.create(size, 0) or {}; -- Table to hold items + + return { + _items = t; + size = size; + count = function (self) return items; end; + push = function (self, item) + if items >= size then + if allow_wrapping then + tail = (tail%size)+1; -- Advance to next oldest item + items = items - 1; + else + return nil, "queue full"; + end + end + t[head] = item; + items = items + 1; + head = (head%size)+1; + return true; + end; + pop = function (self) + if items == 0 then + return nil; + end + local item; + item, t[tail] = t[tail], 0; + tail = (tail%size)+1; + items = items - 1; + return item; + end; + peek = function (self) + if items == 0 then + return nil; + end + return t[tail]; + end; + items = function (self) + return function (t, pos) + if pos >= t:count() then + return nil; + end + local read_pos = tail + pos; + if read_pos > t.size then + read_pos = (read_pos%size); + end + return pos+1, t._items[read_pos]; + end, self, 0; + end; + }; +end + +return { + new = new; +}; +
--- a/scripts/basic_message.scs Wed Sep 23 01:03:56 2015 +0200 +++ b/scripts/basic_message.scs Tue Oct 27 23:14:19 2015 +0000 @@ -1,3 +1,5 @@ +# A script testing basic message routing and delivery + [Client] Romeo jid: user@localhost password: password @@ -6,18 +8,32 @@ jid: juliet@localhost password: password +[Client] Juliet's phone + jid: juliet@localhost + password: password + resource: mobile + --------- +# Act 1, scene 1 +# The clients connect + Romeo connects Juliet connects +Juliet's phone connects + +# Romeo publishes his presence. Juliet has not, and so does not receive presence. + Romeo sends: <presence/> Romeo receives: <presence from="${Romeo's full JID}" /> +# Romeo sends a message to Juliet's full JID + Romeo sends: <message to="${Juliet's full JID}" type="chat"> <body>Hello Juliet!</body> @@ -28,6 +44,115 @@ <body>Hello Juliet!</body> </message> +# Romeo sends a message to Juliet's phone + +Romeo sends: + <message to="${Juliet's phone's full JID}" type="chat"> + <body>Hello Juliet, on your phone.</body> + </message> + +Juliet's phone receives: + <message to="${Juliet's phone's full JID}" from="${Romeo's full JID}" type="chat"> + <body>Hello Juliet, on your phone.</body> + </message> + +# Scene 2 +# This requires the server to support offline messages (which is optional). + +# Romeo sends a message to Juliet's bare JID. This is not immediately delivered, as she +# has not published presence on either of her resources. + +Romeo sends: + <message to="juliet@localhost" type="chat"> + <body>Hello Juliet, are you there?</body> + </message> + +# Juliet sends presence on her phone, and should receive the message there + +Juliet's phone sends: + <presence/> + +Juliet's phone receives: + <presence/> + +Juliet's phone receives: + <message from="${Romeo's full JID}" type="chat"> + <body>Hello Juliet, are you there?</body> + <delay xmlns='urn:xmpp:delay' from='localhost' /> + </message> + +# Romeo sends another bare-JID message, it should be delivered +# instantly to Juliet's phone + +Romeo sends: + <message to="juliet@localhost" type="chat"> + <body>Oh, hi!</body> + </message> + +Juliet's phone receives: + <message from="${Romeo's full JID}" type="chat"> + <body>Oh, hi!</body> + </message> + +# Juliet's laptop goes online, but with a negative priority + +Juliet sends: + <presence> + <priority>-1</priority> + </presence> + +# Again, Romeo sends a message to her bare JID, but it should +# only get delivered to her phone: + +Romeo sends: + <message to="juliet@localhost" type="chat"> + <body>How are you?</body> + </message> + +Juliet's phone receives: + <message from="${Romeo's full JID}" type="chat"> + <body>How are you?</body> + </message> + +# Romeo sends direct to Juliet's full JID, and she should receive it + +Romeo sends: + <message to="${Juliet's full JID}" type="chat"> + <body>Are you hiding?</body> + </message> + +Juliet receives: + <message from="${Romeo's full JID}" type="chat"> + <body>Are you hiding?</body> + </message> + +# Juliet publishes non-negative presence + +Juliet sends: + <presence/> + +# And now Romeo's bare JID messages get delivered to both resources +# (server behaviour may vary here) + +Romeo sends: + <message to="juliet@localhost" type="chat"> + <body>There!</body> + </message> + +Juliet receives: + <message from="${Romeo's full JID}" type="chat"> + <body>There!</body> + </message> + +Juliet's phone receives: + <message from="${Romeo's full JID}" type="chat"> + <body>There!</body> + </message> + +# The End + Romeo disconnects Juliet disconnects + +Juliet's phone disconnects