Index: DefaultStatement.java
===================================================================
--- DefaultStatement.java	(revision 44749)
+++ DefaultStatement.java	(revision 44750)
@@ -9,6 +9,7 @@
 import org.apache.commons.lang3.tuple.Pair;
 import org.gvsig.expressionevaluator.Code;
 import org.gvsig.expressionevaluator.Code.Caller;
+import org.gvsig.expressionevaluator.CodeBuilder;
 import org.gvsig.expressionevaluator.Codes;
 import static org.gvsig.expressionevaluator.ExpressionBuilder.FUNCTION_LIST;
 import org.gvsig.expressionevaluator.ExpressionSyntaxException;
@@ -20,7 +21,6 @@
 import org.gvsig.expressionevaluator.Statement.StatementContext;
 import org.gvsig.expressionevaluator.impl.function.programming.CodeBlockFunction;
 import org.gvsig.expressionevaluator.impl.DefaultCodeBuilder.BaseCodes;
-import org.gvsig.expressionevaluator.spi.AbstractLexicalAnalyzer;
 
 /**
  *
@@ -46,10 +46,17 @@
     public class RuleRequireAnyToken implements IsApplicableRule {
 
         private final String[] required_token;
+        private String id;
 
         public RuleRequireAnyToken(String... required_token) {
             this.required_token = required_token;
         }
+        
+        @Override
+        public RuleRequireAnyToken capture_as(String... ids) {
+          this.id = ids[0];
+          return this;
+        }
 
         @Override
         public void parse(StatementContext context) {
@@ -64,6 +71,10 @@
                         context.getLexicalAnalyzer()
                 );
             }
+            if( this.id != null ) {
+              Code code = context.getCodeBuilder().constant(token.getValue());
+              context.setCode(id, code);
+            }
             context.next_token();
         }
 
@@ -84,13 +95,18 @@
 
     public class RuleRequireIdentifier implements IsApplicableRule {
 
-        private final String id;
+        private String id;
 
-        public RuleRequireIdentifier(String id) {
-            this.id = id;
+        public RuleRequireIdentifier() {
         }
 
         @Override
+        public Rule capture_as(String... ids) {
+          this.id = ids[0];
+          return this;
+        }
+
+        @Override
         public void parse(StatementContext context) {
             context.trace(this.toString()+".parse");
             Token token = context.look_token();
@@ -100,9 +116,11 @@
                         context.getLexicalAnalyzer()
                 );
             }
-            String identifier = (String) token.getLiteral();
-            Code code = context.getCodeBuilder().constant(identifier);
-            context.setCode(id, code);
+            if( this.id!=null ) {
+              String identifier = (String) token.getLiteral();
+              Code code = context.getCodeBuilder().constant(identifier);
+              context.setCode(id, code);
+            }
             context.next_token();
         }
 
@@ -122,13 +140,18 @@
 
     public class RuleRequireLiteralString implements Rule {
 
-        private final String id;
+        private String id;
 
-        public RuleRequireLiteralString(String id) {
-            this.id = id;
+        public RuleRequireLiteralString() {
         }
 
         @Override
+        public Rule capture_as(String... ids) {
+          this.id = ids[0];
+          return this;
+        }
+
+        @Override
         public void parse(StatementContext context) {
             context.trace(this.toString()+".parse");
             Token token = context.look_token();
@@ -138,10 +161,12 @@
                         context.getLexicalAnalyzer()
                 );
             }
-            String identifier = (String) token.getValue();
-            Code code = context.getCodeBuilder().constant(identifier);
-            context.setCode(id, code);
-            context.next_token();
+            if( this.id!=null ) {
+              String identifier = (String) token.getValue();
+              Code code = context.getCodeBuilder().constant(identifier);
+              context.setCode(id, code);
+              context.next_token();
+            }
         }
 
         @Override
@@ -152,20 +177,27 @@
 
     public class RuleRequireExpression implements Rule {
 
-        private final String id;
+        private String id;
 
-        public RuleRequireExpression(String id) {
-            this.id = id;
+        public RuleRequireExpression() {
         }
 
         @Override
+        public Rule capture_as(String... ids) {
+          this.id = ids[0];
+          return this;
+        }
+
+        @Override
         public void parse(StatementContext context) {
             context.trace(this.toString()+".parse");
             Code code = context.parse_expression();
             if (code == null) {
                 throw new ExpressionSyntaxException(context.getLexicalAnalyzer());
             }
-            context.setCode(id, code);
+            if( this.id!=null ) {
+              context.setCode(id, code);
+            }
         }
 
         @Override
@@ -186,6 +218,11 @@
         }
 
         @Override
+        public Rule capture_as(String... ids) {
+          throw new UnsupportedOperationException("Unsupported operation.");
+        }
+
+        @Override
         public void parse(StatementContext context) {
             context.trace(this.toString()+".parse");
             if( this.value instanceof Code ) {
@@ -203,28 +240,35 @@
 
     public class RuleRequiereExpressions implements Rule {
 
-        private final String id;
+        private String id;
         private final String separator;
 
-        public RuleRequiereExpressions(String id, String separator) {
-            this.id = id;
+        public RuleRequiereExpressions(String separator) {
             this.separator = separator;
         }
 
         @Override
+        public Rule capture_as(String... ids) {
+          this.id = ids[0];
+          return this;
+        }
+        
+        @Override
         public void parse(StatementContext context) {
             context.trace(this.toString()+".parse");
             Codes codes = context.parse_expressions(this.separator);
             if (codes == null) {
                 throw new ExpressionSyntaxException(context.getLexicalAnalyzer());
             }
-            Code code;
-            if( codes.size()==1 ) {
-                code = codes.get(0);
-            } else {
-                code = context.getCodeBuilder().function(CodeBlockFunction.NAME, codes);
+            if( this.id!=null ) {
+              Code code;
+              if( codes.size()==1 ) {
+                  code = codes.get(0);
+              } else {
+                  code = context.getCodeBuilder().function(CodeBlockFunction.NAME, codes);
+              }
+              context.setCode(id, code);
             }
-            context.setCode(id, code);
         }
 
         @Override
@@ -243,6 +287,11 @@
             this.onFalseRules = new ArrayList<>();
         }
 
+        @Override
+        public ConditionalRule capture_as(String... ids) {
+          throw new UnsupportedOperationException("Operation not suppted.");
+        }
+
         protected void parseOnTrueRules(StatementContext context) {
             for (Rule rule : this.onTrueRules) {
                 rule.parse(context);
@@ -274,6 +323,7 @@
         private final String[] optional_token;
         private final List<Rule> onTrueRules;
         private final List<Rule> onFalseRules;
+        private String id;
 
         public RuleOptionalAnyToken(String... optional_token) {
             this.optional_token = optional_token;
@@ -282,10 +332,20 @@
         }
 
         @Override
+        public ConditionalRule capture_as(String... ids) {
+          this.id = ids[0];
+          return this;
+        }
+        
+        @Override
         public void parse(StatementContext context) {
             context.trace(this.toString()+".parse");
             Token token = context.look_token();
             if (token.is(this.optional_token)) {
+                if( this.id!=null ) {
+                  Code code = context.getCodeBuilder().constant(token.getValue());
+                  context.setCode(id, code);
+                }
                 context.next_token();
                 for (Rule rule : this.onTrueRules) {
                     rule.parse(context);
@@ -325,6 +385,11 @@
         }
 
         @Override
+        public Rule capture_as(String... ids) {
+          throw new UnsupportedOperationException("Unsupported operation.");
+        }
+
+        @Override
         public void parse(StatementContext context) {
             context.trace(this.toString()+".parse");
             Token token = context.look_token();
@@ -374,6 +439,11 @@
         }
 
         @Override
+        public Rule capture_as(String... ids) {
+          throw new UnsupportedOperationException("Operation not suppted.");
+        }
+        
+        @Override
         public void parse(StatementContext context) {
             context.trace(this.toString()+".parse");
             String save = context.getCodeClassifier();
@@ -421,8 +491,13 @@
         public RuleRepeat() {
             this.rules = new ArrayList<>();
         }
-
+        
         @Override
+        public Rule capture_as(String... ids) {
+          throw new UnsupportedOperationException("Operation not suppted.");
+        }
+        
+        @Override
         public void parse(StatementContext context) {
             context.trace(this.toString()+".parse");
             String save = context.getCodeClassifier();
@@ -474,6 +549,11 @@
         }
 
         @Override
+        public Rule capture_as(String... ids) {
+          throw new UnsupportedOperationException("Operation not suppted.");
+        }
+
+        @Override
         public void parse(StatementContext context) {
           context.trace(this.toString()+".parse");
           throw new BreakLoopException();
@@ -485,18 +565,45 @@
         }
     }
 
+        public static class RuleFail implements Rule {
+
+        public RuleFail() {
+        }
+
+        @Override
+        public Rule capture_as(String... ids) {
+          throw new UnsupportedOperationException("Operation not suppted.");
+        }
+
+        @Override
+        public void parse(StatementContext context) {
+          context.trace(this.toString()+".parse");
+          throw new ExpressionSyntaxException();
+        }
+
+        @Override
+        public String toString() {
+            return "fail()";
+        }
+    }
+
     public class RuleOptionalIdentifiers extends AbstractConditionalRule implements ConditionalRule {
 
-        private final String id;
+        private String id;
         private final String separator;
 
-        public RuleOptionalIdentifiers(String id, String separator) {
+        public RuleOptionalIdentifiers(String separator) {
             super();
-            this.id = id;
             this.separator = separator;
         }
 
         @Override
+        public ConditionalRule capture_as(String... ids) {
+          this.id = ids[0];
+          return this;
+        }
+
+        @Override
         public void parse(StatementContext context) {
             context.trace(this.toString()+".parse");
             BaseCodes args = (BaseCodes) context.getCodeBuilder().args();
@@ -517,9 +624,11 @@
                 token = context.look_token();
             }
             if (args.size() != 0) {
+              if( this.id!=null ) {
                 Code code = context.getCodeBuilder().function(FUNCTION_LIST, args);
                 context.setCode(id, code);
-                this.parseOnTrueRules(context);
+              }
+              this.parseOnTrueRules(context);
             } else {
                 this.parseOnFalseRules(context);
             }
@@ -533,16 +642,21 @@
 
     public class RuleRequireIdentifiers implements Rule {
 
-        private final String id;
+        private String id;
         private final String separator;
 
-        public RuleRequireIdentifiers(String id, String separator) {
+        public RuleRequireIdentifiers(String separator) {
             super();
-            this.id = id;
             this.separator = separator;
         }
 
         @Override
+        public Rule capture_as(String... ids) {
+          this.id = ids[0];
+          return this;
+        }
+
+        @Override
         public void parse(StatementContext context) {
             context.trace(this.toString()+".parse");
             BaseCodes args = (BaseCodes) context.getCodeBuilder().args();
@@ -568,8 +682,10 @@
                         context.getLexicalAnalyzer()
                 );
             }
-            Code code = context.getCodeBuilder().function(FUNCTION_LIST, args);
-            context.setCode(id, code);
+            if( this.id!=null ) {
+              Code code = context.getCodeBuilder().function(FUNCTION_LIST, args);
+              context.setCode(id, code);
+            }
         }
 
         @Override
@@ -580,22 +696,29 @@
 
     public class RuleOptionalLiteralString extends AbstractConditionalRule implements ConditionalRule {
 
-        private final String id;
+        private String id;
 
-        public RuleOptionalLiteralString(String id) {
+        public RuleOptionalLiteralString() {
             super();
-            this.id = id;
         }
 
         @Override
+        public ConditionalRule capture_as(String... ids) {
+          this.id = ids[0];
+          return this;
+        }
+        
+        @Override
         public void parse(StatementContext context) {
             context.trace(this.toString()+".parse");
             Token token = context.look_token();
             if (token.getType() == Token.STRING_LITERAL) {
-                String s = (String) token.getValue();
-                Code code = context.getCodeBuilder().constant(s);
-                context.setCode(id, code);
-                context.next_token();
+                if( this.id!=null ) {
+                  String s = (String) token.getValue();
+                  Code code = context.getCodeBuilder().constant(s);
+                  context.setCode(id, code);
+                  context.next_token();
+                }
                 this.parseOnTrueRules(context);
             } else {
                 this.parseOnFalseRules(context);
@@ -634,7 +757,7 @@
                         if (code == null) {
                             break;
                         }
-                        args.add(argNameX, code);
+                        args.add(code);
                         n++;
                     }
                 } else {
@@ -642,13 +765,58 @@
 //                    if( code == null) {
 //                        code = context.getCodeBuilder().constant(null);
 //                    }
-                    args.add(argName,code);
+                    args.add(code);
                 }
             }
             return args;
         }
     }
 
+    public static class FixedArgsBuilderFromNames implements ArgsBuilder {
+
+        protected final String[] argNames;
+
+        public FixedArgsBuilderFromNames(String... argNames) {
+            this.argNames = argNames;
+        }
+
+        @Override
+        public String toString() {
+          return "fixed_args_names("+StringUtils.join(argNames,",")+")";
+        }
+        
+        @Override
+        public Codes build(StatementContext context) {
+            context.trace(this.toString()+".build");
+            CodeBuilder codeBuilder = context.getCodeBuilder();
+            BaseCodes args = (BaseCodes) codeBuilder.args();
+            for (String argName : argNames) {
+                if (argName.contains("#")) {
+                    int n = 1;
+                    BaseCodes argsX = (BaseCodes) codeBuilder.args();
+                    while (true) {
+                        String argNameX = StringUtils.replace(argName, "#", String.valueOf(n).trim(), 1);
+                        Code code = context.getCode(argNameX);
+                        if (code == null) {
+                            break;
+                        }
+                        argsX.add(code);
+                        n++;
+                    }
+                    
+                    args.add(codeBuilder.tuple(argsX));
+                } else {
+                    Code code = context.getCode(argName);
+                    if( code == null) {
+                        code = context.getCodeBuilder().constant(null);
+                    }
+                    args.add(code);
+                }
+            }
+            return args;
+        }
+    }
+
     public static class ArgsBuilderExpand extends ArgsBuilderFromNames {
 
         public ArgsBuilderExpand(String... argNames) {
@@ -745,18 +913,18 @@
     }
 
     @Override
-    public Rule require_identifier(String id) {
-        return new RuleRequireIdentifier(id);
+    public Rule require_identifier() {
+        return new RuleRequireIdentifier();
     }
 
     @Override
-    public Rule require_identifiers(String id, String sep) {
-        return new RuleRequireIdentifiers(id, sep);
+    public Rule require_identifiers(String sep) {
+        return new RuleRequireIdentifiers(sep);
     }
 
     @Override
-    public Rule require_literal_string(String id) {
-        return new RuleRequireLiteralString(id);
+    public Rule require_literal_string() {
+        return new RuleRequireLiteralString();
     }
 
     @Override
@@ -765,13 +933,13 @@
     }
 
     @Override
-    public Rule require_expression(String id) {
-        return new RuleRequireExpression(id);
+    public Rule require_expression() {
+        return new RuleRequireExpression();
     }
 
     @Override
-    public Rule require_expressions(String id, String separator) {
-        return new RuleRequiereExpressions(id, separator);
+    public Rule require_expressions(String separator) {
+        return new RuleRequiereExpressions(separator);
     }
 
     @Override
@@ -780,13 +948,13 @@
     }
 
     @Override
-    public ConditionalRule optional_identifiers(String id, String separator) {
-        return new RuleOptionalIdentifiers(id, separator);
+    public ConditionalRule optional_identifiers(String separator) {
+        return new RuleOptionalIdentifiers(separator);
     }
 
     @Override
-    public ConditionalRule optional_literal_string(String id) {
-        return new RuleOptionalLiteralString(id);
+    public ConditionalRule optional_literal_string() {
+        return new RuleOptionalLiteralString();
     }
 
     @Override
@@ -806,23 +974,28 @@
             return false;
         }
         context.trace(this.getName()+".isApplicable");
-        AbstractLexicalAnalyzer lexer = (AbstractLexicalAnalyzer) context.getLexicalAnalyzer();
-        lexer.push_state();
+        context.save_state();
         try {
-          boolean applicable = false;
           for (Rule rule : rules) {
-            if (!(rule instanceof IsApplicableRule)) {
-                break;
+            if( rule == null ) {
+              continue;
             }
-            if( !((IsApplicableRule) rule).isApplicable(context) ) {
-              return false;
+            if (rule instanceof IsApplicableRule) {
+              if( !((IsApplicableRule) rule).isApplicable(context) ) {
+                context.trace(this.getName()+".isApplicable return false");
+                return false;
+              }
+            } else {
+              rule.parse(context);
             }
-            applicable = true;
           }
-          context.trace(this.getName()+".isApplicable return "+applicable);
-          return applicable;
+          context.trace(this.getName()+".isApplicable return true");
+          return true;
+        } catch(Exception ex) {
+          context.trace(this.getName()+".isApplicable return false (error)");
+          return false;
         } finally {
-          lexer.pop_state();
+          context.restore_state();
         }
     }
 
@@ -833,6 +1006,12 @@
     }
     
     @Override
+    public ArgsBuilder fixed_args_names(String... args) {
+        ArgsBuilder x = new FixedArgsBuilderFromNames(args);
+        return x;
+    }
+    
+    @Override
     public ArgsBuilder args_expand(String... args) {
         ArgsBuilder x = new ArgsBuilderExpand(args);
         return x;
@@ -871,6 +1050,11 @@
     }
 
     @Override
+    public Rule fail() {
+      return new RuleFail();
+    }
+
+    @Override
     public Rule break_loop() {
       return new RuleBreakLoop();
     }
