1 // Copyright 2015 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
9 // This implementation is a port based on the reference implementation found at:
10 // https://www.unicode.org/Public/PROGRAMS/BidiReferenceJava/
12 // described in Unicode Bidirectional Algorithm (UAX #9).
15 // There are two levels of input to the algorithm, since clients may prefer to
16 // supply some information from out-of-band sources rather than relying on the
20 // - Bidi class array, with externally supplied base line direction
23 // Output is separated into several stages:
25 // - levels array over entire paragraph
26 // - reordering array over entire paragraph
27 // - levels array over line
28 // - reordering array over line
30 // Note that for conformance to the Unicode Bidirectional Algorithm,
31 // implementations are only required to generate correct reordering and
32 // character directionality (odd or even levels) over a line. Generating
33 // identical level arrays over a line is not required. Bidi explicit format
34 // codes (LRE, RLE, LRO, RLO, PDF) and BN can be assigned arbitrary levels and
35 // positions as long as the rest of the input is properly reordered.
37 // As the algorithm is defined to operate on a single paragraph at a time, this
38 // implementation is written to handle single paragraphs. Thus rule P1 is
39 // presumed by this implementation-- the data provided to the implementation is
40 // assumed to be a single paragraph, and either contains no 'B' codes, or a
41 // single 'B' code at the end of the input. 'B' is allowed as input to
42 // illustrate how the algorithm assigns it a level.
44 // Also note that rules L3 and L4 depend on the rendering engine that uses the
45 // result of the bidi algorithm. This implementation assumes that the rendering
46 // engine expects combining marks in visual order (e.g. to the left of their
47 // base character in RTL runs) and that it adjusts the glyphs used to render
48 // mirrored characters that are in RTL runs so that they render appropriately.
50 // level is the embedding level of a character. Even embedding levels indicate
51 // left-to-right order and odd levels indicate right-to-left order. The special
52 // level of -1 is reserved for undefined order.
55 const implicitLevel level = -1
57 // in returns if x is equal to any of the values in set.
58 func (c Class) in(set ...Class) bool {
59 for _, s := range set {
67 // A paragraph contains the state of a paragraph.
68 type paragraph struct {
71 // Arrays of properties needed for paired bracket evaluation in N0
72 pairTypes []bracketType // paired Bracket types for paragraph
73 pairValues []rune // rune for opening bracket or pbOpen and pbClose; 0 for pbNone
75 embeddingLevel level // default: = implicitLevel;
77 // at the paragraph levels
81 // Index of matching PDI for isolate initiator characters. For other
82 // characters, the value of matchingPDI will be set to -1. For isolate
83 // initiators with no matching PDI, matchingPDI will be set to the length of
87 // Index of matching isolate initiator for PDI characters. For other
88 // characters, and for PDIs with no matching isolate initiator, the value of
89 // matchingIsolateInitiator will be set to -1.
90 matchingIsolateInitiator []int
93 // newParagraph initializes a paragraph. The user needs to supply a few arrays
94 // corresponding to the preprocessed text input. The types correspond to the
95 // Unicode BiDi classes for each rune. pairTypes indicates the bracket type for
96 // each rune. pairValues provides a unique bracket class identifier for each
97 // rune (suggested is the rune of the open bracket for opening and matching
98 // close brackets, after normalization). The embedding levels are optional, but
99 // may be supplied to encode embedding levels of styled text.
101 // TODO: return an error.
102 func newParagraph(types []Class, pairTypes []bracketType, pairValues []rune, levels level) *paragraph {
104 validatePbTypes(pairTypes)
105 validatePbValues(pairValues, pairTypes)
106 validateParagraphEmbeddingLevel(levels)
109 initialTypes: append([]Class(nil), types...),
110 embeddingLevel: levels,
112 pairTypes: pairTypes,
113 pairValues: pairValues,
115 resultTypes: append([]Class(nil), types...),
121 func (p *paragraph) Len() int { return len(p.initialTypes) }
123 // The algorithm. Does not include line-based processing (Rules L1, L2).
124 // These are applied later in the line-based phase of the algorithm.
125 func (p *paragraph) run() {
126 p.determineMatchingIsolates()
128 // 1) determining the paragraph level
129 // Rule P1 is the requirement for entering this algorithm.
131 // If no externally supplied paragraph embedding level, use default.
132 if p.embeddingLevel == implicitLevel {
133 p.embeddingLevel = p.determineParagraphEmbeddingLevel(0, p.Len())
136 // Initialize result levels to paragraph embedding level.
137 p.resultLevels = make([]level, p.Len())
138 setLevels(p.resultLevels, p.embeddingLevel)
140 // 2) Explicit levels and directions
142 p.determineExplicitEmbeddingLevels()
145 // We do not remove the embeddings, the overrides, the PDFs, and the BNs
146 // from the string explicitly. But they are not copied into isolating run
147 // sequences when they are created, so they are removed for all
148 // practical purposes.
151 // Run remainder of algorithm one isolating run sequence at a time
152 for _, seq := range p.determineIsolatingRunSequences() {
153 // 3) resolving weak types
155 seq.resolveWeakTypes()
157 // 4a) resolving paired brackets
159 resolvePairedBrackets(seq)
161 // 4b) resolving neutral types
163 seq.resolveNeutralTypes()
165 // 5) resolving implicit embedding levels
167 seq.resolveImplicitLevels()
169 // Apply the computed levels and types
170 seq.applyLevelsAndTypes()
173 // Assign appropriate levels to 'hide' LREs, RLEs, LROs, RLOs, PDFs, and
174 // BNs. This is for convenience, so the resulting level array will have
175 // a value for every character.
176 p.assignLevelsToCharactersRemovedByX9()
179 // determineMatchingIsolates determines the matching PDI for each isolate
180 // initiator and vice versa.
184 // At the end of this function:
186 // - The member variable matchingPDI is set to point to the index of the
187 // matching PDI character for each isolate initiator character. If there is
188 // no matching PDI, it is set to the length of the input text. For other
189 // characters, it is set to -1.
190 // - The member variable matchingIsolateInitiator is set to point to the
191 // index of the matching isolate initiator character for each PDI character.
192 // If there is no matching isolate initiator, or the character is not a PDI,
194 func (p *paragraph) determineMatchingIsolates() {
195 p.matchingPDI = make([]int, p.Len())
196 p.matchingIsolateInitiator = make([]int, p.Len())
198 for i := range p.matchingIsolateInitiator {
199 p.matchingIsolateInitiator[i] = -1
202 for i := range p.matchingPDI {
203 p.matchingPDI[i] = -1
205 if t := p.resultTypes[i]; t.in(LRI, RLI, FSI) {
207 for j := i + 1; j < p.Len(); j++ {
208 if u := p.resultTypes[j]; u.in(LRI, RLI, FSI) {
211 if depthCounter--; depthCounter == 0 {
213 p.matchingIsolateInitiator[j] = i
218 if p.matchingPDI[i] == -1 {
219 p.matchingPDI[i] = p.Len()
225 // determineParagraphEmbeddingLevel reports the resolved paragraph direction of
226 // the substring limited by the given range [start, end).
228 // Determines the paragraph level based on rules P2, P3. This is also used
229 // in rule X5c to find if an FSI should resolve to LRI or RLI.
230 func (p *paragraph) determineParagraphEmbeddingLevel(start, end int) level {
231 var strongType Class = unknownClass
234 for i := start; i < end; i++ {
235 if t := p.resultTypes[i]; t.in(L, AL, R) {
238 } else if t.in(FSI, LRI, RLI) {
239 i = p.matchingPDI[i] // skip over to the matching PDI
241 log.Panic("assert (i <= end)")
247 case unknownClass: // none found
248 // default embedding level when no strong types found is 0.
259 // This stack will store the embedding levels and override and isolated
261 type directionalStatusStack struct {
263 embeddingLevelStack [maxDepth + 1]level
264 overrideStatusStack [maxDepth + 1]Class
265 isolateStatusStack [maxDepth + 1]bool
268 func (s *directionalStatusStack) empty() { s.stackCounter = 0 }
269 func (s *directionalStatusStack) pop() { s.stackCounter-- }
270 func (s *directionalStatusStack) depth() int { return s.stackCounter }
272 func (s *directionalStatusStack) push(level level, overrideStatus Class, isolateStatus bool) {
273 s.embeddingLevelStack[s.stackCounter] = level
274 s.overrideStatusStack[s.stackCounter] = overrideStatus
275 s.isolateStatusStack[s.stackCounter] = isolateStatus
279 func (s *directionalStatusStack) lastEmbeddingLevel() level {
280 return s.embeddingLevelStack[s.stackCounter-1]
283 func (s *directionalStatusStack) lastDirectionalOverrideStatus() Class {
284 return s.overrideStatusStack[s.stackCounter-1]
287 func (s *directionalStatusStack) lastDirectionalIsolateStatus() bool {
288 return s.isolateStatusStack[s.stackCounter-1]
291 // Determine explicit levels using rules X1 - X8
292 func (p *paragraph) determineExplicitEmbeddingLevels() {
293 var stack directionalStatusStack
294 var overflowIsolateCount, overflowEmbeddingCount, validIsolateCount int
297 stack.push(p.embeddingLevel, ON, false)
299 for i, t := range p.resultTypes {
300 // Rules X2, X3, X4, X5, X5a, X5b, X5c
302 case RLE, LRE, RLO, LRO, RLI, LRI, FSI:
303 isIsolate := t.in(RLI, LRI, FSI)
304 isRTL := t.in(RLE, RLO, RLI)
306 // override if this is an FSI that resolves to RLI
308 isRTL = (p.determineParagraphEmbeddingLevel(i+1, p.matchingPDI[i]) == 1)
311 p.resultLevels[i] = stack.lastEmbeddingLevel()
312 if stack.lastDirectionalOverrideStatus() != ON {
313 p.resultTypes[i] = stack.lastDirectionalOverrideStatus()
320 newLevel = (stack.lastEmbeddingLevel() + 1) | 1
322 // least greater even
323 newLevel = (stack.lastEmbeddingLevel() + 2) &^ 1
326 if newLevel <= maxDepth && overflowIsolateCount == 0 && overflowEmbeddingCount == 0 {
330 // Push new embedding level, override status, and isolated
332 // No check for valid stack counter, since the level check
336 stack.push(newLevel, L, isIsolate)
338 stack.push(newLevel, R, isIsolate)
340 stack.push(newLevel, ON, isIsolate)
342 // Not really part of the spec
344 p.resultLevels[i] = newLevel
347 // This is an invalid explicit formatting character,
348 // so apply the "Otherwise" part of rules X2-X5b.
350 overflowIsolateCount++
351 } else { // !isIsolate
352 if overflowIsolateCount == 0 {
353 overflowEmbeddingCount++
360 if overflowIsolateCount > 0 {
361 overflowIsolateCount--
362 } else if validIsolateCount == 0 {
365 overflowEmbeddingCount = 0
366 for !stack.lastDirectionalIsolateStatus() {
372 p.resultLevels[i] = stack.lastEmbeddingLevel()
376 // Not really part of the spec
377 p.resultLevels[i] = stack.lastEmbeddingLevel()
379 if overflowIsolateCount > 0 {
381 } else if overflowEmbeddingCount > 0 {
382 overflowEmbeddingCount--
383 } else if !stack.lastDirectionalIsolateStatus() && stack.depth() >= 2 {
387 case B: // paragraph separator.
390 // These values are reset for clarity, in this implementation B
391 // can only occur as the last code in the array.
393 overflowIsolateCount = 0
394 overflowEmbeddingCount = 0
395 validIsolateCount = 0
396 p.resultLevels[i] = p.embeddingLevel
399 p.resultLevels[i] = stack.lastEmbeddingLevel()
400 if stack.lastDirectionalOverrideStatus() != ON {
401 p.resultTypes[i] = stack.lastDirectionalOverrideStatus()
407 type isolatingRunSequence struct {
410 indexes []int // indexes to the original string
412 types []Class // type of each character using the index
413 resolvedLevels []level // resolved levels after application of rules
418 func (i *isolatingRunSequence) Len() int { return len(i.indexes) }
420 func maxLevel(a, b level) level {
427 // Rule X10, second bullet: Determine the start-of-sequence (sos) and end-of-sequence (eos) types,
428 // either L or R, for each isolating run sequence.
429 func (p *paragraph) isolatingRunSequence(indexes []int) *isolatingRunSequence {
430 length := len(indexes)
431 types := make([]Class, length)
432 for i, x := range indexes {
433 types[i] = p.resultTypes[x]
436 // assign level, sos and eos
437 prevChar := indexes[0] - 1
438 for prevChar >= 0 && isRemovedByX9(p.initialTypes[prevChar]) {
441 prevLevel := p.embeddingLevel
443 prevLevel = p.resultLevels[prevChar]
447 lastType := types[length-1]
448 if lastType.in(LRI, RLI, FSI) {
449 succLevel = p.embeddingLevel
451 // the first character after the end of run sequence
452 limit := indexes[length-1] + 1
453 for ; limit < p.Len() && isRemovedByX9(p.initialTypes[limit]); limit++ {
456 succLevel = p.embeddingLevel
458 succLevel = p.resultLevels[limit]
461 level := p.resultLevels[indexes[0]]
462 return &isolatingRunSequence{
467 sos: typeForLevel(maxLevel(prevLevel, level)),
468 eos: typeForLevel(maxLevel(succLevel, level)),
472 // Resolving weak types Rules W1-W7.
474 // Note that some weak types (EN, AN) remain after this processing is
476 func (s *isolatingRunSequence) resolveWeakTypes() {
478 // on entry, only these types remain
479 s.assertOnly(L, R, AL, EN, ES, ET, AN, CS, B, S, WS, ON, NSM, LRI, RLI, FSI, PDI)
483 preceedingCharacterType := s.sos
484 for i, t := range s.types {
486 s.types[i] = preceedingCharacterType
488 if t.in(LRI, RLI, FSI, PDI) {
489 preceedingCharacterType = ON
491 preceedingCharacterType = t
496 // EN does not change at the start of the run, because sos != AL.
497 for i, t := range s.types {
499 for j := i - 1; j >= 0; j-- {
500 if t := s.types[j]; t.in(L, R, AL) {
511 for i, t := range s.types {
518 // Since there must be values on both sides for this rule to have an
519 // effect, the scan skips the first and last value.
521 // Although the scan proceeds left to right, and changes the type
522 // values in a way that would appear to affect the computations
523 // later in the scan, there is actually no problem. A change in the
524 // current value can only affect the value to its immediate right,
525 // and only affect it if it is ES or CS. But the current value can
526 // only change if the value to its right is not ES or CS. Thus
527 // either the current value will not change, or its change will have
528 // no effect on the remainder of the analysis.
530 for i := 1; i < s.Len()-1; i++ {
532 if t == ES || t == CS {
533 prevSepType := s.types[i-1]
534 succSepType := s.types[i+1]
535 if prevSepType == EN && succSepType == EN {
537 } else if s.types[i] == CS && prevSepType == AN && succSepType == AN {
544 for i, t := range s.types {
546 // locate end of sequence
548 runEnd := s.findRunLimit(runStart, ET)
550 // check values at ends of sequence
553 t = s.types[runStart-1]
557 if runEnd < len(s.types) {
562 setTypes(s.types[runStart:runEnd], EN)
564 // continue at end of sequence
570 for i, t := range s.types {
571 if t.in(ES, ET, CS) {
577 for i, t := range s.types {
579 // set default if we reach start of run
580 prevStrongType := s.sos
581 for j := i - 1; j >= 0; j-- {
583 if t == L || t == R { // AL's have been changed to R
588 if prevStrongType == L {
595 // 6) resolving neutral types Rules N1-N2.
596 func (s *isolatingRunSequence) resolveNeutralTypes() {
598 // on entry, only these types can be in resultTypes
599 s.assertOnly(L, R, EN, AN, B, S, WS, ON, RLI, LRI, FSI, PDI)
601 for i, t := range s.types {
603 case WS, ON, B, S, RLI, LRI, FSI, PDI:
604 // find bounds of run of neutrals
606 runEnd := s.findRunLimit(runStart, B, S, WS, ON, RLI, LRI, FSI, PDI)
608 // determine effective types at ends of run
609 var leadType, trailType Class
611 // Note that the character found can only be L, R, AN, or
616 leadType = s.types[runStart-1]
617 if leadType.in(AN, EN) {
621 if runEnd == len(s.types) {
624 trailType = s.types[runEnd]
625 if trailType.in(AN, EN) {
630 var resolvedType Class
631 if leadType == trailType {
633 resolvedType = leadType
636 // Notice the embedding level of the run is used, not
637 // the paragraph embedding level.
638 resolvedType = typeForLevel(s.level)
641 setTypes(s.types[runStart:runEnd], resolvedType)
643 // skip over run of (former) neutrals
649 func setLevels(levels []level, newLevel level) {
650 for i := range levels {
655 func setTypes(types []Class, newType Class) {
656 for i := range types {
661 // 7) resolving implicit embedding levels Rules I1, I2.
662 func (s *isolatingRunSequence) resolveImplicitLevels() {
664 // on entry, only these types can be in resultTypes
665 s.assertOnly(L, R, EN, AN)
667 s.resolvedLevels = make([]level, len(s.types))
668 setLevels(s.resolvedLevels, s.level)
670 if (s.level & 1) == 0 { // even level
671 for i, t := range s.types {
676 s.resolvedLevels[i] += 1
677 } else { // t == AN || t == EN
678 s.resolvedLevels[i] += 2
681 } else { // odd level
682 for i, t := range s.types {
686 } else { // t == L || t == AN || t == EN
687 s.resolvedLevels[i] += 1
693 // Applies the levels and types resolved in rules W1-I2 to the
694 // resultLevels array.
695 func (s *isolatingRunSequence) applyLevelsAndTypes() {
696 for i, x := range s.indexes {
697 s.p.resultTypes[x] = s.types[i]
698 s.p.resultLevels[x] = s.resolvedLevels[i]
702 // Return the limit of the run consisting only of the types in validSet
703 // starting at index. This checks the value at index, and will return
704 // index if that value is not in validSet.
705 func (s *isolatingRunSequence) findRunLimit(index int, validSet ...Class) int {
707 for ; index < len(s.types); index++ {
709 for _, valid := range validSet {
714 return index // didn't find a match in validSet
719 // Algorithm validation. Assert that all values in types are in the
721 func (s *isolatingRunSequence) assertOnly(codes ...Class) {
723 for i, t := range s.types {
724 for _, c := range codes {
729 log.Panicf("invalid bidi code %v present in assertOnly at position %d", t, s.indexes[i])
733 // determineLevelRuns returns an array of level runs. Each level run is
734 // described as an array of indexes into the input string.
736 // Determines the level runs. Rule X9 will be applied in determining the
737 // runs, in the way that makes sure the characters that are supposed to be
738 // removed are not included in the runs.
739 func (p *paragraph) determineLevelRuns() [][]int {
742 currentLevel := implicitLevel
744 for i := range p.initialTypes {
745 if !isRemovedByX9(p.initialTypes[i]) {
746 if p.resultLevels[i] != currentLevel {
747 // we just encountered a new run; wrap up last run
748 if currentLevel >= 0 { // only wrap it up if there was a run
749 allRuns = append(allRuns, run)
753 currentLevel = p.resultLevels[i]
758 // Wrap up the final run, if any
760 allRuns = append(allRuns, run)
765 // Definition BD13. Determine isolating run sequences.
766 func (p *paragraph) determineIsolatingRunSequences() []*isolatingRunSequence {
767 levelRuns := p.determineLevelRuns()
769 // Compute the run that each character belongs to
770 runForCharacter := make([]int, p.Len())
771 for i, run := range levelRuns {
772 for _, index := range run {
773 runForCharacter[index] = i
777 sequences := []*isolatingRunSequence{}
779 var currentRunSequence []int
781 for _, run := range levelRuns {
783 if p.initialTypes[first] != PDI || p.matchingIsolateInitiator[first] == -1 {
784 currentRunSequence = nil
787 // Copy this level run into currentRunSequence
788 currentRunSequence = append(currentRunSequence, run...)
790 last := currentRunSequence[len(currentRunSequence)-1]
791 lastT := p.initialTypes[last]
792 if lastT.in(LRI, RLI, FSI) && p.matchingPDI[last] != p.Len() {
793 run = levelRuns[runForCharacter[p.matchingPDI[last]]]
798 sequences = append(sequences, p.isolatingRunSequence(currentRunSequence))
804 // Assign level information to characters removed by rule X9. This is for
805 // ease of relating the level information to the original input data. Note
806 // that the levels assigned to these codes are arbitrary, they're chosen so
807 // as to avoid breaking level runs.
808 func (p *paragraph) assignLevelsToCharactersRemovedByX9() {
809 for i, t := range p.initialTypes {
810 if t.in(LRE, RLE, LRO, RLO, PDF, BN) {
812 p.resultLevels[i] = -1
815 // now propagate forward the levels information (could have
816 // propagated backward, the main thing is not to introduce a level
817 // break where one doesn't already exist).
819 if p.resultLevels[0] == -1 {
820 p.resultLevels[0] = p.embeddingLevel
822 for i := 1; i < len(p.initialTypes); i++ {
823 if p.resultLevels[i] == -1 {
824 p.resultLevels[i] = p.resultLevels[i-1]
827 // Embedding information is for informational purposes only so need not be
835 // getLevels computes levels array breaking lines at offsets in linebreaks.
838 // The linebreaks array must include at least one value. The values must be
839 // in strictly increasing order (no duplicates) between 1 and the length of
840 // the text, inclusive. The last value must be the length of the text.
841 func (p *paragraph) getLevels(linebreaks []int) []level {
842 // Note that since the previous processing has removed all
843 // P, S, and WS values from resultTypes, the values referred to
844 // in these rules are the initial types, before any processing
845 // has been applied (including processing of overrides).
847 // This example implementation has reinserted explicit format codes
848 // and BN, in order that the levels array correspond to the
849 // initial text. Their final placement is not normative.
850 // These codes are treated like WS in this implementation,
851 // so they don't interrupt sequences of WS.
853 validateLineBreaks(linebreaks, p.Len())
855 result := append([]level(nil), p.resultLevels...)
857 // don't worry about linebreaks since if there is a break within
858 // a series of WS values preceding S, the linebreak itself
860 for i, t := range p.initialTypes {
862 // Rule L1, clauses one and two.
863 result[i] = p.embeddingLevel
865 // Rule L1, clause three.
866 for j := i - 1; j >= 0; j-- {
867 if isWhitespace(p.initialTypes[j]) { // including format codes
868 result[j] = p.embeddingLevel
876 // Rule L1, clause four.
878 for _, limit := range linebreaks {
879 for j := limit - 1; j >= start; j-- {
880 if isWhitespace(p.initialTypes[j]) { // including format codes
881 result[j] = p.embeddingLevel
892 // getReordering returns the reordering of lines from a visual index to a
893 // logical index for line breaks at the given offsets.
895 // Lines are concatenated from left to right. So for example, the fifth
896 // character from the left on the third line is
898 // getReordering(linebreaks)[linebreaks[1] + 4]
900 // (linebreaks[1] is the position after the last character of the second
901 // line, which is also the index of the first character on the third line,
902 // and adding four gets the fifth character from the left).
904 // The linebreaks array must include at least one value. The values must be
905 // in strictly increasing order (no duplicates) between 1 and the length of
906 // the text, inclusive. The last value must be the length of the text.
907 func (p *paragraph) getReordering(linebreaks []int) []int {
908 validateLineBreaks(linebreaks, p.Len())
910 return computeMultilineReordering(p.getLevels(linebreaks), linebreaks)
913 // Return multiline reordering array for a given level array. Reordering
914 // does not occur across a line break.
915 func computeMultilineReordering(levels []level, linebreaks []int) []int {
916 result := make([]int, len(levels))
919 for _, limit := range linebreaks {
920 tempLevels := make([]level, limit-start)
921 copy(tempLevels, levels[start:])
923 for j, order := range computeReordering(tempLevels) {
924 result[start+j] = order + start
931 // Return reordering array for a given level array. This reorders a single
932 // line. The reordering is a visual to logical map. For example, the
933 // leftmost char is string.charAt(order[0]). Rule L2.
934 func computeReordering(levels []level) []int {
935 result := make([]int, len(levels))
937 for i := range result {
941 // locate highest level found on line.
942 // Note the rules say text, but no reordering across line bounds is
943 // performed, so this is sufficient.
944 highestLevel := level(0)
945 lowestOddLevel := level(maxDepth + 2)
946 for _, level := range levels {
947 if level > highestLevel {
950 if level&1 != 0 && level < lowestOddLevel {
951 lowestOddLevel = level
955 for level := highestLevel; level >= lowestOddLevel; level-- {
956 for i := 0; i < len(levels); i++ {
957 if levels[i] >= level {
958 // find range of text at or above this level
961 for limit < len(levels) && levels[limit] >= level {
965 for j, k := start, limit-1; j < k; j, k = j+1, k-1 {
966 result[j], result[k] = result[k], result[j]
968 // skip to end of level run
977 // isWhitespace reports whether the type is considered a whitespace type for the
979 func isWhitespace(c Class) bool {
981 case LRE, RLE, LRO, RLO, PDF, LRI, RLI, FSI, PDI, BN, WS:
987 // isRemovedByX9 reports whether the type is one of the types removed in X9.
988 func isRemovedByX9(c Class) bool {
990 case LRE, RLE, LRO, RLO, PDF, BN:
996 // typeForLevel reports the strong type (L or R) corresponding to the level.
997 func typeForLevel(level level) Class {
998 if (level & 0x1) == 0 {
1004 // TODO: change validation to not panic
1006 func validateTypes(types []Class) {
1007 if len(types) == 0 {
1008 log.Panic("types is null")
1010 for i, t := range types[:len(types)-1] {
1012 log.Panicf("B type before end of paragraph at index: %d", i)
1017 func validateParagraphEmbeddingLevel(embeddingLevel level) {
1018 if embeddingLevel != implicitLevel &&
1019 embeddingLevel != 0 &&
1020 embeddingLevel != 1 {
1021 log.Panicf("illegal paragraph embedding level: %d", embeddingLevel)
1025 func validateLineBreaks(linebreaks []int, textLength int) {
1027 for i, next := range linebreaks {
1029 log.Panicf("bad linebreak: %d at index: %d", next, i)
1033 if prev != textLength {
1034 log.Panicf("last linebreak was %d, want %d", prev, textLength)
1038 func validatePbTypes(pairTypes []bracketType) {
1039 if len(pairTypes) == 0 {
1040 log.Panic("pairTypes is null")
1042 for i, pt := range pairTypes {
1044 case bpNone, bpOpen, bpClose:
1046 log.Panicf("illegal pairType value at %d: %v", i, pairTypes[i])
1051 func validatePbValues(pairValues []rune, pairTypes []bracketType) {
1052 if pairValues == nil {
1053 log.Panic("pairValues is null")
1055 if len(pairTypes) != len(pairValues) {
1056 log.Panic("pairTypes is different length from pairValues")