Циклическая зависимость модулей - PullRequest
0 голосов
/ 08 июня 2018

Я хочу написать парсер на F #, и по причинам я должен использовать Antlr.Это означает, что я должен определить класс Visitor для каждого узла AST, который я хочу проанализировать.Теперь у меня проблема в том, что есть некоторые правила с циклическими зависимостями, такие как:

boolExpr : boolTerm 'or' boolTerm ;
boolTerm : boolAtom 'and' boolAtom ;
boolAtom : '(' boolExpr ')' 
         | ... ;

, что означает, что мне нужно 3 класса посетителя, которые имеют одинаковую циклическую зависимость, и я хочу, чтобы каждый из них был в отдельном файле

//BoolExprVisitor.fs
let boolExprVisitor = { new BaseVisitor<AST.BoolExpr>() with
    override __.VisitBoolExpr(context: BoolExprContext) =
        context.boolTerm() |> mapAccept boolTermVisitor |> AST.BoolExpr
}

//BoolTermVisitor.fs
let boolTermVisitor = { new BaseVisitor<AST.BoolTerm>() with
    override __.VisitBoolTerm(context: BoolTermContext) =
        context.boolAtom() |> mapAccept boolAtomVisitor |> AST.BoolTerm
}

//BoolAtomVisitor.fs
let boolAtomVisitor = { new BaseVisitor<AST.BoolAtom>() with
    override __.VisitBoolAtom(context: BoolAtomContext) =
        context.boolExpr() |> accept boolExprVisitor |> AST.BoolAtom
}

Но F # не любит эти циклические зависимости.Как я могу заставить F # принять их или реструктурировать своих посетителей так, чтобы они не нуждались в циклических зависимостях?

Ответы [ 2 ]

0 голосов
/ 22 июня 2018

Для тех, кто столкнется с этой проблемой в будущем:
Как сказал rmunn, тот факт, что я хотел, чтобы классы находились в разных файлах, был просто не хорошим дизайном.Кроме того, мне не нужны были разные узлы AST для BoolTerm, BoolAtom и BoolExpr, поскольку все они могут быть описаны как один и тот же узел BoolExpr.

. Моим решением было объединить все логические выражения.посетителей в одном классе (и объединить все файлы для выражения посетителей в один файл):

//AST.fs
type BoolExpr = 
    | BoolConjunctionExpr of BoolOp * BoolExpr list
    | ...

//ExpressionVisitors.fs
let boolExprVisitor = { new BaseVisitor<AST.BoolExpr>() with
    override this.VisitBoolExpr(context: BoolExprContext) =
        context.boolTerm() |> mapAccept this |> AST.BoolConjunctionExpr AST.Or

    override this.VisitBoolTerm(context: BoolTermContext) =
        context.boolAtom() |> mapAccept this |> AST.BoolConjunctionExpr AST.And

    override this.VisitBoolAtom(context: BoolAtomContext) =
        context.boolExpr() |> accept this
}
0 голосов
/ 08 июня 2018

Я думаю, что если вы не хотите создавать экземпляры посетителей одновременно с использованием and, вам придется прибегнуть к изменяемым переменным.Создайте изменяемую переменную для одного из посетителей со значением по умолчанию для его типа.Затем, когда у вас есть фактический экземпляр, присвойте его переменной.

//BoolExprVisitor.fs
module BoolExprVisitor =
    let mutable boolTermVisitor = Unchecked.defaultof<BaseVisitor<AST.BoolTerm>>

    let boolExprVisitor = { new BaseVisitor<AST.BoolExpr>() with
        override __.VisitBoolExpr(context: BoolExprContext) =
            context.boolTerm() |> mapAccept boolTermVisitor |> AST.BoolExpr
    }

//BoolAtomVisitor.fs
module BoolAtomVisitor =
    open BoolExprVisitor
    let boolAtomVisitor = { new BaseVisitor<AST.BoolAtom>() with
        override __.VisitBoolAtom(context: BoolAtomContext) =
            context.boolExpr() |> accept boolExprVisitor |> AST.BoolAtom
    }


//BoolTermVisitor.fs
module BoolTermVisitor
    open BoolAtomVisitor

    let boolTermVisitor = { new BaseVisitor<AST.BoolTerm>() with
        override __.VisitBoolTerm(context: BoolTermContext) =
            context.boolAtom() |> mapAccept boolAtomVisitor |> AST.BoolTerm
    }

    BoolExprVisitor.boolTermVisitor <- boolTermVisitor

Обратите внимание, что это по сути то же самое, что и использование createParserForwardedToRef в FParsec.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...