mirror of
https://github.com/flynx/pWiki.git
synced 2026-10-03 12:51:40 +00:00
Compare commits
No commits in common. "e8b0cb435ca333264b5a31422ca909f69a1c5dfa" and "46d768e4e1572e725463cb38cf81e49b582f72e2" have entirely different histories.
e8b0cb435c
...
46d768e4e1
@ -247,36 +247,6 @@ module.BaseParser = {
|
|||||||
?? args.text }
|
?? args.text }
|
||||||
return macro.call(this, page, args, body, state, ...rest) },
|
return macro.call(this, page, args, body, state, ...rest) },
|
||||||
|
|
||||||
// place join block between block elements...
|
|
||||||
joinBlocks: function(page, blocks, join, state){
|
|
||||||
var that = this
|
|
||||||
if(typeof(blocks) == 'string'
|
|
||||||
|| join == null){
|
|
||||||
return blocks }
|
|
||||||
// we do not need to rebuild the ast for each use...
|
|
||||||
// XXX this can break things -- need to store the parse stage
|
|
||||||
// in the structure so as to determine where we left off...
|
|
||||||
// ...the problem is that we can't tell the difference
|
|
||||||
// between a parsed and expanded stages -- one way, re-running
|
|
||||||
// a stage is not an issue but the other way, skipping can
|
|
||||||
// break...
|
|
||||||
//join = this.ast(page, join, state)
|
|
||||||
return blocks
|
|
||||||
.map(function(block, i, l){
|
|
||||||
return [
|
|
||||||
block,
|
|
||||||
...(i < l.length-1 ?
|
|
||||||
that.expand(page, join, state)
|
|
||||||
: []),
|
|
||||||
] })
|
|
||||||
.flat() },
|
|
||||||
filterBlocks: function(page, blocks, filter, state){
|
|
||||||
if(filter == null){
|
|
||||||
return blocks }
|
|
||||||
|
|
||||||
// XXX
|
|
||||||
|
|
||||||
return blocks },
|
|
||||||
|
|
||||||
// Strip comments...
|
// Strip comments...
|
||||||
//
|
//
|
||||||
@ -313,7 +283,7 @@ module.BaseParser = {
|
|||||||
//
|
//
|
||||||
// NOTE: this internally uses .macros' keys to generate the
|
// NOTE: this internally uses .macros' keys to generate the
|
||||||
// lexing pattern.
|
// lexing pattern.
|
||||||
lex: function*(str){
|
lex: function*(page, str){
|
||||||
str = typeof(str) != 'string' ?
|
str = typeof(str) != 'string' ?
|
||||||
str+''
|
str+''
|
||||||
: str
|
: str
|
||||||
@ -442,9 +412,9 @@ 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*(lex, to=false, parent, context){
|
group: function*(page, lex, to=false, parent, context){
|
||||||
lex = typeof(lex) != 'object' ?
|
lex = typeof(lex) != 'object' ?
|
||||||
this.lex(lex)
|
this.lex(page, lex)
|
||||||
: lex
|
: lex
|
||||||
|
|
||||||
var quoting = to
|
var quoting = to
|
||||||
@ -496,8 +466,8 @@ module.BaseParser = {
|
|||||||
|
|
||||||
// open block...
|
// open block...
|
||||||
if(value.type == 'opening'){
|
if(value.type == 'opening'){
|
||||||
//value.body = [...this.group(lex, value.name)]
|
//value.body = [...this.group(page, lex, value.name)]
|
||||||
value.body = [...this.group(lex, value.name, value)]
|
value.body = [...this.group(page, lex, value.name, value)]
|
||||||
value.type = 'block'
|
value.type = 'block'
|
||||||
|
|
||||||
// unify .body, .args.body and .args.text into .body...
|
// unify .body, .args.body and .args.text into .body...
|
||||||
@ -507,6 +477,7 @@ module.BaseParser = {
|
|||||||
?? value.args.text)){
|
?? value.args.text)){
|
||||||
value.body =
|
value.body =
|
||||||
[...this.group(
|
[...this.group(
|
||||||
|
page,
|
||||||
value.args.body
|
value.args.body
|
||||||
?? value.args.text,
|
?? value.args.text,
|
||||||
false,
|
false,
|
||||||
@ -600,7 +571,7 @@ module.BaseParser = {
|
|||||||
expand: function(page, ast, state={}){
|
expand: function(page, ast, state={}){
|
||||||
var that = this
|
var that = this
|
||||||
ast = typeof(ast) != 'object' ?
|
ast = typeof(ast) != 'object' ?
|
||||||
this.group(ast)
|
this.group(page, ast)
|
||||||
: ast instanceof types.Generator ?
|
: ast instanceof types.Generator ?
|
||||||
ast
|
ast
|
||||||
: ast.iter()
|
: ast.iter()
|
||||||
@ -636,7 +607,6 @@ module.BaseParser = {
|
|||||||
continue }
|
continue }
|
||||||
|
|
||||||
// expand down...
|
// expand down...
|
||||||
// XXX cache this as an AST
|
|
||||||
body
|
body
|
||||||
&& !that.macros[name].lazy
|
&& !that.macros[name].lazy
|
||||||
&& (body = this.expand(page, body, state))
|
&& (body = this.expand(page, body, state))
|
||||||
@ -710,8 +680,7 @@ module.BaseParser = {
|
|||||||
|
|
||||||
// resolve stage II macros and merge results...
|
// resolve stage II macros and merge results...
|
||||||
//
|
//
|
||||||
// XXX DNF
|
/* XXX
|
||||||
//
|
|
||||||
// XXX these yeild different results:
|
// XXX these yeild different results:
|
||||||
// r = parser.resolve(
|
// r = parser.resolve(
|
||||||
// tests.P,
|
// tests.P,
|
||||||
@ -731,6 +700,84 @@ module.BaseParser = {
|
|||||||
// XXX it looks like the first slot is resolved before the last
|
// 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
|
// slot has a chance to set the value as it is waiting for
|
||||||
// the first slot to finish...
|
// the first slot to finish...
|
||||||
|
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...
|
||||||
|
var elems = []
|
||||||
|
var merge = function(...args){
|
||||||
|
var prev = ''
|
||||||
|
while(args.length > 0){
|
||||||
|
while(args.length > 0
|
||||||
|
&& typeof(args[0]) != 'object'
|
||||||
|
&& typeof(args[0]) != 'function'){
|
||||||
|
prev += args.shift() }
|
||||||
|
// merged section...
|
||||||
|
if(prev.length > 0){
|
||||||
|
if(elems.length > 0
|
||||||
|
&& typeof(elems.at(-1)) == 'string'){
|
||||||
|
elems[elems.length-1] += prev
|
||||||
|
} else {
|
||||||
|
elems.push(prev) }
|
||||||
|
prev = '' }
|
||||||
|
if(args.length > 0){
|
||||||
|
elems.push(args.shift()) } } }
|
||||||
|
|
||||||
|
// 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 elems can be unresolved -- need a merge strategy for them...
|
||||||
|
//var waiting = []
|
||||||
|
for(var elem of ast){
|
||||||
|
// nesting...
|
||||||
|
while(elem && elem.value){
|
||||||
|
elem = elem.value
|
||||||
|
// exec stage II macros...
|
||||||
|
// XXX this should be called when all the promises before
|
||||||
|
// are resolved...
|
||||||
|
if(typeof(elem) == 'function'){
|
||||||
|
elem = elem(state) } }
|
||||||
|
if(elem == null){
|
||||||
|
continue }
|
||||||
|
// atomic values...
|
||||||
|
if(typeof(elem) != 'object'){
|
||||||
|
merge(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...
|
||||||
|
merge(...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...
|
||||||
|
merge(elem) }
|
||||||
|
|
||||||
|
return elems }) },
|
||||||
|
//*/
|
||||||
resolve: function(page, ast, state={}){
|
resolve: function(page, ast, state={}){
|
||||||
var that = this
|
var that = this
|
||||||
ast = typeof(ast) != 'object' ?
|
ast = typeof(ast) != 'object' ?
|
||||||
@ -1081,10 +1128,6 @@ function(macro){
|
|||||||
return macro }
|
return macro }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// XXX RENAME...
|
// XXX RENAME...
|
||||||
// ...this is more of an expander/executer...
|
// ...this is more of an expander/executer...
|
||||||
// ...might be a good idea to also do a check without executing...
|
// ...might be a good idea to also do a check without executing...
|
||||||
@ -1465,8 +1508,6 @@ module.parser = {
|
|||||||
// not 100% correct manner focusing on path depth and ignoring
|
// not 100% correct manner focusing on path depth and ignoring
|
||||||
// the context, this potentially can lead to false positives.
|
// the context, this potentially can lead to false positives.
|
||||||
//
|
//
|
||||||
// XXX might be a good idea to add a <content/> tag to place the
|
|
||||||
// loaded text...
|
|
||||||
// XXX add path recursion test to data -- fail if two paths resolve
|
// XXX add path recursion test to data -- fail if two paths resolve
|
||||||
// to the same context...
|
// to the same context...
|
||||||
// XXX need a way to make encode option transparent...
|
// XXX need a way to make encode option transparent...
|
||||||
@ -1492,16 +1533,10 @@ module.parser = {
|
|||||||
function(page, args, body, state, handler){
|
function(page, args, body, state, handler){
|
||||||
var that = this
|
var that = this
|
||||||
|
|
||||||
/* XXX see .joinBlocks(..) join caching for more info...
|
|
||||||
// cache body ast...
|
|
||||||
body = body ?
|
|
||||||
this.ast(body)
|
|
||||||
: body
|
|
||||||
//*/
|
|
||||||
|
|
||||||
var recursive =
|
var recursive =
|
||||||
state.recursive =
|
state.recursive =
|
||||||
args.recursive
|
args.recursive
|
||||||
|
?? body
|
||||||
?? state.recursive
|
?? state.recursive
|
||||||
|
|
||||||
var base = page.basepath
|
var base = page.basepath
|
||||||
@ -1511,20 +1546,6 @@ module.parser = {
|
|||||||
return Promise.awaitOrRun(
|
return Promise.awaitOrRun(
|
||||||
this.parseNested(page, src, state),
|
this.parseNested(page, src, state),
|
||||||
function(src){
|
function(src){
|
||||||
// check for recursion...
|
|
||||||
// XXX do we do this for pattern paths???
|
|
||||||
// XXX this does not catch the /A/A/A/... recursion...
|
|
||||||
var stack = state.include_stack ??= []
|
|
||||||
// XXX is this the right separator???
|
|
||||||
// ...need something that can't be in a path...
|
|
||||||
var base_src = base +'|'+ src
|
|
||||||
if(stack.includes(base_src)){
|
|
||||||
if(recursive){
|
|
||||||
return that.expand(page, recursive, state) }
|
|
||||||
throw new Error('Recursion:\n\t'+
|
|
||||||
[...stack, base_src].join('\n\t\t-> ')) }
|
|
||||||
stack.push(base_src)
|
|
||||||
|
|
||||||
var cache = state.cache ??= {}
|
var cache = state.cache ??= {}
|
||||||
if(cache[src]){
|
if(cache[src]){
|
||||||
return cache[src] }
|
return cache[src] }
|
||||||
@ -1539,6 +1560,20 @@ module.parser = {
|
|||||||
// out of context...
|
// out of context...
|
||||||
var depends = ((state.depends ??= {})[src] ??= {})
|
var depends = ((state.depends ??= {})[src] ??= {})
|
||||||
|
|
||||||
|
// check for recursion...
|
||||||
|
// XXX do we do this for pattern paths???
|
||||||
|
// XXX this does not catch the /A/A/A/... recursion...
|
||||||
|
var stack = state.include_stack ??= []
|
||||||
|
// XXX is this the right separator???
|
||||||
|
// ...need something that can't be in a path...
|
||||||
|
var base_src = base +'|'+ src
|
||||||
|
if(stack.includes(base_src)){
|
||||||
|
if(recursive){
|
||||||
|
return recursive }
|
||||||
|
throw new Error('Recursion:\n\t'+
|
||||||
|
[...stack, base_src].join('\n\t\t-> ')) }
|
||||||
|
stack.push(base_src)
|
||||||
|
|
||||||
// content handler...
|
// content handler...
|
||||||
handler ??=
|
handler ??=
|
||||||
function(page, text, state){
|
function(page, text, state){
|
||||||
@ -1556,14 +1591,21 @@ module.parser = {
|
|||||||
: this.expand(page, text, state)}
|
: this.expand(page, text, state)}
|
||||||
|
|
||||||
var pageHandler =
|
var pageHandler =
|
||||||
function(text){
|
function(text, i, l){
|
||||||
// XXX handle body / <content/>...
|
return [
|
||||||
// XXX
|
handler.call(that, page, text, state),
|
||||||
return handler.call(that, page, text, state) }
|
// join...
|
||||||
|
(args.join
|
||||||
|
&& i < l.length - 1) ?
|
||||||
|
that.expand(page, args.join, state)
|
||||||
|
: [],
|
||||||
|
].flat() }
|
||||||
var resultHandler =
|
var resultHandler =
|
||||||
function(pages){
|
function(pages){
|
||||||
state.include_stack.at(-1) == base_src
|
// XXX not sure if this can happen or why...
|
||||||
&& state.include_stack.pop()
|
if(state.include_stack.at(-1) != base_src){
|
||||||
|
throw new Error('Include stack error') }
|
||||||
|
state.include_stack.pop()
|
||||||
// cleanup...
|
// cleanup...
|
||||||
if(state.include_stack.length == 0){
|
if(state.include_stack.length == 0){
|
||||||
delete state.include_stack
|
delete state.include_stack
|
||||||
@ -1582,12 +1624,8 @@ module.parser = {
|
|||||||
return Promise.awaitOrRun(
|
return Promise.awaitOrRun(
|
||||||
// handle pages...
|
// handle pages...
|
||||||
Promise
|
Promise
|
||||||
.iter(
|
.iter(pages
|
||||||
that.joinBlocks(
|
.map( pageHandler ))
|
||||||
page,
|
|
||||||
pages.map(pageHandler),
|
|
||||||
args.join,
|
|
||||||
state))
|
|
||||||
.flat()
|
.flat()
|
||||||
.sync(),
|
.sync(),
|
||||||
resultHandler ) }) }) })),
|
resultHandler ) }) }) })),
|
||||||
@ -1599,7 +1637,9 @@ module.parser = {
|
|||||||
// i.e. for @include(PATH) the paths within the included page
|
// i.e. for @include(PATH) the paths within the included page
|
||||||
// are resolved relative to PATH while for @source(PATH)
|
// are resolved relative to PATH while for @source(PATH)
|
||||||
// relative to the page containing the @source(..) statement...
|
// relative to the page containing the @source(..) statement...
|
||||||
|
// XXX UPDATE...
|
||||||
source: Macro(
|
source: Macro(
|
||||||
|
// XXX should this have the same args as include???
|
||||||
['src', 'recursive', 'join',
|
['src', 'recursive', 'join',
|
||||||
['s', 'strict']],
|
['s', 'strict']],
|
||||||
//['src'],
|
//['src'],
|
||||||
@ -1637,6 +1677,7 @@ module.parser = {
|
|||||||
function(src){
|
function(src){
|
||||||
var text =
|
var text =
|
||||||
src ?
|
src ?
|
||||||
|
// can be async...
|
||||||
page.get(src).raw
|
page.get(src).raw
|
||||||
: body ?
|
: body ?
|
||||||
body
|
body
|
||||||
@ -1644,15 +1685,13 @@ module.parser = {
|
|||||||
return Promise.awaitOrRun(
|
return Promise.awaitOrRun(
|
||||||
text,
|
text,
|
||||||
function(text){
|
function(text){
|
||||||
return that.joinBlocks(
|
// filter...
|
||||||
page,
|
if(args.filter){
|
||||||
that.filterBlocks(
|
// XXX
|
||||||
page,
|
}
|
||||||
text,
|
// XXX join...
|
||||||
args.filter,
|
// XXX
|
||||||
state),
|
return text }) }) })),
|
||||||
args.join,
|
|
||||||
state) }) }) })),
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// @quote(<src>)
|
// @quote(<src>)
|
||||||
|
|||||||
@ -17,16 +17,7 @@ module.exports.PAGES = {
|
|||||||
'/page': 'Page',
|
'/page': 'Page',
|
||||||
'/async/page': Promise.resolve('Page'),
|
'/async/page': Promise.resolve('Page'),
|
||||||
'/includePage': '@include(/page)',
|
'/includePage': '@include(/page)',
|
||||||
'/isolated': '@slot(slot original)',
|
'/isolated': '@slot(slot original)'
|
||||||
|
|
||||||
'/recursive/Self': '<< @include(/recursive/Self) >>',
|
|
||||||
'/recursive/OtherSelf': '<< @include(/recursive/SelfOther) >>',
|
|
||||||
'/recursive/SelfOther': '<< @include(/recursive/OtherSelf) >>',
|
|
||||||
'/async/recursive/Self': Promise.resolve('<< @include(/async/recursive/Self) >>'),
|
|
||||||
'/async/recursive/OtherSelf': Promise.resolve('<< @include(/async/recursive/SelfOther) >>'),
|
|
||||||
'/async/recursive/SelfOther': Promise.resolve('<< @include(/async/recursive/OtherSelf) >>'),
|
|
||||||
|
|
||||||
'/multi/page': [ 'A', 'B', 'C' ],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var P =
|
var P =
|
||||||
@ -225,26 +216,8 @@ test.Setups({
|
|||||||
'original overloaded',
|
'original overloaded',
|
||||||
],
|
],
|
||||||
} },
|
} },
|
||||||
// recursion...
|
// XXX recursion...
|
||||||
// XXX test path recursion: /A -> /A/A -> /A/A/A -> ...
|
// XXX
|
||||||
include_recursive_a: function(assert){
|
|
||||||
return {
|
|
||||||
page: P,
|
|
||||||
code:[
|
|
||||||
'@include(/recursive/Self recursive="recursion found")',
|
|
||||||
'@include(/async/recursive/Self recursive="recursion found")',
|
|
||||||
'<< recursion found >>', ], } },
|
|
||||||
include_recursive_b: function(assert){
|
|
||||||
return {
|
|
||||||
page: P,
|
|
||||||
code:[
|
|
||||||
'@include(/recursive/SelfOther recursive="recursion found")',
|
|
||||||
'@include(/async/recursive/SelfOther recursive="recursion found")',
|
|
||||||
'<< << recursion found >> >>', ], } },
|
|
||||||
|
|
||||||
// quote...
|
|
||||||
// for inline quoting see: test.Modifiers.quote
|
|
||||||
// XXX <quote src=.. />
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@ -273,49 +246,32 @@ test.Modifiers({
|
|||||||
`[[ ${state.code.at(-1)} ]]`,
|
`[[ ${state.code.at(-1)} ]]`,
|
||||||
]
|
]
|
||||||
return state },
|
return state },
|
||||||
quote: function(assert, state){
|
|
||||||
return state.code
|
|
||||||
.slice(0, -1)
|
|
||||||
.map(function(code){
|
|
||||||
code = code.replace(/<\/quote>/, '</quote>')
|
|
||||||
return {
|
|
||||||
page: state.P,
|
|
||||||
code: [
|
|
||||||
`<quote>${ code }</quote>`,
|
|
||||||
code,
|
|
||||||
],
|
|
||||||
} }) },
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
test.Tests({
|
test.Tests({
|
||||||
parse: async function(assert, state){
|
parse: async function(assert, state){
|
||||||
var states =
|
var {page, code, st} = state
|
||||||
state instanceof Array ?
|
page ??= {}
|
||||||
state
|
st ??= {}
|
||||||
: [state]
|
|
||||||
for(state of states){
|
|
||||||
var {page, code, st} = state
|
|
||||||
page ??= {}
|
|
||||||
st ??= {}
|
|
||||||
|
|
||||||
var res
|
var res
|
||||||
var inputs = code.slice(0, -1)
|
var inputs = code.slice(0, -1)
|
||||||
var expect = code.at(-1)
|
var expect = code.at(-1)
|
||||||
var i = 0
|
var i = 0
|
||||||
for(var input of inputs){
|
for(var input of inputs){
|
||||||
var p = serialize.partialDeepCopy(page)
|
var p = serialize.partialDeepCopy(page)
|
||||||
var s = serialize.partialDeepCopy(st)
|
var s = serialize.partialDeepCopy(st)
|
||||||
assert(
|
assert(
|
||||||
(res = await parser.parse(
|
(res = await parser.parse(
|
||||||
p,
|
p,
|
||||||
input,
|
input,
|
||||||
s))
|
s))
|
||||||
=== expect,
|
=== expect,
|
||||||
'Parsing:',
|
'Parsing:',
|
||||||
'\n\t in: "'+ input +'"',
|
'\n\t in: "'+ input +'"',
|
||||||
'\n\t out: "'+ res +'"',
|
'\n\t out: "'+ res +'"',
|
||||||
'\n\texpected: "'+ expect +'"') } } },
|
'\n\texpected: "'+ expect +'"') } },
|
||||||
//asyncParse: async function(assert, state){
|
//asyncParse: async function(assert, state){
|
||||||
// return await this.parse(assert, state) },
|
// return await this.parse(assert, state) },
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user