| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | /********************************************************************** | 
					
						
							|  |  |  | *  | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | **********************************************************************/ | 
					
						
							|  |  |  | ((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define) | 
					
						
							|  |  |  | (function(require){ var module={} // make module AMD/node compatible...
 | 
					
						
							|  |  |  | /*********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var types = require('ig-types') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // Parser...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // XXX should we warn about stuff like <macro src=/moo/> -- currently 
 | 
					
						
							|  |  |  | // 		this will simply be ignored, i.e. passed trough the parser 
 | 
					
						
							|  |  |  | // 		without change...
 | 
					
						
							|  |  |  | // XXX might be a good idea to both think of a good async parse and
 | 
					
						
							|  |  |  | // 		create tools for sync parsing (get links etc.)...
 | 
					
						
							| 
									
										
										
										
											2023-02-26 23:47:23 +03:00
										 |  |  | // XXX need to correctly handle nested and escaped quotes...
 | 
					
						
							|  |  |  | // 		i.e.
 | 
					
						
							|  |  |  | // 			"aaa \"bbb \\"ccc\\" bbb\" aaa"
 | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | var BaseParser = | 
					
						
							|  |  |  | module.BaseParser = { | 
					
						
							|  |  |  | 	// patterns...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// The way the patterns are organized might seem a bit overcomplicated
 | 
					
						
							|  |  |  | 	// and it has to be to be able to reuse the same pattern in different 
 | 
					
						
							|  |  |  | 	// contexts, e.g. the arguments pattern...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// needs:
 | 
					
						
							|  |  |  | 	// 	STOP -- '\\>' or ')'
 | 
					
						
							|  |  |  | 	// 	PREFIX -- 'inline' or 'elem'
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	MACRO_ARGS: ['(\\s*(',[ | 
					
						
							|  |  |  | 				// arg='val' | arg="val" | arg=val
 | 
					
						
							| 
									
										
										
										
											2022-11-05 01:49:39 +03:00
										 |  |  | 				'(?<PREFIXArgName>[a-z:-_]+)\\s*=\\s*(?<PREFIXArgValue>'+([ | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 					// XXX CHROME/NODE BUG: this does not work yet...
 | 
					
						
							|  |  |  | 					//'\\s+(?<quote>[\'"])[^\\k<quote>]*\\k<quote>',
 | 
					
						
							| 
									
										
										
										
											2022-08-31 18:49:59 +03:00
										 |  |  | 					'"(?<PREFIXDoubleQuotedValue>(\\"|[^"])*?)"', | 
					
						
							|  |  |  | 					"'(?<PREFIXSingleQuotedValue>(\\'|[^'])*?)'", | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 					'(?<PREFIXValue>[^\\sSTOP\'"]+)', | 
					
						
							|  |  |  | 				].join('|'))+')', | 
					
						
							|  |  |  | 				// "arg" | 'arg'
 | 
					
						
							|  |  |  | 				// XXX CHROME/NODE BUG: this does not work yet...
 | 
					
						
							|  |  |  | 				//'\\s+(?<quote>[\'"])[^\\k<quote>]*\\k<quote>',
 | 
					
						
							| 
									
										
										
										
											2022-08-31 18:49:59 +03:00
										 |  |  | 				'"(?<PREFIXDoubleQuotedArg>(\\"|[^"])*?)"', | 
					
						
							|  |  |  | 				"'(?<PREFIXSingleQuotedArg>(\\'|[^'])*?)'", | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 				// arg
 | 
					
						
							| 
									
										
										
										
											2022-08-15 17:32:07 +03:00
										 |  |  | 				// NOTE: this is last because it could eat up parts of 
 | 
					
						
							|  |  |  | 				// 		the above alternatives...
 | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 				//'|\\s+[^\\s\\/>\'"]+',
 | 
					
						
							|  |  |  | 				'(?<PREFIXArg>[^\\sSTOP\'"]+)', | 
					
						
							|  |  |  | 			].join('|'), | 
					
						
							|  |  |  | 		'))'].join(''), | 
					
						
							|  |  |  | 	MACRO_ARGS_PATTERN: undefined, | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	.buildArgsPattern(<prefix>[, <stop>[, <flags>]])
 | 
					
						
							|  |  |  | 	// 		-> <pattern>
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	.buildArgsPattern(<prefix>[, <stop>[, false]])
 | 
					
						
							|  |  |  | 	// 		-> <string>
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	buildArgsPattern: function(prefix='elem', stop='', regexp='smig'){ | 
					
						
							|  |  |  | 		var pattern = this.MACRO_ARGS | 
					
						
							|  |  |  | 			.replace(/PREFIX/g, prefix) | 
					
						
							|  |  |  | 			.replace(/STOP/g, stop) | 
					
						
							|  |  |  | 		return regexp ? | 
					
						
							|  |  |  | 			new RegExp(pattern, regexp)  | 
					
						
							|  |  |  | 			: pattern }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// needs:
 | 
					
						
							|  |  |  | 	// 	MACROS
 | 
					
						
							| 
									
										
										
										
											2022-08-31 18:49:59 +03:00
										 |  |  | 	// 	INLINE_ARGS
 | 
					
						
							|  |  |  | 	// 	UNNAMED_ARGS
 | 
					
						
							|  |  |  | 	// 	ARGS
 | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	MACRO: '('+([ | 
					
						
							|  |  |  | 			// @macro(arg ..)
 | 
					
						
							|  |  |  | 			'\\\\?@(?<nameInline>MACROS)\\((?<argsInline>INLINE_ARGS)\\)', | 
					
						
							| 
									
										
										
										
											2022-08-31 18:49:59 +03:00
										 |  |  | 			// @(arg ..)
 | 
					
						
							|  |  |  | 			'\\\\?@\\((?<argsUnnamed>UNNAMED_ARGS)\\)', | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 			// <macro ..> | <macro ../>
 | 
					
						
							| 
									
										
										
										
											2022-09-08 01:56:55 +03:00
										 |  |  | 			'<\\s*(?<nameOpen>MACROS)(?<argsOpen>\\sARGS)?\\s*/?>', | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 			// </macro>
 | 
					
						
							|  |  |  | 			'</\\s*(?<nameClose>MACROS)\\s*>', | 
					
						
							|  |  |  | 		].join('|'))+')', | 
					
						
							|  |  |  | 	MACRO_PATTERN: undefined, | 
					
						
							|  |  |  | 	MACRO_PATTERN_GROUPS: undefined, | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	.buildMacroPattern(<macros>[, <flags>])
 | 
					
						
							|  |  |  | 	// 		-> <pattern>
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	.buildMacroPattern(<macros>[, false])
 | 
					
						
							|  |  |  | 	// 		-> <string>
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	buildMacroPattern: function(macros=['MACROS'], regexp='smig'){ | 
					
						
							|  |  |  | 		var pattern = this.MACRO | 
					
						
							| 
									
										
										
										
											2022-08-31 18:49:59 +03:00
										 |  |  | 			.replace(/MACROS/g,  | 
					
						
							|  |  |  | 				macros | 
					
						
							|  |  |  | 					.filter(function(m){  | 
					
						
							|  |  |  | 						return m.length > 0 }) | 
					
						
							|  |  |  | 					.join('|')) | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 			.replace(/INLINE_ARGS/g, | 
					
						
							|  |  |  | 				this.buildArgsPattern('inline', ')', false) +'*') | 
					
						
							| 
									
										
										
										
											2022-08-31 18:49:59 +03:00
										 |  |  | 			.replace(/UNNAMED_ARGS/g, | 
					
						
							|  |  |  | 				this.buildArgsPattern('unnamed', ')', false) +'*') | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 			.replace(/ARGS/g,  | 
					
						
							|  |  |  | 				this.buildArgsPattern('elem', '\\/>', false) +'*') | 
					
						
							|  |  |  | 		return regexp ? | 
					
						
							|  |  |  | 			new RegExp(pattern, regexp)  | 
					
						
							|  |  |  | 			: pattern }, | 
					
						
							|  |  |  | 	countMacroPatternGroups: function(){ | 
					
						
							|  |  |  | 		// NOTE: the -2 here is to compensate for the leading and trailing ""'s...
 | 
					
						
							|  |  |  | 		return '<MACROS>'.split(this.buildMacroPattern()).length - 2 }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// XXX should this be closer to .stripComments(..)
 | 
					
						
							|  |  |  | 	// XXX do we need basic inline and block commets a-la lisp???
 | 
					
						
							|  |  |  | 	COMMENT_PATTERN: RegExp('('+[ | 
					
						
							|  |  |  | 			// <!--[pwiki[ .. ]]-->
 | 
					
						
							| 
									
										
										
										
											2022-08-14 03:10:24 +03:00
										 |  |  | 			'<!--\\[pwiki\\[(?<uncomment>.*?)\\]\\]-->', | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// <pwiki-comment> .. </pwiki-comment>
 | 
					
						
							| 
									
										
										
										
											2022-08-14 03:10:24 +03:00
										 |  |  | 			'<\\s*pwiki-comment[^>]*>.*?<\\/\\s*pwiki-comment\\s*>', | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 			// <pwiki-comment .. />
 | 
					
						
							|  |  |  | 			'<\\s*pwiki-comment[^\\/>]*\\/>', | 
					
						
							| 
									
										
										
										
											2022-08-29 03:42:34 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// html comments...
 | 
					
						
							|  |  |  | 			'<!--.*?-->', | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 		].join('|') +')', 'smig'), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// helpers...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	normalizeFilters: function(filters){ | 
					
						
							|  |  |  | 		var skip = new Set() | 
					
						
							|  |  |  | 		return filters | 
					
						
							|  |  |  | 			.flat() | 
					
						
							|  |  |  | 			.tailUnique() | 
					
						
							|  |  |  | 			.filter(function(filter){ | 
					
						
							|  |  |  | 				filter[0] == '-' | 
					
						
							|  |  |  | 					&& skip.add(filter.slice(1)) | 
					
						
							|  |  |  | 				return filter[0] != '-' })  | 
					
						
							|  |  |  | 			.filter(function(filter){ | 
					
						
							|  |  |  | 				return !skip.has(filter) })}, | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// Spec format:
 | 
					
						
							|  |  |  | 	// 	[<orderd>, ... [<keyword>, ...]]
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-10-30 16:46:31 +03:00
										 |  |  | 	// Keyword arguments if given without a value are true by default, 
 | 
					
						
							|  |  |  | 	// explicitly setting a keyword argument to 'true' or 'yes' will set 
 | 
					
						
							|  |  |  | 	// it to true, explicitly setting to 'false' or 'no' will set it to 
 | 
					
						
							|  |  |  | 	// false, any other value will be set as-is...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 	// NOTE: the input to this is formatted by .lex(..)
 | 
					
						
							|  |  |  | 	// NOTE: arg pre-parsing is dome by .lex(..) but at that stage we do not
 | 
					
						
							|  |  |  | 	// 		yet touch the actual macros (we need them to get the .arg_spec)
 | 
					
						
							|  |  |  | 	// 		so the actual parsing is done in .expand(..)
 | 
					
						
							| 
									
										
										
										
											2022-08-13 17:02:32 +03:00
										 |  |  | 	parseArgs: function(spec, args){ | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 		// spec...
 | 
					
						
							|  |  |  | 		var order = spec.slice() | 
					
						
							|  |  |  | 		var bools = new Set( | 
					
						
							|  |  |  | 			order[order.length-1] instanceof Array ? | 
					
						
							|  |  |  | 				order.pop() | 
					
						
							|  |  |  | 				: []) | 
					
						
							|  |  |  | 		order = order | 
					
						
							|  |  |  | 			.filter(function(k){ | 
					
						
							|  |  |  | 				return !(k in args) }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var res = {} | 
					
						
							|  |  |  | 		var pos = Object.entries(args) | 
					
						
							|  |  |  | 			// stage 1: populate res with explicit data and place the rest in pos...
 | 
					
						
							|  |  |  | 			.reduce(function(pos, [key, value]){ | 
					
						
							|  |  |  | 				;/^[0-9]+$/.test(key) ? | 
					
						
							|  |  |  | 					(bools.has(value) ? | 
					
						
							|  |  |  | 						// bool...
 | 
					
						
							|  |  |  | 						(res[value] = true) | 
					
						
							|  |  |  | 						// positional...
 | 
					
						
							|  |  |  | 						: (pos[key*1] = value)) | 
					
						
							| 
									
										
										
										
											2022-10-30 16:46:31 +03:00
										 |  |  | 					// keyword/bool default values...
 | 
					
						
							|  |  |  | 					: bools.has(key) ? | 
					
						
							|  |  |  | 						(res[key] =  | 
					
						
							|  |  |  | 							// value escaping...
 | 
					
						
							|  |  |  | 							value[0] == '\\' ? | 
					
						
							|  |  |  | 								value.slice(1) | 
					
						
							|  |  |  | 							: (value == 'true' || value == 'yes') ? | 
					
						
							|  |  |  | 								true | 
					
						
							|  |  |  | 							: (value == 'false' || value == 'no') ? | 
					
						
							|  |  |  | 								false | 
					
						
							|  |  |  | 							: value) | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 					// keyword...
 | 
					
						
							|  |  |  | 					: (res[key] = value) | 
					
						
							|  |  |  | 				return pos }, []) | 
					
						
							|  |  |  | 			// stage 2: populate implicit values from pos...
 | 
					
						
							|  |  |  | 			.forEach(function(e, i){ | 
					
						
							|  |  |  | 				order.length == 0 ? | 
					
						
							|  |  |  | 					(res[e] = true) | 
					
						
							|  |  |  | 					: (res[order.shift()] = e) }) | 
					
						
							|  |  |  | 		return res }, | 
					
						
							| 
									
										
										
										
											2022-08-13 17:02:32 +03:00
										 |  |  | 	// XXX should this be here or on page???
 | 
					
						
							|  |  |  | 	callMacro: function(page, name, args, body, state, ...rest){ | 
					
						
							| 
									
										
										
										
											2022-10-31 03:01:10 +03:00
										 |  |  | 		var macro = page.macros[name]  | 
					
						
							|  |  |  | 		return macro.call(page,  | 
					
						
							| 
									
										
										
										
											2022-08-13 17:02:32 +03:00
										 |  |  | 				this.parseArgs( | 
					
						
							| 
									
										
										
										
											2022-10-31 03:01:10 +03:00
										 |  |  | 					macro.arg_spec  | 
					
						
							| 
									
										
										
										
											2022-08-13 17:02:32 +03:00
										 |  |  | 						?? [],  | 
					
						
							|  |  |  | 					args), | 
					
						
							|  |  |  | 				body,  | 
					
						
							|  |  |  | 				state,  | 
					
						
							|  |  |  | 				...rest) }, | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Strip comments...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	stripComments: function(str){ | 
					
						
							|  |  |  | 		return str | 
					
						
							|  |  |  | 			.replace(this.COMMENT_PATTERN,  | 
					
						
							|  |  |  | 				function(...a){ | 
					
						
							|  |  |  | 					return a.pop().uncomment  | 
					
						
							|  |  |  | 						|| '' }) }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-30 14:26:35 +03:00
										 |  |  | 	// Lexically split the string (generator)...
 | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	<item> ::=
 | 
					
						
							|  |  |  | 	// 		<string>
 | 
					
						
							|  |  |  | 	// 		| {
 | 
					
						
							|  |  |  | 	// 			name: <string>,
 | 
					
						
							|  |  |  | 	// 			type: 'inline'
 | 
					
						
							|  |  |  | 	// 				| 'element'
 | 
					
						
							|  |  |  | 	// 				| 'opening'
 | 
					
						
							|  |  |  | 	// 				| 'closing',
 | 
					
						
							|  |  |  | 	// 			args: {
 | 
					
						
							|  |  |  | 	// 				<index>: <value>,
 | 
					
						
							|  |  |  | 	// 				<key>: <value>,
 | 
					
						
							|  |  |  | 	// 				...
 | 
					
						
							|  |  |  | 	// 			}
 | 
					
						
							|  |  |  | 	// 			match: <string>,
 | 
					
						
							|  |  |  | 	// 		}
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: this internally uses page.macros' keys to generate the 
 | 
					
						
							|  |  |  | 	// 		lexing pattern.
 | 
					
						
							|  |  |  | 	lex: function*(page, str){ | 
					
						
							| 
									
										
										
										
											2022-08-21 20:45:29 +03:00
										 |  |  | 		str = typeof(str) != 'string' ? | 
					
						
							|  |  |  | 			str+'' | 
					
						
							|  |  |  | 			: str | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 		// XXX we can't get .raw from the page without going async...
 | 
					
						
							|  |  |  | 		//str = str 
 | 
					
						
							|  |  |  | 		//	?? page.raw
 | 
					
						
							|  |  |  | 		// NOTE: we are doing a separate pass for comments to completely 
 | 
					
						
							|  |  |  | 		// 		decouple them from the base macro syntax, making them fully 
 | 
					
						
							|  |  |  | 		// 		transparent...
 | 
					
						
							|  |  |  | 		str = this.stripComments(str) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// XXX should this be cached???
 | 
					
						
							|  |  |  | 		var macro_pattern = this.MACRO_PATTERN  | 
					
						
							| 
									
										
										
										
											2022-11-20 02:14:50 +03:00
										 |  |  | 			?? this.buildMacroPattern(Object.deepKeys(page.macros)) | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 		var macro_pattern_groups = this.MACRO_PATTERN_GROUPS  | 
					
						
							|  |  |  | 			?? this.countMacroPatternGroups() | 
					
						
							|  |  |  | 		var macro_args_pattern = this.MACRO_ARGS_PATTERN  | 
					
						
							|  |  |  | 			?? this.buildArgsPattern() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var lst = str.split(macro_pattern) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var macro = false | 
					
						
							|  |  |  | 		while(lst.length > 0){ | 
					
						
							|  |  |  | 			if(macro){ | 
					
						
							|  |  |  | 				var match = lst.splice(0, macro_pattern_groups)[0] | 
					
						
							|  |  |  | 				// NOTE: we essentially are parsing the detected macro a 
 | 
					
						
							|  |  |  | 				// 		second time here, this gives us access to named groups
 | 
					
						
							|  |  |  | 				// 		avoiding maintaining match indexes with the .split(..) 
 | 
					
						
							|  |  |  | 				// 		output...
 | 
					
						
							|  |  |  | 				// XXX for some reason .match(..) here returns a list with a string...
 | 
					
						
							|  |  |  | 				var cur = [...match.matchAll(macro_pattern)][0].groups | 
					
						
							|  |  |  | 				// special case: escaped inline macro -> keep as text...
 | 
					
						
							|  |  |  | 				if(match.startsWith('\\@')){ | 
					
						
							|  |  |  | 					yield match | 
					
						
							|  |  |  | 					macro = false  | 
					
						
							|  |  |  | 					continue } | 
					
						
							|  |  |  | 				// args...
 | 
					
						
							|  |  |  | 				var args = {} | 
					
						
							|  |  |  | 				var i = -1 | 
					
						
							|  |  |  | 				for(var {groups}  | 
					
						
							| 
									
										
										
										
											2022-08-31 18:49:59 +03:00
										 |  |  | 						of (cur.argsInline  | 
					
						
							|  |  |  | 								?? cur.argsUnnamed | 
					
						
							|  |  |  | 								?? cur.argsOpen  | 
					
						
							|  |  |  | 								?? '') | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 							.matchAll(macro_args_pattern)){ | 
					
						
							|  |  |  | 					i++ | 
					
						
							|  |  |  | 					args[groups.elemArgName  | 
					
						
							|  |  |  | 							?? groups.inlineArgName  | 
					
						
							| 
									
										
										
										
											2022-08-31 18:49:59 +03:00
										 |  |  | 							?? groups.unnamedArgName  | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 							?? i] = | 
					
						
							| 
									
										
										
										
											2022-08-31 18:49:59 +03:00
										 |  |  | 						(groups.elemSingleQuotedValue  | 
					
						
							|  |  |  | 								?? groups.inlineSingleQuotedValue | 
					
						
							|  |  |  | 								?? groups.unnamedSingleQuotedValue | 
					
						
							|  |  |  | 								?? groups.elemDoubleQuotedValue | 
					
						
							|  |  |  | 								?? groups.inlineDoubleQuotedValue | 
					
						
							|  |  |  | 								?? groups.unnamedDoubleQuotedValue | 
					
						
							|  |  |  | 								?? groups.elemValue | 
					
						
							|  |  |  | 								?? groups.inlineValue | 
					
						
							|  |  |  | 								?? groups.unnamedValue | 
					
						
							|  |  |  | 								?? groups.elemSingleQuotedArg | 
					
						
							|  |  |  | 								?? groups.inlineSingleQuotedArg | 
					
						
							|  |  |  | 								?? groups.unnamedSingleQuotedArg | 
					
						
							|  |  |  | 								?? groups.elemDoubleQuotedArg | 
					
						
							|  |  |  | 								?? groups.inlineDoubleQuotedArg | 
					
						
							|  |  |  | 								?? groups.unnamedDoubleQuotedArg | 
					
						
							|  |  |  | 								?? groups.elemArg | 
					
						
							|  |  |  | 								?? groups.inlineArg | 
					
						
							|  |  |  | 								?? groups.unnamedArg) | 
					
						
							|  |  |  | 							.replace(/\\(["'])/g, '$1') } | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				// macro-spec...
 | 
					
						
							|  |  |  | 				yield { | 
					
						
							|  |  |  | 					name: (cur.nameInline  | 
					
						
							|  |  |  | 							?? cur.nameOpen  | 
					
						
							| 
									
										
										
										
											2022-08-31 18:49:59 +03:00
										 |  |  | 							?? cur.nameClose | 
					
						
							|  |  |  | 							?? '') | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 						.toLowerCase(), | 
					
						
							|  |  |  | 					type: match[0] == '@' ? | 
					
						
							|  |  |  | 							'inline' | 
					
						
							|  |  |  | 						: match[1] == '/' ? | 
					
						
							|  |  |  | 							'closing' | 
					
						
							|  |  |  | 						: match[match.length-2] == '/' ? | 
					
						
							|  |  |  | 							'element' | 
					
						
							|  |  |  | 						: 'opening', | 
					
						
							|  |  |  | 					args,  | 
					
						
							|  |  |  | 					match, | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				macro = false | 
					
						
							|  |  |  | 			// normal text...
 | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				var str = lst.shift() | 
					
						
							|  |  |  | 				// skip empty strings from output...
 | 
					
						
							|  |  |  | 				if(str != ''){ | 
					
						
							|  |  |  | 					yield str } | 
					
						
							|  |  |  | 				macro = true } } }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-30 14:26:35 +03:00
										 |  |  | 	// Group block elements (generator)...
 | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	<item> ::=
 | 
					
						
							|  |  |  | 	// 		<string>
 | 
					
						
							|  |  |  | 	// 		| {
 | 
					
						
							|  |  |  | 	// 			type: 'inline'
 | 
					
						
							|  |  |  | 	// 				| 'element'
 | 
					
						
							|  |  |  | 	// 				| 'block',
 | 
					
						
							|  |  |  | 	// 			body: [
 | 
					
						
							|  |  |  | 	// 				<item>,
 | 
					
						
							|  |  |  | 	// 				...
 | 
					
						
							|  |  |  | 	// 			],
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//			// rest of items are the same as for lex(..)
 | 
					
						
							|  |  |  | 	// 			...
 | 
					
						
							|  |  |  | 	// 		}
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: this internaly uses page.macros to check for propper nesting
 | 
					
						
							|  |  |  | 	//group: function*(page, lex, to=false){
 | 
					
						
							|  |  |  | 	group: function*(page, lex, to=false, parent){ | 
					
						
							|  |  |  | 		// XXX we can't get .raw from the page without going async...
 | 
					
						
							|  |  |  | 		//lex = lex
 | 
					
						
							|  |  |  | 		//	?? this.lex(page) 
 | 
					
						
							| 
									
										
										
										
											2022-08-21 20:45:29 +03:00
										 |  |  | 		lex = typeof(lex) != 'object' ? | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 			this.lex(page, lex) | 
					
						
							|  |  |  | 			: lex | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var quoting = to  | 
					
						
							|  |  |  | 			&& (page.QUOTING_MACROS ?? []).includes(to) | 
					
						
							|  |  |  | 			&& [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// NOTE: we are not using for .. of .. here as it depletes the 
 | 
					
						
							|  |  |  | 		// 		generator even if the end is not reached...
 | 
					
						
							|  |  |  | 		while(true){ | 
					
						
							|  |  |  | 			var {value, done} = lex.next() | 
					
						
							|  |  |  | 			// check if unclosed blocks remaining...
 | 
					
						
							|  |  |  | 			if(done){ | 
					
						
							|  |  |  | 				if(to){ | 
					
						
							|  |  |  | 					throw new Error( | 
					
						
							| 
									
										
										
										
											2022-11-19 04:26:13 +03:00
										 |  |  | 						'Premature end of input: Expected </'+ to +'>') } | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 				return } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// special case: quoting -> collect text...
 | 
					
						
							|  |  |  | 			// NOTE: we do not care about nesting here...
 | 
					
						
							|  |  |  | 			if(quoting !== false){ | 
					
						
							|  |  |  | 				if(value.name == to  | 
					
						
							|  |  |  | 						&& value.type == 'closing'){ | 
					
						
							|  |  |  | 					yield quoting.join('') | 
					
						
							|  |  |  | 					return | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					quoting.push( | 
					
						
							|  |  |  | 						typeof(value) == 'string' ? | 
					
						
							|  |  |  | 							value | 
					
						
							|  |  |  | 							: value.match ) } | 
					
						
							|  |  |  | 				continue } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// assert nesting rules...
 | 
					
						
							|  |  |  | 			// NOTE: we only check for direct nesting...
 | 
					
						
							|  |  |  | 			// XXX might be a good idea to link nested block to the parent...
 | 
					
						
							|  |  |  | 			if(page.macros[value.name] instanceof Array | 
					
						
							|  |  |  | 					&& !page.macros[value.name].includes(to) | 
					
						
							|  |  |  | 					// do not complain about closing nestable tags...
 | 
					
						
							|  |  |  | 					&& !(value.name == to  | 
					
						
							|  |  |  | 						&& value.type == 'closing')){ | 
					
						
							|  |  |  | 				throw new Error( | 
					
						
							| 
									
										
										
										
											2022-11-19 04:26:13 +03:00
										 |  |  | 					'Unexpected <'+ value.name +'> macro'  | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 						+(to ?  | 
					
						
							| 
									
										
										
										
											2022-11-19 04:26:13 +03:00
										 |  |  | 							' in <'+to+'>'  | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 							: '')) } | 
					
						
							|  |  |  | 			// open block...
 | 
					
						
							|  |  |  | 			if(value.type == 'opening'){ | 
					
						
							|  |  |  | 				//value.body = [...this.group(page, lex, value.name)]
 | 
					
						
							|  |  |  | 				value.body = [...this.group(page, lex, value.name, value)] | 
					
						
							|  |  |  | 				value.type = 'block' | 
					
						
							|  |  |  | 			// close block...
 | 
					
						
							|  |  |  | 			} else if(value.type == 'closing'){ | 
					
						
							|  |  |  | 				if(value.name != to){ | 
					
						
							| 
									
										
										
										
											2022-11-19 04:26:13 +03:00
										 |  |  | 					throw new Error('Unexpected </'+ value.name +'>') } | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 				// NOTE: we are intentionally not yielding the value here...
 | 
					
						
							|  |  |  | 				return }  | 
					
						
							|  |  |  | 			// normal value...
 | 
					
						
							|  |  |  | 			yield value } },  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Expand macros...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-08-17 23:38:24 +03:00
										 |  |  | 	//	<ast> ::= [ <item>, .. ]
 | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 	// 	<item> ::=
 | 
					
						
							| 
									
										
										
										
											2022-08-17 23:38:24 +03:00
										 |  |  | 	// 		<func>
 | 
					
						
							|  |  |  | 	// 		| <promise>
 | 
					
						
							|  |  |  | 	// 		| <string>
 | 
					
						
							| 
									
										
										
										
											2022-08-18 01:23:49 +03:00
										 |  |  | 	// 		| { skip: true, ... }
 | 
					
						
							| 
									
										
										
										
											2022-08-17 23:38:24 +03:00
										 |  |  | 	// 		| { data: <ast> }
 | 
					
						
							|  |  |  | 	// 		| <ast>
 | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-08-14 02:08:27 +03:00
										 |  |  | 	// XXX macros: we are mixing up ast state and parse state...
 | 
					
						
							|  |  |  | 	// 		one should only be used for parsing and be forgotten after 
 | 
					
						
							|  |  |  | 	// 		the ast is constructed the other should be part of the ast...
 | 
					
						
							| 
									
										
										
										
											2023-02-06 03:06:45 +03:00
										 |  |  | 	// XXX DOC inconsistent await behavior...
 | 
					
						
							| 
									
										
										
										
											2023-02-05 23:02:30 +03:00
										 |  |  | 	//		in var macro, as an example, there are two paths: the assign and 
 | 
					
						
							|  |  |  | 	//		the get, the assign calls .parse(..) and awaits for the result 
 | 
					
						
							|  |  |  | 	//		while the get path is "sync", this results in the first exec path
 | 
					
						
							|  |  |  | 	//		getting pushed to the next execution frame while the second is run
 | 
					
						
							|  |  |  | 	//		in sync with the caller, here is a simplified demo:
 | 
					
						
							|  |  |  | 	//			console.log(1)
 | 
					
						
							|  |  |  | 	//			// note that we are NOTE await'ing for the function here...
 | 
					
						
							|  |  |  | 	//			(async function f(){ 
 | 
					
						
							|  |  |  | 	//				console.log(2)})()
 | 
					
						
							|  |  |  | 	//			console.log(3)
 | 
					
						
							|  |  |  | 	//				-> prints 1, 2, 3
 | 
					
						
							|  |  |  | 	//		and:
 | 
					
						
							|  |  |  | 	//			console.log(1)
 | 
					
						
							|  |  |  | 	//			(async function f(){ 
 | 
					
						
							|  |  |  | 	//				// note the await -- this is the only difference...
 | 
					
						
							|  |  |  | 	//				console.log(await 2)})()
 | 
					
						
							|  |  |  | 	//			console.log(3)
 | 
					
						
							|  |  |  | 	//				-> prints 1, 3, 2
 | 
					
						
							|  |  |  | 	//		this could both be a bug or a feature depending on how you look
 | 
					
						
							|  |  |  | 	//		at it, but it makes promise sequencing very unpredictable...
 | 
					
						
							|  |  |  | 	//		...another problem here is that in the var macro, simply adding
 | 
					
						
							|  |  |  | 	//		an await of something does not fix the issue, we need to await 
 | 
					
						
							|  |  |  | 	//		for something significant -- await this.parse('') works, while
 | 
					
						
							|  |  |  | 	//		await vars[name] does not -- to the contrary of the above example...
 | 
					
						
							|  |  |  | 	expand: function(page, ast, state={}){ | 
					
						
							| 
									
										
										
										
											2022-12-06 23:40:57 +03:00
										 |  |  | 		var that = this | 
					
						
							| 
									
										
										
										
											2022-12-08 01:42:08 +03:00
										 |  |  | 		ast = ast == null ? | 
					
						
							|  |  |  | 				Promise.awaitOrRun( | 
					
						
							|  |  |  | 					page.raw, | 
					
						
							|  |  |  | 					function(raw){ | 
					
						
							|  |  |  | 						return that.group(page, raw ?? '') }) | 
					
						
							|  |  |  | 			: typeof(ast) != 'object' ? | 
					
						
							|  |  |  | 				this.group(page, ast) | 
					
						
							|  |  |  | 			: ast instanceof types.Generator ? | 
					
						
							|  |  |  | 				ast | 
					
						
							|  |  |  | 			: ast.iter() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-05 23:02:30 +03:00
										 |  |  | 		// NOTE this must execute sequentially for things that depend on 
 | 
					
						
							| 
									
										
										
										
											2022-12-12 22:46:11 +03:00
										 |  |  | 		// 		lexical scope not to get lost in the mess...
 | 
					
						
							| 
									
										
										
										
											2023-01-22 14:37:17 +03:00
										 |  |  | 		return Promise.seqiter(ast,  | 
					
						
							| 
									
										
										
										
											2023-02-05 23:02:30 +03:00
										 |  |  | 				function(value){ | 
					
						
							|  |  |  | 					// text block...
 | 
					
						
							|  |  |  | 					if(typeof(value) == 'string'){ | 
					
						
							|  |  |  | 						return value } | 
					
						
							|  |  |  | 					// macro...
 | 
					
						
							|  |  |  | 					var {name, args, body} = value | 
					
						
							|  |  |  | 					// nested macro -- skip...
 | 
					
						
							|  |  |  | 					if(typeof(page.macros[name]) != 'function'){ | 
					
						
							|  |  |  | 						return {...value, skip: true} } | 
					
						
							|  |  |  | 					// macro call...
 | 
					
						
							|  |  |  | 					return Promise.awaitOrRun( | 
					
						
							|  |  |  | 						that.callMacro(page, name, args, body, state), | 
					
						
							|  |  |  | 						function(res){ | 
					
						
							|  |  |  | 							res = res ?? '' | 
					
						
							|  |  |  | 							// result...
 | 
					
						
							|  |  |  | 							if(res instanceof Array  | 
					
						
							|  |  |  | 									|| page.macros[name] instanceof types.Generator){ | 
					
						
							|  |  |  | 								return res | 
					
						
							|  |  |  | 							} else { | 
					
						
							|  |  |  | 								return [res] } }) }, | 
					
						
							|  |  |  | 				function(err){ | 
					
						
							|  |  |  | 					console.error(err) | 
					
						
							| 
									
										
										
										
											2023-02-11 04:22:53 +03:00
										 |  |  | 					return [page.parse( | 
					
						
							| 
									
										
										
										
											2023-02-05 23:02:30 +03:00
										 |  |  | 						// XXX add line number and page path...
 | 
					
						
							|  |  |  | 						'@include("./ParseError' | 
					
						
							|  |  |  | 							+':path=' | 
					
						
							|  |  |  | 								// XXX use pwpath.encodeElem(..) ???
 | 
					
						
							|  |  |  | 								+ page.path  | 
					
						
							|  |  |  | 							+':msg=' | 
					
						
							|  |  |  | 								+ err.message  | 
					
						
							|  |  |  | 									// quote html stuff...
 | 
					
						
							|  |  |  | 									.replace(/&/g, '&') | 
					
						
							|  |  |  | 									.replace(/</g, '<') | 
					
						
							|  |  |  | 									.replace(/>/g, '>') | 
					
						
							|  |  |  | 									// quote argument syntax...
 | 
					
						
							|  |  |  | 									.replace(/["']/g, function(c){ | 
					
						
							|  |  |  | 										return '%'+ c.charCodeAt().toString(16) }) | 
					
						
							|  |  |  | 									.replace(/:/g, ':') | 
					
						
							|  |  |  | 									.replace(/=/g, '=') | 
					
						
							| 
									
										
										
										
											2023-02-11 04:22:53 +03:00
										 |  |  | 							+'")')] }) | 
					
						
							| 
									
										
										
										
											2023-02-05 23:02:30 +03:00
										 |  |  | 				.sync() }, | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-17 23:38:24 +03:00
										 |  |  | 	// recursively resolve and enumerate the ast...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	<ast> ::= [ <item>, .. ]
 | 
					
						
							|  |  |  | 	// 	<item> ::=
 | 
					
						
							|  |  |  | 	// 		<string>
 | 
					
						
							|  |  |  | 	// 		| { data: <ast> }
 | 
					
						
							| 
									
										
										
										
											2023-02-10 16:51:47 +03:00
										 |  |  | 	// 		| <func>
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	<func>(state)
 | 
					
						
							|  |  |  | 	// 		-> <ast>
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: <func>(..) is called in the context of page...
 | 
					
						
							| 
									
										
										
										
											2022-08-17 23:38:24 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX should this also resolve e.data???
 | 
					
						
							| 
									
										
										
										
											2023-02-10 16:26:00 +03:00
										 |  |  | 	resolve: function(page, ast, state={}){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							|  |  |  | 		ast = ast  | 
					
						
							|  |  |  | 			?? this.expand(page, null, state) | 
					
						
							|  |  |  | 		ast = typeof(ast) != 'object' ? | 
					
						
							|  |  |  | 			this.expand(page, ast, state) | 
					
						
							|  |  |  | 			: ast | 
					
						
							| 
									
										
										
										
											2023-02-17 00:09:07 +03:00
										 |  |  | 		// XXX .awaitOrRun(..) will check inside the input array for promises, do 
 | 
					
						
							| 
									
										
										
										
											2023-02-11 05:05:32 +03:00
										 |  |  | 		// 		we need to do this???
 | 
					
						
							|  |  |  | 		ast = Promise.awaitOrRun( | 
					
						
							|  |  |  | 			ast, | 
					
						
							|  |  |  | 			function(ast){ | 
					
						
							| 
									
										
										
										
											2023-02-17 00:09:07 +03:00
										 |  |  | 				var async_content = false | 
					
						
							| 
									
										
										
										
											2023-02-11 05:05:32 +03:00
										 |  |  | 				return ast | 
					
						
							|  |  |  | 					.map(function(e){ | 
					
						
							|  |  |  | 						// expand delayed sections...
 | 
					
						
							|  |  |  | 						e = typeof(e) == 'function' ? | 
					
						
							|  |  |  | 							e.call(page, state) | 
					
						
							|  |  |  | 							: e | 
					
						
							| 
									
										
										
										
											2023-02-17 00:09:07 +03:00
										 |  |  | 						// promise...
 | 
					
						
							|  |  |  | 						if(e instanceof Promise){ | 
					
						
							|  |  |  | 							async_content = true | 
					
						
							|  |  |  | 							return e | 
					
						
							| 
									
										
										
										
											2023-02-11 05:05:32 +03:00
										 |  |  | 						// expand arrays...
 | 
					
						
							| 
									
										
										
										
											2023-02-17 00:09:07 +03:00
										 |  |  | 						} else if(e instanceof Array  | 
					
						
							| 
									
										
										
										
											2023-02-15 14:15:50 +03:00
										 |  |  | 								|| e instanceof types.Generator){ | 
					
						
							| 
									
										
										
										
											2023-02-11 05:05:32 +03:00
										 |  |  | 							return that.resolve(page, e, state) | 
					
						
							|  |  |  | 						// data -- unwrap content...
 | 
					
						
							|  |  |  | 						} else if(e instanceof Object && 'data' in e){ | 
					
						
							| 
									
										
										
										
											2023-02-17 00:09:07 +03:00
										 |  |  | 							var res = Promise.awaitOrRun( | 
					
						
							| 
									
										
										
										
											2023-02-12 17:57:53 +03:00
										 |  |  | 								that.resolve(page, e.data, state), | 
					
						
							|  |  |  | 								function(e){ | 
					
						
							| 
									
										
										
										
											2023-02-11 05:05:32 +03:00
										 |  |  | 									return { data: e } }) | 
					
						
							| 
									
										
										
										
											2023-02-17 00:09:07 +03:00
										 |  |  | 							res instanceof Promise | 
					
						
							|  |  |  | 								&& (async_content = true) | 
					
						
							|  |  |  | 							return res | 
					
						
							| 
									
										
										
										
											2023-02-11 05:05:32 +03:00
										 |  |  | 						// skipped items...
 | 
					
						
							|  |  |  | 						} else if(e instanceof Object && e.skip){ | 
					
						
							|  |  |  | 							return [] | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							return [e] } }) | 
					
						
							| 
									
										
										
										
											2023-02-17 00:09:07 +03:00
										 |  |  | 					// NOTE: if we still have promises in the ast, wrap the 
 | 
					
						
							|  |  |  | 					// 		whole thing in a promise...
 | 
					
						
							|  |  |  | 					.run(function(){ | 
					
						
							|  |  |  | 						return async_content ? | 
					
						
							|  |  |  | 								Promise.iter(this) | 
					
						
							|  |  |  | 								: this }) | 
					
						
							| 
									
										
										
										
											2023-02-11 05:05:32 +03:00
										 |  |  | 					.flat() }) | 
					
						
							|  |  |  | 		return ast instanceof Promise ? | 
					
						
							|  |  |  | 			// keep the API consistently array-like...
 | 
					
						
							|  |  |  | 			ast.iter() | 
					
						
							|  |  |  | 			: ast }, | 
					
						
							| 
									
										
										
										
											2022-08-17 23:38:24 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 	// Fully parse a page...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// This runs in two stages:
 | 
					
						
							| 
									
										
										
										
											2022-08-17 23:38:24 +03:00
										 |  |  | 	// 	- resolve the page
 | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 	// 		- lex the page -- .lex(..)
 | 
					
						
							|  |  |  | 	// 		- group block elements -- .group(..)
 | 
					
						
							|  |  |  | 	// 		- expand macros -- .expand(..)
 | 
					
						
							| 
									
										
										
										
											2022-08-17 23:38:24 +03:00
										 |  |  | 	// 		- resolve ast -- .resolve(..)
 | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 	// 	- apply filters
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-08-06 01:37:34 +03:00
										 |  |  | 	// NOTE: this has to synchronize everything between stage 1 (up to 
 | 
					
						
							|  |  |  | 	// 		and including expand) and stage 2 (post-handlers, filters, ...)
 | 
					
						
							|  |  |  | 	// 		because the former need a fully loaded and expanded page if 
 | 
					
						
							|  |  |  | 	// 		we want to do this in 2 stages and not 3...
 | 
					
						
							|  |  |  | 	// 		XXX might be fun to try a load-and-tweak approach the first 
 | 
					
						
							|  |  |  | 	// 			version used -- i.e. setting placeholders and replacing 
 | 
					
						
							|  |  |  | 	// 			them on demand rather than on encounter (as is now), e.g.
 | 
					
						
							|  |  |  | 	// 			a slot when loaded will replace the prior occurrences...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | 	// XXX add a special filter to clear pending filters... (???)
 | 
					
						
							| 
									
										
										
										
											2023-02-11 05:05:32 +03:00
										 |  |  | 	parse: function(page, ast, state={}){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							|  |  |  | 		return this.resolve(page, ast, state) | 
					
						
							|  |  |  | 			// filters...
 | 
					
						
							|  |  |  | 			.map(function(section){ | 
					
						
							|  |  |  | 				// normalize types...
 | 
					
						
							|  |  |  | 				section =  | 
					
						
							|  |  |  | 					typeof(section) == 'number' ? | 
					
						
							|  |  |  | 						section + '' | 
					
						
							|  |  |  | 					: section == null ? | 
					
						
							|  |  |  | 						'' | 
					
						
							|  |  |  | 					: section | 
					
						
							|  |  |  | 				return ( | 
					
						
							|  |  |  | 					// expand section...
 | 
					
						
							|  |  |  | 					typeof(section) != 'string' ? | 
					
						
							|  |  |  | 						section.data | 
					
						
							|  |  |  | 					// global filters... 
 | 
					
						
							|  |  |  | 					: state.filters ? | 
					
						
							|  |  |  | 						that.normalizeFilters(state.filters) | 
					
						
							|  |  |  | 							.reduce(function(res, filter){ | 
					
						
							|  |  |  | 								// unknown filter...
 | 
					
						
							|  |  |  | 								// NOTE: we try not to break on user errors
 | 
					
						
							|  |  |  | 								// 		if we can help it...
 | 
					
						
							|  |  |  | 								if(page.filters[filter] == null){ | 
					
						
							|  |  |  | 									console.warn( | 
					
						
							|  |  |  | 										'.parse(..): unsupported filter: '+ filter)  | 
					
						
							|  |  |  | 									return res } | 
					
						
							|  |  |  | 								// NOTE: if a filter returns falsy then it 
 | 
					
						
							|  |  |  | 								// 		will have no effect on the result...
 | 
					
						
							|  |  |  | 								return page.filters[filter].call(page, res)  | 
					
						
							|  |  |  | 									?? res }, section) | 
					
						
							|  |  |  | 					// no global filters...
 | 
					
						
							|  |  |  | 					: section ) }) | 
					
						
							|  |  |  | 			.flat() | 
					
						
							|  |  |  | 			.join('') }, | 
					
						
							| 
									
										
										
										
											2022-08-04 11:00:21 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var parser = | 
					
						
							|  |  |  | module.parser = { | 
					
						
							|  |  |  | 	__proto__: BaseParser, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /********************************************************************** | 
					
						
							|  |  |  | * vim:set ts=4 sw=4 :                               */ return module }) |