now macro errors are printed to page (still need better introspection)

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2022-11-19 04:26:13 +03:00
parent 1e7ff14da8
commit 200f8f976e
4 changed files with 82 additions and 37 deletions

View File

@ -2409,9 +2409,16 @@ module.System = {
// //
// NOTE: these are last resort pages, preferably overloaded in /Templates. // NOTE: these are last resort pages, preferably overloaded in /Templates.
// //
ParseError: {
text: object.doc`
<slot title/>
<div class="error">
<div class="msg" wikiwords="no">ParseError: @(msg)</div>
Page: [@(path)]
</div> `,},
RecursionError: { RecursionError: {
text: 'RECURSION ERROR: @source(../path)' }, text: 'RECURSION ERROR: @source(../path)' },
NotFoundError: { NotFoundError: {
//text: 'NOT FOUND ERROR: @source(./path)' }, //text: 'NOT FOUND ERROR: @source(./path)' },
text: object.doc` text: object.doc`
<slot title/> <slot title/>

View File

@ -378,7 +378,7 @@ module.BaseParser = {
if(done){ if(done){
if(to){ if(to){
throw new Error( throw new Error(
'Premature end of input: Expected closing "'+ to +'"') } 'Premature end of input: Expected </'+ to +'>') }
return } return }
// special case: quoting -> collect text... // special case: quoting -> collect text...
@ -404,9 +404,9 @@ module.BaseParser = {
&& !(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 ?
' in "'+to+'"' ' in <'+to+'>'
: '')) } : '')) }
// open block... // open block...
if(value.type == 'opening'){ if(value.type == 'opening'){
@ -416,7 +416,7 @@ module.BaseParser = {
// close block... // close block...
} else if(value.type == 'closing'){ } else if(value.type == 'closing'){
if(value.name != to){ if(value.name != to){
throw new Error('Unexpected closing "'+ value.name +'"') } throw new Error('Unexpected </'+ value.name +'>') }
// NOTE: we are intentionally not yielding the value here... // NOTE: we are intentionally not yielding the value here...
return } return }
// normal value... // normal value...
@ -437,42 +437,66 @@ module.BaseParser = {
// one should only be used for parsing and be forgotten after // one should only be used for parsing and be forgotten after
// the ast is constructed the other should be part of the ast... // the ast is constructed the other should be part of the ast...
expand: async function*(page, ast, state={}){ expand: async function*(page, ast, state={}){
ast = ast == null ? try{
//this.group(page) ast = ast == null ?
this.group(page, await page.raw ?? '') //this.group(page)
: typeof(ast) != 'object' ? this.group(page, await page.raw ?? '')
this.group(page, ast) : typeof(ast) != 'object' ?
: ast instanceof types.Generator ? this.group(page, ast)
ast : ast instanceof types.Generator ?
: ast.iter() ast
: ast.iter()
while(true){ while(true){
var {value, done} = ast.next() var {value, done} = ast.next()
if(done){ if(done){
return } return }
// text block... // text block...
if(typeof(value) == 'string'){ if(typeof(value) == 'string'){
yield value yield value
continue } continue }
// macro... // macro...
var {name, args, body} = value var {name, args, body} = value
// nested macro -- skip... // nested macro -- skip...
if(typeof(page.macros[name]) != 'function'){ if(typeof(page.macros[name]) != 'function'){
yield {...value, skip: true} yield {...value, skip: true}
continue } continue }
var res = var res =
await this.callMacro(page, name, args, body, state) await this.callMacro(page, name, args, body, state)
?? '' ?? ''
// result... // result...
if(res instanceof Array if(res instanceof Array
|| page.macros[name] instanceof types.Generator){ || page.macros[name] instanceof types.Generator){
yield* res yield* res
} else { } else {
yield res } } }, yield res } }
// error...
}catch(err){
console.error(err)
yield await page.parse(
// XXX add line number and page path...
'@include("./ParseError'
+':path='
// XXX use pwiki.encodeElem(..) ???
+ page.path
+':msg='
+ err.message
// quote html stuff...
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
// quote argument syntax...
.replace(/["']/g, function(c){
return '%'+ c.charCodeAt().toString(16) })
.replace(/:/g, '&colon;')
.replace(/=/g, '&equals;')
+'")')
return } },
// recursively resolve and enumerate the ast... // recursively resolve and enumerate the ast...
// //

View File

@ -150,6 +150,18 @@ body.loading .page.spinner span {
} }
} }
.error .msg {
font-weight: bold;
color: red;
margin-bottom: 1em;
}
.error {
background: lightyellow;
padding: 1em;
box-shadow: inset 3px 5px 15px 5px rgba(0,0,0,0.05);
border: dashed red 1px;
}
textarea { textarea {
font-size: 1.2em; font-size: 1.2em;
border: none; border: none;

View File

@ -36,12 +36,14 @@
* - * -
* *
* *
* XXX BUG? can't use < and > (bot?) in page title...
* XXX parser: error handling: revise page quoting...
* XXX parser: error handling: add line number + context... (???)
* XXX BUG: parser: * XXX BUG: parser:
* This will break: * This will break:
* await pwiki.parse('<macro src=../tags join=", ">@source(.)</macro>') * await pwiki.parse('<macro src=../tags join=", ">@source(.)</macro>')
* This will not: * This will not:
* await pwiki.parse('<macro src="../tags" join=", ">@source(.)</macro>') * await pwiki.parse('<macro src="../tags" join=", ">@source(.)</macro>')
* XXX ASAP parser: error handling: must output to page and be informative...
* XXX ASAP test: can we store the file handler with permissions in a ServiceWorker?? * XXX ASAP test: can we store the file handler with permissions in a ServiceWorker??
* XXX the parser should handle all action return values, including: * XXX the parser should handle all action return values, including:
* - lists -- XXX * - lists -- XXX