DSL error when comparing two string variables

Hi,

I have an issue with comparing two string variables. When I execute the code below the logfile states:

RULE[this is a test] dark_start = SUNSET

RULE[this is a test] processed_sun_phase = DAYLIGHT

Script execution of rule with UID ‘datetime-4’ failed: Unknown variable or command ‘==’; line 154, column 8, length 33 in datetime

Does anybody know what is wrong here?

Thanks!

// ===================================================
rule “this is a test”
// ===================================================
when
Item Debug_Define_day_or_night changed to ON
then
val sunphases = newArrayList(
“DAYLIGHT”,
“SUN_SET”
)
val string dark_start = Dark_Sun_Phase.state.toString
logInfo(“datetime.rules”, “RULE[this is a test] dark_start = {}”, dark_start)
var int i = 0;
var string processed_sun_phase = “”
while(i<3) {
processed_sun_phase = sunphases.get(i).toString
if (processed_sun_phase == dark_start) logInfo(“datetime.rules”,“RULE[this is a test] equal”)
i=i+1
}
end

At least there are some don’ts in the rule.
Don’t use Primitives but Objects, i.e.

val String dark_start

instead of

val string dark_start

Don’t use the ; other than for return;

I’m not sure about ArrayList, but it’s possible that you have to set the type:

val List<String> sunphases = newArrayList("DAYLIGHT","SUN_SET")

Maybe better iterate through the list:

sunphases.forEach[s|
    processed_sun_phase = s
   ...
]

Not sure about this either :confused:

The problem is that your String is not capitalized:

rule a
when
  System reached start level 100
then
  val string a = "77t"
  if (a == "77") logError("A", "B")
end

logs

[INFO ] [el.core.internal.ModelRepositoryImpl] - Loading DSL model 'a.rules'
[ERROR] [.handler.AbstractScriptModuleHandler] - Script execution of rule with UID 'a-1' failed: Unknown variable or command '=='; line 6, column 7, length 9 in a

whereas capitalized String works:

rule a
when
  System reached start level 100
then
  val String a = "77t"
  if (a == "77") logError("A", "B") else logError("A", "C")
end

logs:

[INFO ] [el.core.internal.ModelRepositoryImpl] - Loading DSL model 'a.rules'
[INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in DSL model 'a.rules', using it anyway:
Constant condition is always false.
[ERROR] [org.openhab.core.model.script.A     ] - C

I suggest to remove the type after val and var, it is the implicitly correct, unless it is ambiguous:

val sunphases = #['DAYLIGHT”, “SUN_SET”]
val dark_start = Dark_Sun_Phase.state.toString
var processed_sun_phase = “”

above sunphases is ArrayList, to make it of type String[] use:

val String[] sunphases = #['DAYLIGHT”, “SUN_SET”]

Is string comparison with == really supported in Xtext, or does it mean what it means in Java (comparing instances, not string contents)?

Hi,

Indeed the capital String solves the issue. Thanks you both!

Not sure what happens in the background with a string variable that is defined with lower case and why the error message is as given. Would it be possible to put a syntax check on defining string variables with lowercase?

Possible perhaps, but not practical/smart. We can’t “ban” the word string, it’s e.g. a valid variable name. In Java/Xtext, types always start with a capital letter if they aren’t primitives (int, boolean, long, double etc.).

Hi Nadahar,

Ok clear, thanks for clarifying.

In Xbase e1 == e2 calls e1.operator_equals(e2). There are many implementations of the method operator_equals(). It this case is invoked org.eclipse.xtext.xbase.lib.ObjectExtensions::operator_equals():

public static boolean operator_equals(Object a, Object b) {
        return Objects.equals(a, b);
}

The most reasonable thing would be to remove from the examples in the openHAB Documentation the explicit types provided to variables and constants (val and var), when these types are not necessary at runtime, when the types make no difference at runtime. This way users will not do spelling mistakes, when they try to spell the return type. Currently the documentation has examples with val int dimAsInt = dimVal.intValue, so a user logically concludes that it is similar to write val string text = item.state.toString.

As to int vs Integer in variable types, I do not think it makes any difference here. It would make difference, if Xbase was used to generate .java files, but in openHAB Xbase is used to interpret the code. In the past I tried to create variables of types int and Integer, then called on them .class.toString and in both case the class was the same - so even Java primitives, when interpreted by Xbase, are the same as the corresponding object.

To find all potential places, where the explicit variabale type can be removed, one can use git grep -P "va[lr] +[^ =]+ +[a-zA-Z]+ *=", More Xbase style by dilyanpalauzov · Pull Request #2699 · openhab/openhab-docs · GitHub proposes the removal of redundant variable type on one place - from val ReentrantLock lock = new ReentrantLock.

In question of int vs. Integer, there is a difference.

Please try it by yourself:

// test 1
var int myInt = 0
myInt ++

// test 2
var Integer myInt = 0
myInt ++

// test 3
var int myInt = 0
myInt +=1

// test 4
var Integer myInt = 0
myInt +=1

:blush:

rule a
when
  System reached start level 100
then
  var int a = 0
  var int b = 0
  var Integer c = 0
  var Integer d = 0
  a++
  b+=1
  c++
  d+=1
  logError("A", a.toString)
  logError("B", b.toString)
  logError("C", c.toString)
  logError("D", d.toString)
end

prints

[INFO ] [el.core.internal.ModelRepositoryImpl] - Loading DSL model 'a.rules'
[ERROR] [org.openhab.core.model.script.A     ] - 1
[ERROR] [org.openhab.core.model.script.B     ] - 1
[ERROR] [org.openhab.core.model.script.C     ] - 1
[ERROR] [org.openhab.core.model.script.D     ] - 1

In this example I see no difference between variables in Xbase declared int versus variables declared Integer.

I made a change to the logXxx() methods in 5.2.0, so you no longer needs to send a string as the 2nd argument. toString() will automatically be invoked if the object received isn’t a string.

Hm. Changed behavior.
There was an issue where ++ only worked for int while only +=1 worked for Integer.

Traditionally in other languages we’d get an error message saying that string is invalid or something. That would’ve avoid this entire thread since the user would immediately see what the problem is.

Some errors or warnings from DSL Rules and DSL Scripts are just suppressed by openHAB - Restore model validation not to fail on diagnostic errors for rules and scripts by jimtng · Pull Request #5351 · openhab/openhab-core · GitHub . It could be the case here, this is an assumption, that this is a suppressed, not logged, discarded error, which Xbase does fire.

Invalid explicit variable type does not produce proper error in DSL Rule, but correct error message in DSL Scripts, because errors and warnings originating from Xbase are handled differently for DSL Scripts and DSL Rules by ModelRepositoryImpl.validateModel:

Following the changes from Restore model validation not to fail on diagnostic errors for rules and scripts by jimtng · Pull Request #5351 · openhab/openhab-core · GitHub and https://github.com/openhab/openhab-core/pull/5467 currently org.openhab.core.model.core.internal.ModelRepositoryImpl.validateModel() contains:

switch (modelType) {
     case "rules":
         if (d instanceof AbstractValidationDiagnostic vd
             && d.getSeverity() == org.eclipse.emf.common.util.Diagnostic.ERROR
             && ("uid".equals(vd.getIssueCode()) || "time".equals(vd.getIssueCode())))
             errors.add(d.getMessage());
         else
             warnings.add(d.getMessage());
         break;
    case "script":
         warnings.add(d.getMessage());
         break;
    default:
         if (d.getSeverity() == org.eclipse.emf.common.util.Diagnostic.ERROR)
             errors.add(d.getMessage());
          else
             warnings.add(d.getMessage());
}

So errors and warnings from DSL Scripts are converted to warnings, whereas some errors from DSL Rules are kept as errors and others are converted to warnings and later this apparently makes a difference.

Using openHAB 5.3.0 build 5492 this DSL Script

var string s = "abcdf"
if (s == "abc") logError("A", "B") else logError("C", "D")

prints at runtime:

[INFO ] [el.core.internal.ModelRepositoryImpl] - Loading DSL model 'b.script'
[ERROR] [.handler.AbstractScriptModuleHandler] - Script execution of rule with UID 'b.script' failed: var  ___ string s = "abcdf"
if (s == "abc") logError("A", "B") else logError("C", "D")

   string cannot be resolved to a type.; line 1, column 4, length 6

whereas this DSL Rule:

rule a
when
  System reached start level 100
then
  var string s = "abcdf"
  if (s == "abc") logError("A", "B") else logError("C", "D")
end

prints

[INFO ] [el.core.internal.ModelRepositoryImpl] - Loading DSL model 'a.rules'
[ERROR] [.handler.AbstractScriptModuleHandler] - Script execution of rule with UID 'a-1' failed: Unknown variable or command '=='; line 6, column 7, length 10 in a

At Different handling of Xbase errors in DSL Scripts vs DSL Rules produces sometimes strange error messages only for Rules · Issue #5716 · openhab/openhab-core · GitHub I suggested to handle errors and warnings from Xbase in DSL Scripts and DSL Rules in the same way in openHAB-core. This can result printing for var string s = "123" error message, that string cannot be resolved to a type in DSL Rules, as it is already the case for DSL Scripts.

To recap what I remember here, it was like this: Originally, DSL errors were converted to warnings, which caused confusion, and it was changed at some stage. This broke a lot of rules, because the rules are parsed before all valid objects/contexts exist during startup. So, the conversion to warning was reintroduced, because it’s there to allow rules that aren’t valid at one point, but that become valid later, to exist without failing.

When we made the UID validation, we needed those exact errors to still be actual errors, which is why I made this “hack” to allow designating some errors as “true errors”.

Reverting all errors to errors, will reintroduce the problem with rules being parsed before everything is ready.

The real solution to this is to control the startup order in a way that makes sure that those objects/contexts exist in time for the evaluation. Only then can the “error → warning hack” be removed. But, until somebody figures out how to make sure of this, the hack must remain.

The problems, which happened with openHAB 5.1.0 when errors were handled as errors, are described at https://community.openhab.org/t/rules-dsl-in-5-1-is-now-unloading-rules-if-it-has-unreachable-expressions/ :

  • unconditional early return is an error
  • lambdas must spell explicitly parameter types and return type, when these cannot be deduced

Are you saying that the explanation about things not being ready in time is wrong? I didn’t study that myself, I only read it, and it seemed plausible.

If the errors that caused the reversals are indeed “real errors”, I think it’s time to prepare to change it back. Perhaps actually logging them as errors for a while, combined with “this will cause the rule to fail in a future release” might be the correct way to go.