made the parser async -- quite ugly, needs a rethink...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2022-06-15 16:10:16 +03:00
parent f0c217ef4b
commit 9a1324e544

186
pwiki2.js
View File

@ -405,11 +405,12 @@ module.BaseStore = {
path = await this.resolve(path, strict) path = await this.resolve(path, strict)
return path instanceof Array ? return path instanceof Array ?
// XXX should we return matched paths??? // XXX should we return matched paths???
path.map(function(p){ Promise.iter(path)
// NOTE: p can match a non existing page at this point, .map(function(p){
// this can be the result of matching a/* in a a/b/c // NOTE: p can match a non existing page at this point,
// and returning a a/b which can be undefined... // this can be the result of matching a/* in a a/b/c
return that.get(p) }) // and returning a a/b which can be undefined...
return that.get(p) })
: (await this.__get__(path) : (await this.__get__(path)
// XXX NEXT // XXX NEXT
?? ((this.next || {}).__get__ ?? ((this.next || {}).__get__
@ -533,7 +534,6 @@ function(meth, drop_cache=false, post){
//: this.data[store][meth](path.slice(store.length), ...args) //: this.data[store][meth](path.slice(store.length), ...args)
: this.data[store][target](path.slice(store.length), ...args) : this.data[store][target](path.slice(store.length), ...args)
//console.log('---', path, target, '(', store, ...args, ') ->', res)
if(drop_cache){ if(drop_cache){
delete this.__substores } delete this.__substores }
post post
@ -617,7 +617,7 @@ module.localStorageStore = {
localStorage localStorage
: undefined, : undefined,
__paths__: async function(){ __paths__: function(){
var that = this var that = this
return Object.keys(this.data) return Object.keys(this.data)
.map(function(k){ .map(function(k){
@ -625,22 +625,22 @@ module.localStorageStore = {
k.slice((that.__prefix__ ?? '').length) k.slice((that.__prefix__ ?? '').length)
: [] }) : [] })
.flat() }, .flat() },
__exists__: async function(path){ __exists__: function(path){
return ((this.__prefix__ ?? '')+ path) in this.data return ((this.__prefix__ ?? '')+ path) in this.data
&& path }, && path },
__get__: async function(path){ __get__: function(path){
path = (this.__prefix__ ?? '')+ path path = (this.__prefix__ ?? '')+ path
return path in this.data ? return path in this.data ?
JSON.parse(this.data[path]) JSON.parse(this.data[path])
: undefined }, : undefined },
__update__: async function(path, data={}){ __update__: function(path, data={}){
this.data[(this.__prefix__ ?? '')+ path] = this.data[(this.__prefix__ ?? '')+ path] =
JSON.stringify(data) }, JSON.stringify(data) },
__delete__: async function(path){ __delete__: function(path){
delete this.data[(this.__prefix__ ?? '')+ path] }, delete this.data[(this.__prefix__ ?? '')+ path] },
// XXX // XXX
load: async function(){ load: function(){
}, },
} }
@ -720,8 +720,10 @@ module.FileStore = {
// XXX // XXX
}, },
load: function(data){ load: function(data){
// XXX
}, },
json: function(asstring=false){ json: function(asstring=false){
// XXX
}, },
} }
@ -970,9 +972,9 @@ object.Constructor('BasePage', {
// //
// XXX we are only doing modifiers here... // XXX we are only doing modifiers here...
// ...these ar mainly used to disable writing in .ro(..) // ...these ar mainly used to disable writing in .ro(..)
__update__: async function(data){ __update__: function(data){
return this.store.update(this.location, data) }, return this.store.update(this.location, data) },
__delete__: async function(path='.'){ __delete__: function(path='.'){
return this.store.delete(module.path.relative(this.location, path)) }, return this.store.delete(module.path.relative(this.location, path)) },
// page data... // page data...
@ -1004,10 +1006,16 @@ object.Constructor('BasePage', {
this.__update__(value) }, this.__update__(value) },
// number of matching pages... // number of matching pages...
// NOTE: this can be both sync and async...
get length(){ get length(){
var p = this.resolve(this.location) var p = this.resolve(this.location)
return p instanceof Array ? return p instanceof Array ?
p.length p.length
: p instanceof Promise ?
p.then(function(res){
return res instanceof Array ?
res.length
: 1 })
: 1 }, : 1 },
// relative proxies to store... // relative proxies to store...
@ -1016,8 +1024,6 @@ object.Constructor('BasePage', {
match: relMatchProxy('match'), match: relMatchProxy('match'),
resolve: relMatchProxy('resolve'), resolve: relMatchProxy('resolve'),
delete: function(path='.'){ delete: function(path='.'){
//this.store.delete(module.path.relative(this.location, path))
//return this },
return this.__delete__() }, return this.__delete__() },
// //
@ -1050,7 +1056,9 @@ object.Constructor('BasePage', {
this.resolve(path) this.resolve(path)
: path : path
paths = paths instanceof Array ? paths = paths instanceof Array ?
paths paths
: paths instanceof Promise ?
paths.iter()
: [paths] : [paths]
return paths return paths
.map(function(path){ .map(function(path){
@ -1066,22 +1074,22 @@ object.Constructor('BasePage', {
// sorting... // sorting...
// //
// XXX should this be page-level (current) store level??? // XXX should this be page-level (current) store level???
sort: function(cmp){ // XXX when this is async, should this return a promise????
sort: async function(cmp){
// not sorting single pages... // not sorting single pages...
if(this.length <= 1){ if(this.length <= 1){
return this } return this }
// sort... // sort...
this.metadata = this.metadata = { order: await this.each()
{ order: this.each() .sort(...arguments)
.sort(...arguments) .map(function(p){
.map(function(p){ return p.path }) }
return p.path }) }
return this }, return this },
reverse: function(){ reverse: async function(){
// not sorting single pages... // not sorting single pages...
if(this.length <= 1){ if(this.length <= 1){
return this } return this }
this.metadata = { order: this.match().reverse() } this.metadata = { order: (await this.match()).reverse() }
return this }, return this },
// //
@ -1196,6 +1204,7 @@ object.Constructor('BasePage', {
// XXX how should this work on multiple pages... // XXX how should this work on multiple pages...
// ...right now this will write what-ever is given, even if it // ...right now this will write what-ever is given, even if it
// will never be explicitly be accessible... // will never be explicitly be accessible...
// XXX sync/async???
update: function(...data){ update: function(...data){
return Object.assign(this, ...data) }, return Object.assign(this, ...data) },
@ -1409,8 +1418,8 @@ module.BaseParser = {
// //
// NOTE: this internally uses macros' keys to generate the lexing pattern. // NOTE: this internally uses macros' keys to generate the lexing pattern.
lex: function*(page, str){ lex: function*(page, str){
str = str //str = str
?? page.raw // ?? page.raw
// NOTE: we are doing a separate pass for comments to completely // NOTE: we are doing a separate pass for comments to completely
// decouple them from the base macro syntax, making them fully // decouple them from the base macro syntax, making them fully
// transparent... // transparent...
@ -1509,8 +1518,8 @@ module.BaseParser = {
// NOTE: this internaly uses macros to check for propper nesting // NOTE: this internaly uses macros to check for propper nesting
//group: function*(page, lex, to=false){ //group: function*(page, lex, to=false){
group: function*(page, lex, to=false, parent){ group: function*(page, lex, to=false, parent){
lex = lex //lex = lex
?? this.lex(page) // ?? this.lex(page)
lex = typeof(lex) == 'string' ? lex = typeof(lex) == 'string' ?
this.lex(page, lex) this.lex(page, lex)
: lex : lex
@ -1585,9 +1594,10 @@ module.BaseParser = {
// data: [ <item>, .. ], // data: [ <item>, .. ],
// } // }
// //
expand: function*(page, ast, state={}){ expand: async function*(page, ast, state={}){
ast = ast == null ? ast = ast == null ?
this.group(page) //this.group(page)
this.group(page, await page.raw)
: typeof(ast) == 'string' ? : typeof(ast) == 'string' ?
this.group(page, ast) this.group(page, ast)
: ast instanceof types.Generator ? : ast instanceof types.Generator ?
@ -1617,7 +1627,7 @@ module.BaseParser = {
state) state)
// call... // call...
var res = var res =
page.macros[name].call(page, args, body, state, value) await page.macros[name].call(page, args, body, state, value)
?? '' ?? ''
// result... // result...
if(res instanceof Array if(res instanceof Array
@ -1636,16 +1646,24 @@ module.BaseParser = {
// - apply filters // - apply filters
// //
// XXX add a special filter to clear pending filters... (???) // XXX add a special filter to clear pending filters... (???)
parse: function(page, ast, state={}){ parse: async function(page, ast, state={}){
var that = this var that = this
// XXX should we handle strings as input??? // XXX should we handle strings as input???
ast = ast ast = ast
?? this.expand(page, null, state) ?? await this.expand(page, null, state)
ast = typeof(ast) == 'string' ? ast = typeof(ast) == 'string' ?
this.expand(page, ast, state) await this.expand(page, ast, state)
: ast : ast
//* XXX this is quite ugly...
var blocks = []
for await (var a of ast){
blocks.push(a) }
return blocks
/*/
return [...ast] return [...ast]
//*/
// post handlers... // post handlers...
.map(function(section){ .map(function(section){
return typeof(section) == 'function' ? return typeof(section) == 'function' ?
@ -1845,11 +1863,11 @@ object.Constructor('Page', BasePage, {
state.filters = outer_filters state.filters = outer_filters
// parse the body after we are done expanding... // parse the body after we are done expanding...
return function(state){ return async function(state){
var outer_filters = state.filters var outer_filters = state.filters
state.filters = this.__parser__.normalizeFilters(filters) state.filters = this.__parser__.normalizeFilters(filters)
var res = var res =
[...this.parse(ast, state)] [...await this.parse(ast, state)]
.flat() .flat()
.join('') .join('')
state.filters = outer_filters state.filters = outer_filters
@ -1870,7 +1888,7 @@ object.Constructor('Page', BasePage, {
// XXX should this be lazy??? // XXX should this be lazy???
include: Macro( include: Macro(
['src', 'recursive', ['isolated']], ['src', 'recursive', ['isolated']],
function(args, body, state, key='included', handler){ async function(args, body, state, key='included', handler){
var macro = 'include' var macro = 'include'
if(typeof(args) == 'string'){ if(typeof(args) == 'string'){
var [macro, args, body, state, key="included", handler] = arguments } var [macro, args, body, state, key="included", handler] = arguments }
@ -1882,7 +1900,7 @@ object.Constructor('Page', BasePage, {
if(!src){ if(!src){
return } return }
// parse arg values... // parse arg values...
src = this.parse(src, state) src = await this.parse(src, state)
handler = handler handler = handler
?? function(){ ?? function(){
@ -1908,7 +1926,7 @@ object.Constructor('Page', BasePage, {
seen.push(src) seen.push(src)
// load the included page... // load the included page...
var res = handler.call(this) var res = await handler.call(this)
// restore previous include chain... // restore previous include chain...
if(parent_seen){ if(parent_seen){
@ -1919,17 +1937,17 @@ object.Constructor('Page', BasePage, {
return res }), return res }),
source: Macro( source: Macro(
['src'], ['src'],
function(args, body, state){ async function(args, body, state){
var src = args.src var src = args.src
// parse arg values... // parse arg values...
src = src ? src = src ?
this.parse(src, state) await this.parse(src, state)
: src : src
return this.macros.include.call(this, return this.macros.include.call(this,
'source', 'source',
args, body, state, 'sources', args, body, state, 'sources',
function(){ async function(){
return this.parse(this.get(src).raw +'', state) }) }), return await this.parse(await this.get(src).raw +'', state) }) }),
// //
// @quote(<src>) // @quote(<src>)
// //
@ -1951,18 +1969,18 @@ object.Constructor('Page', BasePage, {
// XXX need a way to escape macros -- i.e. include </quote> in a quoted text... // XXX need a way to escape macros -- i.e. include </quote> in a quoted text...
quote: Macro( quote: Macro(
['src', 'filter', 'text'], ['src', 'filter', 'text'],
function(args, body, state){ async function(args, body, state){
var src = args.src //|| args[0] var src = args.src //|| args[0]
var text = args.text var text = args.text
?? body ?? body
?? [] ?? []
// parse arg values... // parse arg values...
src = src ? src = src ?
this.parse(src, state) await this.parse(src, state)
: src : src
text = src ? text = src ?
// source page... // source page...
this.get(src).raw await this.get(src).raw
: text instanceof Array ? : text instanceof Array ?
text.join('') text.join('')
: text : text
@ -2027,7 +2045,7 @@ object.Constructor('Page', BasePage, {
// ...seems that we'll fall into recursion on definition... // ...seems that we'll fall into recursion on definition...
slot: Macro( slot: Macro(
['name', 'text', ['shown', 'hidden']], ['name', 'text', ['shown', 'hidden']],
function(args, body, state){ async function(args, body, state){
var name = args.name var name = args.name
var text = args.text var text = args.text
?? body ?? body
@ -2041,7 +2059,7 @@ object.Constructor('Page', BasePage, {
// parse arg values... // parse arg values...
name = name ? name = name ?
this.parse(name, state) await this.parse(name, state)
: name : name
//var hidden = name in slots //var hidden = name in slots
@ -2055,7 +2073,7 @@ object.Constructor('Page', BasePage, {
// show first instance... // show first instance...
: name in slots) : name in slots)
slots[name] = [...this.__parser__.expand(this, text, state)] slots[name] = [...await this.__parser__.expand(this, text, state)]
return hidden ? return hidden ?
'' ''
@ -2095,7 +2113,7 @@ object.Constructor('Page', BasePage, {
// ...does not work yet... // ...does not work yet...
macro: Macro( macro: Macro(
['name', 'src', 'sort', 'text', 'join', 'else', ['strict', 'nonstrict']], ['name', 'src', 'sort', 'text', 'join', 'else', ['strict', 'nonstrict']],
function(args, body, state){ async function(args, body, state){
var that = this var that = this
var name = args.name //?? args[0] var name = args.name //?? args[0]
var src = args.src var src = args.src
@ -2134,7 +2152,7 @@ object.Constructor('Page', BasePage, {
return block } return block }
if(name){ if(name){
name = this.parse(name, state) name = await this.parse(name, state)
// define new named macro... // define new named macro...
if(text){ if(text){
;(state.macros = state.macros ?? {})[name] = text ;(state.macros = state.macros ?? {})[name] = text
@ -2144,24 +2162,24 @@ object.Constructor('Page', BasePage, {
text = state.macros[name] } } text = state.macros[name] } }
if(src){ if(src){
src = this.parse(src, state) src = await this.parse(src, state)
/* XXX ARRAY page... /* XXX ARRAY page...
var pages = this.get(src, strict).each() var pages = this.get(src, strict).each()
/*/ /*/
var pages = this.get(src, strict) var pages = this.get(src, strict)
pages = pages.isArray ? pages = await pages.isArray ?
// XXX should we wrap this in pages... // XXX should we wrap this in pages...
pages.raw (await pages.raw)
.map(function(data){ .map(function(data){
return that.virtual({text: data}) }) return that.virtual({text: data}) })
: pages.each() : await pages.each()
//*/ //*/
// no matching pages -> get the else block... // no matching pages -> get the else block...
if(pages.length == 0 if(pages.length == 0
&& (text || args['else'])){ && (text || args['else'])){
var else_block = _getBlock('else') var else_block = _getBlock('else')
return else_block ? return else_block ?
[...this.__parser__.expand(this, else_block, state)] [...await this.__parser__.expand(this, else_block, state)]
: undefined } : undefined }
// sort pages... // sort pages...
@ -2172,14 +2190,25 @@ object.Constructor('Page', BasePage, {
var join_block = _getBlock('join') var join_block = _getBlock('join')
// apply macro text... // apply macro text...
return pages return Promise.iter(pages)
.map(function(page, i){ .map(async function(page, i){
//* XXX really ugly...
var res = []
for await (var c of that.__parser__.expand(page, text, state)){
res.push(c) }
if(join_block && i < pages.length-1){
for await (var c of that.__parser__.expand(page, join_block, state)){
res.push(c) } }
return res
/*/
return [ return [
...that.__parser__.expand(page, text, state), ...await that.__parser__.expand(page, text, state),
...((join_block && i < pages.length-1) ? ...((join_block && i < pages.length-1) ?
that.__parser__.expand(page, join_block, state) await that.__parser__.expand(page, join_block, state)
: []), : []),
] }) ]
//*/
})
.flat() } }), .flat() } }),
// nesting rules... // nesting rules...
@ -2204,7 +2233,8 @@ object.Constructor('Page', BasePage, {
// page parser... // page parser...
// //
__parser__: module.parser, __parser__: module.parser,
parse: function(text, state){ parse: async function(text, state){
var that = this
// .parser(<state>) // .parser(<state>)
if(arguments.length == 1 if(arguments.length == 1
&& text instanceof Object && text instanceof Object
@ -2212,10 +2242,12 @@ object.Constructor('Page', BasePage, {
state = text state = text
text = null } text = null }
state = state ?? {} state = state ?? {}
text = text ?? this.raw text = text
?? await this.raw
return text instanceof Array ? return text instanceof Array ?
text.map(function(text){ Promise.iter(text)
return this.__parser__.parse(this, text, state) }.bind(this)) .map(function(text){
return that.__parser__.parse(that, text, state) })
: this.__parser__.parse(this, text, state) }, : this.__parser__.parse(this, text, state) },
// true if page has an array value but is not a pattern page... // true if page has an array value but is not a pattern page...
@ -2223,19 +2255,19 @@ object.Constructor('Page', BasePage, {
// XXX the split into pattern and array pages feels a bit overcomplicated... // XXX the split into pattern and array pages feels a bit overcomplicated...
// ...can we merge the two and simplify things??? // ...can we merge the two and simplify things???
// XXX EXPERIMENTAL // XXX EXPERIMENTAL
get isArray(){ get isArray(){ return (async function(){
return !this.isPattern return !this.isPattern
// NOTE: we can't only use .data here as it can be a function // NOTE: we can't only use .data here as it can be a function
// that will return an array... // that will return an array...
&& this.raw instanceof Array }, && await this.raw instanceof Array }).call(this) },
// raw page text... // raw page text...
// //
// NOTE: writing to .raw is the same as writing to .text... // NOTE: writing to .raw is the same as writing to .text...
// NOTE: when matching multiple pages this will return a list... // NOTE: when matching multiple pages this will return a list...
get raw(){ get raw(){ return (async function(){
var that = this var that = this
var data = this.data var data = await this.data
// no data... // no data...
// NOTE: if we hit this it means that nothing was resolved, // NOTE: if we hit this it means that nothing was resolved,
// not even the System/NotFound page, i.e. something // not even the System/NotFound page, i.e. something
@ -2261,7 +2293,7 @@ object.Constructor('Page', BasePage, {
d.call(that) d.call(that)
: d.text }) : d.text })
.flat() .flat()
: data.text }, : data.text }).call(this) },
set raw(value){ set raw(value){
this.__update__({text: value}) }, this.__update__({text: value}) },
//this.onTextUpdate(value) }, //this.onTextUpdate(value) },
@ -2270,14 +2302,14 @@ object.Constructor('Page', BasePage, {
// //
// NOTE: this uses .PAGE_TPL to render the page. // NOTE: this uses .PAGE_TPL to render the page.
// NOTE: writing to .raw is the same as writing to .text... // NOTE: writing to .raw is the same as writing to .text...
get text(){ get text(){ return (async function(){
var tpl = '/'+ this.find('./'+ this.PAGE_TPL) var tpl = '/'+ await this.find('./'+ this.PAGE_TPL)
return [this.parse( return [await this.parse(
tpl.endsWith(this.PAGE_TPL.split(/[\\\/]/).pop()) ? tpl.endsWith(this.PAGE_TPL.split(/[\\\/]/).pop()) ?
[this.get(tpl).raw] [await this.get(tpl).raw]
: [] )] : [] )]
.flat() .flat()
.join('\n') }, .join('\n') }).call(this) },
set text(value){ set text(value){
this.__update__({text: value}) }, this.__update__({text: value}) },
//this.onTextUpdate(value) }, //this.onTextUpdate(value) },