JParser (10) AST 프로세서 구현 (WIP)
이 포스팅에선 앞선 포스팅에서 설명한 AST 문법을 실제로 어떻게 구현했는지 좀 더 자세히 이야기해보고자 한다. 최종적으로 스칼라 코드가 생성되고, 클래스 자료 구조 코드를 생성해주고, parse tree를 받아서 ast 데이터를 반환하는 match<넌터미널 이름>
(e.g. matchStart
, matchExpr
) 함수들을 생성해준다.
TODO 실제 사례
##
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를 받아서 문법을 정의하는 Symbol
과 ValuefyExpr
의 페어를 반환하는 valuefySymbol
, valuefyPExpr
, valuefySequence
등의 함수가 GrammarTransformer.scala에 정의되어 있다.
TypeInferer, InferredTypeCollector
그래프..로 구현했다 복잡해서 그냥 반복하게 바꿨던듯? 그런 류의 알고리즘을 통칭하는 이름이 있는데..
그래프로 구현하면 더 깔끔하고 아름답긴 했겠지만 너무 복잡할땐 ** 알고리즘도 좋은 방법이 된다. 너무 느려서 문제가 되면 나중에 고칠 수도 있는 거니까.
TypeRelationCollector, ClassRelationCollector
future work - directly convert parsing context history to AST
전체 목차:
이제 jparser에 대한 이야기는 얼추 마무리 되었다. 다음 포스팅에서는 jparser를 실제로 사용해서 구현한 프로젝트에 대해서 이야기해보고, 이어서 앞으로 남은 할 일에 대해 이야기해보자.
- JParser (1) 파싱
- JParser (2) Conditional Derivation Grammar
- JParser (3) CDG 사용 팁
- JParser (4) Naive 파싱 알고리즘
- JParser (5) Naive 파싱 알고리즘 동작 예제
- JParser (6) 승인 조건
- JParser (7) 파스 트리 재구성
- JParser (8) 마일스톤 파싱 알고리즘 (WIP)
- JParser (9) Abstract Syntax Tree 프로세서
- JParser (10) AST 프로세서 구현 (WIP)
- JParser (11) 결론 1 실제 사용 사례
- JParser (12) 결론 2 future works