JParser (10) AST 프로세서 구현 (WIP)

이 포스팅에선 앞선 포스팅에서 설명한 AST 문법을 실제로 어떻게 구현했는지 좀 더 자세히 이야기해보고자 한다. 최종적으로 스칼라 코드가 생성되고, 클래스 자료 구조 코드를 생성해주고, parse tree를 받아서 ast 데이터를 반환하는 match<넌터미널 이름>(e.g. matchStart, matchExpr) 함수들을 생성해준다.

TODO 실제 사례

code

##

symbol과 match 함수의 내용이 잘 correspond해야하기 때문에 문법을 순회하면서 symbol과 프로세서를 동시에 생성한다. 예를 들어, TODO

프로세서는 다음과 같은 형태로 자료 구조를 잡았다.

각 프로세서가 실제 코드로 생성될 때는 다음과 같이 된다.

ValuefyExpr

ValuefyExpr.scala에 정의되어 있다.

sealed class ValuefyExpr

object ValuefyExpr {
  case object InputNode extends ValuefyExpr

  // input ParseNode를 nonterminal의 RHS로 보고 valuefy
  case class MatchNonterminal(nonterminalName: String) extends ValuefyExpr

  // input ParseNode를 Unbind해서 binding된 심볼이 symbol인지 확인하고 body에 대해서 expr로 valuefy
  case class Unbind(symbol: Symbols.Symbol, expr: ValuefyExpr) extends ValuefyExpr

  // input ParseNode가 joinSymbol의 JoinNode일 것으로 가정. JoinNode의 sym 부분을 bodyProcessor로 valuefy
  case class JoinBody(bodyProcessor: ValuefyExpr) extends ValuefyExpr

  // input ParseNode가 joinSymbol의 JoinNode일 것으로 가정. JoinNode의 join 부분을 bodyProcessor로 valuefy
  case class JoinCond(condProcessor: ValuefyExpr) extends ValuefyExpr

  // input ParseNode가 SequenceNode일 것으로 가정하고, index번째 Node에 대해 expr로 valuefy
  case class SeqElemAt(index: Int, expr: ValuefyExpr) extends ValuefyExpr

  // input ParseNode가 0+ Repeat symbol일 것으로 보고 풀어서, 각 ParseNode에 대해 elemProcessor로 valuefy
  case class UnrollRepeatFromZero(elemProcessor: ValuefyExpr) extends ValuefyExpr

  // input ParseNode가 1+ Repeat symbol일 것으로 보고 풀어서, 각 ParseNode에 대해 elemProcessor로 valuefy
  case class UnrollRepeatFromOne(elemProcessor: ValuefyExpr) extends ValuefyExpr

  // input ParseNode가 BindNode일 것으로 보고, 어떤 symbol로 bind됐는지 보고 맞는 node에 대해 valuefy
  case class UnrollChoices(choices: Map[Symbols.Symbol, ValuefyExpr]) extends ValuefyExpr

  // input ParamNode를 params로 valuefy해서 class 생성해서 반환
  case class ConstructCall(className: String, params: List[ValuefyExpr]) extends ValuefyExpr

  object FuncType extends Enumeration {
    val IsPresent, IsEmpty, Chr, Str = Value
  }

  // TODO input ParamNode를 params로 valuefy해서 builtin function 콜해서 반환
  case class FuncCall(funcType: FuncType.Value, params: List[ValuefyExpr]) extends ValuefyExpr

  // input ParamNode를 elems로 valuefy해서 array 반환
  case class ArrayExpr(elems: List[ValuefyExpr]) extends ValuefyExpr

  object BinOpType extends Enumeration {
    val ADD, EQ, NE, BOOL_AND, BOOL_OR = Value
  }

  // TODO input ParseNode를 lhs로 가공한 값, rhs로 가공한 값을 op으로 binary 연산해서 반환
  case class BinOp(op: BinOpType.Value, lhs: ValuefyExpr, rhs: ValuefyExpr) extends ValuefyExpr

  object PreOpType extends Enumeration {
    val NOT: PreOpType.Value = Value
  }

  // TODO input ParseNode를 expr로 가공한 값을 op으로 binary 연산해서 반환
  case class PreOp(op: PreOpType.Value, expr: ValuefyExpr) extends ValuefyExpr

  // TODO input ParseNode를 expr로 가공한 값, ifNull로 가공한 값을 elvis 연산해서 반환
  case class ElvisOp(expr: ValuefyExpr, ifNull: ValuefyExpr) extends ValuefyExpr

  // TODO input ParseNode를 condition으로 가공한 값이 true이면 input을 ifTrue로 가공한 값을, false이면 input을 ifFalse로 가공한 값을 반환
  case class TernaryOp(condition: ValuefyExpr, ifTrue: ValuefyExpr, ifFalse: ValuefyExpr) extends ValuefyExpr

  // Literal과 Enum은 input ParseNode와 관계없이 지정된 값을 반환
  abstract sealed class Literal extends ValuefyExpr

  case object NullLiteral extends Literal

  case class BoolLiteral(value: Boolean) extends Literal

  case class CharLiteral(value: Char) extends Literal

  // TerminalNode를 받아서 그 값을 CharValue로 변환
  case object CharFromTerminalLiteral extends Literal

  case class StringLiteral(value: String) extends Literal

  abstract sealed class EnumValue extends ValuefyExpr

  case class CanonicalEnumValue(enumName: String, enumValue: String) extends EnumValue

  case class ShortenedEnumValue(unspecifiedEnumTypeId: Int, enumValue: String) extends EnumValue
}

TODO ValuefyExpr 예제로 설명. 코드 생성도 설명 TODO Nonterminal -> ValuefyExpr

AST를 받아서 문법을 정의하는 SymbolValuefyExpr의 페어를 반환하는 valuefySymbol, valuefyPExpr, valuefySequence 등의 함수가 GrammarTransformer.scala에 정의되어 있다.

TypeInferer, InferredTypeCollector

그래프..로 구현했다 복잡해서 그냥 반복하게 바꿨던듯? 그런 류의 알고리즘을 통칭하는 이름이 있는데..

그래프로 구현하면 더 깔끔하고 아름답긴 했겠지만 너무 복잡할땐 ** 알고리즘도 좋은 방법이 된다. 너무 느려서 문제가 되면 나중에 고칠 수도 있는 거니까.

TypeRelationCollector, ClassRelationCollector

future work - directly convert parsing context history to AST

전체 목차:

이제 jparser에 대한 이야기는 얼추 마무리 되었다. 다음 포스팅에서는 jparser를 실제로 사용해서 구현한 프로젝트에 대해서 이야기해보고, 이어서 앞으로 남은 할 일에 대해 이야기해보자.

이 페이지에서 오류나 문제점을 발견하시면 이메일로 알려주시면 감사하겠습니다.
뒤로