From dd3a98c473c723919d7a52658f19add97638b858 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Wed, 27 Jun 2018 20:11:20 +0200 Subject: cal: process lines into nested representation --- cal.lua | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- inout.lua | 11 +++- 2 files changed, 196 insertions(+), 29 deletions(-) mode change 100755 => 100644 cal.lua mode change 100644 => 100755 inout.lua diff --git a/cal.lua b/cal.lua old mode 100755 new mode 100644 index bb48c68..b7f3a9d --- 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 old mode 100644 new mode 100755 index ad1c23d..5878c16 --- 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 ()) -- cgit v1.2.3