ANTLR4 Order in Recursive Rule - antlr4

I want to describe DELPHI's BEGIN/END blocs.
I use the following source code as input :
begin
ModalResult := mrCancel;
FGradient.Free;
if FMemorizeDimensions then
begin
if ERROR_SUCCESS = RegCreateKey(HKEY_CURRENT_USER,
PChar(gcsREG_KEYBASE),
hkResult) then
try
dwValueType := REG_DWORD;
dwValue := Ord(WindowState);
dwValueSize := SizeOf(dwValue);
finally
RegCloseKey(hkResult);
end;
end;
inherited;
end;
Here is a part of my grammar :
bloc
:BEGIN
statement
|conditionalBloc
|bloc
END SEMI
;
I have no problem generating parser, lexer and so on ...
But when i try to use this other grammar (switching 'conditionalBloc' and 'bloc'):
bloc
:BEGIN
statement
|bloc
|conditionalBloc
END SEMI
;
I am getting this exception :
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.
java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAcces
sorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.antlr.v4.parse.GrammarTreeVisitor.visit(GrammarTreeVisitor.java:2
07)
at org.antlr.v4.parse.GrammarTreeVisitor.visitGrammar(GrammarTreeVisitor
.java:201)
at org.antlr.v4.semantics.SymbolCollector.process(SymbolCollector.java:7
6)
at org.antlr.v4.semantics.SemanticPipeline.process(SemanticPipeline.java
:103)
at org.antlr.v4.Tool.processNonCombinedGrammar(Tool.java:397)
at org.antlr.v4.Tool.process(Tool.java:384)
at org.antlr.v4.Tool.processGrammarsOnCommandLine(Tool.java:343)
at org.antlr.v4.Tool.main(Tool.java:190)
Caused by: java.lang.ArrayIndexOutOfBoundsException: 2
at org.antlr.v4.semantics.SymbolCollector.discoverOuterAlt(SymbolCollect
or.java:111)
at org.antlr.v4.parse.GrammarTreeVisitor.outerAlternative(GrammarTreeVis
itor.java:2285)
at org.antlr.v4.parse.GrammarTreeVisitor.ruleBlock(GrammarTreeVisitor.ja
va:2198)
at org.antlr.v4.parse.GrammarTreeVisitor.rule(GrammarTreeVisitor.java:16
64)
at org.antlr.v4.parse.GrammarTreeVisitor.rules(GrammarTreeVisitor.java:1
242)
at org.antlr.v4.parse.GrammarTreeVisitor.grammarSpec(GrammarTreeVisitor.
java:481)
... 12 more
Exception in thread "main" java.lang.NullPointerException
at org.antlr.v4.tool.Rule.getAltLabels(Rule.java:212)
at org.antlr.v4.tool.Rule.hasAltSpecificContexts(Rule.java:200)
at org.antlr.v4.semantics.SymbolChecks.checkForLabelConflicts(SymbolChec
ks.java:151)
at org.antlr.v4.semantics.SymbolChecks.process(SymbolChecks.java:99)
at org.antlr.v4.semantics.SemanticPipeline.process(SemanticPipeline.java
:107)
at org.antlr.v4.Tool.processNonCombinedGrammar(Tool.java:397)
at org.antlr.v4.Tool.process(Tool.java:384)
at org.antlr.v4.Tool.processGrammarsOnCommandLine(Tool.java:343)
at org.antlr.v4.Tool.main(Tool.java:190)
Can someone help me understand this situation please ?

The following is a reformatted copy of your bloc rule using the ANTLR 4 order of operations:
bloc
: BEGIN statement
| bloc
| conditionalBloc END SEMI
;
I believe this is what you meant instead:
bloc
: BEGIN
( statement
| bloc
| conditionalBloc
)
END SEMI
;
The exception is a failure to handle the recursive bloc alternative which does not contain anything after the left-recursive reference. You can open an issue on the project issue tracker here:
https://github.com/antlr/antlr4/issues

Related

Array variables and dynamic access in [Code] section

My installer has Components which come associated with downloadable files. These things are changing from build to build, so I'm using #insert to create the [Components] section as well as the appropriate entries in the [Files] section.
Some of these components rely on common downloadable files.
To now include the correct urls in the downloads page, I'm currently defining array variables that are named like the component and have as values the names of the required downloadable files, for example:
#dim myfeature[2] {"01aed27862e2087bd117e9b677a8685aebb0be09744723b4a948ba78d6011bac", "677756ac5969f814fd01ae677dbb832ab2e642513fea44ea0a529430d5ec1fdc"}
In the code for the download page I'm checking which components where selected via WizardSelectedComponents() and after converting the string to an array of strings, I'm trying to get to the previously defined variable and that is where I'm failing:
function GetDownloads(): Array of String;
var
Downloads: Array of String;
SelectedComponents: String;
SelectedArray: Array of String;
begin
SelectedComponents := WizardSelectedComponents(False);
// a custom procedure to parse the comma seperated string
SelectedArray := ParseArray(SelectedComponents, SelectedArray);
// trying to get to the constant array now this works:
MsgBox(ExpandConstant('{#myfeature[0]}'), mbInformation, MB_OK);
// same but trying to use the selected component value returns this as a literal
// '+SelectedArray[0]+' instead the expanded value
MsgBox(ExpandConstant('{#' + SelectedArray[0] + '[0]}'), mbInformation, MB_OK);
end;
So I understand something is up with the # mark but I could not find a way to solve this properly.
Thank you!
Markus
ExpandConstant expands Inno Setup "constants", not preprocessor values. See also Evaluate preprocessor macro on run time in Inno Setup Pascal Script.
You cannot access elements of a preprocessor compile-time array using run-time indexes.
If you know C/C++, it's like if you were trying to do:
#define VALUE1 123
#define VALUE2 456
int index = 1;
int value = VALUE ## index
I'm not really sure I completely understand what are you doing. But it seems that you need to create an array on compile time from various sources and use it on runtime.
There are several approaches that can be used for that. But you definitely need runtime array initialized on run time. But the code that initializes it can be generated on compile time.
An example of the approach follows (and some links to other approaches are at the end).
At the beginning of your script, define these support functions:
[Code]
var
FeatureDownloads: TStrings;
function AddFeature(
Feature: Integer; CommaSeparatedListOfDownloads: string): Boolean;
begin
if not Assigned(FeatureDownloads) then
begin
FeatureDownloads := TStringList.Create();
end;
while FeatureDownloads.Count <= Feature do
FeatureDownloads.Add('');
if FeatureDownloads[Feature] <> '' then
RaiseException('Downloads for feature already defined');
FeatureDownloads[Feature] := CommaSeparatedListOfDownloads;
Result := True;
end;
#define AddFeature(Feature, CommaSeparatedListOfDownloads) \
"<event('InitializeSetup')>" + NewLine + \
"function InitializeSetupFeature" + Str(Feature) + "(): Boolean;" + NewLine + \
"begin" + NewLine + \
" Result := AddFeature(" + Str(Feature) + ", '" + CommaSeparatedListOfDownloads + "');" + NewLine + \
"end;"
In your components include files, do:
#emit AddFeature(2, "01aed27862e2087bd117e9b677a8685aebb0be09744723b4a948ba78d6011bac,677756ac5969f814fd01ae677dbb832ab2e642513fea44ea0a529430d5ec1fdc")
If you add:
#expr SaveToFile(AddBackslash(SourcePath) + "Preprocessed.iss")
to the end of your main script, you will see in the Preprocessed.iss generated by the preprocessor/compiler that the #emit directive expands to:
<event('InitializeSetup')>
function InitializeSetupFeature2(): Boolean;
begin
Result := AddFeature(2, '01aed27862e2087bd117e9b677a8685aebb0be09744723b4a948ba78d6011bac,677756ac5969f814fd01ae677dbb832ab2e642513fea44ea0a529430d5ec1fdc');
end;
Now you have FeatureDownloads Pascal Script runtime variable that you can access using FeatureDownloads[SelectedArray[0]] to get comma-separated string, which you can parse to the individual downloads.
This can be optimimized/improved a lot, but I do not know/understand the extent of your task. But I believe that once you grasp the concept (it might be difficult at the beginning), you will be able to do it yourself.
Another similar questions:
Evaluate a collection of data from preprocessor on run time in Inno Setup Pascal Script (simple example that be easier to grasp initially)
Scripting capabilities in the Registry section (slightly different approach from times event attributes were not available yet – and that's YOUR question)

End_Error help, Using unbounded Strings in Ada

Can someone please tell me why I am getting an End_Error exception, I do not see how I am getting past the end of the file if I have a loop that opts out before it reaches this point. If there is an easy fix, I'd love to hear it, I've been stuck for a while and unbounded strings are not really my forte.
with Ada.Text_IO;
use Ada.Text_IO;
with Ada.Strings.Unbounded;
use Ada.Strings.Unbounded;
with Ada.Strings.Unbounded.Text_IO;
use Ada.Strings.UNbounded.Text_IO;
procedure checker is
InWord : Unbounded_String;
dictionary : File_Type;
count : Integer;
begin
Ada.Text_IO.Open
(File => dictionary, Mode => In_File, Name => "dictionary.txt");
loop
exit when End_of_File;
InWord := Get_Line(File => dictionary);
Put(InWord);
New_Line;
end loop;
end checker;
raised ADA.IO_EXCEPTIONS.END_ERROR : a-textio.adb:690
You have
exit when End_of_File;
but since you’re looking for EOF on the input file it should be
exit when End_Of_File(File => Dictionary);
I’m not sure why you see the effect you do - when I tried it, nothing happened until I typed a couple of RETs and then I got the End_Error exception.
As you see, it’s nothing to do with strings, unbounded or otherwise!
As stated in the ARM, End_Of_File without parameters refers to the current input file.
In your case, it just refers to the standard input and not to your file.
Considering Simon's answer and the behaviour you got, I think you use your program with a redirection of the standard input (something like "cat myFile.txt | checker"). Am I right ?

Javascript click and problems with string

I need the string recognizes the comma
var
script2 : string;
begin
script2 := 'javascript:doSubmit(13,'S', 'S');'
ExecuteScript(webbrowser1.Document as IHTMLDocument2, script2, 'javascript');
end;
however I am getting the following error message
The error message is due to the embedded apostrophes (single quotes) in your string. From the help, "Two sequential apostrophes in a quoted string denote a single character", so your code should be:
script2 := 'javascript:doSubmit(13, ''S'', ''S'')';

String concatenation within an exception

In my trigger procedures I use RAISE EXCEPTION for messages. I have no problem with simple messages, but if I want to give the user some more complex feedback, I face a problem: the concatenation operator doesn't work within RAISE EXCEPTION statement.
First, I tried this:
CREATE OR REPLACE FUNCTION hlidej_datum_kon() RETURNS trigger AS $$
DECLARE
od date;
BEGIN
SELECT a.datum_od FROM akce AS a WHERE a.kod_akce = (
SELECT b.kod_akce FROM sj AS b WHERE b.kod_sj = NEW.kod_sj
) INTO od;
IF NEW.datum < od THEN
RAISE EXCEPTION 'Kontext nemohl být odkryt před začátkem akce ('||TO_CHAR(od)||')!'
ROLLBACK;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Didn't work. So I tried to put the whole text to a text variable, but I didn't find how to put the variable's contents to the exception statement so that it would be printed as a message.
My question is: how to print a message containing variables in a PostgreSQL trigger function?
Just for sake of completeness, here is my trigger:
CREATE TRIGGER hlidej_datum_kon
AFTER INSERT OR UPDATE ON kontext
FOR EACH ROW
EXECUTE PROCEDURE hlidej_datum_kon();
END;
You not need to use concat. You can use wildcards instead:
RAISE EXCEPTION 'Kontext nemohl být odkryt před začátkem akce (%)!', od;
There are two bugs
first parameter of RAISE statement is format string - this string should be constant. It can contains a substitution symbols '%' and values for these symbols are places as others parameters of RAISE statement.
There should not be used ROLLBACK statement. RAISE EXCEPTION throws exceptions and ROLLBACK statement is newer executed. You cannot control transaction explicitly in PL/pgSQL - so you cannot use ROLLBACK or COMMIT statement in plpgsql ever.You can use a exception trapping
BEGIN
RAISE EXCEPTION 'blabla';
EXCEPTION WHEN some_exception_identif_see_list_of_exception_in_doc THEN
.. do some or do nothing
END;

How to change defaultdirname parameter just before Install in Inno Setup?

I want to change defaultdirname parameter in ssInstall part. How can I do that? Is there a function for setting [Setup] parameters.
The following global objects are available :
MainForm of type TMainForm, WizardForm of type TWizardForm and UninstallProgressForm of type TUninstallProgressForm and one special constant: crHand of type TControl.Cursor.
If you want to set the default directory in the wizard, just access to it's componants like you would in normal delphi code.
For example, set the directory to a custom value:
WizardForm.DirEdit.Text := 'c:\test';
to read that value you can use the WizardDirValue function.
I say 'just access'... but it took me an hour to figure out ;)
There seems to be no way to change a script constant via scripting.
I think your best bet is to modify the target directory for each entry in the [Files] section, e.g.
[Files]
Source: "MYPROG.EXE"; DestDir: "{code:NewTargetDir}"
and derive your new installation directory like this:
[Code]
function NewTargetDir(Param: String): String;
begin
Result := ExpandConstant('{app}') + '\MySubDir';
end;
Since the NewTargetDir function will be called just before the file is actually copied, this should work.
However, I think you should reconsider your approach. First asking the user to specify a directory to installinto, and then actually installing into a different directory, which seems to be your intent, is the wrong way, IMO. Do you really have a compelling reason to install into another directory than the one specified by the user? Besides, the result of my example code could just as well be achieved by specifying
[Files]
Source: "MYPROG.EXE"; DestDir: "{app}\MySubDir"
without any scripting needed. When in doubt, go for the simpler solution.
I have a similar situation, where the setup app is receiving the install path from the command line.
I'm using the solution proposed by Jonx:
WizardForm.DirEdit.Text := 'c:\test';
Example:
function CompareParameter(param, expected: String): Boolean;
begin
Result := False;
if Length(param) >= Length(expected) then
begin
if CompareText(Copy(param, 1, Length(expected)), expected) = 0 then
begin
Result := True;
end;
end;
end;
function GetParameter(expectedParam: String): String;
var
i : LongInt;
begin
Result := '';
for i := 0 to ParamCount() do
begin
if CompareParameter(ParamStr(i), '/' + expectedParam + '=') then
begin
Result := Copy(ParamStr(i), Length(expectedParam) + 3, Length(ParamStr(i)));
break;
end;
end;
end;
procedure InitializeWizard();
var
newInstallFolder: String;
begin
newInstallFolder := GetParameter('INSTALL_FOLDER');
if Length(newInstallFolder) > 2 then
begin
if Copy(newInstallFolder, 1, 1) = '"' then
newInstallFolder := Copy(newInstallFolder, 2, Length(newInstallFolder) - 2)
if Length(newInstallFolder) > 1 then
WizardForm.DirEdit.Text := newInstallFolder;
end;
end;
The setup app is being started from another setup, in silent mode. It worked OK for me.

Resources