Javacc grammar not working with optional tokens - javacc

I have a DFM (is a Delphi source file, like JSON, to define form component layouts) parser created with javaCC.
My grammar (.jj file) define this:
private DfmObject dfmObject():
{
DfmObject res = new DfmObject();
DfmProperty prop;
DfmObject obj;
Token tName;
Token tType;
}
{
<OBJECT>
(tName = <IDENTIFIER> { res.setName(tName.image); } <COLON>)?
tType = <IDENTIFIER> { res.setType(tType.image); }
<ENDLINE>
( prop = property() { res.addProperty(prop); } )*
( obj = dfmObject() { res.addChild(obj); } (<ENDLINE>)*)*
<END>
{ return res; }
}
This is for parsing 2 types of object definitions:
object name: Type
end
as so
object Type
end
So, the name : is optional.
But, when I try to parse this second DFM, I always get this error:
Exception in thread "main" eu.kaszkowiak.jdfm.parser.ParseException: Encountered " <ENDLINE> "\r\n"" at line 1, column 12.
Was expecting:
":" ...
What I'm doing wrong?

A solution/workaround is, to make optional the : Type part and switch between the name and type values when the type == null.
See the grammar implementation:
private DfmObject dfmObject():
{
DfmObject res = new DfmObject();
DfmProperty prop;
DfmObject obj;
Token tName;
Token tType;
}
{
(
<OBJECT>
(
tName = <IDENTIFIER> { res.setName(tName.image); }
)
( <COLON> tType = <IDENTIFIER> { res.setType(tType.image); } )?
<ENDLINE>
)
( prop = property() { res.addProperty(prop); } )*
( obj = dfmObject() { res.addChild(obj); } (<ENDLINE>)*)*
<END>
{
if (res.getType() == null) {
res.setType(res.getName());
res.setName(null);
}
return res;
}
}

Related

Javacc error reporting results in “Expansion can be matched by empty string.”

I am trying to add some custom error messages to my javacc parser to hopefully make the error messages more specific and the language problems easier to find and correct.
The first error that I am trying to focus in on is how to detect that the correct number of arguments have been provided to a 'function' call. Rather than the default message, I would like to print out something like "missing argument to function".
My simplified language and my attempt to catch a missing argument error looks something like:
double arg(boolean allowMissing):
{ double v; Token t; }
{
t = <INT> { return Double.parseDouble(t.image); }
| t = <DOUBLE> { return Double.parseDouble(t.image); }
| v = functions() { return v; }
| { if (!allowMissing) throw new ParseException("Missing argument");} // #1 Throw error if missing argument
}
double functions() :
{ double v1, v2, result;
double[] array;
}
{
(<MIN> "(" v1=arg(false) "," v2=arg(false) ")") { return (v1<v2)?v1:v2; }
| (<MAX> "(" v1=arg(false) "," v2=arg(false) ")") { return (v1>v2)?v1:v2; }
| (<POW> "(" v1=arg(false) "," v2=arg(false) ")") { return Math.pow(v1, v2); }
| (<SUM> "(" array=argList() ")") { result=0; for (double v:array) result+=v; return result;}
}
double[] argList() :
{
ArrayList<Double> list = new ArrayList<>();
double v;
}
{
( (v=arg(true) { list.add(v);} ( "," v=arg(false) {list.add(v);} )*)?) { // #2 Expansion can be matched by empty string here
double[] arr = new double[list.size()];
for (int i=0; i<list.size(); i++)
arr[i] = list.get(i);
return arr;
}
}
As you can see functions will recursively resolve their arguments, and this allows function call to be nested.
Here are a few valid expressions that can be parsed in this language:
"min(1,2)",
"max(1,2)",
"max(pow(2,2),2)",
"sum(1,2,3,4,5)",
"sum()"
Here is an invalid expression:
"min()"
This all works well until I tried to check for missing arguments (code location #1). This works fine for the functions that have a fixed number of arguments. The problem is that the sum function (code location #2) is allowed to have zero arguments. I even passed in a flag to not throw an error if missing arguments are allowed. however, javacc gives me an error at location #2 that "Expansion within "(...)?" can be matched by empty string". I understand why I get this error. I have also read the answer for JavaCC custom errors cause "Expansion can be matched by empty string." but it did not help me.
My problem is that I just cannot see how I can have this both ways. I want to throw an error for missing arguments in the functions that have a fixed number of arguments, but I don't want an error in the function that allows no arguments. Is there a way to refactor my parser so that I still use the recursive style, catch missing arguments from the functions that take a fixed arguments, yet allow some functions to have zero arguments?
Or is there a better way to add in custom error messages? I am not really seeing much in the documentation.
Also, any pointers to examples that use more sophisticated error reporting would be greatly appreciated. I am actually using jjtree, but I simplified it down for this example.
Here's what I would do.
Instead of using a boolean argument in function arg, I would use the ? operator:
double arg():
{ double v; Token t; }
{
t = <INT> { return Double.parseDouble(t.image); }
| t = <DOUBLE> { return Double.parseDouble(t.image); }
| v = functions() { return v; }
}
double functions() :
{ double v1=0, v2=0, result;
double[] array;
}
{
(<MIN> "(" (v1=arg())? "," (v2=arg())? ")") { return (v1<v2)?v1:v2; }
| (<MAX> "(" (v1=arg())? "," (v2=arg())? ")") { return (v1>v2)?v1:v2; }
| (<POW> "(" (v1=arg())? "," (v2=arg())? ")") { return Math.pow(v1, v2); }
| (<SUM> "(" array=argList() ")") { result=0; for (double v:array) result+=v; return result;}
}
double[] argList() :
{
List<Double> list = new ArrayList<Double>();
double v;
}
{
( (v=arg() { list.add(v); } | { list.add(0.); } )
( "," (v=arg() { list.add(v); } | { list.add(0.); } ) )*) {
double[] arr = new double[list.size()];
for (int i=0; i<list.size(); i++)
arr[i] = list.get(i);
return arr;
}
}
You could do this
double arg():
{ double v; Token t; }
{
t = <INT> { return Double.parseDouble(t.image); }
| t = <DOUBLE> { return Double.parseDouble(t.image); }
| v = functions() { return v; }
}
double argRequired():
{ double v; }
{
v = arg() { return v ; }
| { if (!allowMissing) throw new ParseException("Missing argument");} // #1 Throw error if missing argument
}
double argOptional( double defaultValue ): // Not needed for this example, but might be useful.
{ double v; }
{
v = arg() { return v ; }
| { return defaultValue ; }
}
double functions() :
{ double v1, v2, result;
double[] array;
}
{
(<MIN> "(" v1=argRequired() "," v2=argRequired() ")") { return (v1<v2)?v1:v2; }
| (<MAX> "(" v1=argRequired() "," v2=argRequired() ")") { return (v1>v2)?v1:v2; }
| (<POW> "(" v1=argRequired() "," v2=argRequired() ")") { return Math.pow(v1, v2); }
| (<SUM> "(" array=argList() ")") { result=0; for (double v:array) result+=v; return result;}
}
double[] argList( ) :
{
ArrayList<Double> list = new ArrayList<>();
double v;
}
{
( v=arg() { list.add(v);}
( "," v=argRequired() {list.add(v);}
)*
)?
{
double[] arr = new double[list.size()];
for (int i=0; i<list.size(); i++)
arr[i] = list.get(i);
return arr;
}
}

Ceylon metamodel

I am studying Ceylon and have question about it metamodel. I want to create some create some base class 'DataContainer' which allow to instantiate immutable classes with build-in equals-hash implementation:
e.g. Identifier(125, "ab") == Identifier(125, "ab")
So base class should collect all shared non-variable values and use this information in 'hash' an 'equals' methods.
I have wrote this code:
shared abstract class DataContainer(ClassDeclaration declaration) {
value members = {
for (i in declaration.memberDeclarations<ValueDeclaration>())
if (!i.variable, i.name != "hash", i.name != "string") i
};
variable Integer? hashCode = null;
shared actual Boolean equals(Object that) {
if (is DataContainer that) {
for (item in members) {
value thisMember = item.memberGet(this);
value thatMember = item.memberGet(that);
if (exists thisMember, exists thatMember) {
if (thisMember != thatMember) { return false; }
} else if (thisMember exists != thatMember exists) { return false; }
}
return true;
}
return false;
}
shared actual Integer hash => hashCode else (hashCode = calculateHash());
Integer calculateHash() {
variable value result = 0;
for(i in members) {
if (exists member = i.memberGet(this)) {
result = result.xor(member.hash);
}
}
return result;
}
}
class Identifier(shared Integer? id, shared String? name) extends DataContainer(`class`) {}
The Identifier class is the client of DataContainer. I like this solution in whole but I have to pass 'class' into the super class constructor because if I use 'class' inside DataContainer it doesn't see any members of subclass.
How can I obtain actual list of extended class's members in base class methods?
Something like 'this' doesn't work...
I found solution thanks to guys from Ceylon community. The function classDeclaration(this) from ceylon.language.meta should be used instead of 'class'.
This is the final code:
shared abstract class DataContainer() {
variable Integer? _hash = null;
variable ValueDeclaration[]? _members = null;
shared actual Boolean equals(Object that) {
if (is DataContainer that) {
for (i in members) {
value thisMember = i.memberGet(this);
value thatMember = i.memberGet(that);
if (exists thisMember, exists thatMember) {
if (thisMember != thatMember) { return false; }
} else if (thisMember exists != thatMember exists) { return false; }
}
return true;
}
return false;
}
shared actual Integer hash => _hash else (_hash = calculateHash());
ValueDeclaration[] members => _members else (_members = [
for (i in classDeclaration(this).memberDeclarations<ValueDeclaration>())
if (!i.variable, i.name != "string", i.name != "hash") i
]);
Integer calculateHash() {
variable Integer result = 0;
for (i in members) {
if (exists member = i.memberGet(this)) {
result = result.xor(member.hash);
}
}
return result;
}
}
class Identifier(shared Integer? number, shared String? name) extends DataContainer() {}
Adding to Alexander's formidable answer, I took the liberty to implement a string function as well so you can just print(myObj) and it renders nicely:
shared default actual String string {
value s = StringBuilder();
s.append(type(this).declaration.name).append(" { ");
for (i in members) {
value m = i.memberGet(this);
s.append(i.name).append(":");
s.append(if(exists m) then m.string else "<null>");
}
return s.append(" }").string;
}

JavaCC simple example not working

I am trying javacc for the first time with a simple naive example which is not working. My BNF is as follows:
<exp>:= <num>"+"<num>
<num>:= <digit> | <digit><num>
<digit>:= [0-9]
Based on this BNF, I am writing the SimpleAdd.jj as follows:
options
{
}
PARSER_BEGIN(SimpleAdd)
public class SimpleAdd
{
}
PARSER_END(SimpleAdd)
SKIP :
{
" "
| "\r"
| "\t"
| "\n"
}
TOKEN:
{
< NUMBER: (["0"-"9"])+ >
}
int expr():
{
int leftValue ;
int rightValue ;
}
{
leftValue = num()
"+"
rightValue = num()
{ return leftValue+rightValue; }
}
int num():
{
Token t;
}
{
t = <NUMBER> { return Integer.parseInt(t.toString()); }
}
using the above file, I am generating the java source classes. My main class is as follows:
public class Main {
public static void main(String [] args) throws ParseException {
SimpleAdd parser = new SimpleAdd(System.in);
int x = parser.expr();
System.out.println(x);
}
}
When I am entering the expression via System.in, I am getting the following error:
11+11^D
Exception in thread "main" SimpleAddTest.ParseException: Encountered "<EOF>" at line 0, column 0.
Was expecting:
<NUMBER> ...
at SimpleAddTest.SimpleAdd.generateParseException(SimpleAdd.java:200)
at SimpleAddTest.SimpleAdd.jj_consume_token(SimpleAdd.java:138)
at SimpleAddTest.SimpleAdd.num(SimpleAdd.java:16)
at SimpleAddTest.SimpleAdd.expr(SimpleAdd.java:7)
at SimpleAddTest.Main.main(Main.java:9)
Any hint to solve the problem ?
Edit Note that this answer answers an earlier version of the question.
When a BNF production uses a nonterminal that returns a result, you can record that result in a variable.
First declare the variables in the declaration part of the BNF production
int expr():
{
int leftValue ;
int rightValue ;
}
{
Second, in the main body of the production, record the results in the variables.
leftValue = num()
"+"
rightValue = num()
Finally, use the values of those variables to compute the result of this production.
{ return leftValue+rightValue; }
}

SQLite SQLITE_MISUSE error in Swift 3

I´m trying to create a function that injects queries to SQLite in Swift 3 but I´m having problems with the escaping chars
class SQLiteQueryManager {
static func insertNewStore(StoreId: Int64, Name: String, Address: String) -> Bool {
let vCommand = "INSERT INTO Store (Id, StoreId, Name, Address) VALUES (\(SQLiteConnectionManager.nextID("Store")),\(StoreId),'\(Name)','\(Address)')"
return SQLiteConnectionManager.insertDatabase(vCommand)
}
}
The problem ins that when I´m trying to execute SQLiteConnectionManager.insertDatabase function the string that I´m sending to SQLite looks like this:
INSERT INTO Store (Id, StoreId, Name, Address) VALUES (1,1,\'Tienda
1\',\'Dirección 1\')
And SQLite is rejecting the query.
I have tried .replacingOccurrences(of: "\", with: "") but it dose not work.
I have tested in DB Browser the query and works
INSERT INTO Store (Id, StoreId, Name, Address) VALUES (1,1,'Tienda
1','Dirección 1')
How can I remove the \??
My SQLite function is this:
static func insertDatabase(_ pCommand: String) -> Bool
{
var vInsertStatement: OpaquePointer? = nil
let vDB: OpaquePointer = SQLiteConnectionManager.openDatabase()
var vReturn: Bool
if sqlite3_prepare_v2(vDB, pCommand.replacingOccurrences(of: "\\", with: ""), -1, &vInsertStatement, nil) == SQLITE_OK {
if sqlite3_step(vInsertStatement) == SQLITE_DONE {
print("insertDatabase() correct with statement \(pCommand)")
vReturn = true
} else {
print("insertDatabase() fail with statement \(pCommand)")
vReturn = false
}
} else {
print("insertDatabase() pCommand could not be prepared")
vReturn = false
}
sqlite3_finalize(vInsertStatement)
sqlite3_close(vDB)
return vReturn
}
The Open function return ok so my guess is the escaping char or something like that.
This is the print output of the function:
insertDatabase() fail with statement INSERT INTO Store (Id, StoreId,
Name, Address) VALUES (1,1,'Tienda 1','Dirección 1')
UPDATE 1:
sqlite3_step(vInsertStatement) is returning SQLITE_MISUSE but I can't find the mistake, the DB is in the bundle and the open() statement work and a select I'm doing works, what can be wrong?
UPDATE 2:
This is how I open de DB and returns OK:
private static func openDatabase() -> OpaquePointer {
var vDB: OpaquePointer? = nil
mDBURL = Bundle.main.url(forResource: "ARDB", withExtension: "db")
if let vDBURL = mDBURL{
if sqlite3_open(vDBURL.absoluteString, &vDB) == SQLITE_OK {
print("The database is open.")
} else {
print("Unable to open database in method openDatabase().")
}
return vDB!
} else {
return vDB!
}
}
Then I run this to get the last Id and works:
static func nextID(_ pTableName: String!) -> Int
{
var vGetIdStatement: OpaquePointer? = nil
let vDB: OpaquePointer = SQLiteConnectionManager.openDatabase()
let vCommand = String(format: "SELECT Id FROM %# ORDER BY Id DESC LIMIT 1", pTableName)
var vResult: Int32? = 0
if sqlite3_prepare_v2(vDB, vCommand, -1, &vGetIdStatement, nil) == SQLITE_OK {
if sqlite3_step(vGetIdStatement) == SQLITE_ROW {
vResult = sqlite3_column_int(vGetIdStatement, 0)
print("nextID() correct with statement \(vCommand)")
} else {
print("nextID() fail with statement \(vCommand)")
}
} else {
print("nextID() statement could not be prepared")
}
sqlite3_finalize(vGetIdStatement)
sqlite3_close(vDB)
var id: Int = 1
if (vResult != nil)
{
id = Int(vResult!) + 1
}
return id
}
I have change my insert function to this with or without cString statement:
static func insertDatabase(_ pCommand: String) -> Bool
{
var vInsertStatement: OpaquePointer? = nil
let vDB: OpaquePointer = SQLiteConnectionManager.openDatabase()
var vReturn: Bool
if sqlite3_prepare_v2(vDB, pCommand.cString(using: .utf8), -1, &vInsertStatement, nil) == SQLITE_OK {
if sqlite3_step(vInsertStatement) == SQLITE_DONE {
print("insertDatabase() correct with statement \(pCommand)")
vReturn = true
} else {
print("insertDatabase() fail with statement \(pCommand) with error: \(sqlite3_step(vInsertStatement)) : \(sqlite3_errmsg(vDB))")
vReturn = false
}
} else {
print("insertDatabase() \(pCommand) could not be prepared with error: \(sqlite3_prepare_v2(vDB, pCommand.cString(using: .utf8), -1, &vInsertStatement, nil))")
vReturn = false
}
sqlite3_finalize(vInsertStatement)
sqlite3_close(vDB)
return vReturn
}
If I print the sqlite3_errmsg(vDB) in the console I get this that does not help:
▿ Optional> ▿ some : 0x0000000105347e80
- pointerValue : 4382293632
If I print sqlite3_step(vInsertStatement) returns 21 SQLITE_MISUSE
Any help will be appreciated.
Thanks in advance.
I just found the problem, we don´t have permission of writing in the main bundle.
So you have to copy the DB first, here is the code:
private static func copyFromBundle () {
let vBundlePath = Bundle.main.path(forResource: "ARDB", ofType: ".db")
let vDestPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let vFileManager = FileManager.default
let vFullDestPath = URL(fileURLWithPath: vDestPath).appendingPathComponent("ARDB.db")
mDocumentsPath = ""
if vFileManager.fileExists(atPath: vFullDestPath.path){
print("Database file exist don´t copy.")
mDocumentsPath = vFullDestPath.path
} else {
if vFileManager.fileExists(atPath: vBundlePath!) {
do {
try vFileManager.copyItem(atPath: vBundlePath!, toPath: vFullDestPath.path)
print("Database file does´t exist copy it.")
mDocumentsPath = vFullDestPath.path
} catch {
print("copyFromBundle() fail with error: ",error)
}
}
}
}
Thanks for the help.
Happy coding.

Enumerate the properties of an AS3 object that may or may not be dynamic

In order to send a POST request I need to enumerate all properties of a given object. This object may or may not be dynamic. I'm looking for the most elegant solution. This is what I've got so far:
function createURLVariables(params:Object):URLVariables
{
// Workaround: Flash Player performs a GET if no params are passed
params ||= {forcePost: true};
var vars:URLVariables = new URLVariables();
var propertyName:String;
var propertyList:XMLList = describeType(params)..variable;
var propertyListLength:int = propertyList.length();
// A dynamic object won't return properties in this fashion
if (propertyListLength > 0)
{
for (var i:int; i < propertyListLength; i++)
{
propertyName = propertyList[i].#name;
vars[propertyName] = params[propertyName];
}
}
else
{
for (propertyName in params)
vars[propertyName] = params[propertyName];
}
return vars;
}
One potential problem is that this won't return properties for getters (accessors).
I took the following approach in the as3corelib JSON Encoder. You'll have to modify this to suit your needs, but it should give you an idea to work from. Note that there is some recursion in here (the convertToString call, which you might not need:
/**
* Converts an object to it's JSON string equivalent
*
* #param o The object to convert
* #return The JSON string representation of <code>o</code>
*/
private function objectToString( o:Object ):String
{
// create a string to store the object's jsonstring value
var s:String = "";
// determine if o is a class instance or a plain object
var classInfo:XML = describeType( o );
if ( classInfo.#name.toString() == "Object" )
{
// the value of o[key] in the loop below - store this
// as a variable so we don't have to keep looking up o[key]
// when testing for valid values to convert
var value:Object;
// loop over the keys in the object and add their converted
// values to the string
for ( var key:String in o )
{
// assign value to a variable for quick lookup
value = o[key];
// don't add function's to the JSON string
if ( value is Function )
{
// skip this key and try another
continue;
}
// when the length is 0 we're adding the first item so
// no comma is necessary
if ( s.length > 0 ) {
// we've already added an item, so add the comma separator
s += ","
}
s += escapeString( key ) + ":" + convertToString( value );
}
}
else // o is a class instance
{
// Loop over all of the variables and accessors in the class and
// serialize them along with their values.
for each ( var v:XML in classInfo..*.(
name() == "variable"
||
(
name() == "accessor"
// Issue #116 - Make sure accessors are readable
&& attribute( "access" ).charAt( 0 ) == "r" )
) )
{
// Issue #110 - If [Transient] metadata exists, then we should skip
if ( v.metadata && v.metadata.( #name == "Transient" ).length() > 0 )
{
continue;
}
// When the length is 0 we're adding the first item so
// no comma is necessary
if ( s.length > 0 ) {
// We've already added an item, so add the comma separator
s += ","
}
s += escapeString( v.#name.toString() ) + ":"
+ convertToString( o[ v.#name ] );
}
}
return "{" + s + "}";
}

Resources