Javascript中的抽象语法树递归性如何?

Javascript中的抽象语法树递归性如何?,javascript,regex,recursion,backtracking,recursive-descent,Javascript,Regex,Recursion,Backtracking,Recursive Descent,我正在尝试用javascript构建一个编译器,到目前为止,我已经成功构建了一个基于输入创建令牌的lexer: =测试输入(带可选分号): =实际Lexer结果(来自控制台-令牌数组): lexer做得很好(变量和函数的数据和任务是关键字)但是我想创建一些正则表达式,这样就可以捕获函数声明、变量声明等。只使用当前标记对象作为输入 如果是文本,我会用以下正则表达式捕获函数声明: task\s+[a-zA-Z][a-zA-Z0-9]*\s*\{\s*(1)*\s*\} (1) 是指令块的正则表达式

我正在尝试用javascript构建一个编译器,到目前为止,我已经成功构建了一个基于输入创建令牌的lexer:

=测试输入(带可选分号):

=实际Lexer结果(来自控制台-令牌数组):

lexer做得很好(变量和函数的数据和任务是关键字但是我想创建一些正则表达式,这样就可以捕获函数声明、变量声明等。只使用当前标记对象作为输入 如果是文本,我会用以下正则表达式捕获函数声明:

task\s+[a-zA-Z][a-zA-Z0-9]*\s*\{\s*(1)*\s*\}
(1) 是指令块的正则表达式代码,包括接收等关键字函数

在本例中,有没有一种方法可以匹配变量/函数声明,即从for循环期间更改的索引开始

例如:

=我使用for循环遍历了我的令牌列表,在索引9中,我第一次找到了这个对象:

9: {content: "task", denominal: "keyword"}
=现在,我想开始搜索对象上的函数声明。这意味着:

1) -如果功能正确如声明、论据等

2) -这个函数意味着多少个对象像从索引9到索引30的一样,所有这些对象都形成一个名为“Eat”的函数,它有3个指令块:

  • 1个特殊接收指令块,将强制放在函数的开头(甚至为空),包含格式正确的参数[variableName:variableType]

  • 1个特殊打印指令块,并正确给出其参数

  • 1特殊返回指令块,将强制放在函数开头(如果为空,则不返回任何内容:Void),包含格式正确的参数[variableName:variableType]
3) -在何处停止,因此现在我知道函数定义是结束
,在这种情况下,我可以从
最终索引+1=31开始搜索其他内容(例如变量声明、EOF等)

如果你愿意告诉我方法,这样我就可以建立一个特定指令块的存在,创建上面的函数描述示例,那将是非常棒的

理想的结果(针对此问题)是这样的数组:

Instruction Object:

[0: {
     "instruction": "variable_declaration",
     "variable_name": "myVariable",
     "variable_value": "4",
     "variable_type": "Integer"
    }
 1: {
     "instruction": "variable_declaration",
     "variable_name": "myVariable2",
     "variable_value": ""myName"",
     "variable_type": "String"
    }
 2: {
     "instruction": "function_declaration",
     "function_name": "Eat",
     "body_instructions": [0: {
                               "instruction": "receives_instruction", 
                               "arguments": [0: {
                                                 "argument_name": "whatToEat",
                                                 "argument_type": "String"
                                                }
                                             1: {
                                                 "argument_name": "howMuchTime",
                                                 "argument_type": "Float"
                                                }]
                           1: {
                               "instruction": "print_instruction",
                               "arguments": [0: {
                                                 "argument_name": "myVariable",
                                                 "argument_value": "4",
                                                 "argument_type": "Integer"
                                                }]
                           2: {
                               "instruction": "returns_instruction", 
                               "arguments": [0: {
                                                 "argument_name": "Nothing",
                                                 "argument_value": "",
                                                 "argument_type": "Void"
                                                }]
                              }]
    }] // EOF object optional

我感谢你的帮助


提前多谢

我不知道您的语言的底层语法是什么,但大多数编程语言都基于上下文无关语法,需要比有限自动机(例如regex引擎)更强大的功能来解析它们;他们需要一个下推自动机。尽管一些正则表达式引擎已经被增强以模拟下推自动机,但我还是忍不住觉得,对于下一个阶段,即解析,您需要(或应该)切换到不同的工具。也许可以查看递归下降解析。只要我的两分钱。@Ronaldaronson我很感激你的建议,但是对于这个编译器(至少在它的情况下),我希望尽可能地保持它的基础性-例如,这些变量/函数唯一的声明被制作成在JS代码之后立即“转换”—离.replace()不远但更聪明一点:)问题是,在递归性的情况下,我必须知道“推入”对象的确切位置,这对我来说是最困难的部分。@Ronaldaronson例如,我会从位置“null”开始,然后在创建函数_声明后,我不知道是否应该用“AST[0]”或“AST”替换位置[1] 。最初,这是我的第一个选择,但我发现我不知道如何在代码中设置此位置(通过在新子对象中“输入”或“退出”完整子对象以返回其父对象/数组)。如果你能附上一个具有这种递归性的例子,你将是一个伟大的天才!非常感谢!:):)一个问题是标点符号
}
是否可以出现在函数体中。如果可以,那么你几乎肯定不能用类似正则表达式的工具来完成。你需要更复杂的东西。我还没有做过大量的解析,但我已经成功地用Peg.js编写了一些解析器,并且只使用模拟下推自动机的普通状态机。我认为这两种方法都可以在这里工作。@ScottSauyet请给我一个信息丰富的示例,说明考虑到函数内部没有}(就像Python if-s:))。为我的编译器构建此AST的主要目的是检查基于糟糕输入外观的任何语法错误&以某种方式将此对象转换回JavaScript代码,之后可能会使用eval。因此,主要需要的是对象的实际转换,而不是例外情况,以及是否可以附加任何可能请帮助我(我最初认为是基于递归的方法),这将是非常棒的。非常感谢!
9: {content: "task", denominal: "keyword"}
Instruction Object:

[0: {
     "instruction": "variable_declaration",
     "variable_name": "myVariable",
     "variable_value": "4",
     "variable_type": "Integer"
    }
 1: {
     "instruction": "variable_declaration",
     "variable_name": "myVariable2",
     "variable_value": ""myName"",
     "variable_type": "String"
    }
 2: {
     "instruction": "function_declaration",
     "function_name": "Eat",
     "body_instructions": [0: {
                               "instruction": "receives_instruction", 
                               "arguments": [0: {
                                                 "argument_name": "whatToEat",
                                                 "argument_type": "String"
                                                }
                                             1: {
                                                 "argument_name": "howMuchTime",
                                                 "argument_type": "Float"
                                                }]
                           1: {
                               "instruction": "print_instruction",
                               "arguments": [0: {
                                                 "argument_name": "myVariable",
                                                 "argument_value": "4",
                                                 "argument_type": "Integer"
                                                }]
                           2: {
                               "instruction": "returns_instruction", 
                               "arguments": [0: {
                                                 "argument_name": "Nothing",
                                                 "argument_value": "",
                                                 "argument_type": "Void"
                                                }]
                              }]
    }] // EOF object optional