From 71a4b7f41b712e913ceb943452671f4467a97d48 Mon Sep 17 00:00:00 2001 From: "Alex A. Naanou" Date: Thu, 9 Jul 2026 17:10:50 +0300 Subject: [PATCH] filters mostly working... Signed-off-by: Alex A. Naanou --- v3/pwiki/parser.js | 189 ++++++++++++++++++++++----------------------- 1 file changed, 94 insertions(+), 95 deletions(-) diff --git a/v3/pwiki/parser.js b/v3/pwiki/parser.js index cd4c692..514dbce 100644 --- a/v3/pwiki/parser.js +++ b/v3/pwiki/parser.js @@ -270,13 +270,19 @@ module.BaseParser = { : []), ] }) .flat() }, - filterBlocks: function(page, blocks, filter, state){ - if(filter == null){ - return blocks } - // XXX - - return blocks }, + applyFilters: function(filters, str, state={}){ + var that = this + filters = this.normalizeFilters(filters) + .filter(function(f){ + return f in (that.filters ?? {})}) + var handle = function(str){ + return filters + .reduce(function(res, filter){ + return that.filters[filter].call(that, str, state) }, str) } + return str instanceof Array ? + str.map(handle) + : handle(str) }, // Strip comments... @@ -600,7 +606,6 @@ module.BaseParser = { // states and the like, async/await can't... // XXX Q: do we need generators? // XXX Handle errors... - fullAST: true, // XXX this needs a careful rewrite of the .macros.* for the new scheme: // - expand // - "merge" @@ -687,6 +692,8 @@ module.BaseParser = { delete state.waitNested } delete elem.resolving elem.value = value + // XXX should we resolve to value or elem??? + // ...elem seems more consistent... return value }, function(err){ state.errors ??= [] @@ -694,14 +701,8 @@ module.BaseParser = { delete elem.resolving elem.error = err }) // sync... - } else if('fullAST' in state ? - state.fullAST - : 'fullAST' in page ? - page.fullAST - : this.fullAST){ - elem.value = res } else { - elem = res } + elem.value = res } elems.push(elem) } // cleanup... @@ -814,8 +815,20 @@ module.BaseParser = { // XXX }, - exec: function(page, ast, state={}, nested_handlers={}, wait='wait'){ + // merge stage III... + // + // XXX FILTER should filters he handled here or in .resolve(..) ??? + // ...and how do we handle filters in @include(..)'s??? + merge: function(page, ast, state={}, nested_handlers={}, wait='wait'){ var that = this + + var stage3 = function(ast){ + return ast + .map(function(e){ + return typeof(e) == 'function' ? + e.call(that, state) + : e }) + .flat() } // XXX might be a good idea to limit recursion depth here... var merge = function(ast){ return Promise.awaitOrRun( @@ -823,7 +836,8 @@ module.BaseParser = { function(){ delete state.unresolved // re-resolve... - ast = that.resolve(page, ast, state, nested_handlers) + ast = stage3( + that.resolve(page, ast, state, nested_handlers)) // NOTE: this is essentially running in the same frame // as .resolve(..) above so there should not be @@ -832,17 +846,26 @@ module.BaseParser = { merge(ast) : (ast ?? '').join('') }) } + ast = stage3( + this.resolve(page, ast, state, nested_handlers)) + return Promise.awaitOrRun( - this.resolve(page, ast, state, nested_handlers), state[wait], - function(ast){ + function(){ + //ast = stage3(ast) // NOTE: in an async world where any promised macro can // call .exec(..) / .execNested(..) we can't trust // the lack of .unresolved in state... - return (state.unresolved - || !that.isResolved(ast)) ? - merge(ast) - : (ast ?? '').join('') }) }, + return that.applyFilters( + state.filters ?? [], + (state.unresolved + || !that.isResolved(ast)) ? + merge(ast) + : (ast ?? '').join(''), + state) }) }, + + exec: function(page, ast, state={}, nested_handlers={}, wait='wait'){ + return this.merge(...arguments) }, // XXX how should this play with filters??? @@ -900,6 +923,12 @@ module.parser = { INCLUDE_NEST_LIMIT: 20, + // XXX should this be here, in page, or both? + filters: { + upper: function(str){ + return str.toUpperCase() }, + }, + // // (, , ){ .. } // -> undefined @@ -926,74 +955,39 @@ module.parser = { // // | - // - // XXX the rewrite: - // - these need to be called after everything is done - // - globally in .exec(..) ??? - // - localy after the item content is ready - // -> should we have and .onDone(..) event somwhere in the tree??? - // ...this would be usefull for the callback renderer too... - // - // XXX BUG: this does not show any results: - // pwiki.exec('moo test') - // -> '' - // while these do: - // pwiki.exec('moo test') - // -> 'moo TEST' - // await pwiki.exec('moo test@var()') - // -> 'moo TEST' - // for more info see: - // file:///L:/work/pWiki/pwiki2.html#/Editors/Results - // XXX do we fix this or revise how/when filters work??? - // ...including accounting for variables/expansions and the like... - // XXX REVISE... - // XXX UPDATE... - filter: function(parser, args, body, state, expand=true){ + // XXX need a way to exclude some filters in some nested locks... + // XXX BUG: RECURSION: this breaks @include(..)'s recursion tests... + filter: function(page, args, body, state){ var that = this - var outer = state.filters = - state.filters ?? [] - var local = Object.keys(args) + delete args.text + delete args.body + var filters = Object.keys(args) - // trigger quote-filter... - var quote = local - .map(function(filter){ - return (that.filters[filter] ?? {})['quote'] ?? [] }) - .flat() - quote.length > 0 - && parser.macros['quote-filter'].call( - this, - parser, - Object.fromEntries(Object.entries(quote)), - null, - state) - - // local filters... - if(body != null){ - // expand the body... - var ast = expand ? - this.__parser__.expand(this, body, state) - : body instanceof Array ? - body - // NOTE: wrapping the body in an array effectively - // escapes it from parsing... - : [body] + // local filter... + if(body){ + // stage I + body = this.expand(page, body, state) + // stage II return function(state){ - // XXX can we loose stuff from state this way??? - // ...at this stage it should more or less be static -- check! - return Promise.awaitOrRun( - // XXX how do we resolve the await loop? - parser.execNested(this, ast, { - ...state, - filters: local.includes(this.ISOLATED_FILTERS) ? - local - : [...outer, ...local], - }), - function(res){ - return {data: res} }) } - // global filters... - } else { - state.filters = [...outer, ...local] } }, + body = that.resolve(page, body, state) + + // stage III + return function(state){ + return Promise.awaitOrRun( + that.merge(page, body, state), + function(body){ + // apply the filters... + // XXX combine with state.filters??? + return that.applyFilters(filters, body, state) }) } } + + // global filter... + } else if(filters.length > 0){ + (state.filters = (state.filters ??= [])) + .push(...filters) } + + return '' }, // Args... @@ -1286,9 +1280,7 @@ module.parser = { // not 100% correct manner focusing on path depth and ignoring // the context, this potentially can lead to false positives. // - // XXX do we need the nested macro??? - // XXX should this resolve join and/or body in the context of the - // included page or the outer page (current)??? + // XXX FILTER do we skip includes from outer filters??? // XXX add path recursion test to data -- fail if two paths resolve // to the same context... // XXX need a way to make encode option transparent... @@ -1297,9 +1289,6 @@ module.parser = { include: Macro( ['src', 'recursive', 'join', ['s', 'strict', 'isolated']], - // XXX thinking that reimplementing this is a bit less boring than - // refactoring, and should be cleaner... - // XXX if the src is empty return nothing... // XXX need a wrapper protocol -- is this the level for it??? // XXX page API used: // .resolvePathVars(path) @@ -1337,6 +1326,7 @@ module.parser = { handler ??= function(page, body, path, text, state){ // re-include limit... + //* XXX RECURSION // XXX HACK??? if( ++(state.included ??= {[path]: 0})[path] > this.INCLUDE_NEST_LIMIT ?? 20){ @@ -1357,19 +1347,24 @@ module.parser = { throw new Error('Recursive macro: '+include_stack) } return that.expand(page, recursive, state) } include_stack.push(path) + //*/ // XXX check cache??? var nested var res = args.isolated ? - this.resolve( + //this.resolve( + this.merge( page, text, nested = args.isolated == 'partial' ? serialize.partialDeepCopy(state) : {}) + // XXX FILTER need to localize target page + // filters to it, somehow... : this.expand(page, text, state) + //* XXX RECURSION // handle recursion... Promise.awaitOrRun( (nested ?? {}).waitAll, @@ -1381,6 +1376,7 @@ module.parser = { if(state.include_stack.length == 0){ delete state.include_stack delete state.recursive } }) + //*/ return res } @@ -1491,6 +1487,10 @@ module.parser = { quoting( function(page, args, body, state){ var that = this + var filters = (args.filter ?? '') + .split(/\s+/) + .filter(function(s){ + return s.length > 0 }) return Promise.awaitOrRun( args.src && this.execNested(page, args.src, state), @@ -1507,10 +1507,9 @@ module.parser = { function(text){ return that.joinBlocks( page, - that.filterBlocks( - page, + that.applyFilters( + filters, text, - args.filter, state), args.join, state) }) }) })),