OLD | NEW |
1 #!/usr/bin/env dart | 1 #!/usr/bin/env dart |
2 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 2 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
3 // for details. All rights reserved. Use of this source code is governed by a | 3 // for details. All rights reserved. Use of this source code is governed by a |
4 // BSD-style license that can be found in the LICENSE file. | 4 // BSD-style license that can be found in the LICENSE file. |
5 // | 5 // |
6 // ---------------------------------------------------------------------- | 6 // ---------------------------------------------------------------------- |
7 // This is a very specialized tool which was created in order to support | 7 // This is a very specialized tool which was created in order to support |
8 // adding hash values used as location markers in the LaTeX source of the | 8 // adding hash values used as location markers in the LaTeX source of the |
9 // language specification. It is intended to take its input file as the | 9 // language specification. It is intended to take its input file as the |
10 // first argument, an output file name as the second argument, and a | 10 // first argument, an output file name as the second argument, and a |
11 // hash listing file name as the third argument. From docs/language a | 11 // hash listing file name as the third argument. From docs/language a |
12 // typical usage would be as follows: | 12 // typical usage would be as follows: |
13 // | 13 // |
14 // dart | 14 // dart |
15 // --package-root=<build dir>/packages \ | 15 // --package-root=<build dir>/packages \ |
16 // ../../tools/addlatexhash.dart dartLangSpec.tex out.tex hash.txt | 16 // ../../tools/addlatexhash.dart dartLangSpec.tex out.tex hash.txt |
17 // | 17 // |
18 // This will produce a normalized variant out.tex of the language | 18 // This will produce a normalized variant out.tex of the language |
19 // specification with hash values filled in, and a listing hash.txt of | 19 // specification with hash values filled in, and a listing hash.txt of |
20 // all the hash values along with the label of their textual context | 20 // all the hash values along with the label of their textual context |
21 // (section, subsection, subsubsection, paragraph) . For more details, | 21 // (section, subsection, subsubsection, paragraph) . For more details, |
22 // please check the language specification source itself. | 22 // please check the language specification source itself. |
23 // | 23 // |
24 // NB: This utility assumes UN*X style line endings, \n, in the LaTeX | 24 // NB: This utility assumes UN*X style line endings, \n, in the LaTeX |
25 // source file receieved as input; it will not work with other styles. | 25 // source file receieved as input; it will not work with other styles. |
26 | 26 |
27 import 'dart:io'; | 27 import 'dart:io'; |
28 import 'dart:convert'; | 28 import 'dart:convert'; |
29 | 29 |
30 import 'package:crypto/crypto.dart'; | 30 import 'package:crypto/crypto.dart'; |
31 import 'package:convert/convert.dart'; | 31 import 'package:convert/convert.dart'; |
32 import 'package:utf/utf.dart'; | 32 import 'package:utf/utf.dart'; |
33 | 33 |
34 // ---------------------------------------------------------------------- | 34 // ---------------------------------------------------------------------- |
35 // Normalization of the text: removal or normalization of parts that | 35 // Normalization of the text: removal or normalization of parts that |
36 // do not affect the output from latex, such as white space. | 36 // do not affect the output from latex, such as white space. |
37 | 37 |
38 final commentRE = new RegExp(r"[^\\]%.*"); // NB: . does not match \n. | 38 final commentRE = new RegExp(r"[^\\]%.*"); // NB: . does not match \n. |
39 final whitespaceAllRE = new RegExp(r"^\s+$"); | 39 final whitespaceAllRE = new RegExp(r"^\s+$"); |
40 final whitespaceRE = new RegExp(r"(?:(?=\s).){2,}"); // \s except end-of-line | 40 final whitespaceRE = new RegExp(r"(?:(?=\s).){2,}"); // \s except end-of-line |
41 | 41 |
42 /// Removes [match]ing part of [line], adjusting that part with the | 42 /// Removes [match]ing part of [line], adjusting that part with the |
43 /// given [startOffset] and [endOffset], bounded to be valid indices | 43 /// given [startOffset] and [endOffset], bounded to be valid indices |
44 /// into the string if needed, then inserts [glue] where text was | 44 /// into the string if needed, then inserts [glue] where text was |
45 /// removed. If there is no match then [line] is returned. | 45 /// removed. If there is no match then [line] is returned. |
46 cutMatch(line, match, {startOffset: 0, endOffset: 0, glue: ""}) { | 46 cutMatch(line, match, {startOffset: 0, endOffset: 0, glue: ""}) { |
47 if (match == null) return line; | 47 if (match == null) return line; |
48 var start = match.start + startOffset; | 48 var start = match.start + startOffset; |
49 var end = match.end + endOffset; | 49 var end = match.end + endOffset; |
50 var len = line.length; | 50 var len = line.length; |
51 if (start < 0) start = 0; | 51 if (start < 0) start = 0; |
52 if (end > len) end = len; | 52 if (end > len) end = len; |
53 return line.substring(0, start) + glue + line.substring(end); | 53 return line.substring(0, start) + glue + line.substring(end); |
54 } | 54 } |
55 | 55 |
56 cutRegexp(line, re, {startOffset: 0, endOffset: 0, glue: ""}) { | 56 cutRegexp(line, re, {startOffset: 0, endOffset: 0, glue: ""}) { |
57 return cutMatch(line, re.firstMatch(line), | 57 return cutMatch(line, re.firstMatch(line), |
58 startOffset: startOffset, | 58 startOffset: startOffset, endOffset: endOffset, glue: glue); |
59 endOffset: endOffset, | |
60 glue: glue); | |
61 } | 59 } |
62 | 60 |
63 /// Removes the rest of [line] starting from the beginning of the | 61 /// Removes the rest of [line] starting from the beginning of the |
64 /// given [match], and adjusting with the given [offset]. If there | 62 /// given [match], and adjusting with the given [offset]. If there |
65 /// is no match then [line] is returned. | 63 /// is no match then [line] is returned. |
66 cutFromMatch(line, match, {offset: 0, glue: ""}) { | 64 cutFromMatch(line, match, {offset: 0, glue: ""}) { |
67 if (match == null) return line; | 65 if (match == null) return line; |
68 return line.substring(0, match.start + offset) + glue; | 66 return line.substring(0, match.start + offset) + glue; |
69 } | 67 } |
70 | 68 |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
107 var trimLine = line.trimLeft(); | 105 var trimLine = line.trimLeft(); |
108 if (trimLine.isEmpty) return justEol(line); | 106 if (trimLine.isEmpty) return justEol(line); |
109 return trimLine.replaceAll(whitespaceRE, " "); | 107 return trimLine.replaceAll(whitespaceRE, " "); |
110 } | 108 } |
111 | 109 |
112 /// Reduces sequences of >1 white-space-only lines in [lines] to 1, | 110 /// Reduces sequences of >1 white-space-only lines in [lines] to 1, |
113 /// and sequences of >1 comment-only lines to 1. Treats comment-only | 111 /// and sequences of >1 comment-only lines to 1. Treats comment-only |
114 /// lines as white-space-only when they occur in white-space-only | 112 /// lines as white-space-only when they occur in white-space-only |
115 /// line blocks. | 113 /// line blocks. |
116 multilineNormalize(lines) { | 114 multilineNormalize(lines) { |
117 var afterBlankLines = false; // Does [line] succeed >0 empty lines? | 115 var afterBlankLines = false; // Does [line] succeed >0 empty lines? |
118 var afterCommentLines = false; // Does [line] succeed >0 commentOnly lines? | 116 var afterCommentLines = false; // Does [line] succeed >0 commentOnly lines? |
119 var newLines = new List(); | 117 var newLines = new List(); |
120 for (var line in lines) { | 118 for (var line in lines) { |
121 if (afterBlankLines && afterCommentLines) { | 119 if (afterBlankLines && afterCommentLines) { |
122 // Previous line was both blank and a comment: not possible. | 120 // Previous line was both blank and a comment: not possible. |
123 throw "Bug, please report to eernst@"; | 121 throw "Bug, please report to eernst@"; |
124 } else if (afterBlankLines && !afterCommentLines) { | 122 } else if (afterBlankLines && !afterCommentLines) { |
125 // At least one line before [line] is wsOnly. | 123 // At least one line before [line] is wsOnly. |
126 if (!isWsOnly(line)) { | 124 if (!isWsOnly(line)) { |
127 // Blank line block ended. | 125 // Blank line block ended. |
128 afterCommentLines = isCommentOnly(line); | 126 afterCommentLines = isCommentOnly(line); |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
174 /// Selects the elements in the normalization pipeline. | 172 /// Selects the elements in the normalization pipeline. |
175 normalize(line) => normalizeWhitespace(stripComment(line)); | 173 normalize(line) => normalizeWhitespace(stripComment(line)); |
176 | 174 |
177 /// Selects the elements in the significant-spacing block | 175 /// Selects the elements in the significant-spacing block |
178 /// normalization pipeline. | 176 /// normalization pipeline. |
179 sispNormalize(line) => stripComment(line); | 177 sispNormalize(line) => stripComment(line); |
180 | 178 |
181 // Managing fragments with significant spacing. | 179 // Managing fragments with significant spacing. |
182 | 180 |
183 final dartCodeBeginRE = new RegExp(r"^\s*\\begin\s*\{dartCode\}"); | 181 final dartCodeBeginRE = new RegExp(r"^\s*\\begin\s*\{dartCode\}"); |
184 final dartCodeEndRE = new RegExp (r"^\s*\\end\s*\{dartCode\}"); | 182 final dartCodeEndRE = new RegExp(r"^\s*\\end\s*\{dartCode\}"); |
185 | 183 |
186 /// Recognizes beginning of dartCode block. | 184 /// Recognizes beginning of dartCode block. |
187 sispIsDartBegin(line) => line.contains(dartCodeBeginRE); | 185 sispIsDartBegin(line) => line.contains(dartCodeBeginRE); |
188 | 186 |
189 /// Recognizes end of dartCode block. | 187 /// Recognizes end of dartCode block. |
190 sispIsDartEnd(line) => line.contains(dartCodeEndRE); | 188 sispIsDartEnd(line) => line.contains(dartCodeEndRE); |
191 | 189 |
192 // ---------------------------------------------------------------------- | 190 // ---------------------------------------------------------------------- |
193 // Analyzing the input to point out "interesting" lines | 191 // Analyzing the input to point out "interesting" lines |
194 | 192 |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
265 | 263 |
266 /// Returns null except for \LMHash{} events, where it returns | 264 /// Returns null except for \LMHash{} events, where it returns |
267 /// the startLineNumber. This serves to specify a boundary because | 265 /// the startLineNumber. This serves to specify a boundary because |
268 /// the preceding \LMHash{} block should stop before the line of | 266 /// the preceding \LMHash{} block should stop before the line of |
269 /// this \LMHash{} command. Note that hash blocks may stop earlier, | 267 /// this \LMHash{} command. Note that hash blocks may stop earlier, |
270 /// because they cannot contain sectioning commands. | 268 /// because they cannot contain sectioning commands. |
271 getStartLineNumber() => null; | 269 getStartLineNumber() => null; |
272 } | 270 } |
273 | 271 |
274 class HashMarkerEvent extends HashEvent { | 272 class HashMarkerEvent extends HashEvent { |
275 | |
276 // Line number of first line in block that gets hashed. | 273 // Line number of first line in block that gets hashed. |
277 var startLineNumber; | 274 var startLineNumber; |
278 | 275 |
279 // Highest possible number of first line after block that gets | 276 // Highest possible number of first line after block that gets |
280 // hashed (where the next \LMHash{} occurs). Note that this value | 277 // hashed (where the next \LMHash{} occurs). Note that this value |
281 // is not known initially (because that line has not yet been | 278 // is not known initially (because that line has not yet been |
282 // reached), so [endLineNumber] will be initialized in a separate | 279 // reached), so [endLineNumber] will be initialized in a separate |
283 // scan. Also note that the block may end earlier, because a block | 280 // scan. Also note that the block may end earlier, because a block |
284 // ends if it would otherwise include a sectioning command. | 281 // ends if it would otherwise include a sectioning command. |
285 var endLineNumber; | 282 var endLineNumber; |
286 | 283 |
287 HashMarkerEvent(this.startLineNumber); | 284 HashMarkerEvent(this.startLineNumber); |
288 | 285 |
289 setEndLineNumber(n) { endLineNumber = n; } | 286 setEndLineNumber(n) { |
| 287 endLineNumber = n; |
| 288 } |
| 289 |
290 getStartLineNumber() => startLineNumber; | 290 getStartLineNumber() => startLineNumber; |
291 } | 291 } |
292 | 292 |
293 class HashLabelEvent extends HashEvent { | 293 class HashLabelEvent extends HashEvent { |
294 var labelText; | 294 var labelText; |
295 HashLabelEvent(this.labelText); | 295 HashLabelEvent(this.labelText); |
296 } | 296 } |
297 | 297 |
298 class HashAnalyzer { | 298 class HashAnalyzer { |
299 // List of kinds of pending (= most recently seen) sectioning command. | 299 // List of kinds of pending (= most recently seen) sectioning command. |
(...skipping 24 matching lines...) Expand all Loading... |
324 setPendingToParagraph() { | 324 setPendingToParagraph() { |
325 pendingSectioning = PENDING_IS_PARAGRAPH; | 325 pendingSectioning = PENDING_IS_PARAGRAPH; |
326 } | 326 } |
327 | 327 |
328 clearPending() { | 328 clearPending() { |
329 pendingSectioning = PENDING_IS_NONE; | 329 pendingSectioning = PENDING_IS_NONE; |
330 } | 330 } |
331 | 331 |
332 sectioningPrefix() { | 332 sectioningPrefix() { |
333 switch (pendingSectioning) { | 333 switch (pendingSectioning) { |
334 case PENDING_IS_SECTION: return "sec:"; | 334 case PENDING_IS_SECTION: |
335 case PENDING_IS_SUBSECTION: return "subsec:"; | 335 return "sec:"; |
336 case PENDING_IS_SUBSUBSECTION: return "subsubsec:"; | 336 case PENDING_IS_SUBSECTION: |
337 case PENDING_IS_PARAGRAPH: return "par:"; | 337 return "subsec:"; |
| 338 case PENDING_IS_SUBSUBSECTION: |
| 339 return "subsubsec:"; |
| 340 case PENDING_IS_PARAGRAPH: |
| 341 return "par:"; |
338 case PENDING_IS_NONE: | 342 case PENDING_IS_NONE: |
339 throw | 343 throw "\\LMHash{..} should only be used after a sectioning command " + |
340 "\\LMHash{..} should only be used after a sectioning command " + | |
341 "(\\section, \\subsection, \\subsubsection, \\paragraph)"; | 344 "(\\section, \\subsection, \\subsubsection, \\paragraph)"; |
342 default: | 345 default: |
343 // set of PENDING_IS_.. was extended, but updates here omitted | 346 // set of PENDING_IS_.. was extended, but updates here omitted |
344 throw "Bug, please report to eernst@"; | 347 throw "Bug, please report to eernst@"; |
345 } | 348 } |
346 } | 349 } |
347 | 350 |
348 analyze(line) { | 351 analyze(line) { |
349 var currentLineNumber = lineNumber++; | 352 var currentLineNumber = lineNumber++; |
350 if (isHashMarker(line)) { | 353 if (isHashMarker(line)) { |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
386 // ---------------------------------------------------------------------- | 389 // ---------------------------------------------------------------------- |
387 // Removal of non-normative elements of the text (rationale, commentary). | 390 // Removal of non-normative elements of the text (rationale, commentary). |
388 | 391 |
389 /// Returns [line] without the command [cmdName] (based on a match | 392 /// Returns [line] without the command [cmdName] (based on a match |
390 /// on "\\cmdName\s*{..}") starting at [startIndex]; note that it is | 393 /// on "\\cmdName\s*{..}") starting at [startIndex]; note that it is |
391 /// assumed but not checked that [line] contains "\\cmdType\s*{..", | 394 /// assumed but not checked that [line] contains "\\cmdType\s*{..", |
392 /// and note that the end of the {..} block is found via brace matching | 395 /// and note that the end of the {..} block is found via brace matching |
393 /// (i.e., nested {..} blocks are handled), but it may break if '{' is | 396 /// (i.e., nested {..} blocks are handled), but it may break if '{' is |
394 /// made an active character etc.etc. | 397 /// made an active character etc.etc. |
395 removeCommand(line, cmdName, startIndex) { | 398 removeCommand(line, cmdName, startIndex) { |
396 const BACKSLASH = 92; // char code for '\\'. | 399 const BACKSLASH = 92; // char code for '\\'. |
397 const BRACE_BEGIN = 123; // char code for '{'. | 400 const BRACE_BEGIN = 123; // char code for '{'. |
398 const BRACE_END = 125; // char code for '}'. | 401 const BRACE_END = 125; // char code for '}'. |
399 | 402 |
400 var blockStartIndex = startIndex + cmdName.length + 1; | 403 var blockStartIndex = startIndex + cmdName.length + 1; |
401 while (blockStartIndex < line.length && | 404 while (blockStartIndex < line.length && |
402 line.codeUnitAt(blockStartIndex) != BRACE_BEGIN) { | 405 line.codeUnitAt(blockStartIndex) != BRACE_BEGIN) { |
403 blockStartIndex++; | 406 blockStartIndex++; |
404 } | 407 } |
405 blockStartIndex++; | 408 blockStartIndex++; |
406 if (blockStartIndex > line.length) { | 409 if (blockStartIndex > line.length) { |
407 throw "Bug, please report to eernst@"; | 410 throw "Bug, please report to eernst@"; |
408 } | 411 } |
409 // [blockStartIndex] has index just after '{'. | 412 // [blockStartIndex] has index just after '{'. |
410 | 413 |
411 var afterEscape = false; // Is true iff [index] is just after '{'. | 414 var afterEscape = false; // Is true iff [index] is just after '{'. |
412 var braceLevel = 1; // Have seen so many '{'s minus so many '}'s. | 415 var braceLevel = 1; // Have seen so many '{'s minus so many '}'s. |
413 | 416 |
414 for (var index = blockStartIndex; index < line.length; index++) { | 417 for (var index = blockStartIndex; index < line.length; index++) { |
415 switch (line.codeUnitAt(index)) { | 418 switch (line.codeUnitAt(index)) { |
416 case BRACE_BEGIN: | 419 case BRACE_BEGIN: |
417 if (afterEscape) { | 420 if (afterEscape) { |
418 afterEscape = false; | 421 afterEscape = false; |
419 } else { | 422 } else { |
420 braceLevel++; | 423 braceLevel++; |
421 } | 424 } |
422 break; | 425 break; |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
471 // Recognition of line blocks, insertion of block hash into \LMHash{}. | 474 // Recognition of line blocks, insertion of block hash into \LMHash{}. |
472 | 475 |
473 final latexArgumentRE = new RegExp(r"\{.*\}"); | 476 final latexArgumentRE = new RegExp(r"\{.*\}"); |
474 | 477 |
475 cleanupLine(line) => cutRegexp(line, commentRE, startOffset: 1).trimRight(); | 478 cleanupLine(line) => cutRegexp(line, commentRE, startOffset: 1).trimRight(); |
476 | 479 |
477 /// Returns concatenation of all lines from [startIndex] in [lines] until | 480 /// Returns concatenation of all lines from [startIndex] in [lines] until |
478 /// a hash block terminator is encountered or [nextIndex] reached (if so, | 481 /// a hash block terminator is encountered or [nextIndex] reached (if so, |
479 /// the line lines[nextIndex] itself is not included); each line is cleaned | 482 /// the line lines[nextIndex] itself is not included); each line is cleaned |
480 /// up using [cleanupLine], and " " is inserted between the lines gathered. | 483 /// up using [cleanupLine], and " " is inserted between the lines gathered. |
481 gatherLines(lines, startIndex, nextIndex) => | 484 gatherLines(lines, startIndex, nextIndex) => lines |
482 lines.getRange(startIndex, nextIndex) | 485 .getRange(startIndex, nextIndex) |
483 .takeWhile(isntHashBlockTerminator) | 486 .takeWhile(isntHashBlockTerminator) |
484 .map(cleanupLine) | 487 .map(cleanupLine) |
485 .join(" "); | 488 .join(" "); |
486 | 489 |
487 /// Computes the hash value for the line block starting at [startIndex] | 490 /// Computes the hash value for the line block starting at [startIndex] |
488 /// in [lines], stopping just before [nextIndex]. SIDE EFFECT: | 491 /// in [lines], stopping just before [nextIndex]. SIDE EFFECT: |
489 /// Outputs the simplified text and its hash value to [listSink]. | 492 /// Outputs the simplified text and its hash value to [listSink]. |
490 computeHashValue(lines, startIndex, nextIndex, listSink) { | 493 computeHashValue(lines, startIndex, nextIndex, listSink) { |
491 final gatheredLine = gatherLines(lines, startIndex, nextIndex); | 494 final gatheredLine = gatherLines(lines, startIndex, nextIndex); |
492 final simplifiedLine = simplifyLine(gatheredLine); | 495 final simplifiedLine = simplifyLine(gatheredLine); |
493 listSink.write(" % $simplifiedLine\n"); | 496 listSink.write(" % $simplifiedLine\n"); |
494 var digest = sha1.convert(encodeUtf8(simplifiedLine)); | 497 var digest = sha1.convert(encodeUtf8(simplifiedLine)); |
495 return digest.bytes; | 498 return digest.bytes; |
496 } | 499 } |
497 | 500 |
498 computeHashString(lines, startIndex, nextIndex, listSink) => | 501 computeHashString(lines, startIndex, nextIndex, listSink) => |
499 hex.encode(computeHashValue(lines, | 502 hex.encode(computeHashValue(lines, startIndex, nextIndex, listSink)); |
500 startIndex, | |
501 nextIndex, | |
502 listSink)); | |
503 | 503 |
504 /// Computes and adds hashes to \LMHash{} lines in [lines] (which | 504 /// Computes and adds hashes to \LMHash{} lines in [lines] (which |
505 /// must be on the line numbers specified in [hashEvents]), and emits | 505 /// must be on the line numbers specified in [hashEvents]), and emits |
506 /// sectioning markers and hash values to [listSink], along with | 506 /// sectioning markers and hash values to [listSink], along with |
507 /// "comments" containing the simplified text (using the format | 507 /// "comments" containing the simplified text (using the format |
508 /// ' % <text>', where the text is one, long line, for easy grepping | 508 /// ' % <text>', where the text is one, long line, for easy grepping |
509 /// etc.). | 509 /// etc.). |
510 addHashMarks(lines, hashEvents, listSink) { | 510 addHashMarks(lines, hashEvents, listSink) { |
511 for (var hashEvent in hashEvents) { | 511 for (var hashEvent in hashEvents) { |
512 if (hashEvent is HashMarkerEvent) { | 512 if (hashEvent is HashMarkerEvent) { |
513 var start = hashEvent.startLineNumber; | 513 var start = hashEvent.startLineNumber; |
514 var end = hashEvent.endLineNumber; | 514 var end = hashEvent.endLineNumber; |
515 final hashValue = computeHashString(lines, start + 1, end, listSink); | 515 final hashValue = computeHashString(lines, start + 1, end, listSink); |
516 lines[start] = | 516 lines[start] = |
517 lines[start].replaceAll(latexArgumentRE, "{" + hashValue + "}"); | 517 lines[start].replaceAll(latexArgumentRE, "{" + hashValue + "}"); |
518 listSink.write(" $hashValue\n"); | 518 listSink.write(" $hashValue\n"); |
519 } else if (hashEvent is HashLabelEvent) { | 519 } else if (hashEvent is HashLabelEvent) { |
520 listSink.write("${hashEvent.labelText}\n"); | 520 listSink.write("${hashEvent.labelText}\n"); |
521 } | 521 } |
522 } | 522 } |
523 } | 523 } |
524 | 524 |
525 /// Transforms LaTeX input to LaTeX output plus hash value list file. | 525 /// Transforms LaTeX input to LaTeX output plus hash value list file. |
526 main ([args]) { | 526 main([args]) { |
527 if (args.length != 3) { | 527 if (args.length != 3) { |
528 print("Usage: addlatexhash.dart <input-file> <output-file> <list-file>"); | 528 print("Usage: addlatexhash.dart <input-file> <output-file> <list-file>"); |
529 throw "Received ${args.length} arguments, expected three"; | 529 throw "Received ${args.length} arguments, expected three"; |
530 } | 530 } |
531 | 531 |
532 // Get LaTeX source. | 532 // Get LaTeX source. |
533 var inputFile = new File(args[0]); | 533 var inputFile = new File(args[0]); |
534 assert(inputFile.existsSync()); | 534 assert(inputFile.existsSync()); |
535 var lines = inputFile.readAsLinesSync(); | 535 var lines = inputFile.readAsLinesSync(); |
536 | 536 |
(...skipping 25 matching lines...) Expand all Loading... |
562 normalizedLines = multilineNormalize(normalizedLines); | 562 normalizedLines = multilineNormalize(normalizedLines); |
563 | 563 |
564 // Insert hash values. | 564 // Insert hash values. |
565 var hashEvents = findHashEvents(normalizedLines); | 565 var hashEvents = findHashEvents(normalizedLines); |
566 addHashMarks(normalizedLines, hashEvents, listSink); | 566 addHashMarks(normalizedLines, hashEvents, listSink); |
567 | 567 |
568 // Produce/finalize output. | 568 // Produce/finalize output. |
569 outputFile.writeAsStringSync(normalizedLines.join()); | 569 outputFile.writeAsStringSync(normalizedLines.join()); |
570 listSink.close(); | 570 listSink.close(); |
571 } | 571 } |
OLD | NEW |