لماذا لا يوجد بديل قابل للتطبيق لعبارة #include في ANTLR 4 مع قواعد اللغة C؟

StackOverflow https://stackoverflow.com//questions/25010496

  •  20-12-2019
  •  | 
  •  

سؤال

لقد بدأت للتو مع أنتلر v4 وأنا في حيرة من أمري ...

أنا أستخدم الملف النحوي C من مشروع antlr هنا للعمل مع الجزء التالي من C:

#include <stdio.h>

int main()
{
   printf("Hello");
   return 0;
}

(تم الحفظ باسم C:\Users\Public .c).

لقد قمت بإنشاء المحلل اللغوي C مثل ذلك:

java -cp lib/antlr-4.4-complete.jar org.antlr.v4.Tool -o src/cparser src/C.g4

وقمت بتحرير الملفات التي تم إنشاؤها لوضع بيان الحزمة في الأعلى.

قمت بعد ذلك بإعداد مشروع Java صغير يتضمن هذه الملفات التي تم إنشاؤها والمراجع antlr-runtime-4.4.jar مع فئة رئيسية تبدو هكذا:

package antlrtest;

import java.io.IOException;

import org.antlr.v4.runtime.ANTLRFileStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTreeWalker;

import cparser.CLexer;
import cparser.CParser;
import cparser.CParser.CompilationUnitContext;

public class AntlrTestMain {
    public static void main(String[] arguments) {
        try {           
            CParser parser = new CParser(
                new CommonTokenStream(
                        new CLexer(
                                new ANTLRFileStream("C:\\Users\\Public\\t.c"))));

            parser.setBuildParseTree(true);

            // This line prints the error
            CompilationUnitContext ctx = parser.compilationUnit();

            MyListener listener = new MyListener();
            ParseTreeWalker.DEFAULT.walk(listener, ctx);            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }   
}

ومن أجل الاكتمال، على الرغم من أنني لا أعتقد أنه مهم، يبدو المستمع هكذا (فارغ فقط، وأخطط لوضع شيء ما هنا بالطبع):

package antlrtest;

import cparser.CBaseListener;

public class MyListener extends CBaseListener {
}

الآن ما يحدث عندما أقوم بالتشغيل هو عندما أتصل بـ compilationUnit الطريقة التي أحصل بها على الأخطاء التالية مطبوعة على وحدة التحكم:

line 1:0 token recognition error at: '#i'
line 1:9 no viable alternative at input 'nclude<'

أنا متأكد تمامًا من أن رمز C صالح ولم أقم بتحرير ملف C.g4 الملف على الإطلاق، فما الخطأ الذي أفعله هنا - لماذا أتلقى هذه الأخطاء؟

يتصل compilationUnit() ربما يكون هذا هو الشيء الخطأ الذي يجب فعله، إذا كان الأمر كذلك، فما الذي يجب أن أتصل به للمرور إلى مشاة الشجرة؟

هل كانت مفيدة؟

المحلول

المشكلة هي:

لا يمكنك تحليل ملف بشكل عام إلا إذا تمت معالجته مسبقًا.ربما هذا هو السبب وراء تضمين عناصر المعالج المسبق في نطاق محدود للغاية.بعض الأمثلة البسيطة:

#define FOO  if (a
void main ()
{
    int a;
    FOO );
}

لذلك عليك إنشاء قواعد نحوية للمعالج المسبق أولاً.لقد فعلت شيئًا مشابهًا وفعلته بهذه الطريقة:

  1. قم بترميز الملف الكامل
  2. اسمح للمحلل اللغوي للمعالج المسبق بالقيام بعمله واستبدال بعض الرموز المميزة للمعالج المسبق بالرموز المميزة "الافتراضية" التي تمثل استبدال الماكرو للمعالج المسبق (هنا: if, a, ().
  3. استخدم المحلل اللغوي العادي باستخدام دفق الرمز المميز المعدل.

ما يمكنك فعله هو ما يلي:

أضف قاعدة للتضمين إلى الملف النحوي في نهاية الملف (بحيث ستتم مطابقة عناصر المعالج المسبق الأخرى إن أمكن):

SomePreprocessorStuff
     :   '#' ~[\r\n]*
          -> skip
     ;

نصائح أخرى

تتطلب قواعد لغة C المضمنة في مشروع ANTLR ملفات مصدر تمت معالجتها مسبقًا كمدخلات.لا تقوم القواعد النحوية بأي عملية تضمين للملفات أو توسيع الماكرو أو أي ميزة أخرى يوفرها المعالج المسبق.إذا لم تقم بإجراء المعالجة المسبقة قبل استخدام هذه القواعد، فإن شجرة التحليل التي تنتجها لن تكون تمثيلاً دقيقًا لوحدة الترجمة.

لاحظ أن تخطي "عناصر المعالج المسبق" ليس بديلاً لاستخدام المعالج المسبق مقدمًا، نظرًا لأن تضمين الملف ليس سوى جزء واحد من المعالج المسبق.

كتحديث، ألقيت نظرة على المعالج JCPP وجعلته يعمل بمجرد لفه في ملف قارئ باستخدام CppReader الذي تم تضمينه في المعالج المسبق المذكور.

هذا ليس النهج الأفضل حقًا (من حيث الكفاءة على الأقل)، ربما ينبغي عليك إنشاء ملف TokenStream من تدفق الرمز المميز الخاص بـ JCPP نظرًا لأننا هنا نقوم بالقراءة مرتين (مرة واحدة بواسطة JCPP حتى يتمكن من المعالجة المسبقة ثم مرة أخرى بواسطة ANTLR) ولكن كوسيلة لتحقيق ذلك، يبدو أنه يعمل وعلى الأقل في الاختبار الأساسي الخاص بي ليتم تجهيزها بشكل صحيح.

لذا، على أية حال، هذا هو الكود الموجود في السؤال، والذي تم تحديثه للمعالجة المسبقة باستخدام JCPP:

public class AntlrTestMain {

    public static void main(String[] args) {

        String mainFileName = "C:\\Users\\Public\\t.c";

        try {
            // Construct the preprocessor with the main file to look at
            Preprocessor pp = new Preprocessor(new File(mainFileName));

            // Set up the preprocessor - you probably want to set more stuff
            // here than just the include path - have a look in the javadoc
            List<String> systemInclude = new ArrayList<String>();
            systemInclude.add("C:\\MYCPPCOMPILER\\include");            
            pp.setSystemIncludePath(systemInclude);

            // Get the parser by wrapping up the preprocessor in a reader
            CParser parser = new CParser(
                new CommonTokenStream(
                    new CLexer(
                        new ANTLRInputStream(new CppReader(pp)))));

            // Use ANTLR to do whatever you want...
            parser.setBuildParseTree(true);
            MyListener listener = new MyListener();
            ParseTreeWalker.DEFAULT.walk(listener, parser.compilationUnit());

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

ستحتاج إلى هذه الواردات للرمز أعلاه:

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.anarres.cpp.CppReader;
import org.anarres.cpp.Preprocessor;

import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTreeWalker;

import cparser.CLexer;
import cparser.CParser;

لا أعتقد أن هناك أي خطأ في الكود الخاص بك.لا يحتوي الملف النحوي على قاعدة محددة لـ #include <foo.h>.

إذن ما يمكنك فعله هو توسيع القواعد النحوية (والتي قد تكون معقدة إلى حد ما عندما لا تكون على دراية بـ antlr) أو حذف بيان التضمين في الوقت الحالي للحصول على عمل antlr مع القواعد النحوية الخاصة بك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top