From f00d287301a7d9bc89132f967352c47df3af8538 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Tue, 13 Feb 2024 19:04:24 +0100 Subject: allow printing/replacing certain expressions with user-specified templates --- lib/output.js | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 139 insertions(+), 10 deletions(-) diff --git a/lib/output.js b/lib/output.js index c35c8ffc..48dcfff7 100644 --- a/lib/output.js +++ b/lib/output.js @@ -41,6 +41,14 @@ ***********************************************************************/ +/*********************************************************************** + +Modified by Wojtek Kosior to allow printing/replacing +certain expressions with user-specified templates. These modifications are +thereby released under Creative Commons Zero v1.0. + +***********************************************************************/ + "use strict"; function is_some_comments(comment) { @@ -49,7 +57,17 @@ function is_some_comments(comment) { } function OutputStream(options) { - options = defaults(options, { + const template_option_names = [ + "CALL", "PROPERTY_CALL", + "BINARY", "LAZY_BINARY", "ASSIGNMENT_BINARY", + ]; + + const template_options = template_option_names.reduce( + (acc, name) => Object.assign(acc, {[`template_for_${name}`]: null}), + {} + ); + + options = defaults(options, Object.assign(template_options, { annotations : false, ascii_only : false, beautify : false, @@ -73,7 +91,7 @@ function OutputStream(options) { webkit : false, width : 80, wrap_iife : false, - }, true); + }, true)); // Convert comment option to RegExp if neccessary and set up comments filter var comment_filter = return_false; // Default case, throw all comments away @@ -365,6 +383,73 @@ function OutputStream(options) { last = str; }; + var re_verbose = function(flags="") { + return (...args) => new RegExp( + String.raw(...args) + .split("\n") + .map(line => + /^\s*(?([^#]|\S#+)*([^#\s]|\S#+))?(\s+(#.*)?)?/ + .exec(line).groups.regex_part) + .join(""), + flags + ); + }; + + var template_substitution_regex = re_verbose("g")` + (?\/\*\*\/) + # name to substitute + |\/\*(?[^*]+)\*\/ + # name to substitute while also removing the non-whitespace that + # appears after it + |\/\*(?[^*]+)\*\*\/[^\s\/]* + # opening paren or square paren + |(?[\[\(]) + # closing one + |(?[\]\)]) + # whitespace + |(?\s+) + `; + + var print_template = function(template, src_start_token, print_callbacks) { + template = template.trim(); + + print_callbacks = ["line", "col"].reduce( + (acc, prop) => Object.assign( + acc, + {[prop]: () => print(src_start_token[prop] + "")} + ), + Object.create(print_callbacks) + ); + + let start_pos = 0; + + for (const match of template.matchAll(template_substitution_regex)) { + print(template.substring(start_pos, match.index)); + + start_pos = match.index + match[0].length; + + const name = match.groups.name1 || match.groups.name2; + + if (match.groups.empty_substitution) { + /* print nothing */ + } else if (name) { + print_callbacks[name](this); + } else if (match.groups.opening) { + print(match.groups.opening); + may_add_newline(); + } else if (match.groups.closing) { + may_add_newline(); + print(match.groups.closing); + } else if (match.groups.whitespace) { + space(); + } else { + throw new Error("programming error"); + } + } + + print(template.substring(start_pos)); + }; + var space = options.beautify ? function() { print(" "); } : function() { @@ -579,6 +664,7 @@ function OutputStream(options) { has_parens : function() { return last.slice(-1) == "(" }, newline : newline, print : print, + print_template : print_template, space : space, comma : comma, colon : colon, @@ -1480,9 +1566,38 @@ function OutputStream(options) { DEFPRINT(AST_Call, function(output) { var self = this; print_annotation(self, output); - self.expression.print(output); - if (self.optional) output.print("?."); - print_call_args(self, output); + + if (self.expression instanceof AST_PropAccess) { + const template = output.option("template_for_PROPERTY_CALL") || + "/*function_expression*//*optional*//*parented_args*/"; + + function print_property_expression(out) { + if (typeof self.expression.property === "string") + out.print_string(self.expression.property, '"'); + else + self.expression.property.print(out); + } + + output.print_template(template, self.start, { + this_expression : out => (self.expression.expression + .print(out)), + property_expression : print_property_expression, + property_optional : out => (self.expression.optional && + out.print("?.")), + function_expression : out => self.expression.print(out), + optional : out => self.optional && out.print("?."), + parented_args : out => print_call_args(self, out) + }); + } else { + const template = output.option("template_for_CALL") || + "/*expression*//*optional*//*parented_args*/"; + + output.print_template(template, self.start, { + expression : out => self.expression.print(out), + optional : out => self.optional && out.print("?."), + parented_args : out => print_call_args(self, out) + }); + } }); DEFPRINT(AST_New, function(output) { var self = this; @@ -1551,11 +1666,25 @@ function OutputStream(options) { }); DEFPRINT(AST_Binary, function(output) { var self = this; - self.left.print(output); - output.space(); - output.print(self.operator); - output.space(); - self.right.print(output); + + for (const [regex, option] of [ + [/^(&&|\|\|)$/, "LAZY_BINARY"], + [/^([^=]+|[!=<>]==?)$/, "BINARY"], + [/()/, "ASSIGNMENT_BINARY"], + ]) { + if (regex.test(self.operator)) { + const template = output.option(`template_for_${option}`) || + "/*left*/ /*operator*/ /*right*/"; + + output.print_template(template, self.start, { + left : out => self.left.print(out), + right : out => self.right.print(out), + operator : out => out.print(self.operator) + }); + + return; + } + } }); DEFPRINT(AST_Conditional, function(output) { var self = this; -- cgit v1.2.3