|
| 1 | +using Tokenize |
| 2 | + |
1 | 3 | # ––––––––––––––– |
2 | 4 | # Some file utils |
3 | 5 | # ––––––––––––––– |
|
50 | 52 | # –––––––––––––– |
51 | 53 | # The Good Stuff |
52 | 54 | # –––––––––––––– |
| 55 | +const SCOPE_STARTERS = [Tokens.BEGIN, |
| 56 | + Tokens.WHILE, |
| 57 | + Tokens.IF, |
| 58 | + Tokens.FOR, |
| 59 | + Tokens.TRY, |
| 60 | + Tokens.FUNCTION, |
| 61 | + Tokens.MACRO, |
| 62 | + Tokens.LET, |
| 63 | + Tokens.ABSTRACT, |
| 64 | + Tokens.TYPE, |
| 65 | + Tokens.BITSTYPE, |
| 66 | + Tokens.IMMUTABLE, |
| 67 | + Tokens.DO, |
| 68 | + Tokens.QUOTE] |
| 69 | + |
| 70 | +const MODULE_STARTERS = [Tokens.MODULE, Tokens.BAREMODULE] |
53 | 71 |
|
54 | 72 | """ |
55 | 73 | Takes Julia source code and a line number, gives back the string name |
56 | 74 | of the module at that line. |
57 | 75 | """ |
58 | | -# TODO: do this properly (e.g. by using JuliaParser): `end`-recognition is super |
59 | | -# naive below |
60 | 76 | function codemodule(code, line) |
61 | 77 | stack = String[] |
62 | | - # count all unterminated block openers |
| 78 | + # count all unterminated block openers and brackets |
63 | 79 | n_openers = 0 |
64 | | - for l in split(code, "\n")[1:line] |
65 | | - # match all new modules and push them to stack |
66 | | - m = match(r"^\s*(?:module|baremodule) ([A-Za-z]+)", l) |
67 | | - if m != nothing |
68 | | - push!(stack, m.captures[1]) |
69 | | - continue |
70 | | - end |
71 | | - |
72 | | - # match all block openers that aren't modules |
73 | | - if ismatch(r"\b(if|while|for|begin|function|macro|type|immutable|try|let|do|quote)\b(?!.*(\s|;)end\b).*$", l) |
74 | | - n_openers += 1 |
75 | | - continue |
76 | | - end |
77 | | - |
78 | | - # match all `end`s with only whitespace around them |
79 | | - if ismatch(r"^\s*(?:end)\s*$", l) |
80 | | - # if there are no more open blocks, pop the latest |
81 | | - # added (sub)module from the stack if it isn't empty already |
82 | | - if n_openers == 0 |
83 | | - !isempty(stack) && pop!(stack) |
84 | | - else |
85 | | - n_openers -= 1 |
| 80 | + n_brackets = 0 |
| 81 | + # index of next modulename token |
| 82 | + next_modulename = -1 |
| 83 | + |
| 84 | + ts = tokenize(code) |
| 85 | + |
| 86 | + for (i, t) in enumerate(ts) |
| 87 | + Tokens.startpos(t)[1] > line && break |
| 88 | + |
| 89 | + # ignore everything in square brackets, because of the ambiguity |
| 90 | + # with `end` indexing |
| 91 | + if Tokens.kind(t) == Tokens.LSQUARE |
| 92 | + n_brackets += 1 |
| 93 | + elseif n_brackets > 0 |
| 94 | + if Tokens.kind(t) == Tokens.RSQUARE |
| 95 | + n_brackets -= 1 |
86 | 96 | end |
| 97 | + elseif Tokens.exactkind(t) in MODULE_STARTERS # new module |
| 98 | + next_modulename = i + 2 |
| 99 | + elseif i == next_modulename && Tokens.kind(t) == Tokens.IDENTIFIER |
| 100 | + push!(stack, Tokens.untokenize(t)) |
| 101 | + elseif Tokens.exactkind(t) in SCOPE_STARTERS # new non-module scope |
| 102 | + n_openers += 1 |
| 103 | + elseif Tokens.exactkind(t) == Tokens.END # scope ended |
| 104 | + n_openers == 0 ? (!isempty(stack) && pop!(stack)) : n_openers -= 1 |
87 | 105 | end |
88 | 106 | end |
| 107 | + |
89 | 108 | return join(stack, ".") |
90 | 109 | end |
91 | 110 |
|
|
0 commit comments