3 * Copyright (C) 2008-2010 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 public TokenType
read_regex_token (out SourceLocation token_begin
, out SourceLocation token_end
) {
99 char* begin
= current
;
100 token_begin
.pos
= begin
;
101 token_begin
.line
= line
;
102 token_begin
.column
= column
;
104 int token_length_in_chars
= -1;
106 if (current
>= end
) {
107 type
= TokenType
.EOF
;
109 switch (current
[0]) {
111 type
= TokenType
.CLOSE_REGEX_LITERAL
;
113 state_stack
.length
--;
118 while (current
[0] == 'i' || current
[0] == 's' || current
[0] == 'm' || current
[0] == 'x') {
119 switch (current
[0]) {
122 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "modifier 'i' used more than once");
128 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "modifier 's' used more than once");
134 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "modifier 'm' used more than once");
140 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "modifier 'x' used more than once");
146 token_length_in_chars
++;
150 type
= TokenType
.REGEX_LITERAL
;
151 token_length_in_chars
= 0;
152 while (current
< end
&& current
[0] != '/') {
153 if (current
[0] == '\\') {
155 token_length_in_chars
++;
156 if (current
>= end
) {
160 switch (current
[0]) {
211 token_length_in_chars
++;
214 // hexadecimal escape character
216 token_length_in_chars
++;
217 while (current
< end
&& current
[0].isxdigit ()) {
219 token_length_in_chars
++;
223 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "invalid escape sequence");
226 } else if (current
[0] == '\n') {
229 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
230 if (u
!= (unichar
) (-1)) {
231 current
+= u
.to_utf8 (null);
232 token_length_in_chars
++;
235 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "invalid UTF-8 character");
239 if (current
>= end
|| current
[0] == '\n') {
240 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "syntax error, expected \"");
241 state_stack
.length
--;
242 return read_token (out token_begin
, out token_end
);
248 if (token_length_in_chars
< 0) {
249 column
+= (int) (current
- begin
);
251 column
+= token_length_in_chars
;
254 token_end
.pos
= current
;
255 token_end
.line
= line
;
256 token_end
.column
= column
- 1;
261 public static TokenType
get_identifier_or_keyword (char* begin
, int len
) {
266 if (matches (begin
, "as")) return TokenType
.AS
;
269 if (matches (begin
, "do")) return TokenType
.DO
;
286 if (matches (begin
, "for")) return TokenType
.FOR
;
289 if (matches (begin
, "get")) return TokenType
.GET
;
292 if (matches (begin
, "new")) return TokenType
.NEW
;
295 if (matches (begin
, "out")) return TokenType
.OUT
;
298 if (matches (begin
, "ref")) return TokenType
.REF
;
301 if (matches (begin
, "set")) return TokenType
.SET
;
304 if (matches (begin
, "try")) return TokenType
.TRY
;
307 if (matches (begin
, "var")) return TokenType
.VAR
;
314 if (matches (begin
, "base")) return TokenType
.BASE
;
317 if (matches (begin
, "case")) return TokenType
.CASE
;
322 if (matches (begin
, "else")) return TokenType
.ELSE
;
325 if (matches (begin
, "enum")) return TokenType
.ENUM
;
330 if (matches (begin
, "lock")) return TokenType
.LOCK
;
333 if (matches (begin
, "null")) return TokenType
.NULL
;
338 if (matches (begin
, "this")) return TokenType
.THIS
;
341 if (matches (begin
, "true")) return TokenType
.TRUE
;
346 if (matches (begin
, "void")) return TokenType
.VOID
;
349 if (matches (begin
, "weak")) return TokenType
.WEAK
;
356 if (matches (begin
, "async")) return TokenType
.ASYNC
;
359 if (matches (begin
, "break")) return TokenType
.BREAK
;
364 if (matches (begin
, "catch")) return TokenType
.CATCH
;
367 if (matches (begin
, "class")) return TokenType
.CLASS
;
370 if (matches (begin
, "const")) return TokenType
.CONST
;
375 if (matches (begin
, "false")) return TokenType
.FALSE
;
378 if (matches (begin
, "owned")) return TokenType
.OWNED
;
381 if (matches (begin
, "throw")) return TokenType
.THROW
;
384 if (matches (begin
, "using")) return TokenType
.USING
;
387 if (matches (begin
, "while")) return TokenType
.WHILE
;
390 if (matches (begin
, "yield")) return TokenType
.YIELD
;
397 if (matches (begin
, "delete")) return TokenType
.DELETE
;
400 if (matches (begin
, "extern")) return TokenType
.EXTERN
;
403 if (matches (begin
, "inline")) return TokenType
.INLINE
;
408 if (matches (begin
, "params")) return TokenType
.PARAMS
;
411 if (matches (begin
, "public")) return TokenType
.PUBLIC
;
416 if (matches (begin
, "return")) return TokenType
.RETURN
;
421 if (matches (begin
, "sealed")) return TokenType
.SEALED
;
426 if (matches (begin
, "signal")) return TokenType
.SIGNAL
;
429 if (matches (begin
, "sizeof")) return TokenType
.SIZEOF
;
436 if (matches (begin
, "static")) return TokenType
.STATIC
;
439 if (matches (begin
, "struct")) return TokenType
.STRUCT
;
444 if (matches (begin
, "switch")) return TokenType
.SWITCH
;
451 if (matches (begin
, "throws")) return TokenType
.THROWS
;
454 if (matches (begin
, "typeof")) return TokenType
.TYPEOF
;
465 if (matches (begin
, "default")) return TokenType
.DEFAULT
;
468 if (matches (begin
, "dynamic")) return TokenType
.DYNAMIC
;
473 if (matches (begin
, "ensures")) return TokenType
.ENSURES
;
478 if (matches (begin
, "finally")) return TokenType
.FINALLY
;
481 if (matches (begin
, "foreach")) return TokenType
.FOREACH
;
486 if (matches (begin
, "private")) return TokenType
.PRIVATE
;
489 if (matches (begin
, "unowned")) return TokenType
.UNOWNED
;
492 if (matches (begin
, "virtual")) return TokenType
.VIRTUAL
;
499 if (matches (begin
, "abstract")) return TokenType
.ABSTRACT
;
502 if (matches (begin
, "continue")) return TokenType
.CONTINUE
;
505 if (matches (begin
, "delegate")) return TokenType
.DELEGATE
;
508 if (matches (begin
, "internal")) return TokenType
.INTERNAL
;
511 if (matches (begin
, "override")) return TokenType
.OVERRIDE
;
514 if (matches (begin
, "requires")) return TokenType
.REQUIRES
;
517 if (matches (begin
, "volatile")) return TokenType
.VOLATILE
;
524 if (matches (begin
, "construct")) return TokenType
.CONSTRUCT
;
527 if (matches (begin
, "interface")) return TokenType
.INTERFACE
;
530 if (matches (begin
, "namespace")) return TokenType
.NAMESPACE
;
533 if (matches (begin
, "protected")) return TokenType
.PROTECTED
;
538 if (matches (begin
, "errordomain")) return TokenType
.ERRORDOMAIN
;
541 return TokenType
.IDENTIFIER
;
544 TokenType
read_number () {
545 var type
= TokenType
.INTEGER_LITERAL
;
548 if (current
< end
- 2 && current
[0] == '0'
549 && current
[1] == 'x' && current
[2].isxdigit ()) {
550 // hexadecimal integer literal
552 while (current
< end
&& current
[0].isxdigit ()) {
557 while (current
< end
&& current
[0].isdigit ()) {
563 if (current
< end
- 1 && current
[0] == '.' && current
[1].isdigit ()) {
564 type
= TokenType
.REAL_LITERAL
;
566 while (current
< end
&& current
[0].isdigit ()) {
572 if (current
< end
&& current
[0].tolower () == 'e') {
573 type
= TokenType
.REAL_LITERAL
;
575 if (current
< end
&& (current
[0] == '+' || current
[0] == '-')) {
578 while (current
< end
&& current
[0].isdigit ()) {
585 bool real_literal
= (type
== TokenType
.REAL_LITERAL
);
587 switch (current
[0]) {
590 if (type
== TokenType
.INTEGER_LITERAL
) {
592 if (current
< end
&& current
[0].tolower () == 'l') {
599 if (type
== TokenType
.INTEGER_LITERAL
) {
601 if (current
< end
&& current
[0].tolower () == 'l') {
603 if (current
< end
&& current
[0].tolower () == 'l') {
613 type
= TokenType
.REAL_LITERAL
;
618 if (!real_literal
&& is_ident_char (current
[0])) {
619 // allow identifiers to start with a digit
620 // as long as they contain at least one char
621 while (current
< end
&& is_ident_char (current
[0])) {
624 type
= TokenType
.IDENTIFIER
;
631 public TokenType
read_template_token (out SourceLocation token_begin
, out SourceLocation token_end
) {
633 char* begin
= current
;
634 token_begin
.pos
= begin
;
635 token_begin
.line
= line
;
636 token_begin
.column
= column
;
638 int token_length_in_chars
= -1;
640 if (current
>= end
) {
641 type
= TokenType
.EOF
;
643 switch (current
[0]) {
645 type
= TokenType
.CLOSE_TEMPLATE
;
647 state_stack
.length
--;
650 token_begin
.pos
++; // $ is not part of following token
652 if (current
[0].isalpha () || current
[0] == '_') {
654 while (current
< end
&& is_ident_char (current
[0])) {
658 type
= TokenType
.IDENTIFIER
;
659 state_stack
+= State
.TEMPLATE_PART
;
660 } else if (current
[0] == '(') {
663 state_stack
+= State
.PARENS
;
664 return read_token (out token_begin
, out token_end
);
665 } else if (current
[0] == '$') {
666 type
= TokenType
.TEMPLATE_STRING_LITERAL
;
668 state_stack
+= State
.TEMPLATE_PART
;
670 Report
.error (new
SourceReference (source_file
, line
, column
+ 1, line
, column
+ 1), "unexpected character");
671 return read_template_token (out token_begin
, out token_end
);
675 type
= TokenType
.TEMPLATE_STRING_LITERAL
;
676 token_length_in_chars
= 0;
677 while (current
< end
&& current
[0] != '"' && current
[0] != '$') {
678 if (current
[0] == '\\') {
680 token_length_in_chars
++;
681 if (current
>= end
) {
685 switch (current
[0]) {
696 token_length_in_chars
++;
699 // hexadecimal escape character
701 token_length_in_chars
++;
702 while (current
< end
&& current
[0].isxdigit ()) {
704 token_length_in_chars
++;
708 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "invalid escape sequence");
711 } else if (current
[0] == '\n') {
715 token_length_in_chars
= 1;
717 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
718 if (u
!= (unichar
) (-1)) {
719 current
+= u
.to_utf8 (null);
720 token_length_in_chars
++;
723 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "invalid UTF-8 character");
727 if (current
>= end
) {
728 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "syntax error, expected \"");
729 state_stack
.length
--;
730 return read_token (out token_begin
, out token_end
);
732 state_stack
+= State
.TEMPLATE_PART
;
737 if (token_length_in_chars
< 0) {
738 column
+= (int) (current
- begin
);
740 column
+= token_length_in_chars
;
743 token_end
.pos
= current
;
744 token_end
.line
= line
;
745 token_end
.column
= column
- 1;
750 public TokenType
read_token (out SourceLocation token_begin
, out SourceLocation token_end
) {
751 if (in_template ()) {
752 return read_template_token (out token_begin
, out token_end
);
753 } else if (in_template_part ()) {
754 state_stack
.length
--;
756 token_begin
.pos
= current
;
757 token_begin
.line
= line
;
758 token_begin
.column
= column
;
760 token_end
.pos
= current
;
761 token_end
.line
= line
;
762 token_end
.column
= column
- 1;
764 return TokenType
.COMMA
;
765 } else if (in_regex_literal ()) {
766 return read_regex_token (out token_begin
, out token_end
);
772 char* begin
= current
;
773 token_begin
.pos
= begin
;
774 token_begin
.line
= line
;
775 token_begin
.column
= column
;
777 int token_length_in_chars
= -1;
779 if (current
>= end
) {
780 type
= TokenType
.EOF
;
781 } else if (current
[0].isalpha () || current
[0] == '_') {
783 while (current
< end
&& is_ident_char (current
[0])) {
787 type
= get_identifier_or_keyword (begin
, len
);
788 } else if (current
[0] == '@' && source_file
.context
.profile
!= Profile
.DOVA
) {
789 if (current
< end
- 1 && current
[1] == '"') {
790 type
= TokenType
.OPEN_TEMPLATE
;
792 state_stack
+= State
.TEMPLATE
;
794 token_begin
.pos
++; // @ is not part of the identifier
797 while (current
< end
&& is_ident_char (current
[0])) {
801 type
= TokenType
.IDENTIFIER
;
803 } else if (current
[0].isdigit ()) {
804 type
= read_number ();
806 switch (current
[0]) {
808 type
= TokenType
.OPEN_BRACE
;
810 state_stack
+= State
.BRACE
;
813 type
= TokenType
.CLOSE_BRACE
;
815 if (state_stack
.length
> 0) {
816 state_stack
.length
--;
820 type
= TokenType
.OPEN_PARENS
;
822 state_stack
+= State
.PARENS
;
825 type
= TokenType
.CLOSE_PARENS
;
827 if (state_stack
.length
> 0) {
828 state_stack
.length
--;
830 if (in_template ()) {
831 type
= TokenType
.COMMA
;
835 type
= TokenType
.OPEN_BRACKET
;
837 state_stack
+= State
.BRACKET
;
840 type
= TokenType
.CLOSE_BRACKET
;
842 if (state_stack
.length
> 0) {
843 state_stack
.length
--;
847 type
= TokenType
.DOT
;
849 if (current
< end
- 1) {
850 if (current
[0] == '.' && current
[1] == '.') {
851 type
= TokenType
.ELLIPSIS
;
857 type
= TokenType
.COLON
;
859 if (current
< end
&& current
[0] == ':') {
860 type
= TokenType
.DOUBLE_COLON
;
865 type
= TokenType
.COMMA
;
869 type
= TokenType
.SEMICOLON
;
873 type
= TokenType
.HASH
;
877 type
= TokenType
.INTERR
;
879 if (current
< end
&& current
[0] == '?') {
880 type
= TokenType
.OP_COALESCING
;
885 type
= TokenType
.BITWISE_OR
;
888 switch (current
[0]) {
890 type
= TokenType
.ASSIGN_BITWISE_OR
;
894 type
= TokenType
.OP_OR
;
901 type
= TokenType
.BITWISE_AND
;
904 switch (current
[0]) {
906 type
= TokenType
.ASSIGN_BITWISE_AND
;
910 type
= TokenType
.OP_AND
;
917 type
= TokenType
.CARRET
;
919 if (current
< end
&& current
[0] == '=') {
920 type
= TokenType
.ASSIGN_BITWISE_XOR
;
925 type
= TokenType
.TILDE
;
929 type
= TokenType
.ASSIGN
;
932 switch (current
[0]) {
934 type
= TokenType
.OP_EQ
;
938 type
= TokenType
.LAMBDA
;
945 type
= TokenType
.OP_LT
;
948 switch (current
[0]) {
950 type
= TokenType
.OP_LE
;
954 type
= TokenType
.OP_SHIFT_LEFT
;
956 if (current
< end
&& current
[0] == '=') {
957 type
= TokenType
.ASSIGN_SHIFT_LEFT
;
965 type
= TokenType
.OP_GT
;
967 if (current
< end
&& current
[0] == '=') {
968 type
= TokenType
.OP_GE
;
973 type
= TokenType
.OP_NEG
;
975 if (current
< end
&& current
[0] == '=') {
976 type
= TokenType
.OP_NE
;
981 type
= TokenType
.PLUS
;
984 switch (current
[0]) {
986 type
= TokenType
.ASSIGN_ADD
;
990 type
= TokenType
.OP_INC
;
997 type
= TokenType
.MINUS
;
1000 switch (current
[0]) {
1002 type
= TokenType
.ASSIGN_SUB
;
1006 type
= TokenType
.OP_DEC
;
1010 type
= TokenType
.OP_PTR
;
1017 type
= TokenType
.STAR
;
1019 if (current
< end
&& current
[0] == '=') {
1020 type
= TokenType
.ASSIGN_MUL
;
1026 case TokenType
.ASSIGN
:
1027 case TokenType
.COMMA
:
1028 case TokenType
.MINUS
:
1029 case TokenType
.OP_AND
:
1030 case TokenType
.OP_COALESCING
:
1031 case TokenType
.OP_EQ
:
1032 case TokenType
.OP_GE
:
1033 case TokenType
.OP_GT
:
1034 case TokenType
.OP_LE
:
1035 case TokenType
.OP_LT
:
1036 case TokenType
.OP_NE
:
1037 case TokenType
.OP_NEG
:
1038 case TokenType
.OP_OR
:
1039 case TokenType
.OPEN_BRACE
:
1040 case TokenType
.OPEN_PARENS
:
1041 case TokenType
.PLUS
:
1042 case TokenType
.RETURN
:
1043 type
= TokenType
.OPEN_REGEX_LITERAL
;
1044 state_stack
+= State
.REGEX_LITERAL
;
1048 type
= TokenType
.DIV
;
1050 if (current
< end
&& current
[0] == '=') {
1051 type
= TokenType
.ASSIGN_DIV
;
1058 type
= TokenType
.PERCENT
;
1060 if (current
< end
&& current
[0] == '=') {
1061 type
= TokenType
.ASSIGN_PERCENT
;
1067 if (begin
[0] == '\'') {
1068 type
= TokenType
.CHARACTER_LITERAL
;
1069 } else if (current
< end
- 6 && begin
[1] == '"' && begin
[2] == '"') {
1070 type
= TokenType
.VERBATIM_STRING_LITERAL
;
1071 token_length_in_chars
= 6;
1073 while (current
< end
- 4) {
1074 if (current
[0] == '"' && current
[1] == '"' && current
[2] == '"' && current
[3] != '"') {
1076 } else if (current
[0] == '\n') {
1080 token_length_in_chars
= 3;
1082 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
1083 if (u
!= (unichar
) (-1)) {
1084 current
+= u
.to_utf8 (null);
1085 token_length_in_chars
++;
1087 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "invalid UTF-8 character");
1091 if (current
[0] == '"' && current
[1] == '"' && current
[2] == '"') {
1094 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "syntax error, expected \"\"\"");
1098 type
= TokenType
.STRING_LITERAL
;
1100 token_length_in_chars
= 2;
1102 while (current
< end
&& current
[0] != begin
[0]) {
1103 if (current
[0] == '\\') {
1105 token_length_in_chars
++;
1106 if (current
>= end
) {
1110 switch (current
[0]) {
1122 token_length_in_chars
++;
1125 // hexadecimal escape character
1127 token_length_in_chars
++;
1128 while (current
< end
&& current
[0].isxdigit ()) {
1130 token_length_in_chars
++;
1134 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "invalid escape sequence");
1137 } else if (current
[0] == '\n') {
1141 token_length_in_chars
= 1;
1143 if (type
== TokenType
.STRING_LITERAL
&& source_file
.context
.profile
== Profile
.DOVA
&& current
[0] == '$') {
1145 type
= TokenType
.OPEN_TEMPLATE
;
1147 state_stack
+= State
.TEMPLATE
;
1150 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
1151 if (u
!= (unichar
) (-1)) {
1152 current
+= u
.to_utf8 (null);
1153 token_length_in_chars
++;
1156 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "invalid UTF-8 character");
1159 if (current
< end
&& begin
[0] == '\'' && current
[0] != '\'') {
1160 // multiple characters in single character literal
1161 Report
.error (new
SourceReference (source_file
, line
, column
+ token_length_in_chars
, line
, column
+ token_length_in_chars
), "invalid character literal");
1164 if (current
< end
) {
1167 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]));
1171 unichar u
= ((string) current
).get_char_validated ((long) (end
- current
));
1172 if (u
!= (unichar
) (-1)) {
1173 current
+= u
.to_utf8 (null);
1174 Report
.error (new
SourceReference (source_file
, line
, column
, line
, column
), "syntax error, unexpected character");
1177 Report
.error (new
SourceReference (source_file
, line
, column
, line
, column
), "invalid UTF-8 character");
1180 return read_token (out token_begin
, out token_end
);
1184 if (token_length_in_chars
< 0) {
1185 column
+= (int) (current
- begin
);
1187 column
+= token_length_in_chars
;
1190 token_end
.pos
= current
;
1191 token_end
.line
= line
;
1192 token_end
.column
= column
- 1;
1198 static bool matches (char* begin
, string keyword
) {
1199 char* keyword_array
= (char*) keyword
;
1200 long len
= keyword
.length
;
1201 for (int i
= 0; i
< len
; i
++) {
1202 if (begin
[i
] != keyword_array
[i
]) {
1209 bool pp_whitespace () {
1211 while (current
< end
&& current
[0].isspace () && current
[0] != '\n') {
1219 void pp_directive () {
1224 if (line
== 1 && column
== 2 && current
< end
&& current
[0] == '!') {
1226 // skip until end of line or end of file
1227 while (current
< end
&& current
[0] != '\n') {
1235 char* begin
= current
;
1237 while (current
< end
&& current
[0].isalnum ()) {
1243 if (len
== 2 && matches (begin
, "if")) {
1245 } else if (len
== 4 && matches (begin
, "elif")) {
1247 } else if (len
== 4 && matches (begin
, "else")) {
1249 } else if (len
== 5 && matches (begin
, "endif")) {
1252 Report
.error (new
SourceReference (source_file
, line
, column
- len
, line
, column
), "syntax error, invalid preprocessing directive");
1255 if (conditional_stack
.length
> 0
1256 && conditional_stack
[conditional_stack
.length
- 1].skip_section
) {
1257 // skip lines until next preprocessing directive
1259 while (current
< end
) {
1260 if (bol
&& current
[0] == '#') {
1261 // go back to begin of line
1262 current
-= (column
- 1);
1266 if (current
[0] == '\n') {
1270 } else if (!current
[0].isspace ()) {
1281 if (current
>= end
|| current
[0] != '\n') {
1282 Report
.error (new
SourceReference (source_file
, line
, column
, line
, column
), "syntax error, expected newline");
1286 void parse_pp_if () {
1289 bool condition
= parse_pp_expression ();
1293 conditional_stack
+= Conditional ();
1295 if (condition
&& (conditional_stack
.length
== 1 || !conditional_stack
[conditional_stack
.length
- 2].skip_section
)) {
1296 // condition true => process code within if
1297 conditional_stack
[conditional_stack
.length
- 1].matched
= true;
1299 // skip lines until next preprocessing directive
1300 conditional_stack
[conditional_stack
.length
- 1].skip_section
= true;
1304 void parse_pp_elif () {
1307 bool condition
= parse_pp_expression ();
1311 if (conditional_stack
.length
== 0 || conditional_stack
[conditional_stack
.length
- 1].else_found
) {
1312 Report
.error (new
SourceReference (source_file
, line
, column
, line
, column
), "syntax error, unexpected #elif");
1316 if (condition
&& !conditional_stack
[conditional_stack
.length
- 1].matched
1317 && (conditional_stack
.length
== 1 || !conditional_stack
[conditional_stack
.length
- 2].skip_section
)) {
1318 // condition true => process code within if
1319 conditional_stack
[conditional_stack
.length
- 1].matched
= true;
1320 conditional_stack
[conditional_stack
.length
- 1].skip_section
= false;
1322 // skip lines until next preprocessing directive
1323 conditional_stack
[conditional_stack
.length
- 1].skip_section
= true;
1327 void parse_pp_else () {
1330 if (conditional_stack
.length
== 0 || conditional_stack
[conditional_stack
.length
- 1].else_found
) {
1331 Report
.error (new
SourceReference (source_file
, line
, column
, line
, column
), "syntax error, unexpected #else");
1335 if (!conditional_stack
[conditional_stack
.length
- 1].matched
1336 && (conditional_stack
.length
== 1 || !conditional_stack
[conditional_stack
.length
- 2].skip_section
)) {
1337 // condition true => process code within if
1338 conditional_stack
[conditional_stack
.length
- 1].matched
= true;
1339 conditional_stack
[conditional_stack
.length
- 1].skip_section
= false;
1341 // skip lines until next preprocessing directive
1342 conditional_stack
[conditional_stack
.length
- 1].skip_section
= true;
1346 void parse_pp_endif () {
1349 if (conditional_stack
.length
== 0) {
1350 Report
.error (new
SourceReference (source_file
, line
, column
, line
, column
), "syntax error, unexpected #endif");
1354 conditional_stack
.length
--;
1357 bool parse_pp_symbol () {
1359 while (current
< end
&& is_ident_char (current
[0])) {
1366 Report
.error (new
SourceReference (source_file
, line
, column
, line
, column
), "syntax error, expected identifier");
1370 string identifier
= ((string) (current
- len
)).substring (0, len
);
1372 if (identifier
== "true") {
1374 } else if (identifier
== "false") {
1377 defined
= source_file
.context
.is_defined (identifier
);
1383 bool parse_pp_primary_expression () {
1384 if (current
>= end
) {
1385 Report
.error (new
SourceReference (source_file
, line
, column
, line
, column
), "syntax error, expected identifier");
1386 } else if (is_ident_char (current
[0])) {
1387 return parse_pp_symbol ();
1388 } else if (current
[0] == '(') {
1392 bool result
= parse_pp_expression ();
1394 if (current
< end
&& current
[0] == ')') {
1398 Report
.error (new
SourceReference (source_file
, line
, column
, line
, column
), "syntax error, expected `)'");
1402 Report
.error (new
SourceReference (source_file
, line
, column
, line
, column
), "syntax error, expected identifier");
1407 bool parse_pp_unary_expression () {
1408 if (current
< end
&& current
[0] == '!') {
1412 return !parse_pp_unary_expression ();
1415 return parse_pp_primary_expression ();
1418 bool parse_pp_equality_expression () {
1419 bool left
= parse_pp_unary_expression ();
1422 if (current
< end
- 1 && current
[0] == '=' && current
[1] == '=') {
1426 bool right
= parse_pp_unary_expression ();
1427 left
= (left
== right
);
1428 } else if (current
< end
- 1 && current
[0] == '!' && current
[1] == '=') {
1432 bool right
= parse_pp_unary_expression ();
1433 left
= (left
!= right
);
1441 bool parse_pp_and_expression () {
1442 bool left
= parse_pp_equality_expression ();
1444 while (current
< end
- 1 && current
[0] == '&' && current
[1] == '&') {
1448 bool right
= parse_pp_equality_expression ();
1449 left
= left
&& right
;
1454 bool parse_pp_or_expression () {
1455 bool left
= parse_pp_and_expression ();
1457 while (current
< end
- 1 && current
[0] == '|' && current
[1] == '|') {
1461 bool right
= parse_pp_and_expression ();
1462 left
= left
|| right
;
1467 bool parse_pp_expression () {
1468 return parse_pp_or_expression ();
1471 bool whitespace () {
1473 bool bol
= (column
== 1);
1474 while (current
< end
&& current
[0].isspace ()) {
1475 if (current
[0] == '\n') {
1484 if (bol
&& current
< end
&& current
[0] == '#') {
1491 bool comment (bool file_comment
= false) {
1493 || current
> end
- 2
1494 || current
[0] != '/'
1495 || (current
[1] != '/' && current
[1] != '*')) {
1499 if (current
[1] == '/') {
1500 SourceReference source_reference
= null;
1502 source_reference
= new
SourceReference (source_file
, line
, column
, line
, column
);
1505 // single-line comment
1507 char* begin
= current
;
1509 // skip until end of line or end of file
1510 while (current
< end
&& current
[0] != '\n') {
1514 if (source_reference
!= null) {
1515 push_comment (((string) begin
).substring (0, (long) (current
- begin
)), source_reference
, file_comment
);
1518 SourceReference source_reference
= null;
1520 if (file_comment
&& current
[2] == '*') {
1524 if (current
[2] == '*' || file_comment
) {
1525 source_reference
= new
SourceReference (source_file
, line
, column
, line
, column
);
1530 char* begin
= current
;
1531 while (current
< end
- 1
1532 && (current
[0] != '*' || current
[1] != '/')) {
1533 if (current
[0] == '\n') {
1541 if (current
== end
- 1) {
1542 Report
.error (new
SourceReference (source_file
, line
, column
, line
, column
), "syntax error, expected */");
1546 if (source_reference
!= null) {
1547 push_comment (((string) begin
).substring (0, (long) (current
- begin
)), source_reference
, file_comment
);
1558 while (whitespace () || comment ()) {
1562 public void parse_file_comments () {
1563 while (whitespace () || comment (true)) {
1567 void push_comment (string comment_item
, SourceReference source_reference
, bool file_comment
) {
1568 if (comment_item
[0] == '*') {
1569 if (_comment
!= null) {
1570 // extra doc comment, add it to source file comments
1571 source_file
.add_comment (_comment
);
1573 _comment
= new
Comment (comment_item
, source_reference
);
1577 source_file
.add_comment (new
Comment (comment_item
, source_reference
));
1583 * Clears and returns the content of the comment stack.
1585 * @return saved comment
1587 public Comment?
pop_comment () {
1588 if (_comment
== null) {
1592 var comment
= _comment
;