mirror of
https://github.com/flynx/pWiki.git
synced 2026-10-03 04:41:39 +00:00
Compare commits
4 Commits
a59ddf61a9
...
31b22be60b
| Author | SHA1 | Date | |
|---|---|---|---|
| 31b22be60b | |||
| 5f64d70fbb | |||
| d24257d3e2 | |||
| c7be62a48f |
@ -730,27 +730,21 @@ module.BaseParser = {
|
|||||||
|
|
||||||
// resolve stage II macros and merge results...
|
// resolve stage II macros and merge results...
|
||||||
//
|
//
|
||||||
// XXX DNF
|
// <ast> ::= [ <item>, ... ]
|
||||||
|
// <item> ::=
|
||||||
|
// <basic-value>
|
||||||
|
// | <elem>
|
||||||
//
|
//
|
||||||
// XXX these yeild different results:
|
// <elem> is returned if its value is not resolved yet.
|
||||||
// r = parser.resolve(
|
//
|
||||||
// tests.P,
|
//
|
||||||
// '<slot X>@include(/async/page)</slot> <slot X value>',
|
// NOTE: to fully resolve the ast this may need to be called several
|
||||||
// s = {})
|
// times...
|
||||||
// s.wait.then(function(){
|
//
|
||||||
// console.log(r) }) // -> ['Page '] (err)
|
//
|
||||||
// and:
|
// XXX can we prevent reaces over state.unresolved???
|
||||||
// r = parser.expand(
|
// it can be deleted when calling .parse(..) / .parseNested(..)
|
||||||
// tests.P,
|
// while parsing, for example from within a macro...
|
||||||
// '<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={}, nested_handlers={}){
|
resolve: function(page, ast, state={}, nested_handlers={}){
|
||||||
var that = this
|
var that = this
|
||||||
ast = typeof(ast) != 'object' ?
|
ast = typeof(ast) != 'object' ?
|
||||||
@ -760,8 +754,6 @@ module.BaseParser = {
|
|||||||
: ast.iter()
|
: ast.iter()
|
||||||
|
|
||||||
// merge resolved elements into the last item of elems...
|
// 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 = []
|
var elems = []
|
||||||
for(var elem of ast){
|
for(var elem of ast){
|
||||||
// nesting...
|
// nesting...
|
||||||
@ -769,9 +761,9 @@ module.BaseParser = {
|
|||||||
// exec stage II macros...
|
// exec stage II macros...
|
||||||
if(typeof(elem.value) == 'function'){
|
if(typeof(elem.value) == 'function'){
|
||||||
let e = elem
|
let e = elem
|
||||||
// NOTE: if not everything is resolved, delay the stage II
|
|
||||||
// callbacks till .wait is done...
|
|
||||||
Promise.awaitOrRun(
|
Promise.awaitOrRun(
|
||||||
|
// if not everything is resolved, delay the stage II
|
||||||
|
// callbacks till .wait is done...
|
||||||
state.wait,
|
state.wait,
|
||||||
function(){
|
function(){
|
||||||
return elem = e.value =
|
return elem = e.value =
|
||||||
@ -791,24 +783,29 @@ module.BaseParser = {
|
|||||||
continue }
|
continue }
|
||||||
// expand ast...
|
// expand ast...
|
||||||
if(elem instanceof Array){
|
if(elem instanceof Array){
|
||||||
// XXX this can be or containe promises...
|
|
||||||
elems.push(...that.resolve(page, elem, state))
|
elems.push(...that.resolve(page, elem, state))
|
||||||
continue }
|
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...
|
// nested macro with no value set -- skip...
|
||||||
if(that.macros[elem.name] instanceof Array){
|
if(that.macros[elem.name] instanceof Array){
|
||||||
continue }
|
continue }
|
||||||
// unresolved...
|
;(state.unresolved ??= [])
|
||||||
|
.push(elem.resolving instanceof Promise ?
|
||||||
|
elem.resolving
|
||||||
|
: elem)
|
||||||
|
//*/
|
||||||
|
// NOTE: we do not need to expand .body attributes as these
|
||||||
|
// are the responsibility of the respective macros...
|
||||||
elems.push(elem) }
|
elems.push(elem) }
|
||||||
|
|
||||||
return elems },
|
return elems },
|
||||||
|
|
||||||
|
isResolved: function(ast){
|
||||||
|
if(!(ast instanceof Array)){
|
||||||
|
return false }
|
||||||
|
for(var e of ast){
|
||||||
|
if(typeof(e) == 'object'){
|
||||||
|
return false } }
|
||||||
|
return true },
|
||||||
|
|
||||||
// XXX render api...
|
// XXX render api...
|
||||||
// XXX how should this play with filters???
|
// XXX how should this play with filters???
|
||||||
@ -817,18 +814,37 @@ module.BaseParser = {
|
|||||||
// XXX
|
// XXX
|
||||||
},
|
},
|
||||||
|
|
||||||
// XXX
|
|
||||||
parse: function(page, ast, state={}, nested_handlers={}, wait='wait'){
|
parse: function(page, ast, state={}, nested_handlers={}, wait='wait'){
|
||||||
var that = this
|
var that = this
|
||||||
var reresolve = !!state.wait
|
// XXX might be a good idea to limit recursion depth here...
|
||||||
|
var merge = function(ast){
|
||||||
|
return Promise.awaitOrRun(
|
||||||
|
...(state.unresolved ?? []),
|
||||||
|
function(){
|
||||||
|
delete state.unresolved
|
||||||
|
// re-resolve...
|
||||||
|
ast = that.resolve(page, ast, state, nested_handlers)
|
||||||
|
|
||||||
|
// NOTE: this is essentially running in the same frame
|
||||||
|
// as .resolve(..) above so there should not be
|
||||||
|
// any races to delete .unresolved...
|
||||||
|
return state.unresolved ?
|
||||||
|
merge(ast)
|
||||||
|
: (ast ?? '').join('') }) }
|
||||||
|
|
||||||
return Promise.awaitOrRun(
|
return Promise.awaitOrRun(
|
||||||
this.resolve(page, ast, state, nested_handlers),
|
this.resolve(page, ast, state, nested_handlers),
|
||||||
state[wait],
|
state[wait],
|
||||||
function(ast, reresolve){
|
function(ast){
|
||||||
ast = reresolve ?
|
// NOTE: in an async world where any promised macro can
|
||||||
that.resolve(page, ast, state, nested_handlers)
|
// call .parse(..) / .parseNested(..) we can't trust
|
||||||
: ast
|
// the lack of .unresolved in state...
|
||||||
return (ast ?? '').join('') }) },
|
return (state.unresolved
|
||||||
|
|| !that.isResolved(ast)) ?
|
||||||
|
merge(ast)
|
||||||
|
: (ast ?? '').join('') }) },
|
||||||
|
|
||||||
|
|
||||||
// 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={}, nested_handlers={}){
|
parseNested: function(page, ast, state={}, nested_handlers={}){
|
||||||
@ -1292,12 +1308,11 @@ module.parser = {
|
|||||||
recursive ??= ''
|
recursive ??= ''
|
||||||
|
|
||||||
var base = page.basepath
|
var base = page.basepath
|
||||||
// XXX we do this before or after we parse???
|
|
||||||
var src = page.resolvePathVars(args.src)
|
|
||||||
|
|
||||||
return Promise.awaitOrRun(
|
return Promise.awaitOrRun(
|
||||||
this.parseNested(page, src, state),
|
this.parseNested(page, args.src, state),
|
||||||
function(src){
|
function(src){
|
||||||
|
//src = page.resolvePathVars(src)
|
||||||
|
|
||||||
// XXX should this be a tree??
|
// XXX should this be a tree??
|
||||||
// ...need to at least split direct and
|
// ...need to at least split direct and
|
||||||
// indirect dependencies...
|
// indirect dependencies...
|
||||||
@ -1335,19 +1350,21 @@ module.parser = {
|
|||||||
|
|
||||||
// XXX check cache???
|
// XXX check cache???
|
||||||
|
|
||||||
|
var nested
|
||||||
var res = args.isolated ?
|
var res = args.isolated ?
|
||||||
this.resolve(
|
this.resolve(
|
||||||
page,
|
page,
|
||||||
text,
|
text,
|
||||||
args.isolated == 'partial' ?
|
nested = args.isolated == 'partial' ?
|
||||||
serialize.partialDeepCopy(state)
|
serialize.partialDeepCopy(state)
|
||||||
: {})
|
: {})
|
||||||
: this.expand(page, text, state)
|
: this.expand(page, text, state)
|
||||||
|
|
||||||
|
// handle recursion...
|
||||||
Promise.awaitOrRun(
|
Promise.awaitOrRun(
|
||||||
|
(nested ?? {}).waitAll,
|
||||||
state.waitAll,
|
state.waitAll,
|
||||||
function(){
|
function(){
|
||||||
// handle recursion...
|
|
||||||
state.include_stack.at(-1) == src
|
state.include_stack.at(-1) == src
|
||||||
&& state.include_stack.pop()
|
&& state.include_stack.pop()
|
||||||
// cleanup...
|
// cleanup...
|
||||||
@ -1413,7 +1430,7 @@ module.parser = {
|
|||||||
var that = this
|
var that = this
|
||||||
return this.macros['include'].call(this,
|
return this.macros['include'].call(this,
|
||||||
page, args, body, state,
|
page, args, body, state,
|
||||||
function(page, src, text, state){
|
function(page, body, path, text, state){
|
||||||
return that.expand(page, text, state) }) }),
|
return that.expand(page, text, state) }) }),
|
||||||
|
|
||||||
// Load macro and slot definitions but ignore the page text...
|
// Load macro and slot definitions but ignore the page text...
|
||||||
@ -1424,7 +1441,8 @@ module.parser = {
|
|||||||
function(page, args, body, state){
|
function(page, args, body, state){
|
||||||
var that = this
|
var that = this
|
||||||
return Promise.awaitOrRun(
|
return Promise.awaitOrRun(
|
||||||
this.macros['include'].call(this, page, args, body, state),
|
this.macros['include'].call(this,
|
||||||
|
page, {src: args.src}, body, state),
|
||||||
function(){
|
function(){
|
||||||
return '' }) }),
|
return '' }) }),
|
||||||
|
|
||||||
@ -1467,6 +1485,7 @@ module.parser = {
|
|||||||
args.src
|
args.src
|
||||||
&& this.parseNested(page, args.src, state),
|
&& this.parseNested(page, args.src, state),
|
||||||
function(src){
|
function(src){
|
||||||
|
//src = page.resolvePathVars(src)
|
||||||
var text =
|
var text =
|
||||||
src ?
|
src ?
|
||||||
page.get(src).raw
|
page.get(src).raw
|
||||||
@ -1476,20 +1495,6 @@ module.parser = {
|
|||||||
return Promise.awaitOrRun(
|
return Promise.awaitOrRun(
|
||||||
text,
|
text,
|
||||||
function(text){
|
function(text){
|
||||||
// XXX not sure I like that this has two "modes"...
|
|
||||||
text = src && body ?
|
|
||||||
// XXX do we need to account for generators???
|
|
||||||
(text instanceof Array ?
|
|
||||||
text
|
|
||||||
: [text])
|
|
||||||
.map(function(text){
|
|
||||||
return that.expand(
|
|
||||||
page,
|
|
||||||
that.ast(body.join(''), false, 'quote'),
|
|
||||||
state,
|
|
||||||
{ content: function(){
|
|
||||||
return text }, }) })
|
|
||||||
: text
|
|
||||||
return that.joinBlocks(
|
return that.joinBlocks(
|
||||||
page,
|
page,
|
||||||
that.filterBlocks(
|
that.filterBlocks(
|
||||||
@ -1564,40 +1569,20 @@ module.parser = {
|
|||||||
// get macro...
|
// get macro...
|
||||||
} else if(name){
|
} else if(name){
|
||||||
body = (state.macros ?? {})[name] }
|
body = (state.macros ?? {})[name] }
|
||||||
|
|
||||||
|
// else...
|
||||||
|
if(args.src
|
||||||
|
&& !page.get(args.src).exists()){
|
||||||
|
for(var elem of body){
|
||||||
|
if(elem.name == 'else'){
|
||||||
|
body = elem.body
|
||||||
|
return that.expand(page.get(args.src), elem.body, state) } } }
|
||||||
|
|
||||||
return args.src && body ?
|
return args.src && body ?
|
||||||
// run macro...
|
// run macro...
|
||||||
that.macros.include.call(that, page, args, body, state,
|
that.macros.include.call(that, page, args, body, state,
|
||||||
function(page, body, path, text, state){
|
function(page, body, path, text, state){
|
||||||
var that = this
|
return this.expand(page.get(path), body, state) })
|
||||||
|
|
||||||
var handle = function(page, text, state){
|
|
||||||
return args.isolated ?
|
|
||||||
that.resolve(
|
|
||||||
page,
|
|
||||||
text,
|
|
||||||
Object.assign(
|
|
||||||
args.isolated == 'partial' ?
|
|
||||||
serialize.partialDeepCopy(state)
|
|
||||||
: {},
|
|
||||||
{include_stack: state.include_stack ?? []}))
|
|
||||||
: that.expand(page, text, state) }
|
|
||||||
|
|
||||||
//var content_handled = false
|
|
||||||
return body ?
|
|
||||||
// handle body / <content/>...
|
|
||||||
Promise.awaitOrRun(
|
|
||||||
that.expand(page.get(path), body, state,
|
|
||||||
{ content: function(){
|
|
||||||
content_handled = true
|
|
||||||
return text = handle(page, text, state) } }),
|
|
||||||
function(text){
|
|
||||||
// if no <content/> present we still
|
|
||||||
// need to handle the included page...
|
|
||||||
//content_handled
|
|
||||||
// || handle.call(that, page, text, state)
|
|
||||||
return text })
|
|
||||||
// place as-is...
|
|
||||||
: handle(page, text, state) })
|
|
||||||
: '' }) })),
|
: '' }) })),
|
||||||
|
|
||||||
// nesting rules...
|
// nesting rules...
|
||||||
|
|||||||
@ -56,6 +56,9 @@ module.exports.P = {
|
|||||||
get matched(){
|
get matched(){
|
||||||
var path_action_pattern = /\/path\/?/
|
var path_action_pattern = /\/path\/?/
|
||||||
var path = this.path.replace(path_action_pattern, '')
|
var path = this.path.replace(path_action_pattern, '')
|
||||||
|
path = path == '' ?
|
||||||
|
'/'
|
||||||
|
: path
|
||||||
var path_action = path != this.path
|
var path_action = path != this.path
|
||||||
|
|
||||||
if(path.includes('*')){
|
if(path.includes('*')){
|
||||||
@ -72,17 +75,27 @@ module.exports.P = {
|
|||||||
: [] })
|
: [] })
|
||||||
.flat() }
|
.flat() }
|
||||||
return [this.path] },
|
return [this.path] },
|
||||||
|
exists: function(path){
|
||||||
|
var that = this
|
||||||
|
return (path ?
|
||||||
|
this.get(path).matched
|
||||||
|
: this.matched)
|
||||||
|
.filter(function(p){
|
||||||
|
return p in that.__pages__ })
|
||||||
|
.length > 0 },
|
||||||
|
|
||||||
// * -> a promise or a list of promises???
|
// * -> a promise or a list of promises???
|
||||||
get raw(){
|
get raw(){
|
||||||
var res = []
|
var res = []
|
||||||
var path_action_pattern = /\/path\/?/
|
var path_action_pattern = /\/path\/?/
|
||||||
var path_action = this.path.match(path_action_pattern)
|
var path_action = path_action_pattern.test(this.path)
|
||||||
|
|
||||||
for(var path of this.matched){
|
for(var path of this.matched){
|
||||||
res.push(
|
res.push(
|
||||||
path_action ?
|
path_action ?
|
||||||
path.replace(path_action_pattern, '')
|
((path = path.replace(path_action_pattern, '')) == '' ?
|
||||||
|
'/'
|
||||||
|
: path)
|
||||||
: this.__pages__[path]) }
|
: this.__pages__[path]) }
|
||||||
|
|
||||||
return res.length == 1 ?
|
return res.length == 1 ?
|
||||||
@ -315,13 +328,7 @@ test.Setups({
|
|||||||
|
|
||||||
// quote...
|
// quote...
|
||||||
// for inline quoting see: test.Modifiers.quote
|
// for inline quoting see: test.Modifiers.quote
|
||||||
// XXX <quote src=.. />
|
// XXX
|
||||||
quote_content: function(assert, path='/isolated'){
|
|
||||||
return {
|
|
||||||
page: P,
|
|
||||||
code: [
|
|
||||||
'<quote "'+ path +'">[[ <content/> ]]</quote>',
|
|
||||||
'[[ '+ P.get(path).raw +' ]]', ], } },
|
|
||||||
|
|
||||||
// XXX macro...
|
// XXX macro...
|
||||||
// XXX
|
// XXX
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user