2009-11-25 31 views
9

Tôi đang cố định nghĩa ngữ pháp cho các lệnh bên dưới.Scala Parser Token Delimiter Vấn đề

object ParserWorkshop { 
    def main(args: Array[String]) = { 
     ChoiceParser("todo link todo to database") 
     ChoiceParser("todo link todo to database deadline: next tuesday context: app.model") 
    } 
} 

Lệnh thứ hai nên được tokenized như:

action = todo 
message = link todo to database 
properties = [deadline: next tuesday, context: app.model] 

Khi tôi chạy đầu vào này về ngữ pháp được định nghĩa dưới đây, tôi nhận được thông báo lỗi sau:

[1.27] parsed: Command(todo,link todo to database,List()) 
[1.36] failure: string matching regex `\z' expected but `:' found 

todo link todo to database deadline: next tuesday context: app.model 
           ^

As far as Tôi có thể thấy nó không thành công vì mẫu phù hợp với các từ của thông điệp gần giống với mẫu cho khóa của cặp khóa giá trị: thuộc tính, do đó trình phân tích cú pháp không thể biết được thông báo kết thúc ở đâu và tài sản bắt đầu. Tôi có thể giải quyết vấn đề này bằng cách nhấn mạnh rằng mã thông báo bắt đầu được sử dụng cho mỗi thuộc tính như vậy:

todo link todo to database :deadline: next tuesday :context: app.model 

Nhưng tôi muốn giữ lệnh như ngôn ngữ tự nhiên gần nhất có thể. Tôi có hai câu hỏi:

Thông báo lỗi thực sự có ý nghĩa gì? Và làm cách nào để tôi sửa đổi ngữ pháp hiện tại để làm việc cho các chuỗi đầu vào đã cho?

import scala.util.parsing.combinator._ 

case class Command(action: String, message: String, properties: List[Property]) 
case class Property(name: String, value: String) 

object ChoiceParser extends JavaTokenParsers { 
    def apply(input: String) = println(parseAll(command, input)) 

    def command = action~message~properties ^^ {case a~m~p => new Command(a, m, p)} 

    def action = ident 

    def message = """[\w\d\s\.]+""".r 

    def properties = rep(property) 

    def property = propertyName~":"~propertyValue ^^ { 
     case n~":"~v => new Property(n, v) 
    } 

    def propertyName: Parser[String] = ident 

    def propertyValue: Parser[String] = """[\w\d\s\.]+""".r 
} 
+0

Tôi nghĩ bạn nên thay đổi cú pháp của mình thành một cái gì đó như thế này: todo "liên kết todo với cơ sở dữ liệu": hạn chót: "ngày thứ ba tới": ngữ cảnh: "ứng dụng.mô hình " – ziggystar

+0

Đây là một giải pháp tôi muốn tránh, vì tôi muốn giữ ngữ pháp Todo càng gần với ngôn ngữ tự nhiên càng tốt. –

Trả lời

21

Nó thực sự đơn giản. Khi bạn sử dụng ~, bạn phải hiểu rằng không có backtracking trên các trình phân tích cú pháp riêng lẻ đã hoàn thành thành công.

Vì vậy, ví dụ: message có mọi thứ tới trước dấu hai chấm, vì tất cả đều là mẫu được chấp nhận. Tiếp theo, propertiesrep của property, yêu cầu propertyName, nhưng chỉ tìm thấy dấu hai chấm (ký tự đầu tiên không bị giật bởi message). Vì vậy, propertyName không thành công và property không thành công. Bây giờ, properties, như đã đề cập, là rep, vì vậy nó kết thúc thành công với 0 lần lặp lại, sau đó làm cho command kết thúc thành công.

Vì vậy, hãy quay lại parseAll. Trình phân tích cú pháp command đã trả về thành công, đã tiêu thụ mọi thứ trước dấu hai chấm. Sau đó nó sẽ đặt ra câu hỏi: chúng ta ở cuối đầu vào (\z)? Không, bởi vì có một tràng đại tràng ngay sau đó. Vì vậy, nó dự kiến ​​kết thúc đầu vào, nhưng có một dấu hai chấm.

Bạn sẽ phải thay đổi regex để nó sẽ không tiêu thụ số nhận dạng cuối cùng trước dấu hai chấm. Ví dụ:

def message = """[\w\d\s\.]+(?![:\w])""".r 

Bằng cách này, khi bạn sử dụng def bạn buộc các biểu thức để được đánh giá lại. Nói cách khác, mỗi defs tạo ra một phân tích cú pháp mỗi khi mỗi một được gọi. Các biểu thức chính quy được khởi tạo mỗi khi các trình phân tích cú pháp chúng thuộc về được xử lý. Nếu bạn thay đổi mọi thứ thành val, bạn sẽ nhận được hiệu suất tốt hơn nhiều.

Hãy nhớ rằng, những điều này xác định trình phân tích cú pháp, chúng không chạy. Đó là parseAll chạy trình phân tích cú pháp.

+0

Cảm ơn Daniel, lời giải thích rõ ràng, rõ ràng –