使用C#进行交互式输入?

时间:2018-05-19 20:07:50

标签: c# antlr4

我是C#和ANTLR新手,我无法弄清楚如何使用ANTLR4执行交互式逐行解析,方法与ANTLR 4 Book'第10章中的actions / tools / calc.java示例,它使用BufferedReader类(据我所知,.NET / C#不具有等价物),其中每行输入都被立即解析而不是全部解析在末尾。显然,我可以为每行输入实例化输入流,词法分析器,标记流和解析器的新实例,但这似乎不是最有效的解决方案。这样做的正确C#方式是什么?

是否存在包含示例程序的C#转换的存储库?另外,是否有一个描述Java运行时和C#运行时之间差异的文档? (例如在calc.java示例中,作者有这一行:

ExprParser parser = new ExprParser(null); // share single parser instance

,它不适用于C#运行时)

以下是calc.java代码供参考:

/***
 * Excerpted from "The Definitive ANTLR 4 Reference",
 * published by The Pragmatic Bookshelf.
 * Copyrights apply to this code. It may not be used to create training material, 
 * courses, books, articles, and the like. Contact us if you are in doubt.
 * We make no guarantees that this code is fit for any purpose. 
 * Visit http://www.pragmaticprogrammer.com/titles/tpantlr2 for more book information.
***/
package tools;

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

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;

public class Calc {
    public static void main(String[] args) throws Exception {
        String inputFile = null;
        if ( args.length>0 ) inputFile = args[0];
        InputStream is = System.in;
        if ( inputFile!=null ) {
            is = new FileInputStream(inputFile);
        }

        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        String expr = br.readLine();              // get first expression
        int line = 1;                             // track input expr line numbers

        ExprParser parser = new ExprParser(null); // share single parser instance
        parser.setBuildParseTree(false);          // don't need trees

        while ( expr!=null ) {             // while we have more expressions
            // create new lexer and token stream for each line (expression)
            ANTLRInputStream input = new ANTLRInputStream(expr+"\n");
            ExprLexer lexer = new ExprLexer(input);
            lexer.setLine(line);           // notify lexer of input position
            lexer.setCharPositionInLine(0);
            CommonTokenStream tokens = new CommonTokenStream(lexer);
            parser.setInputStream(tokens); // notify parser of new token stream
            parser.stat();                 // start the parser
            expr = br.readLine();          // see if there's another line
            line++;
        }
    }
}

这是语法:

/** Grammar from tour chapter augmented with actions */

grammar Expr;

options
    {
    language = CSharp;              // The semantic actions are written in C#, so this grammar can only be used with the C# code generator
    }

@parser::members 
    {
        /** "memory" for our calculator; variable/value pairs go here */
        Dictionary <string, int> memory = new Dictionary <string, int> ();

        int eval (int left, int op, int right)
            {
            switch (op)
                {
                case MUL : return left * right;
                case DIV : return left / right;
                case ADD : return left + right;
                case SUB : return left - right;
                }
            return 0;
            }   // End eval

    }

stat:   e NEWLINE           {Console.WriteLine ($e.v);}
    |   ID '=' e NEWLINE    {memory.Add ($ID.text, $e.v);}
    |   NEWLINE                   
    ;

e returns [int v]
    : a=e op=('*'|'/') b=e  {$v = eval ($a.v, $op.type, $b.v);}
    | a=e op=('+'|'-') b=e  {$v = eval ($a.v, $op.type, $b.v);}  
    | INT                   {$v = $INT.int;}    
    | ID
      {
      string id = $ID.text;
      $v = memory.ContainsKey (id) ? memory [id] : 0;
      }
    | '(' e ')'             {$v = $e.v;}       
    ; 

MUL : '*' ;
DIV : '/' ;
ADD : '+' ;
SUB : '-' ;

ID  :   [a-zA-Z]+ ;      // match identifiers
INT :   [0-9]+ ;         // match integers
NEWLINE:'\r'? '\n' ;     // return newlines to parser (is end-statement signal)
WS  :   [ \t]+ -> skip ; // toss out whitespace

2 个答案:

答案 0 :(得分:1)

BufferedReader班是红鲱鱼。那个(或Scanner)就是你在Java中读取一行输入所需要的。如果您知道如何从C#中的文件或标准输入读取一行,那么您就可以获得所需的全部内容。

  

显然,我可以为每行输入实例化输入流,词法分析器,标记流和解析器的新实例,但这似乎不是最有效的解决方案。

除了解析器之外,所有这些都是在Java代码的每次迭代中新创建的。只有解析器不是新创建的。相反,它会在现有实例上调用setInputStream

所以你需要的是C#方法的setInputStream等价物。它看起来像TokenStream属性,可以设置。这条线变为:

parser.TokenStream = tokens;

答案 1 :(得分:0)

以下是我提出的解决问题的代码。可能有更有效的方法来做到这一点,我可能会在几年内因此而感到尴尬; - )

//
// C# version of code\actions\tools\Calc.java in Chapter 10 of "The Definitive ANTLR 4 Reference"
//

using System;
using System.IO;
using System.Text;

using Antlr4.Runtime;

namespace Calc
    {
    class Program
        {
        static void Main (string [] Args)
            {
            StreamReader  input_src;

            //
            // If there is a file name on the command line, then use it as the input source; otherwise,
            // use the console (keyboard) as the input source
            //

            if (Args.Length > 0)
                {
                input_src = File.OpenText (Args [0]);
                }
            else
                {
                Console.WriteLine ("Enter expressions to evaluate");
                input_src = new StreamReader (Console.OpenStandardInput (), Console.InputEncoding);
                }

            //
            // Read the first line from the input source
            //

            string              input = input_src.ReadLine ();
            int                 cur_line = 1;                                           // Needed when parsing lines in a file

            //
            // Create a parser without a token source. This allows us to instantiate the parser just
            // once, preserving the @parser::members declared in the grammar. Later, we'll attach the
            // parser to a token stream
            //

            ExprParser          parser = new ExprParser (null);

            parser.BuildParseTree = false;

            //
            // Loop getting input from the input source (console or file) until end of file (or CTRL-Z if input is console)
            //

            while (input != null)
                {

                //
                // The grammar is expecting a NEWLINE as a statement terminator, but that isn't included by ReadLine so add a NEWLINE
                // to the end of the input string
                //

                input = input + "\n";

                //
                // Turn the input string into a stream compatible with ANTLR
                //

                byte []             input_bytes = Encoding.ASCII.GetBytes (input);
                MemoryStream        mem_stream = new MemoryStream (input_bytes);

                //
                // Attach ANTLR to the memory stream
                //

                AntlrInputStream    input_stream = new AntlrInputStream (mem_stream);   // Create a stream that reads from the input source
                ExprLexer           lexer = new ExprLexer (input_stream);               // Create a lexer that feeds off of the input stream

                //
                // When reading from a file the line number is important for error messages. Normally, we would read the entire file into
                // a string and then parse it, but we're not doing that; we are parsing each line as we read it, so tell the lexer the current
                // line number and character position before it lexes each input line. If we didn't do this, the error reporting mechanism 
                // would always report that the error was on line 1
                //

                lexer.Line = cur_line;
                lexer.Column = 0;

                CommonTokenStream   tokens = new CommonTokenStream (lexer);             // Create a buffer of tokens pulled from the lexer

                //
                // Attach the parser to the new token stream (the current line), and start the parse by calling the 'stat' rule in the grammar
                // The semantic actions will then do all the work of outputting the results from processing the expressions
                //

                parser.TokenStream = tokens;
                parser.stat ();

                //
                // Get the next line of input from the input source
                //

                input = input_src.ReadLine ();
                cur_line = cur_line + 1;
                }   // End while

            }   // End Main

        }   // End class Program

    }   // End namespace Calc