1 /*-----------------------------------------------------------------------
2 SDCC.lex - lexical analyser for use with sdcc ( a freeware compiler for
3 8/16 bit microcontrollers)
4 Written by : Sandeep Dutta . sandeep.dutta@usa.net (1997)
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 In other words, you are welcome to use, share and improve this program.
21 You are forbidden to forbid anyone else to use, share and improve
22 what you give them. Help stamp out software-hoarding!
23 -------------------------------------------------------------------------*/
40 #define TKEYWORD(token) return (isTargetKeyword(yytext) ? token :\
43 #define TKEYWORDSDCC(token) return (options.std_sdcc && isTargetKeyword(yytext)\
44 ? token : check_type())
46 #define TKEYWORD99(token) return (options.std_c99 ? token : check_type())
48 extern int lineno, column;
49 extern char *filename;
51 /* global definitions */
55 /* local definitions */
56 static struct dbuf_s asmbuff;
58 /* forward declarations */
59 static char *stringLiteral(void);
60 static void count(void);
61 static int process_pragma(char *);
62 static int check_type(void);
63 static int isTargetKeyword(char *s);
64 static int checkCurrFile(char *s);
71 if (!options.std_sdcc && yytext[1] != '_')
73 assert(asmbuff.alloc == 0 && asmbuff.len == 0 && asmbuff.buf == NULL);
74 dbuf_init(&asmbuff, INITIAL_INLINEASM);
79 if (!options.std_sdcc && yytext[1] != '_')
81 dbuf_append(&asmbuff, yytext, strlen(yytext));
85 yylval.yyinline = dbuf_c_str(&asmbuff);
86 dbuf_detach(&asmbuff);
93 dbuf_append(&asmbuff, yytext, 1);
96 dbuf_append(&asmbuff, yytext, 1);
98 "at" { count(); TKEYWORDSDCC(AT); }
99 "__at" { count(); TKEYWORD(AT); }
100 "auto" { count(); return(AUTO); }
101 "bit" { count(); TKEYWORDSDCC(BIT); }
102 "__bit" { count(); TKEYWORD(BIT); }
103 "break" { count(); return(BREAK); }
104 "case" { count(); return(CASE); }
105 "char" { count(); return(CHAR); }
106 "code" { count(); TKEYWORDSDCC(CODE); }
107 "__code" { count(); TKEYWORD(CODE); }
108 "const" { count(); return(CONST); }
109 "continue" { count(); return(CONTINUE); }
110 "critical" { count(); TKEYWORDSDCC(CRITICAL); }
111 "__critical" { count(); TKEYWORD(CRITICAL); }
112 "data" { count(); TKEYWORDSDCC(DATA); }
113 "__data" { count(); TKEYWORD(DATA); }
114 "default" { count(); return(DEFAULT); }
115 "do" { count(); return(DO); }
116 "double" { count(); werror(W_DOUBLE_UNSUPPORTED);return(FLOAT); }
117 "else" { count(); return(ELSE); }
118 "enum" { count(); return(ENUM); }
119 "extern" { count(); return(EXTERN); }
120 "far" { count(); TKEYWORDSDCC(XDATA); }
121 "__far" { count(); TKEYWORD(XDATA); }
122 "eeprom" { count(); TKEYWORDSDCC(EEPROM); }
123 "__eeprom" { count(); TKEYWORD(EEPROM); }
124 "float" { count(); return(FLOAT); }
125 "flash" { count(); TKEYWORDSDCC(CODE); }
126 "__flash" { count(); TKEYWORD(CODE); }
127 "for" { count(); return(FOR); }
128 "goto" { count(); return(GOTO); }
129 "idata" { count(); TKEYWORDSDCC(IDATA); }
130 "__idata" { count(); TKEYWORD(IDATA); }
131 "if" { count(); return(IF); }
132 "int" { count(); return(INT); }
133 "interrupt" { count(); TKEYWORDSDCC(INTERRUPT); }
134 "__interrupt" { count(); TKEYWORD(INTERRUPT); }
135 "nonbanked" { count(); TKEYWORDSDCC(NONBANKED); }
136 "__nonbanked" { count(); TKEYWORD(NONBANKED); }
137 "banked" { count(); TKEYWORDSDCC(BANKED); }
138 "__banked" { count(); TKEYWORD(BANKED); }
139 "long" { count(); return(LONG); }
140 "near" { count(); TKEYWORDSDCC(DATA); }
141 "__near" { count(); TKEYWORD(DATA); }
142 "pdata" { count(); TKEYWORDSDCC(PDATA); }
143 "__pdata" { count(); TKEYWORD(PDATA); }
144 "reentrant" { count(); TKEYWORDSDCC(REENTRANT); }
145 "__reentrant" { count(); TKEYWORD(REENTRANT); }
146 "shadowregs" { count(); TKEYWORDSDCC(SHADOWREGS); }
147 "__shadowregs" { count(); TKEYWORD(SHADOWREGS); }
148 "wparam" { count(); TKEYWORDSDCC(WPARAM); }
149 "__wparam" { count(); TKEYWORD(WPARAM); }
150 "register" { count(); return(REGISTER); }
151 "return" { count(); return(RETURN); }
152 "sfr" { count(); TKEYWORDSDCC(SFR); }
153 "__sfr" { count(); TKEYWORD(SFR); }
154 "sfr16" { count(); TKEYWORDSDCC(SFR16); }
155 "__sfr16" { count(); TKEYWORD(SFR16); }
156 "sfr32" { count(); TKEYWORDSDCC(SFR32); }
157 "__sfr32" { count(); TKEYWORD(SFR32); }
158 "sbit" { count(); TKEYWORDSDCC(SBIT); }
159 "__sbit" { count(); TKEYWORD(SBIT); }
160 "short" { count(); return(SHORT); }
161 "signed" { count(); return(SIGNED); }
162 "sizeof" { count(); return(SIZEOF); }
163 "sram" { count(); TKEYWORDSDCC(XDATA); }
164 "__sram" { count(); TKEYWORD(XDATA); }
165 "static" { count(); return(STATIC); }
166 "struct" { count(); return(STRUCT); }
167 "switch" { count(); return(SWITCH); }
168 "typedef" { count(); return(TYPEDEF); }
169 "union" { count(); return(UNION); }
170 "unsigned" { count(); return(UNSIGNED); }
171 "void" { count(); return(VOID); }
172 "volatile" { count(); return(VOLATILE); }
173 "using" { count(); TKEYWORDSDCC(USING); }
174 "__using" { count(); TKEYWORD(USING); }
175 "_naked" { count(); TKEYWORDSDCC(NAKED); }
176 "__naked" { count(); TKEYWORD(NAKED); }
177 "while" { count(); return(WHILE); }
178 "xdata" { count(); TKEYWORDSDCC(XDATA); }
179 "__xdata" { count(); TKEYWORD(XDATA); }
180 "..." { count(); return(VAR_ARGS); }
181 "__typeof" { count(); return TYPEOF; }
182 "_JavaNative" { count(); TKEYWORD(JAVANATIVE); }
183 "_overlay" { count(); TKEYWORDSDCC(OVERLAY); }
184 "__overlay" { count(); TKEYWORD(OVERLAY); }
185 "inline" { count(); TKEYWORD99(INLINE); }
186 "restrict" { count(); TKEYWORD99(RESTRICT); }
187 {L}({L}|{D})* { count(); return(check_type()); }
188 0[xX]{H}+{IS}? { count(); yylval.val = constVal(yytext); return(CONSTANT); }
189 0[0-7]*{IS}? { count(); yylval.val = constVal(yytext); return(CONSTANT); }
190 [1-9]{D}*{IS}? { count(); yylval.val = constVal(yytext); return(CONSTANT); }
191 '(\\.|[^\\'])+' { count();yylval.val = charVal (yytext); return(CONSTANT); /* ' make syntax highliter happy */ }
192 {D}+{E}{FS}? { count(); yylval.val = constFloatVal(yytext);return(CONSTANT); }
193 {D}*"."{D}+({E})?{FS}? { count(); yylval.val = constFloatVal(yytext);return(CONSTANT); }
194 {D}+"."{D}*({E})?{FS}? { count(); yylval.val = constFloatVal(yytext);return(CONSTANT); }
195 \" { count(); yylval.val=strVal(stringLiteral()); return(STRING_LITERAL); }
196 ">>=" { count(); yylval.yyint = RIGHT_ASSIGN ; return(RIGHT_ASSIGN); }
197 "<<=" { count(); yylval.yyint = LEFT_ASSIGN ; return(LEFT_ASSIGN); }
198 "+=" { count(); yylval.yyint = ADD_ASSIGN ; return(ADD_ASSIGN); }
199 "-=" { count(); yylval.yyint = SUB_ASSIGN ; return(SUB_ASSIGN); }
200 "*=" { count(); yylval.yyint = MUL_ASSIGN ; return(MUL_ASSIGN); }
201 "/=" { count(); yylval.yyint = DIV_ASSIGN ; return(DIV_ASSIGN); }
202 "%=" { count(); yylval.yyint = MOD_ASSIGN ; return(MOD_ASSIGN); }
203 "&=" { count(); yylval.yyint = AND_ASSIGN ; return(AND_ASSIGN); }
204 "^=" { count(); yylval.yyint = XOR_ASSIGN ; return(XOR_ASSIGN); }
205 "|=" { count(); yylval.yyint = OR_ASSIGN ; return(OR_ASSIGN); }
206 ">>" { count(); return(RIGHT_OP); }
207 "<<" { count(); return(LEFT_OP); }
208 "++" { count(); return(INC_OP); }
209 "--" { count(); return(DEC_OP); }
210 "->" { count(); return(PTR_OP); }
211 "&&" { count(); return(AND_OP); }
212 "||" { count(); return(OR_OP); }
213 "<=" { count(); return(LE_OP); }
214 ">=" { count(); return(GE_OP); }
215 "==" { count(); return(EQ_OP); }
216 "!=" { count(); return(NE_OP); }
217 ";" { count(); return(';'); }
218 "{" { count(); NestLevel++ ; ignoreTypedefType = 0; return('{'); }
219 "}" { count(); NestLevel--; return('}'); }
220 "," { count(); return(','); }
221 ":" { count(); return(':'); }
222 "=" { count(); return('='); }
223 "(" { count(); ignoreTypedefType = 0; return('('); }
224 ")" { count(); return(')'); }
225 "[" { count(); return('['); }
226 "]" { count(); return(']'); }
227 "." { count(); return('.'); }
228 "&" { count(); return('&'); }
229 "!" { count(); return('!'); }
230 "~" { count(); return('~'); }
231 "-" { count(); return('-'); }
232 "+" { count(); return('+'); }
233 "*" { count(); return('*'); }
234 "/" { count(); return('/'); }
235 "%" { count(); return('%'); }
236 "<" { count(); return('<'); }
237 ">" { count(); return('>'); }
238 "^" { count(); return('^'); }
239 "|" { count(); return('|'); }
240 "?" { count(); return('?'); }
241 ^#pragma.*"\n" { count(); process_pragma(yytext); }
242 ^(#line.*"\n")|(#.*"\n") { count(); checkCurrFile(yytext); }
244 ^[^(]+"("[0-9]+") : error"[^\n]+ { werror(E_PRE_PROC_FAILED, yytext); count(); }
245 ^[^(]+"("[0-9]+") : warning"[^\n]+ { werror(W_PRE_PROC_WARNING, yytext); count(); }
248 [ \t\v\f] { count(); }
252 /* that could have been removed by the preprocessor anyway */
253 werror (W_STRAY_BACKSLASH, column);
260 /* flex 2.5.31 undefines yytext_ptr, so we have to define it again */
262 #define yytext_ptr yytext
266 static int checkCurrFile (char *s)
271 /* skip '#' character */
275 /* check if this is a #line
276 this is not standard and can be removed in the future */
277 #define LINE_STR "line"
278 #define LINE_LEN ((sizeof LINE_STR) - 1)
280 if (strncmp(s, LINE_STR, LINE_LEN) == 0)
283 /* get the line number */
284 lNum = strtol(s, &tptr, 10);
285 if (tptr == s || !isspace(*tptr))
289 /* now see if we have a file name */
290 while (*s != '\"' && *s)
293 /* if we don't have a filename then */
294 /* set the current line number to */
295 /* line number if printFlag is on */
297 lineno = mylineno = lNum;
301 /* if we have a filename then check */
302 /* if it is "standard in" if yes then */
303 /* get the currentfile name info */
306 /* in c1mode fullSrcFileName is NULL */
307 if (fullSrcFileName &&
308 strncmp(s, fullSrcFileName, strlen(fullSrcFileName)) == 0) {
309 lineno = mylineno = lNum;
310 currFname = fullSrcFileName;
313 /* mark the end of the filename */
314 while (*s != '"') s++;
316 currFname = strdup (sb);
317 lineno = mylineno = lNum;
319 filename = currFname ;
326 static void count(void)
329 for (i = 0; yytext[i] != '\0'; i++) {
330 if (yytext[i] == '\n') {
335 if (yytext[i] == '\t')
336 column += 8 - (column % 8);
343 static int check_type(void)
345 symbol *sym = findSym(SymbolTab, NULL, yytext);
347 /* check if it is in the table as a typedef */
348 if (!ignoreTypedefType && sym && IS_SPEC (sym->etype)
349 && SPEC_TYPEDEF (sym->etype)) {
350 strncpyz(yylval.yychar, yytext, SDCC_NAME_MAX);
354 strncpyz (yylval.yychar, yytext, SDCC_NAME_MAX);
360 * Change by JTV 2001-05-19 to not concantenate strings
361 * to support ANSI hex and octal escape sequences in string literals
364 static char *stringLiteral(void)
366 #define STR_BUF_CHUNCK_LEN 1024
368 static struct dbuf_s dbuf;
372 dbuf_init(&dbuf, STR_BUF_CHUNCK_LEN);
374 dbuf_set_size(&dbuf, 0);
376 dbuf_append(&dbuf, "\"", 1);
377 /* put into the buffer till we hit the first \" */
379 while ((ch = input()) != 0) {
382 /* if it is a \ then escape char's are allowed */
385 /* \<newline> is a continuator */
392 dbuf_append(&dbuf, buf, 2); /* get the escape char, no further check */
394 break; /* carry on */
397 /* if new line we have a new line break, which is illegal */
398 werror(W_NEWLINE_IN_STRING);
399 dbuf_append(&dbuf, "\n", 1);
405 /* if this is a quote then we have work to do */
406 /* find the next non whitespace character */
407 /* if that is a double quote then carry on */
408 dbuf_append(&dbuf, "\"", 1); /* Pass end of this string or substring to evaluator */
409 while ((ch = input()) && (isspace(ch) || ch == '\\')) {
412 if ((ch = input()) != '\n') {
413 werror(W_STRAY_BACKSLASH, column);
439 dbuf_append(&dbuf, buf, 1); /* Put next substring introducer into output string */
444 return (char *)dbuf_c_str(&dbuf);
463 P_OVERLAY_, /* I had a strange conflict with P_OVERLAY while */
464 /* cross-compiling for MINGW32 with gcc 3.2 */
478 /* SAVE/RESTORE stack */
479 #define SAVE_RESTORE_SIZE 128
481 STACK_DCL(options_stack, struct options *, SAVE_RESTORE_SIZE)
482 STACK_DCL(optimize_stack, struct optimize *, SAVE_RESTORE_SIZE)
483 STACK_DCL(SDCCERRG_stack, struct SDCCERRG *, SAVE_RESTORE_SIZE)
486 * cloneXxx functions should be updated every time a new set is
487 * added to the options or optimize structure!
490 static struct options *cloneOptions(struct options *opt)
492 struct options *new_opt;
494 new_opt = Safe_malloc(sizeof (struct options));
496 /* clone scalar values */
500 new_opt->calleeSavesSet = setFromSetNonRev(opt->calleeSavesSet);
501 new_opt->excludeRegsSet = setFromSetNonRev(opt->excludeRegsSet);
502 /* not implemented yet: */
503 /* new_opt->olaysSet = setFromSetNonRev(opt->olaysSet); */
508 static struct optimize *cloneOptimize(struct optimize *opt)
510 struct optimize *new_opt;
512 new_opt = Safe_malloc(sizeof (struct optimize));
514 /* clone scalar values */
520 static struct SDCCERRG *cloneSDCCERRG (struct SDCCERRG *val)
522 struct SDCCERRG *new_val;
524 new_val = Safe_malloc(sizeof (struct SDCCERRG));
526 /* clone scalar values */
532 static void copyAndFreeOptions(struct options *dest, struct options *src)
534 /* delete dest sets */
535 deleteSet(&dest->calleeSavesSet);
536 deleteSet(&dest->excludeRegsSet);
537 /* not implemented yet: */
538 /* deleteSet(&dest->olaysSet); */
540 /* copy src to dest */
546 static void copyAndFreeOptimize(struct optimize *dest, struct optimize *src)
548 /* copy src to dest */
554 static void copyAndFreeSDCCERRG(struct SDCCERRG *dest, struct SDCCERRG *src)
556 /* copy src to dest */
562 static void doPragma(int op, char *cp)
569 STACK_PUSH(options_stack, cloneOptions(&options));
570 STACK_PUSH(optimize_stack, cloneOptimize(&optimize));
571 STACK_PUSH(SDCCERRG_stack, cloneSDCCERRG(&_SDCCERRG));
577 struct options *optionsp;
578 struct optimize *optimizep;
579 struct SDCCERRG *sdccerrgp;
581 optionsp = STACK_POP(options_stack);
582 copyAndFreeOptions(&options, optionsp);
584 optimizep = STACK_POP(optimize_stack);
585 copyAndFreeOptimize(&optimize, optimizep);
587 sdccerrgp = STACK_POP(SDCCERRG_stack);
588 copyAndFreeSDCCERRG(&_SDCCERRG, sdccerrgp);
593 optimize.loopInduction = 0;
597 optimize.loopInvariant = 0;
601 optimize.loopInduction = 1;
605 options.stackAuto = 1;
609 optimize.noJTabBoundary = 1;
613 optimize.global_cse = 0;
617 options.noOverlay = 1;
621 options.lessPedantic = 1;
622 setErrorLogLevel(ERROR_LEVEL_WARNING);
626 /* append to the functions already listed
628 setParseWithComma(&options.calleeSavesSet, cp);
633 deleteSet(&options.excludeRegsSet);
634 setParseWithComma(&options.excludeRegsSet, cp);
643 optimize.noLoopReverse = 1;
650 if (sscanf(cp, "%d", &i) && (i<MAX_ERROR_WARNING))
652 setWarningDisabled(i);
657 optimize.codeSpeed = 1;
658 optimize.codeSize = 0;
662 optimize.codeSpeed = 0;
663 optimize.codeSize = 1;
666 case P_OPTCODEBALANCED:
667 optimize.codeSpeed = 0;
668 optimize.codeSize = 0;
673 options.std_sdcc = 0;
678 options.std_sdcc = 0;
683 options.std_sdcc = 1;
688 options.std_sdcc = 1;
694 char *segname = Safe_malloc(15);
695 sscanf(cp, " %8s", str);
697 sprintf(segname, "%-8.8s(CODE)", str);
698 options.code_seg = segname;
705 char *segname = Safe_malloc(15);
706 sscanf(cp, " %8s", str);
708 sprintf(segname, "%-8.8s(CODE)", str);
709 options.const_seg = segname;
715 static int process_pragma(char *s)
717 #define NELEM(x) (sizeof (x) / sizeof (x)[0])
718 #define PRAGMA_STR "#pragma"
719 #define PRAGMA_LEN ((sizeof PRAGMA_STR) - 1)
721 static struct pragma_s
727 { "save", P_SAVE, 0 },
728 { "restore", P_RESTORE, 0 },
729 { "noinduction", P_NOINDUCTION, 0 },
730 { "noinvariant", P_NOINVARIANT, 0 },
731 { "noloopreverse", P_LOOPREV, 0 },
732 { "induction", P_INDUCTION, 0 },
733 { "stackauto", P_STACKAUTO, 0 },
734 { "nojtbound", P_NOJTBOUND, 0 },
735 { "nogcse", P_NOGCSE, 0 },
736 { "nooverlay", P_NOOVERLAY, 0 },
737 { "callee_saves", P_CALLEE_SAVES, 0 },
738 { "exclude", P_EXCLUDE, 0 },
739 { "noiv", P_NOIV, 0 },
740 { "overlay", P_OVERLAY_, 0 },
741 { "less_pedantic", P_LESSPEDANTIC, 0 },
742 { "disable_warning",P_DISABLEWARN, 0 },
743 { "opt_code_speed", P_OPTCODESPEED, 0 },
744 { "opt_code_size", P_OPTCODESIZE, 0 },
745 { "opt_code_balanced", P_OPTCODEBALANCED, 0 },
746 { "std_c89", P_STD_C89, 0 },
747 { "std_c99", P_STD_C99, 0 },
748 { "std_sdcc89", P_STD_SDCC89, 0 },
749 { "std_sdcc99", P_STD_SDCC99, 0 },
750 { "codeseg", P_CODESEG, 0 },
751 { "constseg", P_CONSTSEG, 0 },
754 * The following lines are deprecated pragmas,
755 * only for bacward compatibility.
756 * They should be removed in next major release after 1.4.0
759 { "SAVE", P_SAVE, 1 },
760 { "RESTORE", P_RESTORE, 1 },
761 { "NOINDUCTION", P_NOINDUCTION, 1 },
762 { "NOINVARIANT", P_NOINVARIANT, 1 },
763 { "NOLOOPREVERSE", P_LOOPREV, 1 },
764 { "INDUCTION", P_INDUCTION, 1 },
765 { "STACKAUTO", P_STACKAUTO, 1 },
766 { "NOJTBOUND", P_NOJTBOUND, 1 },
767 { "NOGCSE", P_NOGCSE, 1 },
768 { "NOOVERLAY", P_NOOVERLAY, 1 },
769 { "CALLEE-SAVES", P_CALLEE_SAVES, 1 },
770 { "EXCLUDE", P_EXCLUDE, 1 },
771 { "NOIV", P_NOIV, 1 },
772 { "OVERLAY", P_OVERLAY_, 1 },
773 { "LESS_PEDANTIC", P_LESSPEDANTIC, 1 },
778 /* find the pragma */
779 while (strncmp(s, PRAGMA_STR, PRAGMA_LEN))
783 /* look for the directive */
788 /* look for the end of the directive */
789 while ((!isspace(*s)) && (*s != '\n'))
792 /* skip separating whitespace */
793 while (isspace(*s) && (*s != '\n'))
796 /* First give the port a chance */
797 if (port->process_pragma && !port->process_pragma(cp))
800 for (i = 0; i < NELEM(pragma_tbl); i++)
802 /* now compare and do what needs to be done */
803 size_t len = strlen(pragma_tbl[i].name);
805 if (strncmp(cp, pragma_tbl[i].name, len) == 0)
807 if (pragma_tbl[i].deprecated != 0)
808 werror(W_DEPRECATED_PRAGMA, pragma_tbl[i].name);
810 doPragma(pragma_tbl[i].id, s);
815 werror(W_UNKNOWN_PRAGMA, cp);
819 /* will return 1 if the string is a part
820 of a target specific keyword */
821 static int isTargetKeyword(char *s)
825 if (port->keywords == NULL)
828 if (s[0] == '_' && s[1] == '_')
830 /* Keywords in the port's array have either 0 or 1 underscore, */
831 /* so skip over the appropriate number of chars when comparing */
832 for (i = 0 ; port->keywords[i] ; i++ )
834 if (port->keywords[i][0] == '_' &&
835 strcmp(port->keywords[i],s+1) == 0)
837 else if (strcmp(port->keywords[i],s+2) == 0)
843 for (i = 0 ; port->keywords[i] ; i++ )
845 if (strcmp(port->keywords[i],s) == 0)
855 if (!STACK_EMPTY(options_stack) || !STACK_EMPTY(optimize_stack))
856 werror(W_SAVE_RESTORE);
865 if (mylineno && filename) {
866 if(options.vc_err_style)
867 fprintf(stderr, "\n%s(%d) : %s: token -> '%s' ; column %d\n",
868 filename, mylineno, s, yytext, column);
870 fprintf(stderr, "\n%s:%d: %s: token -> '%s' ; column %d\n",
871 filename, mylineno, s ,yytext, column);
874 /* this comes from an empy file, no problem */