各位大大,编译中 parser 如何能够构建出自己的 抽象语法?

2020-02-17 15:13:49 +08:00
 black11black

大家好,最近想做一个可编程化的类似 cicd 那种流程控制项目

我个人比较菜,想先做个简单的 demo,想要实现如下这样的效果:

def task1(file):
    ffmpeg -i file -o file.mp4 # etc. 

def task2(file):
    # do something else

def workflow(folder):
    foreach file in folder:
        task1(file)
        task2(file)

↑ 想要实现的是比如往程序里输入这样一个配置文件( python 语法抽象比较简洁,姑且先用类似的语法了)然后程序就会按照我设定好的这一套工作流程,按顺序执行所有设定好的工作。

要让程序理解输入的工作流程描述文件,需要进行简单的词法和文法分析,然后抽象成 ast,因为只是个简单的 demo,里面没有什么复杂的东西,只要能实现定义与循环执行就可以了,甚至不需要表达式计算的功能

按我理解,我的算法流程应该是这样的 1、先是词法分析(这个已经搞定了), 2、然后把代码分成一段一段的(比如类似 python 这种按照缩进分段,或者用{}表示,还没想好) 3、然后我需要抽象出一种当前语言的元语法

#比如
stmt : stmt expr
expr : (expr)
expr : NODE

这种感觉的,(就是编译原理课上教的那种) 4、再然后实现一个 LL1 算法,对每段分别处理,就能 parse 出抽象语法树了。

现在的一个犯难是并不知道怎么抽象出这种元语法(甚至在这种只有定义和调用、以及循环的简单情况下) 当初编译原理上的课都还给老师了,唉 现在还记得比如如果单纯要写一个描述运算表达式的元语法是很简单的,大概类似这样:

expr : (expr)
expr : expr + expr
expr : expr * expr
expr : expr - expr
expr : expr / expr
expr : NODE
expr : -NODE

这种感觉,然后实际操作的时候类似一个简单的栈虚拟机就能搞定表达式运算,这个很简单。 但是这种有定义有调用的元语法该怎么写啊,老师当初也没讲过例子啊,啊啊啊啊啊啊啊啊啊啊啊啊

1487 次点击
所在节点    问与答
10 条回复
scalaer
2020-02-17 15:20:58 +08:00
先定个小目标, 实现个能跑的 dsl,dsl 多了, 再进一步抽象呗
black11black
2020-02-17 15:24:33 +08:00
@scalaer

我现在在做的就算 dsl 吧...我觉得我这个目标定的也不大啊

关于 parse,如果不抽象语法树的话,最土的做法是写很多条件判断,比如在 def 后面找一个 name token,然后再找括号,再找......找不到就报错之类的

这么写的话感觉就很 low 啊。。
scalaer
2020-02-17 15:44:14 +08:00
你看看 antlr, 没必要自己写 parser
crella
2020-02-17 16:17:54 +08:00
……隔壁 ruby 做这个感觉还算简单啊,不清楚 python 技术链

反正我在代码里动态 eval 新的函数是没什么问题
secondwtq
2020-02-17 17:04:26 +08:00
随便找个栗子就行: https://www.lua.org/manual/5.3/manual.html
翻到最底下
black11black
2020-02-17 17:30:32 +08:00
@secondwtq

ORZ 好长,翻跪了

program -> { [ def | statement ] EOL }
def -> "def" IDENTIFIER "(" IDENTIFIER ")" : block
block -> "{" statement { EOL statement } "}"
statement -> "foreach" STRING "in" IDENTIFIER ":" block | simple
simple -> { IDENTIFIER } | IDENTIFIER '(' IDENTIFIER ')' | IDENTIFIER '=' STRING

↑ 所以现在我感觉我设计的语法应该可以抽象成这样
只有 define,循环 , 执行三个功能,没有 expr 计算。

然后问题是,这东西咋搞成 LL1parser 啊,看了看感觉全是左循环左因子

网上看了个例子,简单的 expr 运算拆成 LL1 就要写这么长了:

s->e
e->te'
e'->+te'
e'->-te'
e'->空
t->ft'
t'->*ft'
t'->/ft'
t'->空
f->NUMBER
f->'('e')'

感觉上面的要改的话要写死自己啊。。
ai277014717
2020-02-17 17:37:01 +08:00
lex/yacc 或者 antlr 然后直接处理回调就好了。
momocraft
2020-02-17 17:38:44 +08:00
自己撸一整套本来就是是不小的工程

如果你面向实用,要用户学你发明的语言也有成本

要不要考虑用已有的语言+已有的 parser/interpreter
secondwtq
2020-02-17 17:50:53 +08:00
@black11black 你为啥非要死抠 LL(1) 呢
thautwarm
2020-12-06 03:38:56 +08:00
还需要帮忙吗?在下可以带给你新世界

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/645250

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX