3 * Copyright (C) 2008-2012 Jürg Billeter
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 * Jürg Billeter <j@bitron.ch>
21 * Jukka-Pekka Iivonen <jp0409@jippii.fi>
27 * Lexical scanner for Vala source files.
29 public class Vala
.Scanner
{
30 public SourceFile source_file
{ get; private set; }
41 Conditional
[] conditional_stack
;
45 public bool else_found
;
46 public bool skip_section
;
60 public Scanner (SourceFile source_file
) {
61 this
.source_file
= source_file
;
63 char* begin
= source_file
.get_mapped_contents ();
64 end
= begin
+ source_file
.get_mapped_length ();
72 public void seek (SourceLocation location
) {
73 current
= location
.pos
;
75 column
= location
.column
;
77 conditional_stack
= null;
82 return (state_stack
.length
> 0 && state_stack
[state_stack
.length
- 1] == State
.TEMPLATE
);
85 bool in_template_part () {
86 return (state_stack
.length
> 0 && state_stack
[state_stack
.length
- 1] == State
.TEMPLATE_PART
);
89 bool in_regex_literal () {
90 return (state_stack
.length
> 0 && state_stack
[state_stack
.length
- 1] == State
.REGEX_LITERAL
);
93 bool is_ident_char (char c
) {
94 return (c
.isalnum () || c
== '_');
97 SourceReference
get_source_reference (int offset
, int length
= 0) {
98 return new
SourceReference (source_file
, SourceLocation (current
, line
, column
+ offset
), SourceLocation (current
+ length
, line
, column
+ offset
+ length
));
101 public TokenType
read_regex_token (out SourceLocation token_begin
, out SourceLocation token_end
) {
103 char* begin
= current
;
104 token_begin
= SourceLocation (begin
, line
, column
);
106 int token_length_in_chars
= -1;
108 if (current
>= end
) {
109 type
= TokenType
.EOF
;
111 switch (current
[0]) {
113 type
= TokenType
.CLOSE_REGEX_LITERAL
;
115 state_stack
.length
--;
120 while (current
[0] == 'i' || current
[0] == 's' || current
[0] == 'm' || current
[0] == 'x') {
121 switch (current
[0]) {
124 Report
.error (get_source_reference (token_length_in_chars
), "modifier 'i' used more than once");
130 Report
.error (get_source_reference (token_length_in_chars
), "modifier 's' used more than once");
136 Report
.error (get_source_reference (token_length_in_chars
), "modifier 'm' used more than once");
142 Report
.error (get_source_reference (token_length_in_chars
), "modifier 'x' used more than once");
148 token_length_in_chars
++;
152 type
= TokenType
.REGEX_LITERAL
;
153 token_length_in_chars
= 0;
154 while (current
< end
&& current
[0] != '/') {
155 if (current
[0] == '\\') {
157 token_length_in_chars
++;
158 if (current
>= end
) {
162 switch (current
[0]) {
216 token_length_in_chars
++;
219 // u escape character has four hex digits
221 token_length_in_chars
++;
223 for (digit_length
= 0; digit_length
< 4 && current
< end
&& current
[0].isxdigit (); digit_length
++) {
225 token_length_in_chars
++;
227 if (digit_length
!= 4) {
228 Report
.error (get_source_reference (token_length_in_chars
), "\\u requires four hex digits");
232 // hexadecimal escape character requires two hex digits
234 token_length_in_chars
++;
236 for (digit_length
= 0; current
< end
&& current
[0].isxdigit (); digit_length
++) {
238 token_length_in_chars
++;
240 if (digit_length
< 1) {
241 Report
.error (get_source_reference (token_length_in_chars
), "\\x requires at least one hex digit");
245 // back references \1 through \99
246 if (current
[0].isdigit ()) {
248 token_length_in_chars
++;
249 if (current
[0].isdigit ()) {
251 token_length_in_chars
++;
254 Report
.error (get_source_reference (token_length_in_chars
), "invalid escape sequence");
258 } else if (current
[0] == '\n') {
261 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
262 if (u
!= (unichar
) (-1)) {
263 current
+= u
.to_utf8 (null);
264 token_length_in_chars
++;
267 Report
.error (get_source_reference (token_length_in_chars
), "invalid UTF-8 character");
271 if (current
>= end
|| current
[0] == '\n') {
272 Report
.error (get_source_reference (token_length_in_chars
), "syntax error, expected \"");
273 state_stack
.length
--;
274 return read_token (out token_begin
, out token_end
);
280 if (token_length_in_chars
< 0) {
281 column
+= (int) (current
- begin
);
283 column
+= token_length_in_chars
;
286 token_end
= SourceLocation (current
, line
, column
- 1);
291 public static TokenType
get_identifier_or_keyword (char* begin
, int len
) {
296 if (matches (begin
, "as")) return TokenType
.AS
;
299 if (matches (begin
, "do")) return TokenType
.DO
;
316 if (matches (begin
, "for")) return TokenType
.FOR
;
319 if (matches (begin
, "get")) return TokenType
.GET
;
322 if (matches (begin
, "new")) return TokenType
.NEW
;
325 if (matches (begin
, "out")) return TokenType
.OUT
;
328 if (matches (begin
, "ref")) return TokenType
.REF
;
331 if (matches (begin
, "set")) return TokenType
.SET
;
334 if (matches (begin
, "try")) return TokenType
.TRY
;
337 if (matches (begin
, "var")) return TokenType
.VAR
;
344 if (matches (begin
, "base")) return TokenType
.BASE
;
347 if (matches (begin
, "case")) return TokenType
.CASE
;
352 if (matches (begin
, "else")) return TokenType
.ELSE
;
355 if (matches (begin
, "enum")) return TokenType
.ENUM
;
360 if (matches (begin
, "lock")) return TokenType
.LOCK
;
363 if (matches (begin
, "null")) return TokenType
.NULL
;
368 if (matches (begin
, "this")) return TokenType
.THIS
;
371 if (matches (begin
, "true")) return TokenType
.TRUE
;
376 if (matches (begin
, "void")) return TokenType
.VOID
;
379 if (matches (begin
, "weak")) return TokenType
.WEAK
;
386 if (matches (begin
, "async")) return TokenType
.ASYNC
;
389 if (matches (begin
, "break")) return TokenType
.BREAK
;
394 if (matches (begin
, "catch")) return TokenType
.CATCH
;
397 if (matches (begin
, "class")) return TokenType
.CLASS
;
400 if (matches (begin
, "const")) return TokenType
.CONST
;
405 if (matches (begin
, "false")) return TokenType
.FALSE
;
408 if (matches (begin
, "owned")) return TokenType
.OWNED
;
411 if (matches (begin
, "throw")) return TokenType
.THROW
;
414 if (matches (begin
, "using")) return TokenType
.USING
;
417 if (matches (begin
, "while")) return TokenType
.WHILE
;
420 if (matches (begin
, "yield")) return TokenType
.YIELD
;
427 if (matches (begin
, "delete")) return TokenType
.DELETE
;
430 if (matches (begin
, "extern")) return TokenType
.EXTERN
;
433 if (matches (begin
, "inline")) return TokenType
.INLINE
;
438 if (matches (begin
, "params")) return TokenType
.PARAMS
;
441 if (matches (begin
, "public")) return TokenType
.PUBLIC
;
446 if (matches (begin
, "return")) return TokenType
.RETURN
;
451 if (matches (begin
, "sealed")) return TokenType
.SEALED
;
456 if (matches (begin
, "signal")) return TokenType
.SIGNAL
;
459 if (matches (begin
, "sizeof")) return TokenType
.SIZEOF
;
466 if (matches (begin
, "static")) return TokenType
.STATIC
;
469 if (matches (begin
, "struct")) return TokenType
.STRUCT
;
474 if (matches (begin
, "switch")) return TokenType
.SWITCH
;
481 if (matches (begin
, "throws")) return TokenType
.THROWS
;
484 if (matches (begin
, "typeof")) return TokenType
.TYPEOF
;
489 if (matches (begin
, "unlock")) return TokenType
.UNLOCK
;
498 if (matches (begin
, "default")) return TokenType
.DEFAULT
;
501 if (matches (begin
, "dynamic")) return TokenType
.DYNAMIC
;
506 if (matches (begin
, "ensures")) return TokenType
.ENSURES
;
511 if (matches (begin
, "finally")) return TokenType
.FINALLY
;
514 if (matches (begin
, "foreach")) return TokenType
.FOREACH
;
519 if (matches (begin
, "private")) return TokenType
.PRIVATE
;
522 if (matches (begin
, "unowned")) return TokenType
.UNOWNED
;
525 if (matches (begin
, "virtual")) return TokenType
.VIRTUAL
;
532 if (matches (begin
, "abstract")) return TokenType
.ABSTRACT
;
535 if (matches (begin
, "continue")) return TokenType
.CONTINUE
;
538 if (matches (begin
, "delegate")) return TokenType
.DELEGATE
;
541 if (matches (begin
, "internal")) return TokenType
.INTERNAL
;
544 if (matches (begin
, "override")) return TokenType
.OVERRIDE
;
547 if (matches (begin
, "requires")) return TokenType
.REQUIRES
;
550 if (matches (begin
, "volatile")) return TokenType
.VOLATILE
;
557 if (matches (begin
, "construct")) return TokenType
.CONSTRUCT
;
560 if (matches (begin
, "interface")) return TokenType
.INTERFACE
;
563 if (matches (begin
, "namespace")) return TokenType
.NAMESPACE
;
566 if (matches (begin
, "protected")) return TokenType
.PROTECTED
;
571 if (matches (begin
, "errordomain")) return TokenType
.ERRORDOMAIN
;
574 return TokenType
.IDENTIFIER
;
577 TokenType
read_number () {
578 var type
= TokenType
.INTEGER_LITERAL
;
581 if (current
< end
- 2 && current
[0] == '0'
582 && current
[1] == 'x' && current
[2].isxdigit ()) {
583 // hexadecimal integer literal
585 while (current
< end
&& current
[0].isxdigit ()) {
590 while (current
< end
&& current
[0].isdigit ()) {
596 if (current
< end
- 1 && current
[0] == '.' && current
[1].isdigit ()) {
597 type
= TokenType
.REAL_LITERAL
;
599 while (current
< end
&& current
[0].isdigit ()) {
605 if (current
< end
&& current
[0].tolower () == 'e') {
606 type
= TokenType
.REAL_LITERAL
;
608 if (current
< end
&& (current
[0] == '+' || current
[0] == '-')) {
611 while (current
< end
&& current
[0].isdigit ()) {
618 bool real_literal
= (type
== TokenType
.REAL_LITERAL
);
620 switch (current
[0]) {
623 if (type
== TokenType
.INTEGER_LITERAL
) {
625 if (current
< end
&& current
[0].tolower () == 'l') {
632 if (type
== TokenType
.INTEGER_LITERAL
) {
634 if (current
< end
&& current
[0].tolower () == 'l') {
636 if (current
< end
&& current
[0].tolower () == 'l') {
646 type
= TokenType
.REAL_LITERAL
;
651 if (!real_literal
&& is_ident_char (current
[0])) {
652 // allow identifiers to start with a digit
653 // as long as they contain at least one char
654 while (current
< end
&& is_ident_char (current
[0])) {
657 type
= TokenType
.IDENTIFIER
;
664 public TokenType
read_template_token (out SourceLocation token_begin
, out SourceLocation token_end
) {
666 char* begin
= current
;
667 token_begin
= SourceLocation (begin
, line
, column
);
669 int token_length_in_chars
= -1;
671 if (current
>= end
) {
672 type
= TokenType
.EOF
;
674 switch (current
[0]) {
676 type
= TokenType
.CLOSE_TEMPLATE
;
678 state_stack
.length
--;
681 token_begin
.pos
++; // $ is not part of following token
683 if (current
[0].isalpha () || current
[0] == '_') {
685 while (current
< end
&& is_ident_char (current
[0])) {
689 type
= TokenType
.IDENTIFIER
;
690 state_stack
+= State
.TEMPLATE_PART
;
691 } else if (current
[0] == '(') {
694 state_stack
+= State
.PARENS
;
695 return read_token (out token_begin
, out token_end
);
696 } else if (current
[0] == '$') {
697 type
= TokenType
.TEMPLATE_STRING_LITERAL
;
699 state_stack
+= State
.TEMPLATE_PART
;
701 Report
.error (get_source_reference (1), "unexpected character");
702 return read_template_token (out token_begin
, out token_end
);
706 type
= TokenType
.TEMPLATE_STRING_LITERAL
;
707 token_length_in_chars
= 0;
708 while (current
< end
&& current
[0] != '"' && current
[0] != '$') {
709 if (current
[0] == '\\') {
711 token_length_in_chars
++;
712 if (current
>= end
) {
716 switch (current
[0]) {
728 token_length_in_chars
++;
731 // u escape character has four hex digits
733 token_length_in_chars
++;
735 for (digit_length
= 0; digit_length
< 4 && current
< end
&& current
[0].isxdigit (); digit_length
++) {
737 token_length_in_chars
++;
739 if (digit_length
!= 4) {
740 Report
.error (get_source_reference (token_length_in_chars
), "\\u requires four hex digits");
744 // hexadecimal escape character requires two hex digits
746 token_length_in_chars
++;
748 for (digit_length
= 0; current
< end
&& current
[0].isxdigit (); digit_length
++) {
750 token_length_in_chars
++;
752 if (digit_length
< 1) {
753 Report
.error (get_source_reference (token_length_in_chars
), "\\x requires at least one hex digit");
757 Report
.error (get_source_reference (token_length_in_chars
), "invalid escape sequence");
760 } else if (current
[0] == '\n') {
764 token_length_in_chars
= 1;
766 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
767 if (u
!= (unichar
) (-1)) {
768 current
+= u
.to_utf8 (null);
769 token_length_in_chars
++;
772 Report
.error (get_source_reference (token_length_in_chars
), "invalid UTF-8 character");
776 if (current
>= end
) {
777 Report
.error (get_source_reference (token_length_in_chars
), "syntax error, expected \"");
778 state_stack
.length
--;
779 return read_token (out token_begin
, out token_end
);
781 state_stack
+= State
.TEMPLATE_PART
;
786 if (token_length_in_chars
< 0) {
787 column
+= (int) (current
- begin
);
789 column
+= token_length_in_chars
;
792 token_end
= SourceLocation (current
, line
, column
- 1);
797 public TokenType
read_token (out SourceLocation token_begin
, out SourceLocation token_end
) {
798 if (in_template ()) {
799 return read_template_token (out token_begin
, out token_end
);
800 } else if (in_template_part ()) {
801 state_stack
.length
--;
803 token_begin
= SourceLocation (current
, line
, column
);
804 token_end
= SourceLocation (current
, line
, column
- 1);
806 return TokenType
.COMMA
;
807 } else if (in_regex_literal ()) {
808 return read_regex_token (out token_begin
, out token_end
);
814 char* begin
= current
;
815 token_begin
= SourceLocation (begin
, line
, column
);
817 int token_length_in_chars
= -1;
819 if (current
>= end
) {
820 type
= TokenType
.EOF
;
821 } else if (current
[0].isalpha () || current
[0] == '_') {
823 while (current
< end
&& is_ident_char (current
[0])) {
827 type
= get_identifier_or_keyword (begin
, len
);
828 } else if (current
[0] == '@') {
829 if (current
< end
- 1 && current
[1] == '"') {
830 type
= TokenType
.OPEN_TEMPLATE
;
832 state_stack
+= State
.TEMPLATE
;
834 token_begin
.pos
++; // @ is not part of the identifier
837 while (current
< end
&& is_ident_char (current
[0])) {
841 type
= TokenType
.IDENTIFIER
;
843 } else if (current
[0].isdigit ()) {
844 type
= read_number ();
846 switch (current
[0]) {
848 type
= TokenType
.OPEN_BRACE
;
850 state_stack
+= State
.BRACE
;
853 type
= TokenType
.CLOSE_BRACE
;
855 if (state_stack
.length
> 0) {
856 state_stack
.length
--;
860 type
= TokenType
.OPEN_PARENS
;
862 state_stack
+= State
.PARENS
;
865 type
= TokenType
.CLOSE_PARENS
;
867 if (state_stack
.length
> 0) {
868 state_stack
.length
--;
870 if (in_template ()) {
871 type
= TokenType
.COMMA
;
875 type
= TokenType
.OPEN_BRACKET
;
877 state_stack
+= State
.BRACKET
;
880 type
= TokenType
.CLOSE_BRACKET
;
882 if (state_stack
.length
> 0) {
883 state_stack
.length
--;
887 type
= TokenType
.DOT
;
889 if (current
< end
- 1) {
890 if (current
[0] == '.' && current
[1] == '.') {
891 type
= TokenType
.ELLIPSIS
;
897 type
= TokenType
.COLON
;
899 if (current
< end
&& current
[0] == ':') {
900 type
= TokenType
.DOUBLE_COLON
;
905 type
= TokenType
.COMMA
;
909 type
= TokenType
.SEMICOLON
;
913 type
= TokenType
.HASH
;
917 type
= TokenType
.INTERR
;
919 if (current
< end
&& current
[0] == '?') {
920 type
= TokenType
.OP_COALESCING
;
925 type
= TokenType
.BITWISE_OR
;
928 switch (current
[0]) {
930 type
= TokenType
.ASSIGN_BITWISE_OR
;
934 type
= TokenType
.OP_OR
;
941 type
= TokenType
.BITWISE_AND
;
944 switch (current
[0]) {
946 type
= TokenType
.ASSIGN_BITWISE_AND
;
950 type
= TokenType
.OP_AND
;
957 type
= TokenType
.CARRET
;
959 if (current
< end
&& current
[0] == '=') {
960 type
= TokenType
.ASSIGN_BITWISE_XOR
;
965 type
= TokenType
.TILDE
;
969 type
= TokenType
.ASSIGN
;
972 switch (current
[0]) {
974 type
= TokenType
.OP_EQ
;
978 type
= TokenType
.LAMBDA
;
985 type
= TokenType
.OP_LT
;
988 switch (current
[0]) {
990 type
= TokenType
.OP_LE
;
994 type
= TokenType
.OP_SHIFT_LEFT
;
996 if (current
< end
&& current
[0] == '=') {
997 type
= TokenType
.ASSIGN_SHIFT_LEFT
;
1005 type
= TokenType
.OP_GT
;
1007 if (current
< end
&& current
[0] == '=') {
1008 type
= TokenType
.OP_GE
;
1013 type
= TokenType
.OP_NEG
;
1015 if (current
< end
&& current
[0] == '=') {
1016 type
= TokenType
.OP_NE
;
1021 type
= TokenType
.PLUS
;
1023 if (current
< end
) {
1024 switch (current
[0]) {
1026 type
= TokenType
.ASSIGN_ADD
;
1030 type
= TokenType
.OP_INC
;
1037 type
= TokenType
.MINUS
;
1039 if (current
< end
) {
1040 switch (current
[0]) {
1042 type
= TokenType
.ASSIGN_SUB
;
1046 type
= TokenType
.OP_DEC
;
1050 type
= TokenType
.OP_PTR
;
1057 type
= TokenType
.STAR
;
1059 if (current
< end
&& current
[0] == '=') {
1060 type
= TokenType
.ASSIGN_MUL
;
1066 case TokenType
.ASSIGN
:
1067 case TokenType
.COMMA
:
1068 case TokenType
.MINUS
:
1069 case TokenType
.OP_AND
:
1070 case TokenType
.OP_COALESCING
:
1071 case TokenType
.OP_EQ
:
1072 case TokenType
.OP_GE
:
1073 case TokenType
.OP_GT
:
1074 case TokenType
.OP_LE
:
1075 case TokenType
.OP_LT
:
1076 case TokenType
.OP_NE
:
1077 case TokenType
.OP_NEG
:
1078 case TokenType
.OP_OR
:
1079 case TokenType
.OPEN_BRACE
:
1080 case TokenType
.OPEN_PARENS
:
1081 case TokenType
.PLUS
:
1082 case TokenType
.RETURN
:
1083 type
= TokenType
.OPEN_REGEX_LITERAL
;
1084 state_stack
+= State
.REGEX_LITERAL
;
1088 type
= TokenType
.DIV
;
1090 if (current
< end
&& current
[0] == '=') {
1091 type
= TokenType
.ASSIGN_DIV
;
1098 type
= TokenType
.PERCENT
;
1100 if (current
< end
&& current
[0] == '=') {
1101 type
= TokenType
.ASSIGN_PERCENT
;
1107 if (begin
[0] == '\'') {
1108 type
= TokenType
.CHARACTER_LITERAL
;
1109 } else if (current
< end
- 6 && begin
[1] == '"' && begin
[2] == '"') {
1110 type
= TokenType
.VERBATIM_STRING_LITERAL
;
1111 token_length_in_chars
= 6;
1113 while (current
< end
- 4) {
1114 if (current
[0] == '"' && current
[1] == '"' && current
[2] == '"' && current
[3] != '"') {
1116 } else if (current
[0] == '\n') {
1120 token_length_in_chars
= 3;
1122 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
1123 if (u
!= (unichar
) (-1)) {
1124 current
+= u
.to_utf8 (null);
1125 token_length_in_chars
++;
1127 Report
.error (get_source_reference (token_length_in_chars
), "invalid UTF-8 character");
1131 if (current
[0] == '"' && current
[1] == '"' && current
[2] == '"') {
1134 Report
.error (get_source_reference (token_length_in_chars
), "syntax error, expected \"\"\"");
1138 type
= TokenType
.STRING_LITERAL
;
1140 token_length_in_chars
= 2;
1142 while (current
< end
&& current
[0] != begin
[0]) {
1143 if (current
[0] == '\\') {
1145 token_length_in_chars
++;
1146 if (current
>= end
) {
1150 switch (current
[0]) {
1163 token_length_in_chars
++;
1166 // u escape character has four hex digits
1168 token_length_in_chars
++;
1170 for (digit_length
= 0; digit_length
< 4 && current
< end
&& current
[0].isxdigit (); digit_length
++) {
1172 token_length_in_chars
++;
1174 if (digit_length
!= 4) {
1175 Report
.error (get_source_reference (token_length_in_chars
), "\\u requires four hex digits");
1179 // hexadecimal escape character requires two hex digits
1181 token_length_in_chars
++;
1183 for (digit_length
= 0; current
< end
&& current
[0].isxdigit (); digit_length
++) {
1185 token_length_in_chars
++;
1187 if (digit_length
< 1) {
1188 Report
.error (get_source_reference (token_length_in_chars
), "\\x requires at least one hex digit");
1192 Report
.error (get_source_reference (token_length_in_chars
), "invalid escape sequence");
1195 } else if (current
[0] == '\n') {
1199 token_length_in_chars
= 1;
1201 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
1202 if (u
!= (unichar
) (-1)) {
1203 current
+= u
.to_utf8 (null);
1204 token_length_in_chars
++;
1207 Report
.error (get_source_reference (token_length_in_chars
), "invalid UTF-8 character");
1210 if (current
< end
&& begin
[0] == '\'' && current
[0] != '\'') {
1211 // multiple characters in single character literal
1212 Report
.error (get_source_reference (token_length_in_chars
), "invalid character literal");
1215 if (current
< end
) {
1218 Report
.error (get_source_reference (token_length_in_chars
), "syntax error, expected %c".printf (begin
[0]));
1222 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
1223 if (u
!= (unichar
) (-1)) {
1224 current
+= u
.to_utf8 (null);
1225 Report
.error (get_source_reference (0), "syntax error, unexpected character");
1228 Report
.error (get_source_reference (0), "invalid UTF-8 character");
1231 return read_token (out token_begin
, out token_end
);
1235 if (token_length_in_chars
< 0) {
1236 column
+= (int) (current
- begin
);
1238 column
+= token_length_in_chars
;
1241 token_end
= SourceLocation (current
, line
, column
- 1);
1247 static bool matches (char* begin
, string keyword
) {
1248 char* keyword_array
= (char*) keyword
;
1249 long len
= keyword
.length
;
1250 for (int i
= 0; i
< len
; i
++) {
1251 if (begin
[i
] != keyword_array
[i
]) {
1258 bool pp_whitespace () {
1260 while (current
< end
&& current
[0].isspace () && current
[0] != '\n') {
1269 while (pp_whitespace () || comment ()) {
1273 void pp_directive () {
1278 if (line
== 1 && column
== 2 && current
< end
&& current
[0] == '!') {
1280 // skip until end of line or end of file
1281 while (current
< end
&& current
[0] != '\n') {
1289 char* begin
= current
;
1291 while (current
< end
&& current
[0].isalnum ()) {
1297 if (len
== 2 && matches (begin
, "if")) {
1299 } else if (len
== 4 && matches (begin
, "elif")) {
1301 } else if (len
== 4 && matches (begin
, "else")) {
1303 } else if (len
== 5 && matches (begin
, "endif")) {
1306 Report
.error (get_source_reference (-len
, len
), "syntax error, invalid preprocessing directive");
1309 if (conditional_stack
.length
> 0
1310 && conditional_stack
[conditional_stack
.length
- 1].skip_section
) {
1311 // skip lines until next preprocessing directive
1313 while (current
< end
) {
1314 if (bol
&& current
[0] == '#') {
1315 // go back to begin of line
1316 current
-= (column
- 1);
1320 if (current
[0] == '\n') {
1324 } else if (!current
[0].isspace ()) {
1335 if (current
>= end
|| current
[0] != '\n') {
1336 Report
.error (get_source_reference (0), "syntax error, expected newline");
1340 void parse_pp_if () {
1343 bool condition
= parse_pp_expression ();
1347 conditional_stack
+= Conditional ();
1349 if (condition
&& (conditional_stack
.length
== 1 || !conditional_stack
[conditional_stack
.length
- 2].skip_section
)) {
1350 // condition true => process code within if
1351 conditional_stack
[conditional_stack
.length
- 1].matched
= true;
1353 // skip lines until next preprocessing directive
1354 conditional_stack
[conditional_stack
.length
- 1].skip_section
= true;
1358 void parse_pp_elif () {
1361 bool condition
= parse_pp_expression ();
1365 if (conditional_stack
.length
== 0 || conditional_stack
[conditional_stack
.length
- 1].else_found
) {
1366 Report
.error (get_source_reference (0), "syntax error, unexpected #elif");
1370 if (condition
&& !conditional_stack
[conditional_stack
.length
- 1].matched
1371 && (conditional_stack
.length
== 1 || !conditional_stack
[conditional_stack
.length
- 2].skip_section
)) {
1372 // condition true => process code within if
1373 conditional_stack
[conditional_stack
.length
- 1].matched
= true;
1374 conditional_stack
[conditional_stack
.length
- 1].skip_section
= false;
1376 // skip lines until next preprocessing directive
1377 conditional_stack
[conditional_stack
.length
- 1].skip_section
= true;
1381 void parse_pp_else () {
1384 if (conditional_stack
.length
== 0 || conditional_stack
[conditional_stack
.length
- 1].else_found
) {
1385 Report
.error (get_source_reference (0), "syntax error, unexpected #else");
1389 if (!conditional_stack
[conditional_stack
.length
- 1].matched
1390 && (conditional_stack
.length
== 1 || !conditional_stack
[conditional_stack
.length
- 2].skip_section
)) {
1391 // condition true => process code within if
1392 conditional_stack
[conditional_stack
.length
- 1].matched
= true;
1393 conditional_stack
[conditional_stack
.length
- 1].skip_section
= false;
1395 // skip lines until next preprocessing directive
1396 conditional_stack
[conditional_stack
.length
- 1].skip_section
= true;
1400 void parse_pp_endif () {
1403 if (conditional_stack
.length
== 0) {
1404 Report
.error (get_source_reference (0), "syntax error, unexpected #endif");
1408 conditional_stack
.length
--;
1411 bool parse_pp_symbol () {
1413 while (current
< end
&& is_ident_char (current
[0])) {
1420 Report
.error (get_source_reference (0), "syntax error, expected identifier");
1424 string identifier
= ((string) (current
- len
)).substring (0, len
);
1426 if (identifier
== "true") {
1428 } else if (identifier
== "false") {
1431 defined
= source_file
.context
.is_defined (identifier
);
1437 bool parse_pp_primary_expression () {
1438 if (current
>= end
) {
1439 Report
.error (get_source_reference (0), "syntax error, expected identifier");
1440 } else if (is_ident_char (current
[0])) {
1441 return parse_pp_symbol ();
1442 } else if (current
[0] == '(') {
1446 bool result
= parse_pp_expression ();
1448 if (current
< end
&& current
[0] == ')') {
1452 Report
.error (get_source_reference (0), "syntax error, expected `)'");
1456 Report
.error (get_source_reference (0), "syntax error, expected identifier");
1461 bool parse_pp_unary_expression () {
1462 if (current
< end
&& current
[0] == '!') {
1466 return !parse_pp_unary_expression ();
1469 return parse_pp_primary_expression ();
1472 bool parse_pp_equality_expression () {
1473 bool left
= parse_pp_unary_expression ();
1476 if (current
< end
- 1 && current
[0] == '=' && current
[1] == '=') {
1480 bool right
= parse_pp_unary_expression ();
1481 left
= (left
== right
);
1482 } else if (current
< end
- 1 && current
[0] == '!' && current
[1] == '=') {
1486 bool right
= parse_pp_unary_expression ();
1487 left
= (left
!= right
);
1495 bool parse_pp_and_expression () {
1496 bool left
= parse_pp_equality_expression ();
1498 while (current
< end
- 1 && current
[0] == '&' && current
[1] == '&') {
1502 bool right
= parse_pp_equality_expression ();
1503 left
= left
&& right
;
1508 bool parse_pp_or_expression () {
1509 bool left
= parse_pp_and_expression ();
1511 while (current
< end
- 1 && current
[0] == '|' && current
[1] == '|') {
1515 bool right
= parse_pp_and_expression ();
1516 left
= left
|| right
;
1521 bool parse_pp_expression () {
1522 return parse_pp_or_expression ();
1525 bool whitespace () {
1527 bool bol
= (column
== 1);
1528 while (current
< end
&& current
[0].isspace ()) {
1529 if (current
[0] == '\n') {
1538 if (bol
&& current
< end
&& current
[0] == '#') {
1545 bool comment (bool file_comment
= false) {
1547 || current
> end
- 2
1548 || current
[0] != '/'
1549 || (current
[1] != '/' && current
[1] != '*')) {
1553 if (current
[1] == '/') {
1554 SourceReference source_reference
= null;
1556 source_reference
= get_source_reference (0);
1559 // single-line comment
1561 char* begin
= current
;
1563 // skip until end of line or end of file
1564 while (current
< end
&& current
[0] != '\n') {
1568 if (source_reference
!= null) {
1569 push_comment (((string) begin
).substring (0, (long) (current
- begin
)), source_reference
, file_comment
);
1572 SourceReference source_reference
= null;
1574 if (file_comment
&& current
[2] == '*') {
1578 if (current
[2] == '*' || file_comment
) {
1579 source_reference
= get_source_reference (0);
1585 char* begin
= current
;
1586 while (current
< end
- 1
1587 && (current
[0] != '*' || current
[1] != '/')) {
1588 if (current
[0] == '\n') {
1596 if (current
== end
- 1) {
1597 Report
.error (get_source_reference (0), "syntax error, expected */");
1601 if (source_reference
!= null) {
1602 push_comment (((string) begin
).substring (0, (long) (current
- begin
)), source_reference
, file_comment
);
1613 while (whitespace () || comment ()) {
1617 public void parse_file_comments () {
1618 while (whitespace () || comment (true)) {
1622 void push_comment (string comment_item
, SourceReference source_reference
, bool file_comment
) {
1623 if (comment_item
[0] == '*') {
1624 if (_comment
!= null) {
1625 // extra doc comment, add it to source file comments
1626 source_file
.add_comment (_comment
);
1628 _comment
= new
Comment (comment_item
, source_reference
);
1632 source_file
.add_comment (new
Comment (comment_item
, source_reference
));
1638 * Clears and returns the content of the comment stack.
1640 * @return saved comment
1642 public Comment?
pop_comment () {
1643 if (_comment
== null) {
1647 var comment
= _comment
;