Compare commits

...

3 Commits

Author SHA1 Message Date
64c7a63930 refactoring: naming: parser -> macros...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
2026-08-05 00:27:43 +03:00
811b0a433d cleanup...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
2026-08-04 22:07:57 +03:00
74b6c19753 filter sync bug fixed, at last...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
2026-08-04 21:50:21 +03:00
2 changed files with 53 additions and 41 deletions

View File

@ -18,10 +18,15 @@ var pwpath = require('./path')
//--------------------------------------------------------------------- //---------------------------------------------------------------------
// Parser... // Parser/Runner...
// XXX TODO: // XXX TODO:
// callbacks on elements resolving... // - callbacks on elements resolving...
// - option/way to wrap macro output -- callback??
// - naming:
// parser -> macro
//
//
// XXX ASAP move the macros here... // XXX ASAP move the macros here...
// XXX BUG?: <macro src=/moo/> is not parsed correctly... // XXX BUG?: <macro src=/moo/> is not parsed correctly...
// XXX need to correctly handle nested and escaped quotes... // XXX need to correctly handle nested and escaped quotes...
@ -30,8 +35,8 @@ var pwpath = require('./path')
// XXX RENAME... // XXX RENAME...
// ...this handles the syntax and execution... // ...this handles the syntax and execution...
var BaseParser = var BaseMacros =
module.BaseParser = { module.BaseMacros = {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Passive parsing... // Passive parsing...
@ -280,7 +285,8 @@ module.BaseParser = {
return f in (that.filters ?? {})}) return f in (that.filters ?? {})})
var handle = function(str){ var handle = function(str){
// skip non-basic data... // skip non-basic data...
if(typeof(str) == 'object' if(str == null
|| typeof(str) == 'object'
|| typeof(str) == 'function'){ || typeof(str) == 'function'){
return str } return str }
return filters return filters
@ -749,6 +755,7 @@ module.BaseParser = {
// <ast> ::= [ <item>, ... ] // <ast> ::= [ <item>, ... ]
// <item> ::= // <item> ::=
// <basic-value> // <basic-value>
// | Promise(<value>)
// | <elem> // | <elem>
// //
// <elem> is returned if its value is not resolved yet. // <elem> is returned if its value is not resolved yet.
@ -768,7 +775,7 @@ module.BaseParser = {
var unresolved = [] var unresolved = []
// merge resolved elements into the last item of elems... // merge resolved elements into the last item of elems...
var elems = [] var elems = []
for(var elem of ast){ for(var elem of ast){
// nesting... // nesting...
while(elem && elem.value){ while(elem && elem.value){
@ -776,18 +783,15 @@ module.BaseParser = {
if(typeof(elem.value) == 'function'){ if(typeof(elem.value) == 'function'){
let e = elem let e = elem
let func = e.value let func = e.value
Promise.awaitOrRun( elem = Promise.awaitOrRun(
// if not everything is resolved, delay the stage II // if not everything is resolved, delay the stage II
// callbacks till .wait is done... // callbacks till .wait is done...
// NOTE: this depends on that JS is single thread // NOTE: this depends on that JS is single thread
// and we can't have state.wait resolve in // and we can't have state.wait resolve in
// the middle of this loop. // the middle of this loop.
// NOTE: waiting promises resolving is also done in
// FIFO orderm thus maintaining order of execution
state.wait, state.wait,
function(){ function(){
return elem = e.value = return func(state) })
func(state) })
break } break }
elem = elem.value } elem = elem.value }
if(elem == null){ if(elem == null){
@ -846,13 +850,8 @@ module.BaseParser = {
// - apply global filters // - apply global filters
// - apply stage III post handlers // - apply stage III post handlers
// //
// // XXX do we report recursion errors here???
// XXX BUG: this seems to always return a promise...
// XXX RECURSION might be a good idea to limit recursion/nesting depth
// of inner resolve(..)...
// XXX RENAME...
finalize: function(page, ast, state={}, nested_handlers={}, wait='wait'){ finalize: function(page, ast, state={}, nested_handlers={}, wait='wait'){
//finalize: function(page, ast, state={}, nested_handlers={}, wait='waitNested'){
var that = this var that = this
var stage3 = function(ast){ var stage3 = function(ast){
@ -862,12 +861,13 @@ module.BaseParser = {
e.call(that, state) e.call(that, state)
: e }) : e })
.flat() } .flat() }
// NOTE: we are not guarding against recursion here as there is no
// point of manually doing what JS does anyway, unless there
// is an explicit reason to do so (e.g. report error).
var resolve = function(ast){ var resolve = function(ast){
return Promise.awaitOrRun( return Promise.awaitOrRun(
// XXX LOCAL_STATE
state.hasOwnProperty('wait') ? state.hasOwnProperty('wait') ?
state.wait state.wait
// XXX
: null, : null,
function(){ function(){
//delete state.unresolved //delete state.unresolved
@ -886,20 +886,19 @@ module.BaseParser = {
ast = this.resolve(page, ast, state, nested_handlers) ast = this.resolve(page, ast, state, nested_handlers)
return Promise.awaitOrRun( return Promise.awaitOrRun(
// XXX LOCAL_STATE // in case ast contains value promises, expand them...
// XXX do we actually need to wait here??? Promise
// ...each macro should already be waiting... .iter(ast)
//...[state[wait]].flat(), .sync(),
// wait...
(wait && state.hasOwnProperty(wait)) ? (wait && state.hasOwnProperty(wait)) ?
state[wait] state[wait]
: null, : null,
function(){ function(ast){
// NOTE: in an async world where any promised macro can // NOTE: in an async world where any promised macro can
// call .exec(..) / .execNested(..) we can't trust // call .exec(..) / .execNested(..) we can't trust
// the lack of .unresolved in state... // the lack of .unresolved in state...
return Promise.awaitOrRun( return Promise.awaitOrRun(
// XXX LOCAL_STATE if we are nested we should not wait
// for anything after the caller...
that.isResolved(ast) ? that.isResolved(ast) ?
ast ast
: resolve(ast), : resolve(ast),
@ -972,9 +971,9 @@ function(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...
var parser = var macros =
module.parser = { module.macros = {
__proto__: BaseParser, __proto__: BaseMacros,
// String to be substetuted for a recursive include... // String to be substetuted for a recursive include...
// //
@ -1026,13 +1025,6 @@ module.parser = {
// //
// XXX should we include the global filters (current) or exclude // XXX should we include the global filters (current) or exclude
// them by default??? // them by default???
// XXX BUG: async body breaks nested filters...
// '<filter upper/>aaa <filter -upper> moo @source(/async/page) </filter> bbb'
// -> 'AAA MOO PAGE BBB'
// XXX and this deadlocks with LOCAL_STATE
// while:
// '<filter upper/>aaa <filter -upper> moo @source(/page) </filter> bbb'
// -> 'AAA moo Page BBB'
filter: Macro( filter: Macro(
[['clear']], [['clear']],
function(page, args, body, state){ function(page, args, body, state){

View File

@ -6,12 +6,12 @@ var path = require('path')
var test = require('ig-test') var test = require('ig-test')
var serialize = require('ig-serialize') var serialize = require('ig-serialize')
var parser = require('../parser').parser var macros = require('../macros').macros
//--------------------------------------------------------------------- //---------------------------------------------------------------------
;(parser.filters ??= {}).upper = ;(macros.filters ??= {}).upper =
function(str){ function(str){
return str.toUpperCase() } return str.toUpperCase() }
@ -56,7 +56,7 @@ module.exports.PAGES = {
var P = var P =
module.exports.P = { module.exports.P = {
__pages__: PAGES, __pages__: PAGES,
__parser__: parser, __parser__: macros,
path: '/', path: '/',
@ -435,6 +435,26 @@ test.Setups({
code: [ code: [
'<filter upper/>@include(/async/page)', '<filter upper/>@include(/async/page)',
'PAGE' ]} }, 'PAGE' ]} },
filter_exclude: function(assert){
return {
page: P,
code: [
'<filter upper/>filtered <filter -upper>unfiltered</filter>',
'FILTERED unfiltered' ]} },
filter_exclude_sync: function(assert){
var state = this.filter_exclude(assert)
return {
...state,
code: [
state.code[0] +' @include(/page)',
state.code[1] +' PAGE' ]} },
filter_exclude_async: function(assert){
var state = this.filter_exclude(assert)
return {
...state,
code: [
state.code[0] +' @include(/async/page)',
state.code[1] +' PAGE' ]} },
filter_mixed_isolated_sync: function(assert){ filter_mixed_isolated_sync: function(assert){
return { return {
page: P, page: P,
@ -518,7 +538,7 @@ test.Tests({
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.exec( (res = await macros.exec(
p, p,
input, input,
s)) s))