2 // TokenStream.cs: Port of Mozilla's Rhino TokenStream
3 // This class implements the JScript scanner
6 // Cesar Lopez Nataren (cesar@ciencias.unam.mx)
8 // (C) 2004, Cesar Lopez Nataren
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System
.Collections
;
35 using System
.Globalization
;
37 namespace Microsoft
.JScript
{
38 public class TokenStream
{
44 public string SourceName
{
45 get { return source_name; }
46 set { source_name = value; }
50 public int LineNumber
{
51 get { return line_number; }
52 set { line_number = value; }
57 get { return hit_eof; }
61 public int TokenNumber
{
62 get { return token_number; }
63 set { token_number = value; }
71 int string_buffer_top
;
72 char [] string_buffer
= new char [128];
74 // Room backtrace from to < on failed match of the last - in <!--
75 int [] unget_buffer
= new int [3];
82 char [] source_buffer
;
86 static int EOF_CHAR
= -1;
87 static int EOL_HINT_MASK
= 0xdfd0;
89 StreamReader source_reader
;
94 public string GetString
{
95 get { return _string; }
98 static bool reserved_keyword_as_identifier
;
101 public double GetNumber
{
102 get { return number; }
112 internal bool allow_reg_exp
;
114 internal string reg_exp_flags
;
120 public TokenStream (StreamReader source_reader
, string source_string
, string source_name
, int line_number
)
122 pushback_token
= Token
.EOF
;
123 SourceName
= source_name
;
124 this.line_number
= line_number
;
125 if (source_reader
!= null) {
126 if (source_string
!= null)
128 this.source_reader
= source_reader
;
129 source_buffer
= new char [512];
132 if (source_string
== null)
134 this.source_string
= source_string
;
135 source_end
= source_string
.Length
;
140 static bool IsKeyword (string s
)
142 return Token
.EOF
!= StringToKeyword (s
);
145 static int StringToKeyword (string name
)
147 // The following assumes that Token.EOF == 0
149 Id_break
= Token
.BREAK
,
150 Id_case
= Token
.CASE
,
151 Id_continue
= Token
.CONTINUE
,
152 Id_default
= Token
.DEFAULT
,
153 Id_delete
= Token
.DELPROP
,
155 Id_else
= Token
.ELSE
,
156 Id_export
= Token
.EXPORT
,
157 Id_false
= Token
.FALSE
,
159 Id_function
= Token
.FUNCTION
,
163 Id_null
= Token
.NULL
,
164 Id_return
= Token
.RETURN
,
165 Id_switch
= Token
.SWITCH
,
166 Id_this
= Token
.THIS
,
167 Id_true
= Token
.TRUE
,
168 Id_typeof
= Token
.TYPEOF
,
170 Id_void
= Token
.VOID
,
171 Id_while
= Token
.WHILE
,
172 Id_with
= Token
.WITH
,
174 // the following are #ifdef RESERVE_JAVA_KEYWORDS in jsscan.c
175 Id_abstract
= Token
.RESERVED
,
176 Id_boolean
= Token
.RESERVED
,
177 Id_byte
= Token
.RESERVED
,
178 Id_catch
= Token
.CATCH
,
179 Id_char
= Token
.RESERVED
,
180 Id_class
= Token
.RESERVED
,
181 Id_const
= Token
.RESERVED
,
182 Id_debugger
= Token
.RESERVED
,
183 Id_double
= Token
.RESERVED
,
184 Id_enum
= Token
.RESERVED
,
185 Id_extends
= Token
.RESERVED
,
186 Id_final
= Token
.RESERVED
,
187 Id_finally
= Token
.FINALLY
,
188 Id_float
= Token
.RESERVED
,
189 Id_goto
= Token
.RESERVED
,
190 Id_implements
= Token
.RESERVED
,
191 Id_import
= Token
.IMPORT
,
192 Id_instanceof
= Token
.INSTANCEOF
,
193 Id_int
= Token
.RESERVED
,
194 Id_interface
= Token
.RESERVED
,
195 Id_long
= Token
.RESERVED
,
196 Id_native
= Token
.RESERVED
,
197 Id_package
= Token
.RESERVED
,
198 Id_private
= Token
.RESERVED
,
199 Id_protected
= Token
.RESERVED
,
200 Id_public
= Token
.RESERVED
,
201 Id_short
= Token
.RESERVED
,
202 Id_static
= Token
.RESERVED
,
203 Id_super
= Token
.RESERVED
,
204 Id_synchronized
= Token
.RESERVED
,
205 Id_throw
= Token
.THROW
,
206 Id_throws
= Token
.RESERVED
,
207 Id_transient
= Token
.RESERVED
,
209 Id_volatile
= Token
.RESERVED
;
216 string X
= String
.Empty
;
227 } else if (c
== 'n') {
232 } else if (c
== 'o') {
242 if (s
[2] == 'r' && s
[1] == 'o') {
248 if (s
[2] == 't' && s
[1] == 'n') {
254 if (s
[2] == 'w' && s
[1] == 'e') {
260 if (s
[2] == 'y' && s
[1] == 'r') {
266 if (s
[2] == 'r' && s
[1] == 'a') {
282 if (s
[2] == 's' && s
[1] == 'a') {
286 } else if (c
== 'r') {
287 if (s
[2] == 'a' && s
[1] == 'h') {
296 if (s
[2] == 's' && s
[1] == 'l') {
300 } else if (c
== 'm') {
301 if (s
[2] == 'u' && s
[1] == 'n') {
318 if (s
[2] == 'u' && s
[1] == 'r') {
322 } else if (c
== 's') {
323 if (s
[2] == 'i' && s
[1] == 'h') {
362 } else if (c
== 'f') {
372 } else if (c
== 's') {
402 } else if (c
== 'r') {
498 } else if (c
== 'p') {
501 } else if (c
== 't') {
511 } else if (c
== 'n') {
518 id
= Id_synchronized
;
523 if (X
!= null && X
!= s
&& !X
.Equals (s
))
534 // return and pop the token from the stream if it matches otherwise return null
536 public bool MatchToken (int to_match
)
538 int token
= GetToken ();
539 if (token
== to_match
)
541 // did not match, push back the token
543 pushback_token
= token
;
547 public void UnGetToken (int tt
)
549 // Can not unreadmore than one token
550 if (pushback_token
!= Token
.EOF
&& tt
!= Token
.ERROR
)
556 public int PeekToken ()
558 int result
= GetToken ();
559 pushback_token
= result
;
564 public int PeekTokenSameLine ()
566 significant_eol
= true;
567 int result
= GetToken ();
568 pushback_token
= result
;
570 significant_eol
= false;
574 public int GetToken ()
579 // Check for pushed-back token
580 if (pushback_token
!= Token
.EOF
) {
581 int result
= pushback_token
;
582 pushback_token
= Token
.EOF
;
583 if (result
!= Token
.EOL
|| significant_eol
)
589 // Eat whitespace, possibly sensitive to newlines
594 else if (c
== '\n') {
598 } else if (!IsJSSpace (c
)) {
605 // identifier/keyword/instanceof?
606 // watch out for starting with a <backslash>
607 bool identifier_start
;
608 bool is_unicode_escape_start
= false;
613 identifier_start
= true;
614 is_unicode_escape_start
= true;
615 string_buffer_top
= 0;
617 identifier_start
= false;
622 identifier_start
= IsJavaIdentifierStart ((char) c
);
623 if (identifier_start
) {
624 string_buffer_top
= 0;
629 if (identifier_start
) {
630 bool contains_escape
= is_unicode_escape_start
;
632 if (is_unicode_escape_start
) {
633 // strictly speaking we should probably push-back
634 // all the bad characters if the <backslash>uXXXX
635 // sequence is malformed. But since there isn't a
636 // correct context(is there?) for a bad Unicode
637 // escape sequence in an identifier, we can report
640 for (int i
= 0; i
!= 4; ++i
) {
642 escape_val
= (escape_val
<< 4) | xDigitToInt (c
);
643 // Next check takes care about c < 0 and bad escape
647 if (escape_val
< 0) {
648 ReportCurrentLineError ("msg.invalid.escape");
651 AddToString (escape_val
);
652 is_unicode_escape_start
= false;
658 is_unicode_escape_start
= true;
659 contains_escape
= true;
661 ReportCurrentLineError ("msg.illegal.character");
665 if (c
== EOF_CHAR
|| !IsJavaIdentifierPart ((char) c
))
673 string str
= GetStringFromBuffer ();
674 if (!contains_escape
) {
675 // OPT we shouldn't have to make a string (object!) to
676 // check if it's a keyword.
678 // Return the corresponding token if it's a keyword
679 int result
= StringToKeyword (str
);
680 if (result
!= Token
.EOF
) {
681 if (result
!= Token
.RESERVED
)
683 else if (!reserved_keyword_as_identifier
)
686 // If implementation permits to use future reserved
687 // keywords in violation with the EcmaScript,
688 // treat it as name but issue warning
689 ReportCurrentLineWarning ("msg.reserved.keyword", str
);
690 Console
.WriteLine ("Warning: using future reserved keyword as name");
694 _string
= String
.Intern (str
);
699 if (IsDigit (c
) || (c
== '.' && IsDigit (PeekChar ()))) {
700 string_buffer_top
= 0;
705 if (c
== 'x' || c
== 'X') {
708 } else if (IsDigit (c
))
715 while (0 <= xDigitToInt (c
)) {
720 while ('0' <= c
&& c
<= '9') {
722 * We permit 08 and 09 as decimal numbers, which
723 * makes our behavior a superset of the ECMA
724 * numeric grammar. We might not always be so
725 * permissive, so we warn about it.
727 if (_base
== 8 && c
>= '8') {
728 ReportCurrentLineWarning ("msg.bad.octal.literal", c
== '8' ? "8" : "9");
736 bool is_integer
= true;
738 if (_base
== 10 && (c
== '.' || c
== 'e' || c
== 'E')) {
744 } while (IsDigit (c
));
746 if (c
== 'e' || c
== 'E') {
749 if (c
== '+' || c
== '-') {
754 ReportCurrentLineError ("msg.missing.exponent");
760 } while (IsDigit (c
));
764 string num_string
= GetStringFromBuffer ();
767 if (_base
== 10 && !is_integer
) {
769 // Use C# conversion to number from string
770 dval
= Double
.Parse (num_string
);
771 } catch (FormatException ex
) {
772 ReportCurrentLineError ("msg.caught.nfe");
776 dval
= StringToNumber (num_string
, 0, _base
);
783 if (c
== '"' || c
== '\'') {
784 // We attempt to accumulate a string the fast way, by
785 // building it directly out of the reader. But if there
786 // are any escaped characters in the string, we revert to
787 // building it out of a StringBuffer.
790 string_buffer_top
= 0;
793 strLoop: while (c
!= quote_char
) {
794 if (c
== '\n' || c
== EOF_CHAR
) {
796 ReportCurrentLineError ("msg.unterminated.string.lit");
801 // We've hit an escaped character
806 case 'b': c
= '\b'; break;
807 case 'f': c
= '\f'; break;
808 case 'n': c
= '\n'; break;
809 case 'r': c
= '\r'; break;
810 case 't': c
= '\t'; break;
812 // \v a late addition to the ECMA spec,
813 // it is not in Java, so use 0xb
814 case 'v': c
= 0xb; break;
817 // Get 4 hex digits; if the u escape is not
818 // followed by 4 hex digits, use 'u' + the
819 // literal character sequence that follows.
820 int escape_start
= string_buffer_top
;
823 for (int i
= 0; i
!= 4; ++i
) {
825 escape_val
= (escape_val
<< 4) | xDigitToInt (c
);
830 // prepare for replace of stored 'u' sequence
832 string_buffer_top
= escape_start
;
836 // Get 2 hex digits, defaulting to 'x'+literal
837 // sequence, as above.
839 escape_val
= xDigitToInt (c
);
840 if (escape_val
< 0) {
846 escape_val
= (escape_val
<< 4) | xDigitToInt (c
);
847 if (escape_val
< 0) {
851 } else // got 2 hex digits
856 if ('0' <= c
&& c
< '8') {
859 if ('0' <= c
&& c
< '8') {
860 val
= 8 * val
+ c
- '0';
862 if ('0' <= c
&& c
< '8' && val
<= 037) {
863 // c is 3rd char of octal sequence only
864 // if the resulting val <= 0377
865 val
= 8 * val
+ c
- '0';
878 string str
= GetStringFromBuffer ();
879 _string
= String
.Intern (str
);
884 case ';': return Token
.SEMI
;
885 case '[': return Token
.LB
;
886 case ']': return Token
.RB
;
887 case '{': return Token
.LC
;
888 case '}': return Token
.RC
;
889 case '(': return Token
.LP
;
890 case ')': return Token
.RP
;
891 case ',': return Token
.COMMA
;
892 case '?': return Token
.HOOK
;
893 case ':': return Token
.COLON
;
894 case '.': return Token
.DOT
;
899 else if (MatchChar ('=')) {
901 return Token
.ASSIGNOP
;
906 if (MatchChar ('=')) {
908 return Token
.ASSIGNOP
;
915 else if (MatchChar ('=')) {
917 return Token
.ASSIGNOP
;
922 if (MatchChar ('=')) {
931 if (MatchChar ('=')) {
940 /* NB:treat HTML begin-comment as comment-till-eol */
941 if (MatchChar ('!')) {
942 if (MatchChar ('-')) {
943 if (MatchChar ('-')) {
951 if (MatchChar ('<')) {
952 if (MatchChar ('=')) {
954 return Token
.ASSIGNOP
;
965 if (MatchChar ('>')) {
966 if (MatchChar ('>')) {
967 if (MatchChar ('=')) {
969 return Token
.ASSIGNOP
;
973 if (MatchChar ('=')) {
975 return Token
.ASSIGNOP
;
987 if (MatchChar ('=')) {
989 return Token
.ASSIGNOP
;
994 // is it a // comment?
995 if (MatchChar ('/')) {
999 if (MatchChar ('*')) {
1000 bool look_for_slash
= false;
1003 if (c
== EOF_CHAR
) {
1004 ReportCurrentLineError ("msg.unterminated.comment");
1006 } else if (c
== '*')
1007 look_for_slash
= true;
1008 else if (c
== '/') {
1012 look_for_slash
= false;
1017 if (allow_reg_exp
) {
1018 string_buffer_top
= 0;
1019 while ((c
= GetChar ()) != '/') {
1020 if (c
== '\n' || c
== EOF_CHAR
) {
1022 ReportCurrentLineError ("msg.unterminated.re.lit");
1031 int re_end
= string_buffer_top
;
1034 if (MatchChar ('g'))
1036 else if (MatchChar ('i'))
1038 else if (MatchChar ('m'))
1044 if (IsAlpha (PeekChar ())) {
1045 ReportCurrentLineError ("msg.invalid.re.flag");
1049 _string
= to_string (string_buffer
).Substring (0, re_end
);
1050 reg_exp_flags
= to_string (string_buffer
).Substring (re_end
, string_buffer_top
- re_end
);
1051 return Token
.REGEXP
;
1054 if (MatchChar ('=')) {
1056 return Token
.ASSIGNOP
;
1061 if (MatchChar ('=')) {
1063 return Token
.ASSIGNOP
;
1068 return Token
.BITNOT
;
1071 if (MatchChar ('=')) {
1073 return Token
.ASSIGNOP
;
1074 } else if (MatchChar ('+'))
1080 if (MatchChar ('=')) {
1083 } else if (MatchChar ('-')) {
1085 // treat HTML end-comment after possible whitespace
1086 // after line start as comment-utill-eol
1087 if (MatchChar ('>')) {
1099 ReportCurrentLineError ("msg.illegal.character");
1106 static bool IsAlpha (int c
)
1112 return 'a' <= c
&& c
<= 'z';
1115 double StringToNumber (string s
, int start
, int radix
)
1117 char digit_max
= '9';
1118 char lower_case_bound
= 'a';
1119 char upper_case_bound
= 'A';
1123 lower_case_bound
= (char) ('a' + radix
- 10);
1124 upper_case_bound
= (char) ('A' + radix
- 10);
1130 for (end
= start
; end
< len
; end
++) {
1133 if ('0' <= c
&& c
<= digit_max
)
1134 new_digit
= c
- '0';
1135 else if ('a' <= c
&& c
< lower_case_bound
)
1136 new_digit
= c
- 'a' + 10;
1137 else if ('A' <= c
&& c
< upper_case_bound
)
1138 new_digit
= c
- 'A' + 10;
1141 sum
= sum
* radix
+ new_digit
;
1147 if (sum
>= 9007199254740992.0) {
1149 /* If we're accumulating a decimal number and the number
1150 * is >= 2^53, then the result from the repeated multiply-add
1151 * above may be inaccurate. Call Java to get the correct
1155 return Double
.Parse (s
);
1156 } catch (FormatException fe
) {
1159 } else if (radix
== 2 || radix
== 4 || radix
== 8 ||
1160 radix
== 16 || radix
== 32) {
1161 /* The number may also be inaccurate for one of these bases.
1162 * This happens if the addition in value*radix + digit causes
1163 * a round-down to an even least significant mantissa bit
1164 * when the first dropped bit is a one. If any of the
1165 * following digits in the number (which haven't been added
1166 * in yet) are nonzero then the correct action would have
1167 * been to round up instead of down. An example of this
1168 * occurs when reading the number 0x1000000000000081, which
1169 * rounds to 0x1000000000000000 instead of 0x1000000000000100.
1171 int bit_shift_in_char
= 1;
1174 const int SKIP_LEADING_ZEROS
= 0;
1175 const int FIRST_EXACT_53_BITS
= 1;
1176 const int AFTER_BIT_53
= 2;
1177 const int ZEROS_AFTER_54
= 3;
1178 const int MIXED_AFTER_54
= 4;
1180 int state
= SKIP_LEADING_ZEROS
;
1181 int exact_bits_limit
= 53;
1182 double factor
= 0.0;
1184 // bit54 is the 54th bit (the first dropped from the mantissa)
1188 if (bit_shift_in_char
== 1) {
1191 digit
= s
[start
++];
1192 if ('0' <= digit
&& digit
<= '9')
1194 else if ('a' <= digit
&& digit
<= 'z')
1198 bit_shift_in_char
= radix
;
1200 bit_shift_in_char
>>= 1;
1201 bool bit
= (digit
& bit_shift_in_char
) != 0;
1204 case SKIP_LEADING_ZEROS
:
1208 state
= FIRST_EXACT_53_BITS
;
1211 case FIRST_EXACT_53_BITS
:
1216 if (exact_bits_limit
== 0) {
1218 state
= AFTER_BIT_53
;
1224 state
= ZEROS_AFTER_54
;
1226 // FIXME: check if this work
1227 case ZEROS_AFTER_54
:
1228 case MIXED_AFTER_54
:
1229 if (state
== ZEROS_AFTER_54
&& bit
) {
1230 state
= MIXED_AFTER_54
;
1238 case SKIP_LEADING_ZEROS
:
1241 case FIRST_EXACT_53_BITS
:
1245 case ZEROS_AFTER_54
:
1246 // x1.1 -> x1 + 1 (round up)
1247 // x0.1 -> x0 (round down)
1252 case MIXED_AFTER_54
:
1253 // x.100...1.. -> x + 1 (round up)
1254 // x.0anything -> x (round down)
1261 /* We don't worry about inaccurate numbers for any other base. */
1266 bool IsDigit (int c
)
1268 return '0' <= c
&& c
<= '9';
1271 static int xDigitToInt (int c
)
1273 // use 0..9 < A..Z < a..z
1278 } else if (c
<= 'F') {
1280 return c
- ('A' - 10);
1281 } else if (c
<= 'f') {
1283 return c
- ('a' - 10);
1289 public static bool IsJSSpace (int c
)
1292 return c
== 0x20 || c
== 0x9 || c
== 0xC || c
== 0xB;
1294 return c
== 0xA0 || Char
.GetUnicodeCategory ((char) c
) == UnicodeCategory
.SpaceSeparator
;
1297 public static bool IsJSLineTerminator (int c
)
1299 return c
== '\n' || c
== '\r' || c
== 0x2028 || c
== 0x2029;
1302 static bool IsJSFormatChar (int c
)
1304 return (c
> 127) && (Char
.GetUnicodeCategory ((char) c
) == UnicodeCategory
.Format
);
1307 string GetStringFromBuffer ()
1309 return new string (string_buffer
, 0, string_buffer_top
);
1312 void AddToString (int c
)
1314 int N
= string_buffer_top
;
1315 if (N
== string_buffer
.Length
) {
1316 char [] tmp
= new char [string_buffer
.Length
* 2];
1317 Array
.Copy (string_buffer
, 0, tmp
, 0, N
);
1318 string_buffer
= tmp
;
1320 string_buffer
[N
] = (char) c
;
1321 string_buffer_top
= N
+ 1;
1324 void UnGetChar (int c
)
1326 // can not unread past across line boundary
1327 if (unget_cursor
!= 0 && unget_buffer
[unget_cursor
- 1] == '\n')
1329 unget_buffer
[unget_cursor
++] = c
;
1332 bool MatchChar (int test
)
1353 if (unget_cursor
!= 0)
1354 return unget_buffer
[--unget_cursor
];
1358 if (source_string
!= null) {
1359 if (source_cursor
== source_end
) {
1363 c
= source_string
[source_cursor
++];
1365 if (source_cursor
== source_end
) {
1366 if (!FillSourceBuffer ()) {
1371 c
= source_buffer
[source_cursor
++];
1374 if (line_end_char
>= 0) {
1375 if (line_end_char
== '\r' && c
== '\n') {
1376 line_end_char
= '\n';
1380 line_start
= source_cursor
- 1;
1385 if (c
== '\n' || c
== '\r') {
1390 if (IsJSFormatChar (c
))
1392 if ((c
& EOL_HINT_MASK
) == 0 && IsJSLineTerminator (c
)) {
1403 // skip to end of line
1405 while ((c
= GetChar ()) != EOF_CHAR
&& c
!= '\n')
1410 bool FillSourceBuffer ()
1412 if (source_string
== null)
1414 if (source_end
== source_buffer
.Length
) {
1415 if (line_start
!= 0) {
1416 Array
.Copy (source_buffer
, line_start
, source_buffer
, 0, source_end
- line_start
);
1417 source_end
-= line_start
;
1418 source_cursor
-= line_start
;
1421 char [] tmp
= new char [source_buffer
.Length
* 2];
1422 Array
.Copy (source_buffer
, 0, tmp
, 0, source_end
);
1423 source_buffer
= tmp
;
1426 int n
= source_reader
.Read (source_buffer
, source_end
, source_buffer
.Length
- source_end
);
1433 public void ReportCurrentLineWarning (string message
, string str
)
1435 Console
.WriteLine ("warning: {0}, {1}, {2}, {3}", message
, SourceName
, LineNumber
, str
);
1438 public void ReportCurrentLineError (string message
)
1440 Console
.WriteLine ("error: {0}, {1}, {2}", message
, SourceName
, LineNumber
);
1443 // FIXME: we don't check for combining mark yet
1444 static bool IsJavaIdentifierPart (char c
)
1446 UnicodeCategory unicode_category
= Char
.GetUnicodeCategory (c
);
1447 return Char
.IsLetter (c
) || unicode_category
== UnicodeCategory
.CurrencySymbol
||
1448 unicode_category
== UnicodeCategory
.ConnectorPunctuation
|| Char
.IsDigit (c
) ||
1449 unicode_category
== UnicodeCategory
.LetterNumber
||
1450 unicode_category
== UnicodeCategory
.NonSpacingMark
|| IsIdentifierIgnorable (c
);
1453 static bool IsIdentifierIgnorable (char c
)
1455 return (c
>= '\u0000' && c
<= '\u0008') || (c
>= '\u000E' && c
<= '\u001B') ||
1456 (c
>= '\u007F' && c
<= '\u009F') || Char
.GetUnicodeCategory (c
) == UnicodeCategory
.Format
;
1459 static bool IsJavaIdentifierStart (char c
)
1461 UnicodeCategory unicode_category
= Char
.GetUnicodeCategory (c
);
1462 return Char
.IsLetter (c
) || unicode_category
== UnicodeCategory
.LetterNumber
||
1463 unicode_category
== UnicodeCategory
.CurrencySymbol
||
1464 unicode_category
== UnicodeCategory
.ConnectorPunctuation
;
1467 internal static string to_string (Array a
)
1469 string s
= String
.Empty
;
1470 foreach (char c
in a
)