cortav  Diff

Differences From Artifact [fc129ed31b]:

To Artifact [f20a833e35]:


   150    150   	fns = {
   151    151   		throw = function(me) error(me) end;
   152    152   	}
   153    153   }
   154    154   ct.exnkind = declare {
   155    155   	ident = 'exn-kind';
   156    156   	mk = function(desc, report)
   157         -		return { desc = desc, report = report }
          157  +		return {
          158  +			desc = desc;
          159  +			report = report or function(msg,...)
          160  +				return string.format(msg,...)
          161  +			end;
          162  +		}
   158    163   	end;
   159    164   	call = function(me, ...)
   160    165   		return ct.exn(me, ...)
   161    166   	end;
   162    167   }
   163    168   
   164    169   ct.exns = {
   165    170   	tx = ct.exnkind('translation error', function(msg,...)
   166    171   		return string.format("(%s:%u) "..msg, ...)
   167         -	end)
          172  +	end);
          173  +	io = ct.exnkind('IO error', function(msg, ...)
          174  +		return string.format("<%s %s> "..msg, ...)
          175  +	end);
          176  +	cli = ct.exnkind 'command line parse error';
          177  +	mode = ct.exnkind('bad mode', function(msg, ...)
          178  +		return string.format("mode “%s” "..msg, ...)
          179  +	end);
   168    180   }
   169    181   
   170    182   ct.ctx = declare {
   171    183   	mk = function(src) return {src = src} end;
   172    184   	ident = 'context';
   173    185   	cast = {
   174    186   		string = function(me)
................................................................................
   390    402   		end
   391    403   
   392    404   		local span_renderers = {}
   393    405   		local function htmlSpan(spans, block, sec)
   394    406   			local text = {}
   395    407   			for k,v in pairs(spans) do
   396    408   				if type(v) == 'string' then
   397         -					table.insert(text,(v:gsub('[<>&]',
          409  +					table.insert(text,(v:gsub('[<>&"]',
   398    410   						function(x)
   399         -							return string.format('&#%02u', string.byte(x))
          411  +							return string.format('&#%02u;', string.byte(x))
   400    412   						end)))
   401    413   				else
   402    414   					table.insert(text, span_renderers[v.kind](v, block, sec))
   403    415   				end
   404    416   			end
   405    417   			return table.concat(text)
   406    418   		end
   407    419   
   408    420   		function span_renderers.format(sp)
   409    421   			local tags = { strong = 'strong', emph = 'em', strike = 'del', insert = 'ins', literal = 'code' }
   410         -			if sp.style == 'literal' then
          422  +			if sp.style == 'literal' and not opts['fossil-uv'] then
   411    423   				stylesNeeded.code = true
   412    424   			end
   413    425   			if sp.style == 'del' or sp.style == 'ins' then
   414    426   				stylesNeeded.editors_markup = true
   415    427   			end
   416    428   			return tag(tags[sp.style],nil,htmlSpan(sp.spans))
   417    429   		end
................................................................................
   677    689   
   678    690   	local styles = {}
   679    691   	for k in pairs(stylesNeeded) do
   680    692   		table.insert(styles, (stylesets[k]:gsub('%s+',' ')))
   681    693   	end
   682    694   
   683    695   	local head = {}
          696  +	local styletag = ''
          697  +	if opts['link-css'] then
          698  +		local css = opts['link-css']
          699  +		if type(css) ~= 'string' then ct.exns.mode('must be a string', 'html:link-css'):throw() end
          700  +		styletag = styletag .. elt('link',{rel='stylesheet',type='text/css',href=opts['link-css']})
          701  +	end
   684    702   	if next(styles) then
   685         -		table.insert(head, tag('style',{type='text/css'},table.concat(styles)))
          703  +		if opts['gen-styles'] then
          704  +			styletag = styletag .. tag('style',{type='text/css'},table.concat(styles))
          705  +		end
          706  +		table.insert(head, styletag)
   686    707   	end
   687    708   
   688         -	if opts.snippet then
   689         -		return body
          709  +	if opts['fossil-uv'] then
          710  +		return tag('div',{class='fossil-doc',['data-title']=doctitle},styletag .. body)
          711  +	elseif opts.snippet then
          712  +		return styletag .. body
   690    713   	else
   691    714   		return dr.htmlDoc(doctitle, next(head) and table.concat(head), body)
   692    715   	end
   693    716   end
   694    717   
   695    718   local function
   696    719   startswith(str, pfx)
................................................................................
   929    952   		if     a1 == ':' and a2 ~= ':' then
   930    953   			align = 'left'
   931    954   		elseif a1 == ':' and a2 == ':' then
   932    955   			align = 'center'
   933    956   		elseif a1 ~= ':' and a2 == ':' then
   934    957   			align = 'right'
   935    958   		end
          959  +		text = text:match '^%s*(.-)%s*$'
   936    960   		table.insert(row, {
   937    961   			spans = ct.parse_span(text, c);
   938    962   			align = align;
   939    963   			header = header;
   940    964   		})
   941    965   	end
   942    966   	if #c.sec.blocks > 1 and c.sec.blocks[#c.sec.blocks].kind == 'table' then
................................................................................
  1100   1124   		end
  1101   1125   	end
  1102   1126   
  1103   1127   	return ctx.doc
  1104   1128   end
  1105   1129   
  1106   1130   local default_mode = {
  1107         -	format = 'html';
         1131  +	['render:format'] = 'html';
         1132  +	['html:gen-styles'] = true;
  1108   1133   }
         1134  +
         1135  +local function filter(list, fn)
         1136  +	local new = {}
         1137  +	for i, v in ipairs(list) do
         1138  +		if fn(v,i) then table.insert(new, v) end
         1139  +	end
         1140  +	return new
         1141  +end
         1142  +
         1143  +local function kmap(fn, list)
         1144  +	local new = {}
         1145  +	for k, v in pairs(list) do
         1146  +		local nk,nv = fn(k,v)
         1147  +		new[nk or k] = nv or v
         1148  +	end
         1149  +	return new
         1150  +end
         1151  +local function kfilter(list, fn)
         1152  +	local new = {}
         1153  +	for k, v in pairs(list) do
         1154  +		if fn(k,v) then new[k] = v end
         1155  +	end
         1156  +	return new
         1157  +end
  1109   1158   
  1110   1159   local function main(input, output, log, mode, vars)
  1111   1160   	local doc = ct.parse(input.stream, input.src)
  1112   1161   	input.stream:close()
  1113         -	if mode['show-tree'] then
         1162  +	if mode['parse:show-tree'] then
  1114   1163   		log:write(dump(doc))
  1115   1164   	end
  1116   1165   
  1117         -	if not mode.format then
         1166  +	if not mode['render:format'] then
  1118   1167   		error 'what output format should i translate the input to?'
  1119   1168   	end
  1120         -	if not ct.render[mode.format] then
  1121         -		error(string.format('output format “%s” unsupported', mode.format))
         1169  +	if not ct.render[mode['render:format']] then
         1170  +		error(string.format('output format “%s” unsupported', mode['render:format']))
  1122   1171   	end
  1123   1172   	
  1124         -	output:write(ct.render[mode.format](doc, {}))
         1173  +	local render_opts = kmap(function(k,v)
         1174  +		return k:sub(2+#mode['render:format'])
         1175  +	end, kfilter(mode, function(m)
         1176  +		return startswith(m, mode['render:format']..':')
         1177  +	end))
         1178  +
         1179  +	output:write(ct.render[mode['render:format']](doc, render_opts))
  1125   1180   end
  1126   1181   
  1127   1182   local inp,outp,log = io.stdin, io.stdout, io.stderr
  1128   1183   
  1129   1184   local function entry_cli()
  1130   1185   	local mode, vars, input = default_mode, {}, {
  1131   1186   		stream = inp;
  1132   1187   		src = {
  1133   1188   			file = '(stdin)';
  1134   1189   		}
  1135   1190   	}
  1136   1191   
  1137         -	if arg[1] and arg[1] ~= '' then
         1192  +	local optnparams = function(o)
         1193  +		local param_opts = {
         1194  +			out = 1;
         1195  +			log = 1;
         1196  +			define = 2; -- key value
         1197  +			['mode-set'] = 1;
         1198  +			['mode-clear'] = 1;
         1199  +			mode = 2;
         1200  +		}
         1201  +		return param_opts[o] or 0
         1202  +	end
         1203  +
         1204  +	local optmap = {
         1205  +		o = 'out';
         1206  +		l = 'log';
         1207  +		d = 'define';
         1208  +		V = 'version';
         1209  +		h = 'help';
         1210  +		y = 'mode-set', n = 'mode-clear';
         1211  +		m = 'mode';
         1212  +	}
         1213  +
         1214  +	local checkmodekey = function(key)
         1215  +		if not key:match '[^:]+:.+' then
         1216  +			ct.exns.cli('invalid mode key %s', key):throw()
         1217  +		end
         1218  +		return key
         1219  +	end
         1220  +	local onswitch = {
         1221  +		out = function(file)
         1222  +			local nf = io.open(file,'wb')
         1223  +			if nf then outp:close() outp = nf else
         1224  +				ct.exns.io('could not open output file for writing', 'open',file):throw()
         1225  +			end
         1226  +		end;
         1227  +		log = function(file)
         1228  +			local nf = io.open(file,'wb')
         1229  +			if nf then log:close() log = nf else
         1230  +				ct.exns.io('could not open log file for writing', 'open',file):throw()
         1231  +			end
         1232  +		end;
         1233  +		define = function(key,value)
         1234  +			-- set context key
         1235  +		end;
         1236  +		mode = function(key,value) mode[checkmodekey(key)] = value end;
         1237  +		['mode-set'] = function(key) mode[checkmodekey(key)] = true end;
         1238  +		['mode-clear'] = function(key) mode[checkmodekey(key)] = false end;
         1239  +	}
         1240  +
         1241  +	local args = {}
         1242  +	local keepParsing = true
         1243  +	do local i = 1 while i <= #arg do local v = arg[i]
         1244  +		local execLongOpt = function(longopt)
         1245  +			if not onswitch[longopt] then
         1246  +				ct.exns.cli('switch --%s unrecognized', longopt):throw()
         1247  +			end
         1248  +			local nargs = optnparams(longopt)
         1249  +
         1250  +			if nargs > 1 then
         1251  +				if i + nargs > #arg then
         1252  +					ct.exns.cli('not enough arguments for switch --%s (%u expected)', longopt, nargs):throw()
         1253  +				end
         1254  +				local nt = {}
         1255  +				for j = i+1, i+nargs do
         1256  +					table.insert(nt, arg[j])
         1257  +				end
         1258  +				onswitch[longopt](table.unpack(nt))
         1259  +			elseif nargs == 1 then
         1260  +				onswitch[longopt](arg[i+1])
         1261  +			end
         1262  +			i = i + nargs
         1263  +		end
         1264  +		if v == '--' then
         1265  +			keepParsing = false
         1266  +		else
         1267  +			local longopt = v:match '^%-%-(.+)$'
         1268  +			if keepParsing and longopt then
         1269  +				execLongOpt(longopt)
         1270  +			else
         1271  +				if keepParsing and v:sub(1,1) == '-' then
         1272  +					for c,p in eachcode(v:sub(2)) do
         1273  +						if optmap[c] then
         1274  +							execLongOpt(optmap[c])
         1275  +						else
         1276  +							ct.exns.cli('switch -%i unrecognized', c):throw()
         1277  +						end
         1278  +					end
         1279  +				else
         1280  +					table.insert(args, v)
         1281  +				end
         1282  +			end
         1283  +
         1284  +		end
         1285  +	i = i + 1 end end
         1286  +
         1287  +	if args[1] and args[1] ~= '' then
  1138   1288   		local file = io.open(arg[1], "rb")
  1139         -		if not file then error('unable to load file ' .. arg[1]) end
         1289  +		if not file then error('unable to load file ' .. args[1]) end
  1140   1290   		input.stream = file
  1141         -		input.src.file = arg[1]
         1291  +		input.src.file = args[1]
  1142   1292   	end
  1143   1293   
  1144   1294   	main(input, outp, log, mode, vars)
  1145   1295   end
  1146   1296   
  1147   1297   -- local ok, e = pcall(entry_cli)
  1148   1298   local ok, e = true, entry_cli()