05a5e7da16defb45140d39974e4c29157374ff0e
[euphorik.git] / tools / jsmin.rb
1 #!/usr/bin/ruby
2 # jsmin.rb 2007-07-20
3 # Author: Uladzislau Latynski
4 # This work is a translation from C to Ruby of jsmin.c published by
5 # Douglas Crockford. Permission is hereby granted to use the Ruby
6 # version under the same conditions as the jsmin.c on which it is
7 # based.
8 #
9 # /* jsmin.c
10 # 2003-04-21
11 #
12 # Copyright (c) 2002 Douglas Crockford (www.crockford.com)
13 #
14 # Permission is hereby granted, free of charge, to any person obtaining a copy of
15 # this software and associated documentation files (the "Software"), to deal in
16 # the Software without restriction, including without limitation the rights to
17 # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
18 # of the Software, and to permit persons to whom the Software is furnished to do
19 # so, subject to the following conditions:
20 #
21 # The above copyright notice and this permission notice shall be included in all
22 # copies or substantial portions of the Software.
23 #
24 # The Software shall be used for Good, not Evil.
25 #
26 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32 # SOFTWARE.
33
34 EOF = -1
35 $theA = ""
36 $theB = ""
37
38 # isAlphanum -- return true if the character is a letter, digit, underscore,
39 # dollar sign, or non-ASCII character
40 def isAlphanum(c)
41 return false if !c || c == EOF
42 return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
43 (c >= 'A' && c <= 'Z') || c == '_' || c == '$' ||
44 c == '\\' || c[0] > 126)
45 end
46
47 # get -- return the next character from stdin. Watch out for lookahead. If
48 # the character is a control character, translate it to a space or linefeed.
49 def get()
50 c = $stdin.getc
51 return EOF if(!c)
52 c = c.chr
53 return c if (c >= " " || c == "\n" || c.unpack("c") == EOF)
54 return "\n" if (c == "\r")
55 return " "
56 end
57
58 # Get the next character without getting it.
59 def peek()
60 lookaheadChar = $stdin.getc
61 $stdin.ungetc(lookaheadChar)
62 return lookaheadChar.chr
63 end
64
65 # mynext -- get the next character, excluding comments.
66 # peek() is used to see if a '/' is followed by a '/' or '*'.
67 def mynext()
68 c = get
69 # saute les commentaires (également les lignes commencant pas ;;)
70 if (c == "/" or c == ";")
71 prochain = peek
72 if(prochain == "/" or prochain == ";")
73 while(true)
74 c = get
75 if (c[0] <= "\n"[0])
76 return c
77 end
78 end
79 end
80 if(peek == "*")
81 get
82 while(true)
83 case get
84 when "*"
85 if (peek == "/")
86 get
87 return " "
88 end
89 when EOF
90 raise "Unterminated comment"
91 end
92 end
93 end
94 end
95 return c
96 end
97
98
99 # action -- do something! What you do is determined by the argument: 1
100 # Output A. Copy B to A. Get the next B. 2 Copy B to A. Get the next B.
101 # (Delete A). 3 Get the next B. (Delete B). action treats a string as a
102 # single character. Wow! action recognizes a regular expression if it is
103 # preceded by ( or , or =.
104 def action(a)
105 if(a==1)
106 $stdout.write $theA
107 end
108 if(a==1 || a==2)
109 $theA = $theB
110 if ($theA == "\'" || $theA == "\"")
111 while (true)
112 $stdout.write $theA
113 $theA = get
114 break if ($theA == $theB)
115 raise "Unterminated string literal" if ($theA <= "\n")
116 if ($theA == "\\")
117 $stdout.write $theA
118 $theA = get
119 end
120 end
121 end
122 end
123 if(a==1 || a==2 || a==3)
124 $theB = mynext
125 if ($theB == "/" && ($theA == "(" || $theA == "," || $theA == "=" ||
126 $theA == ":" || $theA == "[" || $theA == "!" ||
127 $theA == "&" || $theA == "|" || $theA == "?" ||
128 $theA == "{" || $theA == "}" || $theA == ";" ||
129 $theA == "\n"))
130 $stdout.write $theA
131 $stdout.write $theB
132 while (true)
133 $theA = get
134 if ($theA == "/")
135 break
136 elsif ($theA == "\\")
137 $stdout.write $theA
138 $theA = get
139 elsif ($theA <= "\n")
140 raise "Unterminated RegExp Literal"
141 end
142 $stdout.write $theA
143 end
144 $theB = mynext
145 end
146 end
147 end
148
149 # jsmin -- Copy the input to the output, deleting the characters which are
150 # insignificant to JavaScript. Comments will be removed. Tabs will be
151 # replaced with spaces. Carriage returns will be replaced with linefeeds.
152 # Most spaces and linefeeds will be removed.
153 def jsmin
154 $theA = "\n"
155 action(3)
156 while ($theA != EOF)
157 case $theA
158 when " "
159 if (isAlphanum($theB))
160 action(1)
161 else
162 action(2)
163 end
164 when "\n"
165 case ($theB)
166 when "{","[","(","+","-"
167 action(1)
168 when " "
169 action(3)
170 else
171 if (isAlphanum($theB))
172 action(1)
173 else
174 action(2)
175 end
176 end
177 else
178 case ($theB)
179 when " "
180 if (isAlphanum($theA))
181 action(1)
182 else
183 action(3)
184 end
185 when "\n"
186 case ($theA)
187 when "}","]",")","+","-","\"","\\", "'", '"'
188 action(1)
189 else
190 if (isAlphanum($theA))
191 action(1)
192 else
193 action(3)
194 end
195 end
196 else
197 action(1)
198 end
199 end
200 end
201 end
202
203 ARGV.each do |anArg|
204 $stdout.write "// #{anArg}\n"
205 end
206
207 jsmin