scratch – Blame information for rev 125

Subversion Repositories:
Rev:
Rev Author Line No. Line
75 office 1  
2 # Pattern is a zero-conflict wrapper extending RegExp features
3 # in order to make YAML parsing regex more expressive.
4 #
5 class Pattern
6  
7 # @property [RegExp] The RegExp instance
8 regex: null
9  
10 # @property [String] The raw regex string
11 rawRegex: null
12  
13 # @property [String] The cleaned regex string (used to create the RegExp instance)
14 cleanedRegex: null
15  
16 # @property [Object] The dictionary mapping names to capturing bracket numbers
17 mapping: null
18  
19 # Constructor
20 #
21 # @param [String] rawRegex The raw regex string defining the pattern
22 #
23 constructor: (rawRegex, modifiers = '') ->
24 cleanedRegex = ''
25 len = rawRegex.length
26 mapping = null
27  
28 # Cleanup raw regex and compute mapping
29 capturingBracketNumber = 0
30 i = 0
31 while i < len
32 _char = rawRegex.charAt(i)
33 if _char is '\\'
34 # Ignore next character
35 cleanedRegex += rawRegex[i..i+1]
36 i++
37 else if _char is '('
38 # Increase bracket number, only if it is capturing
39 if i < len - 2
40 part = rawRegex[i..i+2]
41 if part is '(?:'
42 # Non-capturing bracket
43 i += 2
44 cleanedRegex += part
45 else if part is '(?<'
46 # Capturing bracket with possibly a name
47 capturingBracketNumber++
48 i += 2
49 name = ''
50 while i + 1 < len
51 subChar = rawRegex.charAt(i + 1)
52 if subChar is '>'
53 cleanedRegex += '('
54 i++
55 if name.length > 0
56 # Associate a name with a capturing bracket number
57 mapping ?= {}
58 mapping[name] = capturingBracketNumber
59 break
60 else
61 name += subChar
62  
63 i++
64 else
65 cleanedRegex += _char
66 capturingBracketNumber++
67 else
68 cleanedRegex += _char
69 else
70 cleanedRegex += _char
71  
72 i++
73  
74 @rawRegex = rawRegex
75 @cleanedRegex = cleanedRegex
76 @regex = new RegExp @cleanedRegex, 'g'+modifiers.replace('g', '')
77 @mapping = mapping
78  
79  
80 # Executes the pattern's regex and returns the matching values
81 #
82 # @param [String] str The string to use to execute the pattern
83 #
84 # @return [Array] The matching values extracted from capturing brackets or null if nothing matched
85 #
86 exec: (str) ->
87 @regex.lastIndex = 0
88 matches = @regex.exec str
89  
90 if not matches?
91 return null
92  
93 if @mapping?
94 for name, index of @mapping
95 matches[name] = matches[index]
96  
97 return matches
98  
99  
100 # Tests the pattern's regex
101 #
102 # @param [String] str The string to use to test the pattern
103 #
104 # @return [Boolean] true if the string matched
105 #
106 test: (str) ->
107 @regex.lastIndex = 0
108 return @regex.test str
109  
110  
111 # Replaces occurences matching with the pattern's regex with replacement
112 #
113 # @param [String] str The source string to perform replacements
114 # @param [String] replacement The string to use in place of each replaced occurence.
115 #
116 # @return [String] The replaced string
117 #
118 replace: (str, replacement) ->
119 @regex.lastIndex = 0
120 return str.replace @regex, replacement
121  
122  
123 # Replaces occurences matching with the pattern's regex with replacement and
124 # get both the replaced string and the number of replaced occurences in the string.
125 #
126 # @param [String] str The source string to perform replacements
127 # @param [String] replacement The string to use in place of each replaced occurence.
128 # @param [Integer] limit The maximum number of occurences to replace (0 means infinite number of occurences)
129 #
130 # @return [Array] A destructurable array containing the replaced string and the number of replaced occurences. For instance: ["my replaced string", 2]
131 #
132 replaceAll: (str, replacement, limit = 0) ->
133 @regex.lastIndex = 0
134 count = 0
135 while @regex.test(str) and (limit is 0 or count < limit)
136 @regex.lastIndex = 0
125 office 137 str = str.replace @regex, replacement
75 office 138 count++
139  
140 return [str, count]
141  
142  
143 module.exports = Pattern
144