From: <jbo...@li...> - 2006-01-24 13:53:08
|
Author: mic...@jb... Date: 2006-01-24 08:52:33 -0500 (Tue, 24 Jan 2006) New Revision: 2191 Added: trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/ trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/DefaultExpander.java trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/ trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/Chunk.java trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/NLExpressionCompiler.java trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/NLGrammar.java trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/NLMappingItem.java trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/TemplateContext.java trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/TemplateFactory.java trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/ExpanderContextTest.java trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/cheese-rules.drl trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/cheese.dsl.properties trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/dsl/ trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/dsl/template/ trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/dsl/template/NLExpressionCompilerTest.java trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/dsl/template/NLGrammarTest.java trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/dsl/template/TemplateContextTest.java trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/dsl/template/TemplateFactoryTest.java Removed: trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/rule-with-expansion.drl Modified: trunk/labs/jbossrules/drools-core/pom.xml trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/ExpanderContext.java trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/Parser.java trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/ParserTest.java Log: first cut of expander with natural rules Modified: trunk/labs/jbossrules/drools-core/pom.xml =================================================================== --- trunk/labs/jbossrules/drools-core/pom.xml 2006-01-24 12:46:23 UTC (rev 2190) +++ trunk/labs/jbossrules/drools-core/pom.xml 2006-01-24 13:52:33 UTC (rev 2191) @@ -34,7 +34,17 @@ <groupId>jparsec</groupId> <artifactId>jparsec</artifactId> <version>0.01</version> - </dependency> + </dependency> + + <!-- only needed for the default expander in lang.. remove when it is refactored. --> + <dependency> + <groupId>commons-lang</groupId> + <artifactId>commons-lang</artifactId> + <version>2.1</version> + </dependency> + </dependencies> + + </project> \ No newline at end of file Modified: trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/ExpanderContext.java =================================================================== --- trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/ExpanderContext.java 2006-01-24 12:46:23 UTC (rev 2190) +++ trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/ExpanderContext.java 2006-01-24 13:52:33 UTC (rev 2191) @@ -1,5 +1,6 @@ package org.drools.lang; +import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.net.URL; @@ -67,7 +68,7 @@ registerExpander(expander, expanderName); return expander; - } catch (Exception e) { + } catch (IOException e) { throw new IllegalArgumentException("Unable to load expander with name: " + expanderName); } } Modified: trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/Parser.java =================================================================== --- trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/Parser.java 2006-01-24 12:46:23 UTC (rev 2190) +++ trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/Parser.java 2006-01-24 13:52:33 UTC (rev 2191) @@ -303,11 +303,12 @@ if ( line == null ) { throw new ParseException( "end expected", lineNumber ); } - if ( line.trim().equals( "end" ) ) { + String trimLine = line.trim(); + if ( trimLine.equals( "end" ) ) { break; } consumeDiscard(); - line = maybeExpand( line ); + line = maybeExpand( trimLine ); consequence.append( line + "\n" ); } System.err.println( "begin consequence"); Added: trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/DefaultExpander.java =================================================================== --- trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/DefaultExpander.java 2006-01-24 12:46:23 UTC (rev 2190) +++ trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/DefaultExpander.java 2006-01-24 13:52:33 UTC (rev 2191) @@ -0,0 +1,40 @@ +package org.drools.lang.dsl; + +import java.util.Properties; + +import org.drools.lang.Expander; +import org.drools.lang.Parser; +import org.drools.lang.dsl.template.NLExpressionCompiler; +import org.drools.lang.dsl.template.NLGrammar; + +/** + * The default expander uses String templates to provide pseudo natural language, + * as well as general DSLs. + * + * For most people, this should do the job just fine. + * TODO: refactor out the template stuff into the natural module. + */ +public class DefaultExpander + implements + Expander { + + private NLExpressionCompiler compiler; + + public String expand(String pattern, + Parser context) { + return compiler.compile(pattern); + } + + /** + * Properties contain the mapping between the language expressions, and the target expressions. + * Use {0} style notation to place "holes" where data will be parsed from the natural text input. + * + * @see org.drools.lang.dsl.template.NLExpressionCompiler for details. + */ + public DefaultExpander(Properties props) { + NLGrammar grammar = new NLGrammar(); + grammar.loadFromProperties(props); + compiler = new NLExpressionCompiler(grammar); + } + +} Property changes on: trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/DefaultExpander.java ___________________________________________________________________ Name: svn:eol-style + native Added: trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/Chunk.java =================================================================== --- trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/Chunk.java 2006-01-24 12:46:23 UTC (rev 2190) +++ trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/Chunk.java 2006-01-24 13:52:33 UTC (rev 2191) @@ -0,0 +1,112 @@ +package org.drools.lang.dsl.template; + +import java.util.Map; + +import org.apache.commons.lang.StringUtils; + +/** + * This holds a linked list of chunks of natural language. + * A chunk is basically some text, which is delimited by "holes" in the template. + * + * eg: "this is {0} an {1} expression" + * Would have 5 chunks: "this is", "{0}", "an", "{1}" and "expression". + * + * Chunks also know how to parse themselves to work out the value. + * + * This is used by TemplateContext. + * This class is very recursive, to be prepated to be confused. + * + * @author <a href="mailto:mic...@gm..."> Michael Neale</a> + */ +class Chunk { + + //the chunk of text from the dicitonary + final String text; + final boolean isHole; + + Chunk next; + + //for building the substitution string, remember how the {0} was spaced with the rest of the string + //for example: date of '{0}' ---- this needs to be handled as well as: date of ' {0} ' + String padL = ""; + String padR = ""; + + //value parsed out if it is a hole, starts out as null. + String value; + + Chunk(String text) { + this.text = text; + if (text.startsWith("{")) { + isHole = true; + } else { + isHole = false; + } + } + + /** + * This will build up a key to use to substitute the original string with. + * Can then swap it with the target text. + */ + void buildSubtitutionKey(StringBuffer buffer) { + if (isHole) { + buffer.append(padL + value + padR); + } else { + buffer.append(text); + } + if (next != null) { + next.buildSubtitutionKey(buffer); + } + } + + void process(String expression) { + if (isHole) { + //value = text until next next.text is found + if (next == null || next.text == null) { + storeSpacePadding( expression ); + value = expression.trim(); + } else { + String val = StringUtils.substringBefore(expression, next.text); + storeSpacePadding( val ); + value = val.trim(); + } + + } else { + value = text; + } + if (next != null) { + next.process(StringUtils.substringAfter(expression, value)); + } + } + + private void storeSpacePadding(String val) { + if (val.startsWith(" ")) padL = " "; + if (val.endsWith(" ")) padR = " "; + } + + void buildValueMap(Map map) { + if (isHole) { + map.put(text, value); + } + if (next != null) { + next.buildValueMap(map); + } + } + + void addToEnd(Chunk chunk) { + if (next == null) { + next = chunk; + } else { + next.addToEnd(chunk); + } + } + + + /** recursively reset the values */ + public void clearValues() { + this.value = null; + if (this.next != null) { + next.clearValues(); + } + } + +} Property changes on: trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/Chunk.java ___________________________________________________________________ Name: svn:eol-style + native Added: trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/NLExpressionCompiler.java =================================================================== --- trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/NLExpressionCompiler.java 2006-01-24 12:46:23 UTC (rev 2190) +++ trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/NLExpressionCompiler.java 2006-01-24 13:52:33 UTC (rev 2191) @@ -0,0 +1,63 @@ +package org.drools.lang.dsl.template; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * This is the utility class for compiling pseudo natural/DSL expression into the target + * language, via the supplied mappings. + * + * This version works off "string templates" rather then infix operators. + * + * Note that this is not particularly efficient for large grammars - IN THEORY ! + * However, I have tested it with grammars of 200 000 terms, and it took less then a second per expression, + * so its no slouch. This could be a problem for bulk compiling of large rulesets with thousands of conditions. + * + * In general, grammars of < 1000 items should be fine. The cost is a parse time cost for Drools, which can be done + * incrementally in a suitable environment ideally anyway. + * + * It will go through each item in the grammar, trying to apply it regardless of if it is needed. + * This may be improved by some early regex scanning, but most likely this will not really save much. + * + * @author <a href="mailto:mic...@gm..."> Michael Neale</a> + */ +public class NLExpressionCompiler { + + //a map of templates to the template contexts (template contexts are used to populate populate + //the target mappings with data from a real expression. + private Map templateCache; + + + public NLExpressionCompiler(NLGrammar grammar) { + Collection grammarItems = grammar.getMappings(); + this.templateCache = new HashMap(); + + //build up a map of templates + TemplateFactory factory = new TemplateFactory(); + for ( Iterator iter = grammarItems.iterator(); iter.hasNext(); ) { + NLMappingItem mapping = (NLMappingItem) iter.next(); + TemplateContext ctx = factory.getContext(mapping.getNaturalTemplate()); + templateCache.put(mapping, ctx); + } + } + + /** + * This will iterate through the grammar, trying to match any grammar templates with the expression. + * When it can, it will pull the values out of the expression, put them in the target string, and then swap it out with + * the original, and then move on to the next item from the grammar/dictionary. + */ + public String compile(String expression) { + String nl = expression; + for ( Iterator iter = templateCache.entrySet().iterator(); iter.hasNext(); ) { + Map.Entry entry = (Map.Entry) iter.next(); + NLMappingItem mapping = (NLMappingItem) entry.getKey(); + TemplateContext ctx = (TemplateContext) entry.getValue(); + nl = ctx.processAllInstances(nl, mapping.getGrammarTemplate()); + } + return nl; + } + + +} Property changes on: trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/NLExpressionCompiler.java ___________________________________________________________________ Name: svn:eol-style + native Added: trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/NLGrammar.java =================================================================== --- trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/NLGrammar.java 2006-01-24 12:46:23 UTC (rev 2190) +++ trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/NLGrammar.java 2006-01-24 13:52:33 UTC (rev 2191) @@ -0,0 +1,51 @@ +package org.drools.lang.dsl.template; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; + +/** This represents a simple grammar mapping. */ +public class NLGrammar + implements + Serializable { + + + private static final long serialVersionUID = 1L; + private List mappings = new ArrayList(); + + public NLGrammar() { + } + + + /** + * When loading from properties, the order in which they appear in the props file + * is the priority. (TODO: order means nothing to properties it seems). + * + * Which makes sense intuitively, the order you read them it the order in which they will be applied. + */ + public void loadFromProperties(Properties props) { + int i = props.size(); + for ( Iterator iter = props.keySet().iterator(); iter.hasNext(); ) { + String key = (String) iter.next(); + String property = props.getProperty(key); + NLMappingItem item = new NLMappingItem(i, key, property); + addNLItem(item); + i--; + } + } + + public void addNLItem(NLMappingItem item) { + this.mappings.add(item); + } + + public Collection getMappings() { + Collections.sort(mappings); + return Collections.unmodifiableCollection(mappings); + } + + +} Property changes on: trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/NLGrammar.java ___________________________________________________________________ Name: svn:eol-style + native Added: trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/NLMappingItem.java =================================================================== --- trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/NLMappingItem.java 2006-01-24 12:46:23 UTC (rev 2190) +++ trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/NLMappingItem.java 2006-01-24 13:52:33 UTC (rev 2191) @@ -0,0 +1,58 @@ +package org.drools.lang.dsl.template; + +import java.io.Serializable; + +/** + * This contains a single mapping from psuedo NL to a grammarTemplate. + * + * @author <a href="mailto:mic...@gm..."> Michael Neale</a> + * + */ +public class NLMappingItem + implements + Comparable, Serializable { + + + private static final long serialVersionUID = 7185580607729787497L; + + private int priority = 0; + + private String naturalTemplate; + private String grammarTemplate; + + public NLMappingItem(int priority, + String naturalTemplate, + String grammarTemplate) { + this.priority = priority; + this.naturalTemplate = naturalTemplate; + this.grammarTemplate = grammarTemplate; + } + + public String getNaturalTemplate() { + return naturalTemplate; + } + + public String getGrammarTemplate() { + return grammarTemplate; + } + + public int compareTo(Object arg) { + if ( arg instanceof NLMappingItem ) { + NLMappingItem item = (NLMappingItem) arg; + if ( item.priority == this.priority ) return 0; + if ( item.priority > this.priority ) return -1; + if ( item.priority < this.priority ) return 1; + return 0; + } + else { + return 0; + } + } + + int getPriority() { + return this.priority; + } + + + +} Property changes on: trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/NLMappingItem.java ___________________________________________________________________ Name: svn:eol-style + native Added: trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/TemplateContext.java =================================================================== --- trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/TemplateContext.java 2006-01-24 12:46:23 UTC (rev 2190) +++ trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/TemplateContext.java 2006-01-24 13:52:33 UTC (rev 2191) @@ -0,0 +1,130 @@ +package org.drools.lang.dsl.template; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.apache.commons.lang.StringUtils; + + +/** + * This class takes a linked list of Chunk objects, and will replace what the chunks represent + * in an nl string with a interpolated grammar template. + * The values are obtained by matching the chunks with the nl. + * + * @author <a href="mailto:mic...@gm..."> Michael Neale</a> + * This is an alternative approach to the infix parser. + */ +class TemplateContext { + + //the start of the linked list. + Chunk start; + + /** + * Ad a chunk from the dictionary expression. + * A chunk is a piece of nl, or a hole. + * nl & holes must not be mixed. + */ + TemplateContext addChunk(String chunkText) { + Chunk chunk = new Chunk(chunkText); + if (start == null) { + start = chunk; + } else { + start.addToEnd(chunk); + } + return this; + } + + /** + * This will parse the input nl expression, and build a map of values for the "holes" + * in the grammar expression. + * It does this by getting the Chunks of the grammar to parse themselves. + */ + void processNL(String nl, Map map) { + start.clearValues(); + start.process(nl); + start.buildValueMap(map); + } + + /** + * This builds a fragment of the nl expression which can be used + * to swap out a piece of the original with the target expression. + * + * The target expression is the "right hand side" of the grammar map. + */ + String getSubstitutionKey() { + StringBuffer buffer = new StringBuffer(); + start.buildSubtitutionKey(buffer); + return buffer.toString().trim(); //trim so we don't get any erroneous spaces to stop replacing. + } + + /** + * This will build the target string that you can use to substitute the original with. + * @param map The map of values to hole keys. + * @param grammar_r The grammar item which will have the values plugged into the "holes". + * @return The final expression ready for substitution. + */ + String populateTargetString(Map map, + String grammar_r) { + for ( Iterator iter = map.keySet().iterator(); iter.hasNext(); ) { + String key = (String) iter.next(); + grammar_r = StringUtils.replace(grammar_r, key, (String) map.get(key)); + } + return grammar_r; + } + + /** + * @param nl The natural language expression. + * @param subKey The part of the nl expression to be swapped out. + * @param target The chunk to be swapped in to the nl + * @return The nl with the chunk replaced with the target. + */ + String interpolate(String nl, String subKey, String target) { + return StringUtils.replace(nl, subKey, target); + } + + /** + * This does it all as one call. Requires that chunks have been setup. + * @param nl The nl expression to process. + * @param grammarTemplate The grammar expression that will be interpolated (with the values from the original chunks), + * and then inserted in to the nl. + * @return the NL with the populated grammarRHS replacing the original pattern (from the chunks). + */ + public String process(String nl, String grammarTemplate) { + Map values = new HashMap(); + this.processNL(nl, values); + String subKey = this.getSubstitutionKey(); + String target = this.populateTargetString(values, grammarTemplate); + return this.interpolate(nl, subKey, target); + } + + + /** + * Similar to process, but processes iteratively until there is + * no change in the output. This allows for stuff to be repeated in an NL expression. + */ + public String processAllInstances(String nl, String grammarTemplate) { + String result = nl; + + //put an upper limit + int i = 0; + while (i < 10) { + String newResult = process(result, grammarTemplate); + if (newResult.equals(result)) { + break; + } + result = newResult; + + i++; + if (i == 10) { + throw new IllegalArgumentException("To many iterations in processing the expression: [" + + nl + + "] with target template: [" + + grammarTemplate + "]"); + } + } + return result; + } + + +} Property changes on: trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/TemplateContext.java ___________________________________________________________________ Name: svn:eol-style + native Added: trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/TemplateFactory.java =================================================================== --- trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/TemplateFactory.java 2006-01-24 12:46:23 UTC (rev 2190) +++ trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/TemplateFactory.java 2006-01-24 13:52:33 UTC (rev 2191) @@ -0,0 +1,95 @@ +package org.drools.lang.dsl.template; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * This takes a grammar template (the left hand side of a grammar mapping table) + * and builds a TemplateContext for it. + * + * Uses a built in lexer. + * + * @author <a href="mailto:mic...@gm..."> Michael Neale</a> + */ +class TemplateFactory { + + /** + * This will lex the template string into chunks. + * @param template From the grammar. eg "{0} likes cheese" is a template. + * @return A template context ready to apply to a nl expression. + */ + public TemplateContext getContext(String template) { + + TemplateContext ctx = new TemplateContext(); + + List chunkList = lexChunks(template); + for ( Iterator iter = chunkList.iterator(); iter.hasNext(); ) { + ctx.addChunk((String) iter.next()); + + } + return ctx; + } + + + List lexChunks(String grammarTemplate) { + ChunkLexer lexer = new ChunkLexer(); + return lexer.lex(grammarTemplate); + } + + + /** + * Lex out chunks. + * @author <a href="mailto:mic...@gm..."> Michael Neale</a> + */ + static class ChunkLexer { + + private List chunks = new ArrayList(); + + private StringBuffer buffer = new StringBuffer(); + + public List lex(String grammarTemplate) { + + char[] chars = grammarTemplate.toCharArray(); + + for ( int i = 0; i < chars.length; i++ ) { + switch ( chars[i] ) { + case '{' : + startHole(); + break; + case '}' : + endHole(); + break; + default : + buffer.append(chars[i]); + break; + } + } + String buf = this.buffer.toString(); + if (!buf.equals("")) addChunk( buf ); + return this.chunks; + + } + + private boolean addChunk(String buf) { + return this.chunks.add( buf.trim() ); + } + + private void endHole() { + String buf = this.buffer.toString(); + chunks.add("{" + buf + "}"); + this.buffer = new StringBuffer(); + } + + private void startHole() { + String buf = this.buffer.toString(); + if (!buf.equals("")) { + addChunk( buf ); + } + this.buffer = new StringBuffer(); + } + + + } + +} Property changes on: trunk/labs/jbossrules/drools-core/src/main/java/org/drools/lang/dsl/template/TemplateFactory.java ___________________________________________________________________ Name: svn:eol-style + native Added: trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/ExpanderContextTest.java =================================================================== --- trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/ExpanderContextTest.java 2006-01-24 12:46:23 UTC (rev 2190) +++ trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/ExpanderContextTest.java 2006-01-24 13:52:33 UTC (rev 2191) @@ -0,0 +1,36 @@ +package org.drools.lang; + +import junit.framework.TestCase; + +public class ExpanderContextTest extends TestCase { + + public void testExpander() { + ExpanderContext ctx = ExpanderContext.getInstance(); + ctx.registerExpander(new MockExpander("mock1"), "mock1"); + MockExpander exp = (MockExpander) ctx.getExpander("mock1"); + assertEquals("mock1", exp.returnVal); + + //test re-register + ctx.registerExpander(new MockExpander("mock2"), "mock1"); + exp = (MockExpander) ctx.getExpander("mock1"); + assertEquals("mock2", exp.returnVal); + + } + + static class MockExpander implements Expander { + + String returnVal; + + MockExpander(String val) { + returnVal = val; + } + + public String expand(String pattern, + Parser context) { + + return null; + } + + } + +} Property changes on: trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/ExpanderContextTest.java ___________________________________________________________________ Name: svn:eol-style + native Modified: trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/ParserTest.java =================================================================== --- trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/ParserTest.java 2006-01-24 12:46:23 UTC (rev 2190) +++ trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/ParserTest.java 2006-01-24 13:52:33 UTC (rev 2191) @@ -53,8 +53,10 @@ } public void test_with_expander() throws Exception { - Parser parser = parser( "rule-with-expansion.drl" ); + Parser parser = parser( "cheese-rules.drl" ); parser.parse(); + assertEquals("cheese_rules", ((Rule)parser.getRules().get(0)).getName() ); + } protected Parser parser(String name) { Added: trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/cheese-rules.drl =================================================================== --- trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/cheese-rules.drl 2006-01-24 12:46:23 UTC (rev 2190) +++ trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/cheese-rules.drl 2006-01-24 13:52:33 UTC (rev 2191) @@ -0,0 +1,22 @@ +# Example DRL with expansion +# (ie "domain specific language") + +# declare expanders for domain specific and natural language extensions +use expander cheese.dsl.properties +#this one is just a simple properties file + + +rule cheese_rules + when + Bob is in atlanta + Bob likes cheese + #There exists a Guest with name of "Michael" and sex of "Male" + >Guest( name == seatingRightGuestName, rightGuestSex:sex, rightGuestHobby:hobby ) + >Guest( leftGuestName:name , sex != rightGuestSex, hobby == rightGuestHobby ) + + #count => Count() can be replaced by + bind count to Count + then + Send notification to Mark with message "hello" + >System.out.println("and this is code") +end \ No newline at end of file Property changes on: trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/cheese-rules.drl ___________________________________________________________________ Name: svn:eol-style + native Added: trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/cheese.dsl.properties =================================================================== --- trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/cheese.dsl.properties 2006-01-24 12:46:23 UTC (rev 2190) +++ trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/cheese.dsl.properties 2006-01-24 13:52:33 UTC (rev 2191) @@ -0,0 +1,6 @@ +#this is an little dsl + +Bob\ is\ in\ {0}=Bob(location={0}) +{Person}\ likes\ cheese={Person}(cheeseFan==true) +bind\ {var}\ to\ {object}={var} => {object}() +Send\ notification\ to\ {Person}\ with\ message\ {message}=EmailUtil.sendEmail("{Person}", {message}) \ No newline at end of file Property changes on: trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/cheese.dsl.properties ___________________________________________________________________ Name: svn:eol-style + native Added: trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/dsl/template/NLExpressionCompilerTest.java =================================================================== --- trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/dsl/template/NLExpressionCompilerTest.java 2006-01-24 12:46:23 UTC (rev 2190) +++ trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/dsl/template/NLExpressionCompilerTest.java 2006-01-24 13:52:33 UTC (rev 2191) @@ -0,0 +1,103 @@ +package org.drools.lang.dsl.template; + +import java.util.Properties; + +import junit.framework.TestCase; + +public class NLExpressionCompilerTest extends TestCase { + + public void testIntegration() { + NLGrammar grammar = new NLGrammar(); + grammar.addNLItem(new NLMappingItem(0, "{0} likes cheese", "likesCheese({0})")); + + NLExpressionCompiler compiler = new NLExpressionCompiler(grammar); + String result = compiler.compile("bob likes cheese"); + + assertEquals("likesCheese(bob)", result); + + //now lets use a properties + Properties props = new Properties(); + props.setProperty("{0} likes cheese", "likesCheese({0})"); + props.setProperty("the date between {0} and {1}", "dateCompare({0}, {1})"); + props.setProperty("bind", "=>"); + + grammar = new NLGrammar(); + grammar.loadFromProperties(props); + compiler = new NLExpressionCompiler(grammar); + result = compiler.compile("bob likes cheese"); + + assertEquals("likesCheese(bob)", result); + + result = compiler.compile("the date between bob and michael"); + assertEquals("dateCompare(bob, michael)", result); + + result = compiler.compile("bind"); + assertEquals("=>", result); + + } + + public void testLargeGrammar() { + Properties props = new Properties(); + + for (int i = 0; i < 1000; i++ ) { + props.put("some {0} grammar" + i, "some mapping{0}"); + if (i == 42) { + props.put("{0} likes cheese", "{0}.likesCheese()"); + props.put("{0} is happy", "{0}.isHappy()"); + } + } + + + + NLGrammar grammar = new NLGrammar(); + grammar.loadFromProperties(props); + + NLExpressionCompiler compiler = new NLExpressionCompiler(grammar); + + long start = System.currentTimeMillis(); + String result = compiler.compile("michael likes cheese and michael is happy"); + long runtime = System.currentTimeMillis() - start; + System.out.println("Runtime for compile with dictionary of 1000: " + runtime + "ms"); + assertEquals("michael.likesCheese() and michael.isHappy()", result); + + } + + + public void testNestingAndOrderOfExpressions() { + NLGrammar grammar = new NLGrammar(); + + grammar.addNLItem(new NLMappingItem(0, "{0} likes cheese", "{0}.likesCheese()" )); + grammar.addNLItem(new NLMappingItem(1, "print out cheese fan status {0}", "print({0})" )); + + + NLExpressionCompiler compiler = new NLExpressionCompiler(grammar); + String nl = "print out cheese fan status bob likes cheese"; + String expected = "print(bob.likesCheese())"; + + String result = compiler.compile(nl); + + assertEquals(expected, result); + + + grammar = new NLGrammar(); + + grammar.addNLItem(new NLMappingItem(1, "date of '{0}'", "dateOf({0})")); + grammar.addNLItem(new NLMappingItem(2, "age of [ {0} ]", "{0}.getAge()")); + grammar.addNLItem(new NLMappingItem(3, "Today", "new java.util.Date()")); + grammar.addNLItem(new NLMappingItem(4, "{0} is before {1}", "({0}).compareTo({1}) > 0")); + + nl = "date of '10-jul-2006' is before Today"; + + compiler = new NLExpressionCompiler(grammar); + expected = "(dateOf(10-jul-2006)).compareTo(new java.util.Date()) > 0"; + assertEquals(expected, compiler.compile(nl)); + + nl = "age of [ bob ] < age of [ michael ]"; + expected = "bob.getAge() < michael.getAge()"; + assertEquals(expected, compiler.compile(nl)); + + + + } + +} Property changes on: trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/dsl/template/NLExpressionCompilerTest.java ___________________________________________________________________ Name: svn:eol-style + native Added: trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/dsl/template/NLGrammarTest.java =================================================================== --- trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/dsl/template/NLGrammarTest.java 2006-01-24 12:46:23 UTC (rev 2190) +++ trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/dsl/template/NLGrammarTest.java 2006-01-24 13:52:33 UTC (rev 2191) @@ -0,0 +1,63 @@ +package org.drools.lang.dsl.template; + +import java.io.ByteArrayInputStream; +import java.util.Collection; +import java.util.Properties; + +import junit.framework.TestCase; + +public class NLGrammarTest extends TestCase { + + /** Check that it sets up priorities correctly */ + public void testLoadFromProperties() throws Exception { + NLGrammar grammar = new NLGrammar(); + + + String data = "number\\ 1=number 1\n" + + "number\\ 2=number 2"; + + Properties props = new Properties(); + + ByteArrayInputStream stream = new ByteArrayInputStream(data.getBytes("UTF-8")); + + props.load(stream); + + grammar.loadFromProperties(props); + + Collection list = grammar.getMappings(); + Object[] items = list.toArray(); + + NLMappingItem item = (NLMappingItem) items[0]; + assertEquals("number 1", item.getNaturalTemplate()); + assertEquals(1, item.getPriority()); + + item = (NLMappingItem) items[1]; + assertEquals("number 2", item.getNaturalTemplate()); + assertEquals(2, item.getPriority()); + + props = new Properties(); + props.setProperty("znumber 1", "number 1"); + props.setProperty("bnumber 2", "number 2"); + props.setProperty("anumber 3", "number 3"); + + grammar = new NLGrammar(); + grammar.loadFromProperties(props); + + list = grammar.getMappings(); + items = list.toArray(); + + item = (NLMappingItem) items[0]; + assertEquals("znumber 1", item.getNaturalTemplate()); + assertEquals(1, item.getPriority()); + + item = (NLMappingItem) items[1]; + assertEquals("bnumber 2", item.getNaturalTemplate()); + assertEquals(2, item.getPriority()); + + item = (NLMappingItem) items[2]; + assertEquals("anumber 3", item.getNaturalTemplate()); + assertEquals(3, item.getPriority()); + + } + +} Property changes on: trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/dsl/template/NLGrammarTest.java ___________________________________________________________________ Name: svn:eol-style + native Added: trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/dsl/template/TemplateContextTest.java =================================================================== --- trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/dsl/template/TemplateContextTest.java 2006-01-24 12:46:23 UTC (rev 2190) +++ trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/dsl/template/TemplateContextTest.java 2006-01-24 13:52:33 UTC (rev 2191) @@ -0,0 +1,192 @@ +package org.drools.lang.dsl.template; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.apache.commons.lang.StringUtils; + +import junit.framework.TestCase; + +public class TemplateContextTest extends TestCase { + + + public void testAllInOne() { + TemplateContext ctx = new TemplateContext(); + //chunks represent a lexed grammar "left hand side" + ctx.addChunk("baby on board") + .addChunk("{0}") + .addChunk("and") + .addChunk("{1}") + .addChunk("burt ward"); + String result = ctx.process("yeah this is an expression baby on board exp1 and exp2 burt ward end.", "something({0}, {1})"); + assertEquals("yeah this is an expression something(exp1, exp2) end.", result); + + + //and check that the iterative one is OK. + result = ctx.process("yeah this is an expression baby on board exp1 and exp2 burt ward end.", "something({0}, {1})"); + assertEquals("yeah this is an expression something(exp1, exp2) end.", result); + } + + public void testBuildStrings() { + + TemplateContext ctx = new TemplateContext(); + + //chunks represent a lexed grammar "left hand side" + ctx.addChunk("baby on board") + .addChunk("{0}") + .addChunk("and") + .addChunk("{1}") + .addChunk("burt ward"); + + //and this is the right hand side grammar mapping (no lexing required, simple hole filling !). + String grammar_r = "something({0}, {1})"; + + //and this is the full expression + String nl = "yeah this is an expression baby on board exp1 and exp2 burt ward end."; + + //match the pattern in nl, put the values in the map + HashMap map = new HashMap(); + ctx.processNL(nl, map); + + //now get the chunk of nl that will be replaced with the target later. + String subKey = ctx.getSubstitutionKey(); + assertEquals("baby on board exp1 and exp2 burt ward", subKey); + + String target = ctx.populateTargetString( map, grammar_r ); + assertEquals("something(exp1, exp2)", target); + + String result = ctx.interpolate(nl, subKey, target); + + assertEquals("yeah this is an expression something(exp1, exp2) end.", result); + + + + } + + + public void testMultipleReplacement() { + + TemplateContext ctx = new TemplateContext(); + + //chunks represent a lexed grammar "left hand side" + ctx.addChunk("{0}") + .addChunk("likes cheese"); + + String nl = "bob likes cheese and michael likes cheese conan likes cheese"; + String grammarTemplate = "{0}.likesCheese()"; + String expected = "bob.likesCheese() and michael.likesCheese() conan.likesCheese()"; + + + String result = ctx.processAllInstances(nl, grammarTemplate); + assertEquals(expected, result); + } + + + + + + public void testBasicExpression() { + + Chunk chunk1 = new Chunk("baby on board"); + Chunk chunk2 = new Chunk("{0}"); + Chunk chunk3 = new Chunk("and"); + Chunk chunk4 = new Chunk("{1}"); + Chunk chunk5 = new Chunk("burt ward"); + + chunk1.next = chunk2; + chunk2.next = chunk3; + chunk3.next = chunk4; + chunk4.next = chunk5; + + String nl = "yeah this is an expression baby on board exp1 and exp2 burt ward"; + chunk1.process(nl); + + HashMap map = new HashMap(); + chunk1.buildValueMap(map); + + assertEquals("exp1", map.get("{0}")); + assertEquals("exp2", map.get("{1}")); + + } + + public void testStartWith() { + + Chunk chunk1 = new Chunk("{0}"); + Chunk chunk2 = new Chunk("a thing"); + Chunk chunk3 = new Chunk("and"); + Chunk chunk4 = new Chunk("{1}"); + Chunk chunk5 = new Chunk("one more"); + + chunk1.next = chunk2; + chunk2.next = chunk3; + chunk3.next = chunk4; + chunk4.next = chunk5; + + String nl = "exp1 a thing and exp2 one more"; + chunk1.process(nl); + + HashMap map = new HashMap(); + chunk1.buildValueMap(map); + + assertEquals("exp1", map.get("{0}")); + assertEquals("exp2", map.get("{1}")); + + } + + + public void testEndWith() { + + Chunk chunk1 = new Chunk("blah blah blah"); + Chunk chunk2 = new Chunk("{1}"); + + chunk1.next = chunk2; + + String nl = "blah blah blah exp1"; + chunk1.process(nl); + + HashMap map = new HashMap(); + chunk1.buildValueMap(map); + + assertEquals("exp1", map.get("{1}")); + assertEquals(1, map.size()); + } + + public void testOneInTheMiddle() { + Chunk chunk1 = new Chunk("yeah "); + Chunk chunk2 = new Chunk("{abc}"); + Chunk chunk3 = new Chunk("one more"); + + chunk1.next = chunk2; + chunk2.next = chunk3; + + String nl = "yeah exp1 one more "; + chunk1.process(nl); + + HashMap map = new HashMap(); + chunk1.buildValueMap(map); + + assertEquals("exp1", map.get("{abc}")); + + } + + public void testNoTokens() { + Chunk chunk1 = new Chunk("yeah "); + + String nl = "yeah exp1 one more "; + chunk1.process(nl); + + HashMap map = new HashMap(); + chunk1.buildValueMap(map); + + assertEquals(0, map.size()); + } + + + + + + +} + + Property changes on: trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/dsl/template/TemplateContextTest.java ___________________________________________________________________ Name: svn:eol-style + native Added: trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/dsl/template/TemplateFactoryTest.java =================================================================== --- trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/dsl/template/TemplateFactoryTest.java 2006-01-24 12:46:23 UTC (rev 2190) +++ trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/dsl/template/TemplateFactoryTest.java 2006-01-24 13:52:33 UTC (rev 2191) @@ -0,0 +1,41 @@ +package org.drools.lang.dsl.template; + +import java.util.List; + +import junit.framework.TestCase; + +public class TemplateFactoryTest extends TestCase { + + public void testMake() { + TemplateFactory factory = new TemplateFactory(); + TemplateContext ctx = factory.getContext("something {0} going {1} on."); + assertNotNull(ctx); + } + + public void testLex() { + TemplateFactory factory = new TemplateFactory(); + List list = factory.lexChunks("one chunk"); + assertEquals(1, list.size()); + assertEquals("one chunk", list.get(0)); + + + list = factory.lexChunks("three {0} chunks"); + assertEquals(3, list.size()); + + assertEquals("three", list.get(0)); + assertEquals("{0}", list.get(1)); + assertEquals("chunks", list.get(2)); + + + list = factory.lexChunks("{42} more '{0}' chunks"); + assertEquals(4, list.size()); + + assertEquals("{42}", list.get(0)); + assertEquals("more '", list.get(1)); + assertEquals("{0}", list.get(2)); + assertEquals("' chunks", list.get(3)); + + + } + +} Property changes on: trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/dsl/template/TemplateFactoryTest.java ___________________________________________________________________ Name: svn:eol-style + native Deleted: trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/rule-with-expansion.drl =================================================================== --- trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/rule-with-expansion.drl 2006-01-24 12:46:23 UTC (rev 2190) +++ trunk/labs/jbossrules/drools-core/src/test/java/org/drools/lang/rule-with-expansion.drl 2006-01-24 13:52:33 UTC (rev 2191) @@ -1,19 +0,0 @@ -# Example DRL with expansion -# (ie "domain specific language") -# use expanders for domain specific and natural language extensions -use expander cheese.dsl - - -rule find_seating - when - #Bob is in atlanta - #Bob likes cheese - #There exists a Guest with name of "Michael" and sex of "Male" - >Guest( name == seatingRightGuestName, rightGuestSex:sex, rightGuestHobby:hobby ) - >Guest( leftGuestName:name , sex != rightGuestSex, hobby == rightGuestHobby ) - >count => Count() - #bind count to Count - then - #Send notification to Mark with message "hello" - >System.out.println("and this is code") -end \ No newline at end of file |