অস্পষ্টতা পরিচালনা করতে পারে এমন একটি ব্যাকরণ কীভাবে সেটআপ করবেন


9

আমি নির্ধারিত কিছু এক্সেলের মতো সূত্রগুলি বিশ্লেষণের জন্য একটি ব্যাকরণ তৈরি করার চেষ্টা করছি, যেখানে স্ট্রিংয়ের শুরুতে একটি বিশেষ চরিত্র একটি ভিন্ন উত্সকে ইঙ্গিত করে। উদাহরণস্বরূপ, $একটি স্ট্রিংকে ইঙ্গিত $This is textকরতে পারে , সুতরাং প্রোগ্রামে একটি " " স্ট্রিং ইনপুট হিসাবে বিবেচিত হবে এবং &একটি ফাংশনকে বোঝাতে পারে, তাই &foo()অভ্যন্তরীণ ফাংশনে একটি কল হিসাবে বিবেচনা করা যেতে পারে foo

ব্যাকরণটি কীভাবে সঠিকভাবে তৈরি করা যায় তা হ'ল আমি যে সমস্যার মুখোমুখি হচ্ছি is উদাহরণস্বরূপ, এটি এমডাব্লুইই হিসাবে একটি সরলীকৃত সংস্করণ:

grammar = r'''start: instruction

?instruction: simple
            | func

STARTSYMBOL: "!"|"#"|"$"|"&"|"~"
SINGLESTR: (LETTER+|DIGIT+|"_"|" ")*
simple: STARTSYMBOL [SINGLESTR] (WORDSEP SINGLESTR)*
ARGSEP: ",," // argument separator
WORDSEP: "," // word separator
CONDSEP: ";;" // condition separator
STAR: "*"
func: STARTSYMBOL SINGLESTR "(" [simple|func] (ARGSEP simple|func)* ")"

%import common.LETTER
%import common.WORD
%import common.DIGIT
%ignore ARGSEP
%ignore WORDSEP
'''
parser = lark.Lark(grammar, parser='earley')

তো, এই ব্যাকরণ সঙ্গে, ভালো জিনিস: $This is a string, &foo(), &foo(#arg1), &foo($arg1,,#arg2)এবং &foo(!w1,w2,w3,,!w4,w5,w6)সব আশানুরূপ পার্স করা হয়। তবে আমি যদি আমার simpleটার্মিনালের সাথে আরও নমনীয়তা যুক্ত করতে চাই , তবে SINGLESTRটোকেন সংজ্ঞাটি যা আপনার পক্ষে সুবিধাজনক নয় তা দিয়ে আমার চারপাশে ফিডিং শুরু করা দরকার ।

আমি কি চেষ্টা করেছি

আমি যে অংশটি অতীত হতে পারি না তা হ'ল যদি আমি বন্ধনী (যা আক্ষরিক func) সহ একটি স্ট্রিং রাখতে চাই , তবে আমি আমার বর্তমান পরিস্থিতিতে সেগুলি পরিচালনা করতে পারি না।

  • তাহলে আমি প্রথম বন্ধনী যোগ SINGLESTRতারপর, আমি পেতে Expected STARTSYMBOLকারণ এটি সঙ্গে মিশ্রিত হয়ে যাচ্ছে, funcসংজ্ঞা এবং এটি মনে করেন যে একটি ফাংশন যুক্তি পাস করা উচিত, যা জ্ঞান করে তোলে।
  • আমি যদি কেবলমাত্র ক্রিয়াকলাপের জন্য এম্পারস্যান্ড প্রতীক সংরক্ষণ করার জন্য ব্যাকরণটিকে পুনরায় সংজ্ঞায়িত করি এবং এর মধ্যে প্রথম বন্ধনী যুক্ত SINGLESTRকরি, তবে আমি প্রথম বন্ধনীর সাথে একটি স্ট্রিং পার্স করতে পারি, তবে প্রতিটি ফাংশন যা আমি বিশ্লেষণের চেষ্টা করছি Expected LPAR

আমার উদ্দেশ্য হ'ল একটি দিয়ে শুরু হওয়া যে কোনও কিছুই টোকেন $হিসাবে পার্স করা হবে SINGLESTRএবং তারপরে আমি পছন্দ মতো জিনিসগুলি পার্স করতে পারি &foo($first arg (has) parentheses,,$second arg)

আমার সমাধান, আপাতত, আমি আমার স্ট্রিংগুলিতে LEFTPAR এবং RIGHTPAR এর মতো 'পলায়ন' শব্দ ব্যবহার করছি এবং আমি যখন গাছটি প্রসেস করি তখন সেগুলি বন্ধনীতে পরিণত করতে সহায়ক ফাংশন লিখেছি। সুতরাং, $This is a LEFTPARtestRIGHTPARসঠিক গাছ উত্পাদন করে এবং যখন আমি এটি প্রক্রিয়া করি, তখন এটি অনুবাদ হয় This is a (test)

একটি সাধারণ প্রশ্ন গঠনের জন্য: আমি কীভাবে আমার ব্যাকরণটি এমনভাবে সংজ্ঞায়িত করতে পারি যে ব্যাকরণের বিশেষ কিছু চরিত্রকে কিছু পরিস্থিতিতে স্বাভাবিক চরিত্র হিসাবে বিবেচনা করা হয় এবং অন্য কোনও ক্ষেত্রে বিশেষ হিসাবে বিবেচনা করা হয়?


সম্পাদনা 1

একটি মন্তব্য উপর ভিত্তি করে থেকে jbndlrআমি পৃথক শুরু প্রতীক উপর ভিত্তি করে মোড তৈরি করতে আমার ব্যাকরণ সংশোধিত:

grammar = r'''start: instruction

?instruction: simple
            | func

SINGLESTR: (LETTER+|DIGIT+|"_"|" ") (LETTER+|DIGIT+|"_"|" "|"("|")")*
FUNCNAME: (LETTER+) (LETTER+|DIGIT+|"_")* // no parentheses allowed in the func name
DB: "!" SINGLESTR (WORDSEP SINGLESTR)*
TEXT: "$" SINGLESTR
MD: "#" SINGLESTR
simple: TEXT|DB|MD
ARGSEP: ",," // argument separator
WORDSEP: "," // word separator
CONDSEP: ";;" // condition separator
STAR: "*"
func: "&" FUNCNAME "(" [simple|func] (ARGSEP simple|func)* ")"

%import common.LETTER
%import common.WORD
%import common.DIGIT
%ignore ARGSEP
%ignore WORDSEP
'''

এটি আমার দ্বিতীয় পরীক্ষার মামলার অধীনে (কিছুটা) পড়েছে। আমি সমস্ত simpleধরণের স্ট্রিং (পাঠ্য, এমডি বা ডিবি টোকেন যাতে প্রথম বন্ধনী থাকতে পারে) এবং ফাঁকা ফাঁকা ফাংশন পার্স করতে পারি ; উদাহরণস্বরূপ, &foo()বা &foo(&bar())সঠিকভাবে বিশ্লেষণ করুন। যে মুহুর্তে আমি কোনও ফাংশনের মধ্যে আর্গুমেন্ট রাখি (কোনও ধরণের বিষয় নয়), আমি একটি পেয়ে যাই UnexpectedEOF Error: Expected ampersand, RPAR or ARGSEP। ধারণার প্রমাণ হিসাবে, আমি যদি উপরের নতুন ব্যাকরণে SINGLESTR সংজ্ঞা থেকে বন্ধনীগুলি সরিয়ে ফেলি, তবে সবকিছু যেমনটি করা উচিত তেমন কাজ করে, তবে আমি আবার স্কোয়ার একে ফিরে এসেছি।


আপনার এমন অক্ষর রয়েছে যা তাদের (আপনার STARTSYMBOL) পরে কী ঘটছে তা চিহ্নিত করে এবং আপনি পৃথককারী এবং প্রথম বন্ধনী যুক্ত করেছেন যেখানে পরিষ্কার হওয়া দরকার; আমি এখানে কোনও অস্পষ্টতা দেখছি না। STARTSYMBOLস্বতন্ত্র হওয়ার জন্য আপনাকে নিজের তালিকাটি পৃথক আইটেমগুলিতে বিভক্ত করতে হবে।
jbndlr

আমি শীঘ্রই একটি উত্তর পোস্ট করব, বেশ কয়েক দিন ধরে এটি নিয়ে কাজ করছি।
পরিচিত

আমি একটি উত্তর সরবরাহ করেছি। যদিও অনুগ্রহের মেয়াদ শেষ হওয়ার মাত্র 2 ঘন্টা, আপনি এখনও 24 ঘন্টা নিম্নলিখিত অনুগ্রহকালীন সময়ে ম্যানুয়ালি এই অনুদানটি পুরষ্কার দিতে পারেন। যদি আমার উত্তরটি ভাল না হয় দয়া করে আমাকে তাড়াতাড়ি বলুন এবং আমি এটি ঠিক করে দেব।
পরিচিত

উত্তর:


2
import lark
grammar = r'''start: instruction

?instruction: simple
            | func

MIDTEXTRPAR: /\)+(?!(\)|,,|$))/
SINGLESTR: (LETTER+|DIGIT+|"_"|" ") (LETTER+|DIGIT+|"_"|" "|"("|MIDTEXTRPAR)*
FUNCNAME: (LETTER+) (LETTER+|DIGIT+|"_")* // no parentheses allowed in the func name
DB: "!" SINGLESTR (WORDSEP SINGLESTR)*
TEXT: "$" SINGLESTR
MD: "#" SINGLESTR
simple: TEXT|DB|MD
ARGSEP: ",," // argument separator
WORDSEP: "," // word separator
CONDSEP: ";;" // condition separator
STAR: "*"
func: "&" FUNCNAME "(" [simple|func] (ARGSEP simple|func)* ")"

%import common.LETTER
%import common.WORD
%import common.DIGIT
%ignore ARGSEP
%ignore WORDSEP
'''

parser = lark.Lark(grammar, parser='earley')
parser.parse("&foo($first arg (has) parentheses,,$second arg)")

আউটপুট:

Tree(start, [Tree(func, [Token(FUNCNAME, 'foo'), Tree(simple, [Token(TEXT, '$first arg (has) parentheses')]), Token(ARGSEP, ',,'), Tree(simple, [Token(TEXT, '$second arg')])])])

আমি আশা করি এটি আপনি যা খুঁজছিলেন

যারা কিছু দিন পাগল হয়েছে। আমি লার্ক চেষ্টা করেছিলাম এবং ব্যর্থ হয়েছি। আমিও চেষ্টা করেছি persimoniousএবং pyparsing। এই সমস্ত পৃথক পার্সারগুলির সমস্তই 'যুক্তি' টোকেনটি ফাংশনের অংশ হিসাবে থাকা সঠিক বন্ধনী ব্যবহারের ক্ষেত্রে একই সমস্যা ছিল, শেষ পর্যন্ত ব্যর্থ হয়েছিল কারণ ফাংশনটির প্রথম বন্ধনী বন্ধ ছিল না।

কৌতুকটি ছিল কীভাবে আপনি "বিশেষ নয়" এমন একটি সঠিক বন্ধনীর সংজ্ঞা দিচ্ছেন তা বোঝার জন্য। MIDTEXTRPARউপরের কোডের জন্য নিয়মিত প্রকাশটি দেখুন । আমি এটিকে একটি সঠিক বন্ধনী হিসাবে সংজ্ঞায়িত করেছি যা আর্গুমেন্ট বিচ্ছেদ দ্বারা বা স্ট্রিংয়ের শেষে অনুসরণ করা হয় না। আমি নিয়মিত এক্সপ্রেশন এক্সটেনশন ব্যবহার করে (?!...)যা এটি অনুসরণ করে তবেই তা ...অক্ষর গ্রাস না করে matches ভাগ্যক্রমে এটি এই বিশেষ নিয়মিত এক্সপ্রেশন এক্সটেনশনের অভ্যন্তরে স্ট্রিংয়ের সমাপ্তির অনুমতি দেয়।

সম্পাদনা করুন:

উল্লিখিত পদ্ধতিটি কেবল তখনই কাজ করে যদি আপনার সাথে ক এর সাথে শেষ হওয়ার কোনও যুক্তি না থাকে), তবে তারপরে মিডটেক্সটআরপিএর নিয়মিত অভিব্যক্তি এটি ধরবে না) এবং প্রক্রিয়াটির আরও যুক্তি থাকা সত্ত্বেও এটি ফাংশনটির সমাপ্তি বলে মনে করবে। এছাড়াও, অস্পষ্টতা থাকতে পারে যেমন ... এসএসডিএফ) ,, ..., এটি কোনও আর্গুমেন্টের ভিতরে কোনও ফাংশন ঘোষণার সমাপ্তি হতে পারে, বা একটি আর্গুমেন্টের অভ্যন্তরে 'পাঠ্য-মতো') হতে পারে এবং ফাংশন ঘোষণাটি এগিয়ে যায়।

এই সমস্যাটির সাথে সম্পর্কিত যে আপনি আপনার প্রশ্নে যা বর্ণনা করেছেন তা প্রসঙ্গমুক্ত ব্যাকরণ নয় ( https://en.wikedia.org/wiki/Context-free_grammar ) যার জন্য লার্কের মতো পার্সার উপস্থিত রয়েছে। পরিবর্তে এটি একটি প্রসঙ্গ-সংবেদনশীল ব্যাকরণ ( https://en.wikedia.org/wiki/Context- সেনসেটিভ_গ্রামগ্রাম )।

এটি একটি প্রসঙ্গে সংবেদনশীল ব্যাকরণ হওয়ার কারণ হ'ল আপনার মনে রাখতে হবে যে এটি কোনও ফাংশনের অভ্যন্তরে বাসা বাঁধার এবং 'বাসা বাঁধার কতগুলি স্তর রয়েছে' এবং এটি স্মৃতিটি কোনওভাবে ব্যাকরণের বাক্য গঠনের ভিতরে পাওয়া যায় '

EDIT2:

নীচের পার্সারটি একবার দেখুন যা প্রসঙ্গ-সংবেদনশীল এবং সমস্যাটি সমাধান বলে মনে হচ্ছে তবে নেস্টেড ফাংশনগুলির সংখ্যায় একটি ক্ষতিকারক সময় জটিলতা রয়েছে, কারণ এটি কাজ করে এমন একটি সন্ধান না পাওয়া পর্যন্ত সমস্ত সম্ভাব্য ফাংশন বাধা বিশ্লেষণ করার চেষ্টা করে। আমি বিশ্বাস করি যেহেতু এটি প্রসঙ্গ-মুক্ত নয়, কারণ এটির একটি সূক্ষ্ম জটিলতা থাকতে হবে।


_funcPrefix = '&'
_debug = False

class ParseException(Exception):
    pass

def GetRecursive(c):
    if isinstance(c,ParserBase):
        return c.GetRecursive()
    else:
        return c

class ParserBase:
    def __str__(self):
        return type(self).__name__ + ": [" + ','.join(str(x) for x in self.contents) +"]"
    def GetRecursive(self):
        return (type(self).__name__,[GetRecursive(c) for c in self.contents])

class Simple(ParserBase):
    def __init__(self,s):
        self.contents = [s]

class MD(Simple):
    pass

class DB(ParserBase):
    def __init__(self,s):
        self.contents = s.split(',')

class Func(ParserBase):
    def __init__(self,s):
        if s[-1] != ')':
            raise ParseException("Can't find right parenthesis: '%s'" % s)
        lparInd = s.find('(')
        if lparInd < 0:
            raise ParseException("Can't find left parenthesis: '%s'" % s)
        self.contents = [s[:lparInd]]
        argsStr = s[(lparInd+1):-1]
        args = list(argsStr.split(',,'))
        i = 0
        while i<len(args):
            a = args[i]
            if a[0] != _funcPrefix:
                self.contents.append(Parse(a))
                i += 1
            else:
                j = i+1
                while j<=len(args):
                    nestedFunc = ',,'.join(args[i:j])
                    if _debug:
                        print(nestedFunc)
                    try:
                        self.contents.append(Parse(nestedFunc))
                        break
                    except ParseException as PE:
                        if _debug:
                            print(PE)
                        j += 1
                if j>len(args):
                    raise ParseException("Can't parse nested function: '%s'" % (',,'.join(args[i:])))
                i = j

def Parse(arg):
    if arg[0] not in _starterSymbols:
        raise ParseException("Bad prefix: " + arg[0])
    return _starterSymbols[arg[0]](arg[1:])

_starterSymbols = {_funcPrefix:Func,'$':Simple,'!':DB,'#':MD}

P = Parse("&foo($first arg (has)) parentheses,,&f($asdf,,&nested2($23423))),,&second(!arg,wer))")
print(P)

import pprint
pprint.pprint(P.GetRecursive())

1
ধন্যবাদ, এটি উদ্দেশ্য হিসাবে কাজ করে! আপনার কোনওভাবেই প্রথম বন্ধনীর হাত থেকে রক্ষা পাওয়ার দরকার নেই বলে অনুগ্রহ করে পুরষ্কার। আপনি অতিরিক্ত মাইল গিয়েছিলেন এবং এটি দেখায়! এখনও একটি 'টেক্সট' যুক্তির প্রান্ত কেসটি বন্ধনী দিয়ে শেষ হচ্ছে, তবে আমাকে কেবল এটির সাথেই বাঁচতে হবে। আপনি অস্পষ্টতাগুলি একটি পরিষ্কার উপায়ে ব্যাখ্যা করেছিলেন এবং আমার আরও কিছুটা পরীক্ষা করা দরকার তবে আমি মনে করি আমার উদ্দেশ্যগুলির জন্য এটি খুব ভালভাবে কাজ করবে। প্রসঙ্গে সংবেদনশীল ব্যাকরণ সম্পর্কে আরও তথ্য সরবরাহ করার জন্য ধন্যবাদ। আমি সত্যিই এটার প্রশংসা করছি!
Dima1982

@ Dima1982 আপনাকে অনেক ধন্যবাদ!
iliar

@ Dima1982 সম্পাদনাটি একবার দেখুন, আমি এমন একটি পার্সার তৈরি করেছি যা সম্ভবত কোনও তাত্পর্যপূর্ণ সময় জটিলতার জন্য আপনার সমস্যার সমাধান করতে পারে। এছাড়াও, আমি এটি সম্পর্কে ভেবেছিলাম এবং যদি আপনার সমস্যাটি ব্যবহারিক মূল্য হয় তবে প্রথম বন্ধনী থেকে বেরিয়ে আসা সহজ সমাধান হতে পারে। বা &উদাহরণস্বরূপ ফাংশন আর্গুমেন্ট তালিকার শেষে ডিলিট করার মতো ফাংশন প্রথম বন্ধনী তৈরি করা ।
পরিচিত

1

সমস্যা হ'ল ফাংশনের আর্গুমেন্টগুলি প্রথম বন্ধনে আবদ্ধ থাকে যেখানে আর্গুমেন্টগুলির মধ্যে একটিতে প্রথম বন্ধনী থাকতে পারে।
সম্ভাব্য সমাধানগুলির মধ্যে একটি হ'ল ব্যাকস্পেস ব্যবহার করা \ এর আগে (বা) যখন এটি স্ট্রিংয়ের অংশ হয়

  SINGLESTR: (LETTER+|DIGIT+|"_"|" ") (LETTER+|DIGIT+|"_"|" "|"\("|"\)")*

সি দ্বারা ব্যবহৃত অনুরূপ সমাধান, স্ট্রিং ধ্রুবকের অংশ হিসাবে ডাবল কোট (") অন্তর্ভুক্ত করার জন্য যেখানে স্ট্রিং ধ্রুবকটি ডাবল উদ্ধৃতিতে আবদ্ধ থাকে।

  example_string1='&f(!g\()'
  example_string2='&f(#g)'
  print(parser.parse(example_string1).pretty())
  print(parser.parse(example_string2).pretty())

আউটপুট হয়

   start
     func
       f
       simple   !g\(

   start
     func
      f
      simple    #g

আমি মনে করি এটি অনেকটা ওএপি এর নিজস্ব সমাধান "(" এবং ")" এর সাথে এলএইচটিপিএআর এবং রাইটটিপারের পরিবর্তে same
পরিচিত
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.