I am having some difficulty in understanding how a String[] can be represented in a Guvnor rule. How can an array of strings be passed to a Java method that uses String[] as an argument from a rule in Guvnor?
I keep getting mismatched input errors, Error Code 102 when I attempt to validate the rule in Guvnor.
Any pointers/tips welcome
In the following rule, comm is a global object with a function sendMail with the function signature (String[] recipientlist, String alertType, String message)
rule "list-email"
dialect "java"
when
$result : Grade( subject == "Math" , $marks : mark >= 99.0 )
$emailList : "{xyz#abc.com, fgh#def.com}"
then
comm.sendMail($emailList, "High Grade Alert", "Scored: " + " Marks:" + Double.toString($marks));
It's not a good idea to try and introduce a String[] on the LHS - you aren't matching with it, and I doubt the syntax is correct. Use this - on the RHS it's Java:
rule "list-email"
dialect "java"
when
$result : Grade( subject == "Math" , $marks : mark >= 99.0 )
then
String[] addrs = new String[]{"xyz#abc.com", "fgh#def.com"};
comm.sendMail(addrs, "High Grade Alert", "Scored: " + " Marks:" + $marks );
end
Related
I have the following ANTLR (version 3) grammar:
grammar GRM;
options
{
language = C;
output = AST;
}
create_statement : CREATE_KEYWORD SPACE_KEYWORD FILE_KEYWORD SPACE_KEYWORD value -> ^(value);
value : NUMBER | STRING;
CREATE_KEYWORD : 'CREATE';
FILE_KEYWORD : 'FILE';
SPACE_KEYWORD : ' ';
NUMBER : DIGIT+;
STRING : (LETTER | DIGIT)+;
fragment DIGIT : '0'..'9';
fragment LETTER : 'a'..'z' | 'A'..'Z';
With this grammar, I am able to successfully parse strings like CREATE FILE dump or CREATE FILE output. However, when I try to parse a string like CREATE FILE file it doesn't work. ANTLR matches the text file (in the string) with lexer rule FILE_KEYWORD which is not the match that I was expecting. I was expecting it to match with lexer rule STRING.
How can I force ANTLR to do this?
Your problem is a variant on classic contextual keyword vs identifier issue, it seems.
Either "value" should be a lexer rule, not a parser rule, it's too late otherwise, or you should reorder the rules (or both).
Hence using VALUE = NUMBER | STRING (lexer rule) instead of lower case value (grammar rule) will help. The order of the lexer rules are also important, usually definition of ID ("VALUE" in your code) comes after keyword definitions.
See also : 'IDENTIFIER' rule also consumes keyword in ANTLR Lexer grammar
grammar GMR;
options
{
language = C;
output = AST;
}
create_statement : CREATE_KEYWORD SPACE_KEYWORD FILE_KEYWORD SPACE_KEYWORD value -> ^(value);
CREATE_KEYWORD : 'CREATE';
FILE_KEYWORD : 'FILE';
value : (LETTER | DIGIT) + | FILE_KEYWORD | CREATE_KEYWORD ;
SPACE_KEYWORD : ' ';
this works for me in ANTLRworks for input CREATE FILE file and for input CREATE FILE FILE if needed.
I have 2 classes: one with 2 properties and one with an array. I want to make an array of objects of the first class.
The example compiles, but gives a wrong answer. Why?
[indent=4]
class data
prop first_name : string = " "
prop last_name : string = " "
class Arr : Object
person : data
dataset : array of data[]
init
person = new data()
dataset = new array of data[3]
def date_input()
print "\n data input \n"
person.first_name = "Egon"
person.last_name = "Meier"
dataset[0] = person
print dataset[0].first_name + " " + dataset[0].last_name
person.first_name = "John"
person.last_name = "Schneider"
dataset[1] = person
print dataset[1].first_name + " " + dataset[1].last_name
person.first_name = "Erwin"
person.last_name = "Müller"
dataset[2] = person
print dataset[2].first_name + " " + dataset[2].last_name
def date_output()
print "\n data output \n"
for i : int = 0 to 2
print dataset[i].first_name + " " + dataset[i].last_name
init
Intl.setlocale()
var a = new Arr()
a.date_input()
a.date_output()
The fundamental problem is you are referring to the same person three times, but changing their name each time. In Genie there are both value types and reference types. Value types are simpler and automatically copied on assignment. For example:
[indent=4]
init
a:int = 2
b:int = a
b = 3
print( "a is still %i", a )
A reference type has the advantage that it can be easily copied, Genie simply keeps a count of the references made. So to copy a reference type the reference count is increased by one, but this means that changes to the underlying object will affect all variables that refer to it:
[indent=4]
init
a:ReferenceTypeExample = new ReferenceTypeExample()
a.field = 2
b:ReferenceTypeExample = a
b.field = 3
print( "a.field is not 2, but %i", a.field )
class ReferenceTypeExample
field:int = 0
In the working example below I have made Person a value object by using readonly properties:
[indent=4]
init
Intl.setlocale()
var group = new Group()
print "\n data input \n"
try
group.add_person( new Person( "Egon", "Meier" ))
group.add_person( new Person( "John", "Schneider" ))
group.add_person( new Person( "Erwin", "Müller" ))
except err:GroupError
print( err.message )
print( #"$group" )
class Person
prop readonly first_name:string = ""
prop readonly last_name:string = ""
construct( first:string, last:string )
_first_name = first
_last_name = last
exception GroupError
GROUP_FULL
class Group
_people_count:int = -1
_group:new array of Person
_max_size:int = 2
construct()
_group = new array of Person[ _max_size ]
def add_person( person:Person ) raises GroupError
_people_count ++
if _people_count > _max_size
_people_count = _max_size
raise new GroupError.GROUP_FULL(
"Group is full. Maximum is %i members",
_max_size + 1
)
_group[ _people_count ] = person
print( " " + _group[ _people_count ].first_name +
" " + _group[ _people_count ].last_name
)
def to_string():string
result:string = "\n data output \n\n"
if _people_count < 0
result += " empty group"
return result
for i:int = 0 to _people_count
result += " " + _group[i].first_name + \
" " + _group[i].last_name + "\n"
return result
Some details about the code:
By changing the properties to be readonly an error will be given when you compile the program if it tries to change a detail of Person. In your example, if you change the properties of data to be readonly then the Vala compiler will warn you are trying to re-write the current object
Person has its data values set in the constructor and then any attempt after that to change them is an error
For a simple property Genie generates an automatic backing field that starts with an underscore. For example in Person the property first_name has the backing field _first_name and it is that field that is set in the constructor
Instead of including the people's names in the method itself, a method called add_person() is used that takes a Person as a parameter. This separates the concrete data from the abstract class
Checks have been added to the add_person() method to make sure the array doesn't go beyond its limits. If more people are added to the group than are allowed then an exception is raised
The add_person() calls in the init block create the Person as part of the method call. This means a reference to a Person object is not kept in the init block
I am writing my first custom matcher in rspec. I would like to provide a failure message with a break down on why the comparison has failed. Effectively I would like to output the differences between the expected and the actual object. I effectively just need to do this with 2 arrays on the object. I have done some research and am trying to use =~ as described here. It mentions it has an informative failure message but I am struggling to access the failure message. I would effectively just like to return the combined failure message for two separate arrays to give an informative reason for the matcher returning false.
My attempt is as follows
RSpec::Matchers.define :have_same_state_as_measure_table do |expected_measure_table , max_delta = 1e-06|
match do |actual_measure_table|
actual_measure_table.equivalence(expected_measure_table, max_delta)
end
description do
"checks if measure has same state as expected measure table within a given number of precision"
end
# Optional method description
description do
"checks if measure has same state as expected measure table, within a given level of precision"
end
# Optional failure messages
failure_message do |actual_measure_table|
mismatch_string = ""
mismatch_string += (actual_measure_table.columns =~ expected_measure_table.columns || "")
mismatch_string += (actual_measure_table.names =~ expected_measure_table.names || "")
"Measure tables missmatch as follows %s" % (mismatch_string.to_s)
end
failure_message_when_negated do |actual_measure_table|
"expected friend not to be in zipcode"
end
end
My final matcher was as follows :
class CompareMeasureTables
attr_reader :expected_measure_table, :max_delta, :actual_measure_table
def initialize(expected_measure_table, max_delta=1e-06)
#expected_measure_table = expected_measure_table
#max_delta = max_delta
end
def description
"Checks if measure has same state as expected measure table, within a given level of precision"
end
def matches?(actual_measure_table)
#actual_measure_table = actual_measure_table
actual_measure_table.equivalence(expected_measure_table, max_delta, false)
end
def failure_message
#mismatch_description = ""
if actual_measure_table.columns.sort != expected_measure_table.columns.sort
#mismatch_description += "\nColumns mismatch \nExpected =" + expected_measure_table.columns.inspect
#mismatch_description += "\nActual =" + actual_measure_table.columns.inspect
end
if (#mismatch_description == "")
#mismatch_description += "\nData mismatch \nExpected =" + (expected_measure_table.records - actual_measure_table.records).inspect
#mismatch_description += "\nActual =" + (actual_measure_table.records - expected_measure_table.records).inspect
#mismatch_description += "\nTolerance set at #{#max_delta}"
end
"Measure tables mismatch as follows %s" % (#mismatch_description)
end
end
I have the following rule:
rule "AddSource"
when
$model : MFMModel ()
Node( type == "source", funName : name )
$ffs : Structure( ffsName : name )
WholePart( structure == ffsName, ffunction == funName )
eval (Test.checkExsit($model,$ffs))
then
System.out.println( ffsName + ":" + funName);
Source s = new Source( funName );
insert (s);
$ffs.addToStructure( s );
System.out.println(Test.checkExsit($model,$ffs));
end
The rule fires when I delete the eval function in the when part, and the printout says Test.checkExsit($model,$ffs) return true.
But with the eval function, the rule will never fire.
Anyone knows what the problem is?
I'm trying to use the ANTLR3 C Target to make sense of an AST, but am running into some difficulties.
I have a simple SQL-like grammar file:
grammar sql;
options
{
language = C;
output=AST;
ASTLabelType=pANTLR3_BASE_TREE;
}
sql : VERB fields;
fields : FIELD (',' FIELD)*;
VERB : 'SELECT' | 'UPDATE' | 'INSERT';
FIELD : CHAR+;
fragment
CHAR : 'a'..'z';
and this works as expected within ANTLRWorks.
In my C code I have:
const char pInput[] = "SELECT one,two,three";
pANTLR3_INPUT_STREAM pNewStrm = antlr3NewAsciiStringInPlaceStream((pANTLR3_UINT8) pInput,sizeof(pInput),NULL);
psqlLexer lex = sqlLexerNew (pNewStrm);
pANTLR3_COMMON_TOKEN_STREAM tstream = antlr3CommonTokenStreamSourceNew(ANTLR3_SIZE_HINT,
TOKENSOURCE(lex));
psqlParser ps = sqlParserNew( tstream );
sqlParser_sql_return ret = ps->sql(ps);
pANTLR3_BASE_TREE pTree = ret.tree;
cout << "Tree: " << pTree->toStringTree(pTree)->chars << endl;
ParseSubTree(0,pTree);
This outputs a flat tree structure when you use ->getChildCount and ->children->get to recurse through the tree.
void ParseSubTree(int level,pANTLR3_BASE_TREE pTree)
{
ANTLR3_UINT32 childcount = pTree->getChildCount(pTree);
for (int i=0;i<childcount;i++)
{
pANTLR3_BASE_TREE pChild = (pANTLR3_BASE_TREE) pTree->children->get(pTree->children,i);
for (int j=0;j<level;j++)
{
std::cout << " - ";
}
std::cout <<
pChild->getText(pChild)->chars <<
std::endl;
int f=pChild->getChildCount(pChild);
if (f>0)
{
ParseSubTree(level+1,pChild);
}
}
}
Program output:
Tree: SELECT one , two , three
SELECT
one
,
two
,
three
Now, if I alter the grammar file:
sql : VERB ^fields;
.. the call to ParseSubTree only displays the child nodes of fields.
Program output:
Tree: (SELECT one , two , three)
one
,
two
,
three
My question is: why, in the second case, is Antlr just give the child nodes? (in effect missing out the SELECT token)
I'd be very grateful if anybody can give me any pointers for making sense of the tree returned by Antlr.
Useful Information:
AntlrWorks 1.4.2,
Antlr C Target 3.3,
MSVC 10
Placing output=AST; in the options section will not produce an actual AST, it only causes ANTLR to create CommonTree tokens instead of CommonTokens (or, in your case, the equivalent C structs).
If you use output=AST;, the next step is to put tree operators, or rewrite rules inside your parser rules that give shape to your AST.
See this previous Q&A to find out how to create a proper AST.
For example, the following grammar (with rewrite rules):
options {
output=AST;
// ...
}
sql // make VERB the root
: VERB fields -> ^(VERB fields)
;
fields // omit the comma's from the AST
: FIELD (',' FIELD)* -> FIELD+
;
VERB : 'SELECT' | 'UPDATE' | 'INSERT';
FIELD : CHAR+;
SPACE : ' ' {$channel=HIDDEN;};
fragment CHAR : 'a'..'z';
will parse the following input:
UPDATE field, foo , bar
into the following AST:
I think it is important that you realize that the tree you see in Antrlworks is not the AST. The ".tree" in your code is the AST but may look different from what you expect. In order to create the AST, you need to specify the nodes using the ^ symbol in strategic places using rewrite rules.
You can read more here