I am doing an attempt to create a custom-interpreted programming language. It works by going over every line of code that the user has written in a script. But that is done in a function. The problem is that at the end of the function it calls itself to run the next line, and it keeps doing that until the interpreter reaches the end of the script. This is fine for small programs but if it reaches around 430 lines of code, the interpreter throws a StackOverflowException because the Execute() function was called too much. Currently, the interpreter supports variable declaration + assignment (boolean and float), if statements, comparisons, and calculations. So basically my question is how to prevent that StackOverflowException? Or is there any other way to do this interpreter thing without needing to rewrite the whole thing? This is done in C#-Dotnet 6.0 If someone could take a look at my code, that would be very nice.
Preprocess() is called first:
//Get links in code, get function indexes
private void PreprocessCode()
{
var conditions = new List<ConditionBlock>();
int depth = 0;
//If statements
for (int i = 0; i < lines.Count; i++)
{
if (string.IsNullOrEmpty(lines[i]) || string.IsNullOrWhiteSpace(lines[i]))
continue;
Lexer lexer = new Lexer(lines[i]);
var tokens = lexer.MakeTokens();
var condition = new ConditionBlock(i, depth);
//Create if statement blockinfo
if(tokens[0].type == TokenTypes.KEYWORD)
{
if(tokens[0].ValueMatches("if"))
{
condition.type = ConditionBlock.Type.If;
conditions.Add(condition);
}
else if (tokens[0].ValueMatches("elif"))
{
condition.type = ConditionBlock.Type.Elseif;
conditions.Add(condition);
}
else if (tokens[0].ValueMatches("else"))
{
condition.type = ConditionBlock.Type.Else;
conditions.Add(condition);
}
}
else if(tokens[0].type == TokenTypes.RCURLY)
{
depth--;
}
else if (tokens[0].type == TokenTypes.LCURLY)
{
depth++;
}
}
for(int i = 0; i < conditions.Count; i++)
{
var condition = conditions[i];
//Try find an elif|else block attached to a non-else block
if(condition.type != ConditionBlock.Type.Else)
{
//Get next conditionindex
for(int j = i + 1; j < conditions.Count; j++)
{
var nextCondition = conditions[j];
if (condition.depth == nextCondition.depth)
{
//Start of new if statement, so is end of current one
if (nextCondition.type == ConditionBlock.Type.If)
break;
condition.nextInChain = nextCondition;
nextCondition.previousInChain = condition;
break;
}
//Out of scope, so is end of if statement
else if (nextCondition.depth < condition.depth)
break;
}
}
conditionByLineIndex.Add(condition.start, condition);
}
//Start run
Execute(0, lines.Count);
return;
}
Execute() Function:
private void Execute(int lineIndex, int stop)
{
if (lineIndex >= lines.Count || lineIndex == stop)
return;
Lexer lexer = new Lexer(lines[lineIndex]);
var tokens = lexer.MakeTokens(); //Returns list of tokens in line
this.tokens = tokens;
tokenIndex = -1;
Advance();
//Check if current line is an if statement
if (conditionByLineIndex.ContainsKey(lineIndex))
{
var toks = ToksBetweenIdx(0, tokens.Count);
Expression expression = new Expression(toks);
var condition = conditionByLineIndex[lineIndex];
bool runCondition = false;
if (toks.Count > 0 && condition.type != ConditionBlock.Type.Else)
runCondition = expression.Evaluate();
else if (condition.type == ConditionBlock.Type.Else)
runCondition = true;
if (condition.type == ConditionBlock.Type.Else)
runCondition = true;
if (condition.type == ConditionBlock.Type.If)
hasRunChain = false;
if (condition.type != ConditionBlock.Type.If)
{
var previousInChain = condition.previousInChain;
while (previousInChain != null)
{
if (previousInChain.lastEvaluation)
{
runCondition = false;
break;
}
previousInChain = previousInChain.previousInChain;
}
}
conditionByLineIndex[lineIndex].lastEvaluation = runCondition;
HandleCondition(runCondition, lineIndex, stop);
return;
}
while (tokenIndex < tokens.Count)
{
//Skip over { and }
if (currentToken.type == TokenTypes.RCURLY || currentToken.type == TokenTypes.LCURLY)
Advance();
if (currentToken.type == TokenTypes.KEYWORD)
{
if (currentToken.ValueMatches("var"))
{
Advance();
if (currentToken.type == TokenTypes.IDENTIFIER)
{
Token identifier = currentToken;
Advance();
if (currentToken.type == TokenTypes.EQUAL)
{
List<Token> toks = ToksAfterIdx(tokenIndex);
Expression expression = new Expression(toks);
// identifier.value -> TYPE:IDENTIFIER|VALUE:thisIsAVariableName
currentVariables.CreateVariable(identifier.value, expression.Evaluate());
break;
}
}
}
}
else if (currentToken.type == TokenTypes.IDENTIFIER)
{
Token identifier = currentToken;
if (tokenIndex + 1 == tokens.Count)
{
//Print out the value of the variable
Console.WriteLine($"Print: {currentVariables.AccesVariable(identifier.value)}");
break;
}
else
{
Advance();
if (currentToken.type == TokenTypes.EQUAL)
{
List<Token> toks = ToksAfterIdx(tokenIndex);
Expression expression = new Expression(toks);
currentVariables.AssignVariable(identifier.value, expression.Evaluate());
break;
}
else if (currentToken.type == TokenTypes.INCREMENT)
{
float value = currentVariables.AccesVariable(identifier.value);
currentVariables.AssignVariable(identifier.value, value + 1);
break;
}
else if (currentToken.type == TokenTypes.DECREMENT)
{
float value = currentVariables.AccesVariable(identifier.value);
currentVariables.AssignVariable(identifier.value, value - 1);
break;
}
else if (currentToken.type == TokenTypes.PLUSEQ)
{
float value = currentVariables.AccesVariable(identifier.value);
List<Token> toks = ToksAfterIdx(tokenIndex);
Expression expression = new Expression(toks);
currentVariables.AssignVariable(identifier.value, value + expression.Evaluate());
break;
}
else if (currentToken.type == TokenTypes.MINEQ)
{
float value = currentVariables.AccesVariable(identifier.value);
List<Token> toks = ToksAfterIdx(tokenIndex);
Expression expression = new Expression(toks);
currentVariables.AssignVariable(identifier.value, value - expression.Evaluate());
break;
}
else if (currentToken.type == TokenTypes.MULEQ)
{
float value = currentVariables.AccesVariable(identifier.value);
List<Token> toks = ToksAfterIdx(tokenIndex);
Expression expression = new Expression(toks);
currentVariables.AssignVariable(identifier.value, value * expression.Evaluate());
break;
}
else if (currentToken.type == TokenTypes.DIVEQ)
{
float value = currentVariables.AccesVariable(identifier.value);
List<Token> toks = ToksAfterIdx(tokenIndex);
Expression expression = new Expression(toks);
currentVariables.AssignVariable(identifier.value, value / expression.Evaluate());
break;
}
}
}
}
if (lineIndex + 1 < stop && lineIndex + 1 < lines.Count)
{
//Run next line
Execute(lineIndex + 1, stop);
}
}
Greetings