3 * Copyright (C) 2008-2009 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>
26 * Lexical scanner for Vala source files.
28 public class Vala
.Scanner
{
29 public SourceFile source_file
{ get; private set; }
39 Conditional
[] conditional_stack
;
43 public bool else_found
;
44 public bool skip_section
;
57 public Scanner (SourceFile source_file
) {
58 this
.source_file
= source_file
;
60 char* begin
= source_file
.get_mapped_contents ();
61 end
= begin
+ source_file
.get_mapped_length ();
70 return (state_stack
.length
> 0 && state_stack
[state_stack
.length
- 1] == State
.TEMPLATE
);
73 bool in_template_part () {
74 return (state_stack
.length
> 0 && state_stack
[state_stack
.length
- 1] == State
.TEMPLATE_PART
);
77 bool is_ident_char (char c
) {
78 return (c
.isalnum () || c
== '_');
81 public static TokenType
get_identifier_or_keyword (char* begin
, int len
) {
86 if (matches (begin
, "as")) return TokenType
.AS
;
89 if (matches (begin
, "do")) return TokenType
.DO
;
106 if (matches (begin
, "for")) return TokenType
.FOR
;
109 if (matches (begin
, "get")) return TokenType
.GET
;
112 if (matches (begin
, "new")) return TokenType
.NEW
;
115 if (matches (begin
, "out")) return TokenType
.OUT
;
118 if (matches (begin
, "ref")) return TokenType
.REF
;
121 if (matches (begin
, "set")) return TokenType
.SET
;
124 if (matches (begin
, "try")) return TokenType
.TRY
;
127 if (matches (begin
, "var")) return TokenType
.VAR
;
134 if (matches (begin
, "base")) return TokenType
.BASE
;
137 if (matches (begin
, "case")) return TokenType
.CASE
;
142 if (matches (begin
, "else")) return TokenType
.ELSE
;
145 if (matches (begin
, "enum")) return TokenType
.ENUM
;
150 if (matches (begin
, "lock")) return TokenType
.LOCK
;
153 if (matches (begin
, "null")) return TokenType
.NULL
;
158 if (matches (begin
, "this")) return TokenType
.THIS
;
161 if (matches (begin
, "true")) return TokenType
.TRUE
;
166 if (matches (begin
, "void")) return TokenType
.VOID
;
169 if (matches (begin
, "weak")) return TokenType
.WEAK
;
176 if (matches (begin
, "async")) return TokenType
.ASYNC
;
179 if (matches (begin
, "break")) return TokenType
.BREAK
;
184 if (matches (begin
, "catch")) return TokenType
.CATCH
;
187 if (matches (begin
, "class")) return TokenType
.CLASS
;
190 if (matches (begin
, "const")) return TokenType
.CONST
;
195 if (matches (begin
, "false")) return TokenType
.FALSE
;
198 if (matches (begin
, "owned")) return TokenType
.OWNED
;
201 if (matches (begin
, "throw")) return TokenType
.THROW
;
204 if (matches (begin
, "using")) return TokenType
.USING
;
207 if (matches (begin
, "while")) return TokenType
.WHILE
;
210 if (matches (begin
, "yield")) return TokenType
.YIELD
;
217 if (matches (begin
, "delete")) return TokenType
.DELETE
;
220 if (matches (begin
, "extern")) return TokenType
.EXTERN
;
223 if (matches (begin
, "inline")) return TokenType
.INLINE
;
228 if (matches (begin
, "params")) return TokenType
.PARAMS
;
231 if (matches (begin
, "public")) return TokenType
.PUBLIC
;
236 if (matches (begin
, "return")) return TokenType
.RETURN
;
243 if (matches (begin
, "signal")) return TokenType
.SIGNAL
;
246 if (matches (begin
, "sizeof")) return TokenType
.SIZEOF
;
253 if (matches (begin
, "static")) return TokenType
.STATIC
;
256 if (matches (begin
, "struct")) return TokenType
.STRUCT
;
261 if (matches (begin
, "switch")) return TokenType
.SWITCH
;
268 if (matches (begin
, "throws")) return TokenType
.THROWS
;
271 if (matches (begin
, "typeof")) return TokenType
.TYPEOF
;
282 if (matches (begin
, "default")) return TokenType
.DEFAULT
;
285 if (matches (begin
, "dynamic")) return TokenType
.DYNAMIC
;
290 if (matches (begin
, "ensures")) return TokenType
.ENSURES
;
295 if (matches (begin
, "finally")) return TokenType
.FINALLY
;
298 if (matches (begin
, "foreach")) return TokenType
.FOREACH
;
303 if (matches (begin
, "private")) return TokenType
.PRIVATE
;
306 if (matches (begin
, "unowned")) return TokenType
.UNOWNED
;
309 if (matches (begin
, "virtual")) return TokenType
.VIRTUAL
;
316 if (matches (begin
, "abstract")) return TokenType
.ABSTRACT
;
319 if (matches (begin
, "continue")) return TokenType
.CONTINUE
;
322 if (matches (begin
, "delegate")) return TokenType
.DELEGATE
;
325 if (matches (begin
, "internal")) return TokenType
.INTERNAL
;
328 if (matches (begin
, "override")) return TokenType
.OVERRIDE
;
331 if (matches (begin
, "requires")) return TokenType
.REQUIRES
;
334 if (matches (begin
, "volatile")) return TokenType
.VOLATILE
;
341 if (matches (begin
, "construct")) return TokenType
.CONSTRUCT
;
344 if (matches (begin
, "interface")) return TokenType
.INTERFACE
;
347 if (matches (begin
, "namespace")) return TokenType
.NAMESPACE
;
350 if (matches (begin
, "protected")) return TokenType
.PROTECTED
;
355 if (matches (begin
, "errordomain")) return TokenType
.ERRORDOMAIN
;
358 return TokenType
.IDENTIFIER
;
361 TokenType
read_number () {
362 var type
= TokenType
.INTEGER_LITERAL
;
365 if (current
< end
- 2 && current
[0] == '0'
366 && current
[1] == 'x' && current
[2].isxdigit ()) {
367 // hexadecimal integer literal
369 while (current
< end
&& current
[0].isxdigit ()) {
374 while (current
< end
&& current
[0].isdigit ()) {
380 if (current
< end
- 1 && current
[0] == '.' && current
[1].isdigit ()) {
381 type
= TokenType
.REAL_LITERAL
;
383 while (current
< end
&& current
[0].isdigit ()) {
389 if (current
< end
&& current
[0].tolower () == 'e') {
390 type
= TokenType
.REAL_LITERAL
;
392 if (current
< end
&& (current
[0] == '+' || current
[0] == '-')) {
395 while (current
< end
&& current
[0].isdigit ()) {
402 bool real_literal
= (type
== TokenType
.REAL_LITERAL
);
404 switch (current
[0]) {
407 if (type
== TokenType
.INTEGER_LITERAL
) {
409 if (current
< end
&& current
[0].tolower () == 'l') {
416 if (type
== TokenType
.INTEGER_LITERAL
) {
418 if (current
< end
&& current
[0].tolower () == 'l') {
420 if (current
< end
&& current
[0].tolower () == 'l') {
430 type
= TokenType
.REAL_LITERAL
;
435 if (!real_literal
&& is_ident_char (current
[0])) {
436 // allow identifiers to start with a digit
437 // as long as they contain at least one char
438 while (current
< end
&& is_ident_char (current
[0])) {
441 type
= TokenType
.IDENTIFIER
;
448 public TokenType
read_template_token (out SourceLocation token_begin
, out SourceLocation token_end
) {
450 char* begin
= current
;
451 token_begin
.pos
= begin
;
452 token_begin
.line
= line
;
453 token_begin
.column
= column
;
455 int token_length_in_chars
= -1;
457 if (current
>= end
) {
458 type
= TokenType
.EOF
;
460 switch (current
[0]) {
462 type
= TokenType
.CLOSE_TEMPLATE
;
464 state_stack
.length
--;
467 token_begin
.pos
++; // $ is not part of following token
469 if (current
[0].isalpha () || current
[0] == '_') {
471 while (current
< end
&& is_ident_char (current
[0])) {
475 type
= TokenType
.IDENTIFIER
;
476 state_stack
+= State
.TEMPLATE_PART
;
477 } else if (current
[0] == '(') {
480 state_stack
+= State
.PARENS
;
481 return read_token (out token_begin
, out token_end
);
482 } else if (current
[0] == '$') {
483 type
= TokenType
.TEMPLATE_STRING_LITERAL
;
485 state_stack
+= State
.TEMPLATE_PART
;
487 Report
.error (new
SourceReference (source_file
, line
, column
+ 1, line
, column
+ 1), "unexpected character");
488 return read_template_token (out token_begin
, out token_end
);
492 type
= TokenType
.TEMPLATE_STRING_LITERAL
;
493 token_length_in_chars
= 0;
494 while (current
< end
&& current
[0] != '"' && current
[0] != '$') {
495 if (current
[0] == '\\') {
497 token_length_in_chars
++;
498 if (current
>= end
) {
502 switch (current
[0]) {
513 token_length_in_chars
++;
516 // hexadecimal escape character
518 token_length_in_chars
++;
519 while (current
< end
&& current
[0].isxdigit ()) {
521 token_length_in_chars
++;
525 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "invalid escape sequence");
528 } else if (current
[0] == '\n') {
531 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
532 if (u
!= (unichar
) (-1)) {
533 current
+= u
.to_utf8 (null);
534 token_length_in_chars
++;
537 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "invalid UTF-8 character");
541 if (current
>= end
|| current
[0] == '\n') {
542 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "syntax error, expected \"");
543 state_stack
.length
--;
544 return read_token (out token_begin
, out token_end
);
546 state_stack
+= State
.TEMPLATE_PART
;
551 if (token_length_in_chars
< 0) {
552 column
+= (int) (current
- begin
);
554 column
+= token_length_in_chars
;
557 token_end
.pos
= current
;
558 token_end
.line
= line
;
559 token_end
.column
= column
- 1;
564 public TokenType
read_token (out SourceLocation token_begin
, out SourceLocation token_end
) {
565 if (in_template ()) {
566 return read_template_token (out token_begin
, out token_end
);
567 } else if (in_template_part ()) {
568 state_stack
.length
--;
570 token_begin
.pos
= current
;
571 token_begin
.line
= line
;
572 token_begin
.column
= column
;
574 token_end
.pos
= current
;
575 token_end
.line
= line
;
576 token_end
.column
= column
- 1;
578 return TokenType
.COMMA
;
584 char* begin
= current
;
585 token_begin
.pos
= begin
;
586 token_begin
.line
= line
;
587 token_begin
.column
= column
;
589 int token_length_in_chars
= -1;
591 if (current
>= end
) {
592 type
= TokenType
.EOF
;
593 } else if (current
[0].isalpha () || current
[0] == '_') {
595 while (current
< end
&& is_ident_char (current
[0])) {
599 type
= get_identifier_or_keyword (begin
, len
);
600 } else if (current
[0] == '@') {
601 if (current
< end
- 1 && current
[1] == '"') {
602 type
= TokenType
.OPEN_TEMPLATE
;
604 state_stack
+= State
.TEMPLATE
;
606 token_begin
.pos
++; // @ is not part of the identifier
609 while (current
< end
&& is_ident_char (current
[0])) {
613 type
= TokenType
.IDENTIFIER
;
615 } else if (current
[0].isdigit ()) {
616 type
= read_number ();
618 switch (current
[0]) {
620 type
= TokenType
.OPEN_BRACE
;
622 state_stack
+= State
.BRACE
;
625 type
= TokenType
.CLOSE_BRACE
;
627 state_stack
.length
--;
630 type
= TokenType
.OPEN_PARENS
;
632 state_stack
+= State
.PARENS
;
635 type
= TokenType
.CLOSE_PARENS
;
637 state_stack
.length
--;
638 if (in_template ()) {
639 type
= TokenType
.COMMA
;
643 type
= TokenType
.OPEN_BRACKET
;
645 state_stack
+= State
.BRACKET
;
648 type
= TokenType
.CLOSE_BRACKET
;
650 state_stack
.length
--;
653 type
= TokenType
.DOT
;
655 if (current
< end
- 1) {
656 if (current
[0] == '.' && current
[1] == '.') {
657 type
= TokenType
.ELLIPSIS
;
663 type
= TokenType
.COLON
;
665 if (current
< end
&& current
[0] == ':') {
666 type
= TokenType
.DOUBLE_COLON
;
671 type
= TokenType
.COMMA
;
675 type
= TokenType
.SEMICOLON
;
679 type
= TokenType
.HASH
;
683 type
= TokenType
.INTERR
;
687 type
= TokenType
.BITWISE_OR
;
690 switch (current
[0]) {
692 type
= TokenType
.ASSIGN_BITWISE_OR
;
696 type
= TokenType
.OP_OR
;
703 type
= TokenType
.BITWISE_AND
;
706 switch (current
[0]) {
708 type
= TokenType
.ASSIGN_BITWISE_AND
;
712 type
= TokenType
.OP_AND
;
719 type
= TokenType
.CARRET
;
721 if (current
< end
&& current
[0] == '=') {
722 type
= TokenType
.ASSIGN_BITWISE_XOR
;
727 type
= TokenType
.TILDE
;
731 type
= TokenType
.ASSIGN
;
734 switch (current
[0]) {
736 type
= TokenType
.OP_EQ
;
740 type
= TokenType
.LAMBDA
;
747 type
= TokenType
.OP_LT
;
750 switch (current
[0]) {
752 type
= TokenType
.OP_LE
;
756 type
= TokenType
.OP_SHIFT_LEFT
;
758 if (current
< end
&& current
[0] == '=') {
759 type
= TokenType
.ASSIGN_SHIFT_LEFT
;
767 type
= TokenType
.OP_GT
;
769 if (current
< end
&& current
[0] == '=') {
770 type
= TokenType
.OP_GE
;
775 type
= TokenType
.OP_NEG
;
777 if (current
< end
&& current
[0] == '=') {
778 type
= TokenType
.OP_NE
;
783 type
= TokenType
.PLUS
;
786 switch (current
[0]) {
788 type
= TokenType
.ASSIGN_ADD
;
792 type
= TokenType
.OP_INC
;
799 type
= TokenType
.MINUS
;
802 switch (current
[0]) {
804 type
= TokenType
.ASSIGN_SUB
;
808 type
= TokenType
.OP_DEC
;
812 type
= TokenType
.OP_PTR
;
819 type
= TokenType
.STAR
;
821 if (current
< end
&& current
[0] == '=') {
822 type
= TokenType
.ASSIGN_MUL
;
827 type
= TokenType
.DIV
;
829 if (current
< end
&& current
[0] == '=') {
830 type
= TokenType
.ASSIGN_DIV
;
835 type
= TokenType
.PERCENT
;
837 if (current
< end
&& current
[0] == '=') {
838 type
= TokenType
.ASSIGN_PERCENT
;
844 if (begin
[0] == '\'') {
845 type
= TokenType
.CHARACTER_LITERAL
;
846 } else if (current
< end
- 6 && begin
[1] == '"' && begin
[2] == '"') {
847 type
= TokenType
.VERBATIM_STRING_LITERAL
;
848 token_length_in_chars
= 6;
850 while (current
< end
- 4) {
851 if (current
[0] == '"' && current
[1] == '"' && current
[2] == '"' && current
[3] != '"') {
853 } else if (current
[0] == '\n') {
857 token_length_in_chars
= 3;
859 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
860 if (u
!= (unichar
) (-1)) {
861 current
+= u
.to_utf8 (null);
862 token_length_in_chars
++;
864 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "invalid UTF-8 character");
868 if (current
[0] == '"' && current
[1] == '"' && current
[2] == '"') {
871 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "syntax error, expected \"\"\"");
875 type
= TokenType
.STRING_LITERAL
;
877 token_length_in_chars
= 2;
879 while (current
< end
&& current
[0] != begin
[0]) {
880 if (current
[0] == '\\') {
882 token_length_in_chars
++;
883 if (current
>= end
) {
887 switch (current
[0]) {
898 token_length_in_chars
++;
901 // hexadecimal escape character
903 token_length_in_chars
++;
904 while (current
< end
&& current
[0].isxdigit ()) {
906 token_length_in_chars
++;
910 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "invalid escape sequence");
913 } else if (current
[0] == '\n') {
916 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
917 if (u
!= (unichar
) (-1)) {
918 current
+= u
.to_utf8 (null);
919 token_length_in_chars
++;
922 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "invalid UTF-8 character");
926 if (current
< end
&& current
[0] != '\n') {
929 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "syntax error, expected %c".printf (begin
[0]));
933 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
934 if (u
!= (unichar
) (-1)) {
935 current
+= u
.to_utf8 (null);
936 Report
.error (new
SourceReference (source_file
, line
, column
, line
, column
), "syntax error, unexpected character");
939 Report
.error (new
SourceReference (source_file
, line
, column
, line
, column
), "invalid UTF-8 character");
942 return read_token (out token_begin
, out token_end
);
946 if (token_length_in_chars
< 0) {
947 column
+= (int) (current
- begin
);
949 column
+= token_length_in_chars
;
952 token_end
.pos
= current
;
953 token_end
.line
= line
;
954 token_end
.column
= column
- 1;
959 static bool matches (char* begin
, string keyword
) {
960 char* keyword_array
= (char*) keyword
;
961 long len
= keyword
.len ();
962 for (int i
= 0; i
< len
; i
++) {
963 if (begin
[i
] != keyword_array
[i
]) {
970 bool pp_whitespace () {
972 while (current
< end
&& current
[0].isspace () && current
[0] != '\n') {
980 void pp_directive () {
987 char* begin
= current
;
989 while (current
< end
&& current
[0].isalnum ()) {
995 if (len
== 2 && matches (begin
, "if")) {
997 } else if (len
== 4 && matches (begin
, "elif")) {
999 } else if (len
== 4 && matches (begin
, "else")) {
1001 } else if (len
== 5 && matches (begin
, "endif")) {
1004 Report
.error (new
SourceReference (source_file
, line
, column
- len
, line
, column
), "syntax error, invalid preprocessing directive");
1007 if (conditional_stack
.length
> 0
1008 && conditional_stack
[conditional_stack
.length
- 1].skip_section
) {
1009 // skip lines until next preprocessing directive
1011 while (current
< end
) {
1012 if (bol
&& current
[0] == '#') {
1013 // go back to begin of line
1014 current
-= (column
- 1);
1018 if (current
[0] == '\n') {
1022 } else if (!current
[0].isspace ()) {
1033 if (current
>= end
|| current
[0] != '\n') {
1034 Report
.error (new
SourceReference (source_file
, line
, column
, line
, column
), "syntax error, expected newline");
1038 void parse_pp_if () {
1041 bool condition
= parse_pp_expression ();
1045 conditional_stack
+= Conditional ();
1047 if (condition
&& (conditional_stack
.length
== 1 || !conditional_stack
[conditional_stack
.length
- 2].skip_section
)) {
1048 // condition true => process code within if
1049 conditional_stack
[conditional_stack
.length
- 1].matched
= true;
1051 // skip lines until next preprocessing directive
1052 conditional_stack
[conditional_stack
.length
- 1].skip_section
= true;
1056 void parse_pp_elif () {
1059 bool condition
= parse_pp_expression ();
1063 if (conditional_stack
.length
== 0 || conditional_stack
[conditional_stack
.length
- 1].else_found
) {
1064 Report
.error (new
SourceReference (source_file
, line
, column
, line
, column
), "syntax error, unexpected #elif");
1068 if (condition
&& !conditional_stack
[conditional_stack
.length
- 1].matched
1069 && (conditional_stack
.length
== 1 || !conditional_stack
[conditional_stack
.length
- 2].skip_section
)) {
1070 // condition true => process code within if
1071 conditional_stack
[conditional_stack
.length
- 1].matched
= true;
1072 conditional_stack
[conditional_stack
.length
- 1].skip_section
= false;
1074 // skip lines until next preprocessing directive
1075 conditional_stack
[conditional_stack
.length
- 1].skip_section
= true;
1079 void parse_pp_else () {
1082 if (conditional_stack
.length
== 0 || conditional_stack
[conditional_stack
.length
- 1].else_found
) {
1083 Report
.error (new
SourceReference (source_file
, line
, column
, line
, column
), "syntax error, unexpected #else");
1087 if (!conditional_stack
[conditional_stack
.length
- 1].matched
1088 && (conditional_stack
.length
== 1 || !conditional_stack
[conditional_stack
.length
- 2].skip_section
)) {
1089 // condition true => process code within if
1090 conditional_stack
[conditional_stack
.length
- 1].matched
= true;
1091 conditional_stack
[conditional_stack
.length
- 1].skip_section
= false;
1093 // skip lines until next preprocessing directive
1094 conditional_stack
[conditional_stack
.length
- 1].skip_section
= true;
1098 void parse_pp_endif () {
1101 if (conditional_stack
.length
== 0) {
1102 Report
.error (new
SourceReference (source_file
, line
, column
, line
, column
), "syntax error, unexpected #endif");
1106 conditional_stack
.length
--;
1109 bool parse_pp_symbol () {
1111 while (current
< end
&& is_ident_char (current
[0])) {
1118 Report
.error (new
SourceReference (source_file
, line
, column
, line
, column
), "syntax error, expected identifier");
1122 string identifier
= ((string) (current
- len
)).ndup (len
);
1124 if (identifier
== "true") {
1126 } else if (identifier
== "false") {
1129 defined
= source_file
.context
.is_defined (identifier
);
1135 bool parse_pp_primary_expression () {
1136 if (current
>= end
) {
1137 Report
.error (new
SourceReference (source_file
, line
, column
, line
, column
), "syntax error, expected identifier");
1138 } else if (is_ident_char (current
[0])) {
1139 return parse_pp_symbol ();
1140 } else if (current
[0] == '(') {
1144 bool result
= parse_pp_expression ();
1146 if (current
< end
&& current
[0] == ')') {
1150 Report
.error (new
SourceReference (source_file
, line
, column
, line
, column
), "syntax error, expected `)'");
1154 Report
.error (new
SourceReference (source_file
, line
, column
, line
, column
), "syntax error, expected identifier");
1159 bool parse_pp_unary_expression () {
1160 if (current
< end
&& current
[0] == '!') {
1164 return !parse_pp_unary_expression ();
1167 return parse_pp_primary_expression ();
1170 bool parse_pp_equality_expression () {
1171 bool left
= parse_pp_unary_expression ();
1174 if (current
< end
- 1 && current
[0] == '=' && current
[1] == '=') {
1178 bool right
= parse_pp_unary_expression ();
1179 left
= (left
== right
);
1180 } else if (current
< end
- 1 && current
[0] == '!' && current
[1] == '=') {
1184 bool right
= parse_pp_unary_expression ();
1185 left
= (left
!= right
);
1193 bool parse_pp_and_expression () {
1194 bool left
= parse_pp_equality_expression ();
1196 while (current
< end
- 1 && current
[0] == '&' && current
[1] == '&') {
1200 bool right
= parse_pp_equality_expression ();
1201 left
= left
&& right
;
1206 bool parse_pp_or_expression () {
1207 bool left
= parse_pp_and_expression ();
1209 while (current
< end
- 1 && current
[0] == '|' && current
[1] == '|') {
1213 bool right
= parse_pp_and_expression ();
1214 left
= left
|| right
;
1219 bool parse_pp_expression () {
1220 return parse_pp_or_expression ();
1223 bool whitespace () {
1225 bool bol
= (column
== 1);
1226 while (current
< end
&& current
[0].isspace ()) {
1227 if (current
[0] == '\n') {
1236 if (bol
&& current
< end
&& current
[0] == '#') {
1243 bool comment (bool file_comment
= false) {
1244 if (current
> end
- 2
1245 || current
[0] != '/'
1246 || (current
[1] != '/' && current
[1] != '*')) {
1250 if (current
[1] == '/') {
1251 SourceReference source_reference
= null;
1253 source_reference
= new
SourceReference (source_file
, line
, column
, line
, column
);
1256 // single-line comment
1258 char* begin
= current
;
1260 // skip until end of line or end of file
1261 while (current
< end
&& current
[0] != '\n') {
1265 if (source_reference
!= null) {
1266 push_comment (((string) begin
).ndup ((long) (current
- begin
)), source_reference
, file_comment
);
1269 SourceReference source_reference
= null;
1271 if (file_comment
&& current
[2] == '*') {
1275 if (current
[2] == '*' || file_comment
) {
1276 source_reference
= new
SourceReference (source_file
, line
, column
, line
, column
);
1281 char* begin
= current
;
1282 while (current
< end
- 1
1283 && (current
[0] != '*' || current
[1] != '/')) {
1284 if (current
[0] == '\n') {
1292 if (current
== end
- 1) {
1293 Report
.error (new
SourceReference (source_file
, line
, column
, line
, column
), "syntax error, expected */");
1297 if (source_reference
!= null) {
1298 push_comment (((string) begin
).ndup ((long) (current
- begin
)), source_reference
, file_comment
);
1309 while (whitespace () || comment ()) {
1313 public void parse_file_comments () {
1314 while (whitespace () || comment (true)) {
1318 void push_comment (string comment_item
, SourceReference source_reference
, bool file_comment
) {
1319 if (comment_item
[0] == '*') {
1320 _comment
= new
Comment (comment_item
, source_reference
);
1324 source_file
.add_comment (new
Comment (comment_item
, source_reference
));
1330 * Clears and returns the content of the comment stack.
1332 * @return saved comment
1334 public Comment?
pop_comment () {
1335 if (_comment
== null) {
1339 var comment
= _comment
;