前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Rc-lang开发周记17 一点AST检查

Rc-lang开发周记17 一点AST检查

作者头像
AkemiHomura
发布2023-04-07 16:08:58
2330
发布2023-04-07 16:08:58
举报
文章被收录于专栏:homura的博客homura的博客
69589494_p0.png
69589494_p0.png

聪明如我怎么会写出ast有错误的代码 pixiv:69589494

先说一声五一快乐!久违的长假,之后会花一些时间把其他一些写到一半的博客整理出来

本来想要好好做一下检查相关以及类型推导的工作,但是目前来说我更需要先学习优化方面的知识,因此关于ast的检查以及类型推导和类型检查做的比较简易,过后有时间再回来做。本周虽然做了部分类型推导和类型检查,但是只做了一半,剩下的部分可能要下周再说了。下周大概就能做完简单的类型推导和检查

AST检查

目前所实现的检查无外乎这么几类

  1. 名称冲突
  2. 未定义符号
  3. 变量的声明类型或者初始值必须有一个存在

我挑出一些经典的部分讲解,不过多赘述重复的部分了

实际上能做的类型无关的检查还有非常多

名称冲突

代码语言:javascript
复制
def dupNameCheck(names: List[Ident]): Result = {
  dupCheck(names, "Name")
}

def dupCheck[T <: ASTNode](values: List[T], valueName: String): Result = {
  val s = Set[T]()
  values.filterNot(s.add).map(n => ValidateError(n, s"$valueName $n Dup"))
}

def checkModule(module: RcModule): Result = {
  dupNameCheck(module.items.map(item => item match
    case Item.Class(name, _, _, _) => name
    case Item.Method(decl, _) => decl.name
  )):::module.items.flatMap(checkItem)
}

比如说Module的检查中对所有item的名字检查是否存在冲突,并且再check每个Item本身

关于返回值的Result只是一个type alias

代码语言:javascript
复制
type Result = List[ValidateError]
case class ValidateError(node: ASTNode, reason: String)

这里还有很多待改进的空间,比如说将实际的错误分类,或者写一个diagnosis类来管理这些错误信息等等

这里使用一个type alias也是为了后面修改时候方便

这里可以看到所有的错误信息都是组合之后返回,原因是我想将代码中的副作用范围缩到最小,这样能够保证调用的结果尽可能的不受外部状态影响

未定义的符号

目前只做了一些简单的处理。这里还没有处理全局的符号(比如说函数和类)

代码语言:javascript
复制
case class Scope(var localTable: Set[Ident] = Set()) {

  def add(ident: Ident): Boolean = {
    localTable.add(ident)
  }

  def contains(ident: Ident): Boolean = {
    localTable.contains(ident)
  }
}

case class ScopeManager() {
  private var scopes = List[Scope]()
  def enter[T](f:() => T): T = {
    enter(Params(List()), f)
  }

  def enter[T](params: Params, f:() => T): T = {
    val oldScope = scopes
    scopes ::= Scope(mutable.Set.from(params.params.map(_.name)))
    val result = f()
    scopes = oldScope
    result
  }

  def curScope: Scope = scopes.last

  def add(ident: Ident): Boolean = curScope.add(ident)

  def contains(ident: Ident): Boolean = {
    !scopes.exists(_.contains(ident))
  }

  def curContains(ident: Ident): Boolean = curScope.contains(ident)
}

每个Scope有自己的table,每次通过enter进入一个table则将当前的放到List中

代码语言:javascript
复制
def checkBlock(block: Block, params: Params = Params(List())): Result = {
  scopes.enter(params, () => {
    block.stmts.flatMap(checkStmt)
  })
}

def checkMethod(method: Method): Result = {
  checkMethodDecl(method.decl)
  checkBlock(method.body, method.decl.inputs)
}

在每次进入一个Block的时候则进入了一个新的scope,比如说一个Method的body的expr

对于Id表达式则会去检查是否存在这个符号,

代码语言:javascript
复制
case Expr.Identifier(id) => checkCond(scopes.contains(id), expr, "$name not decl")

初始值与类型二选一

代码语言:javascript
复制
def fieldDefValid(fieldDef: FieldDef): Result = {
  fieldDef.initValue match {
    case Some(expr) => checkExpr(expr)
    case None => checkCond(fieldDef.ty != TyInfo.Infer, fieldDef, "Field without initValue need spec Type")
  }
}

对于类的field做了这样的检查,存在initValue则去检查expr,否则检查ty是否为需要Infer的。如果没有initValue也没有ty信息,那我们无法在后面类型推导的时候得出类型

本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-05-01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • AST检查
  • 名称冲突
  • 未定义的符号
  • 初始值与类型二选一
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com