From 128b4276f7d7e11184ba5f4db7142560fdc4af01 Mon Sep 17 00:00:00 2001 From: "Alex A. Naanou" Date: Sun, 14 Aug 2022 23:54:02 +0300 Subject: [PATCH] cleanup and fixes... Signed-off-by: Alex A. Naanou --- lib/types/Array.js | 29 +++++++++++ lib/types/Promise.js | 16 ++++++ lib/types/generator.js | 47 ++++++++++++++++++ pwiki/dom/wikiword.js | 8 +-- pwiki/page.js | 110 ++++++++++++++++++++++++----------------- pwiki/parser.js | 1 + pwiki/store/base.js | 4 +- pwiki2-test.js | 22 +++++++++ pwiki2.js | 20 ++++++-- 9 files changed, 204 insertions(+), 53 deletions(-) diff --git a/lib/types/Array.js b/lib/types/Array.js index 5d868dc..364258c 100755 --- a/lib/types/Array.js +++ b/lib/types/Array.js @@ -482,6 +482,35 @@ object.Mixin('ArrayProtoMixin', 'soft', { this.constructor.zip(this, func, ...arrays) : this.constructor.zip(func, this, ...arrays) }, + // Insert new values between elements of an array + // + // .between(value) + // -> array + // + // .between(func) + // -> array + // + // func([a, b], from_index, to_index, array) + // -> elem + // + between: function(func){ + var res = [] + // NOTE: we skip the last element... + for(var i=0; i < this.length; i+=1){ + var pair = new Array(2) + i in this ? + res.push(pair[0] = this[i]) + : (res.length += 1) + if(i+1 >= this.length){ + break } + i+1 in this + && (pair[1] = this[i+1]) + res.push( + typeof(func) == 'function' ? + func.call(this, pair, i, res.length, this) + : func) } + return res }, + // get iterator over array... // // Array.iter() diff --git a/lib/types/Promise.js b/lib/types/Promise.js index 9d2e7ab..215f51b 100755 --- a/lib/types/Promise.js +++ b/lib/types/Promise.js @@ -294,6 +294,22 @@ object.Constructor('IterablePromise', Promise, { .then(function(){ return res }) }, + // XXX BETWEEN... + between: function(func){ + var i = 0 + var j = 0 + var prev + return this.constructor(this, + function(e){ + return i++ > 0 ? + [ + typeof(func) == 'function' ? + func.call(this, [prev, e], i, i + j++) + : func, + e, + ] + : [e] }) }, + // XXX .chain(..) -- see generator.chain(..) flat: function(depth=1){ diff --git a/lib/types/generator.js b/lib/types/generator.js index 4cb7713..23cd3ab 100755 --- a/lib/types/generator.js +++ b/lib/types/generator.js @@ -223,6 +223,15 @@ object.Mixin('GeneratorMixin', 'soft', { reduce: makeGenerator('reduce'), reduceRight: makeGenerator('reduceRight'), + between: makeGenerator('between'), + + // XXX EXPERIMENTAL + // XXX add .toString(..) to this??? + forEach: function(func){ + var that = this + return function(){ + return that(...arguments).forEach(func) } }, + // non-generators... // toArray: function(){ @@ -397,6 +406,26 @@ object.Mixin('GeneratorProtoMixin', 'soft', { greduce: function*(func, res){ yield this.reduce(...arguments) }, + between: stoppable(function*(func){ + var i = 0 + var j = 0 + var prev + for(var e of this){ + if(i > 0){ + yield typeof(func) == 'function' ? + func.call(this, [prev, e], i-1, i + j++, this) + : func } + prev = e + yield e + i++ } }), + + // NOTE: this is a special case in that it will unwind the generator... + // NOTE: this is different from .forEach(..) in that this will + // return the resulting array. + // XXX EXPERIMENTAL + forEach: function(func){ + return [...this].map(func) }, + pop: function(){ return [...this].pop() }, // XXX this needs the value to be iterable... @@ -490,6 +519,9 @@ object.Mixin('AsyncGeneratorMixin', 'soft', { map: makeGenerator('async', 'map'), filter: makeGenerator('async', 'filter'), reduce: makeGenerator('async', 'reduce'), + + // XXX TEST... + between: makeGenerator('async', 'between'), }) var AsyncGeneratorProtoMixin = @@ -547,6 +579,21 @@ object.Mixin('AsyncGeneratorProtoMixin', 'soft', { return [] }) return state }, + // XXX BETWEEN... + between: async function*(func){ + var i = 0 + var j = 0 + var prev + yield* this.iter(function(e){ + return i++ > 0 ? + [ + typeof(func) == 'function' ? + func.call(this, [prev, e], i, i + j++, this) + : func, + e, + ] + : [e] }) }, + // XXX TEST... chain: async function*(...next){ yield* next diff --git a/pwiki/dom/wikiword.js b/pwiki/dom/wikiword.js index 4d5bb99..630912c 100755 --- a/pwiki/dom/wikiword.js +++ b/pwiki/dom/wikiword.js @@ -9,13 +9,13 @@ var WIKIWORD_PATTERN = RegExp('('+[ + // /some/path | ./some/path | ../some/path | >>/some/path + '(?:^|\\s)(|\\.|\\.\\.|>>)[\\/\\\\][^\\s]+', + // [path] + '\\\\?\\[[^\\]]+\\]', // WikiWord //'\\\\?(\\/|\\./|\\.\\./|>>|[A-Z][_a-z0-9]+[A-Z/])[_a-zA-Z0-9/]*', '\\\\?\\/?(\\./|\\.\\./|>>|[A-Z][_a-z0-9]+[A-Z/])[_a-zA-Z0-9/]*', - // [path] - '\\\\?\\[[^\\]]+\\]', - // /some/path | ./some/path | ../some/path | >>/some/path - '(?:^|\\s)(|\\.|\\.\\.|>>)[\\/\\\\][^\\s]+' ].join('|') +')', 'g') // XXX RENAME... diff --git a/pwiki/page.js b/pwiki/page.js index f865d8b..45924d1 100755 --- a/pwiki/page.js +++ b/pwiki/page.js @@ -25,11 +25,7 @@ var relProxy = function(name){ var func = function(path='.', ...args){ return this.store[name]( - /* XXX RELATIVE - pwpath.relative(this.location+'/', path), - /*/ pwpath.relative(this.location, path), - //*/ ...args) } Object.defineProperty(func, 'name', {value: name}) return func } @@ -40,11 +36,7 @@ function(name){ strict = path path = '.' } return this.store[name]( - /* XXX RELATIVE - pwpath.relative(this.location+'/', path), - /*/ pwpath.relative(this.location, path), - //*/ strict) } Object.defineProperty(func, 'name', {value: name}) return func } @@ -227,11 +219,7 @@ object.Constructor('BasePage', { __update__: function(data){ return this.store.update(this.location, data) }, __delete__: function(path='.'){ - /* XXX RELATIVE - return this.store.delete(pwpath.relative(this.location+'/', path)) }, - /*/ return this.store.delete(pwpath.relative(this.location, path)) }, - //*/ // page data... // @@ -277,7 +265,20 @@ object.Constructor('BasePage', { // relative proxies to store... exists: relProxy('exists'), + //* XXX MATCH match: relMatchProxy('match'), + /*/ + match: async function(path='.', strict=false){ + if(path === true || path === false){ + strict = path + path = '.' } + path = pwpath.relative(this.location, path) + var res = await this.store.match(path, strict) + return res.length == 0 ? + // XXX are we going outside of match semantics here??? + this.store.find(path) + : res }, + //*/ resolve: relMatchProxy('resolve'), delete: function(path='.'){ this.__delete__() @@ -308,11 +309,7 @@ object.Constructor('BasePage', { strict = path path = '.' } return this.store.find( - /* XXX RELATIVE - //pwpath.relative(this.location+'/', path), strict) }, - /*/ pwpath.relative(this.location, path), strict) }, - //*/ // // .get([, ]) @@ -346,6 +343,10 @@ object.Constructor('BasePage', { : paths instanceof Promise ? await paths : [paths] + // XXX MATCH + paths = paths.length == 0 ? + [await this.find(path)] + : paths for(var path of paths){ yield this.get('/'+ path) } }, @@ -632,7 +633,6 @@ object.Constructor('Page', BasePage, { // // | - // - //* XXX filter: function(args, body, state, expand=true){ var that = this @@ -685,12 +685,11 @@ object.Constructor('Page', BasePage, { // // // - // XXX RECURSION recursion detection is still a bit off... // XXX 'text' argument is changed to 'recursive'... // XXX revise recursion checks.... // XXX should this be lazy??? include: Macro( - ['src', 'recursive', ['isolated']], + ['src', 'recursive', 'join', ['isolated']], async function(args, body, state, key='included', handler){ var macro = 'include' if(typeof(args) == 'string'){ @@ -698,13 +697,18 @@ object.Constructor('Page', BasePage, { key = key ?? 'included' } // positional args... var src = args.src - var recursive = args.recursive || body - var isolated = args.isolated - + && await this.parse(args.src, state) if(!src){ return } + var recursive = args.recursive || body + var isolated = args.isolated + var join = args.join + && await this + // render join block relative to the path before the first '*'... + .get(this.path.split(/\*/).shift()) + .parse(args.join, state) + // parse arg values... - src = await this.parse(src, state) var base = this.get(src).path handler = handler @@ -715,7 +719,8 @@ object.Constructor('Page', BasePage, { : this.get(src) .parse(state) } - return this.get(src) + //return this.get(src) + var res = this.get(src) .each() .map(async function(page){ var full = page.path @@ -740,7 +745,11 @@ object.Constructor('Page', BasePage, { if(!parent_seen){ delete state.seen } - return res }) }), + return res }) + return join ? + res.between(join) + : res }), + //*/ // NOTE: the main difference between this and @include is that // this renders the src in the context of current page while // include is rendered in the context of its page but with @@ -772,6 +781,7 @@ object.Constructor('Page', BasePage, { // not expanded... // NOTE: the filter argument uses the same filters as @filter(..) // + // XXX need to handle pattern paths (like include: join=...) // XXX need a way to escape macros -- i.e. include in a quoted text... quote: Macro( ['src', 'filter', 'text'], @@ -995,16 +1005,16 @@ object.Constructor('Page', BasePage, { var join_block = _getBlock('join') // apply macro text... - return pages + var res = await pages .map(function(page, i){ - return [ - that.__parser__.expand(page, text, state), - // weave in the join block... - ...((join_block && i < pages.length-1) ? - [that.__parser__.expand(that, join_block, state)] - : []), - ] }) - .flat() } }), + return that.__parser__.expand(page, text, state) }) + return join_block ? + res.between(await that.__parser__.expand( + // render join block relative to the path before the first '*'... + that.get(that.path.split(/\*/).shift()), + join_block, + state)) + : res } }), // nesting rules... 'else': ['macro'], @@ -1221,26 +1231,38 @@ module.System = { // _list: { // text: '- @source(.)' }, // + // XXX all of these should support pattern pages... _text: { - text: '@include(. isolated)' }, - // XXX this does not separate items when getting patterns... - //text: '@include(. isolated)' }, + text: '@include(. isolated join="@source(file-separator)")' }, + // XXX add join... _raw: { text: '@quote(.)' }, // XXX not sure if this is the right way to go... _code: { - text: '
' }, + text: + '' + +'
' + +'
'}, _ed: { //_edit: { text: - '
'
-				+''
-			+'
' }, + '' + +'
'
+					+''
+				+'
' + +'
'}, + paths: { + text: '@source(.)' }, + + // page parts... + // + 'line-separator': { text: '
' }, + 'file-separator': { text: '
' }, // base system pages... // diff --git a/pwiki/parser.js b/pwiki/parser.js index d36923c..ce1d21f 100755 --- a/pwiki/parser.js +++ b/pwiki/parser.js @@ -471,6 +471,7 @@ module.BaseParser = { // NOTE: we need to await for ast here as we need stage 2 of // parsing to happen AFTER everything else completes... return await Promise.iter((await ast) + .flat() // post handlers... .map(function(section){ return typeof(section) == 'function' ? diff --git a/pwiki/store/base.js b/pwiki/store/base.js index c8d4ffc..db47d57 100755 --- a/pwiki/store/base.js +++ b/pwiki/store/base.js @@ -145,8 +145,8 @@ module.BaseStore = { pwpath.normalize(path, 'string') .replace(/^\/|\/$/g, '') .replace(/\//g, '\\/') - .replace(/\*\*/g, '.+') - .replace(/\*/g, '[^\\/]+') + .replace(/\*\*/g, '.*') + .replace(/\*/g, '[^\\/]*') }(?=[\\\\\/]|$)`) return [...(await this.paths()) // NOTE: we are not using .filter(..) here as wee diff --git a/pwiki2-test.js b/pwiki2-test.js index 33345cc..4dab2fb 100755 --- a/pwiki2-test.js +++ b/pwiki2-test.js @@ -58,6 +58,18 @@ pwiki.store.update('@pouch', { // XXX TEST... // XXX add filter tests... pwiki.pwiki + .update({ + location: '/test/sort/*', + order: ['a', 'c', 'b'], }) + .update({ + location: '/test/sort/a', + text: 'a', }) + .update({ + location: '/test/sort/b', + text: 'b', }) + .update({ + location: '/test/sort/c', + text: 'c', }) .update({ location: '/test/comments', text: object.doc` @@ -114,6 +126,16 @@ pwiki.pwiki --- `, }) + // XXX BUG: this prints '' for each + .update({ + location: '/test/macro-quote', + text: object.doc` + Outside of macro: + + + In macro: + -- + `, }) .update({ location: '/test/wikiword', text: object.doc` diff --git a/pwiki2.js b/pwiki2.js index 87282c0..1989b3d 100755 --- a/pwiki2.js +++ b/pwiki2.js @@ -1,8 +1,22 @@ /********************************************************************** * * -* XXX BUG: comments seem to be broken -- see: /Doc/About -* XXX BUG: browser: .get('/*').raw hangs... +* XXX BUG: join block gets repeated three times per page... +* await p.pwiki.get('/test/sort/*').text +* essentially this is the culprit: +* await p.pwiki.get('/test/sort/*').parse('@source(file-separator)') +* XXX BUG: browser: .get('/*').raw hangs in the browser context... +* XXX might be a good idea to add page caching (state.page_cache) relative +* to a path on parsing, to avoid re-matching the same page over and +* over again from the same context +* format: +* { +* : { +* : , +* ... +* }, +* ... +* } * XXX add action to reset overloaded (bootstrap) pages... * - per page * - global @@ -21,7 +35,7 @@ * - basic editor and interactivity -- DONE * - export * - json -- DONE -* - zip +* - zip (json/tree) * - migrate bootstrap * - store topology * - sync and sync conf