@@ -68,6 +68,79 @@ function parse_diff(text, showfunc=dump)
68
68
show_expr_text_diff (stdout , showfunc, ex, fl_ex)
69
69
end
70
70
71
+ function kw_to_eq (ex)
72
+ return Meta. isexpr (:kw , ex) ? Expr (:(= ), ex. args... ) : ex
73
+ end
74
+
75
+ function triple_string_roughly_equal (str, fl_str)
76
+ # Allow some leeway for a bug in the reference parser with
77
+ # triple quoted strings
78
+ lines = split (str, ' \n ' )
79
+ fl_lines = split (fl_str, ' \n ' )
80
+ if length (lines) != length (fl_lines)
81
+ return false
82
+ end
83
+ for (line1, line2) in zip (lines, fl_lines)
84
+ if ! all (c in " \t " for c in line2) && ! endswith (line1, line2)
85
+ return false
86
+ end
87
+ end
88
+ return true
89
+ end
90
+
91
+ # Compare Expr from reference parser expression to JuliaSyntax parser, ignoring
92
+ # differences due to bugs in the reference parser.
93
+ function exprs_roughly_equal (fl_ex, ex)
94
+ if fl_ex isa Float64 && Meta. isexpr (ex, :call , 3 ) &&
95
+ ex. args[1 ] == :* &&
96
+ ex. args[2 ] == fl_ex &&
97
+ (ex. args[3 ] == :f || ex. args[3 ] == :f0 )
98
+ # 0x1p0f
99
+ return true
100
+ elseif ! (fl_ex isa Expr) || ! (ex isa Expr)
101
+ if fl_ex isa String && ex isa String
102
+ if fl_ex == ex
103
+ return true
104
+ else
105
+ return triple_string_roughly_equal (ex, fl_ex)
106
+ end
107
+ else
108
+ return fl_ex == ex
109
+ end
110
+ end
111
+ if fl_ex. head != ex. head
112
+ return false
113
+ end
114
+ h = ex. head
115
+ fl_args = fl_ex. args
116
+ args = ex. args
117
+ if ex. head in (:block , :quote , :toplevel )
118
+ fl_args = filter (x-> ! (x isa LineNumberNode), fl_args)
119
+ args = filter (x-> ! (x isa LineNumberNode), args)
120
+ end
121
+ if (h == :global || h == :local ) && length (args) == 1 && Meta. isexpr (args[1 ], :tuple )
122
+ # Allow invalid syntax like `global (x, y)`
123
+ args = args[1 ]. args
124
+ end
125
+ if length (fl_args) != length (args)
126
+ return false
127
+ end
128
+ if h == :do && length (args) >= 1 && Meta. isexpr (fl_args[1 ], :macrocall )
129
+ # Macrocalls with do, as in `@f(a=1) do\nend` use :kw in the
130
+ # reference parser for the `a=1`, but we regard this as a bug.
131
+ fl_args = copy (fl_args)
132
+ fl_args[1 ] = Expr (:macrocall , map (kw_to_eq, args[1 ]. args)... )
133
+ end
134
+ for i = 1 : length (args)
135
+ flarg = fl_args[i]
136
+ arg = args[i]
137
+ if ! exprs_roughly_equal (flarg, arg)
138
+ return false
139
+ end
140
+ end
141
+ return true
142
+ end
143
+
71
144
function parsers_agree_on_file (filename; show_diff= false )
72
145
text = try
73
146
read (filename, String)
@@ -93,6 +166,8 @@ function parsers_agree_on_file(filename; show_diff=false)
93
166
return ! JuliaSyntax. any_error (stream) &&
94
167
JuliaSyntax. remove_linenums! (ex) ==
95
168
JuliaSyntax. remove_linenums! (fl_ex)
169
+ # Could alternatively use
170
+ # exprs_roughly_equal(fl_ex, ex)
96
171
catch exc
97
172
@error " Parsing failed" filename exception= current_exceptions ()
98
173
return false
@@ -308,3 +383,18 @@ function parse_sexpr(code)
308
383
st
309
384
end
310
385
386
+
387
+ @testset " Test tools" begin
388
+ @test exprs_roughly_equal (Expr (:global , :x , :y ),
389
+ Expr (:global , Expr (:tuple , :x , :y )))
390
+ @test exprs_roughly_equal (Expr (:local , :x , :y ),
391
+ Expr (:local , Expr (:tuple , :x , :y )))
392
+ @test exprs_roughly_equal (1.5 ,
393
+ Expr (:call , :* , 1.5 , :f ))
394
+ @test exprs_roughly_equal (1.5 ,
395
+ Expr (:call , :* , 1.5 , :f0 ))
396
+ @test exprs_roughly_equal (Expr (:do , Expr (:macrocall , Symbol (" @f" ), LineNumberNode (1 ), Expr (:kw , :a , 1 )),
397
+ Expr (:-> , Expr (:tuple ), Expr (:block , LineNumberNode (1 )))),
398
+ Expr (:do , Expr (:macrocall , Symbol (" @f" ), LineNumberNode (1 ), Expr (:(= ), :a , 1 )),
399
+ Expr (:-> , Expr (:tuple ), Expr (:block , LineNumberNode (1 )))))
400
+ end
0 commit comments