递归自定义编程语言 C# 中的 StackOverflowException
StackOverflowException in recursive custom programming language C#
我正在尝试创建一种自定义解释的编程语言。它通过遍历用户在脚本中编写的每一行代码来工作。但这是在函数中完成的。
问题在于,在函数的末尾,它调用自己到下一行 运行 ,并且它一直这样做,直到解释器到达脚本的末尾。
这对于小程序来说很好,但是如果它达到大约 430 行代码,解释器将抛出 WhosebugException,因为 Execute() 函数被调用了太多。
目前,解释器支持变量声明+赋值(boolean和float)、if语句、比较和计算。
所以基本上我的问题是如何防止 WhosebugException?或者有没有其他方法可以做这个解释器的事情而不需要重写整个事情?
这是在 C#-Dotnet 6.0 中完成的
如果有人能看一下我的代码,那就太好了。
Preprocess() 首先被调用:
//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;
}
执行()函数:
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);
}
}
问候
您将不得不更改您的逻辑以不使用递归来读取每一行。如果你使用递归,你将为每一层递归添加一个新的函数调用到堆栈,并且在满足退出条件之前不会删除之前的函数调用。如果你进行了 500 次左右的深度调用,你可能会遇到 Whosebug 异常。
现在,我没有时间阅读你的代码,但我可以告诉你你需要做什么:将你的递归调用变成一个循环。
你的代码大概可以分解成这样:
void ExecuteProgramLine(int lineNumber)
{
InterpretAndRunLine(lineNumber);
ExecuteProgramLine(lineNumber + 1);
}
您需要将其转换为:
for(int lineNumber = 0; lineNumber < fileLines) // (foreach loop is probably better)
{
InterpretAndRunLine(lineNumber);
}
我正在尝试创建一种自定义解释的编程语言。它通过遍历用户在脚本中编写的每一行代码来工作。但这是在函数中完成的。 问题在于,在函数的末尾,它调用自己到下一行 运行 ,并且它一直这样做,直到解释器到达脚本的末尾。 这对于小程序来说很好,但是如果它达到大约 430 行代码,解释器将抛出 WhosebugException,因为 Execute() 函数被调用了太多。 目前,解释器支持变量声明+赋值(boolean和float)、if语句、比较和计算。 所以基本上我的问题是如何防止 WhosebugException?或者有没有其他方法可以做这个解释器的事情而不需要重写整个事情? 这是在 C#-Dotnet 6.0 中完成的 如果有人能看一下我的代码,那就太好了。
Preprocess() 首先被调用:
//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;
}
执行()函数:
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);
}
}
问候
您将不得不更改您的逻辑以不使用递归来读取每一行。如果你使用递归,你将为每一层递归添加一个新的函数调用到堆栈,并且在满足退出条件之前不会删除之前的函数调用。如果你进行了 500 次左右的深度调用,你可能会遇到 Whosebug 异常。
现在,我没有时间阅读你的代码,但我可以告诉你你需要做什么:将你的递归调用变成一个循环。
你的代码大概可以分解成这样:
void ExecuteProgramLine(int lineNumber)
{
InterpretAndRunLine(lineNumber);
ExecuteProgramLine(lineNumber + 1);
}
您需要将其转换为:
for(int lineNumber = 0; lineNumber < fileLines) // (foreach loop is probably better)
{
InterpretAndRunLine(lineNumber);
}