1 | 1 | simandl | /* |
2 | | | ParseMaster, version 1.0 (pre-release) (2005/05/12) x6 |
3 | | | Copyright 2005, Dean Edwards |
4 | | | Web: http://dean.edwards.name/ |
5 | | | |
6 | | | This software is licensed under the CC-GNU LGPL |
7 | | | Web: http://creativecommons.org/licenses/LGPL/2.1/ |
8 | | | */ |
9 | | | |
10 | | | /* a multi-pattern parser */ |
11 | | | |
12 | | | /* --- (include) http://dean.edwards.name/my/fix-ie5.js --- */ |
13 | | | /* --- (require) http://dean.edwards.name/common/common.js --- */ |
14 | | | |
15 | | | function ParseMaster() { |
16 | | | // constants |
17 | | | var $EXPRESSION = 0, $REPLACEMENT = 1, $LENGTH = 2; |
18 | | | // used to determine nesting levels |
19 | | | var $GROUPS = /\(/g, $SUB_REPLACE = /\$\d/, $INDEXED = /^\$\d+$/, |
20 | | | $TRIM = /(['"])\1\+(.*)\+\1\1$/, $$ESCAPE = /\\./g, $QUOTE = /'/, |
21 | | | $$DELETED = /\001[^\001]*\001/g; |
22 | | | function $DELETE($match, $offset){return "\001" + $match[$offset] + "\001"}; |
23 | | | // public |
24 | | | this.add = function($expression, $replacement) { |
25 | | | if (!$replacement) $replacement = $DELETE; |
26 | | | // count the number of sub-expressions |
27 | | | // - add one because each pattern is itself a sub-expression |
28 | | | var $length = (_internalEscape(String($expression)).match($GROUPS) || "").length + 1; |
29 | | | // does the pattern deal with sub-expressions? |
30 | | | if (typeof $replacement == "string" && $SUB_REPLACE.test($replacement)) { |
31 | | | // a simple lookup? (e.g. "$2") |
32 | | | if ($INDEXED.test($replacement)) { |
33 | | | // store the index (used for fast retrieval of matched strings) |
34 | | | $replacement = parseInt($replacement.slice(1)) - 1; |
35 | | | } else { // a complicated lookup (e.g. "Hello $2 $1") |
36 | | | // build a function to do the lookup |
37 | | | var i = $length; |
38 | | | var $quote = $QUOTE.test(_internalEscape($replacement)) ? '"' : "'"; |
39 | | | while (i) $replacement = $replacement.split("$" + i--).join($quote + "+a[o+" + i + "]+" + $quote); |
40 | | | $replacement = new Function("a,o", "return" + $quote + $replacement.replace($TRIM, "$1") + $quote); |
41 | | | } |
42 | | | } |
43 | | | // pass the modified arguments |
44 | | | _add($expression || "/^$/", $replacement, $length); |
45 | | | }; |
46 | | | // execute the global replacement |
47 | | | this.exec = function($string) { |
48 | | | return _unescape(_escape($string, this.escapeChar).replace( |
49 | | | new RegExp(_patterns, this.ignoreCase ? "gi" : "g"), _replacement), this.escapeChar).replace($$DELETED, ""); |
50 | | | }; |
51 | | | // clear the patterns collections so that this object may be re-used |
52 | | | this.reset = function() { |
53 | | | _patterns.length = 0; |
54 | | | }; |
55 | | | |
56 | | | // private |
57 | | | var _patterns = []; // patterns stored by index |
58 | | | var _toString = function(){return "(" + String(this[$EXPRESSION]).slice(1, -1) + ")"}; |
59 | | | _patterns.toString = function(){return this.join("|")}; |
60 | | | // create and add a new pattern to the patterns collection |
61 | | | function _add() { |
62 | | | arguments.toString = _toString; |
63 | | | // store the pattern - as an arguments object (i think this is quicker..?) |
64 | | | _patterns[_patterns.length] = arguments; |
65 | | | } |
66 | | | // this is the global replace function (it's quite complicated) |
67 | | | function _replacement() { |
68 | | | if (!arguments[0]) return ""; |
69 | | | var i = 1, j = 0, $pattern; |
70 | | | // loop through the patterns |
71 | | | while ($pattern = _patterns[j++]) { |
72 | | | // do we have a result? |
73 | | | if (arguments[i]) { |
74 | | | var $replacement = $pattern[$REPLACEMENT]; |
75 | | | switch (typeof $replacement) { |
76 | | | case "function": return $replacement(arguments, i); |
77 | | | case "number": return arguments[$replacement + i]; |
78 | | | default: return $replacement; |
79 | | | } |
80 | | | // skip over references to sub-expressions |
81 | | | } else i += $pattern[$LENGTH]; |
82 | | | } |
83 | | | }; |
84 | | | // encode escaped characters |
85 | | | var _escaped = []; |
86 | | | function _escape($string, $escapeChar) { |
87 | | | return $escapeChar ? $string.replace(new RegExp("\\" + $escapeChar + "(.)", "g"), function($match, $char) { |
88 | | | _escaped[_escaped.length] = $char; |
89 | | | return $escapeChar; |
90 | | | }) : $string; |
91 | | | }; |
92 | | | // decode escaped characters |
93 | | | function _unescape($string, $escapeChar) { |
94 | | | var i = 0; |
95 | | | return $escapeChar ? $string.replace(new RegExp("\\" + $escapeChar, "g"), function() { |
96 | | | return $escapeChar + (_escaped[i++] || ""); |
97 | | | }) : $string; |
98 | | | }; |
99 | | | function _internalEscape($string) { |
100 | | | return $string.replace($$ESCAPE, ""); |
101 | | | }; |
102 | | | }; |
103 | | | Common.specialize({ |
104 | | | constructor: ParseMaster, |
105 | | | ignoreCase: false, |
106 | | | escapeChar: "" |
107 | | | }); |