/*
	Copyright 2010-2011 Leszek Dubiel <leszek@dubiel.pl>

	This file is part of Treep.

	Treep is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	Treep is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Treep. If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <ctype.h>
#include <math.h>
#include <float.h>
#include <string.h>
#include <limits.h>
#include <regex.h>
#include <gc/gc.h>
#include "tree.h"
#include "data.h"

/* global variable that holds information about all functions */
struct tree *func = 0;

/* this is a tree that is accessible by "main" function */
struct tree *tree = 0;

/* normalize number, round it do display precision */
long double numb_norm(long double x)
{
	long double y;
	char t[LDBL_DIG + 3];	/* sign + dot + LDBL_DIG digits + null char */
	if (fabsl(x) <= powl(10.0L, 1 - LDBL_DIG) * 0.5L) {
		return 0.0L;
	}
	if (fabsl(x) > powl(10.0L, LDBL_DIG - 1) - 0.1L + 0.05L) {
		fputs("Number out of range.\n", stderr);
		exit(EXIT_FAILURE);
	}
	sprintf(t, "%+.*Lf", LDBL_DIG - 1 - (fabsl(x) < 1.0L ? 0 : (int) log10l(floorl(fabsl(x)))), x);
	sscanf(t, "%Lf", &y);
	return y;
}

/* number to text */
char *numb_text(long double x)
{
	size_t i;
	char *t;
	if (fabsl(x) <= powl(10.0L, 1 - LDBL_DIG) * 0.5L) {
		return "0.0";
	}
	if (fabsl(x) > powl(10.0L, LDBL_DIG - 1) - 0.1L + 0.05L) {
		fputs("Can not convert number to text. Number out of range.\n", stderr);
		exit(EXIT_FAILURE);
	}
	/* allocate memory for string -- sign + dot + LDBL_DIG digits + null character; then print to this buffer; then cut extra zeros */
	t = GC_malloc(LDBL_DIG + 3);
	if (t == 0) {
		fputs("Out of memory.\n", stderr);
		exit(EXIT_FAILURE);
	}
	sprintf(t, "%+.*Lf", LDBL_DIG - 1 - (fabsl(x) < 1.0L ? 0 : (int) log10l(floorl(fabsl(x)))), x);
	for (i = LDBL_DIG + 2; t[i - 1] == '0' && t[i - 2] != '.'; i--)
		t[i - 1] = '\0';
	return t;
}

/* text to number */
long double text_numb(char *t)
{
	long double x;
	size_t i = 0;
	if (t == 0) {
		fputs("Null pointer.\n", stderr);
		exit(EXIT_FAILURE);
	}
	if (t[i] == '0') {
		/* zero */
		i++;
		/* dot */
		if (t[i] != '.') {
			fputs("Can not convert text to number. Dot expected.\n", stderr);
			exit(EXIT_FAILURE);
		}
		i++;
		/* zero */
		if (t[i] != '0') {
			fputs("Can not convert text to number. Zero expected.\n", stderr);
			exit(EXIT_FAILURE);
		}
		i++;
		/* check for extra digit */
		if (isdigit(t[i])) {
			fputs("Can not convert text to number. Unexpected digit.\n", stderr);
			exit(EXIT_FAILURE);
		}
	} else if (t[i] == '+' || t[i] == '-') {
		/* sign */
		i++;
		if (t[i] == '0') {
			/* zero */
			i++;
			/* dot */
			if (t[i] != '.') {
				fputs("Can not convert text to number. Dot expected.\n", stderr);
				exit(EXIT_FAILURE);
			}
			i++;
			/* set of digits, not empty, last not zero */
			do {
				while (t[i] == '0') {
					i++;
					if (i == LDBL_DIG + 3) {
						fputs("Can not convert text to number. Unexpected digit.\n", stderr);
						exit(EXIT_FAILURE);
					}
				}
				if (!isdigit(t[i])) {
					fputs("Can not convert text to number. Digit expected.\n", stderr);
					exit(EXIT_FAILURE);
				}
				i++;
				if (i == LDBL_DIG + 3) {
					fputs("Can not convert text to number. Unexpected digit.\n", stderr);
					exit(EXIT_FAILURE);
				}
			} while (isdigit(t[i]));
		} else if (isdigit(t[i])) {
			/* set of digits, not empty, first not zero */
			do {
				i++;
				if (i == LDBL_DIG + 1) {
					fputs("Can not convert text to number. Unexpected digit.\n", stderr);
					exit(EXIT_FAILURE);
				}
			} while (isdigit(t[i]));
			/* dot */
			if (t[i] != '.') {
				fputs("Can not convert text to number. Dot expected.\n", stderr);
				exit(EXIT_FAILURE);
			}
			i++;
			if (i == LDBL_DIG + 2) {
				fputs("Can not convert text to number. Unexpected digit.\n", stderr);
				exit(EXIT_FAILURE);
			}
			/* first digit after dot, maybe zero */
			if (!isdigit(t[i])) {
				fputs("Can not convert text to number. Digit expected.\n", stderr);
				exit(EXIT_FAILURE);
			}
			i++;
			if (i == LDBL_DIG + 3) {
				fputs("Can not convert text to number. Unexpected digit.\n", stderr);
				exit(EXIT_FAILURE);
			}
			/* set of digits, maybe empty, last not zero */
			while (isdigit(t[i])) {
				while (t[i] == '0') {
					i++;
					if (i == LDBL_DIG + 3) {
						fputs("Can not convert text to number. Unexpected digit.\n", stderr);
						exit(EXIT_FAILURE);
					}
				}
				if (!isdigit(t[i])) {
					fputs("Can not convert text to number. Digit expected.\n", stderr);
					exit(EXIT_FAILURE);
				}
				i++;
				if (i == LDBL_DIG + 3) {
					fputs("Can not convert text to number. Unexpected digit.\n", stderr);
					exit(EXIT_FAILURE);
				}
			}
		}
	} else {
		fputs("Can not convert text to number. Zero, plus or minus expected.\n", stderr);
		exit(EXIT_FAILURE);
	}
	if (t[i] != '\0') {
		fputs("Can not convert text to number. Unexpected character.\n", stderr);
		exit(EXIT_FAILURE);
	}
	/* read number and return it */
	sscanf(t, "%Lf", &x);
	return x;
}

/* read number from stdin */
long double numb_read(void)
{
	long double x;
	char t[LDBL_DIG + 3];	/* sign + dot + LDBL_DIG digits + null char */
	size_t i = 0;
	t[i] = fgetc(stdin);
	if (feof(stdin) || ferror(stdin)) {
		fputs("Input error.\n", stderr);
		exit(EXIT_FAILURE);
	}
	if (t[i] == '0') {
		/* zero */
		i++;
		t[i] = fgetc(stdin);
		if (feof(stdin) || ferror(stdin)) {
			fputs("Input error.\n", stderr);
			exit(EXIT_FAILURE);
		}
		/* dot */
		if (t[i] != '.') {
			fputs("Can not read number. Dot expected.\n", stderr);
			exit(EXIT_FAILURE);
		}
		i++;
		t[i] = fgetc(stdin);
		if (feof(stdin) || ferror(stdin)) {
			fputs("Input error.\n", stderr);
			exit(EXIT_FAILURE);
		}
		/* zero */
		if (t[i] != '0') {
			fputs("Can not read number. Zero expected.\n", stderr);
			exit(EXIT_FAILURE);
		}
		i++;
		t[i] = fgetc(stdin);
		if (feof(stdin) || ferror(stdin)) {
			fputs("Input error.\n", stderr);
			exit(EXIT_FAILURE);
		}
		/* check for extra digit */
		if (isdigit(t[i])) {
			fputs("Can not read number. Unexpected digit.\n", stderr);
			exit(EXIT_FAILURE);
		}
	} else if (t[i] == '+' || t[i] == '-') {
		/* sign */
		i++;
		t[i] = fgetc(stdin);
		if (feof(stdin) || ferror(stdin)) {
			fputs("Input error.\n", stderr);
			exit(EXIT_FAILURE);
		}
		if (t[i] == '0') {
			/* zero */
			i++;
			t[i] = fgetc(stdin);
			if (feof(stdin) || ferror(stdin)) {
				fputs("Input error.\n", stderr);
				exit(EXIT_FAILURE);
			}
			/* dot */
			if (t[i] != '.') {
				fputs("Can not read number. Dot expected.\n", stderr);
				exit(EXIT_FAILURE);
			}
			i++;
			t[i] = fgetc(stdin);
			if (feof(stdin) || ferror(stdin)) {
				fputs("Input error.\n", stderr);
				exit(EXIT_FAILURE);
			}
			/* set of digits, not empty, last not zero */
			do {
				while (t[i] == '0') {
					i++;
					if (i == LDBL_DIG + 2) {
						fputs("Can not read number. Unexpected digit.\n", stderr);
						exit(EXIT_FAILURE);
					}
					t[i] = fgetc(stdin);
					if (feof(stdin) || ferror(stdin)) {
						fputs("Input error.\n", stderr);
						exit(EXIT_FAILURE);
					}
				}
				if (!isdigit(t[i])) {
					fputs("Can not read number. Digit expected.\n", stderr);
					exit(EXIT_FAILURE);
				}
				i++;
				if (i == LDBL_DIG + 3) {
					fputs("Can not read number. Unexpected digit.\n", stderr);
					exit(EXIT_FAILURE);
				}
				t[i] = fgetc(stdin);
				if (feof(stdin) || ferror(stdin)) {
					fputs("Input error.\n", stderr);
					exit(EXIT_FAILURE);
				}
			} while (isdigit(t[i]));
		} else if (isdigit(t[i])) {
			/* set of digits, not empty, first not zero */
			do {
				i++;
				if (i == LDBL_DIG + 1) {
					fputs("Can not read number. Unexpected digit.\n", stderr);
					exit(EXIT_FAILURE);
				}
				t[i] = fgetc(stdin);
				if (feof(stdin) || ferror(stdin)) {
					fputs("Input error.\n", stderr);
					exit(EXIT_FAILURE);
				}
			} while (isdigit(t[i]));
			/* dot */
			if (t[i] != '.') {
				fputs("Can not read number. Dot expected.\n", stderr);
				exit(EXIT_FAILURE);
			}
			i++;
			if (i == LDBL_DIG + 2) {
				fputs("Can not read number. Unexpected digit.\n", stderr);
				exit(EXIT_FAILURE);
			}
			t[i] = fgetc(stdin);
			if (feof(stdin) || ferror(stdin)) {
				fputs("Input error.\n", stderr);
				exit(EXIT_FAILURE);
			}
			/* first digit after dot, maybe zero */
			if (!isdigit(t[i])) {
				fputs("Can not read number. Digit expected.\n", stderr);
				exit(EXIT_FAILURE);
			}
			i++;
			if (i == LDBL_DIG + 3) {
				fputs("Can not read number. Unexpected digit.\n", stderr);
				exit(EXIT_FAILURE);
			}
			t[i] = fgetc(stdin);
			if (feof(stdin) || ferror(stdin)) {
				fputs("Input error.\n", stderr);
				exit(EXIT_FAILURE);
			}
			/* set of digits, maybe empty, last not zero */
			while (isdigit(t[i])) {
				while (t[i] == '0') {
					i++;
					if (i == LDBL_DIG + 3) {
						fputs("Can not read number. Unexpected digit.\n", stderr);
						exit(EXIT_FAILURE);
					}
					t[i] = fgetc(stdin);
					if (feof(stdin) || ferror(stdin)) {
						fputs("Input error.\n", stderr);
						exit(EXIT_FAILURE);
					}
				}
				if (!isdigit(t[i])) {
					fputs("Can not read number. Digit expected.\n", stderr);
					exit(EXIT_FAILURE);
				}
				i++;
				if (i == LDBL_DIG + 3) {
					fputs("Can not read number. Unexpected digit.\n", stderr);
					exit(EXIT_FAILURE);
				}
				t[i] = fgetc(stdin);
				if (feof(stdin) || ferror(stdin)) {
					fputs("Input error.\n", stderr);
					exit(EXIT_FAILURE);
				}
			}
		}
	} else {
		fputs("Can not read number. Zero, plus or minus expected.\n", stderr);
		exit(EXIT_FAILURE);
	}
	ungetc(t[i], stdin);
	/* read number and return it */
	sscanf(t, "%Lf", &x);
	return x;
}

/* write number to stdout */
void numb_wrte(long double x)
{
	size_t i;
	char t[LDBL_DIG + 3];	/* sign + dot + LDBL_DIG digits + null char */
	if (fabsl(x) <= powl(10.0L, 1 - LDBL_DIG) * 0.5L) {
		fputs("0.0", stdout);
		if (feof(stdout) || ferror(stdout)) {
			fputs("Output error.\n", stderr);
			exit(EXIT_FAILURE);
		}
		return;
	}
	if (fabsl(x) > powl(10.0L, LDBL_DIG - 1) - 0.1L + 0.05L) {
		fputs("Can not write number. Number out of range.\n", stderr);
		exit(EXIT_FAILURE);
	}
	sprintf(t, "%+.*Lf", LDBL_DIG - 1 - (fabsl(x) < 1.0L ? 0 : (int) log10l(floorl(fabsl(x)))), x);
	for (i = LDBL_DIG + 2; t[i - 1] == '0' && t[i - 2] != '.'; i--)
		t[i - 1] = '\0';
	fputs(t, stdout);
	if (feof(stdout) || ferror(stdout)) {
		fputs("Output error.\n", stderr);
		exit(EXIT_FAILURE);
	}
}

/* read name from stdin */
char *name_read(void)
{
	size_t i = 0, l = 4096;
	char *b;
	int c;

	b = GC_malloc(l);
	if (b == 0) {
		fputs("Out of memory.\n", stderr);
		exit(EXIT_FAILURE);
	}

	c = fgetc(stdin);
	if (feof(stdin) || ferror(stdin)) {
		fputs("Input error.\n", stderr);
		exit(EXIT_FAILURE);
	}
	if (!isalpha(c)) {
		fputs("Can not read name. Letter expected.\n", stderr);
		exit(EXIT_FAILURE);
	}
	b[i] = c;
	i++;

	c = fgetc(stdin);
	if (feof(stdin) || ferror(stdin)) {
		fputs("Input error.\n", stderr);
		exit(EXIT_FAILURE);
	}
	while (c == '_' || isalnum(c)) {

		if (c == '_') {
			if (i == l) {
				if (l > SIZE_MAX / 2) {
					fputs("Limit reached.\n", stderr);
					exit(EXIT_FAILURE);
				}
				l *= 2;
				b = GC_realloc(b, l);
				if (b == 0) {
					fputs("Out of memory.\n", stderr);
					exit(EXIT_FAILURE);
				}
			}
			b[i] = c;
			i++;
			c = fgetc(stdin);
			if (feof(stdin) || ferror(stdin)) {
				fputs("Input error.\n", stderr);
				exit(EXIT_FAILURE);
			}
			if (!isalnum(c)) {
				fputs("Can not read name. Letter or digit expected.\n", stderr);
				exit(EXIT_FAILURE);
			}
		}

		if (i == l) {
			if (l > SIZE_MAX / 2) {
				fputs("Limit reached.\n", stderr);
				exit(EXIT_FAILURE);
			}
			l *= 2;
			b = GC_realloc(b, l);
			if (b == 0) {
				fputs("Out of memory.\n", stderr);
				exit(EXIT_FAILURE);
			}
		}
		b[i] = c;
		i++;

		c = fgetc(stdin);
		if (feof(stdin) || ferror(stdin)) {
			fputs("Input error.\n", stderr);
			exit(EXIT_FAILURE);
		}
	}
	ungetc(c, stdin);
	if (feof(stdin) || ferror(stdin)) {
		fputs("Input error.\n", stderr);
		exit(EXIT_FAILURE);
	}

	if (i == SIZE_MAX) {
		fputs("Limit reached.\n", stderr);
		exit(EXIT_FAILURE);
	}
	b = GC_realloc(b, i + 1);
	if (b == 0) {
		fputs("Out of memory.\n", stderr);
		exit(EXIT_FAILURE);
	}
	b[i] = '\0';

	return b;
}

/* write name to stdout */
void name_wrte(char *b)
{
	if (b == 0) {
		fputs("Null pointer.\n", stderr);
		exit(EXIT_FAILURE);
	}
	if (!isalpha(*b)) {
		fputs("Can not write name. Letter expected.\n", stderr);
		exit(EXIT_FAILURE);
	}
	while (*b != '\0') {
		if (*b == '_') {
			fputc(*b, stdout);
			if (feof(stdout) || ferror(stdout)) {
				fputs("Output error.\n", stderr);
				exit(EXIT_FAILURE);
			}
			b++;
		}
		if (!isalnum(*b)) {
			fputs("Can not write name. Letter or digit expected.\n", stderr);
			exit(EXIT_FAILURE);
		}
		fputc(*b, stdout);
		if (feof(stdout) || ferror(stdout)) {
			fputs("Output error.\n", stderr);
			exit(EXIT_FAILURE);
		}
		b++;
	}
}

/* read text from stdin */
char *text_read(void)
{
	size_t i = 0, l = 4096;
	char *b;
	int c;

	b = GC_malloc(l);
	if (b == 0) {
		fputs("Out of memory.\n", stderr);
		exit(EXIT_FAILURE);
	}

	c = fgetc(stdin);
	if (feof(stdin) || ferror(stdin)) {
		fputs("Input error.\n", stderr);
		exit(EXIT_FAILURE);
	}
	if (c != '"') {
		fputs("Can not read text. Quotation expected.\n", stderr);
		exit(EXIT_FAILURE);
	}

	c = fgetc(stdin);
	if (feof(stdin) || ferror(stdin)) {
		fputs("Input error.\n", stderr);
		exit(EXIT_FAILURE);
	}
	while (c != '"') {
		if (!isprint(c)) {
			fputs("Can not read text. Nonprintable character.\n", stderr);
			exit(EXIT_FAILURE);
		}
		if (c == '\\') {
			c = fgetc(stdin);
			if (feof(stdin) || ferror(stdin)) {
				fputs("Input error.\n", stderr);
				exit(EXIT_FAILURE);
			}
			if (c != '"' && c != '\\') {
				fputs("Can not read text. Wrong escape sequence.\n", stderr);
				exit(EXIT_FAILURE);
			}
		}
		if (i == l) {
			if (l > SIZE_MAX / 2) {
				fputs("Limit reached.\n", stderr);
				exit(EXIT_FAILURE);
			}
			l *= 2;
			b = GC_realloc(b, l);
			if (b == 0) {
				fputs("Out of memory.\n", stderr);
				exit(EXIT_FAILURE);
			}
		}
		b[i] = c;
		i++;
		c = fgetc(stdin);
		if (feof(stdin) || ferror(stdin)) {
			fputs("Input error.\n", stderr);
			exit(EXIT_FAILURE);
		}
	}

	if (i == SIZE_MAX) {
		fputs("Limit reached.\n", stderr);
		exit(EXIT_FAILURE);
	}
	b = GC_realloc(b, i + 1);
	if (b == 0) {
		fputs("Out of memory.\n", stderr);
		exit(EXIT_FAILURE);
	}
	b[i] = '\0';

	return b;
}

/* write text to stdout */
void text_wrte(char *b)
{
	if (b == 0) {
		fputs("Null pointer.\n", stderr);
		exit(EXIT_FAILURE);
	}
	fputc('"', stdout);
	if (feof(stdout) || ferror(stdout)) {
		fputs("Output error.\n", stderr);
		exit(EXIT_FAILURE);
	}
	while (*b != '\0') {
		if (!isprint(*b)) {
			fputs("Can not write text. Nonprintable character.\n", stderr);
			exit(EXIT_FAILURE);
		}
		if (*b == '"' || *b == '\\') {
			fputc('\\', stdout);
			if (feof(stdout) || ferror(stdout)) {
				fputs("Output error.\n", stderr);
				exit(EXIT_FAILURE);
			}
		}
		fputc(*b, stdout);
		if (feof(stdout) || ferror(stdout)) {
			fputs("Output error.\n", stderr);
			exit(EXIT_FAILURE);
		}
		b++;
	}
	fputc('"', stdout);
	if (feof(stdout) || ferror(stdout)) {
		fputs("Output error.\n", stderr);
		exit(EXIT_FAILURE);
	}
}

/* read value from stdin */
struct vlue *vlue_read(void)
{
	struct vlue *v;
	int c;

	v = GC_malloc(sizeof(struct vlue));
	if (v == 0) {
		fputs("Out of memory.\n", stderr);
		exit(EXIT_FAILURE);
	}

	c = fgetc(stdin);
	if (ferror(stdin)) {
		fputs("Input error.\n", stderr);
		exit(EXIT_FAILURE);
	}
	if (feof(stdin)) {
		v->type = VOID;
	} else if (c == ' ') {
		v->type = SPAC;
	} else if (c == '\t') {
		v->type = TABU;
	} else if (c == '\n') {
		v->type = LINE;
	} else if (c == '(') {
		v->type = LPAR;
	} else if (c == ')') {
		v->type = RPAR;
	} else if (c == '0' || c == '+' || c == '-') {
		ungetc(c, stdin);
		if (feof(stdin) || ferror(stdin)) {
			fputs("Input error.\n", stderr);
			exit(EXIT_FAILURE);
		}
		v->type = NUMB;
		v->data.numb = numb_read();
	} else if (isalpha(c)) {
		ungetc(c, stdin);
		if (feof(stdin) || ferror(stdin)) {
			fputs("Input error.\n", stderr);
			exit(EXIT_FAILURE);
		}
		v->type = NAME;
		v->data.name = name_read();
	} else if (c == '"') {
		ungetc(c, stdin);
		if (feof(stdin) || ferror(stdin)) {
			fputs("Input error.\n", stderr);
			exit(EXIT_FAILURE);
		}
		v->type = TEXT;
		v->data.text = text_read();
	} else {
		fputs("Can not read value. Unexpected character.\n", stderr);
		exit(EXIT_FAILURE);
	}

	return v;
}

/* write value to stdout */
void vlue_wrte(struct vlue *v)
{
	if (v->type == VOID) {
		/* write nothing */
	} else if (v->type == SPAC) {
		fputc(' ', stdout);
		if (feof(stdout) || ferror(stdout)) {
			fputs("Output error.\n", stderr);
			exit(EXIT_FAILURE);
		}
	} else if (v->type == TABU) {
		fputc('\t', stdout);
		if (feof(stdout) || ferror(stdout)) {
			fputs("Output error.\n", stderr);
			exit(EXIT_FAILURE);
		}
	} else if (v->type == LINE) {
		fputc('\n', stdout);
		if (feof(stdout) || ferror(stdout)) {
			fputs("Output error.\n", stderr);
			exit(EXIT_FAILURE);
		}
	} else if (v->type == LPAR) {
		fputc('(', stdout);
		if (feof(stdout) || ferror(stdout)) {
			fputs("Output error.\n", stderr);
			exit(EXIT_FAILURE);
		}
	} else if (v->type == RPAR) {
		fputc(')', stdout);
		if (feof(stdout) || ferror(stdout)) {
			fputs("Output error.\n", stderr);
			exit(EXIT_FAILURE);
		}
	} else if (v->type == NUMB) {
		numb_wrte(v->data.numb);
	} else if (v->type == NAME) {
		name_wrte(v->data.name);
	} else if (v->type == TEXT) {
		text_wrte(v->data.text);
	} else if (v->type == BOOL || v->type == TREE || v->type == NODE) {
		fputs("Can not write value. Wrong type.\n", stderr);
		exit(EXIT_FAILURE);
	} else {
		fputs("Wrong data.\n", stderr);
		exit(EXIT_FAILURE);
	}
}

/* read expression from stdin */
struct expr *expr_read(void)
{
	/* ev (vector), es (size), et (top) is a stack that keeps track where to read expression */
	struct expr *expr, ***ev;
	size_t es = 4096, et = 0;
	/* lv tells the the number of nested function calls */
	unsigned int lv = 0;
	int c;

	ev = GC_malloc(es * sizeof(struct expr **));
	if (ev == 0) {
		fputs("Out of memory.\n", stderr);
		exit(EXIT_FAILURE);
	}
	ev[et] = &expr;
	et++;

	while (et != 0) {

		c = fgetc(stdin);
		if (feof(stdin) || ferror(stdin)) {
			fputs("Input error.\n", stderr);
			exit(EXIT_FAILURE);
		}
		if (lv != 0)
			while (c == ' ' || c == '\t' || c == '\n') {
				c = fgetc(stdin);
				if (feof(stdin) || ferror(stdin)) {
					fputs("Input error.\n", stderr);
					exit(EXIT_FAILURE);
				}
			}

		if (c == '(') {
			*ev[et - 1] = GC_malloc(sizeof(struct expr));
			if (*ev[et - 1] == 0) {
				fputs("Out of memory.\n", stderr);
				exit(EXIT_FAILURE);
			}
			(*ev[et - 1])->type = CALL;

			if (lv == 0) {
				(*ev[et - 1])->next = 0;
				ev[et - 1] = &(*ev[et - 1])->data.call;
			} else {
				if (et == es) {
					if (es > SIZE_MAX / 2 / sizeof(struct expr **)) {
						fputs("Limit reached.\n", stderr);
						exit(EXIT_FAILURE);
					}
					es *= 2;
					ev = GC_realloc(ev, es * sizeof(struct expr **));
					if (ev == 0) {
						fputs("Out of memory.\n", stderr);
						exit(EXIT_FAILURE);
					}
				}
				ev[et] = &(*ev[et - 1])->data.call;
				ev[et - 1] = &(*ev[et - 1])->next;
				et++;
			}
			if (lv == UINT_MAX) {
				fputs("Limit reached.\n", stderr);
				exit(EXIT_FAILURE);
			}
			lv++;

			c = fgetc(stdin);
			if (feof(stdin) || ferror(stdin)) {
				fputs("Input error.\n", stderr);
				exit(EXIT_FAILURE);
			}
			while (c == ' ' || c == '\t' || c == '\n') {
				c = fgetc(stdin);
				if (feof(stdin) || ferror(stdin)) {
					fputs("Input error.\n", stderr);
					exit(EXIT_FAILURE);
				}
			}
			if (c == ')') {
				fputs("Can not read expression. Unexpected parenthesis.\n", stderr);
				exit(EXIT_FAILURE);
			}
			ungetc(c, stdin);
			if (feof(stdin) || ferror(stdin)) {
				fputs("Input error.\n", stderr);
				exit(EXIT_FAILURE);
			}

		} else if (c == ')') {
			*ev[et - 1] = 0;
			et--;
			if (lv == 0) {
				fputs("Can not read expression. Unexpected parenthesis.\n", stderr);
				exit(EXIT_FAILURE);
			}
			lv--;

		} else if (c == '0' || c == '+' || c == '-' || isalpha(c) || c == '"') {
			ungetc(c, stdin);
			if (feof(stdin) || ferror(stdin)) {
				fputs("Input error.\n", stderr);
				exit(EXIT_FAILURE);
			}
			*ev[et - 1] = GC_malloc(sizeof(struct expr));
			if (*ev[et - 1] == 0) {
				fputs("Out of memory.\n", stderr);
				exit(EXIT_FAILURE);
			}
			(*ev[et - 1])->type = VLUE;
			(*ev[et - 1])->data.vlue = vlue_read();
			if (lv == 0) {
				(*ev[et - 1])->next = 0;
				et--;
			} else {
				ev[et - 1] = &(*ev[et - 1])->next;
			}

		} else {
			fputs("Can not read expression. Unexpected character.\n", stderr);
			exit(EXIT_FAILURE);
		}
	}

	return expr;
}

/* write expression to stdout */
void expr_wrte(struct expr *e)
{
	/* ev (vector), es (size) and et (top) is a stack that points what to write */
	struct expr **ev;
	size_t es = 4096, et = 0;
	/* lv (level) tells how many calls were nested */
	unsigned int lv = 0;

	if (e == 0) {
		fputs("Null pointer.\n", stderr);
		exit(EXIT_FAILURE);
	}

	ev = GC_malloc(es * sizeof(struct expr *));
	if (ev == 0) {
		fputs("Out of memory.\n", stderr);
		exit(EXIT_FAILURE);
	}
	ev[et] = e;
	et++;

	while (et != 0) {

		if (ev[et - 1] == 0) {
			fputc(')', stdout);
			if (feof(stdout) || ferror(stdout)) {
				fputs("Output error.\n", stderr);
				exit(EXIT_FAILURE);
			}
			et--;
			lv--;
			if (lv != 0 && ev[et - 1] != 0) {
				fputc(' ', stdout);
				if (feof(stdout) || ferror(stdout)) {
					fputs("Output error.\n", stderr);
					exit(EXIT_FAILURE);
				}
			}

		} else if (ev[et - 1]->type == CALL) {
			fputc('(', stdout);
			if (feof(stdout) || ferror(stdout)) {
				fputs("Output error.\n", stderr);
				exit(EXIT_FAILURE);
			}
			if (lv == 0) {
				ev[et - 1] = ev[et - 1]->data.call;
			} else {
				if (et == es) {
					if (es > SIZE_MAX / 2 / sizeof(struct expr *)) {
						fputs("Limit reached.\n", stderr);
						exit(EXIT_FAILURE);
					}
					es *= 2;
					ev = GC_realloc(ev, es * sizeof(struct expr *));
					if (ev == 0) {
						fputs("Out of memory.\n", stderr);
						exit(EXIT_FAILURE);
					}
				}
				ev[et] = ev[et - 1]->data.call;
				ev[et - 1] = ev[et - 1]->next;
				et++;
			}
			if (lv == UINT_MAX) {
				fputs("Limit reached.\n", stderr);
				exit(EXIT_FAILURE);
			}
			lv++;

		} else if (ev[et - 1]->type == VLUE) {
			vlue_wrte(ev[et - 1]->data.vlue);
			if (lv != 0) {
				ev[et - 1] = ev[et - 1]->next;
				if (ev[et - 1] != 0) {
					fputc(' ', stdout);
					if (feof(stdout) || ferror(stdout)) {
						fputs("Output error.\n", stderr);
						exit(EXIT_FAILURE);
					}
				}
			} else {
				et--;
			}

		} else {
			fputs("Wrong data.\n", stderr);
			exit(EXIT_FAILURE);
		}
	}
}

/* read function from stdin */
struct func *func_read(void)
{
	struct func *f;
	size_t i;
	int c;

	f = GC_malloc(sizeof(struct func));
	if (f == 0) {
		fputs("Out of memory.\n", stderr);
		exit(EXIT_FAILURE);
	}
	f->type = USER;

	f->name = name_read();

	f->body.user.parc = 0;
	c = fgetc(stdin);
	if (feof(stdin) || ferror(stdin)) {
		fputs("Input error.\n", stderr);
		exit(EXIT_FAILURE);
	}
	while (c == ' ' || c == '\t' || c == '\n') {
		c = fgetc(stdin);
		if (feof(stdin) || ferror(stdin)) {
			fputs("Input error.\n", stderr);
			exit(EXIT_FAILURE);
		}
	}
	if (c != '(') {
		fputs("Can not read function. Parenthesis expected.\n", stderr);
		exit(EXIT_FAILURE);
	}
	c = fgetc(stdin);
	if (feof(stdin) || ferror(stdin)) {
		fputs("Input error.\n", stderr);
		exit(EXIT_FAILURE);
	}
	while (c == ' ' || c == '\t' || c == '\n') {
		c = fgetc(stdin);
		if (feof(stdin) || ferror(stdin)) {
			fputs("Input error.\n", stderr);
			exit(EXIT_FAILURE);
		}
	}
	while (c != ')') {
		ungetc(c, stdin);
		if (feof(stdin) || ferror(stdin)) {
			fputs("Input error.\n", stderr);
			exit(EXIT_FAILURE);
		}
		if (f->body.user.parc == SIZE_MAX) {
			fputs("Limit reached.\n", stderr);
			exit(EXIT_FAILURE);
		}
		f->body.user.parc++;
		if (f->body.user.parc == 1) {
			f->body.user.parv = GC_malloc(sizeof(char *));
		} else {
			if (f->body.user.parc > SIZE_MAX / sizeof(char *)) {
				fputs("Limit reached.\n", stderr);
				exit(EXIT_FAILURE);
			}
			f->body.user.parv = GC_realloc(f->body.user.parv, f->body.user.parc * sizeof(char *));
		}
		if (f->body.user.parv == 0) {
			fputs("Out of memory.\n", stderr);
			exit(EXIT_FAILURE);
		}
		f->body.user.parv[f->body.user.parc - 1] = name_read();
		for (i = 0; i < f->body.user.parc - 1; i++)
			if (strcmp(f->body.user.parv[i], f->body.user.parv[f->body.user.parc - 1]) == 0)
				break;
		if (i != f->body.user.parc - 1) {
			fprintf(stderr, "Can not read function \"%s\". Duplicate parameter \"%s\".\n", f->name, f->body.user.parv[i]);
			exit(EXIT_FAILURE);
		}
		c = fgetc(stdin);
		if (feof(stdin) || ferror(stdin)) {
			fputs("Input error.\n", stderr);
			exit(EXIT_FAILURE);
		}
		while (c == ' ' || c == '\t' || c == '\n') {
			c = fgetc(stdin);
			if (feof(stdin) || ferror(stdin)) {
				fputs("Input error.\n", stderr);
				exit(EXIT_FAILURE);
			}
		}
	}

	c = fgetc(stdin);
	if (feof(stdin) || ferror(stdin)) {
		fputs("Input error.\n", stderr);
		exit(EXIT_FAILURE);
	}
	while (c == ' ' || c == '\t' || c == '\n') {
		c = fgetc(stdin);
		if (feof(stdin) || ferror(stdin)) {
			fputs("Input error.\n", stderr);
			exit(EXIT_FAILURE);
		}
	}
	ungetc(c, stdin);
	if (feof(stdin) || ferror(stdin)) {
		fputs("Input error.\n", stderr);
		exit(EXIT_FAILURE);
	}
	f->body.user.expr = expr_read();

	return f;
}

/* write function to stdout */
void func_wrte(struct func *f)
{
	size_t i;
	if (f == 0) {
		fputs("Null pointer.\n", stderr);
		exit(EXIT_FAILURE);
	}
	name_wrte(f->name);
	fputs(" (", stdout);
	if (feof(stdout) || ferror(stdout)) {
		fputs("Output error.\n", stderr);
		exit(EXIT_FAILURE);
	}
	for (i = 0; i < f->body.user.parc; i++) {
		name_wrte(f->body.user.parv[i]);
		if (i + 1 < f->body.user.parc) {
			fputc(' ', stdout);
			if (feof(stdout) || ferror(stdout)) {
				fputs("Output error.\n", stderr);
				exit(EXIT_FAILURE);
			}
		}
	}
	fputs(") ", stdout);
	if (feof(stdout) || ferror(stdout)) {
		fputs("Output error.\n", stderr);
		exit(EXIT_FAILURE);
	}
	expr_wrte(f->body.user.expr);
}

/* compute value of expression */
struct vlue *expr_vlue(struct expr *e)
{
	/* stack for intermediate results; vv -- vlue vector, vs -- vlue size, vt -- vlue root; vt is the index of first empty element in vv */
	struct vlue **vv = 0;
	size_t vs = 4096, vt = 0;
	struct vlue *v;
	/* stack that tracks defined symbols */
	struct symb {
		char *name;	/* where is the name */
		size_t vlue;	/* where is the vlue */
		size_t look;	/* where to look for next symbol */
	} *sv;
	size_t ss = 4096, st = 0;
	/* stack that points expressions to compute */
	struct todo {
		unsigned int type;	/* what type of expression is beeing computed */
		unsigned int step;	/* to track progress */
		struct expr *expr;	/* what expression to compute */
		size_t vltp, sbtp;	/* what was vt and st at the beginning */
		size_t look;	/* where to start symbol lookup? */
	} *tv;
	size_t ts = 4096, tt = 0;
	struct func *f;
	size_t i;

	if (e == 0) {
		fputs("Null pointer.\n", stderr);
		exit(EXIT_FAILURE);
	}

	vv = GC_malloc(vs * sizeof(struct vlue *));
	if (vv == 0) {
		fputs("Out of memory.\n", stderr);
		exit(EXIT_FAILURE);
	}
	sv = GC_malloc(ss * sizeof(struct symb));
	if (sv == 0) {
		fputs("Out of memory.\n", stderr);
		exit(EXIT_FAILURE);
	}
	tv = GC_malloc(ts * sizeof(struct todo));
	if (tv == 0) {
		fputs("Out of memory.\n", stderr);
		exit(EXIT_FAILURE);
	}

	/* push expr on todo stack */
	tv[tt].type = e->type;
	tv[tt].step = 0;
	tv[tt].expr = e;
	tv[tt].vltp = vt;
	tv[tt].sbtp = st;
	tv[tt].look = SIZE_MAX;
	tt++;

	while (tt != 0) {
		/* loop until todo stack gets empty */
		if (tv[tt - 1].type == CALL) {

			if (tv[tt - 1].step == UINT_MAX) {
				/* clean after call of function; vt is at least 1 */
				vv[tv[tt - 1].vltp] = vv[vt - 1];
				vt = tv[tt - 1].vltp + 1;
				st = tv[tt - 1].sbtp;
				tt--;

			} else if (tv[tt - 1].step == 0) {
				/* compute name of function to call */
				if (tt == ts) {
					if (ts > SIZE_MAX / 2 / sizeof(struct todo)) {
						fputs("Limit reached.\n", stderr);
						exit(EXIT_FAILURE);
					}
					ts *= 2;
					tv = GC_realloc(tv, ts * sizeof(struct todo));
					if (tv == 0) {
						fputs("Out of memory.\n", stderr);
						exit(EXIT_FAILURE);
					}
				}
				tv[tt].type = tv[tt - 1].expr->data.call->type;
				tv[tt].step = 0;
				tv[tt].expr = tv[tt - 1].expr->data.call;
				tv[tt].vltp = vt;
				tv[tt].sbtp = st;
				tv[tt].look = tv[tt - 1].look;
				tv[tt - 1].step = 1;
				tv[tt - 1].expr = tv[tt - 1].expr->data.call->next;
				tt++;

			} else {
				/* vv[tv[tt - 1].vltp] contains name of function called */
				if (vv[tv[tt - 1].vltp]->type != NAME) {
					fputs("Can not compute function call. Wrong type for name of function.\n", stderr);
					exit(EXIT_FAILURE);
				}

				if (strcmp(vv[tv[tt - 1].vltp]->data.name, "if") == 0) {
					if (tv[tt - 1].step == 1) {
						/* if there is only one parameter to compute, go to last step to compute else-expression; otherwise go to next step to compute value of condition */
						if (tv[tt - 1].expr == 0) {
							fputs("Can not compute \"if\" call. Wrong number of parameters.\n", stderr);
							exit(EXIT_FAILURE);
						}
						tv[tt - 1].step = tv[tt - 1].expr->next == 0 ? 4 : 2;
					} else if (tv[tt - 1].step == 2) {
						/* compute condition of "if" call then go to next step */
						if (tt == ts) {
							if (ts > SIZE_MAX / 2 / sizeof(struct todo)) {
								fputs("Limit reached.\n", stderr);
								exit(EXIT_FAILURE);
							}
							ts *= 2;
							tv = GC_realloc(tv, ts * sizeof(struct todo));
							if (tv == 0) {
								fputs("Out of memory.\n", stderr);
								exit(EXIT_FAILURE);
							}
						}
						tv[tt].type = tv[tt - 1].expr->type;
						tv[tt].step = 0;
						tv[tt].expr = tv[tt - 1].expr;
						tv[tt].vltp = vt;
						tv[tt].sbtp = st;
						tv[tt].look = tv[tt - 1].look;
						tv[tt - 1].expr = tv[tt - 1].expr->next;
						tv[tt - 1].step = 3;
						tt++;
					} else if (tv[tt - 1].step == 3) {
						/* check condition of "if" call; if condition is true then go to last step to compute following parameter; otherwise skip following parameter and restart computation */
						if (vv[vt - 1]->type != BOOL) {
							fputs("Can not compute \"if\" call. Wrong type for condition.\n", stderr);
							exit(EXIT_FAILURE);
						}
						if (tt == ts) {
							if (ts > SIZE_MAX / 2 / sizeof(struct todo)) {
								fputs("Limit reached.\n", stderr);
								exit(EXIT_FAILURE);
							}
							ts *= 2;
							tv = GC_realloc(tv, ts * sizeof(struct todo));
							if (tv == 0) {
								fputs("Out of memory.\n", stderr);
								exit(EXIT_FAILURE);
							}
						}
						if (vv[vt - 1]->data.bool) {
							tv[tt - 1].step = 4;
						} else {
							tv[tt - 1].expr = tv[tt - 1].expr->next;
							tv[tt - 1].step = 1;
						}
					} else if (tv[tt - 1].step == 4) {
						/* compute final value of "if" call -- this is the value of parameter following true condition or the value of the last parameter (else-expression) */
						if (tt == ts) {
							if (ts > SIZE_MAX / 2 / sizeof(struct todo)) {
								fputs("Limit reached.\n", stderr);
								exit(EXIT_FAILURE);
							}
							ts *= 2;
							tv = GC_realloc(tv, ts * sizeof(struct todo));
							if (tv == 0) {
								fputs("Out of memory.\n", stderr);
								exit(EXIT_FAILURE);
							}
						}
						tv[tt].type = tv[tt - 1].expr->type;
						tv[tt].step = 0;
						tv[tt].expr = tv[tt - 1].expr;
						tv[tt].vltp = vt;
						tv[tt].sbtp = st;
						tv[tt].look = tv[tt - 1].look;
						tv[tt - 1].step = UINT_MAX;
						tt++;
					} else {
						fputs("Wrong data.\n", stderr);
						exit(EXIT_FAILURE);
					}

				} else if (strcmp(vv[tv[tt - 1].vltp]->data.name, "as") == 0) {
					if (tv[tt - 1].step == 1) {
						/* if there is only one parameter to compute, then go to last step to compute a value of whole "as" call; otherwise continue in following steps */
						if (tv[tt - 1].expr == 0) {
							fputs("Can not compute \"as\" call. Wrong number of parameters.\n", stderr);
							exit(EXIT_FAILURE);
						}
						tv[tt - 1].step = tv[tt - 1].expr->next == 0 ? 5 : 2;
					} else if (tv[tt - 1].step == 2) {
						/* compute name of symbol; continue in following step */
						if (tt == ts) {
							if (ts > SIZE_MAX / 2 / sizeof(struct todo)) {
								fputs("Limit reached.\n", stderr);
								exit(EXIT_FAILURE);
							}
							ts *= 2;
							tv = GC_realloc(tv, ts * sizeof(struct todo));
							if (tv == 0) {
								fputs("Out of memory.\n", stderr);
								exit(EXIT_FAILURE);
							}
						}
						tv[tt].type = tv[tt - 1].expr->type;
						tv[tt].step = 0;
						tv[tt].expr = tv[tt - 1].expr;
						tv[tt].vltp = vt;
						tv[tt].sbtp = st;
						tv[tt].look = tv[tt - 1].look;
						tv[tt - 1].expr = tv[tt - 1].expr->next;
						tv[tt - 1].step = 3;
						tt++;
					} else if (tv[tt - 1].step == 3) {
						/* compute vlue of symbol; continue in following step */
						if (tt == ts) {
							if (ts > SIZE_MAX / 2 / sizeof(struct todo)) {
								fputs("Limit reached.\n", stderr);
								exit(EXIT_FAILURE);
							}
							ts *= 2;
							tv = GC_realloc(tv, ts * sizeof(struct todo));
							if (tv == 0) {
								fputs("Out of memory.\n", stderr);
								exit(EXIT_FAILURE);
							}
						}
						tv[tt].type = tv[tt - 1].expr->type;
						tv[tt].step = 0;
						tv[tt].expr = tv[tt - 1].expr;
						tv[tt].vltp = vt;
						tv[tt].sbtp = st;
						tv[tt].look = tv[tt - 1].look;
						tv[tt - 1].expr = tv[tt - 1].expr->next;
						tv[tt - 1].step = 4;
						tt++;
					} else if (tv[tt - 1].step == 4) {
						/* define new symbol and continue from the beginning */
						if (vv[vt - 2]->type != NAME) {
							fputs("Can not compute \"as\" call. Wrong type for name of symbol.\n", stderr);
							exit(EXIT_FAILURE);
						}
						if (st == ss) {
							if (ss > SIZE_MAX / 2 / sizeof(struct symb)) {
								fputs("Limit reached.\n", stderr);
								exit(EXIT_FAILURE);
							}
							ss *= 2;
							sv = GC_realloc(sv, ss * sizeof(struct symb));
							if (sv == 0) {
								fputs("Out of memory.\n", stderr);
								exit(EXIT_FAILURE);
							}
						}
						sv[st].name = vv[vt - 2]->data.name;
						sv[st].vlue = vt - 1;
						sv[st].look = tv[tt - 1].sbtp == st ? tv[tt - 1].look : st - 1;
						st++;
						tv[tt - 1].step = 1;
					} else if (tv[tt - 1].step == 5) {
						/* compute final parameter with all symbols defined; then finish */
						if (tt == ts) {
							if (ts > SIZE_MAX / 2 / sizeof(struct todo)) {
								fputs("Limit reached.\n", stderr);
								exit(EXIT_FAILURE);
							}
							ts *= 2;
							tv = GC_realloc(tv, ts * sizeof(struct todo));
							if (tv == 0) {
								fputs("Out of memory.\n", stderr);
								exit(EXIT_FAILURE);
							}
						}
						tv[tt].type = tv[tt - 1].expr->type;
						tv[tt].step = 0;
						tv[tt].expr = tv[tt - 1].expr;
						tv[tt].vltp = vt;
						tv[tt].sbtp = st;
						tv[tt].look = tv[tt - 1].sbtp == st ? tv[tt - 1].look : st - 1;
						tv[tt - 1].step = UINT_MAX;
						tt++;
					} else {
						fputs("Wrong data.\n", stderr);
						exit(EXIT_FAILURE);
					}

				} else {	/* not "if" nor "as" call */
					if (tv[tt - 1].step == 1) {
						/* if no parameters are left to compute go to next step; otherwise compute parameter and continue in the same step */
						if (tv[tt - 1].expr == 0) {
							tv[tt - 1].step = 2;
						} else {
							if (tt == ts) {
								if (ts > SIZE_MAX / 2 / sizeof(struct todo)) {
									fputs("Limit reached.\n", stderr);
									exit(EXIT_FAILURE);
								}
								ts *= 2;
								tv = GC_realloc(tv, ts * sizeof(struct todo));
								if (tv == 0) {
									fputs("Out of memory.\n", stderr);
									exit(EXIT_FAILURE);
								}
							}
							tv[tt].type = tv[tt - 1].expr->type;
							tv[tt].step = 0;
							tv[tt].expr = tv[tt - 1].expr;
							tv[tt].vltp = vt;
							tv[tt].sbtp = st;
							tv[tt].look = tv[tt - 1].look;
							tv[tt - 1].expr = tv[tt - 1].expr->next;
							tt++;
						}
					} else if (tv[tt - 1].step == 2) {
						/* if name of function is the name of defined symbol, then copy the value of that symbol to the vlue stack; otherwise go to following step */
						for (i = tv[tt - 1].look; i != SIZE_MAX; i = sv[i].look)
							if (strcmp(vv[tv[tt - 1].vltp]->data.name, sv[i].name) == 0)
								break;
						if (i != SIZE_MAX) {
							if (vt - 1 - tv[tt - 1].vltp != 0) {
								fprintf(stderr, "Can not compute \"%s\" call. Wrong number of parameters.\n", sv[i].name);
								exit(EXIT_FAILURE);
							}
							if (vt == vs) {
								if (vs > SIZE_MAX / 2 / sizeof(struct vlue *)) {
									fputs("Limit reached.\n", stderr);
									exit(EXIT_FAILURE);
								}
								vs *= 2;
								vv = GC_realloc(vv, vs * sizeof(struct vlue *));
								if (vv == 0) {
									fputs("Out of memory.\n", stderr);
									exit(EXIT_FAILURE);
								}
							}
							vv[vt] = vv[sv[i].vlue];
							vt++;
							tv[tt - 1].step = UINT_MAX;
						} else {
							tv[tt - 1].step = 3;
						}
					} else if (tv[tt - 1].step == 3) {
						/* find a function defined in global func tree; if function is not defined then go to the next step */
						f = (struct func *) tree_search(func, vv[tv[tt - 1].vltp]->data.name);
						if (f != 0) {
							if (f->type == CORE) {
								/* this is built-in function, so compute value by calling that function */
								if (vt == vs) {
									if (vs > SIZE_MAX / 2 / sizeof(struct vlue *)) {
										fputs("Limit reached.\n", stderr);
										exit(EXIT_FAILURE);
									}
									vs *= 2;
									vv = GC_realloc(vv, vs * sizeof(struct vlue *));
									if (vv == 0) {
										fputs("Out of memory.\n", stderr);
										exit(EXIT_FAILURE);
									}
								}
								vv[vt] = f->body.core(vt - 1 - tv[tt - 1].vltp, vv + tv[tt - 1].vltp + 1);
								vt++;
								tv[tt - 1].step = UINT_MAX;
							} else {
								if (vt - 1 - tv[tt - 1].vltp != f->body.user.parc) {
									fprintf(stderr, "Can not compute \"%s\" call. Wrong number of parameters.\n", f->name);
									exit(EXIT_FAILURE);
								}
								for (i = 0; i < f->body.user.parc; i++) {
									if (st == ss) {
										if (ss > SIZE_MAX / 2 / sizeof(struct symb)) {
											fputs("Limit reached.\n", stderr);
											exit(EXIT_FAILURE);
										}
										ss *= 2;
										sv = GC_realloc(sv, ss * sizeof(struct symb));
										if (sv == 0) {
											fputs("Out of memory.\n", stderr);
											exit(EXIT_FAILURE);
										}
									}
									/* define symbol for parameter of function */
									sv[st].name = f->body.user.parv[i];
									sv[st].vlue = tv[tt - 1].vltp + 1 + i;
									sv[st].look = i == 0 ? SIZE_MAX : st - 1;
									st++;
								}
								if (tt == ts) {
									if (ts > SIZE_MAX / 2 / sizeof(struct todo)) {
										fputs("Limit reached.\n", stderr);
										exit(EXIT_FAILURE);
									}
									ts *= 2;
									tv = GC_realloc(tv, ts * sizeof(struct todo));
									if (tv == 0) {
										fputs("Out of memory.\n", stderr);
										exit(EXIT_FAILURE);
									}
								}
								/* compute body of function with symbols defined */
								tv[tt].type = f->body.user.expr->type;
								tv[tt].step = 0;
								tv[tt].expr = f->body.user.expr;
								tv[tt].vltp = vt;
								tv[tt].sbtp = st;
								tv[tt].look = f->body.user.parc == 0 ? SIZE_MAX : st - 1;
								tv[tt - 1].step = UINT_MAX;
								tt++;
							}
						} else {
							tv[tt - 1].step = 4;
						}
					} else if (tv[tt - 1].step == 4) {
						fprintf(stderr, "Can not compute function call. Function \"%s\" not defined.\n", vv[tv[tt - 1].vltp]->data.name);
						exit(EXIT_FAILURE);
					} else {
						fputs("Wrong data.\n", stderr);
						exit(EXIT_FAILURE);
					}
				}
			}

		} else if (tv[tt - 1].type == VLUE) {
			if (vt == vs) {
				if (vs > SIZE_MAX / 2 / sizeof(struct vlue *)) {
					fputs("Limit reached.\n", stderr);
					exit(EXIT_FAILURE);
				}
				vs *= 2;
				vv = GC_realloc(vv, vs * sizeof(struct vlue *));
				if (vv == 0) {
					fputs("Out of memory.\n", stderr);
					exit(EXIT_FAILURE);
				}
			}
			vv[vt] = tv[tt - 1].expr->data.vlue;
			vt++;
			tt--;

		} else {
			fputs("Wrong data.\n", stderr);
			exit(EXIT_FAILURE);
		}
	}

	GC_free(sv);
	GC_free(tv);
	v = vv[0];
	return v;
}
