diff --git a/pwiki/page.js b/pwiki/page.js index 80ece8f..dda0560 100755 --- a/pwiki/page.js +++ b/pwiki/page.js @@ -305,6 +305,8 @@ object.Constructor('BasePage', { for(var path of paths){ yield this.get('/'+ path) } }, + [Symbol.asyncIterator]: async function*(){ + yield* this.each() }, map: async function(func){ return this.each().map(func) }, @@ -658,11 +660,8 @@ object.Constructor('Page', BasePage, { // At the moment nested recursion is checked in a fast but // not 100% correct manner focusing on path depth and ignoring // the context, this potentially can lead to false positives. - // - // XXX make this a generator... - // XXX should we use .__parser__.expand(..) instead of .parse(..) ??? include: Macro( - ['src', 'recursive', 'join', ['isolated']], + ['src', 'recursive', 'join', ['strict', 'nonstrict', 'isolated']], async function*(args, body, state, key='included', handler){ var macro = 'include' if(typeof(args) == 'string'){ @@ -675,6 +674,8 @@ object.Constructor('Page', BasePage, { return } var recursive = args.recursive ?? body var isolated = args.isolated + var strict = args.strict + && !args.nonstrict var join = args.join && await base.parse(args.join, state) @@ -688,7 +689,7 @@ object.Constructor('Page', BasePage, { .parse(state) } var first = true - for await (var page of this.get(src).each()){ + for await (var page of this.get(src).asPages(strict)){ if(join && !first){ yield join } first = false @@ -774,7 +775,7 @@ object.Constructor('Page', BasePage, { : src var pages = src ? - this.get(src).each() + this.get(src).asPages() : text instanceof Array ? [text.join('')] : typeof(text) == 'string' ? @@ -891,6 +892,7 @@ object.Constructor('Page', BasePage, { // show first instance... : name in slots) + // set slot value... var stack = [] slots[name] && stack.push(slots[name]) @@ -901,7 +903,6 @@ object.Constructor('Page', BasePage, { slot = slots[name] = slots[name] ?? slot - // handle ... for(prev of stack){ // get the first @@ -917,14 +918,12 @@ object.Constructor('Page', BasePage, { .filter(function(e){ return typeof(e) != 'function' || e.slot != name }) ) } - return hidden ? '' : Object.assign( function(state){ return state.slots[name] }, {slot: name}) }), - //*/ 'content': ['slot'], // @@ -952,18 +951,14 @@ object.Constructor('Page', BasePage, { // NOTE: if both strict and nonstrict are given the later takes // precedence. // - // XXX GENERATOR make this a generator... - // XXX ELSE_PRIO should else attr take priority over the tag??? - // ...currently as with text=... the attr takes priority... // XXX SORT sorting not implemented yet.... - // XXX should support arrays... - // e.g. - // ... - // ...does not work yet... - // ....currently resolved returns promises.... macro: Macro( ['name', 'src', 'sort', 'text', 'join', 'else', ['strict', 'nonstrict']], + // XXX GENERATOR... + async function*(args, body, state){ + /*/ async function(args, body, state){ + //*/ var that = this var name = args.name //?? args[0] var src = args.src @@ -1014,37 +1009,22 @@ object.Constructor('Page', BasePage, { if(src){ src = await base.parse(src, state) - var pages = this.get(src, strict) - pages = await pages.isArray ? - // XXX should we wrap this in pages... - (await pages.raw) - .map(function(data){ - return that.virtual({text: data}) }) - : await pages.each() - // no matching pages -> get the else block... - if(pages.length == 0 + + var join = _getBlock('join') + + // expand matches... + var first = true + for await(var page of this.get(src).asPages(strict)){ + if(join && !first){ + yield join } + first = false + yield this.__parser__.expand(page, text, state) } + // else... + if(first && (text || args['else'])){ var else_block = _getBlock('else') - return else_block ? - await this.__parser__.expand(this, else_block, state) - : undefined } - - // sort pages... - // XXX SORT - if(sort.length > 0){ - console.log('XXX: macro sort: not implemented') } - - var join_block = _getBlock('join') - - // apply macro text... - var res = pages - .map(function(page){ - return that.__parser__.expand(page, text, state) }) - return join_block ? - res.between( - // render join block relative to the path before the first '*'... - await that.__parser__.expand(base, join_block, state)) - : res } }), + if(else_block){ + yield this.__parser__.expand(this, else_block, state) } } } }), // nesting rules... 'else': ['macro'], @@ -1126,6 +1106,27 @@ object.Constructor('Page', BasePage, { this.__update__({text: value}) }, //this.onTextUpdate(value) }, + // iterate matches or content list as pages... + // + // XXX revise name... + asPages: async function*(path='.', strict=false){ + if(path === true + || path === false){ + strict = path + path = '.' } + var page = this.get(path, strict) + // handle lists in pages (actions, ... etc.)... + if(!page.isPattern){ + var raw = await page.raw + yield* raw instanceof Array ? + raw + .map(function(p){ + return page.virtual({text: p}) }) + : [page] + // each... + } else { + yield* page } }, + // expanded page text... // // NOTE: this uses .PAGE_TEMPLATE to render the page. diff --git a/pwiki/parser.js b/pwiki/parser.js index d3a88fd..b2e2c11 100755 --- a/pwiki/parser.js +++ b/pwiki/parser.js @@ -460,10 +460,10 @@ module.BaseParser = { /*/ for(var e of await ast){ //*/ + // expand delayed sections... e = typeof(e) == 'function' ? e.call(page, state) : e - // expand arrays... if(e instanceof Array | e instanceof types.Generator){ diff --git a/pwiki2.js b/pwiki2.js index e363307..9164b68 100755 --- a/pwiki2.js +++ b/pwiki2.js @@ -22,15 +22,13 @@ * @include(..) -- DONE * @source(..) -- DONE * @quote(..) -- DONE -* @macro(..) -- -* 3) experiment with back-drivable generators... -* this can be implemented/tested in parallel and integrated into -* the main pipeline if proven successful... +* @macro(..) -- DONE * XXX ranges in pattern paths -- page-size=X page=Y | from=X to=Y / ... * ...url syntax??? * XXX differenced in behaviour between _abc and abc, either need to make * them the same or document the differences and the reasons behind * them... +* XXX add support for tag in include/source/quote??? * XXX BUG?: markdown: when parsing chunks each chunk gets an open/closed *

inserted at start/end -- this breaks stuff returned by macros... * ...there are two ways to dance around this: