1 
2 /*
3  *  Math2
4  *  Copyright (c) 2004-2006 by Mattias Hultgren <mattias_hultgren@tele2.se>
5  *
6  *  See math2.h
7  */
8 
9 
10 #include "math2.h"
11 #include "math2.intern.h"
12 #include "keyfile.h"
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 
17 namespace math
18 {
19 
20 int count_whitespaces(const char *str, int *line_breaks = 0);
21 void overwrite_comments(char *str) throw(error_obj);
22 int get_word( const char *str, utf8_string &word ) throw(error_obj);
23 void overwrite_script_info(char *str);
24 
25 
26 const char *reserved_keywords[] = {
27                                    "Function",
28                                    "Variable",
29                                    "Integer",
30                                    "Real",
31                                    "Complex",
32                                    "String",
33                                    "Matrix",
34                                    "Picture",
35                                    "Array",
36                                    "Boolean",
37                                    "return",
38                                    "if",
39                                    "else",
40                                    "while",
41                                    "break",
42                                    "continue",
43                                    0
44                                   };
45 
46 
check_name_against_reserved(const utf8_string & name)47 void check_name_against_reserved(const utf8_string &name) throw(error_obj)
48 {
49 	for(int i=0;;i++)
50 	{
51 		if(reserved_keywords[i] == 0)
52 			break;
53 
54 		if( name == reserved_keywords[i] )
55 		{
56 			utf8_string tmpstr( _("Illegal use of reserved keyword: ") );
57 			tmpstr.append( name );
58 			THROW_ERROR( ErrorType_General, tmpstr.c_str() );
59 		}
60 	}
61 }
62 
63 
64 
CodeBlockLine()65 CodeBlock::CodeBlockLine::CodeBlockLine()
66 {
67 	line_number = 0;
68 	type = CodeBlock_Type_Start;
69 	sub_type = CodeBlock_SubType_None;
70 	code = 0;
71 }
CodeBlockLine(const CodeBlockLine & src)72 CodeBlock::CodeBlockLine::CodeBlockLine(const CodeBlockLine &src) throw(error_obj)
73 {
74 	line_number = 0;
75 	type = CodeBlock_Type_Start;
76 	sub_type = CodeBlock_SubType_None;
77 	code = 0;
78 
79 	*this = src;
80 }
~CodeBlockLine()81 CodeBlock::CodeBlockLine::~CodeBlockLine()
82 {
83 	if( type == CodeBlock_Type_Code )
84 		delete code;
85 }
operator =(const CodeBlockLine & src)86 void CodeBlock::CodeBlockLine::operator=(const CodeBlockLine &src) throw(error_obj)
87 {
88 	if( src.code != 0  &&  src.type == CodeBlock_Type_Code )
89 	{
90 		if(code == 0)
91 		{
92 			try{  code = new CodeLine;  }
93 			catch(...) {  THROW_ERROR( ErrorType_Memory, _("Couldn't get memory.") );  }
94 		}
95 
96 		try{  *code = *src.code;  }
97 		catch(...)
98 		{
99 			delete code;
100 			code = 0;
101 			throw;
102 		}
103 	}
104 	else
105 	{
106 		if( type == CodeBlock_Type_Code )
107 		{
108 			delete code;
109 			code = 0;
110 		}
111 	}
112 
113 	line_number = src.line_number;
114 	type = src.type;
115 	sub_type = src.sub_type;
116 }
117 
118 
119 
120 
121 
122 
CodeBlock(const CodeBlock & src)123 CodeBlock::CodeBlock(const CodeBlock &src) throw(error_obj)
124 {
125 	inuse = 0;
126 	nroftmpvariables = 0;
127 	*this = src;
128 }
129 
~CodeBlock()130 CodeBlock::~CodeBlock()
131 {
132 	lines.clear();
133 }
134 
set_code(const char * newcode)135 void CodeBlock::set_code(const char *newcode) throw(error_obj)
136 {
137 	char *str;
138 
139 	clear();
140 
141 	try{  str = new char [strlen(newcode) + 1];  }
142 	catch(...) {  THROW_ERROR( ErrorType_Memory, _("Couldn't get memory.") );  }
143 	strcpy(str,newcode);
144 
145 	try{ set_code_intern( str, true ); }
146 	catch(...)
147 	{
148 		delete [] str;
149 		throw;
150 	}
151 	delete [] str;
152 }
153 
set_code_from_file(const utf8_string & new_filename,const utf8_string * only_allow_identifier)154 void CodeBlock::set_code_from_file( const utf8_string &new_filename,
155                                     const utf8_string *only_allow_identifier ) throw(error_obj)
156 {
157 	FILE *fp;
158 	long filesize;
159 	char *str;
160 
161 	clear();
162 
163 	filename = new_filename;
164 
165 	fp = fopen( filename.c_str(), "rb" );
166 	if(fp == 0)
167 	{
168 setcode_thrower_error:
169 		if(fp != 0)
170 			fclose(fp);
171 
172 		clear();
173 		char tmp_err[ERROR_OBJ_MSG_LEN];
174 		snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Couldn't open/read from file (%s)"), new_filename.c_str() );
175 		THROW_ERROR( ErrorType_File_IO, tmp_err );
176 	}
177 
178 	if(fseek(fp,0L,SEEK_END) != 0)
179 		goto setcode_thrower_error;
180 	filesize = ftell(fp);
181 	if(filesize == -1)
182 		goto setcode_thrower_error;
183 	if(fseek(fp,0L,SEEK_SET) != 0)
184 		goto setcode_thrower_error;
185 
186 	try{  str = new char [filesize + 1];  }
187 	catch(...)
188 	{
189 		fclose(fp);
190 		THROW_ERROR( ErrorType_Memory, _("Couldn't get memory.") );
191 	}
192 
193 	if(long(fread(str,1,filesize,fp)) != filesize)
194 	{
195 		delete [] str;
196 		goto setcode_thrower_error;
197 	}
198 	if(fclose(fp) != 0)
199 	{
200 		delete [] str;
201 		clear();
202 		char tmp_err[ERROR_OBJ_MSG_LEN];
203 		snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Couldn't close file (%s)"), new_filename.c_str() );
204 		THROW_ERROR( ErrorType_File_IO, tmp_err );
205 	}
206 
207 	str[filesize] = 0;
208 	try
209 	{
210 		overwrite_comments( str );
211 
212 		while( create_function( str, only_allow_identifier ) ) { }
213 
214 		if( str[ count_whitespaces(str) ] != '\0' )
215 		{
216 			char tmp_err[ERROR_OBJ_MSG_LEN];
217 			snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Invalid format in file %s"), filename.c_str() );
218 			THROW_ERROR( ErrorType_File_IO, tmp_err );
219 		}
220 	}
221 	catch(...)
222 	{
223 		delete [] str;
224 		clear();
225 		throw;
226 	}
227 
228 	delete [] str;
229 	clear();
230 }
231 
calc(Variable * answer)232 void CodeBlock::calc( Variable *answer ) throw(error_obj)
233 {
234 	int i;
235 	CodeBlock_SubType tmp = CodeBlock_SubType_None;
236 	Vector<Variable> tmp_variables;
237 	VariableList this_private_varlist;
238 
239 	tmp_variables.set_size_filled( nroftmpvariables );
240 
241 	if( answer == 0 )
242 		THROW_ERROR( ErrorType_Internal, "No place to store answer in CodeBlock::calc()" );
243 
244 	try
245 	{
246 		inuse++;
247 
248 		i = 0;
249 		if( private_varlist.is_empty() )
250 			run_block( &tmp_variables, 0, tmp, answer, i );
251 		else
252 		{
253 			this_private_varlist = private_varlist; // makes a private copy of the local variable for the function
254 
255 			run_block( &tmp_variables, &this_private_varlist, tmp, answer, i );
256 		}
257 
258 		switch( tmp )
259 		{
260 		case CodeBlock_SubType_None:
261 		case CodeBlock_SubType_Return:
262 			break;
263 
264 		case CodeBlock_SubType_Break:
265 			THROW_ERROR( ErrorType_General, _("break used outside of while") );
266 
267 		case CodeBlock_SubType_Continue:
268 			THROW_ERROR( ErrorType_General, _("continue used outside of while") );
269 
270 		default:
271 			{
272 				char str[50];
273 				snprintf( str, 50, "Unhandled CodeBlock_SubType return %ld", tmp );
274 
275 				THROW_ERROR( ErrorType_Internal, str );
276 			}
277 		}
278 	}
279 	catch(error_obj error)
280 	{
281 		inuse--;
282 		if( filename.get_length() == 0 )
283 			throw error;
284 
285 		if( error.type >= ErrorType_UmpCode_General )
286 			throw error;
287 
288 		error_obj error2;
289 		error2.type = error.type;
290 
291 		snprintf( error2.msg, ERROR_OBJ_MSG_LEN, "%s: %s %ld: %s", filename.c_str(), _("line"),
292 		          lines[i]->line_number, error.msg );
293 		throw error2;
294 	}
295 	catch(...) {  inuse--;  THROW_ERROR( ErrorType_Memory, _("Couldn't get memory.") );  }
296 
297 	inuse--;
298 }
299 
clear(void)300 void CodeBlock::clear(void)
301 {
302 	try{
303 		filename = "";
304 		lines.clear();
305 		nroftmpvariables = 0;
306 	}
307 	catch(...) { }
308 }
309 
operator =(const CodeBlock & src)310 void CodeBlock::operator=(const CodeBlock &src) throw(error_obj)
311 {
312 	printf("\nCodeBlock::operator=() not implemented yet\n");
313 	exit(-1);
314 }
315 
316 
count_whitespaces(const char * str,int * line_breaks)317 int count_whitespaces(const char *str, int *line_breaks)
318 {
319 	for(int i=0;;i++)
320 	{
321 		switch(str[i])
322 		{
323 		case '\n':
324 			if( line_breaks != 0 )
325 				line_breaks[0]++;
326 		case ' ': case '\t':
327 			break;
328 		default:
329 			return i;
330 		}
331 	}
332 }
333 
334 // this function overwrite all comments with spaces ( \n inside comments are not moved)
335 // \t is also overwritten by one space
overwrite_comments(char * str)336 void overwrite_comments(char *str) throw(error_obj)
337 {
338 	int i;
339 	bool inside_string = false;
340 
341 	for(i=0;str[i] != '\0';i++)
342 	{
343 		if(inside_string)
344 		{
345 			if( str[i] == '\"'  ||  str[i] == '\n' )
346 				inside_string = false;
347 		}
348 		else
349 		{
350 			switch(str[i])
351 			{
352 			case '\"':
353 				inside_string = true;
354 				break;
355 
356 			case '\t':
357 				str[i] = ' ';
358 				break;
359 
360 			case '#':
361 				for(;;i++) // inside a one line comment
362 				{
363 					str[i] = ' ';
364 					if( str[i+1] == '\0'  ||  str[i+1] == '\n' )
365 						break;
366 				}
367 				break;
368 
369 			case '/':
370 				if(str[i+1] == '/') // inside a one line comment
371 				{
372 					for(;;i++)
373 					{
374 						str[i] = ' ';
375 						if( str[i+1] == '\0'  ||  str[i+1] == '\n' )
376 							break;
377 					}
378 				}
379 				else if(str[i+1] == '*') // inside multi line comment
380 				{
381 					for(;;i++)
382 					{
383 						if(str[i] != '\n')
384 							str[i] = ' ';
385 						if(str[i+1] == '\0')
386 							THROW_ERROR( ErrorType_General, _("Unexpected end of file") );
387 						if( str[i+1] == '*'  &&  str[i+2] == '/' )
388 						{
389 							str[i+1] = ' ';
390 							str[i+2] = ' ';
391 							break;
392 						}
393 					}
394 				}
395 				break;
396 
397 			default:
398 				break;
399 			}
400 		}
401 	}
402 }
403 
get_word(const char * str,utf8_string & word)404 int get_word( const char *str, utf8_string &word ) throw(error_obj)
405 {
406 	char tknstr[] = " ";
407 	int i=0;
408 
409 	word = "";
410 	if( str[i] >= '0'  &&  str[i] <= '9' )
411 		return 0;
412 	for(;;i++)
413 	{
414 		if( (str[i] >= 'A'  &&  str[i] <= 'Z')  ||  (str[i] >= 'a'  &&  str[i] <= 'z')  ||  str[i] == '_'  ||  (str[i] >= '0'  &&  str[i] <= '9') )
415 		{
416 			tknstr[0] = str[i];
417 			word.append( tknstr );
418 		}
419 		else
420 			break;
421 	}
422 
423 	return i;
424 }
425 
set_code_intern(char * str,bool allow_variable_creation)426 void CodeBlock::set_code_intern(char *str, bool allow_variable_creation ) throw(error_obj)
427 {
428 	utf8_string word;
429 	int i, i_tmp;
430 	int start = 0;
431 	int linenr = 1;
432 	int started_curley = 0;
433 	int started_bracket = 0;
434 	bool inside_string = false;
435 	bool end_by_bracket = false;
436 	CodeBlock_SubType sub_type = CodeBlock_SubType_None;
437 
438 	try
439 	{
440 		for(i=0;;i++)
441 		{
442 			if( i == start )
443 			{
444 				i += count_whitespaces( &str[i], &linenr );
445 				i_tmp = i + get_word( &str[i], word );
446 				if( word == reserved_keywords[reserved_break] )
447 				{
448 					i = i_tmp;
449 					start = i;
450 
451 					i += count_whitespaces( &str[i], &linenr );
452 					if( str[i] == ';'  ||  str[i] == '{'  ||  str[i] == '}' )
453 						add_void( CodeBlock_SubType_Break );
454 					else
455 					{
456 						utf8_string tmpstr( _("Illegal use of reserved keyword: ") );
457 						tmpstr.append( "break" );
458 						THROW_ERROR( ErrorType_General, tmpstr.c_str() );
459 					}
460 				}
461 				else if( word == reserved_keywords[reserved_continue] )
462 				{
463 					i = i_tmp;
464 					start = i;
465 
466 					i += count_whitespaces( &str[i], &linenr );
467 					if( str[i] == ';'  ||  str[i] == '{'  ||  str[i] == '}' )
468 						add_void( CodeBlock_SubType_Continue );
469 					else
470 					{
471 						utf8_string tmpstr( _("Illegal use of reserved keyword: ") );
472 						tmpstr.append( "continue" );
473 						THROW_ERROR( ErrorType_General, tmpstr.c_str() );
474 					}
475 				}
476 				else if( word == reserved_keywords[reserved_return] )
477 				{
478 					i = i_tmp;
479 					start = i;
480 
481 					i += count_whitespaces( &str[i], &linenr );
482 					if( str[i] == ';'  ||  str[i] == '{'  ||  str[i] == '}' )
483 						add_void( CodeBlock_SubType_Return );
484 					else
485 						sub_type = CodeBlock_SubType_Return;
486 				}
487 				else if( word == reserved_keywords[reserved_while] )
488 				{
489 					i = i_tmp;
490 					i += count_whitespaces( &str[i], &linenr );
491 
492 					if( str[i] != '(' )
493 						THROW_ERROR( ErrorType_General, _("Expected ( after while") );
494 
495 					started_bracket = 1;
496 					end_by_bracket = true;
497 					sub_type = CodeBlock_SubType_While;
498 
499 					i++;
500 					start = i;
501 				}
502 				else if( word == reserved_keywords[reserved_if] )
503 				{
504 					i = i_tmp;
505 					i += count_whitespaces( &str[i], &linenr );
506 
507 					if( str[i] != '(' )
508 						THROW_ERROR( ErrorType_General, _("Expected ( after if") );
509 
510 					started_bracket = 1;
511 					end_by_bracket = true;
512 					sub_type = CodeBlock_SubType_If;
513 
514 					i++;
515 					start = i;
516 				}
517 				else if( word == reserved_keywords[reserved_else] )
518 				{
519 					i = i_tmp;
520 					i += count_whitespaces( &str[i], &linenr );
521 
522 					add_void( CodeBlock_SubType_Else );
523 
524 					if( str[i] == '{' )
525 					{
526 						add_curley( CodeBlock_Type_Start );
527 						started_curley++;
528 						i++;
529 					}
530 					else if( str[i] == ';' )
531 					{
532 						add_curley( CodeBlock_Type_Start ); // adding an empty body
533 						add_curley( CodeBlock_Type_Stop );
534 						i++;
535 					}
536 					start = i;
537 					i--;
538 					continue;
539 				}
540 			}
541 
542 			if(inside_string)
543 			{
544 				if(str[i] == '\"')
545 					inside_string = false;
546 				else if( str[i] == '\n'  ||  str[i] == '\0' )
547 					THROW_ERROR( ErrorType_General, _("Expected ending \"") );
548 			}
549 			else
550 			{
551 				switch(str[i])
552 				{
553 				case '\n':
554 					linenr++;
555 					break;
556 
557 				case '\"':
558 					inside_string = true;
559 					break;
560 
561 				case '(':
562 					started_bracket++;
563 					break;
564 
565 				case ')':
566 					started_bracket--;
567 					if( started_bracket == 0  &&  end_by_bracket )
568 					{
569 						end_by_bracket = false;
570 						str[i] = '\0';
571 						add_line( &str[start], linenr, allow_variable_creation, sub_type );
572 						sub_type = CodeBlock_SubType_None;
573 						str[i] = ')';
574 						i += 1 + count_whitespaces( &str[i+1], &linenr );
575 
576 						if( str[i] == ';' )
577 						{
578 							add_curley( CodeBlock_Type_Start ); // adding an empty body
579 							add_curley( CodeBlock_Type_Stop );
580 							start = i+1;
581 						}
582 						else
583 						{
584 							start = i;
585 							i--;
586 						}
587 					}
588 					break;
589 
590 				case '{':
591 					if( end_by_bracket )
592 						THROW_ERROR( ErrorType_General, _("Expected ) but found {") );
593 					if(start != i)
594 					{
595 						str[i] = '\0';
596 						add_line( &str[start], linenr, allow_variable_creation, sub_type );
597 						sub_type = CodeBlock_SubType_None;
598 						str[i] = '{';
599 					}
600 					add_curley( CodeBlock_Type_Start );
601 
602 					started_curley++;
603 
604 					start = i+1;
605 					break;
606 
607 				case '}':
608 					if( end_by_bracket )
609 						THROW_ERROR( ErrorType_General, _("Expected ) but found }") );
610 					if(start != i)
611 					{
612 						str[i] = '\0';
613 						add_line( &str[start], linenr, allow_variable_creation, sub_type );
614 						sub_type = CodeBlock_SubType_None;
615 						str[i] = '}';
616 					}
617 					add_curley( CodeBlock_Type_Stop );
618 
619 					if(started_curley == 0)
620 					{
621 						clear();
622 						THROW_ERROR( ErrorType_General, _("Unexpected }") );
623 					}
624 					started_curley--;
625 
626 					start = i+1;
627 					break;
628 
629 				case ';':
630 					if( end_by_bracket )
631 						THROW_ERROR( ErrorType_General, _("Expected ) but found ;") );
632 					if(start != i)
633 					{
634 						str[i] = '\0';
635 						add_line( &str[start], linenr, allow_variable_creation, sub_type );
636 						sub_type = CodeBlock_SubType_None;
637 						str[i] = ';';
638 					}
639 					start = i+1;
640 					break;
641 
642 				case '\0':
643 					if( end_by_bracket )
644 						THROW_ERROR( ErrorType_General, _("Expected )") );
645 					if(start != i)
646 						add_line( &str[start], linenr, allow_variable_creation, sub_type );
647 					break;
648 				}
649 			}
650 
651 			if(str[i] == '\0')
652 				break;
653 		}
654 		if(started_curley != 0)
655 			THROW_ERROR( ErrorType_General, _("Expected }") );
656 
657 
658 		add_automatic_curleys();
659 
660 
661 		for( i=0; i<lines.get_size(); i++ ) // let's check for malplaced else's
662 		{
663 			if( lines[i]->sub_type == CodeBlock_SubType_Else )
664 			{
665 				if( i == 0 )
666 					THROW_ERROR( ErrorType_General, _("Unexpected 'else'") );
667 
668 				if( lines[i-1]->type == CodeBlock_Type_Stop )
669 				{
670 					if( lines[i-1]->pair_with == 0 )
671 						THROW_ERROR( ErrorType_General, _("Unexpected 'else'") );
672 					if( lines[ lines[i-1]->pair_with -1 ]->sub_type != CodeBlock_SubType_If )
673 						THROW_ERROR( ErrorType_General, _("Unexpected 'else'") );
674 				}
675 				else
676 					THROW_ERROR( ErrorType_General, _("Unexpected 'else'") );
677 			}
678 		}
679 
680 		for( i=0, nroftmpvariables=0; i<lines.get_size(); i++ ) // updating nroftmpvariables
681 		{
682 			if( lines[i]->type == CodeBlock_Type_Code )
683 				nroftmpvariables = (nroftmpvariables > lines[i]->code->get_nroftmpvariables()) ? nroftmpvariables : lines[i]->code->get_nroftmpvariables();
684 		}
685 	}
686 	catch(error_obj error)
687 	{
688 		if( filename.get_length() == 0 )
689 		{
690 			clear();
691 			throw error;
692 		}
693 
694 		error_obj error2;
695 		error2.type = error.type;
696 
697 		snprintf( error2.msg, ERROR_OBJ_MSG_LEN, "%s: %s %ld: %s", filename.c_str(), _("line"),
698 		          linenr, error.msg );
699 
700 		clear();
701 		throw error2;
702 	}
703 }
704 
add_automatic_curleys(void)705 void CodeBlock::add_automatic_curleys(void) throw(error_obj)
706 {
707 	bool added_curley;
708 	int i, start;
709 
710 	i = lines.get_size()-1;
711 	if( i == -1 )
712 		return;
713 
714 	if( lines[i]->sub_type == CodeBlock_SubType_While  ||
715 	    lines[i]->sub_type == CodeBlock_SubType_If  ||
716 	    lines[i]->sub_type == CodeBlock_SubType_Else )
717 		THROW_ERROR( ErrorType_General, _("Expected {") );
718 
719 	do
720 	{
721 		added_curley = false;
722 		pair_up_curleys();
723 
724 		for( i=lines.get_size()-2; i>=0; i-- )
725 		{
726 			if( lines[i+1]->type != CodeBlock_Type_Start )
727 			{
728 				if( lines[i]->sub_type == CodeBlock_SubType_While  ||
729 				    lines[i]->sub_type == CodeBlock_SubType_If  ||
730 				    lines[i]->sub_type == CodeBlock_SubType_Else )
731 				{
732 					i++;
733 					start = i;
734 
735 					if( lines[i]->sub_type == CodeBlock_SubType_While )
736 						i = lines[i+1]->pair_with +1;
737 					else if( lines[i]->sub_type == CodeBlock_SubType_If )
738 					{
739 						i = lines[i+1]->pair_with +1;
740 						if( lines[i]->sub_type == CodeBlock_SubType_Else )
741 							i = lines[i+1]->pair_with +1;
742 					}
743 					else
744 						i++;
745 
746 					add_curley( CodeBlock_Type_Stop, CodeBlock_SubType_None, i );
747 					add_curley( CodeBlock_Type_Start, CodeBlock_SubType_None, start );
748 					added_curley = true;
749 					break;
750 				}
751 			}
752 		}
753 	}while( added_curley );
754 }
755 
pair_up_curleys(void)756 void CodeBlock::pair_up_curleys(void)
757 {
758 	int i, i_tmp;
759 	for( i=0; i<lines.get_size(); i++ )
760 	{
761 		if( lines[i]->type == CodeBlock_Type_Start  ||
762 		    lines[i]->type == CodeBlock_Type_Stop )
763 			lines[i]->pair_with = -1;
764 	}
765 	for( i=0; i<lines.get_size(); i++ )
766 	{
767 		if( lines[i]->type == CodeBlock_Type_Stop )
768 		{
769 			if( lines[i]->pair_with == -1 )
770 			{
771 				i_tmp = i;
772 				for( i--; i>=0; i-- )
773 				{
774 					if( lines[i]->type == CodeBlock_Type_Start )
775 					{
776 						if( lines[i]->pair_with == -1 )
777 						{
778 							lines[i]->pair_with = i_tmp;
779 							lines[i_tmp]->pair_with = i;
780 							break;
781 						}
782 					}
783 				}
784 			}
785 		}
786 	}
787 }
788 
789 // return true if a function was created (and overwrited with spaces, \n are left as is)
790 // if an identifier was found that didn't match with only_allow_identifier, false is returned
create_function(char * str,const utf8_string * only_allow_identifier)791 bool CodeBlock::create_function(char *str, const utf8_string *only_allow_identifier) throw(error_obj)
792 {
793 	int i,i_tmp;
794 	utf8_string name, word;
795 	CodeBlock *func;
796 	Vector<ArgumentType> arguments;
797 	ArgumentType tmp_argument;
798 
799 	try
800 	{
801 		func = new CodeBlock;
802 	}
803 	catch(...)  {  THROW_ERROR( ErrorType_Memory, _("Couldn't get memory.") );  }
804 
805 	try
806 	{
807 		i = count_whitespaces( str ); // skipping whitespaces
808 		if(str[i] == '\0')
809 		{
810 			delete func;
811 			return false;
812 		}
813 
814 // Checking for "Function"
815 		i += get_word( &str[i], word );
816 		if( word != reserved_keywords[reserved_function] )
817 		{
818 			delete func;
819 			return false;
820 		}
821 
822 		if( (i_tmp = count_whitespaces( &str[i] )) ==  0 )
823 		{
824 			delete func;
825 			return false; // keyword "Function" must be followed by whitespace(s)
826 		}
827 		i += i_tmp;
828 // Found "Function"
829 
830 // Getting name of function
831 		i += get_word( &str[i], name );
832 
833 // Check so that the name is allowed
834 		if( only_allow_identifier != 0 )
835 		{
836 			if( name != *only_allow_identifier )
837 			{
838 				delete func;
839 				return false;
840 			}
841 		}
842 
843 // Make sure the name is legal and unique
844 		check_name_against_reserved( name );
845 
846 		if( function_list.check_for( name ) )
847 		{
848 			error_obj error;
849 
850 			error.type = ErrorType_Name_isnt_unique;
851 			snprintf( error.msg, ERROR_OBJ_MSG_LEN, "Function name (%s) is not unique", name.c_str() );
852 			throw error;
853 		}
854 
855 		try
856 		{
857 			global_varlist.get_id( name.c_str() ); // this line will throw if the name isn't a Variable
858 			error_obj error;
859 			error.type = ErrorType_Name_isnt_unique;
860 			snprintf( error.msg, ERROR_OBJ_MSG_LEN, "Function name (%s) is not unique", name.c_str() );
861 			throw error;
862 		}
863 		catch(error_obj error) // only throwed error is 'Variable not found' and 'Function name () is not unique'
864 		{
865 			if( error.type == ErrorType_Name_isnt_unique )
866 				throw;
867 		}
868 // The name is now legal and unique
869 
870 // Now its time for the argument brackets
871 		i += count_whitespaces( &str[i] );
872 		if(str[i] == '(')  // checking if the function should have arguments
873 		{
874 			for(i++;;)
875 			{
876 				i += count_whitespaces( &str[i] );
877 
878 				get_word( &str[i], word );
879 				if( word.get_length() != 0 )
880 				{
881 					if( word == reserved_keywords[reserved_variable] )
882 					{
883 						tmp_argument.type = reserved_variable;
884 specified_argument_entry:
885 						i += word.get_length();
886 						i += count_whitespaces( &str[i] );
887 						get_word( &str[i], word );
888 unspecified_argument_entry:
889 						i += word.get_length();
890 						i += count_whitespaces( &str[i] );
891 
892 						check_name_against_reserved( word );
893 						if( word == "i" )
894 							THROW_ERROR( ErrorType_General, _("\'i\' isn't a valid name of an argument variable.") );
895 
896 						try{
897 							func->private_varlist.get_id( word.c_str() );
898 							word.prepend( _("Name is not unique: ") );
899 							THROW_ERROR( ErrorType_Name_isnt_unique, word.c_str() );
900 						}
901 						catch(error_obj error) // only throwed error is 'Variable not found' and 'Name is not unique'
902 						{
903 							if( error.type == ErrorType_Name_isnt_unique )
904 								throw;
905 
906 							try{  tmp_argument.variable_name = word;  }
907 							catch(...) {  THROW_ERROR( ErrorType_Memory, _("Couldn't get memory.") );  }
908 
909 							arguments.append( tmp_argument );
910 
911 							Variable tmpvar;
912 							tmpvar.set_name( word );
913 							func->private_varlist.create( tmpvar, false, false, true );
914 						}
915 						if( str[i] == ')' )
916 							break;
917 						else if( str[i] == ',' )
918 							i++;
919 					}
920 					else if( word == reserved_keywords[reserved_integer] )
921 					{
922 						tmp_argument.type = reserved_integer;
923 						goto specified_argument_entry;
924 					}
925 					else if( word == reserved_keywords[reserved_real] )
926 					{
927 						tmp_argument.type = reserved_real;
928 						goto specified_argument_entry;
929 					}
930 					else if( word == reserved_keywords[reserved_complex] )
931 					{
932 						tmp_argument.type = reserved_complex;
933 						goto specified_argument_entry;
934 					}
935 					else if( word == reserved_keywords[reserved_string] )
936 					{
937 						tmp_argument.type = reserved_string;
938 						goto specified_argument_entry;
939 					}
940 					else if( word == reserved_keywords[reserved_matrix] )
941 					{
942 						tmp_argument.type = reserved_matrix;
943 						goto specified_argument_entry;
944 					}
945 					else if( word == reserved_keywords[reserved_picture] )
946 					{
947 						tmp_argument.type = reserved_picture;
948 						goto specified_argument_entry;
949 					}
950 					else if( word == reserved_keywords[reserved_array] )
951 					{
952 						tmp_argument.type = reserved_array;
953 						goto specified_argument_entry;
954 					}
955 					else if( word == reserved_keywords[reserved_boolean] )
956 					{
957 						tmp_argument.type = reserved_boolean;
958 						goto specified_argument_entry;
959 					}
960 					else
961 					{
962 // this is an unspecified argument make it the same as Variable
963 						tmp_argument.type = reserved_variable;
964 						goto unspecified_argument_entry;
965 					}
966 				}
967 				else
968 				{
969 					if( arguments.get_size() != 0 )
970 						THROW_ERROR( ErrorType_General, _("Expected argument") );
971 
972 					if( str[i] == '\0' )
973 					{
974 						THROW_ERROR( ErrorType_General, _("Expected )") );
975 					}
976 					else if( str[i] == ')' )
977 						break;
978 					else
979 						THROW_ERROR( ErrorType_General, _("Syntax error") );
980 				}
981 			}
982 			i += 1 + count_whitespaces( &str[i+1] );
983 		}
984 // Argument brackets handled
985 
986 // Function body start
987 		if(str[i] != '{')
988 			THROW_ERROR( ErrorType_General, _("Expected {") );
989 		i++;
990 
991 // The functions intern variables should be created
992 		for(;;)
993 		{
994 			i += count_whitespaces( &str[i] );
995 
996 			get_word( &str[i], word );
997 			i += word.get_length();
998 			if( word == reserved_keywords[reserved_variable] )
999 			{
1000 				do
1001 				{
1002 					i += count_whitespaces( &str[i] );
1003 
1004 					i += get_word( &str[i], word );
1005 
1006 					check_name_against_reserved( word );
1007 					if( word == "i" )
1008 						THROW_ERROR( ErrorType_General, _("\'i\' isn't a valid name of an intern variable.") );
1009 
1010 					try{
1011 						func->private_varlist.get_id( word.c_str() );
1012 
1013 						word.prepend( _("Name is not unique: ") );
1014 						THROW_ERROR( ErrorType_Name_isnt_unique, word.c_str() );
1015 					}
1016 					catch(error_obj error) // only throwed error is 'Variable not found' and 'Name is not unique'
1017 					{
1018 						if( error.type == ErrorType_Name_isnt_unique )
1019 							throw;
1020 
1021 						Variable tmpvar;
1022 						tmpvar.set_name( word );
1023 						func->private_varlist.create( tmpvar, false, false, true );
1024 					}
1025 					i += count_whitespaces( &str[i] );
1026 					if( str[i] == ',' )
1027 						i++;
1028 					else if( str[i] == ';' )
1029 					{
1030 						i++;
1031 						break;
1032 					}
1033 					else
1034 						THROW_ERROR( ErrorType_General, _("Expected ;") );
1035 				}while( true );
1036 			}
1037 			else
1038 			{
1039 				i -= word.get_length();
1040 				break;
1041 			}
1042 		}
1043 // All intern variable has been created
1044 
1045 
1046 		{
1047 			bool inside_string = false;
1048 			int nested_blocks = 0;
1049 			for(i_tmp=0;;i_tmp++) // getting the length of the function
1050 			{
1051 				if(str[i+i_tmp] == '\0')
1052 					THROW_ERROR( ErrorType_General, _("Expected }") );
1053 
1054 				if(inside_string)
1055 				{
1056 					if(str[i+i_tmp] == '\"')
1057 						inside_string = false;
1058 				}
1059 				else
1060 				{
1061 					if(str[i+i_tmp] == '{')
1062 						nested_blocks++;
1063 					else if(str[i+i_tmp] == '}')
1064 					{
1065 						if(nested_blocks == 0)
1066 							break;
1067 						else
1068 							nested_blocks--;
1069 					}
1070 					else if(str[i+i_tmp] == '\"')
1071 						inside_string = true;
1072 				}
1073 			}
1074 		}
1075 // Function body ended
1076 
1077 		for(int i2=0; i2 < i; i2++ ) // overwriting  "Function name(){" with spaces
1078 		{
1079 			if( str[i2] != '\n' )
1080 				str[i2] = ' ';
1081 		}
1082 
1083 	}
1084 	catch(error_obj error)
1085 	{
1086 		error_obj error2;
1087 		int linenr = 1;
1088 		error2.type = error.type;
1089 
1090 		delete func;
1091 
1092 		for( i_tmp=0; i_tmp < i; i_tmp++ )
1093 		{
1094 			if( str[i_tmp] == '\n' )
1095 				linenr++;
1096 		}
1097 
1098 		snprintf( error2.msg, ERROR_OBJ_MSG_LEN, "%s: %s %ld: %s", filename.c_str(), _("line"),
1099 		          linenr, error.msg );
1100 		throw error2;
1101 	}
1102 
1103 
1104 	str[i+i_tmp] = '\0'; // overwriting the ending '}'
1105 
1106 	try{  func->filename = filename;  }
1107 	catch(...) { } // don't care about this error, 'cause it just disables filename in error-msg
1108 
1109 	try
1110 	{
1111 // Temporary adding a dummy function (to be able to have recursive calls)
1112 		function_list.add_this_code_block_function( name, 0, arguments );
1113 
1114 		try{  func->set_code_intern( str, false );  }
1115 		catch(...)
1116 		{
1117 			function_list.delete_function( name );
1118 			throw;
1119 		}
1120 
1121 // Removing the dummy function added
1122 		function_list.delete_function( name );
1123 
1124 		function_list.add_this_code_block_function( name, func, arguments );
1125 	}
1126 	catch(...)
1127 	{
1128 		delete func;
1129 		throw;
1130 	}
1131 
1132 	for(int i2=i; i2 <= (i+i_tmp); i2++ ) // overwriting the whole function with spaces
1133 	{
1134 		if( str[i2] != '\n' )
1135 			str[i2] = ' ';
1136 	}
1137 
1138 	return true;
1139 }
1140 
add_line(const char * codeline,int linenr,bool allow_variable_creation,CodeBlock_SubType sub_type)1141 void CodeBlock::add_line(const char *codeline, int linenr, bool allow_variable_creation,
1142                          CodeBlock_SubType sub_type) throw(error_obj)
1143 {
1144 	CodeBlockLine *line = 0;
1145 
1146 	try
1147 	{
1148 		try
1149 		{
1150 			line = new CodeBlockLine;
1151 			lines.append_this( line );
1152 		}
1153 		catch(...)
1154 		{
1155 			delete line;
1156 			throw;
1157 		}
1158 
1159 		line->line_number = linenr;
1160 		line->type = CodeBlock_Type_Code;
1161 		line->sub_type = sub_type;
1162 
1163 		line->code = new CodeLine;
1164 		if( private_varlist.is_empty() )
1165 			line->code->set_code_line( codeline, allow_variable_creation, 0 );
1166 		else
1167 			line->code->set_code_line( codeline, allow_variable_creation, &private_varlist );
1168 
1169 		if( line->code->is_empty() )
1170 			lines.remove( lines.get_size()-1 );
1171 	}
1172 	catch(error_obj error) {  throw;  }
1173 	catch(...) {  THROW_ERROR( ErrorType_Memory, _("Couldn't get memory.") );  }
1174 }
1175 
add_curley(CodeBlock_Type type,CodeBlock_SubType sub_type,int pos)1176 void CodeBlock::add_curley(CodeBlock_Type type, CodeBlock_SubType sub_type, int pos) throw(error_obj)
1177 {
1178 	CodeBlockLine *line = 0;
1179 
1180 	try
1181 	{
1182 		try
1183 		{
1184 			line = new CodeBlockLine;
1185 			lines.insert_this( line, pos );
1186 		}
1187 		catch(...)
1188 		{
1189 			delete line;
1190 			throw;
1191 		}
1192 
1193 		line->type = type;
1194 		line->sub_type = sub_type;
1195 		line->pair_with = -1;
1196 	}
1197 	catch(...) {  THROW_ERROR( ErrorType_Memory, _("Couldn't get memory.") );  }
1198 }
1199 
add_void(CodeBlock_SubType sub_type)1200 void CodeBlock::add_void( CodeBlock_SubType sub_type ) throw(error_obj)
1201 {
1202 	CodeBlockLine *line = 0;
1203 
1204 	try
1205 	{
1206 		try
1207 		{
1208 			line = new CodeBlockLine;
1209 			lines.append_this( line );
1210 		}
1211 		catch(...)
1212 		{
1213 			delete line;
1214 			throw;
1215 		}
1216 
1217 		line->type = CodeBlock_Type_Void;
1218 		line->sub_type = sub_type;
1219 	}
1220 	catch(...) {  THROW_ERROR( ErrorType_Memory, _("Couldn't get memory.") );  }
1221 }
1222 
run_block(Vector<Variable> * tmp_variables,VariableList * this_private_varlist,CodeBlock_SubType & ret,Variable * ans,int & start,bool expect_curley_ending) const1223 void CodeBlock::run_block( Vector<Variable> *tmp_variables, VariableList *this_private_varlist, CodeBlock_SubType &ret,
1224                            Variable *ans, int &start, bool expect_curley_ending ) const throw(error_obj)
1225 {
1226 	Variable *tmp;
1227 
1228 	for( ; start<lines.get_size(); start++ )
1229 	{
1230 		switch( lines[start]->type )
1231 		{
1232 		case CodeBlock_Type_Code:
1233 			if( start == (lines.get_size()-1) )
1234 				tmp = ans;
1235 			else
1236 			{
1237 				switch( lines[start]->sub_type )
1238 				{
1239 				case CodeBlock_SubType_If:
1240 				case CodeBlock_SubType_While:
1241 				case CodeBlock_SubType_Return:
1242 					tmp = ans;
1243 					break;
1244 				default:
1245 					tmp = 0;
1246 				}
1247 			}
1248 			lines[start]->code->calc( tmp, this_private_varlist, tmp_variables );
1249 
1250 			if( tmp != 0 )
1251 			{
1252 				switch( lines[start]->sub_type )
1253 				{
1254 				case CodeBlock_SubType_Return:
1255 					ret = CodeBlock_SubType_Return;
1256 					return;
1257 
1258 				case CodeBlock_SubType_While:
1259 					{
1260 						int tmp;
1261 
1262 						if( ans->get_type() != VariableType_Boolean )
1263 						{
1264 							char tmp_err[ERROR_OBJ_MSG_LEN];
1265 							snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Data type is unsupported by '%s'"), "while" );
1266 							THROW_ERROR( ErrorType_General, tmp_err );
1267 						}
1268 
1269 						if( ans->get_boolean() )
1270 						{  // lets run the 'while'-block
1271 							tmp = start;
1272 							start += 2;
1273 							run_block( tmp_variables, this_private_varlist, ret, ans, start, true );
1274 							switch( ret )
1275 							{
1276 							case CodeBlock_SubType_None:
1277 							case CodeBlock_SubType_Continue:
1278 								start = tmp-1;
1279 								break;
1280 
1281 							case CodeBlock_SubType_Break:
1282 								start = lines[tmp+1]->pair_with;
1283 								break;
1284 
1285 							case CodeBlock_SubType_Return:
1286 								return;
1287 							}
1288 							ret = CodeBlock_SubType_None;
1289 						}
1290 						else  // lets skip the 'while'-block
1291 							start = lines[start+1]->pair_with;
1292 					}
1293 					break;
1294 
1295 				case CodeBlock_SubType_If:
1296 					if( ans->get_type() != VariableType_Boolean )
1297 					{
1298 						char tmp_err[ERROR_OBJ_MSG_LEN];
1299 						snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Data type is unsupported by '%s'"), "if" );
1300 						THROW_ERROR( ErrorType_General, tmp_err );
1301 					}
1302 
1303 					if( ans->get_boolean() )
1304 					{  // lets run the 'if'-block
1305 						start += 2;
1306 						run_block( tmp_variables, this_private_varlist, ret, ans, start, true );
1307 						if( ret != CodeBlock_SubType_None )
1308 							return;
1309 // 'if' was followed by 'else', lets skip that block
1310 						if( lines.get_size() > start+2 )
1311 							if( lines[start+1]->sub_type == CodeBlock_SubType_Else )
1312 								start = lines[start+2]->pair_with;
1313 					}
1314 					else
1315 					{  // lets skip the 'if'-block
1316 						start = lines[start+1]->pair_with;
1317 // 'if' was followed by 'else', lets run that block
1318 						if( lines.get_size() > start+1 )
1319 						{
1320 							if( lines[start+1]->sub_type == CodeBlock_SubType_Else )
1321 							{
1322 								start += 3; // this skips     }    else    {
1323 								run_block( tmp_variables, this_private_varlist, ret, ans, start, true );
1324 								if( ret != CodeBlock_SubType_None )
1325 									return;
1326 							}
1327 						}
1328 					}
1329 					break;
1330 
1331 				default:
1332 					break;
1333 				}
1334 			}
1335 			break;
1336 
1337 		case CodeBlock_Type_Start:
1338 			start++;
1339 			run_block( tmp_variables, this_private_varlist, ret, ans, start, true );
1340 			if( ret != CodeBlock_SubType_None )
1341 				return;
1342 			break;
1343 
1344 		case CodeBlock_Type_Stop:
1345 			if( expect_curley_ending )
1346 				return;
1347 			THROW_ERROR( ErrorType_General, _("Unexpected }") );
1348 
1349 		case CodeBlock_Type_Void:
1350 			switch( lines[start]->sub_type )
1351 			{
1352 			case CodeBlock_SubType_Return:
1353 				ans->set_void();
1354 				ret = CodeBlock_SubType_Return;
1355 				return;
1356 
1357 			case CodeBlock_SubType_Break:
1358 				ret = CodeBlock_SubType_Break;
1359 				return;
1360 
1361 			case CodeBlock_SubType_Continue:
1362 				ret = CodeBlock_SubType_Continue;
1363 				return;
1364 
1365 			case CodeBlock_SubType_None:
1366 				break;
1367 
1368 			default:
1369 				printf("not yet implemented CodeBlock_Type_Void with sub_type: %ld\n", lines[start]->sub_type);
1370 			}
1371 			break;
1372 		}
1373 	}
1374 }
1375 
1376 } // namespace math
1377