I have a binary tree node like this:
class BinaryTreeNode<T> {
BinaryTreeNode(this.value, {this.leftChild, this.rightChild});
T value;
BinaryTreeNode? leftChild;
BinaryTreeNode? rightChild;
}
I'd like to add a toString method so that I can have a visual representation of the contents of the binary tree.
This is a Dart version of this Java question. I was able to port one of the answers there so I am adding it below.
Here is a modified version of this answer:
class BinaryTreeNode<T> {
BinaryTreeNode(this.value, {this.leftChild, this.rightChild});
T value;
BinaryTreeNode<T>? leftChild;
BinaryTreeNode<T>? rightChild;
#override
String toString() {
final out = StringBuffer();
rightChild?._buildTree(out, true, '');
out.writeln(value);
leftChild?._buildTree(out, false, '');
return out.toString();
}
void _buildTree(StringBuffer out, bool isRight, String indent) {
rightChild?._buildTree(out, true, indent + (isRight ? ' ' : '│ '));
out
..write(indent)
..write(isRight ? '┌─── ' : '└─── ')
..writeln(value);
leftChild?._buildTree(out, false, indent + (isRight ? '│ ' : ' '));
}
}
If you build a tree like so:
void main() {
final tree = BinaryTreeNode('D',
leftChild: BinaryTreeNode('A'),
rightChild: BinaryTreeNode(
'R',
leftChild: BinaryTreeNode('T'),
rightChild: BinaryTreeNode('Fun'),
));
print(tree);
}
The output looks like this:
┌─── Fun
┌─── R
│ └─── T
D
└─── A
Feel free to modify my answer to simply the code. I feel like the toString method could be simplified to not repeat so much code.
Suggested solution from lrn
The following solution is more efficient since we are avoiding creating intermediate string objects as we go though the tree structure. Thanks to lrn for suggesting this approach:
class BinaryTreeNode<T> {
BinaryTreeNode(this.value, {this.leftChild, this.rightChild});
T value;
BinaryTreeNode<T>? leftChild;
BinaryTreeNode<T>? rightChild;
#override
String toString() {
final out = StringBuffer();
final indents = <String>[];
rightChild?._buildTree(out, true, indents);
out.writeln(value);
leftChild?._buildTree(out, false, indents);
return out.toString();
}
void _buildTree(StringBuffer out, bool isRight, List<String> indents) {
if (rightChild != null) {
indents.add(isRight ? ' ' : '│ ');
rightChild!._buildTree(out, true, indents);
indents.removeLast();
}
out
..writeAll(indents)
..write(isRight ? '┌─── ' : '└─── ')
..writeln(value);
if (leftChild != null) {
indents.add(isRight ? '│ ' : ' ');
leftChild!._buildTree(out, false, indents);
indents.removeLast();
}
}
}
Related
How to get a tab space for beautification after writing a statement in Xtext.
Here is my code is in Xtext grammar :
Block:
'_block'
name=ID
'_endblock'
;
and UI template is
override complete_BLOCK(EObject model, RuleCall ruleCall, ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
super.complete_BLOCK(model, ruleCall, context, acceptor)
acceptor.accept(createCompletionProposal("_block \n
_endblock","_block",null,context));
}
How do I indent for a tab space after writing a block statement?
to implement a formatter
open the mwe2 file
add formatter = {generateStub = true} to the language = StandardLanguage { section of the workflow
regenerate the language
open the MyDslFormatter Xtend class and implement it
to call the formatter
mark the section to format or dont mark to format everything
call rightclick -> Source -> Format or the Shortcut Cmd/Crtl + Shift + F
here is a very naive no failsafe impl of an auto edit strategy
package org.xtext.example.mydsl1.ui;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentCommand;
import org.eclipse.jface.text.IAutoEditStrategy;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.xtext.ui.editor.autoedit.DefaultAutoEditStrategyProvider;
import com.google.inject.Inject;
import com.google.inject.Provider;
public class YourAutoEditStrategyProvider extends DefaultAutoEditStrategyProvider {
public static class BlockStrategy implements IAutoEditStrategy {
private static final String BLOCK = "_block";
protected int findEndOfWhiteSpace(IDocument document, int offset, int end) throws BadLocationException {
while (offset < end) {
char c= document.getChar(offset);
if (c != ' ' && c != '\t') {
return offset;
}
offset++;
}
return end;
}
#Override
public void customizeDocumentCommand(IDocument d, DocumentCommand c) {
if ("\n".equals(c.text)) {
if (d.getLength()> BLOCK.length()) {
try {
if ((BLOCK+" ").equals(d.get(c.offset-BLOCK.length()-1, BLOCK.length()+1)) || (BLOCK).equals(d.get(c.offset-BLOCK.length(), BLOCK.length()))) {
int p= (c.offset == d.getLength() ? c.offset - 1 : c.offset);
IRegion info= d.getLineInformationOfOffset(p);
int start= info.getOffset();
// find white spaces
int end= findEndOfWhiteSpace(d, start, c.offset);
int l = 0;
StringBuilder buf= new StringBuilder(c.text);
if (end > start) {
// append to input
buf.append(d.get(start, end - start));
l += (end - start);
}
buf.append("\t");
buf.append("\n");
buf.append(d.get(start, end - start));
c.text= buf.toString();
c.caretOffset = c.offset+2+l;
c.shiftsCaret=false;
}
} catch (BadLocationException e) {
e.printStackTrace();
}
}
}
}
}
#Inject
private Provider<BlockStrategy> blockStrategy;
#Override
protected void configure(IEditStrategyAcceptor acceptor) {
super.configure(acceptor);
acceptor.accept(blockStrategy.get(), IDocument.DEFAULT_CONTENT_TYPE);
}
}
and dont forget to bind
class MyDslUiModule extends AbstractMyDslUiModule {
override bindAbstractEditStrategyProvider() {
YourAutoEditStrategyProvider
}
}
I would like to know how I can return the node names instead of the node IDs in the Java console.
The following output is shown in the console:
The desired output should look like:
Just without all the information but only with the Node names (which equal Airportnames).
My Java code looks like the following:
package com.routesNeo4j;
import org.neo4j.driver.v1.*;
import java.util.ArrayList;
import java.util.List;
/**
* Created by e on 11.06.17.
*/
public class Neo4JRouting implements AutoCloseable, Neo4J_Connector {
static Driver driver;
public Neo4JRouting(String startAirport, String destinationAirport, StatementResult shortestPath) {
driver = GraphDatabase.driver("bolt://ec2-13-58-101-13.us-east-2.compute.amazonaws.com:7687",
AuthTokens.basic("neo4j", "Einloggen_123"));
try(Session session = driver.session()) {
shortestPath = session.run("MATCH (a:" + startAirport.toLowerCase() + "), (b:" + destinationAirport.toLowerCase()
+ "), p = allShortestPaths((a)-[r*1..4]-(b)) UNWIND rels(p) AS rel RETURN nodes(p), sum(rel.weight) " +
"AS weight ORDER BY sum(rel.weight)");
List<Record> storeList = storeList(shortestPath);
while (shortestPath.hasNext()) {
System.out.println(shortestPath.next().toString());
}
System.out.println(storeList);
} catch (Exception e) {
e.printStackTrace();
}
}
public List<Record> storeList(StatementResult statementResult) {
List<Record> list = new ArrayList<>();
while (statementResult.hasNext()) {
list.add(statementResult.next());
}
return list;
}
#Override
public Driver runDriver(String user, AuthToken basicAuthToken) throws Exception {
return null;
}
#Override
public void close() throws Exception {
}
}
I am looking forward to your answers. Many thanks!
Every row you are returning contains a list of nodes and a weight. That's what you ask in your query and that's what you get. So you have to "unpack" that result into the format that you desire.
Couple of code-snippets to show what I mean :
StatementResult vResult = vSession.run(aCypher);
while (vResult.hasNext()) {
Record vRecord = vResult.next();
vMutator.pushNode("row");
for (Pair <String, Value> vListEntry : vRecord.fields()) {
process_listentry(vSession, vMutator, vListEntry.key(), vListEntry.value());
}
vMutator.popNode(); // row
}
and then in process_listentry :
private void process_listentry(Session vSession, IHDSMutator vMutator, String vKey, Value vValue) {
...
else if (vValue.type().equals(vSession.typeSystem().NODE())){
vMutator.pushNode(vKey);
vMutator.addNode("_id", Long.toString(vValue.asNode().id()));
for (String lLabel : vValue.asNode().labels()) {
vMutator.addNode("_label", lLabel);
}
for (String lKey : vValue.asNode().keys()) {
Value lValue = vValue.asNode().get(lKey);
process_listentry(vSession, vMutator, lKey, lValue);
}
vMutator.popNode();
}
...
but it does depend on what you ask in the query and thus have to unpack ...
Hope this helps,
Tom
I have a DSL that includes blocks that need to be wrapped as methods returned inside an anonymous class created by the generated code. For example:
model {
task {
val x = 2*5;
Math.pow(2, x)
}
}
should compile to (note task becoming an instance of Runnable, with the body of the task becoming the body of the Runnable.run() method):
import java.util.Collection;
#SuppressWarnings("all")
public class MyFile {
public Collection<Runnable> tasks() {
ArrayList<Runnable> tasks = new ArrayList<>();
tasks.add(getTask0());
return tasks;
}
public static Runnable getTask0() {
Runnable _runnable = new Runnable() {
public void run() {
final int x = (2 * 5);
Math.pow(2, x);
}
}
return _runnable;
}
}
Following the discussion in this question, I was able to get this particular example to work. (Github repo includes unit tests.) But I had to do it by representing the Task element in the grammar as a sequence of XExpressions (source), which my XbaseCompiler subclass had to iterate over (source).
Instead, it would have been nice to be able to just have Task contain an XBlockExpression in a property action, and then in the compiler just do doInternalToJavaStatement(expr.action, it, isReferenced). My sense is that this is really the "right" solution in my case, but when I tried it, this would result in an empty body of the generated run method, as if the block was not processed at all. What's going on, and am I missing some required bits of setup/wiring things together/bindings that are necessary for this to work?
you ususally try to avoid that by using a better inference strategy e.g.
Grammar
Model:
{Model}"model" "{"
vars+=Variable*
tasks+=Task*
"}"
;
Variable:
"var" name=ID ":" type=JvmParameterizedTypeReference
;
Task:
{Task} "task" content=XBlockExpression
;
Inferrer
class MyDslJvmModelInferrer extends AbstractModelInferrer {
#Inject extension JvmTypesBuilder
def dispatch void infer(Model element, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
acceptor.accept(element.toClass("test.Model2")) [
for (v : element.vars) {
members+=v.toField(v.name, v.type.cloneWithProxies) [
]
}
var i = 0;
for (t : element.tasks) {
val doRunName = "doRun"+i
members += t.toMethod("task"+i, Runnable.typeRef()) [
body = '''
return new «Runnable» () {
public void run() {
«doRunName»();
}
};
'''
]
members += t.toMethod(doRunName, Void.TYPE.typeRef()) [
body = t.content
]
i = i + 1
}
]
}
}
and that basically is it.
you may follow https://bugs.eclipse.org/bugs/show_bug.cgi?id=481992
If you really want to adapt the xbase typesystem that may be a lot more of work e.g. (just covering a minimal case)
Grammar
Model:
{Model}"model" "{"
vars+=Variable*
tasks+=Task*
"}"
;
Variable:
"var" name=ID ":" type=JvmParameterizedTypeReference
;
Task:
{Task} "task" content=XTaskContent
;
XTaskContent returns xbase::XExpression:
{XTaskContent} block=XBlockExpression
;
Inferrer
class MyDslJvmModelInferrer extends AbstractModelInferrer {
#Inject extension JvmTypesBuilder
def dispatch void infer(Model element, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
acceptor.accept(element.toClass("test.Model")) [
for (v : element.vars) {
members+=v.toField(v.name, v.type.cloneWithProxies) [
]
}
var i = 0;
for (t : element.tasks) {
members += t.toMethod("task"+i, Runnable.typeRef()) [
body = t.content
]
i = i + 1
}
]
}
}
Type Computer
class MyDslTypeComputer extends XbaseTypeComputer {
override computeTypes(XExpression expression, ITypeComputationState state) {
if (expression instanceof XTaskContent) {
_computeTypes(expression as XTaskContent, state);
} else {
super.computeTypes(expression, state)
}
}
protected def void _computeTypes(XTaskContent object, ITypeComputationState state) {
state.withExpectation(getPrimitiveVoid(state)).computeTypes(object.block)
state.acceptActualType(getTypeForName(Runnable, state), ConformanceFlags.CHECKED_SUCCESS )
}
}
Compiler
class MyDslCompiler extends XbaseCompiler {
override protected internalToConvertedExpression(XExpression obj, ITreeAppendable appendable) {
if (obj instanceof XTaskContent) {
appendable.append("new ").append(Runnable).append("() {").newLine
appendable.increaseIndentation
appendable.append("public void run()").newLine
reassignThisInClosure(appendable, null)
internalToJavaStatement(obj.block, appendable, false)
appendable.newLine
appendable.decreaseIndentation
appendable.newLine.append("}")
} else {
super.internalToConvertedExpression(obj, appendable)
}
}
}
Bindings
class MyDslRuntimeModule extends AbstractMyDslRuntimeModule {
def Class<? extends ITypeComputer> bindITypeComputer() {
return MyDslTypeComputer
}
def Class<? extends XbaseCompiler> bindXbaseCompiler() {
return MyDslCompiler
}
}
I have a certain part of my XText grammer that defines a block for classes that shall print all its expressions. The XText grammar part for this looks as follows:
Print:
{Print}
'print' '{'
print += PrintLine*
'}';
PrintLine:
obj += XExpression;
Now I use the following inferrer code to create a print() method:
Print: {
members += feature.toMethod('print', typeRef(void)) [
body = '''
«FOR printline : feature.print»
System.out.println(«printline.obj»);
«ENDFOR»
'''
]
}
Ok, I go ahead and test it with the following code in a class:
print {
"hallo"
4
6 + 7
}
And the result is the following:
public void print() {
System.out.println([org.eclipse.xtext.xbase.impl.XStringLiteralImpl#20196ba8 (value: hallo)]);
System.out.println([org.eclipse.xtext.xbase.impl.XNumberLiteralImpl#7d0b0f7d (value: 4)]);
System.out.println([<XNumberLiteralImpl> + <XNumberLiteralImpl>]);}
Of course, I was hoping for:
public void print() {
System.out.println("hallo");
System.out.println(4);
System.out.println(6+7);
}
I understand that I might have to call the compiler somehow in the inferrer for «printline.obj», but I am really not sure how.
i think you are doing this on a wrong basis. this sounds to me like an extension of xbase, not only a simple use.
import "http://www.eclipse.org/xtext/xbase/Xbase" as xbase
Print:
{Print}
'print'
print=XPrintBlock
;
XPrintBlock returns xbase::XBlockExpression:
{xbase::XBlockExpression}'{'
expressions+=XPrintLine*
'}'
;
XPrintLine returns xbase::XExpression:
{PrintLine} obj=XExpression
;
Type Computer
class MyDslTypeComputer extends XbaseTypeComputer {
def dispatch computeTypes(XPrintLine literal, ITypeComputationState state) {
state.withNonVoidExpectation.computeTypes(literal.obj)
state.acceptActualType(getPrimitiveVoid(state))
}
}
Compiler
class MyDslXbaseCompiler extends XbaseCompiler {
override protected doInternalToJavaStatement(XExpression obj, ITreeAppendable appendable, boolean isReferenced) {
if (obj instanceof XPrintLine) {
appendable.trace(obj)
appendable.append("System.out.println(")
internalToJavaExpression(obj.obj,appendable);
appendable.append(");")
appendable.newLine
return
}
super.doInternalToJavaStatement(obj, appendable, isReferenced)
}
}
XExpressionHelper
class MyDslXExpressionHelper extends XExpressionHelper {
override hasSideEffects(XExpression expr) {
if (expr instanceof XPrintLine || expr.eContainer instanceof XPrintLine) {
return true
}
super.hasSideEffects(expr)
}
}
JvmModelInferrer
def dispatch void infer(Print print, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
acceptor.accept(
print.toClass("a.b.C") [
members+=print.toMethod("demo", Void.TYPE.typeRef) [
body = print.print
]
]
)
}
Bindings
class MyDslRuntimeModule extends AbstractMyDslRuntimeModule {
def Class<? extends ITypeComputer> bindITypeComputer() {
MyDslTypeComputer
}
def Class<? extends XbaseCompiler> bindXbaseCompiler() {
MyDslXbaseCompiler
}
def Class<? extends XExpressionHelper> bindXExpressionHelper() {
MyDslXExpressionHelper
}
}
I'm trying to write a piece of code that will take an ANTLR4 parser and use it to generate ASTs for inputs similar to the ones given by the -tree option on grun (misc.TestRig). However, I'd additionally like for the output to include all the line number/offset information.
For example, instead of printing
(add (int 5) '+' (int 6))
I'd like to get
(add (int 5 [line 3, offset 6:7]) '+' (int 6 [line 3, offset 8:9]) [line 3, offset 5:10])
Or something similar.
There aren't a tremendous number of visitor examples for ANTLR4 yet, but I am pretty sure I can do most of this by copying the default implementation for toStringTree (used by grun). However, I do not see any information about the line numbers or offsets.
I expected to be able to write super simple code like this:
String visit(ParseTree t) {
return "(" + t.productionName + t.visitChildren() + t.lineNumber + ")";
}
but it doesn't seem to be this simple. I'm guessing I should be able to get line number information from the parser, but I haven't figured out how to do so. How can I grab this line number/offset information in my traversal?
To fill in the few blanks in the solution below, I used:
List<String> ruleNames = Arrays.asList(parser.getRuleNames());
parser.setBuildParseTree(true);
ParserRuleContext prc = parser.program();
ParseTree tree = prc;
to get the tree and the ruleNames. program is the name for the top production in my grammar.
The Trees.toStringTree method can be implemented using a ParseTreeListener. The following listener produces exactly the same output as Trees.toStringTree.
public class TreePrinterListener implements ParseTreeListener {
private final List<String> ruleNames;
private final StringBuilder builder = new StringBuilder();
public TreePrinterListener(Parser parser) {
this.ruleNames = Arrays.asList(parser.getRuleNames());
}
public TreePrinterListener(List<String> ruleNames) {
this.ruleNames = ruleNames;
}
#Override
public void visitTerminal(TerminalNode node) {
if (builder.length() > 0) {
builder.append(' ');
}
builder.append(Utils.escapeWhitespace(Trees.getNodeText(node, ruleNames), false));
}
#Override
public void visitErrorNode(ErrorNode node) {
if (builder.length() > 0) {
builder.append(' ');
}
builder.append(Utils.escapeWhitespace(Trees.getNodeText(node, ruleNames), false));
}
#Override
public void enterEveryRule(ParserRuleContext ctx) {
if (builder.length() > 0) {
builder.append(' ');
}
if (ctx.getChildCount() > 0) {
builder.append('(');
}
int ruleIndex = ctx.getRuleIndex();
String ruleName;
if (ruleIndex >= 0 && ruleIndex < ruleNames.size()) {
ruleName = ruleNames.get(ruleIndex);
}
else {
ruleName = Integer.toString(ruleIndex);
}
builder.append(ruleName);
}
#Override
public void exitEveryRule(ParserRuleContext ctx) {
if (ctx.getChildCount() > 0) {
builder.append(')');
}
}
#Override
public String toString() {
return builder.toString();
}
}
The class can be used as follows:
List<String> ruleNames = ...;
ParseTree tree = ...;
TreePrinterListener listener = new TreePrinterListener(ruleNames);
ParseTreeWalker.DEFAULT.walk(listener, tree);
String formatted = listener.toString();
The class can be modified to produce the information in your output by updating the exitEveryRule method:
#Override
public void exitEveryRule(ParserRuleContext ctx) {
if (ctx.getChildCount() > 0) {
Token positionToken = ctx.getStart();
if (positionToken != null) {
builder.append(" [line ");
builder.append(positionToken.getLine());
builder.append(", offset ");
builder.append(positionToken.getStartIndex());
builder.append(':');
builder.append(positionToken.getStopIndex());
builder.append("])");
}
else {
builder.append(')');
}
}
}