Differences From
Artifact [550cdedbd6]:
4 4 -- ? utility library with functionality common to
5 5 -- cortav.lua and its extensions
6 6 -- from Ranuir "software utility"
7 7 -- > local ss = require 'sirsem.lua'
8 8
9 9 local ss
10 10 do -- pull ourselves up by our own bootstraps
11 - local package = _G.package -- prevent namespace from being broken by env shenanigans
11 + local package = _G.package
12 + -- prevent namespace from being broken by env shenanigans
12 13 local function namespace(name, tbl)
13 14 local pkg = tbl or {}
14 15 if package then
15 16 package.loaded[name] = pkg
16 17 end
17 18 return pkg
18 19 end
................................................................................
600 601
601 602 if c.op then
602 603 cls.__add = c.op.sum
603 604 cls.__sub = c.op.sub
604 605 cls.__div = c.op.div
605 606 cls.__mul = c.op.mul
606 607 cls.__concat = c.op.cat
608 + cls.__eq = c.op.eq
609 + cls.__lt = c.op.lt
607 610 end
608 611
609 612 cls.mk = function(...)
610 613 local val = setmetatable(c.mk and c.mk(...) or {}, cls)
611 614 if c.init then
612 615 for k,v in pairs(c.init) do
613 616 val[k] = v
................................................................................
685 688
686 689 ss.version = ss.declare {
687 690 name = 'version';
688 691 mk = function(tbl) return tbl end;
689 692 fns = {
690 693 pre = function(self,other) end;
691 694 post = function(self,other) end;
692 - string = function(self) return tostring(self) end;
695 + string = function(self) return tostring(self) end;
693 696 };
694 697 cast = {
695 698 string = function(vers)
696 699 if not(next(vers)) then return '0.0' end
697 700 local str = ''
698 701 for _,v in pairs(vers) do
699 702 if type(v) == 'string' then
................................................................................
766 769 function ss.tuple.cdr(x, ...) return ... end
767 770
768 771 ss.stack = ss.declare {
769 772 ident = 'stack';
770 773 mk = function() return {
771 774 top = 0;
772 775 store = {};
773 - } end;
776 + } end;
774 777 index = function(me, i)
775 778 if i <= 0 then
776 779 return me.store[me.top + i]
777 780 else
778 781 return me.store[i]
779 782 end
780 783 end;
781 784 fns = {
782 785 push = function(me, val, ...)
783 - if val~=nil then
784 - me.top = me.top + 1
785 - me.store[me.top] = val
786 - me:push(...)
787 - end
788 - return val, ...
789 - end;
790 - pop = function(me,n) n = n or 1
791 - local r = {}
786 + if val~=nil then
787 + me.top = me.top + 1
788 + me.store[me.top] = val
789 + me:push(...)
790 + end
791 + return val, ...
792 + end;
793 + pop = function(me,n) n = n or 1
794 + local r = {}
792 795 if n < me.top then
793 796 for i = 0,n-1 do
794 797 r[i+1] = me.store[me.top - i]
795 798 me.store[me.top - i] = nil
796 799 end
797 800 me.top = me.top - n
798 - else
799 - r = me.store
801 + else
802 + r = me.store
800 803 me.store = {}
801 - end
804 + end
802 805 return table.unpack(r)
803 - end;
804 - set = function(me,val)
805 - if me.top == 0 then
806 - me.top = me.top + 1 --autopush
807 - end
808 - me.store[me.top] = val
809 - end;
810 - all = function(me) return table.unpack(me.store) end;
811 - each = function(forward)
812 - if forward then
813 - local idx = 0
814 - return function()
815 - idx = idx + 1
816 - if idx > top
806 + end;
807 + set = function(me,val)
808 + if me.top == 0 then
809 + me.top = me.top + 1 --autopush
810 + end
811 + me.store[me.top] = val
812 + end;
813 + all = function(me) return table.unpack(me.store) end;
814 + each = function(me,forward)
815 + if forward then
816 + local idx = 0
817 + return function()
818 + idx = idx + 1
819 + if idx > me.top
820 + then return nil
821 + else return me.store[idx], idx
822 + end
823 + end
824 + else
825 + local idx = me.top + 1
826 + return function()
827 + idx = idx - 1
828 + if idx == 0
817 829 then return nil
818 830 else return me.store[idx], idx
819 831 end
820 - end
821 - else
822 - local idx = top + 1
823 - return function()
824 - idx = idx - 1
825 - if idx == 0
826 - then return nil
827 - else return me.store[idx], idx
828 - end
829 - end
830 - end
831 - end;
832 + end
833 + end
834 + end;
832 835 };
833 836 }
834 837
835 838 ss.automat = ss.declare {
836 839 ident = 'automat';
837 840 mk = function() return {
838 841 state = ss.stack();
................................................................................
1107 1110 -- versions at least can launch programs in a sane and secure
1108 1111 -- way.
1109 1112 else
1110 1113 return s
1111 1114 end
1112 1115 end, ...))
1113 1116 end
1117 +
1118 +ss.mime = ss.declare {
1119 + ident = 'mime-type';
1120 + mk = function() return {
1121 + class = nil;
1122 + kind = nil;
1123 + opts = {};
1124 + } end;
1125 + construct = function(me,str)
1126 + if not str then return end
1127 + local p,o = str:match '^([^;]+);?%s*(.-)$'
1128 + if not p then ss.mime.exn('invalid type syntax %s',str):throw() end
1129 + local c,k = p:match '^([^/]+)/?(.-)$'
1130 + me.class = (c ~= '') and c or nil
1131 + me.kind = (k ~= '') and k or nil
1132 + if o and o ~= '' then
1133 + for key, e, val in o:gmatch '%s*([^=;]+)(=?)([^;]*)' do
1134 + if me.opts[key] then
1135 + ss.mime.exn('mime type cannot contain multiple %s options',key):throw()
1136 + elseif me.opts.hex and key == 'base64'
1137 + or me.opts.base64 and key == 'hex' then
1138 + ss.mime.exn('mime type cannot more than one of (base64, hex)',key):throw()
1139 + end
1140 + if e == '' then val = true end
1141 + me.opts[key] = val
1142 + end
1143 + end
1144 + end;
1145 + op = {
1146 + eq = function(self, other)
1147 + -- exact match operator
1148 + if not ss.mime.is(other) then return ss.mime.exn("tried to compare MIME type %s against %s (%s)", tostring(self), type(other), tostring(other)):throw() end
1149 + if (self.kind == other.kind or (self.kind == '*' or other.kind == '*')) and
1150 + (self.class == other.class or (self.class == '*' or other.class == '*')) and
1151 + (#self.opts ==#other.opts) then
1152 + for k,v in pairs(self.opts) do
1153 + if not(other.opts[k] == '*' or (v == '*' and other.opts[k])) then
1154 + if other.opts[k] ~= v then return false end
1155 + end
1156 + end
1157 + for k,v in pairs(other.opts) do
1158 + if not(self.opts[k] == '*' or (v == '*' and self.opts[k])) then
1159 + if self.opts[k] ~= v then return false end
1160 + end
1161 + end
1162 + return true
1163 + else
1164 + return false
1165 + end
1166 + end;
1167 + lt = function(self,other)
1168 + -- lt is the "subset?" operator -- it returns true if self
1169 + -- matches at least as many fields as other has. use this
1170 + -- when you have a base type and want to check whether
1171 + -- another type is compatible with that type. say all you
1172 + -- care about is whether a file is "text/plain", and it
1173 + -- can be encoded however as long as that much fits.
1174 + -- you would then ask ss.mime'text/plain' < file.mime
1175 + return other:superset_of(self)
1176 + end;
1177 + };
1178 + cast = {
1179 + string = function(me)
1180 + local r
1181 + if me.kind and me.class then
1182 + r = string.format('%s/%s',me.class,me.kind)
1183 + elseif me.class then
1184 + r = me.class
1185 + end
1186 + for k,v in pairs(me.opts) do
1187 + if v and v ~= true then
1188 + r = r .. string.format(';%s=%s',k,v)
1189 + elseif v == true then
1190 + r = r .. string.format(';%s',k)
1191 + end
1192 + end
1193 + return r
1194 + end;
1195 + };
1196 + fns = {
1197 + superset_of = function(self, other)
1198 + -- a mime type is greater than another if all the fields
1199 + -- other has have a matching field in self. think of this
1200 + -- as the "superset?" operator -- all fields and options
1201 + -- on other must either match self or be unset
1202 + if not ss.mime.is(other) then return ss.mime.exn("tried to compare MIME type %s against %s (%s)", tostring(self), type(other), tostring(other)):throw() end
1203 + if (other.class and self.class ~= other.class and other.class ~='*')
1204 + or (other.kind and self.kind ~= other.kind and other.kind ~= '*')
1205 + then return false end
1206 + for k,v in pairs(other.opts) do
1207 + if self.opts[k] and self.opts[k] ~= v and v ~='*' then
1208 + return false
1209 + end
1210 + end
1211 + return true
1212 + end;
1213 + is = function(me, pc)
1214 + local mimeclasses = {
1215 + ['application/svg+xml'] = 'image';
1216 + ['application/x-tar'] = 'archive';
1217 + }
1218 + local c = me.class
1219 + for k,v in pairs(mimeclasses) do
1220 + if me > ss.mime(k) then
1221 + c = v break
1222 + end
1223 + end
1224 + print(c)
1225 + return c == pc
1226 + end;
1227 + };
1228 +}
1229 +ss.mime.exn = ss.exnkind 'MIME error'