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]) {
214 token_length_in_chars
++;
217 // u escape character has four hex digits
219 token_length_in_chars
++;
221 for (digit_length
= 0; digit_length
< 4 && current
< end
&& current
[0].isxdigit (); digit_length
++) {
223 token_length_in_chars
++;
225 if (digit_length
!= 4) {
226 Report
.error (get_source_reference (token_length_in_chars
), "\\u requires four hex digits");
230 // hexadecimal escape character requires two hex digits
232 token_length_in_chars
++;
234 for (digit_length
= 0; current
< end
&& current
[0].isxdigit (); digit_length
++) {
236 token_length_in_chars
++;
238 if (digit_length
< 1) {
239 Report
.error (get_source_reference (token_length_in_chars
), "\\x requires at least one hex digit");
243 Report
.error (get_source_reference (token_length_in_chars
), "invalid escape sequence");
246 } else if (current
[0] == '\n') {
249 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
250 if (u
!= (unichar
) (-1)) {
251 current
+= u
.to_utf8 (null);
252 token_length_in_chars
++;
255 Report
.error (get_source_reference (token_length_in_chars
), "invalid UTF-8 character");
259 if (current
>= end
|| current
[0] == '\n') {
260 Report
.error (get_source_reference (token_length_in_chars
), "syntax error, expected \"");
261 state_stack
.length
--;
262 return read_token (out token_begin
, out token_end
);
268 if (token_length_in_chars
< 0) {
269 column
+= (int) (current
- begin
);
271 column
+= token_length_in_chars
;
274 token_end
= SourceLocation (current
, line
, column
- 1);
279 public static TokenType
get_identifier_or_keyword (char* begin
, int len
) {
284 if (matches (begin
, "as")) return TokenType
.AS
;
287 if (matches (begin
, "do")) return TokenType
.DO
;
304 if (matches (begin
, "for")) return TokenType
.FOR
;
307 if (matches (begin
, "get")) return TokenType
.GET
;
310 if (matches (begin
, "new")) return TokenType
.NEW
;
313 if (matches (begin
, "out")) return TokenType
.OUT
;
316 if (matches (begin
, "ref")) return TokenType
.REF
;
319 if (matches (begin
, "set")) return TokenType
.SET
;
322 if (matches (begin
, "try")) return TokenType
.TRY
;
325 if (matches (begin
, "var")) return TokenType
.VAR
;
332 if (matches (begin
, "base")) return TokenType
.BASE
;
335 if (matches (begin
, "case")) return TokenType
.CASE
;
340 if (matches (begin
, "else")) return TokenType
.ELSE
;
343 if (matches (begin
, "enum")) return TokenType
.ENUM
;
348 if (matches (begin
, "lock")) return TokenType
.LOCK
;
351 if (matches (begin
, "null")) return TokenType
.NULL
;
356 if (matches (begin
, "this")) return TokenType
.THIS
;
359 if (matches (begin
, "true")) return TokenType
.TRUE
;
364 if (matches (begin
, "void")) return TokenType
.VOID
;
367 if (matches (begin
, "weak")) return TokenType
.WEAK
;
374 if (matches (begin
, "async")) return TokenType
.ASYNC
;
377 if (matches (begin
, "break")) return TokenType
.BREAK
;
382 if (matches (begin
, "catch")) return TokenType
.CATCH
;
385 if (matches (begin
, "class")) return TokenType
.CLASS
;
388 if (matches (begin
, "const")) return TokenType
.CONST
;
393 if (matches (begin
, "false")) return TokenType
.FALSE
;
396 if (matches (begin
, "owned")) return TokenType
.OWNED
;
399 if (matches (begin
, "throw")) return TokenType
.THROW
;
402 if (matches (begin
, "using")) return TokenType
.USING
;
405 if (matches (begin
, "while")) return TokenType
.WHILE
;
408 if (matches (begin
, "yield")) return TokenType
.YIELD
;
415 if (matches (begin
, "delete")) return TokenType
.DELETE
;
418 if (matches (begin
, "extern")) return TokenType
.EXTERN
;
421 if (matches (begin
, "inline")) return TokenType
.INLINE
;
426 if (matches (begin
, "params")) return TokenType
.PARAMS
;
429 if (matches (begin
, "public")) return TokenType
.PUBLIC
;
434 if (matches (begin
, "return")) return TokenType
.RETURN
;
439 if (matches (begin
, "sealed")) return TokenType
.SEALED
;
444 if (matches (begin
, "signal")) return TokenType
.SIGNAL
;
447 if (matches (begin
, "sizeof")) return TokenType
.SIZEOF
;
454 if (matches (begin
, "static")) return TokenType
.STATIC
;
457 if (matches (begin
, "struct")) return TokenType
.STRUCT
;
462 if (matches (begin
, "switch")) return TokenType
.SWITCH
;
469 if (matches (begin
, "throws")) return TokenType
.THROWS
;
472 if (matches (begin
, "typeof")) return TokenType
.TYPEOF
;
483 if (matches (begin
, "default")) return TokenType
.DEFAULT
;
486 if (matches (begin
, "dynamic")) return TokenType
.DYNAMIC
;
491 if (matches (begin
, "ensures")) return TokenType
.ENSURES
;
496 if (matches (begin
, "finally")) return TokenType
.FINALLY
;
499 if (matches (begin
, "foreach")) return TokenType
.FOREACH
;
504 if (matches (begin
, "private")) return TokenType
.PRIVATE
;
507 if (matches (begin
, "unowned")) return TokenType
.UNOWNED
;
510 if (matches (begin
, "virtual")) return TokenType
.VIRTUAL
;
517 if (matches (begin
, "abstract")) return TokenType
.ABSTRACT
;
520 if (matches (begin
, "continue")) return TokenType
.CONTINUE
;
523 if (matches (begin
, "delegate")) return TokenType
.DELEGATE
;
526 if (matches (begin
, "internal")) return TokenType
.INTERNAL
;
529 if (matches (begin
, "override")) return TokenType
.OVERRIDE
;
532 if (matches (begin
, "requires")) return TokenType
.REQUIRES
;
535 if (matches (begin
, "volatile")) return TokenType
.VOLATILE
;
542 if (matches (begin
, "construct")) return TokenType
.CONSTRUCT
;
545 if (matches (begin
, "interface")) return TokenType
.INTERFACE
;
548 if (matches (begin
, "namespace")) return TokenType
.NAMESPACE
;
551 if (matches (begin
, "protected")) return TokenType
.PROTECTED
;
556 if (matches (begin
, "errordomain")) return TokenType
.ERRORDOMAIN
;
559 return TokenType
.IDENTIFIER
;
562 TokenType
read_number () {
563 var type
= TokenType
.INTEGER_LITERAL
;
566 if (current
< end
- 2 && current
[0] == '0'
567 && current
[1] == 'x' && current
[2].isxdigit ()) {
568 // hexadecimal integer literal
570 while (current
< end
&& current
[0].isxdigit ()) {
575 while (current
< end
&& current
[0].isdigit ()) {
581 if (current
< end
- 1 && current
[0] == '.' && current
[1].isdigit ()) {
582 type
= TokenType
.REAL_LITERAL
;
584 while (current
< end
&& current
[0].isdigit ()) {
590 if (current
< end
&& current
[0].tolower () == 'e') {
591 type
= TokenType
.REAL_LITERAL
;
593 if (current
< end
&& (current
[0] == '+' || current
[0] == '-')) {
596 while (current
< end
&& current
[0].isdigit ()) {
603 bool real_literal
= (type
== TokenType
.REAL_LITERAL
);
605 switch (current
[0]) {
608 if (type
== TokenType
.INTEGER_LITERAL
) {
610 if (current
< end
&& current
[0].tolower () == 'l') {
617 if (type
== TokenType
.INTEGER_LITERAL
) {
619 if (current
< end
&& current
[0].tolower () == 'l') {
621 if (current
< end
&& current
[0].tolower () == 'l') {
631 type
= TokenType
.REAL_LITERAL
;
636 if (!real_literal
&& is_ident_char (current
[0])) {
637 // allow identifiers to start with a digit
638 // as long as they contain at least one char
639 while (current
< end
&& is_ident_char (current
[0])) {
642 type
= TokenType
.IDENTIFIER
;
649 public TokenType
read_template_token (out SourceLocation token_begin
, out SourceLocation token_end
) {
651 char* begin
= current
;
652 token_begin
= SourceLocation (begin
, line
, column
);
654 int token_length_in_chars
= -1;
656 if (current
>= end
) {
657 type
= TokenType
.EOF
;
659 switch (current
[0]) {
661 type
= TokenType
.CLOSE_TEMPLATE
;
663 state_stack
.length
--;
666 token_begin
.pos
++; // $ is not part of following token
668 if (current
[0].isalpha () || current
[0] == '_') {
670 while (current
< end
&& is_ident_char (current
[0])) {
674 type
= TokenType
.IDENTIFIER
;
675 state_stack
+= State
.TEMPLATE_PART
;
676 } else if (current
[0] == '(') {
679 state_stack
+= State
.PARENS
;
680 return read_token (out token_begin
, out token_end
);
681 } else if (current
[0] == '$') {
682 type
= TokenType
.TEMPLATE_STRING_LITERAL
;
684 state_stack
+= State
.TEMPLATE_PART
;
686 Report
.error (get_source_reference (1), "unexpected character");
687 return read_template_token (out token_begin
, out token_end
);
691 type
= TokenType
.TEMPLATE_STRING_LITERAL
;
692 token_length_in_chars
= 0;
693 while (current
< end
&& current
[0] != '"' && current
[0] != '$') {
694 if (current
[0] == '\\') {
696 token_length_in_chars
++;
697 if (current
>= end
) {
701 switch (current
[0]) {
713 token_length_in_chars
++;
716 // u escape character has four hex digits
718 token_length_in_chars
++;
720 for (digit_length
= 0; digit_length
< 4 && current
< end
&& current
[0].isxdigit (); digit_length
++) {
722 token_length_in_chars
++;
724 if (digit_length
!= 4) {
725 Report
.error (get_source_reference (token_length_in_chars
), "\\u requires four hex digits");
729 // hexadecimal escape character requires two hex digits
731 token_length_in_chars
++;
733 for (digit_length
= 0; current
< end
&& current
[0].isxdigit (); digit_length
++) {
735 token_length_in_chars
++;
737 if (digit_length
< 1) {
738 Report
.error (get_source_reference (token_length_in_chars
), "\\x requires at least one hex digit");
742 Report
.error (get_source_reference (token_length_in_chars
), "invalid escape sequence");
745 } else if (current
[0] == '\n') {
749 token_length_in_chars
= 1;
751 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
752 if (u
!= (unichar
) (-1)) {
753 current
+= u
.to_utf8 (null);
754 token_length_in_chars
++;
757 Report
.error (get_source_reference (token_length_in_chars
), "invalid UTF-8 character");
761 if (current
>= end
) {
762 Report
.error (get_source_reference (token_length_in_chars
), "syntax error, expected \"");
763 state_stack
.length
--;
764 return read_token (out token_begin
, out token_end
);
766 state_stack
+= State
.TEMPLATE_PART
;
771 if (token_length_in_chars
< 0) {
772 column
+= (int) (current
- begin
);
774 column
+= token_length_in_chars
;
777 token_end
= SourceLocation (current
, line
, column
- 1);
782 public TokenType
read_token (out SourceLocation token_begin
, out SourceLocation token_end
) {
783 if (in_template ()) {
784 return read_template_token (out token_begin
, out token_end
);
785 } else if (in_template_part ()) {
786 state_stack
.length
--;
788 token_begin
= SourceLocation (current
, line
, column
);
789 token_end
= SourceLocation (current
, line
, column
- 1);
791 return TokenType
.COMMA
;
792 } else if (in_regex_literal ()) {
793 return read_regex_token (out token_begin
, out token_end
);
799 char* begin
= current
;
800 token_begin
= SourceLocation (begin
, line
, column
);
802 int token_length_in_chars
= -1;
804 if (current
>= end
) {
805 type
= TokenType
.EOF
;
806 } else if (current
[0].isalpha () || current
[0] == '_') {
808 while (current
< end
&& is_ident_char (current
[0])) {
812 type
= get_identifier_or_keyword (begin
, len
);
813 } else if (current
[0] == '@') {
814 if (current
< end
- 1 && current
[1] == '"') {
815 type
= TokenType
.OPEN_TEMPLATE
;
817 state_stack
+= State
.TEMPLATE
;
819 token_begin
.pos
++; // @ is not part of the identifier
822 while (current
< end
&& is_ident_char (current
[0])) {
826 type
= TokenType
.IDENTIFIER
;
828 } else if (current
[0].isdigit ()) {
829 type
= read_number ();
831 switch (current
[0]) {
833 type
= TokenType
.OPEN_BRACE
;
835 state_stack
+= State
.BRACE
;
838 type
= TokenType
.CLOSE_BRACE
;
840 if (state_stack
.length
> 0) {
841 state_stack
.length
--;
845 type
= TokenType
.OPEN_PARENS
;
847 state_stack
+= State
.PARENS
;
850 type
= TokenType
.CLOSE_PARENS
;
852 if (state_stack
.length
> 0) {
853 state_stack
.length
--;
855 if (in_template ()) {
856 type
= TokenType
.COMMA
;
860 type
= TokenType
.OPEN_BRACKET
;
862 state_stack
+= State
.BRACKET
;
865 type
= TokenType
.CLOSE_BRACKET
;
867 if (state_stack
.length
> 0) {
868 state_stack
.length
--;
872 type
= TokenType
.DOT
;
874 if (current
< end
- 1) {
875 if (current
[0] == '.' && current
[1] == '.') {
876 type
= TokenType
.ELLIPSIS
;
882 type
= TokenType
.COLON
;
884 if (current
< end
&& current
[0] == ':') {
885 type
= TokenType
.DOUBLE_COLON
;
890 type
= TokenType
.COMMA
;
894 type
= TokenType
.SEMICOLON
;
898 type
= TokenType
.HASH
;
902 type
= TokenType
.INTERR
;
904 if (current
< end
&& current
[0] == '?') {
905 type
= TokenType
.OP_COALESCING
;
910 type
= TokenType
.BITWISE_OR
;
913 switch (current
[0]) {
915 type
= TokenType
.ASSIGN_BITWISE_OR
;
919 type
= TokenType
.OP_OR
;
926 type
= TokenType
.BITWISE_AND
;
929 switch (current
[0]) {
931 type
= TokenType
.ASSIGN_BITWISE_AND
;
935 type
= TokenType
.OP_AND
;
942 type
= TokenType
.CARRET
;
944 if (current
< end
&& current
[0] == '=') {
945 type
= TokenType
.ASSIGN_BITWISE_XOR
;
950 type
= TokenType
.TILDE
;
954 type
= TokenType
.ASSIGN
;
957 switch (current
[0]) {
959 type
= TokenType
.OP_EQ
;
963 type
= TokenType
.LAMBDA
;
970 type
= TokenType
.OP_LT
;
973 switch (current
[0]) {
975 type
= TokenType
.OP_LE
;
979 type
= TokenType
.OP_SHIFT_LEFT
;
981 if (current
< end
&& current
[0] == '=') {
982 type
= TokenType
.ASSIGN_SHIFT_LEFT
;
990 type
= TokenType
.OP_GT
;
992 if (current
< end
&& current
[0] == '=') {
993 type
= TokenType
.OP_GE
;
998 type
= TokenType
.OP_NEG
;
1000 if (current
< end
&& current
[0] == '=') {
1001 type
= TokenType
.OP_NE
;
1006 type
= TokenType
.PLUS
;
1008 if (current
< end
) {
1009 switch (current
[0]) {
1011 type
= TokenType
.ASSIGN_ADD
;
1015 type
= TokenType
.OP_INC
;
1022 type
= TokenType
.MINUS
;
1024 if (current
< end
) {
1025 switch (current
[0]) {
1027 type
= TokenType
.ASSIGN_SUB
;
1031 type
= TokenType
.OP_DEC
;
1035 type
= TokenType
.OP_PTR
;
1042 type
= TokenType
.STAR
;
1044 if (current
< end
&& current
[0] == '=') {
1045 type
= TokenType
.ASSIGN_MUL
;
1051 case TokenType
.ASSIGN
:
1052 case TokenType
.COMMA
:
1053 case TokenType
.MINUS
:
1054 case TokenType
.OP_AND
:
1055 case TokenType
.OP_COALESCING
:
1056 case TokenType
.OP_EQ
:
1057 case TokenType
.OP_GE
:
1058 case TokenType
.OP_GT
:
1059 case TokenType
.OP_LE
:
1060 case TokenType
.OP_LT
:
1061 case TokenType
.OP_NE
:
1062 case TokenType
.OP_NEG
:
1063 case TokenType
.OP_OR
:
1064 case TokenType
.OPEN_BRACE
:
1065 case TokenType
.OPEN_PARENS
:
1066 case TokenType
.PLUS
:
1067 case TokenType
.RETURN
:
1068 type
= TokenType
.OPEN_REGEX_LITERAL
;
1069 state_stack
+= State
.REGEX_LITERAL
;
1073 type
= TokenType
.DIV
;
1075 if (current
< end
&& current
[0] == '=') {
1076 type
= TokenType
.ASSIGN_DIV
;
1083 type
= TokenType
.PERCENT
;
1085 if (current
< end
&& current
[0] == '=') {
1086 type
= TokenType
.ASSIGN_PERCENT
;
1092 if (begin
[0] == '\'') {
1093 type
= TokenType
.CHARACTER_LITERAL
;
1094 } else if (current
< end
- 6 && begin
[1] == '"' && begin
[2] == '"') {
1095 type
= TokenType
.VERBATIM_STRING_LITERAL
;
1096 token_length_in_chars
= 6;
1098 while (current
< end
- 4) {
1099 if (current
[0] == '"' && current
[1] == '"' && current
[2] == '"' && current
[3] != '"') {
1101 } else if (current
[0] == '\n') {
1105 token_length_in_chars
= 3;
1107 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
1108 if (u
!= (unichar
) (-1)) {
1109 current
+= u
.to_utf8 (null);
1110 token_length_in_chars
++;
1112 Report
.error (get_source_reference (token_length_in_chars
), "invalid UTF-8 character");
1116 if (current
[0] == '"' && current
[1] == '"' && current
[2] == '"') {
1119 Report
.error (get_source_reference (token_length_in_chars
), "syntax error, expected \"\"\"");
1123 type
= TokenType
.STRING_LITERAL
;
1125 token_length_in_chars
= 2;
1127 while (current
< end
&& current
[0] != begin
[0]) {
1128 if (current
[0] == '\\') {
1130 token_length_in_chars
++;
1131 if (current
>= end
) {
1135 switch (current
[0]) {
1148 token_length_in_chars
++;
1151 // u escape character has four hex digits
1153 token_length_in_chars
++;
1155 for (digit_length
= 0; digit_length
< 4 && current
< end
&& current
[0].isxdigit (); digit_length
++) {
1157 token_length_in_chars
++;
1159 if (digit_length
!= 4) {
1160 Report
.error (get_source_reference (token_length_in_chars
), "\\u requires four hex digits");
1164 // hexadecimal escape character requires two hex digits
1166 token_length_in_chars
++;
1168 for (digit_length
= 0; current
< end
&& current
[0].isxdigit (); digit_length
++) {
1170 token_length_in_chars
++;
1172 if (digit_length
< 1) {
1173 Report
.error (get_source_reference (token_length_in_chars
), "\\x requires at least one hex digit");
1177 Report
.error (get_source_reference (token_length_in_chars
), "invalid escape sequence");
1180 } else if (current
[0] == '\n') {
1184 token_length_in_chars
= 1;
1186 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
1187 if (u
!= (unichar
) (-1)) {
1188 current
+= u
.to_utf8 (null);
1189 token_length_in_chars
++;
1192 Report
.error (get_source_reference (token_length_in_chars
), "invalid UTF-8 character");
1195 if (current
< end
&& begin
[0] == '\'' && current
[0] != '\'') {
1196 // multiple characters in single character literal
1197 Report
.error (get_source_reference (token_length_in_chars
), "invalid character literal");
1200 if (current
< end
) {
1203 Report
.error (get_source_reference (token_length_in_chars
), "syntax error, expected %c".printf (begin
[0]));
1207 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
1208 if (u
!= (unichar
) (-1)) {
1209 current
+= u
.to_utf8 (null);
1210 Report
.error (get_source_reference (0), "syntax error, unexpected character");
1213 Report
.error (get_source_reference (0), "invalid UTF-8 character");
1216 return read_token (out token_begin
, out token_end
);
1220 if (token_length_in_chars
< 0) {
1221 column
+= (int) (current
- begin
);
1223 column
+= token_length_in_chars
;
1226 token_end
= SourceLocation (current
, line
, column
- 1);
1232 static bool matches (char* begin
, string keyword
) {
1233 char* keyword_array
= (char*) keyword
;
1234 long len
= keyword
.length
;
1235 for (int i
= 0; i
< len
; i
++) {
1236 if (begin
[i
] != keyword_array
[i
]) {
1243 bool pp_whitespace () {
1245 while (current
< end
&& current
[0].isspace () && current
[0] != '\n') {
1254 while (pp_whitespace () || comment ()) {
1258 void pp_directive () {
1263 if (line
== 1 && column
== 2 && current
< end
&& current
[0] == '!') {
1265 // skip until end of line or end of file
1266 while (current
< end
&& current
[0] != '\n') {
1274 char* begin
= current
;
1276 while (current
< end
&& current
[0].isalnum ()) {
1282 if (len
== 2 && matches (begin
, "if")) {
1284 } else if (len
== 4 && matches (begin
, "elif")) {
1286 } else if (len
== 4 && matches (begin
, "else")) {
1288 } else if (len
== 5 && matches (begin
, "endif")) {
1291 Report
.error (get_source_reference (-len
, len
), "syntax error, invalid preprocessing directive");
1294 if (conditional_stack
.length
> 0
1295 && conditional_stack
[conditional_stack
.length
- 1].skip_section
) {
1296 // skip lines until next preprocessing directive
1298 while (current
< end
) {
1299 if (bol
&& current
[0] == '#') {
1300 // go back to begin of line
1301 current
-= (column
- 1);
1305 if (current
[0] == '\n') {
1309 } else if (!current
[0].isspace ()) {
1320 if (current
>= end
|| current
[0] != '\n') {
1321 Report
.error (get_source_reference (0), "syntax error, expected newline");
1325 void parse_pp_if () {
1328 bool condition
= parse_pp_expression ();
1332 conditional_stack
+= Conditional ();
1334 if (condition
&& (conditional_stack
.length
== 1 || !conditional_stack
[conditional_stack
.length
- 2].skip_section
)) {
1335 // condition true => process code within if
1336 conditional_stack
[conditional_stack
.length
- 1].matched
= true;
1338 // skip lines until next preprocessing directive
1339 conditional_stack
[conditional_stack
.length
- 1].skip_section
= true;
1343 void parse_pp_elif () {
1346 bool condition
= parse_pp_expression ();
1350 if (conditional_stack
.length
== 0 || conditional_stack
[conditional_stack
.length
- 1].else_found
) {
1351 Report
.error (get_source_reference (0), "syntax error, unexpected #elif");
1355 if (condition
&& !conditional_stack
[conditional_stack
.length
- 1].matched
1356 && (conditional_stack
.length
== 1 || !conditional_stack
[conditional_stack
.length
- 2].skip_section
)) {
1357 // condition true => process code within if
1358 conditional_stack
[conditional_stack
.length
- 1].matched
= true;
1359 conditional_stack
[conditional_stack
.length
- 1].skip_section
= false;
1361 // skip lines until next preprocessing directive
1362 conditional_stack
[conditional_stack
.length
- 1].skip_section
= true;
1366 void parse_pp_else () {
1369 if (conditional_stack
.length
== 0 || conditional_stack
[conditional_stack
.length
- 1].else_found
) {
1370 Report
.error (get_source_reference (0), "syntax error, unexpected #else");
1374 if (!conditional_stack
[conditional_stack
.length
- 1].matched
1375 && (conditional_stack
.length
== 1 || !conditional_stack
[conditional_stack
.length
- 2].skip_section
)) {
1376 // condition true => process code within if
1377 conditional_stack
[conditional_stack
.length
- 1].matched
= true;
1378 conditional_stack
[conditional_stack
.length
- 1].skip_section
= false;
1380 // skip lines until next preprocessing directive
1381 conditional_stack
[conditional_stack
.length
- 1].skip_section
= true;
1385 void parse_pp_endif () {
1388 if (conditional_stack
.length
== 0) {
1389 Report
.error (get_source_reference (0), "syntax error, unexpected #endif");
1393 conditional_stack
.length
--;
1396 bool parse_pp_symbol () {
1398 while (current
< end
&& is_ident_char (current
[0])) {
1405 Report
.error (get_source_reference (0), "syntax error, expected identifier");
1409 string identifier
= ((string) (current
- len
)).substring (0, len
);
1411 if (identifier
== "true") {
1413 } else if (identifier
== "false") {
1416 defined
= source_file
.context
.is_defined (identifier
);
1422 bool parse_pp_primary_expression () {
1423 if (current
>= end
) {
1424 Report
.error (get_source_reference (0), "syntax error, expected identifier");
1425 } else if (is_ident_char (current
[0])) {
1426 return parse_pp_symbol ();
1427 } else if (current
[0] == '(') {
1431 bool result
= parse_pp_expression ();
1433 if (current
< end
&& current
[0] == ')') {
1437 Report
.error (get_source_reference (0), "syntax error, expected `)'");
1441 Report
.error (get_source_reference (0), "syntax error, expected identifier");
1446 bool parse_pp_unary_expression () {
1447 if (current
< end
&& current
[0] == '!') {
1451 return !parse_pp_unary_expression ();
1454 return parse_pp_primary_expression ();
1457 bool parse_pp_equality_expression () {
1458 bool left
= parse_pp_unary_expression ();
1461 if (current
< end
- 1 && current
[0] == '=' && current
[1] == '=') {
1465 bool right
= parse_pp_unary_expression ();
1466 left
= (left
== right
);
1467 } else if (current
< end
- 1 && current
[0] == '!' && current
[1] == '=') {
1471 bool right
= parse_pp_unary_expression ();
1472 left
= (left
!= right
);
1480 bool parse_pp_and_expression () {
1481 bool left
= parse_pp_equality_expression ();
1483 while (current
< end
- 1 && current
[0] == '&' && current
[1] == '&') {
1487 bool right
= parse_pp_equality_expression ();
1488 left
= left
&& right
;
1493 bool parse_pp_or_expression () {
1494 bool left
= parse_pp_and_expression ();
1496 while (current
< end
- 1 && current
[0] == '|' && current
[1] == '|') {
1500 bool right
= parse_pp_and_expression ();
1501 left
= left
|| right
;
1506 bool parse_pp_expression () {
1507 return parse_pp_or_expression ();
1510 bool whitespace () {
1512 bool bol
= (column
== 1);
1513 while (current
< end
&& current
[0].isspace ()) {
1514 if (current
[0] == '\n') {
1523 if (bol
&& current
< end
&& current
[0] == '#') {
1530 bool comment (bool file_comment
= false) {
1532 || current
> end
- 2
1533 || current
[0] != '/'
1534 || (current
[1] != '/' && current
[1] != '*')) {
1538 if (current
[1] == '/') {
1539 SourceReference source_reference
= null;
1541 source_reference
= get_source_reference (0);
1544 // single-line comment
1546 char* begin
= current
;
1548 // skip until end of line or end of file
1549 while (current
< end
&& current
[0] != '\n') {
1553 if (source_reference
!= null) {
1554 push_comment (((string) begin
).substring (0, (long) (current
- begin
)), source_reference
, file_comment
);
1557 SourceReference source_reference
= null;
1559 if (file_comment
&& current
[2] == '*') {
1563 if (current
[2] == '*' || file_comment
) {
1564 source_reference
= get_source_reference (0);
1570 char* begin
= current
;
1571 while (current
< end
- 1
1572 && (current
[0] != '*' || current
[1] != '/')) {
1573 if (current
[0] == '\n') {
1581 if (current
== end
- 1) {
1582 Report
.error (get_source_reference (0), "syntax error, expected */");
1586 if (source_reference
!= null) {
1587 push_comment (((string) begin
).substring (0, (long) (current
- begin
)), source_reference
, file_comment
);
1598 while (whitespace () || comment ()) {
1602 public void parse_file_comments () {
1603 while (whitespace () || comment (true)) {
1607 void push_comment (string comment_item
, SourceReference source_reference
, bool file_comment
) {
1608 if (comment_item
[0] == '*') {
1609 if (_comment
!= null) {
1610 // extra doc comment, add it to source file comments
1611 source_file
.add_comment (_comment
);
1613 _comment
= new
Comment (comment_item
, source_reference
);
1617 source_file
.add_comment (new
Comment (comment_item
, source_reference
));
1623 * Clears and returns the content of the comment stack.
1625 * @return saved comment
1627 public Comment?
pop_comment () {
1628 if (_comment
== null) {
1632 var comment
= _comment
;