Browse Source

grammar: #110 - generated parsers

Ranides Atterwim 2 years ago
parent
commit
91ec02828b
25 changed files with 577 additions and 74 deletions
  1. 260 0
      assira.core/src/main/java/net/ranides/assira/math/NumberPair.java
  2. 99 0
      assira.core/src/test/java/net/ranides/assira/math/NumberPairTest.java
  3. 30 16
      assira.grammar/src/main/antlr4/net/ranides/grammars/CssColors.g4
  4. 4 4
      assira.grammar/src/main/java/net/ranides/assira/g4/G4Node.java
  5. 3 3
      assira.grammar/src/main/java/net/ranides/assira/g4/G4Parser.java
  6. 2 2
      assira.grammar/src/main/java/net/ranides/assira/g4/G4Selector.java
  7. 13 2
      assira.grammar/src/main/java/net/ranides/assira/g4/G4SelectorBuilder.java
  8. 1 1
      assira.grammar/src/main/java/net/ranides/assira/g4/G4Support.java
  9. 10 5
      assira.grammar/src/main/java/net/ranides/assira/g4/impl/antlr/NodeAbstract.java
  10. 2 2
      assira.grammar/src/main/java/net/ranides/assira/g4/impl/antlr/NodeForRule.java
  11. 2 2
      assira.grammar/src/main/java/net/ranides/assira/g4/impl/antlr/NodeForTerm.java
  12. 2 2
      assira.grammar/src/main/java/net/ranides/assira/g4/impl/antlr/NodeForToken.java
  13. 1 1
      assira.grammar/src/main/java/net/ranides/assira/g4/impl/antlr/NodeSupport.java
  14. 3 3
      assira.grammar/src/main/java/net/ranides/assira/g4/impl/antlr/ParserGenerated.java
  15. 3 3
      assira.grammar/src/main/java/net/ranides/assira/g4/impl/antlr/ParserInterpreted.java
  16. 4 4
      assira.grammar/src/main/java/net/ranides/assira/g4/impl/antlr/RootAbstract.java
  17. 2 2
      assira.grammar/src/main/java/net/ranides/assira/g4/properties/G4NodeProperties.java
  18. 2 2
      assira.grammar/src/main/java/net/ranides/assira/g4/properties/G4NodeProperty.java
  19. 4 4
      assira.grammar/src/test/java/net/ranides/assira/g4/G4NodeTest.java
  20. 1 1
      assira.grammar/src/test/java/net/ranides/assira/g4/G4ParserTest.java
  21. 1 2
      assira.grammar/src/test/java/net/ranides/assira/g4/G4SelectorTest.java
  22. 74 13
      assira.grammar/src/test/java/net/ranides/assira/grammars/CssColorsTest.java
  23. 15 0
      assira.grammar/src/test/resources/CssColors.paths.txt
  24. 15 0
      assira.grammar/src/test/resources/CssColors.tree.txt
  25. 24 0
      doc/Numbers.txt

+ 260 - 0
assira.core/src/main/java/net/ranides/assira/math/NumberPair.java

@@ -0,0 +1,260 @@
+package net.ranides.assira.math;
+
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+@EqualsAndHashCode
+public class NumberPair {
+
+    private final Number a;
+    private final Number b;
+
+    private static final Class<?>[] type2real = {
+        Float.class,        // 0
+        Float.class,        // 1
+        Float.class,        // 2
+        Double.class,       // 3
+        BigDecimal.class    // 4
+    };
+
+    private static final Class<?>[] type2int = {
+        Byte.class,         // 0
+        Short.class,        // 1
+        Integer.class,      // 2
+        Long.class,         // 3
+        BigInteger.class    // 4
+    };
+
+    public NumberPair(Number a, Number b) {
+        this.a = a;
+        this.b = b;
+    }
+
+    public static NumberPair of(Number a, Number b) {
+        return new NumberPair(a,b);
+    }
+
+    private static int real2type(Class<?> a) {
+        if(Float.class.equals(a)) {
+            return 2;
+        }
+        if(Double.class.equals(a)) {
+            return 3;
+        }
+        if(BigDecimal.class.equals(a)) {
+            return 4;
+        }
+        return -1;
+    }
+
+    private static int int2type(Class<?> a) {
+        if(Byte.class.equals(a)) {
+            return 0;
+        }
+        if(Short.class.equals(a)) {
+            return 1;
+        }
+        if(Integer.class.equals(a)) {
+            return 2;
+        }
+        if(Long.class.equals(a)) {
+            return 3;
+        }
+        if(BigInteger.class.equals(a)) {
+            return 4;
+        }
+        return -1;
+    }
+
+    private static Class<? extends Number> wider(Class<?> a, Class<?> b) {
+        int ai = int2type(a);
+        int bi = int2type(b);
+        int mi = Math.max(ai, bi);
+
+        if (ai != -1 && bi != -1) {
+            return (Class)type2int[mi];
+        }
+
+        int af = real2type(a);
+        int bf = real2type(b);
+        int mf = Math.max(af, bf);
+
+        return (Class)type2real[Math.max(mi,mf)];
+    }
+
+    private static <T extends Number> T cast(Class<T> target, Number value) {
+        if(target.isInstance(value)) {
+            return target.cast(value);
+        }
+        if(Float.class.equals(target)) {
+            return target.cast(value.floatValue());
+        }
+        if(Double.class.equals(target)) {
+            return target.cast(value.doubleValue());
+        }
+        if(BigDecimal.class.equals(target)) {
+            return target.cast(asBigDecimal(value));
+        }
+
+        if(Byte.class.equals(target)) {
+            return target.cast(value.byteValue());
+        }
+        if(Short.class.equals(target)) {
+            return target.cast(value.shortValue());
+        }
+        if(Integer.class.equals(target)) {
+            return target.cast(value.intValue());
+        }
+        if(Long.class.equals(target)) {
+            return target.cast(value.longValue());
+        }
+        if(BigInteger.class.equals(target)) {
+            return target.cast(BigInteger.valueOf(value.longValue()));
+        }
+
+        return target.cast(value);
+    }
+
+    private static BigDecimal asBigDecimal(Number value) {
+        if(value instanceof BigInteger) {
+            return new BigDecimal(((BigInteger) value));
+        }
+        if(value instanceof Float || value instanceof Double) {
+            return new BigDecimal(value.doubleValue());
+        }
+        return new BigDecimal(value.longValue());
+    }
+
+    public NumberPair widen() {
+        Class<? extends Number> target = wider(a.getClass(), b.getClass());
+        return new NumberPair(cast(target, a), cast(target, b));
+    }
+
+    public Number add() {
+        Class<? extends Number> target = wider(a.getClass(), b.getClass());
+
+        if(Float.class.equals(target)) {
+            return a.floatValue() + b.floatValue();
+        }
+        if(Double.class.equals(target)) {
+            return a.doubleValue() + b.doubleValue();
+        }
+        if(BigDecimal.class.equals(target)) {
+            return asBigDecimal(a).add(asBigDecimal(b));
+        }
+        if(Byte.class.equals(target)) {
+            return a.byteValue() + b.byteValue();
+        }
+        if(Short.class.equals(target)) {
+            return a.shortValue() + b.shortValue();
+        }
+        if(Integer.class.equals(target)) {
+            return a.intValue() + b.intValue();
+        }
+        if(Long.class.equals(target)) {
+            return a.longValue() + b.longValue();
+        }
+        if(BigInteger.class.equals(target)) {
+            return BigInteger.valueOf(a.longValue()).add(BigInteger.valueOf(b.longValue()));
+        }
+
+        throw new UnsupportedOperationException("Can't add " + a.getClass() + " & " + b.getClass());
+    }
+
+    public Number subtract() {
+        Class<? extends Number> target = wider(a.getClass(), b.getClass());
+
+        if(Float.class.equals(target)) {
+            return a.floatValue() - b.floatValue();
+        }
+        if(Double.class.equals(target)) {
+            return a.doubleValue() - b.doubleValue();
+        }
+        if(BigDecimal.class.equals(target)) {
+            return asBigDecimal(a).subtract(asBigDecimal(b));
+        }
+        if(Byte.class.equals(target)) {
+            return a.byteValue() - b.byteValue();
+        }
+        if(Short.class.equals(target)) {
+            return a.shortValue() - b.shortValue();
+        }
+        if(Integer.class.equals(target)) {
+            return a.intValue() - b.intValue();
+        }
+        if(Long.class.equals(target)) {
+            return a.longValue() - b.longValue();
+        }
+        if(BigInteger.class.equals(target)) {
+            return BigInteger.valueOf(a.longValue()).subtract(BigInteger.valueOf(b.longValue()));
+        }
+
+        throw new UnsupportedOperationException("Can't subtract " + a.getClass() + " & " + b.getClass());
+    }
+
+    public Number multiply() {
+        Class<? extends Number> target = wider(a.getClass(), b.getClass());
+
+        if(Float.class.equals(target)) {
+            return a.floatValue() * b.floatValue();
+        }
+        if(Double.class.equals(target)) {
+            return a.doubleValue() * b.doubleValue();
+        }
+        if(BigDecimal.class.equals(target)) {
+            return asBigDecimal(a).multiply(asBigDecimal(b));
+        }
+        if(Byte.class.equals(target)) {
+            return a.byteValue() * b.byteValue();
+        }
+        if(Short.class.equals(target)) {
+            return a.shortValue() * b.shortValue();
+        }
+        if(Integer.class.equals(target)) {
+            return a.intValue() * b.intValue();
+        }
+        if(Long.class.equals(target)) {
+            return a.longValue() * b.longValue();
+        }
+        if(BigInteger.class.equals(target)) {
+            return BigInteger.valueOf(a.longValue()).multiply(BigInteger.valueOf(b.longValue()));
+        }
+
+        throw new UnsupportedOperationException("Can't multiply " + a.getClass() + " & " + b.getClass());
+    }
+
+    public Number divide() {
+        Class<? extends Number> target = wider(a.getClass(), b.getClass());
+
+        if(Float.class.equals(target)) {
+            return a.floatValue() / b.floatValue();
+        }
+        if(Double.class.equals(target)) {
+            return a.doubleValue() / b.doubleValue();
+        }
+        if(BigDecimal.class.equals(target)) {
+            return asBigDecimal(a).divide(asBigDecimal(b));
+        }
+        if(Byte.class.equals(target)) {
+            return a.byteValue() / b.byteValue();
+        }
+        if(Short.class.equals(target)) {
+            return a.shortValue() / b.shortValue();
+        }
+        if(Integer.class.equals(target)) {
+            return a.intValue() / b.intValue();
+        }
+        if(Long.class.equals(target)) {
+            return a.longValue() / b.longValue();
+        }
+        if(BigInteger.class.equals(target)) {
+            return BigInteger.valueOf(a.longValue()).divide(BigInteger.valueOf(b.longValue()));
+        }
+
+        throw new UnsupportedOperationException("Can't add " + a.getClass() + " & " + b.getClass());
+    }
+
+
+}

+ 99 - 0
assira.core/src/test/java/net/ranides/assira/math/NumberPairTest.java

@@ -0,0 +1,99 @@
+package net.ranides.assira.math;
+
+import org.junit.Test;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import static org.junit.Assert.assertEquals;
+
+public class NumberPairTest {
+
+    @Test
+    public void widen() {
+        assertEquals(new NumberPair(1.0d, 1.0d), new NumberPair(1.0f, 1.0d).widen());
+        assertEquals(new NumberPair(10.0d, 1.0d), new NumberPair(10, 1.0d).widen());
+        assertEquals(new NumberPair(10L, 100L), new NumberPair(10, 100L).widen());
+
+        assertEquals(
+            new NumberPair(BigInteger.valueOf(10), BigInteger.valueOf(15)),
+            new NumberPair(10, BigInteger.valueOf(15)).widen()
+        );
+        assertEquals(
+            new NumberPair(BigDecimal.valueOf(10), BigDecimal.valueOf(15)),
+            new NumberPair(10.0f, BigInteger.valueOf(15)).widen()
+        );
+        assertEquals(
+            new NumberPair(BigDecimal.valueOf(10.5), BigDecimal.valueOf(15)),
+            new NumberPair(10.5f, BigInteger.valueOf(15)).widen()
+        );
+    }
+
+    @Test
+    public void add() {
+        assertEquals(2.0d, new NumberPair(1.0f, 1.0d).add());
+        assertEquals(11.0d, new NumberPair(10, 1.0d).add());
+        assertEquals(110L, new NumberPair(10, 100L).add());
+
+        assertEquals(
+            BigInteger.valueOf(25), new NumberPair(10, BigInteger.valueOf(15)).add()
+        );
+        assertEquals(
+            BigDecimal.valueOf(25), new NumberPair(10.0f, BigInteger.valueOf(15)).add()
+        );
+        assertEquals(
+            BigDecimal.valueOf(25.5), new NumberPair(10.5f, BigInteger.valueOf(15)).add()
+        );
+    }
+
+    @Test
+    public void subtract() {
+        assertEquals(2.0d, new NumberPair(3.0f, 1.0d).subtract());
+        assertEquals(19.0d, new NumberPair(20, 1.0d).subtract());
+        assertEquals(20L, new NumberPair(30, 10L).subtract());
+
+        assertEquals(
+            BigInteger.valueOf(25), new NumberPair(40, BigInteger.valueOf(15)).subtract()
+        );
+        assertEquals(
+            BigDecimal.valueOf(25), new NumberPair(40.0f, BigInteger.valueOf(15)).subtract()
+        );
+        assertEquals(
+            BigDecimal.valueOf(25.5), new NumberPair(40.5f, BigInteger.valueOf(15)).subtract()
+        );
+    }
+
+    @Test
+    public void multiply() {
+        assertEquals(3.0d, new NumberPair(3.0f, 1.0d).multiply());
+        assertEquals(30.0d, new NumberPair(20, 1.5d).multiply());
+        assertEquals(300L, new NumberPair(30, 10L).multiply());
+
+        assertEquals(
+            BigInteger.valueOf(60), new NumberPair(4, BigInteger.valueOf(15)).multiply()
+        );
+        assertEquals(
+            BigDecimal.valueOf(60), new NumberPair(4.0f, BigInteger.valueOf(15)).multiply()
+        );
+        assertEquals(
+            BigDecimal.valueOf(67.5), new NumberPair(4.5f, BigInteger.valueOf(15)).multiply()
+        );
+    }
+
+    @Test
+    public void divide() {
+        assertEquals(1.5d, new NumberPair(3.0f, 2.0d).divide());
+        assertEquals(15.0d, new NumberPair(30, 2.0d).divide());
+        assertEquals(3L, new NumberPair(30, 10L).divide());
+
+        assertEquals(
+            BigInteger.valueOf(3), new NumberPair(45, BigInteger.valueOf(15)).divide()
+        );
+        assertEquals(
+            BigDecimal.valueOf(3), new NumberPair(45.0f, BigInteger.valueOf(15)).divide()
+        );
+        assertEquals(
+            BigDecimal.valueOf(4.75), new NumberPair(9.5f, BigInteger.valueOf(2)).divide()
+        );
+    }
+}

+ 30 - 16
assira.grammar/src/main/antlr4/net/ranides/grammars/CssColors.g4

@@ -1,34 +1,44 @@
 grammar CssColors;
 
-color
-    : value=hslColor #color_first
-    | value=rgbColor #color_second
-    | value=hexColor #color_third
+colors
+    : color+
     ;
 
-
+color
+    : hslColor
+    | hslaColor
+    | rgbColor
+    | rgbaColor
+    | hexColor
+    ;
 
 hslColor
-    : HSL LP h=numberInt CS s=numberPct CS l=numberPct (CS alpha=NUMBER_REAL)? RP
+    : HSL LP h=numberInt CS s=numberPct CS l=numberPct RP
     ;
 
-rgbColor
-    : RGB LP r=numberInt CS g=numberInt CS b=numberInt (CS alpha=NUMBER_REAL)? RP
+hslaColor
+    : HSL LP h=numberInt CS s=numberPct CS l=numberPct CS alpha=NUMBER_REAL RP
     ;
 
-hexColor
-    : NUMBER_HEX
+rgbColor
+    : RGB LP r=numberInt CS g=numberInt CS b=numberInt RP
     ;
 
-numberHex: NUMBER_HEX;
+rgbaColor
+    : RGBA LP r=numberInt CS g=numberInt CS b=numberInt CS alpha=NUMBER_REAL RP
+    ;
 
-numberReal: NUMBER_REAL;
+hexColor
+    : NUMBER_HEX3   #hexColor3
+    | NUMBER_HEX6   #hexColor6
+    ;
 
 numberPct : NUMBER_PCT;
 
 numberInt : NUMBER_INT;
 
-NUMBER_HEX: '#' HEX_DIGIT+;
+NUMBER_HEX6: HEX_PREFIX HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT;
+NUMBER_HEX3: HEX_PREFIX HEX_DIGIT HEX_DIGIT HEX_DIGIT;
 
 NUMBER_REAL : INT_DIGIT* FS INT_DIGIT*;
 
@@ -36,16 +46,20 @@ NUMBER_PCT: INT_DIGIT+ '%';
 
 NUMBER_INT: INT_DIGIT+ ;
 
-
 LP: '(';
 RP: ')';
 CS: ',';
 FS: '.';
 
 RGB: 'rgb';
+RGBA: 'rgba';
 HSL: 'hsl';
+HSLA: 'hsla';
 
-fragment HEX_DIGIT: ('0'..'9'|'a'..'f'|'A'..'F');
-fragment INT_DIGIT: '0'..'9';
+fragment HEX_PREFIX: '#';
+
+fragment HEX_DIGIT: ('0'..'9' | 'a'..'f' | 'A'..'F');
 
+fragment INT_DIGIT: '0'..'9';
 
+WS :  [ \t\r\n]+ -> skip ;

+ 4 - 4
assira.grammar/src/main/java/net/ranides/assira/g4/G4Node.java

@@ -1,7 +1,7 @@
-package net.ranides.assira.g4;
+package net.ranides.assira.grammar;
 
 import net.ranides.assira.collection.query.CQuery;
-import net.ranides.assira.g4.properties.G4NodeProperty;
+import net.ranides.assira.grammar.properties.G4NodeProperty;
 import net.ranides.assira.generic.Ref;
 import net.ranides.assira.reflection.IClass;
 
@@ -33,9 +33,9 @@ public interface G4Node {
 
     G4Node child(String name);
 
-    // @todo refine G4Node children/child/find methods
+    CQuery<G4Node> findAll(String path);
 
-    CQuery<G4Node> find(String path);
+    G4Node find(String path);
 
     String path();
 

+ 3 - 3
assira.grammar/src/main/java/net/ranides/assira/g4/G4Parser.java

@@ -1,8 +1,8 @@
-package net.ranides.assira.g4;
+package net.ranides.assira.grammar;
 
 
-import net.ranides.assira.g4.impl.antlr.ParserGenerated;
-import net.ranides.assira.g4.impl.antlr.ParserInterpreted;
+import net.ranides.assira.grammar.impl.antlr.ParserGenerated;
+import net.ranides.assira.grammar.impl.antlr.ParserInterpreted;
 import net.ranides.assira.reflection.IClass;
 import org.antlr.v4.runtime.Lexer;
 import org.antlr.v4.runtime.Parser;

+ 2 - 2
assira.grammar/src/main/java/net/ranides/assira/g4/G4Selector.java

@@ -1,4 +1,4 @@
-package net.ranides.assira.g4;
+package net.ranides.assira.grammar;
 
 import lombok.RequiredArgsConstructor;
 import net.ranides.assira.collection.iterators.ForwardIterator;
@@ -11,7 +11,7 @@ import net.ranides.assira.collection.query.support.BaseIterable;
 import net.ranides.assira.collection.query.support.BaseState;
 import net.ranides.assira.functional.Consumers;
 import net.ranides.assira.functional.Predicates;
-import net.ranides.assira.g4.properties.G4NodeProperty;
+import net.ranides.assira.grammar.properties.G4NodeProperty;
 import net.ranides.assira.generic.Pair;
 import net.ranides.assira.generic.Ref;
 

+ 13 - 2
assira.grammar/src/main/java/net/ranides/assira/g4/G4SelectorBuilder.java

@@ -1,10 +1,10 @@
-package net.ranides.assira.g4;
+package net.ranides.assira.grammar;
 
 import net.ranides.assira.collection.maps.CrossMap;
 import net.ranides.assira.collection.maps.OpenCrossMap;
 import net.ranides.assira.collection.maps.OpenMap;
 import net.ranides.assira.collection.sets.OpenSet;
-import net.ranides.assira.g4.properties.G4NodeProperty;
+import net.ranides.assira.grammar.properties.G4NodeProperty;
 import net.ranides.assira.generic.Pair;
 
 import java.util.ArrayList;
@@ -78,6 +78,7 @@ public class G4SelectorBuilder<V> {
      * @return self
      */
     public G4SelectorBuilder<V> skipRule(int ruleName) {
+        validateParser();
         skipName.add(parser.ruleName(ruleName));
         return this;
     }
@@ -89,6 +90,7 @@ public class G4SelectorBuilder<V> {
      * @return self
      */
     public G4SelectorBuilder<V> skipRule(Class<?> ruleName) {
+        validateParser();
         skipName.add(parser.ruleName(ruleName));
         return this;
     }
@@ -269,11 +271,13 @@ public class G4SelectorBuilder<V> {
     }
 
     public G4SelectorBuilder<V> mapRule(int ruleName, BiFunction<G4Node, G4Selector<V>, V> function) {
+        validateParser();
         mapForName.put(parser.ruleName(ruleName), function);
         return this;
     }
 
     public <R> G4SelectorBuilder<V> mapRule(Class<R> ruleName, BiFunction<R, G4Selector<V>, V> function) {
+        validateParser();
         nativeMapForName.put(parser.ruleName(ruleName), (BiFunction)function);
         return this;
     }
@@ -299,6 +303,7 @@ public class G4SelectorBuilder<V> {
     }
 
     public G4SelectorBuilder<V> mapToken(int tokenName, BiFunction<G4Node, G4Selector<V>, V> function) {
+        validateParser();
         mapForName.put(parser.tokenName(tokenName), function);
         return this;
     }
@@ -357,4 +362,10 @@ public class G4SelectorBuilder<V> {
         return new G4Selector<>(this);
     }
 
+    private void validateParser() {
+        if(parser == null) {
+            throw new IllegalStateException("You need to assign parser");
+        }
+    }
+
 }

+ 1 - 1
assira.grammar/src/main/java/net/ranides/assira/g4/G4Support.java

@@ -1,4 +1,4 @@
-package net.ranides.assira.g4;
+package net.ranides.assira.grammar;
 
 import lombok.experimental.UtilityClass;
 import net.ranides.assira.text.StrAppender;

+ 10 - 5
assira.grammar/src/main/java/net/ranides/assira/g4/impl/antlr/NodeAbstract.java

@@ -1,9 +1,9 @@
-package net.ranides.assira.g4.impl.antlr;
+package net.ranides.assira.grammar.impl.antlr;
 
 import net.ranides.assira.collection.query.CQuery;
-import net.ranides.assira.g4.G4Node;
-import net.ranides.assira.g4.properties.G4NodeProperties;
-import net.ranides.assira.g4.properties.G4NodeProperty;
+import net.ranides.assira.grammar.G4Node;
+import net.ranides.assira.grammar.properties.G4NodeProperties;
+import net.ranides.assira.grammar.properties.G4NodeProperty;
 import net.ranides.assira.generic.LazyCaller;
 import net.ranides.assira.generic.LazyCallers;
 import net.ranides.assira.generic.Ref;
@@ -75,7 +75,7 @@ abstract class NodeAbstract implements G4Node {
     }
 
     @Override
-    public CQuery<G4Node> find(String path) {
+    public CQuery<G4Node> findAll(String path) {
         String[] names = path.split("/");
         CQuery<G4Node> current = CQuery.from().value(this);
 
@@ -87,6 +87,11 @@ abstract class NodeAbstract implements G4Node {
         return current;
     }
 
+    @Override
+    public G4Node find(String path) {
+        return findAll(path).first().get();
+    }
+
     @Override
     public String name() {
         return shortname() + qualifier();

+ 2 - 2
assira.grammar/src/main/java/net/ranides/assira/g4/impl/antlr/NodeForRule.java

@@ -1,7 +1,7 @@
-package net.ranides.assira.g4.impl.antlr;
+package net.ranides.assira.grammar.impl.antlr;
 
 import net.ranides.assira.collection.query.CQuery;
-import net.ranides.assira.g4.G4Node;
+import net.ranides.assira.grammar.G4Node;
 import net.ranides.assira.generic.LazyCaller;
 import net.ranides.assira.generic.LazyCallers;
 import org.antlr.v4.runtime.ParserRuleContext;

+ 2 - 2
assira.grammar/src/main/java/net/ranides/assira/g4/impl/antlr/NodeForTerm.java

@@ -1,7 +1,7 @@
-package net.ranides.assira.g4.impl.antlr;
+package net.ranides.assira.grammar.impl.antlr;
 
 import net.ranides.assira.collection.query.CQuery;
-import net.ranides.assira.g4.G4Node;
+import net.ranides.assira.grammar.G4Node;
 import org.antlr.v4.runtime.tree.ParseTree;
 import org.antlr.v4.runtime.tree.TerminalNode;
 

+ 2 - 2
assira.grammar/src/main/java/net/ranides/assira/g4/impl/antlr/NodeForToken.java

@@ -1,7 +1,7 @@
-package net.ranides.assira.g4.impl.antlr;
+package net.ranides.assira.grammar.impl.antlr;
 
 import net.ranides.assira.collection.query.CQuery;
-import net.ranides.assira.g4.G4Node;
+import net.ranides.assira.grammar.G4Node;
 import org.antlr.v4.runtime.CommonToken;
 
 import java.util.NoSuchElementException;

+ 1 - 1
assira.grammar/src/main/java/net/ranides/assira/g4/impl/antlr/NodeSupport.java

@@ -1,4 +1,4 @@
-package net.ranides.assira.g4.impl.antlr;
+package net.ranides.assira.grammar.impl.antlr;
 
 import lombok.experimental.UtilityClass;
 

+ 3 - 3
assira.grammar/src/main/java/net/ranides/assira/g4/impl/antlr/ParserGenerated.java

@@ -1,7 +1,7 @@
-package net.ranides.assira.g4.impl.antlr;
+package net.ranides.assira.grammar.impl.antlr;
 
-import net.ranides.assira.g4.G4Node;
-import net.ranides.assira.g4.G4Parser;
+import net.ranides.assira.grammar.G4Node;
+import net.ranides.assira.grammar.G4Parser;
 import net.ranides.assira.generic.Ref;
 import net.ranides.assira.reflection.IClass;
 import net.ranides.assira.reflection.IMethod;

+ 3 - 3
assira.grammar/src/main/java/net/ranides/assira/g4/impl/antlr/ParserInterpreted.java

@@ -1,10 +1,10 @@
-package net.ranides.assira.g4.impl.antlr;
+package net.ranides.assira.grammar.impl.antlr;
 
 
 import net.ranides.assira.collection.maps.CrossMap;
 import net.ranides.assira.collection.maps.OpenCrossMap;
-import net.ranides.assira.g4.G4Node;
-import net.ranides.assira.g4.G4Parser;
+import net.ranides.assira.grammar.G4Node;
+import net.ranides.assira.grammar.G4Parser;
 import net.ranides.assira.generic.Ref;
 import net.ranides.assira.text.StringUtils;
 import org.antlr.runtime.RecognitionException;

+ 4 - 4
assira.grammar/src/main/java/net/ranides/assira/g4/impl/antlr/RootAbstract.java

@@ -1,13 +1,13 @@
-package net.ranides.assira.g4.impl.antlr;
+package net.ranides.assira.grammar.impl.antlr;
 
-import net.ranides.assira.g4.G4Node;
-import net.ranides.assira.g4.G4Parser;
+import net.ranides.assira.grammar.G4Node;
+import net.ranides.assira.grammar.G4Parser;
 import net.ranides.assira.generic.Ref;
 import org.antlr.v4.runtime.CommonToken;
 import org.antlr.v4.runtime.tree.RuleNode;
 import org.antlr.v4.runtime.tree.TerminalNode;
 
-public abstract class RootAbstract {
+abstract class RootAbstract {
 
     public abstract G4Parser parser();
 

+ 2 - 2
assira.grammar/src/main/java/net/ranides/assira/g4/properties/G4NodeProperties.java

@@ -1,6 +1,6 @@
-package net.ranides.assira.g4.properties;
+package net.ranides.assira.grammar.properties;
 
-import net.ranides.assira.g4.G4Node;
+import net.ranides.assira.grammar.G4Node;
 import net.ranides.assira.generic.Ref;
 
 import java.util.Collections;

+ 2 - 2
assira.grammar/src/main/java/net/ranides/assira/g4/properties/G4NodeProperty.java

@@ -1,7 +1,7 @@
-package net.ranides.assira.g4.properties;
+package net.ranides.assira.grammar.properties;
 
 import net.ranides.assira.functional.Functions.Function2;
-import net.ranides.assira.g4.G4Node;
+import net.ranides.assira.grammar.G4Node;
 import net.ranides.assira.trace.PreparedException;
 
 import java.util.NoSuchElementException;

+ 4 - 4
assira.grammar/src/test/java/net/ranides/assira/g4/G4NodeTest.java

@@ -1,4 +1,4 @@
-package net.ranides.assira.g4;
+package net.ranides.assira.grammar;
 
 import net.ranides.assira.io.ResourceUtils;
 import net.ranides.assira.math.MathUtils;
@@ -24,13 +24,13 @@ public class G4NodeTest {
         G4Selector<Integer> pow1 = new G4SelectorBuilder<Integer>()
             .withName("pow")
             .mapRule("operatorPow", ctx -> {
-                List<G4Node> args = ctx.find("../../expression").list();
+                List<G4Node> args = ctx.findAll("../../expression").list();
 
                 Number arg1 = num1.first(args.get(0));
                 Number arg2 = num1.first(args.get(1));
 
-                Number label1 = ctx.find("../../@a").first().map(num1::first).orElse(0);
-                Number label2 = ctx.find("../../@b").first().map(num1::first).orElse(0);
+                Number label1 = ctx.findAll("../../@a").first().map(num1::first).orElse(0);
+                Number label2 = ctx.findAll("../../@b").first().map(num1::first).orElse(0);
 
                 assertEquals(3, arg1.intValue());
                 assertEquals(4, arg2.intValue());

+ 1 - 1
assira.grammar/src/test/java/net/ranides/assira/g4/G4ParserTest.java

@@ -1,4 +1,4 @@
-package net.ranides.assira.g4;
+package net.ranides.assira.grammar;
 
 import net.ranides.assira.collection.query.CQuery;
 import net.ranides.assira.io.ResourceUtils;

+ 1 - 2
assira.grammar/src/test/java/net/ranides/assira/g4/G4SelectorTest.java

@@ -1,11 +1,10 @@
-package net.ranides.assira.g4;
+package net.ranides.assira.grammar;
 
 import net.ranides.assira.io.ResourceUtils;
 import org.junit.Test;
 
 import java.io.IOException;
 import java.util.Arrays;
-import java.util.Iterator;
 import java.util.List;
 import java.util.NoSuchElementException;
 

+ 74 - 13
assira.grammar/src/test/java/net/ranides/assira/grammars/CssColorsTest.java

@@ -1,33 +1,94 @@
 package net.ranides.assira.grammars;
 
-import net.ranides.assira.g4.G4Node;
-import net.ranides.assira.g4.G4Parser;
-import net.ranides.assira.g4.G4Selector;
-import net.ranides.assira.g4.G4Support;
+import net.ranides.assira.awt.ColorUtils;
+import net.ranides.assira.grammar.G4Node;
+import net.ranides.assira.grammar.G4Parser;
+import net.ranides.assira.grammar.G4Selector;
+import net.ranides.assira.grammar.G4SelectorBuilder;
+import net.ranides.assira.grammar.G4Support;
+import net.ranides.assira.io.ResourceUtils;
 import net.ranides.grammars.CssColorsLexer;
 import net.ranides.grammars.CssColorsParser;
 import org.junit.Test;
 
+import java.awt.*;
+import java.io.IOException;
+
+import static net.ranides.assira.junit.NewAssert.assertThrows;
 import static org.junit.Assert.assertEquals;
 
 public class CssColorsTest {
 
     @Test
-    public void basic() {
+    public void basic() throws IOException {
         G4Parser gp = G4Parser.newInstance(CssColorsLexer.class, CssColorsParser.class);
 
-        G4Node out = gp.parse("color", "rgb(15,20,30,0.75)");
+        G4Node out = gp.parse("color", "rgba(15,20,30,0.75)");
+
+        assertEquals(ResourceUtils.text("CssColors.tree.txt"), G4Support.printTree(out));
+        assertEquals(ResourceUtils.text("CssColors.paths.txt"), G4Support.printPath(out));
+
+        assertEquals("rgba(15,20,30,0.75)", out.child("rgbaColor").text());
+        assertEquals("0.75", out.child("rgbaColor").child("@alpha").text());
+
+        assertEquals("color/rgbaColor/NUMBER_REAL", out.find("rgbaColor/@alpha").path());
+
+        assertThrows(e -> e.getMessage().contains("There is no path 'rgbaColor/@unknown' inside node 'color$0=rgba(15,20,30,0.75)'"), () -> {
+            out.find("rgbaColor/@unknown").path();
+        });
+
+        assertThrows(e -> e.getMessage().contains("There is no path 'unknown' inside node 'color/rgbaColor$0=rgba(15,20,30,0.75)'"), () -> {
+            out.child("rgbaColor").find("unknown").path();
+        });
+    }
+
+    @Test
+    public void evaluator() {
+
+        G4Parser parser = G4Parser.newInstance(CssColorsLexer.class, CssColorsParser.class);
+
+        G4Selector<Color> selector = new G4SelectorBuilder<Color>()
+            .withName("selector:color")
+            .withParser(parser)
+            .mapRule(CssColorsParser.RgbColorContext.class, node -> {
+                int r = Integer.parseInt(node.r.getText());
+                int g = Integer.parseInt(node.g.getText());
+                int b = Integer.parseInt(node.b.getText());
+
+                return new Color(r, g, b);
+            })
+            .mapRule("rgbaColor", node -> {
+                int r = Integer.parseInt(node.child("@r").text());
+                int g = Integer.parseInt(node.child("@g").text());
+                int b = Integer.parseInt(node.child("@b").text());
+                int alpha = Math.round(255 * Float.parseFloat(node.child("@alpha").text()));
+
+                return new Color(r, g, b, alpha);
+            })
+            .mapRule("hexColor#hexColor3", v -> {
+                int r = 17 * Integer.parseInt(v.text().substring(1,2), 16);
+                int g = 17 * Integer.parseInt(v.text().substring(2,3), 16);
+                int b = 17 * Integer.parseInt(v.text().substring(3,4), 16);
 
-        assertEquals("color#color_second", out.name());
+                return new Color(r, g, b);
+            })
+            .mapRule("hexColor#hexColor6", v -> {
+                int r = Integer.parseInt(v.text().substring(1,3), 16);
+                int g = Integer.parseInt(v.text().substring(3,5), 16);
+                int b = Integer.parseInt(v.text().substring(5,7), 16);
 
-        G4Node inner = out.child("@value");
+                return new Color(r, g, b);
+            })
 
-        assertEquals("rgb(15,20,30,0.75)", inner.text());
-        assertEquals("0.75", inner.child("@alpha").text());
+            .prepare();
 
-        System.out.println(inner.child("@alpha").name());
-        System.out.println(G4Support.printTree(out));
+        assertEquals("rgba(15,20,30,0.75)", ColorUtils.asCSS3(selector.first(parser.parse("color", "rgba ( 15, 20, 30, 0.75 )"))));
+        assertEquals("rgb(15,20,30)", ColorUtils.asCSS3(selector.first(parser.parse("color", "rgb(15,20,30)"))));
+        assertEquals("#f08040", ColorUtils.asCSS2(selector.first(parser.parse("color", "#f08040"))));
 
-        assertEquals("color/rgbColor/NUMBER_REAL", inner.child("@alpha").path());
+        assertEquals(
+            "#5080a0 #324668 #778899",
+            selector.stream(parser.parse("colors", "rgb ( 80, 128, 160 ) #324668 #789")).map(ColorUtils::asCSS2).join(" ")
+        );
     }
 }

+ 15 - 0
assira.grammar/src/test/resources/CssColors.paths.txt

@@ -0,0 +1,15 @@
+color = 'rgba(15,20,30,0.75)'
+color/rgbaColor = 'rgba(15,20,30,0.75)'
+color/rgbaColor/RGBA = 'rgba'
+color/rgbaColor/LP = '('
+color/rgbaColor/numberInt[0] = '15'
+color/rgbaColor/numberInt[0]/NUMBER_INT = '15'
+color/rgbaColor/CS[0] = ','
+color/rgbaColor/numberInt[1] = '20'
+color/rgbaColor/numberInt[1]/NUMBER_INT = '20'
+color/rgbaColor/CS[1] = ','
+color/rgbaColor/numberInt[2] = '30'
+color/rgbaColor/numberInt[2]/NUMBER_INT = '30'
+color/rgbaColor/CS[2] = ','
+color/rgbaColor/NUMBER_REAL = '0.75'
+color/rgbaColor/RP = ')'

+ 15 - 0
assira.grammar/src/test/resources/CssColors.tree.txt

@@ -0,0 +1,15 @@
+color$0 = 'rgba(15,20,30,0.75)'
+   rgbaColor$0 = 'rgba(15,20,30,0.75)'
+      RGBA = 'rgba'
+      LP = '('
+      numberInt$0 = '15'
+         NUMBER_INT = '15'
+      CS = ','
+      numberInt$0 = '20'
+         NUMBER_INT = '20'
+      CS = ','
+      numberInt$0 = '30'
+         NUMBER_INT = '30'
+      CS = ','
+      NUMBER_REAL = '0.75'
+      RP = ')'

+ 24 - 0
doc/Numbers.txt

@@ -0,0 +1,24 @@
+
+                byte  shor  int   long  floa  doub  BInt  BDec
+byte            byte  shor  int   long  floa  doub  BInt  BDec
+short           shor  shor  int   long  floa  doub  BInt  BDec
+int             int   int   int   long  floa  doub  BInt  BDec
+long            long  long  long  long  floa  doub  BInt  BDec
+float           byte  shor  int   long  floa  doub  BInt  BDec
+double          byte  shor  int   long  floa  doub  BInt  BDec
+BigInteger      byte  shor  int   long  floa  doub  BInt  BDec
+BigDecimal      byte  shor  int   long  floa  doub  BInt  BDec
+
+
+
+                    b   s   i   ii  f   ff  Bi  Bf
+1 byte         b    b   s   i   ii  f   ff  Bi  Bf
+2 short        s    s   s   i   ii  f   ff  Bi  Bf
+3 int          i    i   i   i   ii  f   ff  Bi  Bf
+4 float        f    f   f   f   ff  f   ff  Bf  Bf
+5 long         ii   ii  ii  ii  ii  ff  ff  Bi  Bf
+6 double       ff   ff  ff  ff  ff  ff  ff  Bf  Bf
+7 BigInteger   Bi   Bi  Bi  Bi  Bi  Bf  Bf  Bf  Bf
+8 BigDecimal   Bi   Bi  Bi  Bi  Bi  Bf  Bf  Bf  Bf
+
+64 options