@@ -10,6 +10,8 @@ foam.CLASS({
1010 extends : 'foam.core.test.Test' ,
1111
1212 javaImports : [
13+ 'foam.dao.ArraySink' ,
14+ 'foam.dao.DAO' ,
1315 'foam.lib.parse.PStream' ,
1416 'foam.lib.parse.ParserContext' ,
1517 'foam.lib.parse.ParserContextImpl' ,
@@ -103,6 +105,72 @@ foam.CLASS({
103105
104106 // test user's birthday is between two timestamps
105107 test(evaluate("birthday=" + dateFormat_.format(noon.toInstant()), user), user.getBirthday() + " = "+noon.toString());
108+
109+ // ── Float/Decimal parsing tests ──
110+ QueryParser floatParser = new QueryParser(
111+ foam.parse.test.QueryParserTestUser.getOwnClassInfo());
112+
113+ // Decimal values should parse
114+ test(canParseFloat(floatParser, "amount=6.5"), "Float: parse amount=6.5");
115+ test(canParseFloat(floatParser, "amount=0.10000000009999999995"), "Float: parse long decimal");
116+ test(canParseFloat(floatParser, "amount=0.0"), "Float: parse 0.0");
117+
118+ // Decimal comparisons
119+ test(canParseFloat(floatParser, "amount>6.5"), "Float: parse amount>6.5");
120+ test(canParseFloat(floatParser, "amount>=6.5"), "Float: parse amount>=6.5");
121+ test(canParseFloat(floatParser, "amount<6.5"), "Float: parse amount<6.5");
122+ test(canParseFloat(floatParser, "amount<=6.5"), "Float: parse amount<=6.5");
123+
124+ // Negative decimals
125+ test(canParseFloat(floatParser, "amount>-0.10000000009999999995"), "Float: parse negative decimal");
126+ test(canParseFloat(floatParser, "amount<-6.5"), "Float: parse amount<-6.5");
127+
128+ // Combined with other predicates
129+ test(canParseFloat(floatParser, "amount>=0.1 AND firstName=John"), "Float: decimal in AND");
130+
131+ // Evaluate predicates against objects with float values
132+ foam.parse.test.QueryParserTestUser m1 = new foam.parse.test.QueryParserTestUser();
133+ m1.setId(1);
134+ m1.setFirstName("positive");
135+ m1.setAmount(6.5);
136+
137+ foam.parse.test.QueryParserTestUser m2 = new foam.parse.test.QueryParserTestUser();
138+ m2.setId(2);
139+ m2.setFirstName("negative");
140+ m2.setAmount(-3.14);
141+
142+ foam.parse.test.QueryParserTestUser m3 = new foam.parse.test.QueryParserTestUser();
143+ m3.setId(3);
144+ m3.setFirstName("zero");
145+ m3.setAmount(0.0);
146+
147+ test(evaluateFloat(floatParser, "amount>6.0", m1), "Float: 6.5 > 6.0");
148+ test(!evaluateFloat(floatParser, "amount>6.0", m2), "Float: -3.14 !> 6.0");
149+ test(evaluateFloat(floatParser, "amount<0.0", m2), "Float: -3.14 < 0.0");
150+ test(!evaluateFloat(floatParser, "amount<0.0", m1), "Float: 6.5 !< 0.0");
151+ test(evaluateFloat(floatParser, "amount>=0.0", m3), "Float: 0.0 >= 0.0");
152+ test(evaluateFloat(floatParser, "amount<=0.0", m3), "Float: 0.0 <= 0.0");
153+ test(evaluateFloat(floatParser, "amount>=-3.15", m2), "Float: -3.14 >= -3.15");
154+ test(!evaluateFloat(floatParser, "amount>=-3.13", m2), "Float: -3.14 !>= -3.13");
155+
156+ // DAO select with float predicate
157+ DAO dao = new foam.dao.MapDAO(
158+ foam.parse.test.QueryParserTestUser.getOwnClassInfo());
159+ dao.put(m1);
160+ dao.put(m2);
161+ dao.put(m3);
162+
163+ Predicate floatPred = buildFloatPredicate(floatParser, "amount>0.0");
164+ if ( floatPred != null ) {
165+ ArraySink sink = (ArraySink) dao.where(floatPred).select(new ArraySink());
166+ test(sink.getArray().size() == 1, "Float DAO: amount>0.0 matches 1 record, got " + sink.getArray().size());
167+ }
168+
169+ floatPred = buildFloatPredicate(floatParser, "amount<0.0");
170+ if ( floatPred != null ) {
171+ ArraySink sink = (ArraySink) dao.where(floatPred).select(new ArraySink());
172+ test(sink.getArray().size() == 1, "Float DAO: amount<0.0 matches 1 record, got " + sink.getArray().size());
173+ }
106174 `
107175 } ,
108176 {
@@ -147,6 +215,57 @@ foam.CLASS({
147215 if (predicate == null) return false;
148216 return predicate.f(user);
149217 `
218+ } ,
219+ {
220+ name : 'buildFloatPredicate' ,
221+ type : 'foam.mlang.predicate.Predicate' ,
222+ args : [
223+ { name : 'parser' , javaType : 'foam.parse.QueryParser' } ,
224+ { name : 'query' , type : 'String' }
225+ ] ,
226+ javaCode : `
227+ StringPStream sps = new StringPStream();
228+ sps.setString(query);
229+ PStream ps = sps;
230+ ParserContext px = new ParserContextImpl();
231+ ps = parser.parse(ps, px);
232+ if ( ps == null ) return null;
233+ Predicate pred = (Predicate) ps.value();
234+ return pred.partialEval();
235+ `
236+ } ,
237+ {
238+ name : 'canParseFloat' ,
239+ type : 'Boolean' ,
240+ args : [
241+ { name : 'parser' , javaType : 'foam.parse.QueryParser' } ,
242+ { name : 'query' , type : 'String' }
243+ ] ,
244+ javaCode : `
245+ try {
246+ return buildFloatPredicate(parser, query) != null;
247+ } catch ( Exception e ) {
248+ return false;
249+ }
250+ `
251+ } ,
252+ {
253+ name : 'evaluateFloat' ,
254+ type : 'Boolean' ,
255+ args : [
256+ { name : 'parser' , javaType : 'foam.parse.QueryParser' } ,
257+ { name : 'query' , type : 'String' } ,
258+ { name : 'obj' , type : 'FObject' }
259+ ] ,
260+ javaCode : `
261+ try {
262+ Predicate pred = buildFloatPredicate(parser, query);
263+ if ( pred == null ) return false;
264+ return pred.f(obj);
265+ } catch ( Exception e ) {
266+ return false;
267+ }
268+ `
150269 }
151270 ]
152271} ) ;
0 commit comments