| 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 |