@@ -47,8 +47,97 @@ local function remove_vault(doc)
47
47
end
48
48
end
49
49
50
+ --- Create a deep copy of a table.
51
+ local function copy_table (tbl , depth , seen )
52
+ local tp = type (tbl )
53
+ if tp == ' table' then
54
+ local copy = {}
55
+ -- Iterate 'raw' pairs, i.e., without using metamethods
56
+ for key , value in next , tbl , nil do
57
+ if depth == ' shallow' then
58
+ copy [key ] = value
59
+ else
60
+ copy [copy_table (key )] = copy_table (value )
61
+ end
62
+ end
63
+ return setmetatable (copy , getmetatable (tbl ))
64
+ elseif tp == ' userdata' then
65
+ return tbl :clone ()
66
+ else -- number, string, boolean, etc
67
+ return tbl
68
+ end
69
+ end
70
+
71
+ --- Checks if two tables are equal
72
+ function equals (o1 , o2 )
73
+ if o1 == o2 then
74
+ return true
75
+ end
76
+ local o1type = type (o1 )
77
+ local o2type = type (o2 )
78
+ if o1type ~= o2type or o1type ~= ' table' then
79
+ return false
80
+ end
81
+
82
+ local keys = {}
83
+
84
+ for key1 , value1 in pairs (o1 ) do
85
+ local value2 = o2 [key1 ]
86
+ if value2 == nil or equals (value1 , value2 ) == false then
87
+ return false
88
+ end
89
+ keys [key1 ] = true
90
+ end
91
+
92
+ for key2 in pairs (o2 ) do
93
+ if not keys [key2 ] then return false end
94
+ end
95
+ return true
96
+ end
97
+
98
+ --- Checks if a filter follows the "nondestructive" property.
99
+ -- The nondestructive property is fulfilled if filter functions returns
100
+ -- an explicit object, or if it returns `nil` while leaving the passed
101
+ -- in object unmodified.
102
+ --
103
+ -- An error is raised if the property is violated.
104
+ --
105
+ -- Only filters with this property can use jog safely, without
106
+ -- unintended consequences.
107
+ local function check_nondestructive_property (namedfilter )
108
+ for name , fn in pairs (namedfilter .filter ) do
109
+ if type (fn ) == ' function' then
110
+ local copy = function (x )
111
+ local tp = type (x )
112
+ return tp ~= ' table' and x :clone () or
113
+ (pandoc .utils .type (x ) == ' Meta' and pandoc .Meta (x ) or copy_table (x ))
114
+ end
115
+ namedfilter .filter [name ] = function (obj , context )
116
+ local orig = copy (obj )
117
+ local result , descend = fn (obj , context )
118
+ if result == nil then
119
+ if type (obj ) ~= ' table' and not equals (obj , orig ) then
120
+ warn (
121
+ " \n Function '" .. name .. " ' in filter '" .. namedfilter .name ..
122
+ " ' returned `nil`, but modified the input."
123
+ )
124
+ end
125
+ -- elseif result.t == obj.t and not rawequal(result, obj) then
126
+ -- warn(
127
+ -- "\nFunction '" .. name .. "' in filter '" .. namedfilter.name ..
128
+ -- "' returned a new object instead of passing the original one through."
129
+ -- )
130
+ end
131
+ return result , descend
132
+ end
133
+ end
134
+ end
135
+ return namedfilter
136
+ end
137
+
50
138
local function run_emulated_filter_chain (doc , filters , afterFilterPass , profiling )
51
139
init_trace (doc )
140
+ local compare_jog_and_walk = os.getenv ' QUARTO_JOG_CHECK'
52
141
for i , v in ipairs (filters ) do
53
142
local function callback ()
54
143
if v .flags then
@@ -79,7 +168,17 @@ local function run_emulated_filter_chain(doc, filters, afterFilterPass, profilin
79
168
print (pandoc .write (doc , " native" ))
80
169
else
81
170
_quarto .ast ._current_doc = doc
82
- doc = run_emulated_filter (doc , v .filter )
171
+
172
+ if compare_jog_and_walk and not v .force_pandoc_walk then
173
+ v = check_nondestructive_property (v )
174
+ end
175
+ doc = run_emulated_filter (doc , v .filter , v .force_pandoc_walk )
176
+
177
+ if compare_jog_and_walk and not v .force_pandoc_walk then
178
+ -- Types of meta values are only check on assignment.
179
+ doc .meta = doc .meta
180
+ end
181
+
83
182
ensure_vault (doc )
84
183
85
184
add_trace (doc , v .name )
@@ -204,4 +303,4 @@ function run_as_extended_ast(specTable)
204
303
end
205
304
206
305
return pandocFilterList
207
- end
306
+ end
0 commit comments