I have a sample model i am using as below
grammar org.xtext.example.testdsl.TestDsl with org.eclipse.xtext.common.Terminals
generate testDsl "http://www.xtext.com/test/example/TestDsl"
Model:
prog+=Program*;
Program: g=Greeting de+=DataEntry* s+=Statement*;
Greeting: 'Hello' t=ProgPara '!';
ProgPara: 'PROGRAM' pname=Progname ';';
Progname : name=ID;
DataEntry: a=INT (v=Varname| in=Indexname) ';';
Varname : name = ID;
Statement: c=CopyStmt ';';
CopyStmt: 'COPY' 'TO' qname=[IndexVarname|ID] ;
IndexVarname : (Indexname|Varname);
Indexname : '(' name = ID ')';
Named:Progname|Indexname|Varname;
I have added caching support for Scoping like in the code below:
class TestDslScopeProvider extends AbstractTestDslScopeProvider {
#Inject
IResourceScopeCache cache;
override getScope(EObject context, EReference reference) {
if (context instanceof CopyStmt) {
if (reference.featureID == TestDslPackage.COPY_STMT__QNAME) {
val candidates = cache.get(
"COPY_STMT__QNAME_scope",
reference.eResource,
[|findQNameCandidates(context, reference)]
);
return Scopes.scopeFor(candidates);
}
}
return super.getScope(context, reference);
}
def findQNameCandidates(EObject context, EReference reference) {
val rootElement = EcoreUtil2.getRootContainer(context);
val candidates1 = EcoreUtil2.getAllContentsOfType(rootElement, IndexVarname);
return candidates1;
}
}
Now i have a sample test case as below:
Hello PROGRAM test;!
1 test1;
2 (test4);
3 test3;
COPY TO test4;
COPY TO test4;
When i try to rename test4 using the Rename Element, only the variable in the definition is getting renamed. The references are not getting renamed. Without caching it works fine, but when caching is done, i hit this issue. What am i missing here?
Thanks,
Anitha
you do cache wrong
reference.eResource
you need to use the context resource as cache not the metamodels
context.eResource,
Related
I'm new to Elm and I ran into this problem...
We get translations for our page using something like:
case (translate translation.id) of
Success: -> translation
Failure: -> translation.id
Where translate just finds translation.id in a dictionary and it may or may not be there.
There are no runtime errors because you get a string either way, but we would like to log the missing translation to a rest service logger. But Elm hates side effects in the view that doesn't stem from html events so I'm not sure how to handle this.
Obviously in regular JS you could just crowbar in a fetch inside the failure case block and then return a string afterwards but that doesn't seem to be possible in Elm?
You need to move the effects part of your code into the update function. In this case I suggest doing it when the translation arrives. There you will want something like
OnTranslation translation ->
( { model | translation = translation } -- attach to model
, case translate translation.id of
Ok _ ->
Cmd.none
Err err ->
-- register with the error logger
logMissingTranslation translation.id
)
#Odin Thorsen, use Html.node and Html.Attributes.attribute to reference a custom element in Elm:
-- VIEW
view : Model -> Html.Html ()
view model =
Html.div []
[ translation model "key"
, translation model "junk key"
]
translation : Model -> String -> Html.Html ()
translation { translations } id =
Html.node "translated-text"
[ Html.Attributes.attribute "translation-id" id
, Html.Attributes.attribute "translation-text" <| translate translations id
]
[]
translate : Dict.Dict String String -> String -> String
translate translations id =
case Dict.get id translations of
Just value ->
value
Nothing ->
""
And in Javascript, use customElements.define to create the custom element:
customElements.define("translated-text", class extends HTMLElement {
constructor() { super(); }
connectedCallback() { }
attributeChangedCallback() { this.setTextAndLogFailure(); }
static get observedAttributes() { return ["translation-id", "translation-text"]; }
setTextAndLogFailure() {
var id = this.getAttribute("translation-id");
var text = this.getAttribute("translation-text");
if (text === null) return;
this.textContent = text;
if (!text.length) alert("Unkown translation id: " + id);
}
});
You'd replace alert("Unkown translation id: " + id); with the logging fetch.
Here is an Ellie with a full solution.
I am trying to bind my data class for a sql query but I am getting a error when I am not using all the parameters from my data class. Is there a way to check in the sql query which parameters needs binding and which ones do not or allow to bind parameters which are not used. The error looks as following:
Request error
java.lang.IllegalArgumentException: Identifier 'deleted_at' is not a valid identifier. Should be of the pattern '\$([\d]+)'.
at io.r2dbc.postgresql.ExtendedQueryPostgresqlStatement.getIndex(ExtendedQueryPostgresqlStatement.java:196)
Caused by: java.lang.IllegalArgumentException: Identifier 'deleted_at' is not a valid identifier. Should be of the pattern '\$([\d]+)'.
at io.r2dbc.postgresql.ExtendedQueryPostgresqlStatement.getIndex(ExtendedQueryPostgresqlStatement.java:196)
And this is the code I use:
Repository:
client
.sql(
"""
INSERT INTO app_user_settings (uuid, allows_to_process_transactions, app_user_id) VALUES (:uuid, :allows_to_process_transactions, :app_user_id)
RETURNING *
""".trimIndent()
)
.bind(AppUserSettingsWriteConverter(), appUserSettings)
.map(AppUserSettingsReadConverter()::convert)
.awaitOne()
Custom bind method:
fun <T> DatabaseClient.GenericExecuteSpec.bind(
convertor: Converter<T, OutboundRow>,
value: T
): DatabaseClient.GenericExecuteSpec {
val outboundRow = convertor.convert(value!!)!!
val t = outboundRow.toMap()
var execution = this
t.forEach { (t, u) ->
execution = execution.bind(t.toString(), u)
}
return execution
}
WriteConverter:
class AppUserSettingsWriteConverter : Converter<AppUserSettings, OutboundRow> {
override fun convert(source: AppUserSettings): OutboundRow {
val outboundRow = OutboundRow()
if (source.isSaved()) {
outboundRow[SqlIdentifier.unquoted("id")] = Parameter.from(source.id)
}
outboundRow[SqlIdentifier.unquoted("uuid")] = Parameter.from(source.uuid)
outboundRow[SqlIdentifier.unquoted("allows_to_process_transactions")] = Parameter.from(source.allowsToProcessTransactions)
outboundRow[SqlIdentifier.unquoted("app_user_id")] = Parameter.from(source.appUserId)
outboundRow[SqlIdentifier.unquoted("deleted_at")] = Parameter.fromOrEmpty(source.deletedAt, ZonedDateTime::class.java)
return outboundRow
}
}
I am using now a check if deleted_at is empty and then not bind it but would prefer if there is another way to do it.
My code:
Mono.zip(
credentialService.getCredentials(connect.getACredentialsId()),
credentialService.getCredentials(connect.getBCredentialsId())
)
.flatMap(...
From the frontend we get connect object with 2 fields:
connect{
aCredentialsId : UUID //required
bCredentialsId : UUID //optional
}
So sometimes the second line credentialService.getCredentials(connect.getBCredentialsId())) can return Mono.empty
How to write code to be prepared for this empty Mono when my second field bCredentialsId is null?
What should I do? In case of empty values return Mono.just(new Object) and then check if obj.getValue != null??? I need to fetch data from DB for 2 different values
The strategy I prefer here is to declare an optional() utility method like so:
public class Utils {
public static <T> Mono<Optional<T>> optional(Mono<T> in) {
return in.map(Optional::of).switchIfEmpty(Mono.just(Optional.empty()));
}
}
...which then allows you to transform your second Mono to one that will always return an optional, and thus do something like:
Mono.zip(
credentialService.getCredentials(connect.getACredentialsId()),
credentialService.getCredentials(connect.getBCredentialsId()).transform(Utils::optional)
).map(e -> new Connect(e.getT1(), e.getT2()))
(...assuming you have a Connect object that takes an Optional as the second parameter of course.)
An easier way is using mono's defaultIfEmpty method.
Mono<String> m1 = credentialService.getCredentials(connect.getACredentialsId());
Mono<String> m2 = credentialService.getCredentials(connect.getBCredentialsId()).defaultIfEmpty("");
Mono.zip(m1, m2).map(t -> connectService.connect(t.getT1(), t.getT2()));
Explanation: if m2 is null then get empty string as a default value instead of null.
Instead of using .zip here, I would work with a nullable property of Connect and use .flatMap in combination with .switchIfEmpty for it.
Kotlin-Version:
val aCredentials = credentialService.getCredentials(connect.getACredentialsId())
credentialService.getCredentials(connect.getBCredentialsId())
.flatMap { bCredentials -> aCredentials
.map { Connect(it, bCredentials)}
.switchIfEmpty(Connect(null, bCredentials))
}
.switchIfEmpty { aCredentials.map { Connect(it, null) } }
I have a "statement" definition from the Java language definition as follows.
statement
: block
| ASSERT expression (':' expression)? ';'
| 'if' parExpression statement ('else' statement)?
| 'for' '(' forControl ')' statement
| 'while' parExpression statement
| 'do' statement 'while' parExpression ';'
| 'try' block
( catches 'finally' block
| catches
| 'finally' block
)
| 'switch' parExpression switchBlock
| 'synchronized' parExpression block
| 'return' expression? ';'
| 'throw' expression ';'
| 'break' Identifier? ';'
| 'continue' Identifier? ';'
| ';'
| statementExpression ';'
| Identifier ':' statement
;
When doing the parser, i want to print the full user-written statements also (inculding the spaces in the statements), such as:
Object o = Ma.addToObj(r1);
if(h.isFull() && !h.contains(true)) h.update(o);
But when i use the function "getText()" in "exitStatement", i can only get the statements with all the spaces been deleted, such as:
Objecto=Ma.addToObj(r1);
if(h.isFull()&&!h.contains(true))h.update(o);
How can i get the full user-written statements (inculding the spaces in the statements) in a easy way? Thanks a lot!
The full codes as follows:
public class PrintStatements {
public static class GetStatements extends sdlParserBaseListener {
StringBuilder statements = new StringBuilder();
public void exitStatement(sdlParserParser.StatementContext ctx){
statements.append(ctx.getText());
statements.append("\n");
}
}
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);
}
ANTLRInputStream input = new ANTLRInputStream(is);
sdlParserLexer lexer = new sdlParserLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
sdlParserParser parser = new sdlParserParser(tokens);
ParseTree tree = parser.s();
// create a standard ANTLR parse tree walker
ParseTreeWalker walker = new ParseTreeWalker();
// create listener then feed to walker
GetStatements loader = new GetStatements();
walker.walk(loader, tree); // walk parse tree
System.out.println(loader.statements.toString());
}
}
I've solved this problem by using tokens.getText() in the upper level of the statement, like this:
public void exitE(sdlParserParser.EContext ctx) {
TokenStream tokens = parser.getTokenStream();
String Stmt = null;
Stmt = tokens.getText(ctx.statement());
...
}
I'm pretty new with ANTLR, so maybe i'm wrong with something...
I don't know easy way to do this but you can try something like this.
In your grammar file you probably have something like this:
WS : (' '|'\r'|'\t'|'\u000C'|'\n')
{
if (!preserveWhitespacesAndComments) {
skip();
} else {
$channel = HIDDEN;
}
}
This lexer rule tells parser to ignore whitespaces. More exactly this tokens are sent on HIDDEN channel (parser don't see them). If you comment this lines of code
WS : (' '|'\r'|'\t'|'\u000C'|'\n')
{
if (!preserveWhitespacesAndComments) {
// skip();
} else {
// $channel = HIDDEN;
}
}
all whitespaces will be sent to parser but then you need to rewrite parser rules so he can expect whitespaces at any place.
Object(EXPECT WHITESPACE)o(EXPECT WHITESPACE)=(EXPECT WHITESPACE)Ma.addToObj(r1);
Otherwise parser will report errors.
You need one of two things:
The ability to take file position data for the first and last tokens accepted by a statement parse (either the lexemes or the tree nodes should do), and go to the source file, and extract the text. That will get you the original whitespace.
A prettyprinter, which will regenerate text from the AST, inserting appropriate whitespacing. See my SO answer on how to build a prettyprinter here.
in terms of Antlr4 and Python3, the code looks as follows:
def exitSomeDecl(self, ctx: yourParser.SomeDeclContext):
start_index = ctx.start.tokenIndex
stop_index = ctx.stop.tokenIndex
user_text = self.token_stream.getText(interval=(start_index, stop_index))
here, the self.token_stream: CommonTokenStream is assigned during init:
input_stream = FileStream(file_name)
lexer = sdplLexer(input_stream)
token_stream = CommonTokenStream(lexer)
I would like to parse a file where the first line may or may not contain a definition of a "project" name (like with Pascal's program keyword), and if not, use the name of the file that is being parsed as default. Simplified:
#members{ String projectName; }
project : {projectName = ...} // name of parsed file as default
( PROJECTNAMEKEYWORD ID {projectName = $ID.text;} )?
otherstuff {/*...*/};
Is this even possible? I found this surprisingly hard to find out using google or the antlr manual. By its documentation, input.getSourceName() should be what I am looking for, but it always returns null, and debugging lead me to the class ANTLRStringStream that has a name field whose value is returned by this method but never set.
Simply create a custom constructor in your parser that takes a string that represents your file.
A demo:
grammar T;
#parser::members {
private String projectName;
public TParser(String fileName) throws java.io.IOException {
super(new CommonTokenStream(new TLexer(new ANTLRFileStream(fileName))));
projectName = fileName;
}
public String getProjectName() {
return projectName;
}
}
parse
: (PROJECT ID {projectName = $ID.text;})? EOF
;
PROJECT : 'project';
ID : ('a'..'z' | 'A'..'Z')+;
SPACE : (' ' | '\t' | '\r' | '\n') {skip();};
which can be tested with:
import org.antlr.runtime.*;
public class Main {
public static void main(String[] args) throws Exception {
// 'empty.txt' is, as the name suggests, an empty file
TParser parser = new TParser("empty.txt");
parser.parse();
System.out.println(parser.getProjectName());
// 'test.txt' contains a single line with the words: 'project JustATest'
parser = new TParser("test.txt");
parser.parse();
System.out.println(parser.getProjectName());
}
}
If you run the Main class, the following will be printed to your console:
empty.txt
JustATest