2014-11-27 7 views
5

Sto usando ocamllex per scrivere un lexer per un linguaggio di scripting ma sto affrontando un conflitto con la mia regola per i commenti.E 'possibile in ocamllex definire una regola che guarda al prossimo carattere senza consumarlo?

Desidero consentire ai miei argomenti di comando di essere non quotati purché contengano solo caratteri alfanumerici e barre "/". Per esempio:

echo "quoted argument [email protected]#%" /this/second/argument/is/unquoted 

Inoltre, uno dei miei prerequesites è commenti in stile C++ con "//"

//this is a comment 
echo hello world 

Il problema questo porta è cose come

echo foo//comment 

vorrei il mio lexer per produrre un token "foo", lasciando anche il "//" intatto, da consumare nella prossima volta che chiedo al lexer il token. È possibile? La ragione di ciò è che è possibile che il buffer di input non abbia ancora raggiunto la fine del commento e preferirei immediatamente restituire il token "foo" piuttosto che bloccare inutilmente il tentativo di consumare impazientemente il commento.

+0

Se il parser ha una regola che utilizza solo 'echo foo', non dovrebbe essere sufficiente per rimandare il lexing dei commenti? – didierc

+0

A livello di lexer, devi solo assicurarti che i parametri non quotati non contengano doppie barre, altrimenti dovresti discriminare i casi in cui potrebbe accadere, e forse avere due serie di regole per quando è possibile o meno – didierc

+0

Tu dovrebbe probabilmente fornire un piccolo programma che mostra il problema, in modo che possiamo vedere come risolverlo. – didierc

risposta

5

Quello che segue è un piccolo lexer che corrisponde solo echo, citato e archi non quotate, i commenti, e stampa i gettoni liberati:

{ 
    type token = NEWLINE | ECHO | QUOTED of string | UNQUOTED of string | COMMENT of string 
    exception Eof 

    type state = CODE | LINE_COMMENT 
    let state = ref CODE 
} 

let newline  = '\n' 
let alphanum  = [ 'A'-'Z' 'a'-'z' '0'-'9' '_' ] 
let comment_line = "//"([^ '\n' ]+) 
let space  = [ ' ' '\t' ] 
let quoted  = '"'([^ '"' ]+)'"' 
let unquoted  = ('/'?(alphanum+'/'?)+) 

rule code = parse 
    space+      { code lexbuf } 
| newline      { code lexbuf } 
| "echo"      { ECHO } 
| quoted      { QUOTED (Lexing.lexeme lexbuf) } 
| "//"      { line_comment "" lexbuf } 
| ('/'|alphanum+)    { unquoted (Lexing.lexeme lexbuf) lexbuf } 
| eof       { raise Eof } 

and unquoted buff = parse 
    newline      { UNQUOTED buff } 
| "//"      { state := LINE_COMMENT; if buff = "" then line_comment "" lexbuf else UNQUOTED buff } 
| ('/'|alphanum+)    { unquoted (buff^Lexing.lexeme lexbuf) lexbuf } 
| space+      { UNQUOTED buff } 
| eof       { raise Eof } 

and line_comment buff = parse 
    newline      { state := CODE; COMMENT buff } 
| _       { line_comment (buff^Lexing.lexeme lexbuf) lexbuf } 

{ 

    let lexer lb = 
    match !state with 
     CODE -> code lb 
    | LINE_COMMENT -> line_comment "" lb 

    let _ = 
    try 
     let lexbuf = Lexing.from_channel stdin in 
     while true do 
     let() = 
      match lexer lexbuf with 
      ECHO -> Printf.printf "ECHO\n" 
      | QUOTED s -> Printf.printf "QUOTED(%s)\n" s 
      | UNQUOTED s -> Printf.printf "UNQUOTED(%s)\n" s 
      | COMMENT s -> Printf.printf "COMMENT(%s)\n" s 
      | NEWLINE -> Printf.printf "\n" 
     in flush stdout 
     done 
    with Eof -> exit 0 

} 

E 'un trucco che ho usato in un mio progetto, per superare quella stessa limitazione in ocamllex (rispetto al programma originale C lex che consente di abbinare uno schema in "modalità look ahead"). Fondamentalmente, divide le regole ambigue nei loro distinti radicali, e passa di conseguenza il lexer a un parser differente. Tiene anche traccia dell'annuncio parser attualmente usato al prossimo punto di ingresso.

Nella vostra situazione, gli unici stati di cui ha bisogno di tenere traccia sono quelli predefiniti (CODE) e la modalità commenti (LINE_COMMENT). Questo potrebbe essere esteso per supportare altri stati, se necessario.

+1

Il problema con questa versione è che tutte le barre all'interno di un motto "non quotato" sono seguite da un alfanum. Questo esclude i token che finiscono in una barra, come "foo /".Come hai già notato, a causa dell'ambiguità della grammatica ("foo // comment" essendo parseable come "foo /" + "/ comment" o "foo" + "// comment") devi guardare in testa ogni volta che incontri un "/" per decidere se la sua parte di un "//" o meno e per scegliere come disambiguarla. – hugomg

+1

Questa grammatica esclude anche i token che iniziano con una barra. – hugomg

+0

Penso che dovrebbe funzionare questa volta. – didierc