Интерактивный ввод с использованием C #? - PullRequest
0 голосов
/ 19 мая 2018

Я новичок в C # и ANTLR, и я не могу понять, как выполнять интерактивный построчный анализ, используя ANTLR4, точно так же, как это показано в действиях / инструментах / calc Книги 4 ANTLR.Пример java в главе 10, который использует класс 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 голосов
/ 20 мая 2018

Вот код, который я придумал, который решает проблему.Возможно, есть более эффективные способы сделать это, и я, вероятно, буду смущен этим через несколько лет; -)

//
// 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
0 голосов
/ 20 мая 2018

Класс BufferedReader - это красная сельдь.Это (или Scanner) как раз то, что вам нужно, чтобы прочитать строку ввода в Java.Если вы знаете, как читать строки из файла или стандартного ввода в C #, у вас есть все, что вам нужно.

Очевидно, я мог бы просто создать новые экземпляры входного потока, лексер, поток токенов и анализатор для каждой строки ввода, но это, кажется, не самое эффективное решение.

За исключением синтаксического анализатора, все они заново создаются на каждой итерации вJava-код также.Только парсер не создан заново.Вместо этого он вызывает setInputStream в существующем экземпляре.

Так что вам нужен C# эквивалент setInputStream метода.Похоже, это свойство TokenStream, которое можно установить.Таким образом, строка становится:

parser.TokenStream = tokens;
...