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:
// 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 BUG?: <macro src=/moo/> is not parsed correctly...
// XXX need to correctly handle nested and escaped quotes...
@ -30,8 +35,8 @@ var pwpath = require('./path')
// XXX RENAME...
// ...this handles the syntax and execution...
var BaseParser =
module.BaseParser = {
var BaseMacros =
module.BaseMacros = {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Passive parsing...
@ -280,7 +285,8 @@ module.BaseParser = {
return f in (that.filters ?? {})})
var handle = function(str){
// skip non-basic data...
if(typeof(str) == 'object'
if(str == null
|| typeof(str) == 'object'
|| typeof(str) == 'function'){
return str }
return filters
@ -749,6 +755,7 @@ module.BaseParser = {
// <ast> ::= [ <item>, ... ]
// <item> ::=
// <basic-value>
// | Promise(<value>)
// | <elem>
//
// <elem> is returned if its value is not resolved yet.
@ -776,18 +783,15 @@ module.BaseParser = {
if(typeof(elem.value) == 'function'){
let e = elem
let func = e.value
Promise.awaitOrRun(
elem = Promise.awaitOrRun(
// if not everything is resolved, delay the stage II
// callbacks till .wait is done...
// NOTE: this depends on that JS is single thread
// and we can't have state.wait resolve in
// the middle of this loop.
// NOTE: waiting promises resolving is also done in
// FIFO orderm thus maintaining order of execution
state.wait,
function(){
return elem = e.value =
func(state) })
return func(state) })
break }
elem = elem.value }
if(elem == null){
@ -846,13 +850,8 @@ module.BaseParser = {
// - apply global filters
// - apply stage III post handlers
//
//
// 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...
// XXX do we report recursion errors here???
finalize: function(page, ast, state={}, nested_handlers={}, wait='wait'){
//finalize: function(page, ast, state={}, nested_handlers={}, wait='waitNested'){
var that = this
var stage3 = function(ast){
@ -862,12 +861,13 @@ module.BaseParser = {
e.call(that, state)
: e })
.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){
return Promise.awaitOrRun(
// XXX LOCAL_STATE
state.hasOwnProperty('wait') ?
state.wait
// XXX
: null,
function(){
//delete state.unresolved
@ -886,20 +886,19 @@ module.BaseParser = {
ast = this.resolve(page, ast, state, nested_handlers)
return Promise.awaitOrRun(
// XXX LOCAL_STATE
// XXX do we actually need to wait here???
// ...each macro should already be waiting...
//...[state[wait]].flat(),
// in case ast contains value promises, expand them...
Promise
.iter(ast)
.sync(),
// wait...
(wait && state.hasOwnProperty(wait)) ?
state[wait]
: null,
function(){
function(ast){
// NOTE: in an async world where any promised macro can
// call .exec(..) / .execNested(..) we can't trust
// the lack of .unresolved in state...
return Promise.awaitOrRun(
// XXX LOCAL_STATE if we are nested we should not wait
// for anything after the caller...
that.isResolved(ast) ?
ast
: resolve(ast),
@ -972,9 +971,9 @@ function(macro){
// XXX RENAME...
// ...this is more of an expander/executer...
// ...might be a good idea to also do a check without executing...
var parser =
module.parser = {
__proto__: BaseParser,
var macros =
module.macros = {
__proto__: BaseMacros,
// String to be substetuted for a recursive include...
//
@ -1026,13 +1025,6 @@ module.parser = {
//
// XXX should we include the global filters (current) or exclude
// 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(
[['clear']],
function(page, args, body, state){

View File

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