refactoring + include and source macros...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2022-04-26 20:58:09 +03:00
parent bdc1bbe00c
commit 2af87ac928

148
pwiki2.js
View File

@ -522,17 +522,17 @@ module.parser = {
].join('|') +')', 'smig'), ].join('|') +')', 'smig'),
// General parser API... // Strip comments...
// //
stripComments: function(str){
clearComments: function(str){
return str return str
.replace(this.COMMENT_PATTERN, .replace(this.COMMENT_PATTERN,
function(...a){ function(...a){
return a.pop().uncomment return a.pop().uncomment
|| '' }) }, || '' }) },
// // Lexically split the string...
//
// <item> ::= // <item> ::=
// <string> // <string>
// | { // | {
@ -551,15 +551,13 @@ module.parser = {
// //
// //
// NOTE: this internally uses macros' keys to generate the lexing pattern. // NOTE: this internally uses macros' keys to generate the lexing pattern.
//
// XXX feels a bit ugly...
lex: function*(page, str){ lex: function*(page, str){
str = str str = str
?? page.raw ?? page.raw
// NOTE: we are doing a separate pass for comments to completely // NOTE: we are doing a separate pass for comments to completely
// decouple them from the base macro syntax, making them fully // decouple them from the base macro syntax, making them fully
// transparent... // transparent...
str = this.clearComments(str) str = this.stripComments(str)
// XXX should this be cached??? // XXX should this be cached???
var MACRO_PATTERN = new RegExp( var MACRO_PATTERN = new RegExp(
@ -638,10 +636,7 @@ module.parser = {
// ... // ...
// } // }
// //
//
// NOTE: this internaly uses macros to check for propper nesting // NOTE: this internaly uses macros to check for propper nesting
//
// XXX normalize lex to be a generator (???)
group: function*(page, lex, to=false){ group: function*(page, lex, to=false){
lex = lex lex = lex
?? this.lex(page) ?? this.lex(page)
@ -682,6 +677,18 @@ module.parser = {
// Expand macros... // Expand macros...
// //
// <item> ::=
// <string>
// // returned by .macros.filter(..)
// | {
// filters: [
// '<filter>'
// | '-<filter>',
// ...
// ],
// data: [ <item>, .. ],
// }
//
expand: function*(page, ast, state={}){ expand: function*(page, ast, state={}){
ast = ast == null ? ast = ast == null ?
this.group(page) this.group(page)
@ -715,16 +722,21 @@ module.parser = {
// Fully parse a page... // Fully parse a page...
// //
// This runs in two stages: // This runs in two stages:
// - expand the page text // - expand the page
// - lex the page -- .lex(..)
// - group block elements -- .group(..)
// - expand macros -- .expand(..)
// - apply filters // - apply filters
// //
// XXX add a special filter to clear pending filters... (???) // XXX add a special filter to clear pending filters... (???)
// XXX rename???
parse: function(page, ast, state={}){ parse: function(page, ast, state={}){
var that = this var that = this
// XXX should we handle strings as input??? // XXX should we handle strings as input???
ast = ast ast = ast
?? this.expand(page, null, state) ?? this.expand(page, null, state)
ast = typeof(ast) == 'string' ?
this.expand(page, ast, state)
: ast
var _normalize = function(filters){ var _normalize = function(filters){
var skip = new Set() var skip = new Set()
@ -859,28 +871,82 @@ object.Constructor('Page', BasePage, {
// serialize the block for later processing... // serialize the block for later processing...
var res = { var res = {
filters: state.filters, filters: state.filters,
data: [...this.parser.expand(this, body, state)], data: [...this.__parser__.expand(this, body, state)],
} }
// restore global filters... // restore global filters...
state.filters = parent_filters state.filters = parent_filters
yield res } yield res }
return }, return },
// XXX 'text' argument is changed to 'recursive'...
// XXX should we track recursion via the resolved (current) path
// or the given path???
// XXX should this be lazy???
include: function(args, body, state, key='included', handler){
// positional args...
var src = args.src || args[0]
var recursive = args.recursive || body
if(!src){
return '' }
handler = handler
?? function(){
return this.get(src)
.parse(
args.isolated ?
{[key]: state[key]}
: state) }
// handle recursion...
var parent_seen = state[key]
var seen = state[key] =
(state[key] ?? [this.location]).slice()
var target = this.match(src)
target = target instanceof Array ?
target.join(',')
: target
// recursion detected...
if(this.match() == this.match(src)
|| seen.includes(target)){
if(!recursive){
throw new Error(
'include: include recursion detected: '
+ seen.concat([target]).join(' -> ')) }
// have the 'recursive' arg...
return this.__parser__.parse(this, recursive, state) }
seen.push(target)
// load the included page...
var res = handler.call(this)
// restore previous include chain...
if(parent_seen){
state[key] = parent_seen
} else {
delete state[key] }
return res },
source: function(args, body, state){ source: function(args, body, state){
return args.src ? var src = args.src || args[0]
this.get(src).render(state) return this.macros.include.call(this,
: '' }, args, body, state, 'sources',
include: function(){}, function(){
quote: function(){}, return this.__parser__.parse(this, this.get(src).raw, state) }) },
macro: function(){}, macro: function(){},
slot: function(){}, slot: function(){},
// XXX quote what???
quote: function(){},
// nesting rules... // nesting rules...
'else': ['macro'], 'else': ['macro'],
}, },
parser: module.parser, // page parser...
//
__parser__: module.parser,
parse: function(state={}){ parse: function(state={}){
return this.parser.parse(this, null, state) }, return this.__parser__.parse(this, null, state) },
// raw page text... // raw page text...
@ -939,19 +1005,35 @@ Page('/', '/',
// XXX TEST... // XXX TEST...
console.log('loading test page...') console.log('loading test page...')
pwiki.update({ pwiki
location: '/test', .update({
text: ` location: '/page',
Some test text... text: 'PAGE\n'
+'\n'
@now() // XXX BUG this is parsed incorrectly -- macro pattern...
//+'@include(/test recursive="Recursion type 2 (<now/>)")\n',
<test> +'@include(/test recursive="Recursion type 2 <now/>")\n',
some code... })
</test> .update({
location: '/other',
@filter(test)`, text: 'OTHER',
}) })
.update({
location: '/test',
text: 'TEST\n'
+'Including /other #1: @include(/other)\n'
+'Including /other #2: @include(/other)\n'
+'\n'
// XXX BUG this is parsed incorrectly -- macro pattern...
//+'Including /test: @include(/test recursive="Recursion type 1 (<now/>)")\n'
+'Including /test: @include(/test recursive="Recursion type 1 <now/>")\n'
+'\n'
+'Including /page: @include(/page)\n'
+'\n'
+'Including /: \\@include(/)\n'
+'\n'
+'@filter(test)',
})