mirror of
https://github.com/flynx/pWiki.git
synced 2026-08-24 08:16:43 +00:00
added tests + cleaning up @slot(..)
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
f2a47de99f
commit
b9b11ecbda
@ -11,7 +11,9 @@
|
||||
"ig-actions": "*",
|
||||
"ig-features": "*",
|
||||
"ig-object": "*",
|
||||
"ig-types": "*",
|
||||
"ig-types": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ig-test": "*"
|
||||
},
|
||||
"disabled-dependencies": {
|
||||
|
||||
@ -229,7 +229,9 @@ module.BaseParser = {
|
||||
macro.arg_spec
|
||||
?? [],
|
||||
args),
|
||||
body,
|
||||
// XXX
|
||||
body
|
||||
?? args.body,
|
||||
state,
|
||||
...rest) },
|
||||
|
||||
@ -256,7 +258,12 @@ module.BaseParser = {
|
||||
// args: {
|
||||
// <index>: <value>,
|
||||
// <key>: <value>,
|
||||
//
|
||||
// ...
|
||||
//
|
||||
// // special case: .body argument's value is treated in
|
||||
// // the same way as block body -- it is parsed.
|
||||
// body: <ast>,
|
||||
// }
|
||||
// match: <string>,
|
||||
// }
|
||||
@ -335,6 +342,10 @@ 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
|
||||
@ -384,7 +395,7 @@ module.BaseParser = {
|
||||
//
|
||||
// NOTE: this internaly uses .macros to check for propper nesting
|
||||
//group: function*(page, lex, to=false){
|
||||
group: function*(page, lex, to=false, parent){
|
||||
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)
|
||||
@ -421,14 +432,28 @@ 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...
|
||||
if(this.macros[value.name] instanceof Array
|
||||
// stray nesting...
|
||||
&& (context
|
||||
&& !this.macros[value.name].includes(context))
|
||||
// stray nesting/closing...
|
||||
&& !this.macros[value.name].includes(to)
|
||||
// do not complain about closing nestable tags...
|
||||
&& !(value.name == to
|
||||
&& value.type == 'closing')){
|
||||
&& !(value.name == to
|
||||
&& value.type == 'closing') ){
|
||||
throw new Error(
|
||||
'Unexpected <'+ value.name +'> macro'
|
||||
+(to ?
|
||||
@ -556,6 +581,12 @@ module.BaseParser = {
|
||||
&& typeof(that.macros[name]) != 'string'){
|
||||
continue }
|
||||
|
||||
// expand down...
|
||||
body
|
||||
&& (body = this.expand(page, body, state))
|
||||
;(args ?? {}).body
|
||||
&& (args.body = this.expand(page, args.body, state))
|
||||
|
||||
// cleanup...
|
||||
elem = {...elem}
|
||||
delete elem.error
|
||||
@ -646,11 +677,13 @@ module.BaseParser = {
|
||||
|
||||
for(var elem of ast){
|
||||
// nesting...
|
||||
while(elem.value){
|
||||
while(elem && elem.value){
|
||||
elem = elem.value
|
||||
// exec stage II macros...
|
||||
if(typeof(elem) == 'function'){
|
||||
elem = elem(state) } }
|
||||
elem = elem(state) } }
|
||||
if(elem == null){
|
||||
continue }
|
||||
// atomic values...
|
||||
if(typeof(elem) != 'object'){
|
||||
merge(elem)
|
||||
@ -662,8 +695,12 @@ module.BaseParser = {
|
||||
continue }
|
||||
// expand ast...
|
||||
if(elem instanceof Array){
|
||||
//merge(...this.resolve(page, elem, state))
|
||||
merge(...this.resolve(page, elem, state))
|
||||
continue }
|
||||
// expand .body attribute...
|
||||
if((elem.attrs ?? {}).body instanceof Array){
|
||||
elem.attrs.body = this.resolve(page, elem.attrs.body, state) }
|
||||
// nested macro with no value set -- skip...
|
||||
if(this.macros[elem.name] instanceof Array){
|
||||
continue }
|
||||
@ -697,7 +734,8 @@ module.BaseParser = {
|
||||
if(ast.length > 1){
|
||||
throw new Error('!!!!')
|
||||
}
|
||||
return ast[0] },
|
||||
return ast[0]
|
||||
?? '' },
|
||||
|
||||
// XXX this can't be used from within macros -- will deadlock the results...
|
||||
// XXX how should this play with filters???
|
||||
@ -714,7 +752,8 @@ module.BaseParser = {
|
||||
if(ast.length > 1){
|
||||
throw new Error('!!!!')
|
||||
}
|
||||
return ast[0] },
|
||||
return ast[0]
|
||||
?? '' },
|
||||
|
||||
|
||||
|
||||
@ -1247,6 +1286,10 @@ module.parser = {
|
||||
// This also works for cases where slots override slots they
|
||||
// are contained in, this will not lead to recursion.
|
||||
//
|
||||
// XXX do we actually need <content/>??
|
||||
// <slot slot>
|
||||
// <slot slot.content/>
|
||||
// </slot>
|
||||
// XXX do we show a slot with unfilled content???
|
||||
// ...what's the point in having <content>, can't is just
|
||||
// be replaced by a slot?
|
||||
@ -1260,10 +1303,7 @@ module.parser = {
|
||||
function(parser, args, body, state){
|
||||
var name = args.name
|
||||
var text = args.text
|
||||
?? body
|
||||
// NOTE: this can't be undefined for .expand(..) to work
|
||||
// correctly...
|
||||
?? []
|
||||
|
||||
return Promise.awaitOrRun(
|
||||
parser.parseNested(this, name, state),
|
||||
function(name){
|
||||
@ -1281,19 +1321,28 @@ module.parser = {
|
||||
: name in slots)
|
||||
|
||||
// set slot value...
|
||||
// XXX simplify this...
|
||||
var stack = []
|
||||
slots[name]
|
||||
&& stack.push(slots[name])
|
||||
delete slots[name]
|
||||
var slot = parser.expand(this, text, state)
|
||||
text = text ?
|
||||
parser.expand(this, text ?? [], state)
|
||||
: text
|
||||
if(body && text){
|
||||
stack.push(body)
|
||||
var slot = text
|
||||
} else {
|
||||
var slot = body ?? text }
|
||||
var original = slot
|
||||
slots[name]
|
||||
&& stack.unshift(slot)
|
||||
slot = slots[name] ??= slot
|
||||
// handle <content/>...
|
||||
for(prev of stack){
|
||||
for(prev of stack.reverse()){
|
||||
// get the first <content/>
|
||||
// XXX this is a flat search, should be deep...
|
||||
// NOTE: this is a flat search because we can't
|
||||
// have indirect nesting (see: .group(..))
|
||||
for(var i in prev){
|
||||
if(typeof(prev[i]) != 'string'
|
||||
&& prev[i].name == 'content'){
|
||||
|
||||
189
v3/pwiki/test/parser.js
Executable file
189
v3/pwiki/test/parser.js
Executable file
@ -0,0 +1,189 @@
|
||||
#!/usr/bin/node
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
var test = require('ig-test')
|
||||
|
||||
var parser = require('../parser').parser
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
test.Setups({
|
||||
empty: function(assert){
|
||||
return [ '', '' ] },
|
||||
|
||||
slot_empty: function(assert){
|
||||
return [
|
||||
'@slot(slot)',
|
||||
'@slot("slot")',
|
||||
'@slot(\'slot\')',
|
||||
'@slot(name=slot)',
|
||||
'@slot(name="slot")',
|
||||
'@slot(name=\'slot\')',
|
||||
'<slot slot/>',
|
||||
'<slot "slot"/>',
|
||||
'<slot \'slot\'/>',
|
||||
'<slot name=slot/>',
|
||||
'<slot name="slot"/>',
|
||||
'<slot name=\'slot\'/>',
|
||||
'' ] },
|
||||
slot_value: function(assert){
|
||||
return [
|
||||
'<slot slot value/>',
|
||||
'<slot slot text=value/>',
|
||||
'@slot(slot value)',
|
||||
'@slot(slot text=value)',
|
||||
'value' ] },
|
||||
slot_fill: function(assert){
|
||||
var ins = this.slot_value(assert).slice(0, -1)
|
||||
return [
|
||||
...ins.map(function(e){
|
||||
return e + '@slot(slot other)' }),
|
||||
...ins.map(function(e){
|
||||
return e + '<slot slot other/>' }),
|
||||
...ins.map(function(e){
|
||||
return e + '<slot slot>other</slot>' }),
|
||||
'other' ] },
|
||||
slot_fill_fill: function(assert){
|
||||
var ins = this.slot_fill(assert).slice(0, -1)
|
||||
return [
|
||||
...ins.map(function(e){
|
||||
return e + '@slot(slot third)' }),
|
||||
...ins.map(function(e){
|
||||
return e + '<slot slot third/>' }),
|
||||
...ins.map(function(e){
|
||||
return e + '<slot slot>third</slot>' }),
|
||||
'third' ] },
|
||||
slot_content_empty: function(assert){
|
||||
return [
|
||||
'@slot(slot body="[[ <content/> ]]")',
|
||||
'<slot slot>[[ <content/> ]]</slot>',
|
||||
'[[ ]]' ] },
|
||||
slot_content_default: function(assert){
|
||||
return [
|
||||
'@slot(slot default body="[[ <content/> ]]")',
|
||||
'<slot slot default>[[ <content/> ]]</slot>',
|
||||
'[[ default ]]' ] },
|
||||
slot_content_fill: function(assert){
|
||||
var ins = this.slot_content_default(assert)
|
||||
var expect = ins.pop()
|
||||
return [
|
||||
...ins.map(function(i){
|
||||
return i +'@slot(slot value)' }),
|
||||
expect.replace('default', 'value') ] },
|
||||
|
||||
slot_nested: function(assert){
|
||||
return [
|
||||
'<slot slot>[[ <slot slot.content/> ]]</slot>@slot(slot.content value)',
|
||||
'[[ value ]]' ] },
|
||||
slot_nested_overwrite: function(assert){
|
||||
return [
|
||||
'<slot slot>[[ <slot slot.content/> ]]</slot>@slot(slot value)',
|
||||
'value' ] },
|
||||
|
||||
// XXX the question with the next tow is:
|
||||
// should body override or nest?
|
||||
// ...both are logical but in either case the result should be
|
||||
// consistent.
|
||||
//
|
||||
// XXX should the new body/content override or expand the original???
|
||||
slot_content_content: function(assert){
|
||||
var ins = this.slot_content_default(assert)
|
||||
var expect = ins.pop()
|
||||
return [
|
||||
...ins.map(function(i){
|
||||
return i +'@slot(slot body="(( @content() ))")' }),
|
||||
// XXX should this override the default above???
|
||||
//expect.replace('default', '(( default ))') ] },
|
||||
expect.replace('default', '(( ))') ] },
|
||||
slot_content_content_content: function(assert){
|
||||
var ins = this.slot_content_content(assert)
|
||||
var expect = ins.pop()
|
||||
return [
|
||||
...ins.map(function(i){
|
||||
return i +'@slot(slot body="<< @content() >>")' }),
|
||||
// XXX should this override the default above???
|
||||
//expect.replace('default', '(( default ))') ] },
|
||||
expect.replace('(( ))', '(( << >> ))') ] },
|
||||
// XXX if we are expanding (see above) why are we overriding here???
|
||||
slot_content_content_fill: function(assert){
|
||||
var ins = this.slot_content_content(assert)
|
||||
var expect = ins.pop()
|
||||
return [
|
||||
...ins.map(function(i){
|
||||
return i +'@slot(slot value)' }),
|
||||
expect.replace('(( ))', '(( value ))') ] },
|
||||
//expect.replace('(( ))', 'value') ] },
|
||||
//
|
||||
|
||||
|
||||
// XXX these need to be revised...
|
||||
// ...do we actually need hidden/shown???
|
||||
//
|
||||
slot_shown: function(assert){
|
||||
var ins = this.slot_value(assert)
|
||||
var expect = ins.pop()
|
||||
return [
|
||||
...ins.map(function(i){
|
||||
return i +' @slot(slot that shown)' }),
|
||||
'that that' ] },
|
||||
slot_hidden: function(assert){
|
||||
return [
|
||||
'<slot slot value hidden/>',
|
||||
'@slot(slot value hidden)',
|
||||
'' ] },
|
||||
slot_hidden_value: function(assert){
|
||||
var ins = this.slot_hidden(assert)
|
||||
var expect = ins.pop()
|
||||
return [
|
||||
...ins.map(function(i){
|
||||
return i +'@slot(slot other)' }),
|
||||
'' ] },
|
||||
slot_hidden_shown: function(assert){
|
||||
var ins = this.slot_hidden(assert)
|
||||
var expect = ins.pop()
|
||||
return [
|
||||
...ins.map(function(i){
|
||||
return i +'@slot(slot shown)' }),
|
||||
'' ] },
|
||||
})
|
||||
|
||||
|
||||
test.Modifiers({
|
||||
})
|
||||
|
||||
|
||||
test.Tests({
|
||||
parse: function(assert, state){
|
||||
var res
|
||||
var inputs = state.slice(0, -1)
|
||||
var expect = state.at(-1)
|
||||
var i = 0
|
||||
for(var input of inputs){
|
||||
assert(
|
||||
(res = parser.parse(
|
||||
{},
|
||||
input,
|
||||
{}))
|
||||
=== expect,
|
||||
'Parsing:',
|
||||
'\n\t in: "'+ input +'"',
|
||||
'\n\t out: "'+ res +'"',
|
||||
'\n\texpected: "'+ expect +'"') } },
|
||||
})
|
||||
|
||||
|
||||
test.Cases({
|
||||
})
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// make the test runnable as a standalone script...
|
||||
__filename == (require.main || {}).filename
|
||||
&& test.run()
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// vim:set ts=4 sw=4 :
|
||||
Loading…
x
Reference in New Issue
Block a user