ঠিক কীভাবে একটি বিমূর্ত সিনট্যাক্স ট্রি তৈরি করা হয়?


47

আমি মনে করি আমি একটি এএসটির লক্ষ্য বুঝতে পেরেছি, এবং এর আগে আমি বেশ কয়েকটি গাছের কাঠামো তৈরি করেছি, তবে কখনও এটিএসটি নেই। আমি বেশিরভাগই বিভ্রান্ত কারণ নোডগুলি পাঠ্য এবং সংখ্যা নয়, তাই আমি কোনও কোড পার্স করছি বলে টোকেন / স্ট্রিংয়ের ইনপুট দেওয়ার একটি দুর্দান্ত উপায়টি আমি ভাবতে পারি না।

উদাহরণস্বরূপ, আমি যখন এএসটি'র ডায়াগ্রামের দিকে তাকিয়েছিলাম তখন ভেরিয়েবল এবং এর মান একটি সমান চিহ্নের জন্য পাতার নোড ছিল। এটি আমার কাছে নিখুঁত ধারণা তৈরি করে তবে আমি কীভাবে এটি বাস্তবায়ন করব? আমি অনুমান করি আমি এটি কেস কেসে কেস করতে পারি, যাতে যখন আমি কোনও "=" হোঁচট খায় তখন আমি এটিকে নোড হিসাবে ব্যবহার করি এবং পাতা হিসাবে "=" এর আগে পার্স করা মান যুক্ত করি। এটি কেবল ভুল বলে মনে হচ্ছে, কারণ সিন্টেক্সের উপর নির্ভর করে আমার সম্ভবত টন এবং টন জিনিস তৈরি করতে হবে।

এবং তারপরে আমি আরও একটি সমস্যার মুখোমুখি হয়েছি, গাছটি কীভাবে বিস্তৃত হয়? আমি কি নীচ থেকে নীচে নেমে সমস্ত পথে যাই, এবং নীচের দিকে আঘাত করলে আমি একটি নোড ব্যাক আপ করব এবং প্রতিবেশীর জন্যও একই কাজ করব?

আমি এএসটিগুলিতে প্রচুর ডায়াগ্রাম দেখেছি, তবে কোডের একটিতে আমি মোটামুটি সহজ উদাহরণ খুঁজে পাইনি, যা সম্ভবত সাহায্য করবে।


আপনি যে মূল ধারণাটি হারিয়েছেন তা হ'ল পুনরাবৃত্তি । পুনরাবৃত্তি হ'ল এক ধরণের বিপরীতমুখী, এবং প্রতিটি শিক্ষার্থীর পক্ষে এটি যখন তাদের সাথে শেষ পর্যন্ত 'ক্লিক' করবে তখন এটি আলাদা, তবে পুনরাবৃত্তি না করে পার্সিং বোঝার সহজ উপায় নেই (এবং সম্পূর্ণ অন্যান্য কম্পিউটারের বিষয়গুলিও)।
কিলিয়ান ফথ

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

এটি অনুশীলন হিসাবে আরপিএন ক্যালকুলেটর তৈরির অনুশীলন হতে পারে । এটি আপনার প্রশ্নের উত্তর দেয় না তবে কিছু প্রয়োজনীয় দক্ষতা শিখতে পারে।

আমি আসলে এর আগে একটি আরপিএন ক্যালকুলেটর তৈরি করেছি। উত্তরগুলি আমাকে অনেক সাহায্য করেছে এবং আমি মনে করি আমি এখন একটি বেসিক এএসটি তৈরি করতে পারি। ধন্যবাদ!
হাওকান

উত্তর:


47

সংক্ষিপ্ত উত্তরটি হ'ল আপনি স্ট্যাক ব্যবহার করেন। এটি একটি ভাল উদাহরণ, তবে আমি এটিএসটিতে প্রয়োগ করব।

এফওয়াইআই, এটি এডজার ডিজকস্ট্রার শান্টিং-ইয়ার্ড অ্যালগরিদম

এই ক্ষেত্রে, আমি একটি অপারেটর স্ট্যাক এবং একটি এক্সপ্রেশন স্ট্যাক ব্যবহার করব। যেহেতু সংখ্যাগুলিকে বেশিরভাগ ভাষায় এক্সপ্রেশন হিসাবে বিবেচনা করা হয়, তাই আমি সেগুলি সঞ্চয় করতে অভিব্যক্তি স্ট্যাকটি ব্যবহার করব।

class ExprNode:
    char c
    ExprNode operand1
    ExprNode operand2

    ExprNode(char num):
        c = num
        operand1 = operand2 = nil

    Expr(char op, ExprNode e1, ExprNode e2):
        c = op
        operand1 = e1
        operand2 = e2

# Parser
ExprNode parse(string input):
    char c
    while (c = input.getNextChar()):
        if (c == '('):
            operatorStack.push(c)

        else if (c.isDigit()):
            exprStack.push(ExprNode(c))

        else if (c.isOperator()):
            while(operatorStack.top().precedence >= c.precedence):
                operator = operatorStack.pop()
                # Careful! The second operand was pushed last.
                e2 = exprStack.pop()
                e1 = exprStack.pop()
                exprStack.push(ExprNode(operator, e1, e2))

            operatorStack.push(c)

        else if (c == ')'):
            while (operatorStack.top() != '('):
                operator = operatorStack.pop()
                # Careful! The second operand was pushed last.
                e2 = exprStack.pop()
                e1 = exprStack.pop()
                exprStack.push(ExprNode(operator, e1, e2))

            # Pop the '(' off the operator stack.
            operatorStack.pop()

        else:
            error()
            return nil

    # There should only be one item on exprStack.
    # It's the root node, so we return it.
    return exprStack.pop()

(দয়া করে আমার কোডটি সম্পর্কে সুন্দর হন I আমি জানি এটি শক্তিশালী নয়; এটি কেবল সিউডোকোড হওয়ার কথা)

যাইহোক, আপনি কোড থেকে দেখতে পাচ্ছেন, নির্বিচারে এক্সপ্রেশনগুলি অন্যান্য এক্সপ্রেশনগুলিতে অপারেশন হতে পারে। আপনার যদি নিম্নলিখিত ইনপুট থাকে:

5 * 3 + (4 + 2 % 2 * 8)

আমার লেখা কোডটি এই এএসটি তৈরি করবে:

     +
    / \
   /   \
  *     +
 / \   / \
5   3 4   *
         / \
        %   8
       / \
      2   2

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


এটি অপারেটরদের জন্য কাজ করে যা ডান থেকে বামে নয়, বাম থেকে ডান সাহসীকরণ করেছে।
সাইমন 19

@ সিমন, ডান থেকে বাম অপারেটরদের জন্য ক্ষমতা যুক্ত করা অত্যন্ত সহজ হবে। সবচেয়ে সহজ হ'ল একটি চেহারা আপ টেবিল যুক্ত করা এবং যদি কোনও অপারেটর ডান থেকে বামে, কেবল অপারেন্ডগুলির ক্রমটিকে বিপরীত করে দেয়।
গ্যাভিন হাওয়ার্ড

4
@ সিমন আপনি যদি উভয়কেই সমর্থন করতে চান তবে আপনি পুরো গৌরবতে শান্টিং ইয়ার্ড অ্যালগরিদম সন্ধান করা ভাল । অ্যালগরিদমগুলি যেতে যেতে এটি একটি পরম ক্র্যাকার।
বিজিক্লপ

19

এএসটি কীভাবে পরীক্ষায় সাধারণত চিত্রিত হয় (পাতার নোডগুলিতে নম্বর / ভেরিয়েবল সহ একটি গাছ এবং অভ্যন্তরীণ নোডগুলিতে চিহ্নগুলি) এবং কীভাবে এটি বাস্তবায়িত হয় তার মধ্যে একটি উল্লেখযোগ্য পার্থক্য রয়েছে।

একটি এএসটি এর সাধারণ প্রয়োগ (একটি ওও ভাষায়) বহুবর্ষের ভারী ব্যবহার করে। এএসটিতে নোডগুলি সাধারণত বিভিন্ন শ্রেণীর সাথে প্রয়োগ করা হয়, সমস্ত সাধারণ ASTNodeশ্রেণি থেকে প্রাপ্ত । আপনি যে ভাষায় প্রক্রিয়া করছেন তার প্রতিটি সিনট্যাক্টিকাল কনস্ট্রাক্টের জন্য, এটিএসটিতে সেই কনস্ট্রাক্টের প্রতিনিধিত্ব করার জন্য একটি শ্রেণি থাকবে যেমন ConstantNode(ধ্রুবকগুলির জন্য, 0x10বা যেমন 42), VariableNode(ভেরিয়েবল নামের জন্য), AssignmentNode(অ্যাসাইনমেন্ট অপারেশনের জন্য), ExpressionNode(জেনেরিকের জন্য ) এক্সপ্রেশন) ইত্যাদি
Each এ এর ConstantNodeসাধারণত কোন সন্তান থাকে না, একটিতে AssignmentNodeদুটি থাকে এবং ExpressionBlockNodeএকটিতে অনেকগুলি সন্তান থাকতে পারে।

এএসটি পার্সার দ্বারা নির্মিত হয়েছে, কে জানে এটি কী নির্মাণটি সবেমাত্র পার্স করেছে, তাই এটি সঠিক ধরণের এএসটি নোড তৈরি করতে পারে।

এএসটি অতিক্রম করার সময়, নোডগুলির পলিমারফিজম সত্যই কার্যকর হয়। বেসটি ASTNodeনোডগুলিতে সঞ্চালিত হতে পারে এমন ক্রিয়াকলাপগুলি সংজ্ঞায়িত করে এবং প্রতিটি নির্দিষ্ট নোড প্রকারটি সেই নির্দিষ্ট ক্রিয়াকলাপের জন্য নির্দিষ্ট পদ্ধতিতে সেই ক্রিয়াকলাপগুলি কার্যকর করে।


9

উত্স পাঠ্য থেকে এএসটি তৈরি করা "সহজভাবে" পার্সিং । কীভাবে এটি করা হয় তা নির্ভরযোগ্য আনুষ্ঠানিক ভাষা এবং বাস্তবায়নের উপর নির্ভর করে। তোমার মত পার্সার জেনারেটর ব্যবহার করতে পারে স্মৃতিস্তম্ভসূচক প্রাচীন দণ্ডায়মান প্রস্তর (Ocaml জন্য) , গনুহ bisonসঙ্গে flex, অথবা ANTLR ইত্যাদি ইত্যাদিতে প্রায়ই "ম্যানুয়ালি" কিছু কোডিং দ্বারা সম্পন্ন করা হয় রিকার্সিভ বংশদ্ভুত পার্সার (দেখুন এই উত্তর ব্যাখ্যা কেন)। পার্সিংয়ের প্রাসঙ্গিক দিকটি প্রায়শই অন্য কোথাও করা হয় (প্রতীক টেবিল, বৈশিষ্ট্য, ....)।

তবে বাস্তবে আপনি যা বিশ্বাস করেন তার চেয়ে এএসটি আরও জটিল। উদাহরণস্বরূপ, জিসিসির মতো সংকলকটিতে এএসটি উত্সের অবস্থানের তথ্য এবং কিছু টাইপিংয়ের তথ্য রাখে। জিসিসিতে জেনেরিক ট্রি সম্পর্কে পড়ুন এবং এর জিসিসি / ট্রি.ডিএফের ভিতরে দেখুন । BTW, ভিতরে আরো দেখুন জিসিসি গলে (যা আমি পরিকল্পিত ও বাস্তবায়িত হয়েছে), এটা আপনার প্রশ্নের প্রাসঙ্গিক।


উত্সের পাঠ্যকে পার্স করার জন্য এবং জেএসের একটি অ্যারেতে রূপান্তর করতে আমি একটি লুয়া দোভাষী তৈরি করছি। আমি কি এটি একটি এএসটি বিবেচনা করতে পারি? আমার এই জাতীয় কিছু করার কথা রয়েছে: --My comment #1 print("Hello, ".."world.") `[{" প্রকার ":" - "," সামগ্রী "তে রূপান্তরিত:" আমার মন্তব্য # 1 "}, {" প্রকার ":" কল "," নাম ":" মুদ্রণ "," যুক্তি ": [[{" প্রকার ":" আরআর "," ক্রিয়া ":" .. "," সামগ্রী ":" হ্যালো, "}, type" টাইপ ":" আরআর "," সামগ্রী ": "বিশ্বের।" }]]}] `আমি মনে করি এটি অন্য কোনও ভাষার চেয়ে জেএসে অনেক বেশি সহজ ler
হাইড্রোপার

@TheProHands এটিকে এএসটি নয়, টোকেন হিসাবে বিবেচনা করা হবে।
YoYoYonnY

2

আমি জানি এই প্রশ্নটি 4+ বছর পুরানো তবে আমার মনে হয় আমার আরও বিস্তারিত উত্তর যুক্ত করা উচিত।

বিমূর্ত সিনট্যাক্স গাছগুলি অন্য গাছের চেয়ে আলাদাভাবে তৈরি করা হয়; এই ক্ষেত্রে আরও সত্য বিবৃতিটি হ'ল সিন্ট্যাক্স ট্রি নোডগুলিতে যেমন প্রয়োজন হয় তেমন নোডের বিচিত্র পরিমাণ থাকে।

একটি উদাহরণ বাইনারি এক্সপ্রেশনগুলির মত 1 + 2 একটি সাধারণ অভিব্যক্তি যা ডান এবং বাম নোড ধারণ করে একটি একক মূল নোড তৈরি করবে যা সংখ্যার তথ্য রাখে। সি ভাষায়, এটি দেখতে কিছুটা দেখতে চাই

struct ASTNode;
union SyntaxNode {
    int64_t         llVal;
    uint64_t        ullVal;
    struct {
        struct ASTNode *left, *right;
    } BinaryExpr;
};

enum SyntaxNodeType {
    AST_IntVal, AST_Add, AST_Sub, AST_Mul, AST_Div, AST_Mod,
};

struct ASTNode {
    union SyntaxNode *Data;
    enum SyntaxNodeType Type;
};

আপনার প্রশ্নটি কীভাবে অতিক্রম করতে হবে? এই ক্ষেত্রে ট্র্যাভারিংয়ের নাম ভিজিটিং নোড । প্রতিটি নোডে পরিদর্শন করার জন্য প্রতিটি সিন্ট্যাক্স নোডের ডেটা কীভাবে মূল্যায়ন করা যায় তা নির্ধারণ করতে আপনি প্রতিটি নোড প্রকারের ব্যবহার প্রয়োজন।

এখানে সি এর অন্য একটি উদাহরণ যেখানে আমি কেবল প্রতিটি নোডের বিষয়বস্তু মুদ্রণ করি:

void AST_PrintNode(const ASTNode *node)
{
    if( !node )
        return;

    char *opername = NULL;
    switch( node->Type ) {
        case AST_IntVal:
            printf("AST Integer Literal - %lli\n", node->Data->llVal);
            break;
        case AST_Add:
            if( !opername )
                opername = "+";
        case AST_Sub:
            if( !opername )
                opername = "-";
        case AST_Mul:
            if( !opername )
                opername = "*";
        case AST_Div:
            if( !opername )
                opername = "/";
        case AST_Mod:
            if( !opername )
                opername = "%";
            printf("AST Binary Expr - Oper: \'%s\' Left:\'%p\' | Right:\'%p\'\n", opername, node->Data->BinaryExpr.left, node->Data->BinaryExpr.right);
            AST_PrintNode(node->Data->BinaryExpr.left); // NOTE: Recursively Visit each node.
            AST_PrintNode(node->Data->BinaryExpr.right);
            break;
    }
}

লক্ষ্য করুন যে ফাংশনটি কীভাবে নোডের সাথে আমরা আচরণ করছি তার অনুসারে প্রতিটি নোডকে পুনরাবৃত্তভাবে পরিদর্শন করে।

এর আরও জটিল উদাহরণ যুক্ত করা যাক, একটি ifবিবৃতি নির্মাণ! মনে রাখবেন যে যদি বিবৃতিতে অন্য একটি haveচ্ছিক বিধিও থাকতে পারে। আসুন যদি আমাদের মূল নোড কাঠামোতে if-other বিবৃতি যুক্ত করা যাক। মনে রাখবেন যে বিবৃতিগুলি যদি বিবৃতিতেও থাকে তবে যদি আমাদের নোড সিস্টেমের মধ্যে এক ধরণের পুনরাবৃত্তি ঘটতে পারে। অন্য বিবৃতিগুলি alচ্ছিক তাই elsestmtক্ষেত্রটি NULL হতে পারে যা পুনরাবৃত্তির দর্শনার্থী ফাংশন উপেক্ষা করতে পারে।

struct ASTNode;
union SyntaxNode {
    int64_t         llVal;
    uint64_t        ullVal;
    struct {
        struct ASTNode *left, *right;
    } BinaryExpr;
    struct {
        struct ASTNode *expr, *stmt, *elsestmt;
    } IfStmt;
};

enum SyntaxNodeType {
    AST_IntVal, AST_Add, AST_Sub, AST_Mul, AST_Div, AST_Mod, AST_IfStmt, AST_ElseStmt, AST_Stmt
};

struct ASTNode {
    union SyntaxNode *Data;
    enum SyntaxNodeType Type;
};

আমাদের নোড ভিজিটর মুদ্রণ ফাংশনে ফিরে বলা হয় AST_PrintNode, আমরা ifএই সি কোড যুক্ত করে এএসটি রচনা বিবৃতিটি সমন্বিত করতে পারি :

case AST_IfStmt:
    puts("AST If Statement\n");
    AST_PrintNode(node->Data->IfStmt.expr);
    AST_PrintNode(node->Data->IfStmt.stmt);
    AST_PrintNode(node->Data->IfStmt.elsestmt);
    break;

এর মত সহজ! উপসংহারে, সিনট্যাক্স ট্রি গাছের ট্যাগযুক্ত ইউনিয়নের গাছ এবং এটির নিজের ডেটা থেকে অনেক বেশি নয়!

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.