Differences From
Artifact [dec9af930b]:
761 761 return str
762 762 end
763 763 };
764 764 }
765 765
766 766 function ss.classinstance(o)
767 767 local g = getmetatable(o)
768 - if not o then return nil end
768 + if not g then return nil end
769 769 local mm = getmetatable(g)
770 - if not o then return nil end
770 + if not mm then return nil end
771 771 if mm.__name == 'class' then
772 772 return g
773 773 else
774 774 return nil
775 775 end
776 776 end
777 777
................................................................................
1214 1214 if #a ~= #b then return false end
1215 1215 eq = eq or function(p,q) return p == q end
1216 1216 for i = 1, #a do
1217 1217 if not eq(a[i],b[i]) then return false end
1218 1218 end
1219 1219 return true
1220 1220 end
1221 +
1222 +ss.os = {}
1223 +function ss.os.getcwd()
1224 + return os.getenv 'PWD' -- :((( HAX
1225 +end
1226 +
1227 +ss.path = ss.declare {
1228 + ident = 'path';
1229 + mk = function() return {
1230 + relative = true;
1231 + elts = {};
1232 + } end;
1233 + construct = function(me, o, rel)
1234 + if type(o) == 'string' then
1235 + if o:sub(1,1) == '/' then
1236 + me.relative = false
1237 + end
1238 + me.elts = ss.str.split(ss.str.enc.ascii, o, '/')
1239 + elseif type(o) == 'table' then
1240 + me.elts = o
1241 + me.relative = rel
1242 + end
1243 + end;
1244 + clonesetup = function(me)
1245 + me.elts = ss.clone(me.elts)
1246 + end;
1247 + cast = {
1248 + string = function(me)
1249 + return (me.relative and '' or '/') ..
1250 + table.concat(me.elts, '/')
1251 + end;
1252 + };
1253 + op = {
1254 + sub = function(a,b)
1255 + if a.relative ~= b.relative then
1256 + return nil
1257 + end
1258 +
1259 + local np = ss.path({}, true)
1260 + local brk = false
1261 + for i=1, math.max(#a.elts,#b.elts) do
1262 + if not brk then
1263 + if a.elts[i] ~= b.elts[i] then
1264 + brk = true
1265 + end
1266 + end
1267 + if brk then
1268 + table.insert(np.elts, b.elts[i])
1269 + end
1270 + end
1271 +
1272 + return np
1273 + end;
1274 + sum = function(a,b)
1275 + if b.relative == false then
1276 + return nil
1277 + end
1278 + local n = a:clone()
1279 + local i = #n.elts
1280 + for j, v in ipairs(b.elts) do
1281 + n.elts[i+j] = v
1282 + end
1283 + return n
1284 + end;
1285 + lt = function(a,b)
1286 + -- '/a/b/c' < '/a/b/c/d'
1287 + -- 'q/f/g' < 'q/f/g/p/d'
1288 + -- '/a' !< '/b', '/a' !< 'a'
1289 + if a.relative ~= b.relative then
1290 + return false
1291 + end
1292 + if #a.elts > #b.elts then return false end
1293 + for i=1, #a.elts do
1294 + if a.elts[i] ~= b.elts[i] then
1295 + return false
1296 + end
1297 + end
1298 + return true
1299 + end;
1300 + };
1301 + fns = {
1302 + dir = function(me)
1303 + local n = ss.copy(me.elts)
1304 + n[#n] = nil
1305 + local p = ss.path(n, me.relative)
1306 + end;
1307 + normalize = function(me)
1308 + local np = ss.path({}, me.relative)
1309 + for i, e in ipairs(me.elts) do
1310 + if e == '..' then
1311 + if me.relative and (
1312 + next(np.elts) == nil or
1313 + np.elts[#np.elts] == '..'
1314 + ) then
1315 + table.insert(np.elts, '..')
1316 + else
1317 + table.remove(np.elts)
1318 + end
1319 + elseif e ~= '.' then
1320 + table.insert(np.elts, e)
1321 + end
1322 + end
1323 + return np
1324 + end
1325 + };
1326 + cfns = {
1327 + cwd = function()
1328 + return ss.path(ss.os.getcwd())
1329 + end;
1330 + };
1331 +}
1221 1332
1222 1333 ss.uri = ss.declare {
1223 1334 ident = 'uri';
1224 1335 mk = function() return {
1225 1336 class = nil;
1226 1337 namespace = nil;
1227 1338 path = nil;
................................................................................
1242 1353 local s,r = rem:match '^([^:]+):(.*)$'
1243 1354 s_class, rem = s,r
1244 1355 end
1245 1356 if not rem then
1246 1357 ss.uri.exn('invalid URI ā%sā', str):throw()
1247 1358 end
1248 1359 local s_ns do
1249 - local s,r = rem:match '^//([^/]*)(.*)$'
1360 + local s,r = rem:match '^//([^/?#]*)(.*)$'
1250 1361 if s then s_ns, rem = s,r end
1251 1362 end
1252 1363 local h_query
1253 1364 local s_frag
1254 1365 local s_path if rem ~= '' then
1255 1366 local s,q,r = rem:match '^([^?#]*)([?#]?)(.*)$'
1256 1367 if s == '' then s = nil end
................................................................................
1303 1414 me.namespace = dec(s_ns)
1304 1415 me.path = dec(s_path)
1305 1416 me.query = dec(s_query)
1306 1417 me.frag = dec(s_frag)
1307 1418 end;
1308 1419 cast = {
1309 1420 string = function(me)
1421 + return me:construct('class','namespace','path','query','frag')
1422 + end;
1423 + };
1424 + fns = {
1425 + canfetch = function(me)
1426 + for id, pr in pairs(fetchableProtocols) do
1427 + for _, p in ipairs(pr.proto) do
1428 + if ss.match(me.class, p) then return id end
1429 + end
1430 + end
1431 + return false
1432 + end;
1433 + construct = function(me, ...)
1434 + local parts = {}
1435 + local function add(n, ...)
1436 + if n == nil then return end
1437 + table.insert(parts, me:part(n))
1438 + add(...)
1439 + end
1440 + add(...)
1441 + return table.concat(parts)
1442 + end;
1443 + part = function(me, p)
1310 1444 local function san(str, chars)
1311 1445 -- TODO IRI support
1312 1446 chars = chars or ''
1313 1447 local ptn = '-a-zA-Z0-9_.,;'
1314 1448 ptn = ptn .. chars
1315 1449 return (str:gsub('[^'..ptn..']', function(c)
1316 1450 if c == ' ' then return '+' end
1317 1451 return string.format('%%%02X', string.byte(c))
1318 1452 end))
1319 1453 end
1320 - if me.class == nil or next(me.class) == nil then
1321 - return 'none:'
1322 - end
1323 - local parts = {
1324 - table.concat(ss.map(san,me.class), '+') .. ':';
1325 - }
1326 - if me.namespace or me.auth or me.svc then
1327 - table.insert(parts, '//')
1328 - if me.auth then
1329 - table.insert(parts, san(me.auth,':') .. '@')
1454 + if p == 'class' then
1455 + if me.class == nil or next(me.class) == nil then
1456 + return 'none:'
1330 1457 end
1331 - if me.namespace then
1332 - table.insert(parts, san(me.namespace))
1333 - end
1334 - if me.svc then
1335 - table.insert(parts, ':' .. san(me.svc))
1336 - end
1337 - if me.path and not ss.str.begins(me.path, '/') then
1338 - table.insert(parts, '/')
1458 + return table.concat(ss.map(san,me.class), '+') .. ':';
1459 + else
1460 + if me[p] == nil then return '' end
1461 + if p == 'namespace' then
1462 + local parts = {}
1463 + if me.namespace or me.auth or me.svc then
1464 + table.insert(parts, '//')
1465 + if me.auth then
1466 + table.insert(parts, san(me.auth,':') .. '@')
1467 + end
1468 + if me.namespace then
1469 + table.insert(parts, san(me.namespace))
1470 + end
1471 + if me.svc then
1472 + table.insert(parts, ':' .. san(me.svc))
1473 + end
1474 + if me.path and not ss.str.begins(me.path, '/') then
1475 + table.insert(parts, '/')
1476 + end
1477 + end
1478 + return table.concat(parts)
1479 + elseif p == 'path' then
1480 + return san(me.path,'+/=&')
1481 + elseif p == 'query' then
1482 + return '?' .. san(me.query,'?+/=&')
1483 + elseif p == 'frag' then
1484 + return '#' .. san(me.frag,'+/=&')
1339 1485 end
1340 1486 end
1341 - if me.path then
1342 - table.insert(parts, san(me.path,'+/=&'))
1343 - end
1344 - if me.query then
1345 - table.insert(parts, '?' .. san(me.query,'?+/=&'))
1346 - end
1347 - if me.frag then
1348 - table.insert(parts, '#' .. san(me.frag,'+/=&'))
1349 - end
1350 - return table.concat(parts)
1351 - end;
1352 - };
1353 - fns = {
1354 - canfetch = function(me)
1355 - for id, pr in pairs(fetchableProtocols) do
1356 - for _, p in ipairs(pr.proto) do
1357 - if ss.match(me.class, p) then return id end
1358 - end
1359 - end
1360 - return false
1361 1487 end;
1362 1488 fetch = function(me, env)
1363 1489 local pid = me:canfetch()
1364 1490 if (not pid) or fetchableProtocols[pid].fetch == nil then
1365 1491 ss.uri.exn("URI ā%sā is unfetchable", tostring(me)):throw()
1366 1492 end
1367 1493 local proto = fetchableProtocols[pid]