क्या स्केल के पार्सर संयोजकों का उपयोग करने के लिए एक सुविधाजनक तरीका है जहां इंडेंटेशन महत्वपूर्ण है? (उदाहरण के लिए अजगर)स्कैला पार्सर संयोजकों का उपयोग करके इंडेंटेशन आधारित भाषा का विश्लेषण करना
उत्तर
मान लेते हैं कि हम एक बहुत ही सरल भाषा जहां यह एक वैध कार्यक्रम
block
inside
the
block
है और हम एक String
के रूप में ब्लॉक के अंदर प्रत्येक पंक्ति के साथ एक List[String]
में इस पार्स करने के लिए चाहते हैं।
हम पहले एक ऐसी विधि को परिभाषित करते हैं जो न्यूनतम इंडेंटेशन स्तर लेता है और उस इंडेंटेशन स्तर के साथ एक रेखा के लिए एक पार्सर देता है।
def line(minIndent:Int):Parser[String] =
repN(minIndent + 1,"\\s".r) ~ ".*".r ^^ {case s ~ r => s.mkString + r}
फिर हम लाइनों के बीच एक उपयुक्त विभाजक के साथ लाइन पार्सर को दोहराते हुए एक न्यूनतम खरोज स्तर के साथ एक ब्लॉक परिभाषित करते हैं।
def lines(minIndent:Int):Parser[List[String]] =
rep1sep(line(minIndent), "[\n\r]|(\n\r)".r)
अब हम इस तरह हमारी छोटी सी भाषा के लिए एक पार्सर परिभाषित कर सकते हैं:
val block:Parser[List[String]] =
(("\\s*".r <~ "block\\n".r) ^^ { _.size }) >> lines
यह पहली बार वर्तमान खरोज स्तर निर्धारित करता है और उसके बाद से गुजरता है कि लाइनों पार्सर के लिए कम से कम के रूप में। के परीक्षण करते हैं:
val s =
"""block
inside
the
block
outside
the
block"""
println(block(new CharSequenceReader(s)))
और हम संकलित करने के लिए मिलता है
[4.10] parsed: List( inside, the, block)
इस सब के लिए, आप इन आयात
import scala.util.parsing.combinator.RegexParsers
import scala.util.parsing.input.CharSequenceReader
और तुम एक वस्तु है कि फैली में सब कुछ डाल करने के लिए की जरूरत की जरूरत है RegexParsers
जैसे
object MyParsers extends RegexParsers {
override def skipWhitespace = false
....
जो मुझे पता है, उससे, स्कैला पार्सर संयोजकों को बॉक्स के बाहर इस तरह की चीज़ के लिए समर्थन नहीं है। आप निश्चित रूप से एक सार्थक तरीके से सफेद स्थान को पार्स करके कर सकते हैं, लेकिन आपको कुछ समस्याएं आती हैं क्योंकि आपको इंडेंटेशन स्टैक का ट्रैक रखने के लिए कुछ प्रकार की राज्य मशीन की आवश्यकता होती है।
मैं प्रीप्रोकैसिंग चरण करने की अनुशंसा करता हूं। इस पाठ के लिए
object Preprocessor {
val BlockStartToken = "{"
val BlockEndToken = "}"
val TabSize = 4 //how many spaces does a tab take
def preProcess(text: String): String = {
val lines = text.split('\n').toList.filterNot(_.forall(isWhiteChar))
val processedLines = BlockStartToken :: insertTokens(lines, List(0))
processedLines.mkString("\n")
}
def insertTokens(lines: List[String], stack: List[Int]): List[String] = lines match {
case List() => List.fill(stack.length) { BlockEndToken } //closing all opened blocks
case line :: rest => {
(computeIndentation(line), stack) match {
case (indentation, top :: stackRest) if indentation > top => {
BlockStartToken :: line :: insertTokens(rest, indentation :: stack)
}
case (indentation, top :: stackRest) if indentation == top =>
line :: insertTokens(rest, stack)
case (indentation, top :: stackRest) if indentation < top => {
BlockEndToken :: insertTokens(lines, stackRest)
}
case _ => throw new IllegalStateException("Invalid algorithm")
}
}
}
private def computeIndentation(line: String): Int = {
val whiteSpace = line takeWhile isWhiteChar
(whiteSpace map {
case ' ' => 1
case '\t' => TabSize
}).sum
}
private def isWhiteChar(ch: Char) = ch == ' ' || ch == '\t'
}
निष्पादन देता है::
val text =
"""
|line1
|line2
| line3
| line4
| line5
| line6
| line7
| line8
| line9
|line10
| line11
| line12
| line13
""".stripMargin
println(Preprocessor.preProcess(text))
... निम्न परिणाम
{
line1
line2
{
line3
line4
line5
{
line6
line7
}
}
{
line8
line9
}
line10
{
line11
line12
line13
}
}
और समापन आप कर सकते हैं यह एक छोटा सा पूर्वप्रक्रमक जो दांतेदार ब्लॉक अलग करने के लिए मार्कर कहते है एक सरल फैशन में पार्सिंग करने के लिए संयोजक लाइब्रेरी का उपयोग करें।
आशा है कि यह
'ओवरराइड वैल स्किप व्हाइटसाइट = झूठी' – senia