cortav  Check-in [c15ffd5fef]

Overview
Comment:add weak modes that can be overridden by pragma
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: c15ffd5fef56f39abd50a1e181a3bc84c76b55a803978b4380c215e5dbc3c692
User & Date: lexi on 2021-12-20 14:10:16
Other Links: manifest | tags
Context
2021-12-21
01:22
tidy up repo, add freedesktop file format stuff and viewer handler script check-in: 3b2fea2df1 user: lexi tags: trunk
2021-12-20
14:10
add weak modes that can be overridden by pragma check-in: c15ffd5fef user: lexi tags: trunk
00:40
tweak headers check-in: bdd8d569a9 user: lexi tags: trunk
Changes

Modified cli.lua from [23a0968fc6] to [2240e68512].

    22     22   	for k, v in pairs(list) do
    23     23   		if fn(k,v) then new[k] = v end
    24     24   	end
    25     25   	return new
    26     26   end
    27     27   
    28     28   local function
    29         -main(input, output, log, mode, vars)
           29  +main(input, output, log, mode, suggestions, vars)
    30     30   	local doc = ct.parse(input.stream, input.src, mode)
    31     31   	input.stream:close()
    32     32   	if mode['parse:show-tree'] then
    33     33   		log:write(dump(doc))
    34     34   	end
           35  +
           36  +	-- the document has now had a chance to give its say; if it hasn't specified
           37  +	-- any modes of its own, we now merge in the 'weak modes' (suggestions)
           38  +	for k,v in pairs(suggestions) do
           39  +		if not mode[k] then mode[k] = v end
           40  +	end
    35     41   
    36     42   	if not mode['render:format'] then
    37     43   		error 'what output format should i translate the input to?'
    38     44   	end
    39     45   	if mode['render:format'] == 'none' then return 0 end
    40     46   	if not ct.render[mode['render:format']] then
    41     47   		ct.exns.unimpl('output format “%s” unsupported', mode['render:format']):throw()
................................................................................
    51     57   	
    52     58   	-- this is kind of gross but the context object belongs to the parser,
    53     59   	-- not the renderer, so that's not a suitable place for this information
    54     60   	doc.stage = {
    55     61   		kind = 'render';
    56     62   		format = mode['render:format'];
    57     63   		mode = mode;
           64  +		suggestions = suggestions;
    58     65   	}
    59     66   
    60     67   	output:write(ct.render[mode['render:format']](doc, render_opts))
    61     68   	return 0
    62     69   end
    63     70   
    64     71   local inp,outp,log = io.stdin, io.stdout, io.stderr
    65     72   
    66     73   local function entry_cli()
    67         -	local mode, vars, input = default_mode, {}, {
           74  +	local suggestions, vars, input = default_mode, {}, {
    68     75   		stream = inp;
    69     76   		src = {
    70     77   			file = '(stdin)';
    71     78   		}
    72     79   	}
           80  +
           81  +	local mode = {}
    73     82   
    74     83   	local optnparams = function(o)
    75     84   		local param_opts = {
    76     85   			out = 1;
    77     86   			log = 1;
    78     87   			define = 2; -- key value
           88  +
    79     89   			['mode-set'] = 1;
    80     90   			['mode-clear'] = 1;
    81     91   			mode = 2;
           92  +
           93  +			['mode-set-weak'] = 1;
           94  +			['mode-clear-weak'] = 1;
           95  +			['mode-weak'] = 2;
    82     96   		}
    83     97   		return param_opts[o] or 0
    84     98   	end
    85     99   
    86    100   	local optmap = {
    87    101   		o = 'out';
    88    102   		l = 'log';
    89    103   		d = 'define';
    90    104   		V = 'version';
    91    105   		h = 'help';
    92         -		y = 'mode-set', n = 'mode-clear';
    93         -		m = 'mode';
          106  +		y = 'mode-set',   Y = 'mode-set-weak';
          107  +		n = 'mode-clear', N = 'mode-clear-weak';
          108  +		m = 'mode',       M = 'mode-weak';
    94    109   	}
    95    110   
    96    111   	local checkmodekey = function(key)
    97    112   		if not key:match '[^:]+:.+' then
    98    113   			ct.exns.cli('invalid mode key %s', key):throw()
    99    114   		end
   100    115   		return key
................................................................................
   117    132   				ct.exns.cli 'cannot define variable in restricted namespace':throw()
   118    133   			end
   119    134   			vars[key] = value
   120    135   		end;
   121    136   		mode = function(key,value) mode[checkmodekey(key)] = value end;
   122    137   		['mode-set'] = function(key) mode[checkmodekey(key)] = true end;
   123    138   		['mode-clear'] = function(key) mode[checkmodekey(key)] = false end;
          139  +
          140  +		['mode-weak'] = function(key,value) suggestions[checkmodekey(key)] = value end;
          141  +		['mode-set-weak'] = function(key) suggestions[checkmodekey(key)] = true end;
          142  +		['mode-clear-weak'] = function(key) suggestions[checkmodekey(key)] = false end;
   124    143   	}
   125    144   
   126    145   	local args = {}
   127    146   	local keepParsing = true
   128    147   	do local i = 1 while i <= #arg do local v = arg[i]
   129    148   		local execLongOpt = function(longopt)
   130    149   			if not onswitch[longopt] then
................................................................................
   172    191   	if args[1] and args[1] ~= '' then
   173    192   		local file = io.open(arg[1], "rb")
   174    193   		if not file then error('unable to load file ' .. args[1]) end
   175    194   		input.stream = file
   176    195   		input.src.file = args[1]
   177    196   	end
   178    197   
   179         -	return main(input, outp, log, mode, vars)
          198  +	return main(input, outp, log, mode, suggestions, vars)
   180    199   end
   181    200   
   182    201   local ok, e = pcall(entry_cli)
   183    202   -- local ok, e = true, entry_cli()
   184    203   if not ok then
   185    204   	local str = 'translation failure'
   186    205   	if ss.exn.is(e) then

Modified cortav.ct from [96194d0b88] to [b7a7fbb952].

    89     89   
    90     90   on systems with environment variables, these may be accessed as context variables by prefixing their name with [$env.].
    91     91   
    92     92   different renderers may provide context in different ways, such as from command line options or a context file. any predefined variables should carry an appropriate prefix to prevent conflation. 
    93     93   
    94     94   ## directives
    95     95   	d: [$%[*[##1]]]
    96         -* {d format} gives a hint on how the document should be formatted. the first hint that is understood will be applied; all others will be discarded. standard hints include:
    97         -** essay
    98         -** narrative
    99         -** screenplay: uses asides to denote actions, quotes for dialogue
   100         -** stageplay: uses asides to denote actions, quotes for dialogue
   101         -** manual
   102         -** glossary
   103         -** news
   104     96   * {d author} encodes document authorship
   105     97   * {d cols} specifies the number of columns the next object should be rendered with
   106     98   * {d include} transcludes another file
   107     99   * {d quote} transcludes another file, without expanding the text except for paragraphs 
   108    100   * {d embed}, where possible, embeds another file as an object within the current one. in HTML this could be accomplished with e.g. an iframe.
   109    101   * {d expand} causes the next object (usually a code block) to be fully expanded when it would otherwise not be
          102  +* {d pragma} supplies semantic data about author intent, the kind of information document contains and hints about how it should be displayed to the user. think of them like offhand remarks to the renderer -- there's no guarantee that it'll pay any attention, but if it does, your document will look better. pragmas have no scope; they affect the entire document. the pragma function exists primarily as a means to allow parameters that would normally need to be specified on e.g. the command line to be encoded in the document instead in a way that multiple implementations can understand. a few standard pragmas are defined
          103  +** {d pragma layout} gives a hint on how the document should be layed out. the first hint that is understood will be applied; all others will be discarded. standard hints include:
          104  +*** essay
          105  +*** narrative
          106  +*** screenplay: uses asides to denote actions, quotes for dialogue
          107  +*** stageplay: uses asides to denote actions, quotes for dialogue
          108  +*** manual
          109  +*** glossary
          110  +*** news
          111  +** {d pragma accent} specifies an accent hue (in degrees around the color wheel) for renderers which support colorized output
          112  +** {d pragma accent-spread} is a factor that controls the "spread" of hues used in the document. if 0, only the accent color will be used; if larger, other hues will be used in addition to the primary accent color.
          113  +** {d pragma dark-on-light on|off} controls whether the color scheme used should be light-on-dark or dark-on-light
          114  +** {d pragma page-width} indicates how wide the pages should be
   110    115   
   111    116   ##ex examples
   112    117   
   113    118   ~~~ blockquotes #bq [cortav] ~~~
   114    119   the following excerpts of text were recovered from a partially erased hard drive found in the Hawthorne manor in the weeks after the Incident. context is unknown.
   115    120   
   116    121   #>
................................................................................
   247    252   
   248    253   ~~~
   249    254   $ luac -s -o stdin2html.lc $cortav_repo/{sirsem,cortav,ext/toc}.lua stdin2html.lua
   250    255   ~~~
   251    256   
   252    257   and can then be operated with the command [$lua stdin2html.lc], with no further need for the cortav repository files. note that the order of the [$luac] command is important! [$sirsem.lua] must come first, followed by [$cortav.lua], followed by any extensions. your driver script (i.e. the script with the entry point into the application) should always come last.
   253    258   
          259  +### building custom tools
          260  +generally, most existing file-format conversion tools (cmark, pandoc, and so on) have a crucial limitation: they hardcode specific assumptions like document structure. this means that the files they output are generally not suitable as-is for the users' purposes, and require further munging, usually by hateful shell or perl scripts. some tools do provide libraries end users to use as a basis for designing their own tools, but these are often limited, and in any case the user ends up having to write their own (non-standard) driver. it's no surprise that very few people end up doing this.
          261  +
          262  +[$cortav.lua]'s design lends itself to a more elegant solution. one can of course write their own driver using [$cortav] as a library, but most of the time when you're compiling document sources, you just want a binary you can run from the command line or a makefile. with [$cortav.lua], you can extend its capabilities easily while keeping the same driver.
          263  +
          264  +in the [$cortav] spec, extensions are mostly intended to give different implementations the ability to offer extra capabilities, but the reference implementation uses an extension architecture that makes it easy to write and add your own. for each type of new behavior you want to implement, just create a new extension and list it on the make command line:
          265  +
          266  +~~~
          267  +$ nvim ~/dev/my-cortav-exts/imperial-edict.lua
          268  +$ make cortav extens+=$HOME/dev/my-cortav-exts/*.lua
          269  +~~~
          270  +
          271  +the cortav binary this produces will have all the extra capabilities you personally need, without any need to fork [$cortav.lua] itself or even touch the repository.
          272  +
          273  +there's no reason [$cortav.lua] shouldn't be able to load extensions at runtime as well; i just haven't implemented this behavior yet. it probably would only take a few extra lines of code tho.
          274  +
          275  +i will eventually document the extension API, but for now, look at [$ext/toc.lua] for a simple example of how to register an extension.
          276  +
   254    277   ## command line driver
   255    278   the [$cortav.lua] command line driver can be run from the repository directory with the command [$lua ./cli.lua], or by first compiling it into a bytecode form that links in all its dependencies. this is the preferred method for installation, as it produces a self-contained executable which loads more quickly, but running the driver in script form may be desirable for development or debugging.
   256    279   
   257    280   the repository contains a GNU makefile to automate compilation of the reference implementation on unix-like OSes. simply run [$$ make cortav] or [$$ gmake cortav] from the repository root to produce a self-contained bytecode executable that can be installed anywhere on your filesystem, with no dependencies other than the lua interpreter.
   258    281   
   259    282   ! note that the makefile strips debugging symbols to save space, so running [$cli.lua] directly as a script may be helpful if you encounter errors and need stacktraces or other debugging information.
   260    283   
................................................................................
   270    293   $ cortav readme.ct
   271    294   	# reads from readme.ct, writes to standard output
   272    295   ~~~
   273    296   
   274    297   ### switches
   275    298   [$cortav.lua] offers various switches to control its behavior.
   276    299   + long                      + short + function                                    +
   277         -| [$--out [!file]]         :|:[$-o]:| sets the output file (default stdout)       |
   278         -| [$--log [!file]]         :|:[$-l]:| sets the log file (default stderr)          |
   279         -| [$--define [!var] [!val]]:|:[$-d]:| sets the context variable [$var] to [$val]  |
   280         -| [$--mode-set [!mode]]    :|:[$-y]:| activates the [>refimpl-mode mode] with ID [!mode]
   281         -| [$--mode-clear [!mode]]  :|:[$-n]:| disables the mode with ID [!mode]           |
   282         -| [$--mode [!id] [!val]]   :|:[$-m]:| configures mode [!id] with the value [!val] |
   283         -| [$--help]                :|:[$-h]:| display online help                         |
   284         -| [$--version]             :|:[$-V]:| display the interpreter version             |
          300  +| [$--out [!file]]              :|:[$-o]:| sets the output file (default stdout)       |
          301  +| [$--log [!file]]              :|:[$-l]:| sets the log file (default stderr)          |
          302  +| [$--define [!var] [!val]]     :|:[$-d]:| sets the context variable [$var] to [$val]  |
          303  +| [$--mode-set [!mode]]         :|:[$-y]:| activates the [>refimpl-mode mode] with ID [!mode]
          304  +| [$--mode-clear [!mode]]       :|:[$-n]:| disables the mode with ID [!mode]           |
          305  +| [$--mode [!id] [!val]]        :|:[$-m]:| configures mode [!id] with the value [!val] |
          306  +| [$--mode-set-weak [!mode]]    :|:[$-Y]:| activates the [>refimpl-mode mode] with ID [!mode] if the source file does not specify otherwise
          307  +| [$--mode-clear-weak [!mode]]  :|:[$-N]:| disables the mode with ID [!mode] if the source file does not specify otherwise
          308  +| [$--mode-weak [!id] [!val]]   :|:[$-M]:| configures mode [!id] with the value [!val] if the source file does not specify otherwise
          309  +| [$--help]                     :|:[$-h]:| display online help                         |
          310  +| [$--version]                  :|:[$-V]:| display the interpreter version             |
   285    311   
   286    312   ###refimpl-mode modes
   287    313   most of [$cortav.lua]'s implementation-specific behavior is controlled by use of [!modes]. these are namespaced options which may have a boolean, string, or numeric value. boolean modes are set with the [$-y] [$-n] flags; other modes use the [$-m] flags.
   288    314   
   289    315   most modes are defined by the renderer backend. the following modes affect the behavior of the frontend:
   290    316   
   291    317   + ID                 + type   + effect
................................................................................
   315    341   	-m render:format html \
   316    342   	-m html:width 40em \
   317    343   	-m html:accent 80 \
   318    344   	-m html:hue-spread 35 \
   319    345   	-y html:dark-on-light # could also be written as:
   320    346   $ cortav readme.ct -ommmmy readme.html render:format html html:width 40em html:accent 80 html:hue-spread 35 html:dark-on-light
   321    347   ~~~
          348  +
          349  +## further directions
          350  +
          351  +### LCH support
          352  +right now, the use of color in the HTML renderer is very unsatisfactory. the accent mechanism operates on the basis of the CSS HSL function, which is not perceptually uniform; different hues will present different mixes of brightness and some (yellows?) may be ugly or unreadable.
          353  +
          354  +the ideal solution would be to simply switch to using LCH based colors. unfortunately, only Safari actually supports the LCH color function right now, and it's unlikely (unless Lea Verou and her husband manage to work a miracle) that Colors Level 4 is going to be implemented very widely any time soon.
          355  +
          356  +this leaves us in an awkward position. we can of course do the math ourselves, working in LCH to implement the internal [$@tone] macro, and then "converting" these colors to HSL. unfortunately, you can't actually convert from LCH to HSL; it's like converting from pounds to kilograms. LCH can represent any color the human visual system can perceive; sRGB can't, and CSS HSL is implemented in sRGB. however, we could at least approximate something that would allow for perceptually uniform brightness, which would be an improvement, and this is probably the direction to go in, unless a miracle occurs and [$lch()] or [$color()] pop up in Blink.
          357  +
          358  +it may be possible to do a more reasonable job of handling colors in the postscript and TeX outputs. unsure about SVG but i assume it suffers the same problems HTML/CSS do. does groff even support color??

Modified cortav.lua from [19e0c83304] to [cf364bf145].

   209    209   		fortran = { color = 0xff779a };
   210    210   		python = { color = 0xffd277 };
   211    211   		python = { color = 0xcdd6ff };
   212    212   	}
   213    213   
   214    214   	local stylesets = {
   215    215   		header = [[
   216         -			h1 { font-size: 200%; border-bottom-style: double !important; border-bottom-width: 3px !important; }
   217         -			h2 { font-size: 130%; }
   218         -			h3 { font-size: 110%; }
   219         -			h4 { font-size: 100%; font-weight: normal; }
          216  +			h1,h2,h3,h4,h5,h6 { border-bottom: 1px solid @tone(0.7); }
          217  +			h1 { font-size: 200%; border-bottom-style: double !important; border-bottom-width: 3px !important; margin: 0em -1em; }
          218  +			h2 { font-size: 130%; margin: 0em -0.7em; }
          219  +			h3 { font-size: 110%; margin: 0em -0.5em; }
          220  +			h4 { font-size: 100%; font-weight: normal; margin: 0em -0.2em; }
   220    221   			h5 { font-size: 90%; font-weight: normal; }
   221    222   			h6 { font-size: 80%; font-weight: normal; }
   222    223   			h3, h4, h5, h6 { border-bottom-style: dotted !important; }
          224  +			h1,h2,h3,h4,h5,h6 { 
          225  +				margin-top: 0;
          226  +				margin-bottom: 0;
          227  +			}
          228  +			:is(h1,h2,h3,h4,h5,h6) + p {
          229  +				margin-top: 0.4em;
          230  +			}
          231  +
          232  +		]];
          233  +		headingAnchors = [[
          234  +			:is(h1,h2,h3,h4,h5,h6) > a[href].anchor {
          235  +				text-decoration: none;
          236  +				font-size: 1.2em;
          237  +				padding: 0.3em;
          238  +				opacity: 0%;
          239  +				transition: 0.3s;
          240  +				font-weight: 100;
          241  +			}
          242  +			:is(h1,h2,h3,h4,h5,h6):hover > a[href].anchor {
          243  +				opacity: 50%;
          244  +			}
          245  +			:is(h1,h2,h3,h4,h5,h6) > a[href].anchor:hover {
          246  +				opacity: 100%;
          247  +			}
          248  +
          249  +			]] .. -- this is necessary to avoid the sections jumping around
          250  +			      -- when focus changes from one to another
          251  +			[[ section {
          252  +				border: 1px solid transparent;
          253  +			}
          254  +
          255  +			section:target {
          256  +				margin-left: -2em;
          257  +				margin-right: -2em;
          258  +				padding: 0 2em;
          259  +				background: @tone(0.04);
          260  +				border: 1px dotted @tone(0.3);
          261  +			}
          262  +
          263  +			section:target > :is(h1,h2,h3,h4,h5,h6) {
          264  +
          265  +			}
          266  +		]];
          267  +		paragraph = [[
          268  +			p {
          269  +				margin: 0.7em 0;
          270  +			}
          271  +			section {
          272  +				margin: 1.2em 0;
          273  +			}
          274  +			section:first-child { margin-top: 0; }
   223    275   		]];
   224    276   		accent = [[
   225    277   			body { background: @bg; color: @fg }
   226    278   			a[href] {
   227    279   				color: @tone(0.7 30);
   228    280   				text-decoration-color: @tone/0.4(0.7 30);
   229    281   			}
   230    282   			a[href]:hover {
   231    283   				color: @tone(0.9 30);
   232    284   				text-decoration-color: @tone/0.7(0.7 30);
   233    285   			}
   234         -			h1,h2,h3,h4,h5,h6 { border-bottom: 1px solid @tone(0.7); }
   235    286   			h1 { color: @tone(2); }
   236    287   			h2 { color: @tone(1.5); }
   237    288   			h3 { color: @tone(1.2); }
   238    289   			h4 { color: @tone(1); }
   239    290   			h5,h6 { color: @tone(0.8); }
   240    291   		]];
   241    292   		code = [[
................................................................................
   442    493   				end
   443    494   			end
   444    495   			return lst
   445    496   		end
   446    497   
   447    498   		local block_renderers = {
   448    499   			paragraph = function(b,s)
          500  +				stylesNeeded.paragraph = true;
   449    501   				return tag('p', nil, sr.htmlSpan(b.spans, b, s), b)
   450    502   			end;
   451    503   			directive = function(b,s)
   452    504   				-- deal with renderer directives
   453    505   				local _, cmd, args = b.words(2)
   454    506   				if cmd == 'page-title' then
   455    507   					if not opts.title then doctitle = args end
................................................................................
   544    596   		local irs
   545    597   		if sec.kind == 'ordinary' then
   546    598   			if #(sec.blocks) > 0 then
   547    599   				irs = {tag='section',attrs={id = getSafeID(sec)},nodes={}}
   548    600   
   549    601   				for i, block in ipairs(sec.blocks) do
   550    602   					local rd = irBlockRdrs[block.kind](block,sec)
   551         -					if rd then table.insert(irs.nodes, rd) end
          603  +					if rd then
          604  +						if opts['heading-anchors'] and block == sec.heading_node then
          605  +							stylesNeeded.headingAnchors = true
          606  +							table.insert(rd.nodes, ' ')
          607  +							table.insert(rd.nodes, {
          608  +								tag = 'a';
          609  +								attrs = {href = '#' .. irs.attrs.id, class='anchor'};
          610  +								nodes = {type(opts['heading-anchors'])=='string' and opts['heading-anchors'] or '&sect;'};
          611  +							})
          612  +						end
          613  +						table.insert(irs.nodes, rd)
          614  +					end
   552    615   				end
   553    616   			end
   554    617   		elseif sec.kind == 'blockquote' then
   555    618   		elseif sec.kind == 'listing' then
   556    619   		elseif sec.kind == 'embed' then
   557    620   		end
   558    621   		if irs then table.insert(ir, irs) end

Modified makefile from [5cfd42ea5f] to [5a68812ac4].

     1      1   lua != which lua
     2      2   luac != which luac
     3      3   
     4         -extens ?= $(patsubst ext/%.lua,%,$(wildcard ext/*.lua))
     5         -extens_srcs = $(patsubst %,ext/%.lua,$(extens))
            4  +extens = $(wildcard ext/*.lua)
            5  +extens_names ?= $(basename $(notdir $(extens)))
     6      6   
     7         -cortav: sirsem.lua cortav.lua $(extens_srcs) cli.lua
            7  +cortav: sirsem.lua cortav.lua $(extens) cli.lua
            8  +	@echo ' » building with extensions $(extens_names)'
     8      9   	echo '#!$(lua)' > $@
     9     10   	luac -o - $^ >> $@
    10     11   	chmod +x $@
    11     12   
    12     13   cortav.html: cortav.ct cortav
    13     14   	./cortav $< -o $@ -m render:format html -y html:fossil-uv
    14     15   
    15     16   .PHONY: syncdoc
    16     17   syncdoc: cortav.html
    17     18   	fossil uv add $<
    18     19   	fossil uv sync