| 1 | local lpeg = require "lpeg" |
| 2 | local setmetatable, tonumber = |
| 3 | setmetatable, tonumber; |
| 4 | local s_char = string.char; |
| 5 | |
| 6 | module("csv"); |
| 7 | |
| 8 | local delim = lpeg.P","; |
| 9 | |
| 10 | local char_escape = lpeg.R"az" + lpeg.S"\\\r\n" + delim; |
| 11 | local numeric_escape = (lpeg.R"09"^1)^-3; |
| 12 | local escape = (lpeg.P"\\" * (char_escape + numeric_escape)); |
| 13 | |
| 14 | local quoted_value = lpeg.P"\"" * ((1-lpeg.P"\"")^0) * lpeg.P"\""; |
| 15 | local value = quoted_value + (escape + (1-delim))^0; |
| 16 | |
| 17 | local escape_map = setmetatable({ |
| 18 | t = "\t", b = "\b", f = "\f"; |
| 19 | n = "\n", r = "\r", v = "\v"; }, |
| 20 | { __index = function (_, n) |
| 21 | if tonumber(n) then |
| 22 | print"n" |
| 23 | return s_char(tonumber(n)); |
| 24 | else |
| 25 | return n; |
| 26 | end |
| 27 | end |
| 28 | }); |
| 29 | |
| 30 | function read_record(line, value_callback) |
| 31 | local fieldpos = 0; |
| 32 | local callback = function (v) |
| 33 | return value_callback(v:gsub("\\(.)", escape_map)); |
| 34 | end; |
| 35 | repeat |
| 36 | fieldpos = lpeg.match(value / callback, line, fieldpos+1); |
| 37 | until fieldpos >= #line; |
| 38 | end |
| 39 | |
| 40 | return _M; |