标记化后的良好解析方法
Good parsing approach after tokenization
我编写了一个程序,在其中读取字符串或文件并将其标记化。
示例字符串:
"int w = sad&&s||a|d++ != == < > >= <= -- sadsa++ % int sads = 232.32; if reg string test = \"Hello World\";% % +- + - / * ** false true"
示例输出:
Token[Type:'Identifier',Value:'int',Line:'1',Position:'3']
Token[Type:'Literal',Value:'w',Line:'1',Position:'4']
Token[Type:'Assign',Value:'=',Line:'1',Position:'4']
Token[Type:'Literal',Value:'sad',Line:'1',Position:'8']
Token[Type:'Logical',Value:'&&',Line:'1',Position:'8']
Token[Type:'Literal',Value:'s',Line:'1',Position:'11']
Token[Type:'Logical',Value:'||',Line:'1',Position:'11']
Token[Type:'Literal',Value:'a',Line:'1',Position:'14']
Token[Type:'Unknown',Value:'|d',Line:'1',Position:'14']
Token[Type:'Literal',Value:'d',Line:'1',Position:'16']
Token[Type:'Arithmetic',Value:'++',Line:'1',Position:'16']
Token[Type:'Relational',Value:'!=',Line:'1',Position:'18']
Token[Type:'Relational',Value:'==',Line:'1',Position:'20']
Token[Type:'Relational',Value:'<',Line:'1',Position:'22']
Token[Type:'Relational',Value:'>',Line:'1',Position:'23']
Token[Type:'Relational',Value:'>=',Line:'1',Position:'24']
Token[Type:'Relational',Value:'<=',Line:'1',Position:'26']
Token[Type:'Arithmetic',Value:'--',Line:'1',Position:'28']
Token[Type:'Literal',Value:'sadsa',Line:'1',Position:'35']
Token[Type:'Arithmetic',Value:'++',Line:'1',Position:'35']
Token[Type:'Arithmetic',Value:'%',Line:'1',Position:'37']
Token[Type:'Identifier',Value:'int',Line:'1',Position:'41']
Token[Type:'Literal',Value:'sads',Line:'1',Position:'45']
Token[Type:'Assign',Value:'=',Line:'1',Position:'45']
Token[Type:'DoubleValue',Value:'232.32',Line:'1',Position:'51']
Token[Type:'Semicolon',Value:';',Line:'1',Position:'51']
Token[Type:'Identifier',Value:'if',Line:'1',Position:'53']
Token[Type:'Literal',Value:'reg',Line:'1',Position:'56']
Token[Type:'Identifier',Value:'string',Line:'1',Position:'62']
Token[Type:'Literal',Value:'test',Line:'1',Position:'66']
Token[Type:'Assign',Value:'=',Line:'1',Position:'66']
Token[Type:'StringValue',Value:'Hello World',Line:'1',Position:'78']
Token[Type:'Semicolon',Value:';',Line:'1',Position:'78']
Token[Type:'Arithmetic',Value:'%',Line:'1',Position:'78']
Token[Type:'Arithmetic',Value:'%',Line:'1',Position:'79']
Token[Type:'Unknown',Value:'+-',Line:'1',Position:'80']
Token[Type:'Arithmetic',Value:'+',Line:'1',Position:'82']
Token[Type:'Arithmetic',Value:'-',Line:'1',Position:'83']
Token[Type:'Arithmetic',Value:'/',Line:'1',Position:'84']
Token[Type:'Arithmetic',Value:'*',Line:'1',Position:'85']
Token[Type:'Unknown',Value:'**',Line:'1',Position:'86']
Token[Type:'Identifier',Value:'false',Line:'1',Position:'93']
Token[Type:'Identifier',Value:'true',Line:'1',Position:'97']
Elapsed time: 31
(忽略位置,以后必须修复)
所以现在我真的不知道进一步解释这个的好方法,以便 运行 我自己的一个简单的小脚本语言。
一个好的第一个方法是为每件事起个名字,比如"operation"、"operand"、"literal"、"declaration"等
然后创建一个函数,可以从您的令牌流中读取其中的每一个,并在适当的时候调用其他函数。例如。如果你有:
operation: <operand> <operator> <operand>
operator: + | -
operand: <number> | <string>
等,那么就可以有一个函数(伪代码):
Operation parseOperation( tokens )
{
Operand operand1 = parseOperand( tokens )
Operation theOp = new Operation( tokens.fetchNextToken() )
Operand operand2 = parseOperand( tokens )
theOp.operand[0] = operand1
theOp.operand[1] = operand2
return theOp
}
并以类似的方式实现其他功能。当然,某些函数可能必须检查下一个标记以决定调用哪个其他函数,甚至提前几个标记。
这通常称为 "recursive descent" 解析器。
每个函数 return 都是一个对象,表示一个操作并有其参数挂在它上面。这基本上是一个树结构,然后您可以递归地遍历它 ("depth-first")。
最简单的方法是为您定义的不同对象类型提供您的操作和值方法,例如 asString() 或 asInt(),然后执行以下操作:
int Operation::asInt()
{
int a = operand[0].asInt()
int b = operand[1].asInt()
return a + b;
}
已经是正确类型的对象(如 IntOperand class)将 return 它们的值:
int IntOperand::asInt()
{
return self.numericValue
}
或其他什么。类型不正确的对象要么产生错误而不是 returning 一个值,要么进行自我转换。所以无论表达式多么复杂,最后你得到一个 int 或 string 或其他任何东西。
对于 运行 程序,您现在可以向最底部的对象询问其值。
我编写了一个程序,在其中读取字符串或文件并将其标记化。
示例字符串:
"int w = sad&&s||a|d++ != == < > >= <= -- sadsa++ % int sads = 232.32; if reg string test = \"Hello World\";% % +- + - / * ** false true"
示例输出:
Token[Type:'Identifier',Value:'int',Line:'1',Position:'3']
Token[Type:'Literal',Value:'w',Line:'1',Position:'4']
Token[Type:'Assign',Value:'=',Line:'1',Position:'4']
Token[Type:'Literal',Value:'sad',Line:'1',Position:'8']
Token[Type:'Logical',Value:'&&',Line:'1',Position:'8']
Token[Type:'Literal',Value:'s',Line:'1',Position:'11']
Token[Type:'Logical',Value:'||',Line:'1',Position:'11']
Token[Type:'Literal',Value:'a',Line:'1',Position:'14']
Token[Type:'Unknown',Value:'|d',Line:'1',Position:'14']
Token[Type:'Literal',Value:'d',Line:'1',Position:'16']
Token[Type:'Arithmetic',Value:'++',Line:'1',Position:'16']
Token[Type:'Relational',Value:'!=',Line:'1',Position:'18']
Token[Type:'Relational',Value:'==',Line:'1',Position:'20']
Token[Type:'Relational',Value:'<',Line:'1',Position:'22']
Token[Type:'Relational',Value:'>',Line:'1',Position:'23']
Token[Type:'Relational',Value:'>=',Line:'1',Position:'24']
Token[Type:'Relational',Value:'<=',Line:'1',Position:'26']
Token[Type:'Arithmetic',Value:'--',Line:'1',Position:'28']
Token[Type:'Literal',Value:'sadsa',Line:'1',Position:'35']
Token[Type:'Arithmetic',Value:'++',Line:'1',Position:'35']
Token[Type:'Arithmetic',Value:'%',Line:'1',Position:'37']
Token[Type:'Identifier',Value:'int',Line:'1',Position:'41']
Token[Type:'Literal',Value:'sads',Line:'1',Position:'45']
Token[Type:'Assign',Value:'=',Line:'1',Position:'45']
Token[Type:'DoubleValue',Value:'232.32',Line:'1',Position:'51']
Token[Type:'Semicolon',Value:';',Line:'1',Position:'51']
Token[Type:'Identifier',Value:'if',Line:'1',Position:'53']
Token[Type:'Literal',Value:'reg',Line:'1',Position:'56']
Token[Type:'Identifier',Value:'string',Line:'1',Position:'62']
Token[Type:'Literal',Value:'test',Line:'1',Position:'66']
Token[Type:'Assign',Value:'=',Line:'1',Position:'66']
Token[Type:'StringValue',Value:'Hello World',Line:'1',Position:'78']
Token[Type:'Semicolon',Value:';',Line:'1',Position:'78']
Token[Type:'Arithmetic',Value:'%',Line:'1',Position:'78']
Token[Type:'Arithmetic',Value:'%',Line:'1',Position:'79']
Token[Type:'Unknown',Value:'+-',Line:'1',Position:'80']
Token[Type:'Arithmetic',Value:'+',Line:'1',Position:'82']
Token[Type:'Arithmetic',Value:'-',Line:'1',Position:'83']
Token[Type:'Arithmetic',Value:'/',Line:'1',Position:'84']
Token[Type:'Arithmetic',Value:'*',Line:'1',Position:'85']
Token[Type:'Unknown',Value:'**',Line:'1',Position:'86']
Token[Type:'Identifier',Value:'false',Line:'1',Position:'93']
Token[Type:'Identifier',Value:'true',Line:'1',Position:'97']
Elapsed time: 31
(忽略位置,以后必须修复)
所以现在我真的不知道进一步解释这个的好方法,以便 运行 我自己的一个简单的小脚本语言。
一个好的第一个方法是为每件事起个名字,比如"operation"、"operand"、"literal"、"declaration"等
然后创建一个函数,可以从您的令牌流中读取其中的每一个,并在适当的时候调用其他函数。例如。如果你有:
operation: <operand> <operator> <operand>
operator: + | -
operand: <number> | <string>
等,那么就可以有一个函数(伪代码):
Operation parseOperation( tokens )
{
Operand operand1 = parseOperand( tokens )
Operation theOp = new Operation( tokens.fetchNextToken() )
Operand operand2 = parseOperand( tokens )
theOp.operand[0] = operand1
theOp.operand[1] = operand2
return theOp
}
并以类似的方式实现其他功能。当然,某些函数可能必须检查下一个标记以决定调用哪个其他函数,甚至提前几个标记。
这通常称为 "recursive descent" 解析器。
每个函数 return 都是一个对象,表示一个操作并有其参数挂在它上面。这基本上是一个树结构,然后您可以递归地遍历它 ("depth-first")。
最简单的方法是为您定义的不同对象类型提供您的操作和值方法,例如 asString() 或 asInt(),然后执行以下操作:
int Operation::asInt()
{
int a = operand[0].asInt()
int b = operand[1].asInt()
return a + b;
}
已经是正确类型的对象(如 IntOperand class)将 return 它们的值:
int IntOperand::asInt()
{
return self.numericValue
}
或其他什么。类型不正确的对象要么产生错误而不是 returning 一个值,要么进行自我转换。所以无论表达式多么复杂,最后你得到一个 int 或 string 或其他任何东西。
对于 运行 程序,您现在可以向最底部的对象询问其值。