使用 C# 进行交互式输入?
Interactive input using C#?
我是 C# 和 ANTLR 的新手,我不知道如何使用 ANTLR4 执行交互式逐行解析,就像 ANTLR 4 Book 中显示的一样 actions/tools/calc.java 第 10 章中的示例,它使用 BufferedReader class(据我所知 .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
BufferedReader
class 是一条红鲱鱼。那(或 Scanner
)正是您在 Java 中读取一行输入所需要的。如果您知道如何在 C# 中从文件或标准输入中读取一行,那么您就拥有了所需的一切。
Clearly, I could just instantiate new instances of the input stream, lexer, token stream, and parser for each line of input, but that doesn't seem to be the most efficient solution.
除了解析器,所有这些都是在 Java 代码的每次迭代中新创建的。只有解析器不是新创建的。相反,它会在现有实例上调用 setInputStream
。
所以您需要的是 C#
等价于 setInputStream
方法。看起来就是可以设置的 TokenStream
属性。所以该行变为:
parser.TokenStream = tokens;
这是我想出的解决问题的代码。可能有更有效的方法来做到这一点,几年后我可能会为此感到尴尬;-)
//
// 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
我是 C# 和 ANTLR 的新手,我不知道如何使用 ANTLR4 执行交互式逐行解析,就像 ANTLR 4 Book 中显示的一样 actions/tools/calc.java 第 10 章中的示例,它使用 BufferedReader class(据我所知 .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
BufferedReader
class 是一条红鲱鱼。那(或 Scanner
)正是您在 Java 中读取一行输入所需要的。如果您知道如何在 C# 中从文件或标准输入中读取一行,那么您就拥有了所需的一切。
Clearly, I could just instantiate new instances of the input stream, lexer, token stream, and parser for each line of input, but that doesn't seem to be the most efficient solution.
除了解析器,所有这些都是在 Java 代码的每次迭代中新创建的。只有解析器不是新创建的。相反,它会在现有实例上调用 setInputStream
。
所以您需要的是 C#
等价于 setInputStream
方法。看起来就是可以设置的 TokenStream
属性。所以该行变为:
parser.TokenStream = tokens;
这是我想出的解决问题的代码。可能有更有效的方法来做到这一点,几年后我可能会为此感到尴尬;-)
//
// 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