Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2026-06-30 13:55:29 +03:00
parent e4dc2d0471
commit a68fce6b88
2 changed files with 136 additions and 56 deletions

View File

@ -600,7 +600,13 @@ module.BaseParser = {
elem.resolving = res elem.resolving = res
wait.add(res) wait.add(res)
state.waitAll = res state.waitAll = res
// XXX is .isolated implemented??? // XXX do we need to wait till the last .waitNested is
// resolved?
// ...should it's handlers complete??
// XXX can this be controlled by the macro???
// ...one way to do in is to ger pass a resolve(..)
// func to the macro...
// ...do we need this?
if(!res.isolated){ if(!res.isolated){
state.waitNested = res } state.waitNested = res }
res.then( res.then(
@ -720,40 +726,35 @@ module.BaseParser = {
callback) } }, callback) } },
// XXX // XXX
// XXX how should this play with filters??? _parse: function(page, ast, state={}){
// ...should filters be client-side only?? return Promise.awaitOrRun(
parseNested: function(page, ast, state={}){ this.resolve(page, ast, state),
var that = this function(ast){
ast = this.resolve(page, ast, state)
// XXX
if(state.waitNested){
return state.waitNested
.then(function(){
return that.parse(page, ast, state) }) }
// XXX // XXX
if(ast.length > 1){ if(ast.length > 1){
throw new Error('!!!!') throw new Error('!!!!')
} }
return ast[0] return ast[0]
?? '' }, ?? '' }) },
// XXX
// XXX how should this play with filters???
// ...should filters be client-side only??
parseNested: function(page, ast, state={}){
return Promise.awaitOrRun(
state.waitNested,
this._parse(page, ast, state),
function(_, ast){
return ast }) },
// XXX this can't be used from within macros -- will deadlock the results... // XXX this can't be used from within macros -- will deadlock the results...
// XXX how should this play with filters??? // XXX how should this play with filters???
// ...should filters be client-side only?? // ...should filters be client-side only??
parse: function(page, ast, state={}){ parse: function(page, ast, state={}){
var that = this return Promise.awaitOrRun(
ast = this.resolve(page, ast, state) state.wait,
// XXX .wait or .waitAll ??? this._parse(page, ast, state),
if(state.wait){ function(_, ast){
return state.wait return ast }) },
.then(function(){
return that.parse(page, ast, state) }) }
// XXX
if(ast.length > 1){
throw new Error('!!!!')
}
return ast[0]
?? '' },
@ -1002,6 +1003,13 @@ function(spec, func){
return func } return func }
var isolated =
module.isolated =
function(func){
func.isolated = true
return func }
// XXX RENAME... // XXX RENAME...
// ...this is more of an expander/executer... // ...this is more of an expander/executer...
// ...might be a good idea to also do a check without executing... // ...might be a good idea to also do a check without executing...
@ -1088,7 +1096,7 @@ module.parser = {
// ...at this stage it should more or less be static -- check! // ...at this stage it should more or less be static -- check!
return Promise.awaitOrRun( return Promise.awaitOrRun(
// XXX how do we resolve the await loop? // XXX how do we resolve the await loop?
parser.parse(this, ast, { parser.parseNested(this, ast, {
...state, ...state,
filters: local.includes(this.ISOLATED_FILTERS) ? filters: local.includes(this.ISOLATED_FILTERS) ?
local local
@ -1364,23 +1372,26 @@ module.parser = {
// XXX thinking that reimplementing this is a bit less boring than // XXX thinking that reimplementing this is a bit less boring than
// refactoring, and should be cleaner... // refactoring, and should be cleaner...
// XXX if the src is empty return nothing... // XXX if the src is empty return nothing...
// XXX this needs to be reusable...
// XXX need a wrapper protocol -- is this the level for it??? // XXX need a wrapper protocol -- is this the level for it???
// XXX page API used: // XXX page API used:
// .basepath
// .resolvePathVars(path) // .resolvePathVars(path)
// .get(path) // .get(path)
// is this a promise/value, iterable promise a generator // is this a promise/value, iterable promise a generator
// an async generator, ... or a combination/stack of the above??? // an async generator, ... or a combination/stack of the above???
function(parser, args, body, state, key='included', handler){ function(parser, args, body, state, key='included', handler){
var that = this
var recursive =
state.recursive =
args.recursive
?? body
?? state.recursive
var base = this.basepath var base = this.basepath
// XXX var src = this.resolvePathVars(args.src)
//var src = parser.parse(this.resolvePathVars(args.src))
var src = args.src
return Promise.awaitOrRun( return Promise.awaitOrRun(
parser.parse(this, src, state), parser.parseNested(this, src, state),
function(src){ function(src){
var cache = state.cache ??= {} var cache = state.cache ??= {}
if(cache[src]){ if(cache[src]){
@ -1389,36 +1400,72 @@ module.parser = {
// XXX should this be a tree?? // XXX should this be a tree??
// ...need to at least split direct and // ...need to at least split direct and
// indirect dependencies... // indirect dependencies...
var depends = ((state.depends ??= {})[base] ??= {}) // XXX would be nice to separate direct (in-page)
// depenedencies nad nested...
// XXX do we need the same for real paths???
// no, because actual paths are meaningless
// out of context...
var depends = ((state.depends ??= {})[src] ??= {})
// check for recursion...
// XXX do we do this for pattern paths???
// XXX this does not catch the /A/A/A/... recursion...
var stack = state.include_stack ??= []
// XXX is this the right separator???
// ...need something that can't be in a path...
var base_src = base +'|'+ src
if(stack.includes(base_src)){
if(recursive){
return recursive }
throw new Error('Recursion:\n\t'+
[...stack, base_src].join('\n\t\t-> ')) }
stack.push(base_src)
// content handler...
handler ??= handler ??=
function(parser, page, state){ function(parser, text, state){
// XXX get page text return parser._parse(that, text, state) }
// XXX setup state
return parser.parse(this, page, state) }
var pageHandler =
function(page){
// XXX cache...
return handler.call(this, parser, page, state) }
// XXX can we do: var pageHandler =
// return this.get(...) function(text, i, l){
// .iter() return [
// .map(...) handler.call(that, parser, text, state),
// .sync() // join...
return Promise.awaitOrRun( (args.join
this.get(src), && i < l.length - 1) ?
parser._parse(that, args.join, state)
: []
].flat() }
var resultHandler =
function(pages){ function(pages){
// XXX not sure if this can happen or why...
if(state.include_stack.at(-1) != base_src){
throw new Error('Include stack error') }
state.include_stack.pop()
// cleanup...
if(state.include_stack.length == 0){
delete state.include_stack
delete state.recursive }
// cache the final result...
// XXX wrap??
cache[src] = pages
return pages }
// get and run things...
return Promise.awaitOrRun(
that.get(src).raw,
function(pages){
pages = pages instanceof Array ?
pages
: [pages]
return Promise.awaitOrRun( return Promise.awaitOrRun(
// handle pages... // handle pages...
Promise Promise
.iter( .iter(pages
pages
.map( pageHandler )) .map( pageHandler ))
.flat()
.sync(), .sync(),
// cache the final result... resultHandler ) }) }) }),
function(pages){
return (cache[src] = pages) }) }) }) }),
include: Macro( include: Macro(
['src', 'recursive', 'join', ['src', 'recursive', 'join',
@ -1947,5 +1994,32 @@ module.parser = {
// XXX for testing...
var P =
module.P = {
//* XXX
_content: [
'first page text',
'second page text',
],
/*/
_content: 'page text',
//*/
path: '/path/to/page',
basepath: '/path/to/',
get raw(){
return this._content ?? '' },
get: function(){
return this },
resolvePathVars: function(path){
return path },
}
/********************************************************************** /**********************************************************************
* vim:set ts=4 sw=4 : */ return module }) * vim:set ts=4 sw=4 : */ return module })

View File

@ -73,6 +73,12 @@ test.Setups({
'<slot slot>[[ <slot slot.content/> ]]</slot>@slot(slot value)', '<slot slot>[[ <slot slot.content/> ]]</slot>@slot(slot value)',
'value' ]} }, 'value' ]} },
// XXX should this work???
slot_nested_nested: function(assert){
return {code: [
'<slot slot>[[ <slot slot value/> ]]</slot>',
'value' ]} },
/* XXX SHOWN_HIDDEN /* XXX SHOWN_HIDDEN
// XXX these need to be revised... // XXX these need to be revised...
// ...do we actually need hidden/shown??? // ...do we actually need hidden/shown???