目前为止我们创建的文件列表:

|- com.taozeyu.taolan.analysis|- FirstSetConstructor|- LexicalAnalysis|- LexicalAnalysisException|- NonTerminalSymbol|- SignParser|- SyntacticDefine|- TerminalSymbol|- Token

我们来看看 SyntacticDefine.java 文件(142~159 行):

    node(Exp.StartChunk).or(Exp.Chunk),node(Exp.Chunk).or(Exp.SpaceOrEnter,node().or(Exp.Line, Exp.Space,node().or(token(Type.NewLine), Exp.SpaceOrEnter).or(token(Type.EndSymbol))).sign('?')).sign('*'),node(Exp.Line).or(Exp.Command).or(Exp.Operate).or(Exp.DefineVariable).or(Exp.DefineFunction).or(Exp.IfElseChunk).or(Exp.WhileChunk).or(Exp.DoUntilChunk).or(Exp.ForEachChunk).or(Exp.TryCatch)

我们定义了三个命名了的非终结符:StartChunk、Chunk、Line。

其中 StartChunk 是我们展开式的所有起点,每一个 tao 语言的源代码文件都从 StartChunk 开始展开。这里我简单的将其展开为 Chunk。

而 Chunk 表示一个语法块,它由许多行(Line)构成。但是在文法定义中,我必须为其每行的首位定义许多必要的杂项。从展开式中,可以看出,除了 Line 这个非终结符,还有其他一些非终结符:SpaceOrEnter、Space、NewLine、EndSymbol。

这些非终结符的含义,可以在 SyntacticDefine.java 文件的对应位置找到(可以使用 ctrl+F 搜索),只要找到它们对应的展开式,相信不难理机它们的含义。特别的 EndSymbol,即是 Tokenizer 代表源代码解析结束的终止符。

Chunk 是语言文法定义中最重要的非终结符。

再看看 Line,这个非终结符可以简单的理解为“一行代码”。自然,Chunk 就是由很多行代码组成的。特定的一行代码可以写很多东西,可能用于定义一个变量,也可能是一行赋值语句,也可能是在调用一个方法。总之,它可以是很多很多种东西。从之前这段代码中,可以看到,它有很多种展开式。

大家可以通过 GitHub 自行查看 Line 展开后对应的非终结符具体还能再如何展开,但受限于篇幅,我只会对其中一部分进行讲解。

node(Exp.Operate).or(Exp.L0Expression, Exp.When)

Operate 是 Line 可能展开的形式之一,在 tao 语言中,如下代码就是一行典型的 Operate:

count = size * 2 + 1

从表达式来看,它是由一个 0 级表达式(L0Expression)和一个 When 类型的非终结符组成。

其中 L0Expression 代表一个表达式,它可能是一行赋值语句,也可能是一个函数调用,它是我们文法定义中一个比较复杂,但却到处都会出现的东西,我会在之后的章节进行简单介绍。

随后,紧接而来的是 When 非终结符。我们来看看 When 的定义是怎么样的:

node(Exp.When).or(node().or(Exp.SplitSpaceSign).sign('?'),node().or(token(Type.Keyword, "when"), Exp.SplitSpaceSign, Exp.L0Expression).sign('?'))

它主要由一个 when 关键字紧接令一个 0 级表达式组成。但特别注意结尾的 .sign('?') 数量词表明,整个非终结符 When 都是可选的。也就是说,Operate 后面不写 when 也没有关系。

一行写了 when 的 tao 语言代码可能是如下形式:

count = size * 2 + 1 when size > 1

这是一种简写形式,在 tao 语言中等价于如下写法:

if size > 1count = size * 2 + 1
end

接下来,我们来看看 DefineVariable 非终结符:

node(Exp.DefineVariable).or(token(Type.Keyword, "var"), Exp.Space, Exp.DefineVariableElement,node().or(Exp.Space, token(Type.Sign, ","),Exp.SpaceOrEnter, Exp.DefineVariableElement).sign('*'))

注意到 DefineVariableElement 似乎还可以继续展开:

node(Exp.DefineVariableElement).or(token(Type.Identifier), Exp.Space,node().or(token(Type.Sign, "="), Exp.Space, Exp.L0Expression).sign('?'))

这表明 tao 语言中定义局部变量的形式如下:

var cat = take_cat(), dog = Dog.alloc().init()

当然,从 DefineVariableElement 的展开式中的 .sign('?') 量词可以知道,去掉等号以及等号后面的表达式,也是合法的局部变量定义:

var cat, dog

甚至,一次只定义一个变量,当然也是可以的:

var cat

在接下来,就是 IfElseChunk 了:

node(Exp.IfElseChunk).or(token(Type.Keyword, "if"), Exp.Space,Exp.L0Expression, Exp.SpaceOrEnter,Exp.Chunk,node().or(token(Type.Keyword, "elsif"), Exp.Space,Exp.L0Expression, Exp.SpaceOrEnter,Exp.Chunk).sign('*'),node().or(token(Type.Keyword, "else"), Exp.SpaceOrEnter,Exp.Chunk).sign('?'),Exp.SpaceOrEnter,token(Type.Keyword, "end"))

注意到,这里展开出现了四种关键字:if、elsif、else、end。

tao 语言中,一个典型的 if-else-chunk 是如下这样子的:

if check_condition(100)a = 1 + 1
elsif a > 2a += 3
elsea = 0
end

当然,elsif 可以重复出现0~N 次:

if check_condition(100)a = 1 + 1
elsif a > 2a += 3
elsif a > 3a += 4
elsif a > 4a += 7
elsea = 0
end

而 else 是可选的,可以没有:

if check_condition(100)a = 1 + 1
elsif a > 2a += 3
end

也可以只剩下 if 和 end:

if check_condition(100)a = 1 + 1
end

从文法定义的角度来看,if-else-chunk 这种形式的变化,我通过数量词 sign('*') 和 sign('?') 来控制。

从零开始写个编译器吧 - 程序流控制相关推荐

  1. 编译器入门 语法分析器 java_从零开始写个编译器吧 - Parser 语法分析器

    Parser(语法分析器)的编写相对于 Tokenizer (词法分析器)要复杂得多,因此,在编写之前可能也会铺垫得更多一些.当然,本系列旨在"写出"一个编译器,所以理论方面只会简 ...

  2. 自己写编译器词法分析c语言,从零开始写个编译器吧 - 开始写词法分析器(3)...

    上周周末旅游去了,就没更新了,虽然回到海拔0m的地区,不过目前似乎还在缺氧,所以本次就少更点吧. 这章将结束词法分析的部分. 在之前的章节(第7章从零开始写个编译器吧 - 开始写词法分析器(1))中我 ...

  3. 从零开始写个编译器吧 - 单词化简述(Tokenization)

    2019独角兽企业重金招聘Python工程师标准>>> 实际上,所谓的源代码,我们可以将其视为一段长长的字符串.所谓字符串,即是字符的有序集.但是,字符本身作为编译器的输入单位,粒度 ...

  4. 从零开始写个编译器吧 - tao 语言的文法定义(下)

    目前为止我们创建的文件列表: |- com.taozeyu.taolan.analysis|- FirstSetConstructor|- LexicalAnalysis|- LexicalAnaly ...

  5. Java终结符_从零开始写个编译器吧 - TerminalSymbol.java 与 NonTerminalSymbol.java

    首先是 TerminalSymbol.java 即终结符. package com.taozeyu.taolan.analysis; import java.util.HashSet; import ...

  6. mysql c测试程序_Linux平台下从零开始写一个C语言访问MySQL的测试程序

    Linux 平台下从零开始写一个 C 语言访问 MySQL 的测试程序 2010-8-20 Hu Dennis Chengdu 前置条件: (1) Linux 已经安装好 mysql 数据库: (2) ...

  7. 从零开始写高性能的人脸识别服务器(一)

    文章目录 从零开始写高性能的人脸识别服务器(一) 1 技术选型 2 环境准备 2.1 安装**face_recoginize** 2.2 安装Redis 2.3 Qt5.9.9的安装 3 效果演示 3 ...

  8. 学了编译原理能否用 Java 写一个编译器或解释器?

    16 个回答 默认排序​ RednaxelaFX JavaScript.编译原理.编程 等 7 个话题的优秀回答者 282 人赞同了该回答 能.我一开始学编译原理的时候就是用Java写了好多小编译器和 ...

  9. rust 手动关闭子线程_从零开始写 OS (9) —— 内核线程

    上一篇 小源:从零开始写 OS (8) -- 创建页表​zhuanlan.zhihu.com 本章代码对应 commit :de86ae6e1e8bdfe3388530f82b2081fe29e40b ...

最新文章

  1. vue实现动态css,Vue中如何动态绑定CSS
  2. 用python解“12-24小时制”题
  3. eclipse中测试Hibernate异常报 ORA-00926: 缺失 VALUES 关键字
  4. 周末话题-元编程(metaprogramming)
  5. jdbc调用存储过程的方法
  6. jquery常见操作分享
  7. Qt_Window@Qt Command Prompt从命令行创建工程
  8. 网页制作的基本语言html,网页制作基础语言HTML.ppt
  9. 项目改用GoModules管理依赖的方法和经验总结
  10. C# 引用访问权限,很多老手都不懂
  11. FPGA 38译码器
  12. 关于logo1_.exe(威金病毒)蠕虫病毒的清楚,
  13. 服务器显卡驱动重装系统,GPU显卡驱动重装
  14. vm安装win xp镜像遇到的安装问题之一
  15. 图像处理之颜色检测分类标记(Python OpenCV实现)
  16. Python breakpoint()函数
  17. 微信小程序页面顶部出现一段空白解决方法
  18. 华为发布5G“天罡”“巴龙5000”芯片 5G折叠屏手机2月发布
  19. JSON是什么 JSON怎么用
  20. Valgrind使用简介

热门文章

  1. [合作] 钢结构结构健康监测研究与实验
  2. DbUtils: JDBC Utility Component Examples翻译
  3. 关于IE、Firefox、Opera页面呈现异同(转载)
  4. [CF]Round 516
  5. Illumina联手IBM Watson Health解读癌症基因谱
  6. phpMyadmin出现错误提示
  7. mysql语句大全(2)
  8. 团队作业——微博网站小调查
  9. js小技巧,收藏.作者:空军上将
  10. jQuery-ui源代码重点难点分析