mirror of
https://github.com/flynx/pWiki.git
synced 2026-08-24 16:26:42 +00:00
reforked the exec flow, now async stuff works...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
e7dc0194ad
commit
cc94cdad03
@ -574,14 +574,6 @@ module.BaseParser = {
|
|||||||
ast
|
ast
|
||||||
: ast.iter()
|
: ast.iter()
|
||||||
|
|
||||||
var wait = new Set()
|
|
||||||
state.wait instanceof Promise
|
|
||||||
&& wait.add(state.wait)
|
|
||||||
var resolve, reject
|
|
||||||
state.wait = new Promise(
|
|
||||||
function(){
|
|
||||||
;[resolve, reject] = arguments })
|
|
||||||
|
|
||||||
var elems = []
|
var elems = []
|
||||||
for(let elem of ast){
|
for(let elem of ast){
|
||||||
// text block...
|
// text block...
|
||||||
@ -621,9 +613,10 @@ module.BaseParser = {
|
|||||||
var res = that.callMacro(page, name, args, body, state)
|
var res = that.callMacro(page, name, args, body, state)
|
||||||
// async...
|
// async...
|
||||||
if(res instanceof Promise){
|
if(res instanceof Promise){
|
||||||
|
let all, nested
|
||||||
|
all = state.waitAll =
|
||||||
|
Promise.all([state.waitAll, res])
|
||||||
elem.resolving = res
|
elem.resolving = res
|
||||||
wait.add(res)
|
|
||||||
state.waitAll = res
|
|
||||||
// XXX do we need to wait till the last .waitNested is
|
// XXX do we need to wait till the last .waitNested is
|
||||||
// resolved?
|
// resolved?
|
||||||
// ...should it's handlers complete??
|
// ...should it's handlers complete??
|
||||||
@ -632,13 +625,13 @@ module.BaseParser = {
|
|||||||
// func to the macro...
|
// func to the macro...
|
||||||
// ...do we need this?
|
// ...do we need this?
|
||||||
if(!res.isolated){
|
if(!res.isolated){
|
||||||
state.waitNested = res }
|
nested = state.waitNested =
|
||||||
|
Promise.all([state.waitNested, res]) }
|
||||||
res.then(
|
res.then(
|
||||||
function(value){
|
function(value){
|
||||||
wait.delete(res)
|
if(state.waitAll === all){
|
||||||
if(state.waitAll === res){
|
|
||||||
delete state.waitAll }
|
delete state.waitAll }
|
||||||
if(state.waitNested === res){
|
if(state.waitNested === nested){
|
||||||
delete state.waitNested }
|
delete state.waitNested }
|
||||||
delete elem.resolving
|
delete elem.resolving
|
||||||
elem.value = value
|
elem.value = value
|
||||||
@ -659,25 +652,52 @@ module.BaseParser = {
|
|||||||
elem = res }
|
elem = res }
|
||||||
elems.push(elem) }
|
elems.push(elem) }
|
||||||
|
|
||||||
// handle wait for isolated macros to resolve...
|
// cleanup...
|
||||||
var done = function(){
|
var wait
|
||||||
resolve(state)
|
var waitAll = state.waitAll
|
||||||
delete state.wait }
|
state.waitAll
|
||||||
if(wait.size > 0){
|
&& state.waitAll
|
||||||
Promise.all(wait)
|
.then(function(){
|
||||||
.then(
|
// only cleanup our own mess =)
|
||||||
done,
|
waitAll === state.waitAll
|
||||||
function(err){
|
&& (delete state.waitAll) })
|
||||||
reject(err) })
|
&& (wait = state.wait = waitAll
|
||||||
// done...
|
.then(function(){
|
||||||
} else {
|
wait === state.wait
|
||||||
done() }
|
&& (delete state.wait)
|
||||||
|
return elems }))
|
||||||
|
var waitNested = state.waitNested
|
||||||
|
state.waitNested
|
||||||
|
&& state.waitNested
|
||||||
|
.then(function(){
|
||||||
|
// only cleanup our own mess =)
|
||||||
|
waitNested === state.waitNested
|
||||||
|
&& (delete state.waitNested) })
|
||||||
|
|
||||||
return elems },
|
return elems },
|
||||||
|
|
||||||
// resolve stage II macros and merge results...
|
// resolve stage II macros and merge results...
|
||||||
//
|
//
|
||||||
// XXX BUG: sync/async paths seem to diverge...
|
/* XXX
|
||||||
|
// XXX these yeild different results:
|
||||||
|
// r = parser.resolve(
|
||||||
|
// tests.P,
|
||||||
|
// '<slot X>@include(/async/page)</slot> <slot X value>',
|
||||||
|
// s = {})
|
||||||
|
// s.wait.then(function(){
|
||||||
|
// console.log(r) }) // -> ['Page '] (err)
|
||||||
|
// and:
|
||||||
|
// r = parser.expand(
|
||||||
|
// tests.P,
|
||||||
|
// '<slot X>@include(/async/page)</slot> <slot X slot/>',
|
||||||
|
// s = {})
|
||||||
|
// s.wait.then(function(){
|
||||||
|
// r = parser.resolve(test.P, r, s)
|
||||||
|
// console.log(r) }) // -> ['slot '] (correct)
|
||||||
|
// -> promises seem to not be sequenced correctly here...
|
||||||
|
// XXX it looks like the first slot is resolved before the last
|
||||||
|
// slot has a chance to set the value as it is waiting for
|
||||||
|
// the first slot to finish...
|
||||||
resolve: function(page, ast, state={}){
|
resolve: function(page, ast, state={}){
|
||||||
var that = this
|
var that = this
|
||||||
ast = typeof(ast) != 'object' ?
|
ast = typeof(ast) != 'object' ?
|
||||||
@ -705,26 +725,29 @@ module.BaseParser = {
|
|||||||
prev = '' }
|
prev = '' }
|
||||||
if(args.length > 0){
|
if(args.length > 0){
|
||||||
elems.push(args.shift()) } } }
|
elems.push(args.shift()) } } }
|
||||||
var unpack = function(elem){
|
|
||||||
// XXX
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// XXX can we make this more granular and wait only where we need
|
||||||
|
// to wait?
|
||||||
|
// ....the wait is logical as we need to return a string here
|
||||||
|
// eventually.
|
||||||
|
// should this be split into .resolve(..) nad .merge(..)???
|
||||||
|
return Promise.awaitOrRun(
|
||||||
|
state.wait,
|
||||||
|
function(){
|
||||||
// XXX can ast be a promise???
|
// XXX can ast be a promise???
|
||||||
// XXX elems can be unresolved -- need a merge strategy for them...
|
// XXX elems can be unresolved -- need a merge strategy for them...
|
||||||
|
//var waiting = []
|
||||||
for(var elem of ast){
|
for(var elem of ast){
|
||||||
// nesting...
|
// nesting...
|
||||||
while(elem && elem.value){
|
while(elem && elem.value){
|
||||||
elem = elem.value
|
elem = elem.value
|
||||||
// exec stage II macros...
|
// exec stage II macros...
|
||||||
|
// XXX this should be called when all the promises before
|
||||||
|
// are resolved...
|
||||||
if(typeof(elem) == 'function'){
|
if(typeof(elem) == 'function'){
|
||||||
elem = elem(state) } }
|
elem = elem(state) } }
|
||||||
if(elem == null){
|
if(elem == null){
|
||||||
continue }
|
continue }
|
||||||
// XXX do a delayed merge...
|
|
||||||
// ...but for this we need to also apply the rest of this iteration to the result...
|
|
||||||
if(elem.resolving){
|
|
||||||
console.log('!!!!!!!!!!!!!!!!!!!')
|
|
||||||
}
|
|
||||||
// atomic values...
|
// atomic values...
|
||||||
if(typeof(elem) != 'object'){
|
if(typeof(elem) != 'object'){
|
||||||
merge(elem)
|
merge(elem)
|
||||||
@ -736,61 +759,105 @@ module.BaseParser = {
|
|||||||
continue }
|
continue }
|
||||||
// expand ast...
|
// expand ast...
|
||||||
if(elem instanceof Array){
|
if(elem instanceof Array){
|
||||||
// XXX this can containe promises...
|
// XXX this can be or containe promises...
|
||||||
merge(...this.resolve(page, elem, state))
|
merge(...that.resolve(page, elem, state))
|
||||||
continue }
|
continue }
|
||||||
// expand .body attribute...
|
// expand .body attribute...
|
||||||
if((elem.attrs ?? {}).body instanceof Array){
|
// XXX is this needed here???
|
||||||
// XXX this can containe promises...
|
if(elem.body instanceof Array){
|
||||||
elem.attrs.body = this.resolve(page, elem.attrs.body, state) }
|
console.warn('!!! RESOLVE_BODY')
|
||||||
|
// XXX this can be or containe promises...
|
||||||
|
elem.body = that.resolve(page, elem.body, state) }
|
||||||
// nested macro with no value set -- skip...
|
// nested macro with no value set -- skip...
|
||||||
if(this.macros[elem.name] instanceof Array){
|
if(that.macros[elem.name] instanceof Array){
|
||||||
continue }
|
continue }
|
||||||
// unresolved...
|
// unresolved...
|
||||||
merge(elem) }
|
merge(elem) }
|
||||||
|
|
||||||
|
return elems }) },
|
||||||
|
//*/
|
||||||
|
resolve: function(page, ast, state={}){
|
||||||
|
var that = this
|
||||||
|
ast = typeof(ast) != 'object' ?
|
||||||
|
this.expand(page, ast, state)
|
||||||
|
: ast instanceof types.Generator ?
|
||||||
|
ast
|
||||||
|
: ast.iter()
|
||||||
|
|
||||||
|
// merge resolved elements into the last item of elems...
|
||||||
|
// XXX can ast be a promise???
|
||||||
|
// XXX elems can be unresolved -- need a merge strategy for them...
|
||||||
|
var elems = []
|
||||||
|
for(var elem of ast){
|
||||||
|
// nesting...
|
||||||
|
while(elem && elem.value){
|
||||||
|
// exec stage II macros...
|
||||||
|
if(typeof(elem.value) == 'function'){
|
||||||
|
let e = elem
|
||||||
|
// NOTE: if not everything is resolved, delay the stage II
|
||||||
|
// callbacks till .wait is done...
|
||||||
|
Promise.awaitOrRun(
|
||||||
|
state.wait,
|
||||||
|
function(){
|
||||||
|
return elem = e.value =
|
||||||
|
e.value(state) })
|
||||||
|
break }
|
||||||
|
elem = elem.value }
|
||||||
|
if(elem == null){
|
||||||
|
continue }
|
||||||
|
// atomic values...
|
||||||
|
if(typeof(elem) != 'object'){
|
||||||
|
elems.push(elem)
|
||||||
|
continue }
|
||||||
|
// value is resolved but "empty" -> skip...
|
||||||
|
if('value' in elem
|
||||||
|
&& (elem.value == null
|
||||||
|
|| elem.value == '')){
|
||||||
|
continue }
|
||||||
|
// expand ast...
|
||||||
|
if(elem instanceof Array){
|
||||||
|
// XXX this can be or containe promises...
|
||||||
|
elems.push(...that.resolve(page, elem, state))
|
||||||
|
continue }
|
||||||
|
// expand .body attribute...
|
||||||
|
/* XXX is this needed here???
|
||||||
|
if(elem.body instanceof Array){
|
||||||
|
console.warn('!!! RESOLVE_BODY')
|
||||||
|
// XXX this can be or containe promises...
|
||||||
|
elem.body = that.resolve(page, elem.body, state) }
|
||||||
|
//*/
|
||||||
|
// nested macro with no value set -- skip...
|
||||||
|
if(that.macros[elem.name] instanceof Array){
|
||||||
|
continue }
|
||||||
|
// unresolved...
|
||||||
|
elems.push(elem) }
|
||||||
|
|
||||||
return elems },
|
return elems },
|
||||||
|
|
||||||
|
|
||||||
// XXX render api...
|
// XXX render api...
|
||||||
// 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??
|
||||||
render: function*(page, ast, callback, state={}){
|
render: function*(page, ast, callback, state={}){
|
||||||
ast = this.resolve(page, ast, state)
|
// XXX
|
||||||
for(var elem of ast){
|
},
|
||||||
yield Promise.awaitOrRun(
|
|
||||||
elem,
|
|
||||||
callback) } },
|
|
||||||
|
|
||||||
// XXX
|
// XXX
|
||||||
_parse: function(page, ast, state={}){
|
parse: function(page, ast, state={}, wait='wait'){
|
||||||
|
var that = this
|
||||||
|
var reresolve = !!state.wait
|
||||||
return Promise.awaitOrRun(
|
return Promise.awaitOrRun(
|
||||||
this.resolve(page, ast, state),
|
this.resolve(page, ast, state),
|
||||||
function(ast){
|
state[wait],
|
||||||
// XXX
|
function(ast, reresolve){
|
||||||
if(ast.length > 1){
|
ast = reresolve ?
|
||||||
throw new Error('!!!!')
|
that.resolve(page, ast, state)
|
||||||
}
|
: ast
|
||||||
return ast[0]
|
return (ast ?? '').join('') }) },
|
||||||
?? '' }) },
|
|
||||||
// XXX
|
|
||||||
// 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??
|
||||||
parseNested: function(page, ast, state={}){
|
parseNested: function(page, ast, state={}){
|
||||||
return Promise.awaitOrRun(
|
return this.parse(page, ast, state, 'waitNestedi') },
|
||||||
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 how should this play with filters???
|
|
||||||
// ...should filters be client-side only??
|
|
||||||
parse: function(page, ast, state={}){
|
|
||||||
return Promise.awaitOrRun(
|
|
||||||
state.wait,
|
|
||||||
this._parse(page, ast, state),
|
|
||||||
function(_, ast){
|
|
||||||
return ast }) },
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1505,7 +1572,7 @@ module.parser = {
|
|||||||
// content handler...
|
// content handler...
|
||||||
handler ??=
|
handler ??=
|
||||||
function(page, text, state){
|
function(page, text, state){
|
||||||
return this._parse(page, text, state) }
|
return this.expand(page, text, state) }
|
||||||
|
|
||||||
var pageHandler =
|
var pageHandler =
|
||||||
function(text, i, l){
|
function(text, i, l){
|
||||||
@ -1525,7 +1592,7 @@ module.parser = {
|
|||||||
// join...
|
// join...
|
||||||
(args.join
|
(args.join
|
||||||
&& i < l.length - 1) ?
|
&& i < l.length - 1) ?
|
||||||
that._parse(page, args.join, state)
|
that.expand(page, args.join, state)
|
||||||
: []
|
: []
|
||||||
].flat() }
|
].flat() }
|
||||||
var resultHandler =
|
var resultHandler =
|
||||||
|
|||||||
@ -181,7 +181,6 @@ test.Setups({
|
|||||||
// XXX
|
// XXX
|
||||||
|
|
||||||
// include...
|
// include...
|
||||||
// XXX these do not play well with modifiers...
|
|
||||||
include: function(assert, path='/blank', expected){
|
include: function(assert, path='/blank', expected){
|
||||||
return {
|
return {
|
||||||
page: P,
|
page: P,
|
||||||
@ -190,6 +189,8 @@ test.Setups({
|
|||||||
// XXX for some reason this does not parse int a macro....
|
// XXX for some reason this does not parse int a macro....
|
||||||
//'<include /blank />',
|
//'<include /blank />',
|
||||||
`<include "${path}" />`,
|
`<include "${path}" />`,
|
||||||
|
`<include src="${path}" />`,
|
||||||
|
|
||||||
expected
|
expected
|
||||||
?? P.get(path).raw,
|
?? P.get(path).raw,
|
||||||
],
|
],
|
||||||
@ -198,10 +199,8 @@ test.Setups({
|
|||||||
return this.include(assert, '/page') },
|
return this.include(assert, '/page') },
|
||||||
include_include_page: function(assert){
|
include_include_page: function(assert){
|
||||||
return this.include(assert, '/includePage', 'Page') },
|
return this.include(assert, '/includePage', 'Page') },
|
||||||
/* XXX parser.resolve(..) does not handle promises correctly yet...
|
|
||||||
include_async: function(assert){
|
include_async: function(assert){
|
||||||
return this.include(assert, '/async/page') },
|
return this.include(assert, '/async/page', 'Page') },
|
||||||
//*/
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@ -234,7 +233,7 @@ test.Modifiers({
|
|||||||
|
|
||||||
|
|
||||||
test.Tests({
|
test.Tests({
|
||||||
parse: function(assert, state){
|
parse: async function(assert, state){
|
||||||
var {page, code, st} = state
|
var {page, code, st} = state
|
||||||
page ??= {}
|
page ??= {}
|
||||||
st ??= {}
|
st ??= {}
|
||||||
@ -247,7 +246,7 @@ test.Tests({
|
|||||||
var p = serialize.partialDeepCopy(page)
|
var p = serialize.partialDeepCopy(page)
|
||||||
var s = serialize.partialDeepCopy(st)
|
var s = serialize.partialDeepCopy(st)
|
||||||
assert(
|
assert(
|
||||||
(res = parser.parse(
|
(res = await parser.parse(
|
||||||
p,
|
p,
|
||||||
input,
|
input,
|
||||||
s))
|
s))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user