/* * Copyright (C) 2001-5, Anthony Harrison anh23@pitt.edu This library is free * software; you can redistribute it and/or modify it under the terms of the GNU * Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Created on Jun 4, 2005 by developer */ package org.jactr.io.resolver; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.locks.ReadWriteLock; import java.util.function.Predicate; import javolution.util.FastList; import org.antlr.runtime.tree.CommonTree; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jactr.core.buffer.IActivationBuffer; import org.jactr.core.chunk.IChunk; import org.jactr.core.chunk.ISubsymbolicChunk; import org.jactr.core.chunk.ISymbolicChunk; import org.jactr.core.chunktype.IChunkType; import org.jactr.core.chunktype.ISubsymbolicChunkType; import org.jactr.core.chunktype.ISymbolicChunkType; import org.jactr.core.extensions.IExtension; import org.jactr.core.model.IModel; import org.jactr.core.module.IModule; import org.jactr.core.production.IProduction; import org.jactr.core.production.ISubsymbolicProduction; import org.jactr.core.production.ISymbolicProduction; import org.jactr.core.production.action.AddAction; import org.jactr.core.production.action.IAction; import org.jactr.core.production.action.IBufferAction; import org.jactr.core.production.action.ModifyAction; import org.jactr.core.production.action.OutputAction; import org.jactr.core.production.action.ProxyAction; import org.jactr.core.production.action.RemoveAction; import org.jactr.core.production.action.SetAction; import org.jactr.core.production.action.StopAction; import org.jactr.core.production.condition.ChunkCondition; import org.jactr.core.production.condition.ChunkTypeCondition; import org.jactr.core.production.condition.IBufferCondition; import org.jactr.core.production.condition.ICondition; import org.jactr.core.production.condition.ProxyCondition; import org.jactr.core.production.condition.QueryCondition; import org.jactr.core.production.condition.VariableCondition; import org.jactr.core.slot.IConditionalSlot; import org.jactr.core.slot.ILogicalSlot; import org.jactr.core.slot.ISlot; import org.jactr.core.slot.ISlotContainer; import org.jactr.core.utils.parameter.IParameterized; import org.jactr.io.antlr3.builder.JACTRBuilder; import org.jactr.io.antlr3.misc.ASTSupport; import org.jactr.scripting.action.ScriptableAction; import org.jactr.scripting.condition.ScriptableCondition; public class ASTResolver { /** * Logger definition */ static private final transient Log LOGGER = LogFactory .getLog(ASTResolver.class); static private ASTSupport _support = new ASTSupport(); static public CommonTree toAST(Object obj, boolean fullResolution) { if (obj instanceof IModel) return toAST((IModel) obj, fullResolution); else if (obj instanceof IChunkType) return toAST((IChunkType) obj, fullResolution); else if (obj instanceof IChunk && !((IChunk) obj).hasBeenDisposed()) return toAST((IChunk) obj, false); else if (obj instanceof IProduction) return toAST((IProduction) obj); else if (obj instanceof IActivationBuffer) return toAST((IActivationBuffer) obj); else if (obj instanceof IExtension) return toAST((IExtension) obj); else if (obj instanceof IModule) return toAST((IModule) obj); else throw new RuntimeException("No known transformation for " + obj.getClass().getName()); } /** * create a version of the model.with the option to generate just the model * header (fullResoltuion), or to filter out content when generating a full * dump * * @param model * @param fullResolution * if false, just the model & parameters is generated. filters are * ignored. * @param productionFilter * @param chunkTypeFilter * @param chunkFilter * @return */ static public CommonTree toAST(IModel model, boolean fullResolution, Predicate<IProduction> productionFilter, Predicate<IChunkType> chunkTypeFilter, Predicate<IChunk> chunkFilter) { if (productionFilter == null) productionFilter = p -> true; if (chunkTypeFilter == null) chunkTypeFilter = c -> true; if (chunkFilter == null) chunkFilter = c -> true; /** * lock so that we are the only one right now */ ReadWriteLock lock = model.getLock(); lock.readLock().lock(); try { CommonTree md = _support.createModelTree(model.getName()); // insert all the parameters setParameters( ASTSupport.getFirstDescendantWithType(md, JACTRBuilder.PARAMETERS), model); if (fullResolution) { CommonTree modules = ASTSupport.getFirstDescendantWithType(md, JACTRBuilder.MODULES); for (IModule module : model.getModules()) { CommonTree modDesc = toAST(module); modules.addChild(modDesc); } // if full we add extensions, buffers, chunks, chunktype, and // productions CommonTree extensions = ASTSupport.getFirstDescendantWithType(md, JACTRBuilder.EXTENSIONS); for (IExtension extension : model.getExtensions()) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Generating AST for extension " + extension.getClass().getName()); CommonTree ed = toAST(extension); if (LOGGER.isDebugEnabled()) LOGGER.debug("Returned ast " + ed.toStringTree()); extensions.addChild(ed); } // chunktypes will add the chunks for us CommonTree decWrapper = ASTSupport.getFirstDescendantWithType(md, JACTRBuilder.DECLARATIVE_MEMORY); Set<IChunkType> resolved = new HashSet<IChunkType>(); List<CommonTree> chunkTypes = new ArrayList<CommonTree>(); try { for (IChunkType ct : model.getDeclarativeModule().getChunkTypes() .get()) if (chunkTypeFilter.test(ct)) { List<CommonTree> chunkTypesList = toOrderedAST(ct, resolved, chunkTypeFilter, chunkFilter); chunkTypes.addAll(chunkTypesList); } } catch (InterruptedException ie) { LOGGER.error("Interrupted ", ie); } catch (ExecutionException e) { LOGGER.error("Execution ", e); } // now we can dump them for (CommonTree ctNode : chunkTypes) decWrapper.addChild(ctNode); chunkTypes.clear(); // productions CommonTree proWrapper = ASTSupport.getFirstDescendantWithType(md, JACTRBuilder.PROCEDURAL_MEMORY); try { for (IProduction p : model.getProceduralModule().getProductions() .get()) { if (LOGGER.isDebugEnabled()) LOGGER.debug("generating AST for production " + p); CommonTree pd = toAST(p); if (LOGGER.isDebugEnabled()) LOGGER.debug("returned ast " + pd.toStringTree()); proWrapper.addChild(pd); } } catch (InterruptedException ie) { LOGGER.error("Interrupted ", ie); } catch (ExecutionException e) { LOGGER.error("Execution ", e); } // buffers CommonTree buffersWrapper = ASTSupport.getFirstDescendantWithType(md, JACTRBuilder.BUFFERS); Map<String, CommonTree> chunkTypeNodes = ASTSupport.getMapOfTrees( decWrapper, JACTRBuilder.CHUNK_TYPE); for (IActivationBuffer buffer : model.getActivationBuffers()) { buffersWrapper.addChild(toAST(buffer)); /* * since the chunks in the buffer aren't in the model, they won't be * serialized correctly, so we grab them now and stick them under * their respective chunktype */ for (IChunk source : buffer.getSourceChunks()) { CommonTree sourceChunk = toAST(source, false); CommonTree chunkType = chunkTypeNodes.get(source.getSymbolicChunk() .getChunkType().getSymbolicChunkType().getName().toLowerCase()); if (chunkType != null) ASTSupport.getFirstDescendantWithType(chunkType, JACTRBuilder.CHUNKS).addChild(sourceChunk); } } } return md; } finally { lock.readLock().unlock(); } } /** * create an AST description of the model and optionally, all its children * * @param model * @param fullResolution * @return */ static public CommonTree toAST(IModel model, boolean fullResolution) { return toAST(model, fullResolution, null, null, null); } /** * return a list of all the commonTrees for this chunktype and its parents * sorted in dependency order, with chunkType's commonTree last * * @param chunkType * @return */ @SuppressWarnings("unchecked") static protected List<CommonTree> toOrderedAST(IChunkType chunkType, Set<IChunkType> alreadyConverted, Predicate<IChunkType> chunkTypeFilter, Predicate<IChunk> chunkFilter) { if (alreadyConverted.contains(chunkType)) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Already converted " + chunkType); return Collections.EMPTY_LIST; } ArrayList<CommonTree> rtn = new ArrayList<CommonTree>(); Collection<IChunkType> parents = chunkType.getSymbolicChunkType() .getParents(); // if (parent != null) for (IChunkType parent : parents) if (chunkTypeFilter.test(parent)) { if (LOGGER.isDebugEnabled()) LOGGER.debug("resolving parent " + parent); rtn.addAll(toOrderedAST(parent, alreadyConverted, chunkTypeFilter, chunkFilter)); } if (LOGGER.isDebugEnabled()) LOGGER.debug("Resolving " + chunkType); alreadyConverted.add(chunkType); if (chunkTypeFilter.test(chunkType)) rtn.add(toAST(chunkType, chunkFilter)); return rtn; } /** * return the AST describing this production * * @param production * @return */ static public CommonTree toAST(IProduction production) { ISymbolicProduction sp = production.getSymbolicProduction(); ISubsymbolicProduction ssp = production.getSubsymbolicProduction(); CommonTree pd = _support.createProductionTree(sp.getName()); setParameters( ASTSupport.getFirstDescendantWithType(pd, JACTRBuilder.PARAMETERS), ssp); // now we take care of the actions & conditions.. setConditions(pd, sp.getConditions()); setActions(pd, sp.getActions()); return pd; } /** * return the AST describing this chunk * * @param chunk * @return */ static public CommonTree toAST(IChunk chunk, boolean skipParameters) { ISymbolicChunk sc = chunk.getSymbolicChunk(); ISubsymbolicChunk ssc = chunk.getSubsymbolicChunk(); CommonTree cd = _support.createChunkTree(sc.getName(), sc.getChunkType() .getSymbolicChunkType().getName()); if (!skipParameters) setParameters( ASTSupport.getFirstDescendantWithType(cd, JACTRBuilder.PARAMETERS), ssc); // slots setSlots(ASTSupport.getFirstDescendantWithType(cd, JACTRBuilder.SLOTS), sc); if (LOGGER.isDebugEnabled()) LOGGER.debug("generated " + cd.toStringTree() + " for chunk " + chunk); return cd; } static public CommonTree toAST(IChunkType chunkType, boolean fullResolution) { /* * if we aren't doing full res, filter is null. if we are, accept all */ return toAST(chunkType, !fullResolution ? null : (c) -> true); } /** * return the AST describing the chunktype and optionally all it's immediate * chunks * * @param chunkType * @param fullResolution * @return */ static public CommonTree toAST(IChunkType chunkType, Predicate<IChunk> chunkFilter) { ISymbolicChunkType sct = chunkType.getSymbolicChunkType(); ISubsymbolicChunkType ssct = chunkType.getSubsymbolicChunkType(); Collection<String> parentChunkTypeNames = new FastList<String>(); for (IChunkType parent : sct.getParents()) parentChunkTypeNames.add(parent.getSymbolicChunkType().getName()); CommonTree cd = _support.createChunkTypeTree(sct.getName(), parentChunkTypeNames); setParameters( ASTSupport.getFirstDescendantWithType(cd, JACTRBuilder.PARAMETERS), ssct); // slots setSlots(ASTSupport.getFirstDescendantWithType(cd, JACTRBuilder.SLOTS), sct); if (chunkFilter != null) { CommonTree chunks = ASTSupport.getFirstDescendantWithType(cd, JACTRBuilder.CHUNKS); /* * process the chunks, but only include them if they are a direct child of * chunk type. leave descendants for the derived chunktypes to return */ for (IChunk chunk : sct.getChunks()) if (chunk.isAStrict(chunkType)) if (chunkFilter.test(chunk)) chunks.addChild(toAST(chunk, false)); } return cd; } /** * return the AST describing this buffer * * @param buffer * @return */ static public CommonTree toAST(IActivationBuffer buffer) { CommonTree bd = _support.createBufferTree(buffer.getName()); /* * some buffers may have no parameters.. this is kind of stupid they all * should */ if (buffer instanceof IParameterized) setParameters( ASTSupport.getFirstDescendantWithType(bd, JACTRBuilder.PARAMETERS), (IParameterized) buffer); /* * now for the contents of the buffer.. */ CommonTree chunks = ASTSupport.getFirstDescendantWithType(bd, JACTRBuilder.CHUNKS); for (IChunk c : buffer.getSourceChunks()) try { String chunkName = c.getSymbolicChunk().getName(); chunks.addChild(_support.create(JACTRBuilder.CHUNK_IDENTIFIER, chunkName)); } catch (Exception e) { LOGGER .debug( "chunk access exception, most likely due to chunk merging/encoding during AST generation. Safe to ignore ", e); } return bd; } /** * return the AST describing this extension * * @param extension * @return */ static public CommonTree toAST(IExtension extension) { CommonTree ed = _support .createExtensionTree(extension.getClass().getName()); setParameters( ASTSupport.getFirstDescendantWithType(ed, JACTRBuilder.PARAMETERS), extension); return ed; } static public CommonTree toAST(IModule module) { CommonTree md = _support.createModuleTree(module.getClass().getName()); if (LOGGER.isDebugEnabled()) LOGGER.debug("created module ast " + md.toStringTree()); if (module instanceof IParameterized) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Setting parameters"); setParameters( ASTSupport.getFirstDescendantWithType(md, JACTRBuilder.PARAMETERS), (IParameterized) module); } if (LOGGER.isDebugEnabled()) LOGGER.debug("returning module ast " + md.toStringTree()); return md; } static protected void setConditions(CommonTree pd, Collection<ICondition> conditions) { CommonTree condWrapper = ASTSupport.getFirstDescendantWithType(pd, JACTRBuilder.CONDITIONS); for (ICondition cond : conditions) { CommonTree desc = toAST(cond); if (desc != null) { condWrapper.addChild(desc); if (LOGGER.isDebugEnabled()) LOGGER.debug("generated " + desc.toStringTree() + " for condition " + cond); } else if (LOGGER.isWarnEnabled()) LOGGER.warn("Could not resolve " + cond + " toAST"); } } static public CommonTree toAST(ICondition condition) { CommonTree desc = null; if (condition instanceof IBufferCondition) { String bufferName = ((IBufferCondition) condition).getBufferName(); if (condition instanceof QueryCondition) desc = _support.createQueryTree(bufferName); else { CommonTree content = null; if (condition instanceof ChunkCondition) content = _support.create(JACTRBuilder.CHUNK_IDENTIFIER, ((ChunkCondition) condition).getChunk().getSymbolicChunk() .getName()); else if (condition instanceof ChunkTypeCondition) { // chunktype may be null if (((ChunkTypeCondition) condition).getChunkType() != null) content = _support.create(JACTRBuilder.CHUNK_TYPE_IDENTIFIER, ((ChunkTypeCondition) condition).getChunkType() .getSymbolicChunkType().getName()); } else if (condition instanceof VariableCondition) content = _support.create(JACTRBuilder.VARIABLE, ((VariableCondition) condition).getVariableName()); desc = _support.createMatchTree(bufferName, content); } } else if (condition instanceof ProxyCondition) { // proxy just a tracked node with the text as the class name String className = ((ProxyCondition) condition).getDelegateClassName(); desc = _support.createProxyConditionTree(className); ICondition actual = ((ProxyCondition) condition).getDelegate(); if (actual instanceof ISlotContainer) condition = actual; } else if (condition instanceof ScriptableCondition) { String script = ((ScriptableCondition) condition).getScript(); desc = _support.createScriptableConditionTree( ((ScriptableCondition) condition).getFactory().getLanguageName(), script); } if (condition instanceof ISlotContainer && desc != null) setSlots(ASTSupport.getFirstDescendantWithType(desc, JACTRBuilder.SLOTS), (ISlotContainer) condition); return desc; } static protected void setActions(CommonTree pd, Collection<IAction> actions) { CommonTree actionsWrapper = ASTSupport.getFirstDescendantWithType(pd, JACTRBuilder.ACTIONS); for (IAction act : actions) { CommonTree actionNode = toAST(act); if (actionNode != null) { actionsWrapper.addChild(actionNode); if (LOGGER.isDebugEnabled()) LOGGER.debug("generated " + actionNode.toStringTree() + " for action " + act); } else if (LOGGER.isWarnEnabled()) LOGGER.warn("Could not translate " + act + " toAST"); } } static public CommonTree toAST(IAction action) { CommonTree actionNode = null; String bufferName = null; if (action instanceof IBufferAction) bufferName = ((IBufferAction) action).getBufferName(); if (action instanceof StopAction) actionNode = _support.createProxyActionTree(StopAction.class.getName()); else if (action instanceof ProxyAction) { // proxy just a tracked node with the text as the class name String className = ((ProxyAction) action).getDelegateClassName(); actionNode = _support.createProxyActionTree(className); IAction actual = ((ProxyAction) action).getDelegate(); if (actual instanceof ISlotContainer) action = actual; } else if (action instanceof ScriptableAction) { String script = ((ScriptableAction) action).getScript(); actionNode = _support.createScriptableActionTree( ((ScriptableAction) action).getFactory().getLanguageName(), script); } else if (action instanceof AddAction) { Object ref = ((AddAction) action).getReferant(); CommonTree content = null; if (ref instanceof String) // variable content = _support.create(JACTRBuilder.VARIABLE, (String) ref); else if (ref instanceof IChunk) content = _support.create(JACTRBuilder.CHUNK_IDENTIFIER, ((IChunk) ref) .getSymbolicChunk().getName()); else if (ref instanceof IChunkType) content = _support.create(JACTRBuilder.CHUNK_TYPE_IDENTIFIER, ((IChunkType) ref).getSymbolicChunkType().getName()); actionNode = _support.createAddTree(bufferName, content); } else if (action instanceof SetAction) { Object ref = ((SetAction) action).getReferant(); CommonTree content = null; if (ref instanceof String) // variable content = _support.create(JACTRBuilder.VARIABLE, (String) ref); else if (ref instanceof IChunk) content = _support.create(JACTRBuilder.CHUNK_IDENTIFIER, ((IChunk) ref) .getSymbolicChunk().getName()); actionNode = _support.createSetTree(bufferName, content); } else if (action instanceof RemoveAction) actionNode = _support.createRemoveTree(bufferName, null); else if (action instanceof ModifyAction) actionNode = _support.createModifyTree(bufferName); else if (action instanceof OutputAction) actionNode = _support.createOutputAction(((OutputAction) action) .getText()); // assign the slots if (action instanceof ISlotContainer) setSlots( ASTSupport.getFirstDescendantWithType(actionNode, JACTRBuilder.SLOTS), (ISlotContainer) action); return actionNode; } /** * @param desc * @param sc */ static public void setSlots(CommonTree desc, ISlotContainer sc) { if (desc == null) return; if (!(desc.getType() == JACTRBuilder.SLOTS || desc.getType() == JACTRBuilder.LOGIC)) throw new IllegalArgumentException(desc + " must be of type SLOTS " + JACTRBuilder.SLOTS + " or LOGIC " + JACTRBuilder.LOGIC); for (ISlot slot : sc.getSlots()) if (slot instanceof ILogicalSlot) { ILogicalSlot ls = (ILogicalSlot) slot; CommonTree lsTree = _support.createLogicalSlot(ls.getOperator()); setSlots(lsTree, ls); desc.addChild(lsTree); } else desc.addChild(toAST(slot)); } static private CommonTree toAST(ISlot slot) { if (slot instanceof ILogicalSlot) throw new IllegalArgumentException(slot + " is of type ILogicalSlot; that isn't handled here."); String slotNameStr = slot.getName(); Object actualSlotValue = slot.getValue(); /* * don't know where this started.. i should find all occurences in the code * and get ride of them TODO remove slot containing slot support */ if (actualSlotValue instanceof ISlot) actualSlotValue = ((ISlot) actualSlotValue).getValue(); Object slotValue = actualSlotValue; if (slotValue == null) slotValue = "null"; CommonTree slotName = null; /* * support for variable slot names */ if (slotNameStr.startsWith("=")) slotName = _support.create(JACTRBuilder.VARIABLE, slotNameStr); else slotName = _support.create(JACTRBuilder.NAME, slotNameStr); // now we use the actualSlotValue to determine what token type the value // should be CommonTree content = null; int newType = JACTRBuilder.IDENTIFIER; if (slotValue instanceof Number) newType = JACTRBuilder.NUMBER; else if (slotValue instanceof String || slotValue instanceof StringBuilder) { // stringbuilder to handle string literals in the builder String str = slotValue.toString(); if (str.startsWith("=")) newType = JACTRBuilder.VARIABLE; else newType = JACTRBuilder.STRING; } content = _support.create(newType, slotValue.toString()); CommonTree sd = null; if (slot instanceof IConditionalSlot) { CommonTree condition = null; int cond = ((IConditionalSlot) slot).getCondition(); switch (cond) { case IConditionalSlot.EQUALS: condition = _support.create(JACTRBuilder.EQUALS, "="); break; case IConditionalSlot.LESS_THAN: condition = _support.create(JACTRBuilder.LT, "<"); break; case IConditionalSlot.LESS_THAN_EQUALS: condition = _support.create(JACTRBuilder.LTE, "<="); break; case IConditionalSlot.GREATER_THAN: condition = _support.create(JACTRBuilder.GT, ">"); break; case IConditionalSlot.GREATER_THAN_EQUALS: condition = _support.create(JACTRBuilder.GTE, ">="); break; case IConditionalSlot.NOT_EQUALS: condition = _support.create(JACTRBuilder.NOT, "!="); break; case IConditionalSlot.WITHIN: condition = _support.create(JACTRBuilder.WITHIN, "<>"); break; } sd = _support.createSlot(slotName, condition, content); } else sd = _support.createSlot(slotName, content); if (LOGGER.isDebugEnabled()) LOGGER.debug("generated ast:" + sd.toStringTree() + " for slot:" + slot); return sd; } /** * @param actrDesc * @param parameterized */ static protected void setParameters(CommonTree parameters, IParameterized parameterized) { if (parameters.getType() != JACTRBuilder.PARAMETERS) throw new IllegalArgumentException(parameters.toStringTree() + " must be of type PARAMETERS " + JACTRBuilder.PARAMETERS); Collection<String> setable = parameterized.getSetableParameters(); if (LOGGER.isDebugEnabled()) LOGGER.debug("attempting to set " + setable.size() + " parameters"); for (String parameterName : setable) { Object parameterValue = parameterized.getParameter(parameterName); if (parameterValue == null) parameterValue = "null"; CommonTree p = _support.create(JACTRBuilder.PARAMETER, "parameter"); p.addChild(_support.create(JACTRBuilder.NAME, parameterName)); p.addChild(_support.create(JACTRBuilder.STRING, parameterValue.toString())); if (LOGGER.isDebugEnabled()) LOGGER.debug("adding p:" + p.toStringTree()); parameters.addChild(p); } } }