@@ -52,7 +52,7 @@ sub load_script_file {
52
52
53
53
# Load the given text, but do not execute it.
54
54
# @param $self
55
- # @param $text {String} The source text
55
+ # @param $text {String} The source text, **which load_script_text may modify.**
56
56
# @param $fn {String} Filename to use in debugging messages
57
57
# @param $add_Ln {boolean, default false} If true, add a Ln directive for the
58
58
# current version if there isn't one in the script.
@@ -65,6 +65,14 @@ sub load_script_text {
65
65
# Text to wrap around the script
66
66
my ($leader , $trailer ) = (' ' , ' ' );
67
67
68
+ $text .= " \n " ; # for consistency, e.g., so `axk -e 'L0'` won't crash.
69
+
70
+ # Prep the filename
71
+ # $fn =~ s{\\}{\\\\}g; # This doesn't seem to be necessary based on
72
+ # the regex given for #line in perlsyn.
73
+ $fn =~ s { "} { -} g ;
74
+ # as far as I can tell, #line can't handle embedded quotes.
75
+
68
76
=pod
69
77
70
78
=head1 SPECIFYING THE AXK LANGUAGE VERSION
@@ -96,20 +104,103 @@ files.
96
104
97
105
=cut
98
106
99
- unless ($text =~ s { ^\h *L\h *0*(\d +)\h *;?} { use XML::Axk::L$1 ;} mg ) {
107
+ my $has_lang ;
108
+ my $curr_trailer ;
109
+ my $lines_added = 0;
110
+
111
+ # Regex to match an Ln specification
112
+ my $RE_Ln = qr {
113
+ ^\h *L\h * # has to start the line
114
+ (?|
115
+ (?:0*(\d +)) # digit form
116
+ | (?:``(\w\+ )) # alpha form, e.g., L``foo. I think ``
117
+ ) # is fairly unlikely in real text.
118
+ \b\h *;? # permit trailers for ergonomics
119
+ } mx ;
120
+
121
+ while ( $text =~ m /$RE_Ln / g ) {
122
+ my @idxes =($- [0], $+ [0]);
123
+ my $lang = $1 ;
124
+ # say "Match $lang in =$text= at ", join ',',@idxes;
125
+ my $oldpos = pos ($text );
126
+ my $length_delta = 0; # how much to adjust pos($text) by
127
+ # say "Old pos: ", $oldpos;
128
+
129
+ # Get line number in the original file
130
+ my $orig_lineno = 1 + ( () = substr ($text , 0, $idxes [0]) =~ / \n /g );
131
+ # Thanks to ikegami, http://www.perlmonks.org/?node_id=968352
132
+ $orig_lineno -= $lines_added ;
133
+
134
+ # Ln must be followed by whitespace and a newline.
135
+ # This is to keep the line numbering vaguely consistent.
136
+ my ($removed ) = (substr ($text , $idxes [1]) =~ s /\A (\h *\n )// );
137
+
138
+ unless ($removed ) {
139
+ # Tell the caller where in the source file the problem is
140
+ eval " #line $orig_lineno \" $fn \"\n " .
141
+ " die(\" L$lang indicator must be on its own line\" );" ;
142
+ die $@ ;
143
+ }
144
+
145
+ $length_delta -= length ($removed );
146
+
147
+ my $replacement = ' ' ;
148
+ $has_lang = 1;
149
+
150
+ # End an existing capture if we're switching languages
151
+ if ($curr_trailer ) {
152
+ $replacement .= " \n $curr_trailer \n " .
153
+ " #line $orig_lineno \" $fn \"\n " ;
154
+ $curr_trailer =' ' ;
155
+ ++$lines_added ;
156
+ }
157
+
158
+ # Does this language parse the source text itself?
159
+ my $want_text ;
160
+ eval " require XML::Axk::L$lang " ;
161
+ die " Can't find language $lang : $@ " if $@ ;
162
+ do {
163
+ no strict ' refs' ;
164
+ $want_text = ${" XML::Axk::L${lang} ::C_WANT_TEXT" };
165
+ };
166
+
167
+ unless ($want_text ) { # Easy case: the script's code is still Perl
168
+ $replacement .= " use XML::Axk::L$lang ;\n " ;
169
+
170
+ } else { # Harder case: give the Ln the source text
171
+ $curr_trailer =
172
+ " AXK_EMBEDDED_SOURCE_DO_NOT_TYPE_THIS_YOURSELF_OR_ELSE" ;
173
+
174
+ my $following_lineno = $orig_lineno +1;
175
+ # Number of first line of the text in that language
176
+ $replacement .=
177
+ " use XML::Axk::L$lang \" $fn \" , $following_lineno , " .
178
+ " <<'$curr_trailer ';\n " ;
179
+ }
180
+
181
+ if ($replacement ) {
182
+ substr ($text , $idxes [0], $idxes [1] - $idxes [0]) = $replacement ;
183
+ $length_delta += (length ($replacement ) - ($idxes [1] - $idxes [0]));
184
+ }
185
+
186
+ if ($length_delta ) {
187
+ # say "pos = $oldpos; Delta pos = $length_delta";
188
+ pos ($text ) = $oldpos + $length_delta ;
189
+ }
190
+ } # foreach lang
191
+
192
+ $text .= " \n $curr_trailer \n " if $curr_trailer ;
193
+
194
+ unless ($has_lang ) {
100
195
if ($add_Ln ) {
101
196
$leader = " use XML::Axk::L1;\n " ; # To be updated over time
102
197
} else {
103
198
croak " No language (Ln) specified in file $fn " ;
104
199
}
105
200
}
106
201
107
- # Mark the filename for the sake of error messages.
108
- # $fn =~ s{\\}{\\\\}g; # This doesn't seem to be necessary based on
109
- # the regex given for #line in perlsyn.
110
- $fn =~ s { "} { -} g ;
111
- # as far as I can tell, #line can't handle embedded quotes.
112
202
203
+ # Mark the filename for the sake of error messages.
113
204
$leader .= " ;\n #line 1 \" $fn \"\n " ;
114
205
# Extra ; so the #line directive is in its own statement.
115
206
# Thanks to https://www.effectiveperlprogramming.com/2011/06/set-the-line-number-and-filename-of-string-evals/
0 commit comments