Skip to content

Commit 428eb0a

Browse files
authored
fix JuliaLang#28990, improve top-level location info (JuliaLang#30641)
This gives better location info inside `:toplevel` expressions and for REPL inputs that aren't block constructs. The low-level parser entry points are simplified and renamed to parse-one, parse-all, and parse-file.
1 parent 16898e0 commit 428eb0a

File tree

12 files changed

+139
-85
lines changed

12 files changed

+139
-85
lines changed

base/client.jl

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,15 +146,30 @@ function eval_user_input(@nospecialize(ast), show_value::Bool)
146146
nothing
147147
end
148148

149+
function _parse_input_line_core(s::String, filename::String)
150+
ex = ccall(:jl_parse_all, Any, (Ptr{UInt8}, Csize_t, Ptr{UInt8}, Csize_t),
151+
s, sizeof(s), filename, sizeof(filename))
152+
if ex isa Expr && ex.head === :toplevel
153+
if isempty(ex.args)
154+
return nothing
155+
end
156+
last = ex.args[end]
157+
if last isa Expr && (last.head === :error || last.head === :incomplete)
158+
# if a parse error happens in the middle of a multi-line input
159+
# return only the error, so that none of the input is evaluated.
160+
return last
161+
end
162+
end
163+
return ex
164+
end
165+
149166
function parse_input_line(s::String; filename::String="none", depwarn=true)
150167
# For now, assume all parser warnings are depwarns
151168
ex = if depwarn
152-
ccall(:jl_parse_input_line, Any, (Ptr{UInt8}, Csize_t, Ptr{UInt8}, Csize_t),
153-
s, sizeof(s), filename, sizeof(filename))
169+
_parse_input_line_core(s, filename)
154170
else
155171
with_logger(NullLogger()) do
156-
ccall(:jl_parse_input_line, Any, (Ptr{UInt8}, Csize_t, Ptr{UInt8}, Csize_t),
157-
s, sizeof(s), filename, sizeof(filename))
172+
_parse_input_line_core(s, filename)
158173
end
159174
end
160175
return ex

base/meta.jl

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,6 @@ function parse(str::AbstractString, pos::Integer; greedy::Bool=true, raise::Bool
128128
if raise && isa(ex,Expr) && ex.head === :error
129129
throw(ParseError(ex.args[1]))
130130
end
131-
if ex === ()
132-
raise && throw(ParseError("end of input"))
133-
ex = Expr(:error, "end of input")
134-
end
135131
return ex, pos+1 # C is zero-based, Julia is 1-based
136132
end
137133

src/ast.c

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -763,20 +763,26 @@ static value_t julia_to_scm_(fl_context_t *fl_ctx, jl_value_t *v)
763763
return julia_to_scm_noalloc2(fl_ctx, v);
764764
}
765765

766-
// this is used to parse a line of repl input
767-
JL_DLLEXPORT jl_value_t *jl_parse_input_line(const char *str, size_t len, const char *filename, size_t filename_len)
766+
// parse an entire string like a file, reading multiple expressions
767+
JL_DLLEXPORT jl_value_t *jl_parse_all(const char *str, size_t len, const char *filename, size_t filename_len)
768768
{
769769
JL_TIMING(PARSING);
770770
jl_ast_context_t *ctx = jl_ast_ctx_enter();
771771
fl_context_t *fl_ctx = &ctx->fl;
772772
value_t s = cvalue_static_cstrn(fl_ctx, str, len);
773773
value_t files = cvalue_static_cstrn(fl_ctx, filename, filename_len);
774-
value_t e = fl_applyn(fl_ctx, 2, symbol_value(symbol(fl_ctx, "jl-parse-string")), s, files);
774+
value_t e = fl_applyn(fl_ctx, 2, symbol_value(symbol(fl_ctx, "jl-parse-all")), s, files);
775775
jl_value_t *res = e == fl_ctx->FL_EOF ? jl_nothing : scm_to_julia(fl_ctx, e, NULL);
776776
jl_ast_ctx_leave(ctx);
777777
return res;
778778
}
779779

780+
// for backwards compat
781+
JL_DLLEXPORT jl_value_t *jl_parse_input_line(const char *str, size_t len, const char *filename, size_t filename_len)
782+
{
783+
return jl_parse_all(str, len, filename, filename_len);
784+
}
785+
780786
// this is for parsing one expression out of a string, keeping track of
781787
// the current position.
782788
JL_DLLEXPORT jl_value_t *jl_parse_string(const char *str, size_t len,
@@ -792,7 +798,7 @@ JL_DLLEXPORT jl_value_t *jl_parse_string(const char *str, size_t len,
792798
jl_ast_context_t *ctx = jl_ast_ctx_enter();
793799
fl_context_t *fl_ctx = &ctx->fl;
794800
value_t s = cvalue_static_cstrn(fl_ctx, str, len);
795-
value_t p = fl_applyn(fl_ctx, 3, symbol_value(symbol(fl_ctx, "jl-parse-one-string")),
801+
value_t p = fl_applyn(fl_ctx, 3, symbol_value(symbol(fl_ctx, "jl-parse-one")),
796802
s, fixnum(pos0), greedy?fl_ctx->T:fl_ctx->F);
797803
jl_value_t *expr=NULL, *pos1=NULL;
798804
JL_GC_PUSH2(&expr, &pos1);
@@ -828,7 +834,7 @@ jl_value_t *jl_parse_eval_all(const char *fname,
828834
JL_TIMING(PARSING);
829835
value_t t = cvalue_static_cstrn(fl_ctx, content, contentlen);
830836
fl_gc_handle(fl_ctx, &t);
831-
ast = fl_applyn(fl_ctx, 2, symbol_value(symbol(fl_ctx, "jl-parse-string-stream")), t, f);
837+
ast = fl_applyn(fl_ctx, 2, symbol_value(symbol(fl_ctx, "jl-parse-all")), t, f);
832838
fl_free_gc_handles(fl_ctx, 1);
833839
}
834840
else {
@@ -869,9 +875,9 @@ jl_value_t *jl_parse_eval_all(const char *fname,
869875
}
870876
// expand non-final expressions in statement position (value unused)
871877
expression =
872-
fl_applyn(fl_ctx, 1,
878+
fl_applyn(fl_ctx, 3,
873879
symbol_value(symbol(fl_ctx, iscons(cdr_(ast)) ? "jl-expand-to-thunk-stmt" : "jl-expand-to-thunk")),
874-
expression);
880+
expression, symbol(fl_ctx, jl_filename), fixnum(jl_lineno));
875881
}
876882
jl_get_ptls_states()->world_age = jl_world_counter;
877883
form = scm_to_julia(fl_ctx, expression, inmodule);
@@ -929,6 +935,21 @@ jl_value_t *jl_call_scm_on_ast(const char *funcname, jl_value_t *expr, jl_module
929935
return result;
930936
}
931937

938+
jl_value_t *jl_call_scm_on_ast_and_loc(const char *funcname, jl_value_t *expr, jl_module_t *inmodule,
939+
const char *file, int line)
940+
{
941+
jl_ast_context_t *ctx = jl_ast_ctx_enter();
942+
fl_context_t *fl_ctx = &ctx->fl;
943+
JL_AST_PRESERVE_PUSH(ctx, old_roots, inmodule);
944+
value_t arg = julia_to_scm(fl_ctx, expr);
945+
value_t e = fl_applyn(fl_ctx, 3, symbol_value(symbol(fl_ctx, funcname)), arg,
946+
symbol(fl_ctx, file), fixnum(line));
947+
jl_value_t *result = scm_to_julia(fl_ctx, e, inmodule);
948+
JL_AST_PRESERVE_POP(ctx, old_roots);
949+
jl_ast_ctx_leave(ctx);
950+
return result;
951+
}
952+
932953
// syntax tree accessors
933954

934955
JL_DLLEXPORT jl_value_t *jl_copy_ast(jl_value_t *expr)
@@ -1165,29 +1186,40 @@ JL_DLLEXPORT jl_value_t *jl_macroexpand1(jl_value_t *expr, jl_module_t *inmodule
11651186
return expr;
11661187
}
11671188

1168-
JL_DLLEXPORT jl_value_t *jl_expand(jl_value_t *expr, jl_module_t *inmodule)
1189+
JL_DLLEXPORT jl_value_t *jl_expand_with_loc(jl_value_t *expr, jl_module_t *inmodule,
1190+
const char *file, int line)
11691191
{
11701192
JL_TIMING(LOWERING);
11711193
JL_GC_PUSH1(&expr);
11721194
expr = jl_copy_ast(expr);
11731195
expr = jl_expand_macros(expr, inmodule, NULL, 0);
1174-
expr = jl_call_scm_on_ast("jl-expand-to-thunk", expr, inmodule);
1196+
expr = jl_call_scm_on_ast_and_loc("jl-expand-to-thunk", expr, inmodule, file, line);
11751197
JL_GC_POP();
11761198
return expr;
11771199
}
11781200

1201+
JL_DLLEXPORT jl_value_t *jl_expand(jl_value_t *expr, jl_module_t *inmodule)
1202+
{
1203+
return jl_expand_with_loc(expr, inmodule, "none", 0);
1204+
}
1205+
11791206
// expand in a context where the expression value is unused
1180-
JL_DLLEXPORT jl_value_t *jl_expand_stmt(jl_value_t *expr, jl_module_t *inmodule)
1207+
JL_DLLEXPORT jl_value_t *jl_expand_stmt_with_loc(jl_value_t *expr, jl_module_t *inmodule,
1208+
const char *file, int line)
11811209
{
11821210
JL_TIMING(LOWERING);
11831211
JL_GC_PUSH1(&expr);
11841212
expr = jl_copy_ast(expr);
11851213
expr = jl_expand_macros(expr, inmodule, NULL, 0);
1186-
expr = jl_call_scm_on_ast("jl-expand-to-thunk-stmt", expr, inmodule);
1214+
expr = jl_call_scm_on_ast_and_loc("jl-expand-to-thunk-stmt", expr, inmodule, file, line);
11871215
JL_GC_POP();
11881216
return expr;
11891217
}
11901218

1219+
JL_DLLEXPORT jl_value_t *jl_expand_stmt(jl_value_t *expr, jl_module_t *inmodule)
1220+
{
1221+
return jl_expand_stmt_with_loc(expr, inmodule, "none", 0);
1222+
}
11911223

11921224
#ifdef __cplusplus
11931225
}

src/jlapi.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ JL_DLLEXPORT jl_value_t *jl_eval_string(const char *str)
8888
jl_value_t *r;
8989
JL_TRY {
9090
const char filename[] = "none";
91-
jl_value_t *ast = jl_parse_input_line(str, strlen(str),
91+
jl_value_t *ast = jl_parse_all(str, strlen(str),
9292
filename, strlen(filename));
9393
JL_GC_PUSH1(&ast);
9494
r = jl_toplevel_eval_in(jl_main_module, ast);

src/jlfrontend.scm

Lines changed: 29 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
;; return a lambda expression representing a thunk for a top-level expression
7474
;; note: expansion of stuff inside module is delayed, so the contents obey
7575
;; toplevel expansion order (don't expand until stuff before is evaluated).
76-
(define (expand-toplevel-expr-- e)
76+
(define (expand-toplevel-expr-- e file line)
7777
(let ((ex0 (julia-expand-macroscope e)))
7878
(if (and (pair? ex0) (eq? (car ex0) 'toplevel))
7979
ex0
@@ -94,7 +94,8 @@
9494
(scope-block
9595
(block ,@(map (lambda (v) `(implicit-global ,v)) existing-gv)
9696
,@(map (lambda (v) `(implicit-global ,v)) gv)
97-
,ex))))))
97+
,ex)))
98+
file line)))
9899
(if (and (null? (cdadr (caddr th)))
99100
(and (length= (lam:body th) 2)
100101
(let ((retval (cadadr (lam:body th))))
@@ -114,7 +115,7 @@
114115
(and (eq? (car e) 'global) (every symbol? (cdr e))
115116
(every (lambda (x) (not (memq x '(true false)))) (cdr e))))))
116117

117-
(define (expand-toplevel-expr e)
118+
(define (expand-toplevel-expr e file line)
118119
(cond ((or (atom? e) (toplevel-only-expr? e))
119120
(if (underscore-symbol? e)
120121
(error "all-underscore identifier used as rvalue"))
@@ -124,7 +125,7 @@
124125
(if (not last)
125126
(begin (reset-gensyms)
126127
(set! *in-expand* #t)))
127-
(begin0 (expand-toplevel-expr-- e)
128+
(begin0 (expand-toplevel-expr-- e file line)
128129
(set! *in-expand* last))))))
129130

130131
;; construct default definitions of `eval` for non-bare modules
@@ -146,10 +147,11 @@
146147
(= (call include ,x)
147148
(block
148149
,@loc
149-
(call (top include) ,name ,x)))))))
150+
(call (top include) ,name ,x)))))
151+
'none 0))
150152

151-
;; parse only, returning end position, no expansion.
152-
(define (jl-parse-one-string s pos0 greedy)
153+
;; parse one expression (if greedy) or atom, returning end position
154+
(define (jl-parse-one s pos0 greedy)
153155
(let ((inp (open-input-string s)))
154156
(io.seek inp pos0)
155157
(let ((expr (error-wrap (lambda ()
@@ -158,25 +160,7 @@
158160
(julia-parse inp parse-atom))))))
159161
(cons expr (io.pos inp)))))
160162

161-
(define (jl-parse-string s filename)
162-
(with-bindings ((current-filename (symbol filename)))
163-
(error-wrap (lambda ()
164-
(let ((inp (make-token-stream (open-input-string s))))
165-
;; parse all exprs into a (toplevel ...) form
166-
(let loop ((exprs '()))
167-
;; delay expansion so macros run in the Task executing
168-
;; the input, not the task parsing it (issue #2378)
169-
;; used to be (expand-toplevel-expr expr)
170-
(let ((expr (julia-parse inp)))
171-
(if (eof-object? expr)
172-
(cond ((null? exprs) expr)
173-
((length= exprs 1) (car exprs))
174-
(else (cons 'toplevel (reverse! exprs))))
175-
(if (and (pair? expr) (eq? (car expr) 'toplevel))
176-
(loop (nreconc (cdr expr) exprs))
177-
(loop (cons expr exprs)))))))))))
178-
179-
(define (jl-parse-all io filename)
163+
(define (parse-all- io filename)
180164
(unwind-protect
181165
(with-bindings ((current-filename (symbol filename)))
182166
(let ((stream (make-token-stream io)))
@@ -192,44 +176,47 @@
192176
(julia-parse stream)))))
193177
(if (eof-object? expr)
194178
(cons 'toplevel (reverse! exprs))
195-
(let* ((iserr (and (pair? expr) (eq? (car expr) 'error)))
196-
(next (list* expr
197-
;; for error, get most recent line number (#16720)
198-
(if iserr
199-
`(line ,(input-port-line io))
200-
`(line ,lineno))
201-
exprs)))
179+
(let* ((iserr (and (pair? expr) (eq? (car expr) 'error)))
180+
;; for error, get most recent line number (#16720)
181+
(lineno (if iserr (input-port-line io) lineno))
182+
(next (list* expr
183+
;; include filename in first line node
184+
(if (null? exprs)
185+
`(line ,lineno ,(symbol filename))
186+
`(line ,lineno))
187+
exprs)))
202188
(if iserr
203189
(cons 'toplevel (reverse! next))
204190
(loop next))))))))))
205191
(io.close io)))
206192

207-
;; parse file-in-a-string
208-
(define (jl-parse-string-stream str filename)
209-
(jl-parse-all (open-input-string str) filename))
193+
;; parse all expressions in a string, the same way files are parsed
194+
(define (jl-parse-all str filename)
195+
(parse-all- (open-input-string str) filename))
210196

211197
(define (jl-parse-file filename)
212198
(trycatch
213-
(jl-parse-all (open-input-file filename) filename)
199+
(parse-all- (open-input-file filename) filename)
214200
(lambda (e) #f)))
215201

216202
; expand a piece of raw surface syntax to an executable thunk
217-
(define (jl-expand-to-thunk expr)
203+
(define (jl-expand-to-thunk expr file line)
218204
(error-wrap (lambda ()
219-
(expand-toplevel-expr expr))))
205+
(expand-toplevel-expr expr file line))))
220206

221-
(define (jl-expand-to-thunk-stmt expr)
207+
(define (jl-expand-to-thunk-stmt expr file line)
222208
(jl-expand-to-thunk (if (toplevel-only-expr? expr)
223209
expr
224-
`(block ,expr (null)))))
210+
`(block ,expr (null)))
211+
file line))
225212

226213
(define (jl-expand-macroscope expr)
227214
(error-wrap (lambda ()
228215
(julia-expand-macroscope expr))))
229216

230217
; run whole frontend on a string. useful for testing.
231218
(define (fe str)
232-
(expand-toplevel-expr (julia-parse str)))
219+
(expand-toplevel-expr (julia-parse str) 'none 0))
233220

234221
(define (profile-e s)
235222
(with-exception-catcher

0 commit comments

Comments
 (0)