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-actions": "*",
|
||||||
"ig-features": "*",
|
"ig-features": "*",
|
||||||
"ig-object": "*",
|
"ig-object": "*",
|
||||||
"ig-types": "*",
|
"ig-types": "*"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
"ig-test": "*"
|
"ig-test": "*"
|
||||||
},
|
},
|
||||||
"disabled-dependencies": {
|
"disabled-dependencies": {
|
||||||
|
|||||||
@ -229,7 +229,9 @@ module.BaseParser = {
|
|||||||
macro.arg_spec
|
macro.arg_spec
|
||||||
?? [],
|
?? [],
|
||||||
args),
|
args),
|
||||||
body,
|
// XXX
|
||||||
|
body
|
||||||
|
?? args.body,
|
||||||
state,
|
state,
|
||||||
...rest) },
|
...rest) },
|
||||||
|
|
||||||
@ -256,7 +258,12 @@ module.BaseParser = {
|
|||||||
// args: {
|
// args: {
|
||||||
// <index>: <value>,
|
// <index>: <value>,
|
||||||
// <key>: <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>,
|
// match: <string>,
|
||||||
// }
|
// }
|
||||||
@ -335,6 +342,10 @@ module.BaseParser = {
|
|||||||
?? groups.unnamedArg)
|
?? groups.unnamedArg)
|
||||||
.replace(/\\(["'])/g, '$1') }
|
.replace(/\\(["'])/g, '$1') }
|
||||||
|
|
||||||
|
// special case: .body arg -> lex...
|
||||||
|
if(args.body){
|
||||||
|
args.body = [...this.lex(page, args.body)] }
|
||||||
|
|
||||||
// macro-spec...
|
// macro-spec...
|
||||||
yield {
|
yield {
|
||||||
name: (cur.nameInline
|
name: (cur.nameInline
|
||||||
@ -384,7 +395,7 @@ 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*(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...
|
// XXX we can't get .raw from the page without going async...
|
||||||
//lex = lex
|
//lex = lex
|
||||||
// ?? this.lex(page)
|
// ?? this.lex(page)
|
||||||
@ -421,14 +432,28 @@ module.BaseParser = {
|
|||||||
: value.match ) }
|
: value.match ) }
|
||||||
continue }
|
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...
|
// assert nesting rules...
|
||||||
// NOTE: we only check for direct nesting...
|
// NOTE: we only check for direct nesting...
|
||||||
// XXX might be a good idea to link nested block to the parent...
|
// XXX might be a good idea to link nested block to the parent...
|
||||||
if(this.macros[value.name] instanceof Array
|
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)
|
&& !this.macros[value.name].includes(to)
|
||||||
// do not complain about closing nestable tags...
|
// do not complain about closing nestable tags...
|
||||||
&& !(value.name == to
|
&& !(value.name == to
|
||||||
&& value.type == 'closing')){
|
&& value.type == 'closing') ){
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Unexpected <'+ value.name +'> macro'
|
'Unexpected <'+ value.name +'> macro'
|
||||||
+(to ?
|
+(to ?
|
||||||
@ -556,6 +581,12 @@ module.BaseParser = {
|
|||||||
&& typeof(that.macros[name]) != 'string'){
|
&& typeof(that.macros[name]) != 'string'){
|
||||||
continue }
|
continue }
|
||||||
|
|
||||||
|
// expand down...
|
||||||
|
body
|
||||||
|
&& (body = this.expand(page, body, state))
|
||||||
|
;(args ?? {}).body
|
||||||
|
&& (args.body = this.expand(page, args.body, state))
|
||||||
|
|
||||||
// cleanup...
|
// cleanup...
|
||||||
elem = {...elem}
|
elem = {...elem}
|
||||||
delete elem.error
|
delete elem.error
|
||||||
@ -646,11 +677,13 @@ module.BaseParser = {
|
|||||||
|
|
||||||
for(var elem of ast){
|
for(var elem of ast){
|
||||||
// nesting...
|
// nesting...
|
||||||
while(elem.value){
|
while(elem && elem.value){
|
||||||
elem = elem.value
|
elem = elem.value
|
||||||
// exec stage II macros...
|
// exec stage II macros...
|
||||||
if(typeof(elem) == 'function'){
|
if(typeof(elem) == 'function'){
|
||||||
elem = elem(state) } }
|
elem = elem(state) } }
|
||||||
|
if(elem == null){
|
||||||
|
continue }
|
||||||
// atomic values...
|
// atomic values...
|
||||||
if(typeof(elem) != 'object'){
|
if(typeof(elem) != 'object'){
|
||||||
merge(elem)
|
merge(elem)
|
||||||
@ -662,8 +695,12 @@ module.BaseParser = {
|
|||||||
continue }
|
continue }
|
||||||
// expand ast...
|
// expand ast...
|
||||||
if(elem instanceof Array){
|
if(elem instanceof Array){
|
||||||
|
//merge(...this.resolve(page, elem, state))
|
||||||
merge(...this.resolve(page, elem, state))
|
merge(...this.resolve(page, elem, state))
|
||||||
continue }
|
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...
|
// nested macro with no value set -- skip...
|
||||||
if(this.macros[elem.name] instanceof Array){
|
if(this.macros[elem.name] instanceof Array){
|
||||||
continue }
|
continue }
|
||||||
@ -697,7 +734,8 @@ module.BaseParser = {
|
|||||||
if(ast.length > 1){
|
if(ast.length > 1){
|
||||||
throw new Error('!!!!')
|
throw new Error('!!!!')
|
||||||
}
|
}
|
||||||
return ast[0] },
|
return ast[0]
|
||||||
|
?? '' },
|
||||||
|
|
||||||
// XXX this can't be used from within macros -- will deadlock the results...
|
// XXX this can't be used from within macros -- will deadlock the results...
|
||||||
// XXX how should this play with filters???
|
// XXX how should this play with filters???
|
||||||
@ -714,7 +752,8 @@ module.BaseParser = {
|
|||||||
if(ast.length > 1){
|
if(ast.length > 1){
|
||||||
throw new Error('!!!!')
|
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
|
// This also works for cases where slots override slots they
|
||||||
// are contained in, this will not lead to recursion.
|
// 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???
|
// XXX do we show a slot with unfilled content???
|
||||||
// ...what's the point in having <content>, can't is just
|
// ...what's the point in having <content>, can't is just
|
||||||
// be replaced by a slot?
|
// be replaced by a slot?
|
||||||
@ -1260,10 +1303,7 @@ module.parser = {
|
|||||||
function(parser, args, body, state){
|
function(parser, args, body, state){
|
||||||
var name = args.name
|
var name = args.name
|
||||||
var text = args.text
|
var text = args.text
|
||||||
?? body
|
|
||||||
// NOTE: this can't be undefined for .expand(..) to work
|
|
||||||
// correctly...
|
|
||||||
?? []
|
|
||||||
return Promise.awaitOrRun(
|
return Promise.awaitOrRun(
|
||||||
parser.parseNested(this, name, state),
|
parser.parseNested(this, name, state),
|
||||||
function(name){
|
function(name){
|
||||||
@ -1281,19 +1321,28 @@ module.parser = {
|
|||||||
: name in slots)
|
: name in slots)
|
||||||
|
|
||||||
// set slot value...
|
// set slot value...
|
||||||
|
// XXX simplify this...
|
||||||
var stack = []
|
var stack = []
|
||||||
slots[name]
|
slots[name]
|
||||||
&& stack.push(slots[name])
|
&& stack.push(slots[name])
|
||||||
delete 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
|
var original = slot
|
||||||
slots[name]
|
slots[name]
|
||||||
&& stack.unshift(slot)
|
&& stack.unshift(slot)
|
||||||
slot = slots[name] ??= slot
|
slot = slots[name] ??= slot
|
||||||
// handle <content/>...
|
// handle <content/>...
|
||||||
for(prev of stack){
|
for(prev of stack.reverse()){
|
||||||
// get the first <content/>
|
// 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){
|
for(var i in prev){
|
||||||
if(typeof(prev[i]) != 'string'
|
if(typeof(prev[i]) != 'string'
|
||||||
&& prev[i].name == 'content'){
|
&& 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