【问题标题】:Translate VisualBasic expressions to JavaScript expressions dynamically将 VisualBasic 表达式动态转换为 JavaScript 表达式
【发布时间】:2022-01-05 08:17:23
【问题描述】:

我想将用 VisualBasic 样式编写的表达式(条件)翻译成 JavaScript 代码。因为我还没有找到这样做的图书馆,所以我必须自己做。或者有人知道我可以使用的项目吗?

我的要求: 我从一个文件中得到一个字符串,其中包含用 VB 编写的表达式。它不是完整的 VB 代码,而只是单个 VB 函数,如 IF、TRIM、LEFT 等,以及一些其他函数,如 RECORD_NR()(我认为这不是 vb 的原生函数)。 我现在想把这个字符串翻译成 JavaScript(也可以作为一个字符串来,因为我后来把 JavaScript 代码写到一个文件中)。 问题之一是不能对引号中的所有字符进行功能处理。

示例表达式:

IF(RECORD_NR() = 1, "NR,1",IF(RECORD_NR() = 2, "NR,2", "NR3"))

可能的结果:

var result = "";
if(record.index == 1){
    result = "NR,1";
} else {
    if(record.index == 2){
        result = "NR,2";
    } else {
        result = "NR3";
    }
}

以下是我目前正在使用的方法,我不确定它是否能让我达到我的目标:

  1. 搜索引号中的区域并将位置信息(开始和结束)存储在 json 对象 (jsonQuotes) 中。
  2. 在括号中搜索范围并将位置(开始和结束)保存在 json 对象 (jsonBrackets) 中。在这里,我将根据jsonQuotes 排除引号中的范围。
  3. 搜索不在引号中的关键字(函数名)并将找到的关键字添加到json对象jsonBrackets(这样我就知道哪个函数属于括号区域了)。

此时我有以下 json 对象:

[
    {
        "ID": 1,
        "start": 2,
        "end": 60,
        "fnc": "IF"
    },
    {
        "ID": 2,
        "start": 12,
        "end": 13,
        "fnc": "RECORD_NR"
    },
    {
        "ID": 3,
        "start": 29,
        "end": 59,
        "fnc": "IF"
    },
    {
        "ID": 4,
        "start": 39,
        "end": 40,
        "fnc": "RECORD_NR"
    }
]

现在我很挣扎。我想对 json 对象进行排序或嵌套:

[
    {
        "ID": 1,
        "start": 2,
        "end": 60,
        "fnc": "IF",
        "contains":[
            {
                "ID": 2,
                "start": 12,
                "end": 13,
                "fnc": "RECORD_NR",
                "contains":[]
            },
            {
                "ID": 3,
                "start": 29,
                "end": 59,
                "fnc": "IF",
                "contains":[
                    {
                        "ID": 4,
                        "start": 39,
                        "end": 40,
                        "fnc": "RECORD_NR",
                        "contains":[]
                    }
                ]
            }
        ]
    }
]

有人可以帮助我实现这一目标吗?或者有谁知道更好的方法来获得预期的结果(也许是一种不同的更好的方法)?

【问题讨论】:

  • 你需要做多少?或者它是一个持续的任务?如果它只是一个相对较小的#你必须做一次(可能几十个),我会用手做。
  • 我可能会尝试使用正则表达式来做到这一点。每种输入格式一个正则表达式,类似这样。
  • 这是一项持续的任务,vb 表达式可能非常简单或复杂(例如许多嵌套的 if)。我更喜欢正则表达式解决方案,但我不具备使其工作所需的正则表达式技能。

标签: javascript json vb6


【解决方案1】:

根据需要处理的任务,只需要一个编译器即可。 那需要先把vb程序转成ast,然后遍历ast,再把ast转成js代码,就像babel做的那样。 以下链接是 an example 另外,这里有一个可以使用的第三方库。 https://github.com/zaach/jison

或者用hacky的方式,所有函数都返回一个对象属性,该属性设置一个setter,在赋值的时候,将值赋给指定的字段,最后一个名为if的js函数会处理每个参数值。

【讨论】:

  • 非常感谢。我之前搜索过编译器,但没有找到这些。我会试试的:)
【解决方案2】:

您可以尝试使用一些 PEG 解析器生成器,例如 PEG.js,因为在 PEG 中编写语法更容易。

这是一个示例 PEG.js 语法,它实现了一个表达式解析器,可以解析一些 VB 表达式的子集(没有经过太多测试):

// All operators in VBA are left-associative (incl. power)
// Precedence from https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/operator-precedence

Expr = Logical1

Logical1 = left:Logical2 t:(_ op:OperOr right:Logical2      { return { op, right } }
    )* { return t.reduce((a, e) =>                          { return { kind: "binop", left:a, op:e.op, right:e.right } }, left) }
    
Logical2 = left:Logical3 t:(_ op:'And'i right:Logical3      { return { op, right } }
    )* { return t.reduce((a, e) =>                          { return { kind: "binop", left:a, op:e.op, right:e.right } }, left) }
    
Logical3 = op:'Not'i right:Logical3                         { return { kind: "unop", op, right } }
    / Comp

Comp = left:Arith1 t:(_ op:OperComp right:Arith1            { return { op, right } }
    )* { return t.reduce((a, e) =>                          { return { kind: "binop", left:a, op:e.op, right:e.right } }, left) }

Arith1 = left:Arith2 t:(_ op:[&] right:Arith2               { return { op, right } }
    )* { return t.reduce((a, e) =>                          { return { kind: "binop", left:a, op:e.op, right:e.right } }, left) }
    
Arith2 = left:Arith3 t:(_ op:[+-] right:Arith3              { return { op, right } }
    )* { return t.reduce((a, e) =>                          { return { kind: "binop", left:a, op:e.op, right:e.right } }, left) }
    
Arith3 = left:Arith4 t:(_ op:'Mod'i right:Arith4            { return { op, right } }
    )* { return t.reduce((a, e) =>                          { return { kind: "binop", left:a, op:e.op, right:e.right } }, left) }
    
Arith4 = left:Arith5 t:(_ op:[\\] right:Arith5              { return { op, right } }
    )* { return t.reduce((a, e) =>                          { return { kind: "binop", left:a, op:e.op, right:e.right } }, left) }
    
Arith5 = left:Arith6 t:(_ op:[*/] right:Arith6              { return { op, right } }
    )* { return t.reduce((a, e) =>                          { return { kind: "binop", left:a, op:e.op, right:e.right } }, left) }
    
Arith6 = op:[+-] !(_ Number) right:Arith6                   { return { kind: "unop", op, right } }
    / Arith7
    
Arith7 = left:Primary t:(_ op:[\^] right:Primary            { return { op, right } }
    )* { return t.reduce((a, e) =>                          { return { kind: "binop", left:a, op:e.op, right:e.right } }, left) }

Primary = _ id:Ident _ '(' params:Params _ ')'              { return { kind: "call", id, params } }
    / _ id:Ident                                            { return { kind: "var", id } }
    / _ n:Number                                            { return n }
    / _ s:String                                            { return s }
    / _ '(' e:Expr _ ')'                                    { return e }

Params = p:Expr _ ',' r:Params                              { return [p].concat(r) }
    / e:Expr                                                { return [e] }
    / _                                                     { return [] }

// Lexical rules

OperOr = 'Or'i / 'Xor'i / 'Eqv'i / 'Imp'i
OperComp = '<=' / '=>' / '<>' / [<>=] / 'Like'i / 'Is'i
Ident = [A-Za-z_][A-Za-z0-9_]*                              { return text() }
String = '"' s:('""' / [^"])* '"'                           { return s.join("").replace('""', '"') }
Number = ([+-] _)? Int ('.' Int?)? ([Ee] [+-]? Int)?        { return Number(text()) }
Int = [0-9]+
_ = [ \t\r\n]*

这会将您的IF(RECORD_NR() = -1, "NR,1",IF(RECORD_NR() = 2, "NR,2", "NR3")) 解析成这样的解析树:

{
   "kind": "call",
   "id": "IF",
   "params": [
      {
         "kind": "binop",
         "left": {
            "kind": "call",
            "id": "RECORD_NR",
            "params": []
         },
         "op": "=",
         "right": -1
      },
      "NR,1",
      {
         "kind": "call",
         "id": "IF",
         "params": [
            {
               "kind": "binop",
               "left": {
                  "kind": "call",
                  "id": "RECORD_NR",
                  "params": []
               },
               "op": "=",
               "right": 2
            },
            "NR,2",
            "NR3"
         ]
      }
   ]
}

遍历此类解析树并发出适当的 JS 代码来评估表达式取决于您。

您可以在https://pegjs.org/online 的 PEG.js 操场上在线修改上述语法

【讨论】:

  • 非常感谢。我也会试一试。目前我只翻译部分表达式,并为每个 VB 函数创建了 js 函数。这很好,但也许我可以用 PEG 优化我的代码。
猜你喜欢
  • 1970-01-01
  • 2011-10-02
  • 1970-01-01
  • 2019-05-11
  • 2023-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多