Cách triển khai mẫu khách truy cập cho hàm lồng nhau


8

Tôi là người mới sử dụng Antlr và tôi muốn thực hiện dưới đây bằng Antlr4. Tôi đang có các chức năng được viết dưới đây.

1. FUNCTION.add(Integer a,Integer b)
2. FUNCTION.concat(String a,String b)
3. FUNCTION.mul(Integer a,Integer b)

Và tôi đang lưu trữ các siêu dữ liệu chức năng như thế này.

Map<String,String> map=new HashMap<>();
        map.put("FUNCTION.add","Integer:Integer,Integer");
        map.put("FUNCTION.concat","String:String,String");
        map.put("FUNCTION.mul","Integer:Integer,Integer");

Trong đó, Integer:Integer,Integerđại diện Integerlà loại trả về và tham số đầu vào mà hàm sẽ tích lũy Integer,Integer.

nếu đầu vào là như thế này

FUNCTION.concat(Function.substring(String,Integer,Integer),String)
or
FUNCTION.concat(Function.substring("test",1,1),String)

Sử dụng triển khai khách truy cập Tôi muốn kiểm tra xem đầu vào có hợp lệ hay không đối với các siêu dữ liệu chức năng được lưu trữ trên bản đồ.

Dưới đây là lexer và trình phân tích cú pháp mà tôi đang sử dụng:

Lexer MyFiancesLexer.g4:

lexer grammar MyFunctionsLexer;

FUNCTION: 'FUNCTION';

NAME: [A-Za-z0-9]+;

DOT: '.';

COMMA: ',';

L_BRACKET: '(';

R_BRACKET: ')';

Trình phân tích cú pháp MyFiancesParser.g4:

parser grammar MyFunctionsParser;

options {
    tokenVocab=MyFunctionsLexer;
}

function : FUNCTION '.' NAME '('(function | argument (',' argument)*)')';

argument: (NAME | function);

WS : [ \t\r\n]+ -> skip;

Tôi đang sử dụng Antlr4.

Dưới đây là cách triển khai tôi đang sử dụng theo câu trả lời được đề xuất.

Triển khai của khách truy cập: lớp công khai FunctionValidateVisitorImpl mở rộng MyFiancesParserBaseVisitor {

    Map<String, String> map = new HashMap<String, String>();

    public FunctionValidateVisitorImpl()
    {
        map.put("FUNCTION.add", "Integer:Integer,Integer");
        map.put("FUNCTION.concat", "String:String,String");
        map.put("FUNCTION.mul", "Integer:Integer,Integer");
        map.put("FUNCTION.substring", "String:String,Integer,Integer");
    }

    @Override
    public String visitFunctions(@NotNull MyFunctionsParser.FunctionsContext ctx) {
        System.out.println("entered the visitFunctions::");
        for (int i = 0; i < ctx.getChildCount(); ++i)
        {
            ParseTree c = ctx.getChild(i);
            if (c.getText() == "<EOF>")
                continue;
            String top_level_result = visit(ctx.getChild(i));
            System.out.println(top_level_result);
            if (top_level_result == null)
            {
                System.out.println("Failed semantic analysis: "+ ctx.getChild(i).getText());
            }
        }
        return null;
    }

    @Override
    public String visitFunction( MyFunctionsParser.FunctionContext ctx) {
        // Get function name and expected type information.
        String name = ctx.getChild(2).getText();
        String type=map.get("FUNCTION." + name);
        if (type == null)
        {
            return null; // not declared in function table.
        }
        String result_type = type.split(":")[0];
        String args_types = type.split(":")[1];
        String[] expected_arg_type = args_types.split(",");
        int j = 4;
        ParseTree a = ctx.getChild(j);
        if (a instanceof MyFunctionsParser.FunctionContext)
        {
            String v = visit(a);
            if (v != result_type)
            {
                return null; // Handle type mismatch.
            }
        } else {
            for (int i = j; i < ctx.getChildCount(); i += 2)
            {
                ParseTree parameter = ctx.getChild(i);
                String v = visit(parameter);
                if (v != expected_arg_type[(i - j)/2])
                {
                    return null; // Handle type mismatch.
                }
            }
        }
        return result_type;
    }


    @Override
    public String visitArgument(ArgumentContext ctx){
        ParseTree c = ctx.getChild(0);
        if (c instanceof TerminalNodeImpl)
        {
            // Unclear if what this is supposed to parse:
            // Mutate "1" to "Integer"?
            // Mutate "Integer" to "String"?
            // Or what?
            return c.getText();
        }
        else
            return visit(c);
    }


}

Bản kiểm tra:

public class FunctionValidate {


    public static void main(String[] args) {
        String input = "FUNCTION.concat(FUNCTION.substring(String,Integer,Integer),String)";
        ANTLRInputStream str = new ANTLRInputStream(input);
        MyFunctionsLexer lexer = new MyFunctionsLexer(str);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        MyFunctionsParser parser = new MyFunctionsParser(tokens);
        parser.removeErrorListeners(); // remove ConsoleErrorListener 
        parser.addErrorListener(new VerboseListener()); // add ours
        FunctionsContext tree = parser.functions();
        FunctionValidateVisitorImpl visitor = new FunctionValidateVisitorImpl();
        visitor.visit(tree);
    }


}

Người nói dối

lexer grammar MyFunctionsLexer;
FUNCTION: 'FUNCTION';
NAME: [A-Za-z0-9]+;
DOT: '.';
COMMA: ',';
L_BRACKET: '(';
R_BRACKET: ')';
WS : [ \t\r\n]+ -> skip;

Trình phân tích cú pháp:

parser grammar MyFunctionsParser;
options { tokenVocab=MyFunctionsLexer; }
functions : function* EOF;
function : FUNCTION '.' NAME '(' (function | argument (',' argument)*) ')';
argument: (NAME | function);

Người nghe dài dòng:

public class VerboseListener  extends BaseErrorListener  {

    @Override 
    public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { 
        List<String> stack = ((Parser)recognizer).getRuleInvocationStack();
        Collections.reverse(stack); 
        throw new FunctionInvalidException("line "+line+":"+charPositionInLine+" at "+ offendingSymbol+": "+msg);

    }
}

Đầu ra: Nó không được đưa vào triển khai của khách truy cập vì nó không in System.out.println("entered the visitFunctions::");câu lệnh.

Câu trả lời:


4

Dưới đây là một giải pháp trong C #. Điều này sẽ cung cấp cho bạn một ý tưởng về cách tiến hành. Bạn sẽ có thể dễ dàng dịch mã sang Java.

Để dễ dàng, tôi đã triển khai mã bằng tiện ích mở rộng AntlrVSIX cho Visual Studio 2019 với NET Core C #. Nó làm cho cuộc sống dễ dàng hơn bằng cách sử dụng một IDE đầy đủ hỗ trợ xây dựng các ngữ pháp phân tách / trình phân tích cú pháp, gỡ lỗi và một trình cắm phù hợp để chỉnh sửa các ngữ pháp Antlr.

Có một số điều cần lưu ý với ngữ pháp của bạn. Đầu tiên, ngữ pháp trình phân tích cú pháp của bạn không được Antlr 4.7.2 chấp nhận. Sản xuất "WS: [\ t \ r \ n] + -> bỏ qua;" là một quy tắc từ vựng, nó không thể đi theo ngữ pháp trình phân tích cú pháp. Nó phải đi vào ngữ pháp lexer (hoặc bạn xác định một ngữ pháp kết hợp). Thứ hai, cá nhân tôi sẽ không định nghĩa các ký hiệu từ vựng như DOT, và sau đó sử dụng trong trình phân tích cú pháp RHS của ký hiệu từ vựng trực tiếp trong ngữ pháp của trình phân tích cú pháp, ví dụ: '.'. Thật khó hiểu và tôi khá chắc chắn rằng không có IDE hay biên tập viên nào biết cách đi đến định nghĩa "DOT: '.';" trong ngữ pháp từ vựng nếu bạn định vị con trỏ của mình trên '.' trong ngữ pháp trình phân tích cú pháp. Tôi không bao giờ hiểu tại sao nó được phép ở Antlr, nhưng đó là la vie. Thay vào đó tôi sẽ sử dụng biểu tượng lexer mà bạn xác định. Ngày thứ ba, Tôi sẽ xem xét việc tăng cường ngữ pháp của trình phân tích cú pháp theo cách thông thường với EOF, ví dụ: "function: function * EOF". Nhưng, điều này hoàn toàn phụ thuộc vào bạn.

Bây giờ, trên báo cáo vấn đề, đầu vào ví dụ của bạn chứa sự không nhất quán. Trong trường hợp đầu tiên, "chuỗi con (Chuỗi, Số nguyên, Số nguyên)", đầu vào nằm trong một mô tả giống như meta của chuỗi con (). Trong trường hợp thứ hai, "chuỗi con (\" test \ ", 1,1)", bạn đang phân tích mã. Trường hợp đầu tiên phân tích cú pháp với ngữ pháp của bạn, trường hợp thứ hai thì không - không có quy tắc từ vựng theo nghĩa đen chuỗi được xác định trong ngữ pháp từ vựng của bạn. Không rõ những gì bạn thực sự muốn phân tích.

Nhìn chung, tôi đã xác định mã khách truy cập qua các chuỗi, tức là, mỗi phương thức trả về một chuỗi đại diện cho loại đầu ra của hàm hoặc đối số, ví dụ: "Integer" hoặc "String" hoặc null nếu có lỗi (hoặc bạn có thể ném ngoại lệ cho các lỗi ngữ nghĩa tĩnh). Sau đó, sử dụng Visit () trên mỗi đứa trẻ trong nút cây phân tích cú pháp, kiểm tra chuỗi kết quả nếu nó được mong đợi và xử lý khớp như bạn muốn.

Một điều khác cần lưu ý. Bạn có thể giải quyết vấn đề này thông qua một khách truy cập hoặc lớp người nghe. Lớp khách truy cập rất hữu ích cho các thuộc tính hoàn toàn tổng hợp. Trong giải pháp ví dụ này, tôi trả về một chuỗi đại diện cho loại hàm hoặc lập luận về cây phân tích liên quan, kiểm tra giá trị cho mỗi đứa trẻ quan trọng. Lớp người nghe rất hữu ích cho các ngữ pháp do L quy định - nghĩa là, nơi bạn đang truyền các thuộc tính theo cách định hướng DFS, từ trái sang phải tại mỗi nút trong cây. Trong ví dụ này, bạn có thể sử dụng lớp người nghe và chỉ ghi đè các hàm Thoát (), nhưng sau đó bạn sẽ cần một Bản đồ / Từ điển để ánh xạ "bối cảnh" vào một thuộc tính (chuỗi).

lexer grammar MyFunctionsLexer;
FUNCTION: 'FUNCTION';
NAME: [A-Za-z0-9]+;
DOT: '.';
COMMA: ',';
L_BRACKET: '(';
R_BRACKET: ')';
WS : [ \t\r\n]+ -> skip;
parser grammar MyFunctionsParser;
options { tokenVocab=MyFunctionsLexer; }
functions : function* EOF;
function : FUNCTION '.' NAME '(' (function | argument (',' argument)*) ')';
argument: (NAME | function);
using Antlr4.Runtime;

namespace AntlrConsole2
{
    public class Program
    {
        static void Main(string[] args)
        {
            var input = @"FUNCTION.concat(FUNCTION.substring(String,Integer,Integer),String)";
            var str = new AntlrInputStream(input);
            var lexer = new MyFunctionsLexer(str);
            var tokens = new CommonTokenStream(lexer);
            var parser = new MyFunctionsParser(tokens);
            var listener = new ErrorListener<IToken>();
            parser.AddErrorListener(listener);
            var tree = parser.functions();
            if (listener.had_error)
            {
                System.Console.WriteLine("error in parse.");
            }
            else
            {
                System.Console.WriteLine("parse completed.");
            }
            var visitor = new Validate();
            visitor.Visit(tree);
        }
    }
}
namespace AntlrConsole2
{
    using System;
    using Antlr4.Runtime.Misc;
    using System.Collections.Generic;

    class Validate : MyFunctionsParserBaseVisitor<string>
    {
        Dictionary<String, String> map = new Dictionary<String, String>();

        public Validate()
        {
            map.Add("FUNCTION.add", "Integer:Integer,Integer");
            map.Add("FUNCTION.concat", "String:String,String");
            map.Add("FUNCTION.mul", "Integer:Integer,Integer");
            map.Add("FUNCTION.substring", "String:String,Integer,Integer");
        }

        public override string VisitFunctions([NotNull] MyFunctionsParser.FunctionsContext context)
        {
            for (int i = 0; i < context.ChildCount; ++i)
            {
                var c = context.GetChild(i);
                if (c.GetText() == "<EOF>")
                    continue;
                var top_level_result = Visit(context.GetChild(i));
                if (top_level_result == null)
                {
                    System.Console.WriteLine("Failed semantic analysis: "
                        + context.GetChild(i).GetText());
                }
            }
            return null;
        }

        public override string VisitFunction(MyFunctionsParser.FunctionContext context)
        {
            // Get function name and expected type information.
            var name = context.GetChild(2).GetText();
            map.TryGetValue("FUNCTION." + name, out string type);
            if (type == null)
            {
                return null; // not declared in function table.
            }
            string result_type = type.Split(":")[0];
            string args_types = type.Split(":")[1];
            string[] expected_arg_type = args_types.Split(",");
            const int j = 4;
            var a = context.GetChild(j);
            if (a is MyFunctionsParser.FunctionContext)
            {
                var v = Visit(a);
                if (v != result_type)
                {
                    return null; // Handle type mismatch.
                }
            } else {
                for (int i = j; i < context.ChildCount; i += 2)
                {
                    var parameter = context.GetChild(i);
                    var v = Visit(parameter);
                    if (v != expected_arg_type[(i - j)/2])
                    {
                        return null; // Handle type mismatch.
                    }
                }
            }
            return result_type;
        }

        public override string VisitArgument([NotNull] MyFunctionsParser.ArgumentContext context)
        {
            var c = context.GetChild(0);
            if (c is Antlr4.Runtime.Tree.TerminalNodeImpl)
            {
                // Unclear if what this is supposed to parse:
                // Mutate "1" to "Integer"?
                // Mutate "Integer" to "String"?
                // Or what?
                return c.GetText();
            }
            else
                return Visit(c);
        }
    }
}

Nó không được nhập vào triển khai của khách truy cập vì nó không in System.out.println ("đã nhập visitFifts ::"); tuyên bố.
vikram

Bạn nên thử sử dụng một IDE như IntelliJIDEA và gỡ lỗi chương trình của bạn thay vì dựa vào println. Chương trình của bạn hoạt động với sửa đổi. Bạn nên thay thế các toán tử! = Khi so sánh các chuỗi với! và bằng (). Tôi không thấy mã của bạn cho VerboseListener, vì vậy tôi đã nhận xét điều đó khi thử. Ngoài ra, tôi đã thêm các câu lệnh nhập bị thiếu vào mã của bạn để lấy mã để biên dịch.
kaby76

Trong mã bằng cách sử dụng visitor.visit (cây); tuyên bố Tôi nghĩ rằng nó không nhập bất kỳ phương thức nào, nhưng nếu tôi sử dụng visitor.visitFifts (cây) thì nó đang in. Tui bỏ lỡ điều gì vậy?
vikram

Bây giờ tôi cũng đã cập nhật VerboseListener mà tôi đã sử dụng để ném ngoại lệ.
vikram

Bạn đã thử thiết lập một điểm dừng tại "visitor.visit (cây)" và dòng đầu tiên của visitFifts (), sau đó bước vào hàm visit () và theo logic khi bạn bước một bước hoàn toàn vào mọi chức năng mà nó gọi? Tôi không thể biết chuyện gì đang xảy ra. "visitor.visit (cây)" làm việc cho tôi.
kaby76
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.