summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilipp Gesang <phg@phi-gamma.net>2018-06-27 20:11:20 +0200
committerPhilipp Gesang <phg@phi-gamma.net>2018-07-04 08:08:24 +0200
commitdd3a98c473c723919d7a52658f19add97638b858 (patch)
tree272ef5bdc0c4d065ae86cde7672bb638ec3f17f2
parenta5266a028f7a9414af3ebc38ec569d3f7503cf51 (diff)
downloadcaldr-dd3a98c473c723919d7a52658f19add97638b858.tar.gz
cal: process lines into nested representation
-rw-r--r--[-rwxr-xr-x]cal.lua214
-rwxr-xr-x[-rw-r--r--]inout.lua11
2 files changed, 196 insertions, 29 deletions
diff --git a/cal.lua b/cal.lua
index bb48c68..b7f3a9d 100755..100644
--- a/cal.lua
+++ b/cal.lua
@@ -1,10 +1,11 @@
-#!/usr/bin/env lua
--[[--
RFC2445 parser / printer.
--]]--
+local pcall = pcall
+
local io = require "io"
local ioopen = io.open
local iostdin = io.stdin
@@ -15,6 +16,7 @@ local mathmin = math.min
local string = require "string"
local stringformat = string.format
+local stringlower = string.lower
local stringsub = string.sub
local table = require "table"
@@ -42,7 +44,15 @@ local errorln = mk_out ("stderr", 0, true)
local noiseln = mk_out ("stderr", 1, true)
local debugln = mk_out ("stderr", 2, true)
-local fmt_calendar do
+--[[--
+
+ fmt_calendar_lines (cal) -> string
+ — take a flattened lines representation of a calendar and formats it into a
+ string
+
+--]]--
+
+local fmt_calendar_lines do
local fold_rfc2445 = 75 --- B
local fold_wsp = " " --- could be tab as well
local crlf = "\r\n"
@@ -90,12 +100,13 @@ local fmt_calendar do
return tableconcat (acc, newline)
end
- fmt_calendar = function (cal, fold, newline, i, acc)
+ fmt_calendar_lines = function (cal, fold, newline, i, acc)
+ print("fmt_calendar_lines()", cal, fold, newline, i, acc)
if i == nil then
- return fmt_calendar (cal,
- fold or fold_rfc2445,
- newline or crlf,
- 1, { })
+ return fmt_calendar_lines (cal,
+ fold or fold_rfc2445,
+ newline or crlf,
+ 1, { })
end
if i > #cal then return tableconcat (acc, newline) end
@@ -108,7 +119,7 @@ local fmt_calendar do
acc [#acc + 1] = line
- return fmt_calendar (cal, fold, newline, i + 1, acc)
+ return fmt_calendar_lines (cal, fold, newline, i + 1, acc)
end
end
@@ -128,7 +139,14 @@ local fmt_calendar_params do
end
end
-local parse_calendar do
+--[[--
+
+ parse_calendar_lines(str)
+ — dissect a calendar *str* into lines, unfolding the input if necessary
+
+--]]--
+
+local parse_calendar_lines do
local lpeg = require "lpeg"
local lpegmatch = lpeg.match
local C = lpeg.C
@@ -269,8 +287,8 @@ local parse_calendar do
return lpegmatch (p_skip_line, raw, pos0)
end
- parse_calendar = function (raw, pos0, acc, consumed, nline, nskipped)
- if pos0 == nil then return parse_calendar (raw, 1, { }, 0, 1, 0) end
+ parse_calendar_lines = function (raw, pos0, acc, consumed, nline, nskipped)
+ if pos0 == nil then return parse_calendar_lines (raw, 1, { }, 0, 1, 0) end
local ok, pos1, name, params, value = parse_content_line (raw, pos0)
@@ -291,22 +309,151 @@ local parse_calendar do
, name = name, params = params, value = value }
end
- return parse_calendar (raw, pos1, acc, consumed + (pos1 - pos0), nline + 1,
- nskipped)
+ return parse_calendar_lines (raw, pos1, acc, consumed + (pos1 - pos0),
+ nline + 1, nskipped)
end
-end
+end --- [parse_calendar_lines]
-local loaddata = function (fname)
- local fh = ioopen (fname, "r")
+local calendar_of_lines do
+ --[[--
+
+ line handlers either return *true*, new line index, data or
+ *false*, new line index and an error message.
+
+ --]]--
+ local process
+
+ process = function (lines, n, acc)
+ if n == nil then n = 1 end
+
+ if n > #lines then
+ if acc == nil then
+ return false, n, "no usable input"
+ end
+ return true, n, acc
+ end
+
+ local cur = lines [n]
+ if cur == nil then
+ return false, n, "hit garbage line"
+ end
+
+ local curname = stringlower (cur.name)
+ local curvalue = cur.value
+
+ if curname == "begin" then
+ if stringlower (curvalue) == "vcalendar" then
+ acc = { value = { } }
+ end
- if fh == nil then
- error ("could not open file " .. fname .. " for reading")
+ local ok, nn, ret = process (lines, n + 1,
+ { kind = "scope"
+ , name = curvalue
+ , params = cur.params
+ , value = { }
+ })
+ if not ok then
+ return false, nn, stringformat ("bad nested content in “%s” scope: %s",
+ curvalue, ret)
+ end
+ --- parsing successful, append to current elements and continue with
+ --- the next line
+ acc.value [#acc.value + 1] = ret
+ return process (lines, nn, acc)
+ end
+
+ if curname == "end" then
+ if curvalue ~= acc.name then
+ return false, n, stringformat ("scope mismatch: \z
+ expecting “%s”, got “%s”",
+ n, acc.kind, curvalue)
+ end
+ --- closing the current scope, return accu
+ return true, n + 1, acc
+ end
+
+ acc.value [#acc.value + 1] = { kind = "other"
+ , name = curname
+ , value = curvalue
+ , params = cur.params
+ }
+
+ return process (lines, n + 1, acc)
end
- local ret = fh:read ("*a")
- fh:close ()
+ calendar_of_lines = function (lines)
+ local ok, n, components = process (lines)
+ if not ok then
+ return error (stringformat ("error at index %d: %s", n, components))
+ end
+ return components
+ end
+end --- [calendar_of_lines]
+
+
+--[[--
+
+ lines_of_calendar (cal) -> bool, lines
+ — takes a structured calendar and returns a flattened representation of lines
+
+--]]--
+
+local lines_of_calendar do
+
+ lines_of_calendar = function (cal, n, acc)
+ if n == nil then return lines_of_calendar (cal, 1, { }) end
+
+ local cur = cal [n]
+ if cur == nil then
+ return false, stringformat ("bad calendar object at index %d", n)
+ end
+
+ local curkind = cur.kind
+ local curname = stringupper (cur.name)
+ local curvalue = cur.value
+
+ if curkind == "scope" then
+ acc [#acc + 1] = { name = "BEGIN", value = curvalue }
+ local ok
+ ok, acc = lines_of_calendar (cal, n, acc)
+ if not ok then
+ return false, stringformat ("bad calendar component at index %d \z
+ inside “%s” scope: %s",
+ n, cur.value, acc)
+ end
+ acc [#acc + 1] = { name = "END", value = curvalue }
+
+ return lines_of_calendar (cal, n + 1, acc)
+ end
- return ret
+ if curkind == "other" then
+ acc [#acc + 1] = { name = curname
+ , value = curvalue
+ , prams = cur.params
+ }
+ return lines_of_calendar (cal, n + 1, acc)
+ end
+
+ return false, stringformat ("invalid object kind “%s” at index %d \z
+ in calendar object", curkind, n)
+ end
+end --- [lines_of_calendar]
+
+--[[--
+
+ fmt_calendar (cal) -> string
+ — takes a structured calendar and emits a string in RFC2445 format.
+
+--]]--
+
+local fmt_calendar = function (cal)
+ local ok, lines = lines_of_calendar (cal)
+ if not ok then
+ return ok, stringformat ("failed to convert calendar to temporary line format: %s",
+ lines)
+ end
+
+ return fmt_calendar_lines (lines)
end
-------------------------------------------------------------------------------
@@ -316,20 +463,30 @@ end
local set_verbosity = function (n) verboselvl = n end
local parse = function (raw)
- local ok, obj = pcall (parse_calendar, raw)
- return ok and obj
+ local ok, lines = pcall (parse_calendar_lines, raw)
+ if not ok then
+ return false, "failed to parse calendar object: " .. lines
+ end
+
+ local ok, obj = pcall (calendar_of_lines, lines)
+ if not ok then
+ return false, "failed to internalize calendar structure: " .. obj
+ end
+
+ return true, obj
end
local parse_handle = function (fh)
local raw = fh:read ("*a")
- return raw ~= nil and parse (raw)
+ if raw == nil then return nil end
+ return parse (raw)
end
local parse_file = function (fname)
local fh = ioopen (fname, "r")
- local cal = parse_handle (fh)
+ local ok, cal = parse_handle (fh)
fh:close ()
- return cal
+ return ok, cal
end
local parse_stdin = function ()
@@ -341,7 +498,10 @@ local format = function (cal, newline)
end
local output_handle = function (cal, fh)
- local str = fmt_calendar (cal, nil, newline or "\n")
+ local ok, str = fmt_calendar (cal, nil, newline or "\n")
+ if not ok then
+ return false, str
+ end
fh:write (str)
end
diff --git a/inout.lua b/inout.lua
index ad1c23d..5878c16 100644..100755
--- a/inout.lua
+++ b/inout.lua
@@ -1,9 +1,16 @@
+#!/usr/bin/env lua
local cal = require "cal"
local main = function (argv)
- local ok = cal.output (cal.parse_stdin ())
+ local ok, parsed = cal.parse_stdin ()
+ if not ok then
+ error ("error parsing input: " .. parsed)
+ return -1
+ end
- return ok and 0 or -1
+ cal.output (parsed)
+
+ return 0
end
os.exit (main ())