我是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
答案 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