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
;
495 if (matches (begin
, "default")) return TokenType
.DEFAULT
;
498 if (matches (begin
, "dynamic")) return TokenType
.DYNAMIC
;
503 if (matches (begin
, "ensures")) return TokenType
.ENSURES
;
508 if (matches (begin
, "finally")) return TokenType
.FINALLY
;
511 if (matches (begin
, "foreach")) return TokenType
.FOREACH
;
516 if (matches (begin
, "private")) return TokenType
.PRIVATE
;
519 if (matches (begin
, "unowned")) return TokenType
.UNOWNED
;
522 if (matches (begin
, "virtual")) return TokenType
.VIRTUAL
;
529 if (matches (begin
, "abstract")) return TokenType
.ABSTRACT
;
532 if (matches (begin
, "continue")) return TokenType
.CONTINUE
;
535 if (matches (begin
, "delegate")) return TokenType
.DELEGATE
;
538 if (matches (begin
, "internal")) return TokenType
.INTERNAL
;
541 if (matches (begin
, "override")) return TokenType
.OVERRIDE
;
544 if (matches (begin
, "requires")) return TokenType
.REQUIRES
;
547 if (matches (begin
, "volatile")) return TokenType
.VOLATILE
;
554 if (matches (begin
, "construct")) return TokenType
.CONSTRUCT
;
557 if (matches (begin
, "interface")) return TokenType
.INTERFACE
;
560 if (matches (begin
, "namespace")) return TokenType
.NAMESPACE
;
563 if (matches (begin
, "protected")) return TokenType
.PROTECTED
;
568 if (matches (begin
, "errordomain")) return TokenType
.ERRORDOMAIN
;
571 return TokenType
.IDENTIFIER
;
574 TokenType
read_number () {
575 var type
= TokenType
.INTEGER_LITERAL
;
578 if (current
< end
- 2 && current
[0] == '0'
579 && current
[1] == 'x' && current
[2].isxdigit ()) {
580 // hexadecimal integer literal
582 while (current
< end
&& current
[0].isxdigit ()) {
587 while (current
< end
&& current
[0].isdigit ()) {
593 if (current
< end
- 1 && current
[0] == '.' && current
[1].isdigit ()) {
594 type
= TokenType
.REAL_LITERAL
;
596 while (current
< end
&& current
[0].isdigit ()) {
602 if (current
< end
&& current
[0].tolower () == 'e') {
603 type
= TokenType
.REAL_LITERAL
;
605 if (current
< end
&& (current
[0] == '+' || current
[0] == '-')) {
608 while (current
< end
&& current
[0].isdigit ()) {
615 bool real_literal
= (type
== TokenType
.REAL_LITERAL
);
617 switch (current
[0]) {
620 if (type
== TokenType
.INTEGER_LITERAL
) {
622 if (current
< end
&& current
[0].tolower () == 'l') {
629 if (type
== TokenType
.INTEGER_LITERAL
) {
631 if (current
< end
&& current
[0].tolower () == 'l') {
633 if (current
< end
&& current
[0].tolower () == 'l') {
643 type
= TokenType
.REAL_LITERAL
;
648 if (!real_literal
&& is_ident_char (current
[0])) {
649 // allow identifiers to start with a digit
650 // as long as they contain at least one char
651 while (current
< end
&& is_ident_char (current
[0])) {
654 type
= TokenType
.IDENTIFIER
;
661 public TokenType
read_template_token (out SourceLocation token_begin
, out SourceLocation token_end
) {
663 char* begin
= current
;
664 token_begin
= SourceLocation (begin
, line
, column
);
666 int token_length_in_chars
= -1;
668 if (current
>= end
) {
669 type
= TokenType
.EOF
;
671 switch (current
[0]) {
673 type
= TokenType
.CLOSE_TEMPLATE
;
675 state_stack
.length
--;
678 token_begin
.pos
++; // $ is not part of following token
680 if (current
[0].isalpha () || current
[0] == '_') {
682 while (current
< end
&& is_ident_char (current
[0])) {
686 type
= TokenType
.IDENTIFIER
;
687 state_stack
+= State
.TEMPLATE_PART
;
688 } else if (current
[0] == '(') {
691 state_stack
+= State
.PARENS
;
692 return read_token (out token_begin
, out token_end
);
693 } else if (current
[0] == '$') {
694 type
= TokenType
.TEMPLATE_STRING_LITERAL
;
696 state_stack
+= State
.TEMPLATE_PART
;
698 Report
.error (get_source_reference (1), "unexpected character");
699 return read_template_token (out token_begin
, out token_end
);
703 type
= TokenType
.TEMPLATE_STRING_LITERAL
;
704 token_length_in_chars
= 0;
705 while (current
< end
&& current
[0] != '"' && current
[0] != '$') {
706 if (current
[0] == '\\') {
708 token_length_in_chars
++;
709 if (current
>= end
) {
713 switch (current
[0]) {
725 token_length_in_chars
++;
728 // u escape character has four hex digits
730 token_length_in_chars
++;
732 for (digit_length
= 0; digit_length
< 4 && current
< end
&& current
[0].isxdigit (); digit_length
++) {
734 token_length_in_chars
++;
736 if (digit_length
!= 4) {
737 Report
.error (get_source_reference (token_length_in_chars
), "\\u requires four hex digits");
741 // hexadecimal escape character requires two hex digits
743 token_length_in_chars
++;
745 for (digit_length
= 0; current
< end
&& current
[0].isxdigit (); digit_length
++) {
747 token_length_in_chars
++;
749 if (digit_length
< 1) {
750 Report
.error (get_source_reference (token_length_in_chars
), "\\x requires at least one hex digit");
754 Report
.error (get_source_reference (token_length_in_chars
), "invalid escape sequence");
757 } else if (current
[0] == '\n') {
761 token_length_in_chars
= 1;
763 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
764 if (u
!= (unichar
) (-1)) {
765 current
+= u
.to_utf8 (null);
766 token_length_in_chars
++;
769 Report
.error (get_source_reference (token_length_in_chars
), "invalid UTF-8 character");
773 if (current
>= end
) {
774 Report
.error (get_source_reference (token_length_in_chars
), "syntax error, expected \"");
775 state_stack
.length
--;
776 return read_token (out token_begin
, out token_end
);
778 state_stack
+= State
.TEMPLATE_PART
;
783 if (token_length_in_chars
< 0) {
784 column
+= (int) (current
- begin
);
786 column
+= token_length_in_chars
;
789 token_end
= SourceLocation (current
, line
, column
- 1);
794 public TokenType
read_token (out SourceLocation token_begin
, out SourceLocation token_end
) {
795 if (in_template ()) {
796 return read_template_token (out token_begin
, out token_end
);
797 } else if (in_template_part ()) {
798 state_stack
.length
--;
800 token_begin
= SourceLocation (current
, line
, column
);
801 token_end
= SourceLocation (current
, line
, column
- 1);
803 return TokenType
.COMMA
;
804 } else if (in_regex_literal ()) {
805 return read_regex_token (out token_begin
, out token_end
);
811 char* begin
= current
;
812 token_begin
= SourceLocation (begin
, line
, column
);
814 int token_length_in_chars
= -1;
816 if (current
>= end
) {
817 type
= TokenType
.EOF
;
818 } else if (current
[0].isalpha () || current
[0] == '_') {
820 while (current
< end
&& is_ident_char (current
[0])) {
824 type
= get_identifier_or_keyword (begin
, len
);
825 } else if (current
[0] == '@') {
826 if (current
< end
- 1 && current
[1] == '"') {
827 type
= TokenType
.OPEN_TEMPLATE
;
829 state_stack
+= State
.TEMPLATE
;
831 token_begin
.pos
++; // @ is not part of the identifier
834 while (current
< end
&& is_ident_char (current
[0])) {
838 type
= TokenType
.IDENTIFIER
;
840 } else if (current
[0].isdigit ()) {
841 type
= read_number ();
843 switch (current
[0]) {
845 type
= TokenType
.OPEN_BRACE
;
847 state_stack
+= State
.BRACE
;
850 type
= TokenType
.CLOSE_BRACE
;
852 if (state_stack
.length
> 0) {
853 state_stack
.length
--;
857 type
= TokenType
.OPEN_PARENS
;
859 state_stack
+= State
.PARENS
;
862 type
= TokenType
.CLOSE_PARENS
;
864 if (state_stack
.length
> 0) {
865 state_stack
.length
--;
867 if (in_template ()) {
868 type
= TokenType
.COMMA
;
872 type
= TokenType
.OPEN_BRACKET
;
874 state_stack
+= State
.BRACKET
;
877 type
= TokenType
.CLOSE_BRACKET
;
879 if (state_stack
.length
> 0) {
880 state_stack
.length
--;
884 type
= TokenType
.DOT
;
886 if (current
< end
- 1) {
887 if (current
[0] == '.' && current
[1] == '.') {
888 type
= TokenType
.ELLIPSIS
;
894 type
= TokenType
.COLON
;
896 if (current
< end
&& current
[0] == ':') {
897 type
= TokenType
.DOUBLE_COLON
;
902 type
= TokenType
.COMMA
;
906 type
= TokenType
.SEMICOLON
;
910 type
= TokenType
.HASH
;
914 type
= TokenType
.INTERR
;
916 if (current
< end
&& current
[0] == '?') {
917 type
= TokenType
.OP_COALESCING
;
922 type
= TokenType
.BITWISE_OR
;
925 switch (current
[0]) {
927 type
= TokenType
.ASSIGN_BITWISE_OR
;
931 type
= TokenType
.OP_OR
;
938 type
= TokenType
.BITWISE_AND
;
941 switch (current
[0]) {
943 type
= TokenType
.ASSIGN_BITWISE_AND
;
947 type
= TokenType
.OP_AND
;
954 type
= TokenType
.CARRET
;
956 if (current
< end
&& current
[0] == '=') {
957 type
= TokenType
.ASSIGN_BITWISE_XOR
;
962 type
= TokenType
.TILDE
;
966 type
= TokenType
.ASSIGN
;
969 switch (current
[0]) {
971 type
= TokenType
.OP_EQ
;
975 type
= TokenType
.LAMBDA
;
982 type
= TokenType
.OP_LT
;
985 switch (current
[0]) {
987 type
= TokenType
.OP_LE
;
991 type
= TokenType
.OP_SHIFT_LEFT
;
993 if (current
< end
&& current
[0] == '=') {
994 type
= TokenType
.ASSIGN_SHIFT_LEFT
;
1002 type
= TokenType
.OP_GT
;
1004 if (current
< end
&& current
[0] == '=') {
1005 type
= TokenType
.OP_GE
;
1010 type
= TokenType
.OP_NEG
;
1012 if (current
< end
&& current
[0] == '=') {
1013 type
= TokenType
.OP_NE
;
1018 type
= TokenType
.PLUS
;
1020 if (current
< end
) {
1021 switch (current
[0]) {
1023 type
= TokenType
.ASSIGN_ADD
;
1027 type
= TokenType
.OP_INC
;
1034 type
= TokenType
.MINUS
;
1036 if (current
< end
) {
1037 switch (current
[0]) {
1039 type
= TokenType
.ASSIGN_SUB
;
1043 type
= TokenType
.OP_DEC
;
1047 type
= TokenType
.OP_PTR
;
1054 type
= TokenType
.STAR
;
1056 if (current
< end
&& current
[0] == '=') {
1057 type
= TokenType
.ASSIGN_MUL
;
1063 case TokenType
.ASSIGN
:
1064 case TokenType
.COMMA
:
1065 case TokenType
.MINUS
:
1066 case TokenType
.OP_AND
:
1067 case TokenType
.OP_COALESCING
:
1068 case TokenType
.OP_EQ
:
1069 case TokenType
.OP_GE
:
1070 case TokenType
.OP_GT
:
1071 case TokenType
.OP_LE
:
1072 case TokenType
.OP_LT
:
1073 case TokenType
.OP_NE
:
1074 case TokenType
.OP_NEG
:
1075 case TokenType
.OP_OR
:
1076 case TokenType
.OPEN_BRACE
:
1077 case TokenType
.OPEN_PARENS
:
1078 case TokenType
.PLUS
:
1079 case TokenType
.RETURN
:
1080 type
= TokenType
.OPEN_REGEX_LITERAL
;
1081 state_stack
+= State
.REGEX_LITERAL
;
1085 type
= TokenType
.DIV
;
1087 if (current
< end
&& current
[0] == '=') {
1088 type
= TokenType
.ASSIGN_DIV
;
1095 type
= TokenType
.PERCENT
;
1097 if (current
< end
&& current
[0] == '=') {
1098 type
= TokenType
.ASSIGN_PERCENT
;
1104 if (begin
[0] == '\'') {
1105 type
= TokenType
.CHARACTER_LITERAL
;
1106 } else if (current
< end
- 6 && begin
[1] == '"' && begin
[2] == '"') {
1107 type
= TokenType
.VERBATIM_STRING_LITERAL
;
1108 token_length_in_chars
= 6;
1110 while (current
< end
- 4) {
1111 if (current
[0] == '"' && current
[1] == '"' && current
[2] == '"' && current
[3] != '"') {
1113 } else if (current
[0] == '\n') {
1117 token_length_in_chars
= 3;
1119 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
1120 if (u
!= (unichar
) (-1)) {
1121 current
+= u
.to_utf8 (null);
1122 token_length_in_chars
++;
1124 Report
.error (get_source_reference (token_length_in_chars
), "invalid UTF-8 character");
1128 if (current
[0] == '"' && current
[1] == '"' && current
[2] == '"') {
1131 Report
.error (get_source_reference (token_length_in_chars
), "syntax error, expected \"\"\"");
1135 type
= TokenType
.STRING_LITERAL
;
1137 token_length_in_chars
= 2;
1139 while (current
< end
&& current
[0] != begin
[0]) {
1140 if (current
[0] == '\\') {
1142 token_length_in_chars
++;
1143 if (current
>= end
) {
1147 switch (current
[0]) {
1160 token_length_in_chars
++;
1163 // u escape character has four hex digits
1165 token_length_in_chars
++;
1167 for (digit_length
= 0; digit_length
< 4 && current
< end
&& current
[0].isxdigit (); digit_length
++) {
1169 token_length_in_chars
++;
1171 if (digit_length
!= 4) {
1172 Report
.error (get_source_reference (token_length_in_chars
), "\\u requires four hex digits");
1176 // hexadecimal escape character requires two hex digits
1178 token_length_in_chars
++;
1180 for (digit_length
= 0; current
< end
&& current
[0].isxdigit (); digit_length
++) {
1182 token_length_in_chars
++;
1184 if (digit_length
< 1) {
1185 Report
.error (get_source_reference (token_length_in_chars
), "\\x requires at least one hex digit");
1189 Report
.error (get_source_reference (token_length_in_chars
), "invalid escape sequence");
1192 } else if (current
[0] == '\n') {
1196 token_length_in_chars
= 1;
1198 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
1199 if (u
!= (unichar
) (-1)) {
1200 current
+= u
.to_utf8 (null);
1201 token_length_in_chars
++;
1204 Report
.error (get_source_reference (token_length_in_chars
), "invalid UTF-8 character");
1207 if (current
< end
&& begin
[0] == '\'' && current
[0] != '\'') {
1208 // multiple characters in single character literal
1209 Report
.error (get_source_reference (token_length_in_chars
), "invalid character literal");
1212 if (current
< end
) {
1215 Report
.error (get_source_reference (token_length_in_chars
), "syntax error, expected %c".printf (begin
[0]));
1219 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
1220 if (u
!= (unichar
) (-1)) {
1221 current
+= u
.to_utf8 (null);
1222 Report
.error (get_source_reference (0), "syntax error, unexpected character");
1225 Report
.error (get_source_reference (0), "invalid UTF-8 character");
1228 return read_token (out token_begin
, out token_end
);
1232 if (token_length_in_chars
< 0) {
1233 column
+= (int) (current
- begin
);
1235 column
+= token_length_in_chars
;
1238 token_end
= SourceLocation (current
, line
, column
- 1);
1244 static bool matches (char* begin
, string keyword
) {
1245 char* keyword_array
= (char*) keyword
;
1246 long len
= keyword
.length
;
1247 for (int i
= 0; i
< len
; i
++) {
1248 if (begin
[i
] != keyword_array
[i
]) {
1255 bool pp_whitespace () {
1257 while (current
< end
&& current
[0].isspace () && current
[0] != '\n') {
1266 while (pp_whitespace () || comment ()) {
1270 void pp_directive () {
1275 if (line
== 1 && column
== 2 && current
< end
&& current
[0] == '!') {
1277 // skip until end of line or end of file
1278 while (current
< end
&& current
[0] != '\n') {
1286 char* begin
= current
;
1288 while (current
< end
&& current
[0].isalnum ()) {
1294 if (len
== 2 && matches (begin
, "if")) {
1296 } else if (len
== 4 && matches (begin
, "elif")) {
1298 } else if (len
== 4 && matches (begin
, "else")) {
1300 } else if (len
== 5 && matches (begin
, "endif")) {
1303 Report
.error (get_source_reference (-len
, len
), "syntax error, invalid preprocessing directive");
1306 if (conditional_stack
.length
> 0
1307 && conditional_stack
[conditional_stack
.length
- 1].skip_section
) {
1308 // skip lines until next preprocessing directive
1310 while (current
< end
) {
1311 if (bol
&& current
[0] == '#') {
1312 // go back to begin of line
1313 current
-= (column
- 1);
1317 if (current
[0] == '\n') {
1321 } else if (!current
[0].isspace ()) {
1332 if (current
>= end
|| current
[0] != '\n') {
1333 Report
.error (get_source_reference (0), "syntax error, expected newline");
1337 void parse_pp_if () {
1340 bool condition
= parse_pp_expression ();
1344 conditional_stack
+= Conditional ();
1346 if (condition
&& (conditional_stack
.length
== 1 || !conditional_stack
[conditional_stack
.length
- 2].skip_section
)) {
1347 // condition true => process code within if
1348 conditional_stack
[conditional_stack
.length
- 1].matched
= true;
1350 // skip lines until next preprocessing directive
1351 conditional_stack
[conditional_stack
.length
- 1].skip_section
= true;
1355 void parse_pp_elif () {
1358 bool condition
= parse_pp_expression ();
1362 if (conditional_stack
.length
== 0 || conditional_stack
[conditional_stack
.length
- 1].else_found
) {
1363 Report
.error (get_source_reference (0), "syntax error, unexpected #elif");
1367 if (condition
&& !conditional_stack
[conditional_stack
.length
- 1].matched
1368 && (conditional_stack
.length
== 1 || !conditional_stack
[conditional_stack
.length
- 2].skip_section
)) {
1369 // condition true => process code within if
1370 conditional_stack
[conditional_stack
.length
- 1].matched
= true;
1371 conditional_stack
[conditional_stack
.length
- 1].skip_section
= false;
1373 // skip lines until next preprocessing directive
1374 conditional_stack
[conditional_stack
.length
- 1].skip_section
= true;
1378 void parse_pp_else () {
1381 if (conditional_stack
.length
== 0 || conditional_stack
[conditional_stack
.length
- 1].else_found
) {
1382 Report
.error (get_source_reference (0), "syntax error, unexpected #else");
1386 if (!conditional_stack
[conditional_stack
.length
- 1].matched
1387 && (conditional_stack
.length
== 1 || !conditional_stack
[conditional_stack
.length
- 2].skip_section
)) {
1388 // condition true => process code within if
1389 conditional_stack
[conditional_stack
.length
- 1].matched
= true;
1390 conditional_stack
[conditional_stack
.length
- 1].skip_section
= false;
1392 // skip lines until next preprocessing directive
1393 conditional_stack
[conditional_stack
.length
- 1].skip_section
= true;
1397 void parse_pp_endif () {
1400 if (conditional_stack
.length
== 0) {
1401 Report
.error (get_source_reference (0), "syntax error, unexpected #endif");
1405 conditional_stack
.length
--;
1408 bool parse_pp_symbol () {
1410 while (current
< end
&& is_ident_char (current
[0])) {
1417 Report
.error (get_source_reference (0), "syntax error, expected identifier");
1421 string identifier
= ((string) (current
- len
)).substring (0, len
);
1423 if (identifier
== "true") {
1425 } else if (identifier
== "false") {
1428 defined
= source_file
.context
.is_defined (identifier
);
1434 bool parse_pp_primary_expression () {
1435 if (current
>= end
) {
1436 Report
.error (get_source_reference (0), "syntax error, expected identifier");
1437 } else if (is_ident_char (current
[0])) {
1438 return parse_pp_symbol ();
1439 } else if (current
[0] == '(') {
1443 bool result
= parse_pp_expression ();
1445 if (current
< end
&& current
[0] == ')') {
1449 Report
.error (get_source_reference (0), "syntax error, expected `)'");
1453 Report
.error (get_source_reference (0), "syntax error, expected identifier");
1458 bool parse_pp_unary_expression () {
1459 if (current
< end
&& current
[0] == '!') {
1463 return !parse_pp_unary_expression ();
1466 return parse_pp_primary_expression ();
1469 bool parse_pp_equality_expression () {
1470 bool left
= parse_pp_unary_expression ();
1473 if (current
< end
- 1 && current
[0] == '=' && current
[1] == '=') {
1477 bool right
= parse_pp_unary_expression ();
1478 left
= (left
== right
);
1479 } else if (current
< end
- 1 && current
[0] == '!' && current
[1] == '=') {
1483 bool right
= parse_pp_unary_expression ();
1484 left
= (left
!= right
);
1492 bool parse_pp_and_expression () {
1493 bool left
= parse_pp_equality_expression ();
1495 while (current
< end
- 1 && current
[0] == '&' && current
[1] == '&') {
1499 bool right
= parse_pp_equality_expression ();
1500 left
= left
&& right
;
1505 bool parse_pp_or_expression () {
1506 bool left
= parse_pp_and_expression ();
1508 while (current
< end
- 1 && current
[0] == '|' && current
[1] == '|') {
1512 bool right
= parse_pp_and_expression ();
1513 left
= left
|| right
;
1518 bool parse_pp_expression () {
1519 return parse_pp_or_expression ();
1522 bool whitespace () {
1524 bool bol
= (column
== 1);
1525 while (current
< end
&& current
[0].isspace ()) {
1526 if (current
[0] == '\n') {
1535 if (bol
&& current
< end
&& current
[0] == '#') {
1542 bool comment (bool file_comment
= false) {
1544 || current
> end
- 2
1545 || current
[0] != '/'
1546 || (current
[1] != '/' && current
[1] != '*')) {
1550 if (current
[1] == '/') {
1551 SourceReference source_reference
= null;
1553 source_reference
= get_source_reference (0);
1556 // single-line comment
1558 char* begin
= current
;
1560 // skip until end of line or end of file
1561 while (current
< end
&& current
[0] != '\n') {
1565 if (source_reference
!= null) {
1566 push_comment (((string) begin
).substring (0, (long) (current
- begin
)), source_reference
, file_comment
);
1569 SourceReference source_reference
= null;
1571 if (file_comment
&& current
[2] == '*') {
1575 if (current
[2] == '*' || file_comment
) {
1576 source_reference
= get_source_reference (0);
1582 char* begin
= current
;
1583 while (current
< end
- 1
1584 && (current
[0] != '*' || current
[1] != '/')) {
1585 if (current
[0] == '\n') {
1593 if (current
== end
- 1) {
1594 Report
.error (get_source_reference (0), "syntax error, expected */");
1598 if (source_reference
!= null) {
1599 push_comment (((string) begin
).substring (0, (long) (current
- begin
)), source_reference
, file_comment
);
1610 while (whitespace () || comment ()) {
1614 public void parse_file_comments () {
1615 while (whitespace () || comment (true)) {
1619 void push_comment (string comment_item
, SourceReference source_reference
, bool file_comment
) {
1620 if (comment_item
[0] == '*') {
1621 if (_comment
!= null) {
1622 // extra doc comment, add it to source file comments
1623 source_file
.add_comment (_comment
);
1625 _comment
= new
Comment (comment_item
, source_reference
);
1629 source_file
.add_comment (new
Comment (comment_item
, source_reference
));
1635 * Clears and returns the content of the comment stack.
1637 * @return saved comment
1639 public Comment?
pop_comment () {
1640 if (_comment
== null) {
1644 var comment
= _comment
;