aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/output.js149
1 files 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 <koszko@koszko.org> 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*(?<regex_part>([^#]|\S#+)*([^#\s]|\S#+))?(\s+(#.*)?)?/
+ .exec(line).groups.regex_part)
+ .join(""),
+ flags
+ );
+ };
+
+ var template_substitution_regex = re_verbose("g")`
+ (?<empty_substitution>\/\*\*\/)
+ # name to substitute
+ |\/\*(?<name1>[^*]+)\*\/
+ # name to substitute while also removing the non-whitespace that
+ # appears after it
+ |\/\*(?<name2>[^*]+)\*\*\/[^\s\/]*
+ # opening paren or square paren
+ |(?<opening>[\[\(])
+ # closing one
+ |(?<closing>[\]\)])
+ # whitespace
+ |(?<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;