diff --git a/v3/pwiki/parser.js b/v3/pwiki/parser.js
index 4908b72..43c2a48 100644
--- a/v3/pwiki/parser.js
+++ b/v3/pwiki/parser.js
@@ -9,6 +9,7 @@
var object = require('ig-object')
var types = require('ig-types')
+var serialize = require('ig-serialize')
var pwpath = require('./path')
@@ -219,19 +220,34 @@ module.BaseParser = {
(res[e] = true)
: (res[order.shift()] = e) })
return res },
+ // NOTE: this unifies the body, body argument and text argument (in
+ // order of priority) and passes the value in the body macro
+ // handler argument.
callMacro: function(page, macro, args, body, state, ...rest){
do {
macro = this.macros[macro]
} while(typeof(macro) == 'string')
+ var args =
+ this.parseArgs(
+ macro.arg_spec
+ ?? [],
+ args)
+ body = body == '' ?
+ undefined
+ : body
+ if(args.body
+ || args.text
+ || body){
+ body =
+ args.body =
+ args.text =
+ body
+ ?? args.body
+ ?? args.text }
return macro.call(page,
this,
- this.parseArgs(
- macro.arg_spec
- ?? [],
- args),
- // XXX
- body
- ?? args.body,
+ args,
+ body,
state,
...rest) },
@@ -342,10 +358,6 @@ module.BaseParser = {
?? groups.unnamedArg)
.replace(/\\(["'])/g, '$1') }
- // special case: .body arg -> lex...
- if(args.body){
- args.body = [...this.lex(page, args.body)] }
-
// macro-spec...
yield {
name: (cur.nameInline
@@ -393,12 +405,18 @@ module.BaseParser = {
// ...
// }
//
+ // Special arguments:
+ // .args.body | .args.text
+ // - if .body is given both arges are ignored and dropped
+ // - if .body is empty and one of the args is present it's
+ // content will be set as .body and grouped while the rest
+ // is dropped
+ // - priority order:
+ // .body -> .args.body -> .args.text
+ //
// NOTE: this internaly uses .macros to check for propper nesting
//group: function*(page, lex, to=false){
group: function*(page, lex, to=false, parent, context){
- // XXX we can't get .raw from the page without going async...
- //lex = lex
- // ?? this.lex(page)
lex = typeof(lex) != 'object' ?
this.lex(page, lex)
: lex
@@ -432,16 +450,6 @@ module.BaseParser = {
: value.match ) }
continue }
- // special case: .body argument -> group...
- if((value.args ?? {}).body){
- value.args.body =
- [...this.group(
- page,
- value.args.body.iter(),
- false,
- parent,
- value.name)] }
-
// assert nesting rules...
// NOTE: we only check for direct nesting...
// XXX might be a good idea to link nested block to the parent...
@@ -459,17 +467,37 @@ module.BaseParser = {
+(to ?
' in <'+to+'>'
: '')) }
+
// open block...
if(value.type == 'opening'){
//value.body = [...this.group(page, lex, value.name)]
value.body = [...this.group(page, lex, value.name, value)]
value.type = 'block'
+
+ // unify .body, .args.body and .args.text into .body...
+ // (first non-empty takes precedance, the rest are removed)
+ if(value.body.length == 0
+ && (value.args.body
+ ?? value.args.text)){
+ value.body =
+ [...this.group(
+ page,
+ value.args.body
+ ?? value.args.text,
+ false,
+ parent,
+ value.name)] }
+ delete value.args.body
+ delete value.args.text
+
// close block...
} else if(value.type == 'closing'){
if(value.name != to){
throw new Error('Unexpected '+ value.name +'>') }
// NOTE: we are intentionally not yielding the value here...
+ // ...this supports the above scan use-case.
return }
+
// normal value...
yield value } },
@@ -567,14 +595,21 @@ module.BaseParser = {
// do not re-expand expanded elements...
if('value' in elem
&& !state.forceReExpand){
- elems.push({...elem})
+ elems.push(serialize.partialDeepCopy(elem))
continue }
+ // cleanup...
+ //elem = {...elem}
+ elem = serialize.partialDeepCopy(elem)
+ delete elem.error
+ delete elem.value
+ //delete elem.resolving
+
var {name, args, body} = elem
// nested macro -- skip...
if(that.macros[name] instanceof Array){
- elems.push({...elem})
+ elems.push(elem)
continue }
// drop non-macros/aliases...
if(typeof(that.macros[name]) != 'function'
@@ -583,15 +618,8 @@ module.BaseParser = {
// expand down...
body
+ && !that.macros[name].lazy
&& (body = this.expand(page, body, state))
- ;(args ?? {}).body
- && (args.body = this.expand(page, args.body, state))
-
- // cleanup...
- elem = {...elem}
- delete elem.error
- delete elem.value
- //delete elem.resolving
// call macro...
var res = that.callMacro(page, name, args, body, state)
@@ -990,6 +1018,7 @@ module.BaseParser = {
// XXX do we need anything else like .doc, attrs???
// XXX might be a good idea to offload arg value parsing to here...
+// XXX should macros be lazy by default???
var Macro =
module.Macro =
function(spec, func){
@@ -1005,9 +1034,15 @@ function(spec, func){
var isolated =
module.isolated =
-function(func){
- func.isolated = true
- return func }
+function(macro){
+ macro.isolated = true
+ return macro }
+
+var lazy =
+module.lazy =
+function(macro){
+ macro.lazy = true
+ return macro }
// XXX RENAME...
@@ -1284,6 +1319,12 @@ module.parser = {
//
//
//
+ // NOTE: slots are expanded in order of occurance not in order
+ // of topology, thus nested can override slots they are
+ // nested in, e.g.:
+ // '[[ ]]'
+ // will resolve to:
+ // 'new value'
// NOTE: by default only the first slot with is visible,
// all other slots with will replace its content, unless
// explicit shown/hidden arguments are given.
@@ -1297,9 +1338,9 @@ module.parser = {
// once and just inseerted as-is (revise)
slot: Macro(
['name', 'text', ['shown', 'hidden']],
+ lazy(
function(parser, args, body, state){
var name = args.name
- var text = args.text
return Promise.awaitOrRun(
parser.parseNested(this, name, state),
@@ -1318,22 +1359,34 @@ module.parser = {
: name in slots)
// set slot value...
- text = text ?
- parser.expand(this, text ?? [], state)
- : text
- if(body && text){
- var slot = text
- } else {
- var slot = body ?? text }
- slots[name] = slot
+ delete slots[name]
+ body = body ?
+ parser.expand(this, body ?? [], state)
+ : body
+ // XXX BUG: this breaks into infinite recursion:
+ // '[[ ]]'
+ // -> err
+ // need to somehow break recursion here, if the
+ // nested slot had no/empty body...
+ // slots[name] ??= body ?? []
+ // fixes the recursion but also resets the
+ // parent value...
+ // need to meke the behavior of the following
+ // the saem:
+ // '[[ ]]'
+ // -> err
+ // and:
+ // '[[ ]] @slot(a)'
+ // -> '[[ ]]'
+ slots[name] ??= body
return hidden ?
''
: Object.assign(
function(st){
return ((st ?? state).slots ?? {})[name]
- ?? slot },
- {slot: name}) }) }),
+ ?? body },
+ {slot: name}) }) })),
//
@@ -1432,9 +1485,14 @@ module.parser = {
handler.call(that,
parser,
text,
- args.isolated ?
- // XXX clean (now), partial or derp-copy?
- {}
+ // isolated up -- will see all
+ // the state but can have no
+ // side-effects...
+ args.isolated == 'partial' ?
+ serialize.partialDeepCopy(state)
+ // fully isolated...
+ : args.isolated ?
+ {}
: state),
// join...
(args.join
diff --git a/v3/pwiki/test/parser.js b/v3/pwiki/test/parser.js
index a216e9b..3dd5a6a 100755
--- a/v3/pwiki/test/parser.js
+++ b/v3/pwiki/test/parser.js
@@ -56,14 +56,7 @@ test.Setups({
...ins.map(function(e){
return e + 'third' }),
'third' ]} },
- /* XXX not sure how...
- slot_recursion: function(assert){
- return {code: [
- ' ',
- '', ]} },
- //*/
- // XXX these are an alternative to ...
slot_nested: function(assert){
return {code: [
'[[ ]]@slot(slot.content value)',
@@ -73,11 +66,18 @@ test.Setups({
'[[ ]]@slot(slot value)',
'value' ]} },
- // XXX should this work???
+ // recursion...
slot_nested_nested: function(assert){
return {code: [
'[[ ]]',
+ '[[ ]]',
'value' ]} },
+ /*/ XXX recursion...
+ slot_nested_shown_nested: function(assert){
+ return {code: [
+ '[[ ]]',
+ '[[ ]]' ]} },
+ //*/
/* XXX SHOWN_HIDDEN
// XXX these need to be revised...