4

fslexを使用して、1 つのパターンに対して複数のトークンを返したいのですが、それを実現する方法がわかりません。複数のトークンを返す別のルール関数を使用してもうまくいきます。

私はこのようなものを使用しようとしています:

let identifier = [ 'a'-'z' 'A'-'Z' ]+

// ...

rule tokenize = parse
// ...
| '.' identifier '(' { let value = lexeme lexbuf
                       match operations.TryFind(value) with
                      // TODO: here is the problem:
                      // I would like to return like [DOT; op; LPAREN]
                      | Some op -> op
                      | None    -> ID(value) }

| identifier         { ID (lexeme lexbuf) }
// ...

ここで解決しようとしている問題は、が と の間にoperationsある場合にのみ、事前定義されたトークン (マップを参照)と照合することです。それ以外の場合、一致は として返されます。identifier.(ID

私は fslex にかなり慣れていないので、正しい方向への指針を喜んでいます。

4

3 に答える 3

4

よし、ここだ。

各レクサー ルール (つまり)は、任意のタイプrule <name> = parse .. cases ..の関数<name> : LexBuffer<char> -> 'aを定義します。通常、トークン (おそらく FsYacc によって定義されている) を返すので、次のようにテキストを解析できます。'a

let parse text =
    let lexbuf = LexBuffer<char>.FromString text
    Parser.start Lexer.tokenize lexbuf

Parser.startタイプの解析関数 (FsYacc ファイルから) はどこにありますか(LexBuffer<char> -> Token) -> LexBuffer<char> -> AST(TokenASTはあなたのタイプであり、それらについて特別なことはありません)。

あなたの場合、あなたがしたい<name> : LexBuffer<char> -> 'a listので、あなたがしなければならないのはこれだけです:

let parse' text =
    let lexbuf = LexBuffer<char>.FromString text
    let tokenize =
        let stack = ref []
        fun lexbuf ->
        while List.isEmpty !stack do
            stack := Lexer.tokenize lexbuf
        let (token :: stack') = !stack // can never get match failure,
                                        // else the while wouldn't have exited
        stack := stack'
        token
    Parser.start tokenize lexbuf

これは、レクサーが提供するトークンを単純に保存し、それらを 1 つずつパーサーに渡します (必要に応じてさらにトークンを生成します)。

于 2012-12-19T14:22:13.287 に答える
3

「...識別子が . と ( の間にある場合のみ」のような意味解析をレクサー (fslex) から除外し、代わりにパーサー (fsyacc) 用に保存するようにしてください。つまり、1 つのオプションは、レクサーがoperations:

let identifier = [ 'a'-'z' 'A'-'Z' ]+    
// ...
rule tokenize = parse
// ...
| '.' { DOT }
| '(' { LPAREN }
| identifier { ID (lexeme lexbuf) }
// ...

次に、 fsyacc で次のようなルールで問題を解決します。

| DOT ID LPAREN { match operations.TryFind($2) with
                  | Some op -> Ast.Op(op)
                  | None    -> Ast.Id($2) }

コメントに応じて更新:

おそらく、レクサーで次のようになります。

let identifier = [ 'a'-'z' 'A'-'Z' ]+   
let operations =
  [
    "op1", OP1
    "op2", OP2
    //...
  ] |> Map.ofList 

// ...
rule tokenize = parse
// ...
| '.' { DOT }
| '(' { LPAREN }
| identifier 
  { 
    let input = lexeme lexbuf
    match keywords |> Map.tryFind input with
    | Some(token) -> token
    | None -> ID(input) 
  }
// ...

そしてあなたのパーサーで:

| DOT ID LPAREN { ... }
| DOT OP1 LPAREN { ... }
| DOT OP2 LPAREN { ... }

IDしたがって、パーサーでs とs が aと a のoperation間にある必要があるというルールを強制し、レクサーを本来あるべきように単純に保ちます (トークンのストリームを提供するために、関連するトークンの有効性を強制する方法はほとんどありません)。お互いに)。DOTLPAREN

于 2012-12-19T14:21:14.627 に答える
2

(これは別の答えです)

この特定のケースでは、これにより問題がより適切に解決される場合があります。

...

rule tokenize = parse
...
| '.' { DOT }
| '(' { LPAREN }
| identifier { ID (lexeme lexbuf) }

...

そして使用法:

let parse'' text =
    let lexbuf = LexBuffer<char>.FromString text
    let rec tokenize =
        let stack = ref []
        fun lexbuf ->
        if List.isEmpty !stack then
            stack := [Lexer.tokenize lexbuf]
        let (token :: stack') = !stack // can never get match failure,
                                        // else the while wouldn't have exited
        stack := stack'
        // this match fixes the ID to an OP, if necessary
        // multiple matches (and not a unified large one),
              // else EOF may cause issues - this is quite important
        match token with
        | DOT ->
          match tokenize lexbuf with
          | ID id ->
            match tokenize lexbuf with
            | LPAREN ->
              let op = findOp id
              stack := op :: LPAREN :: !stack
            | t -> stack := ID id :: t :: !stack
          | t -> stack := t :: !stack
        | _ -> ()
        token
    Parser.start tokenize lexbuf

これにより、ID が DOT と LPAREN で囲まれている場合にのみ、ID が操作として修正されます。

PS: 3 つの個別の一致があります。統一された一致では、Lazy<_>値を使用する必要があるため (さらに読みにくくなります)、[DOT; EOF]追加の 3 番目のトークンが必要になるため、一連の で失敗します。

于 2012-12-19T15:58:48.637 に答える