Summary ======= Rework of DSL expansion. Simplify the antlr parsing; fix some bugs; add some new features: permit empty value section and line breaks in DSL; expansion of modified variable values (case conversions); use variables from previous lines; implement debug options for DSL programmers. Documentation ============= DSL definitions of replacements for condition and consequence phrases may be broken into several lines. Any line starting with a '[' (as the first non-blank characteer) is assumed to be the begin of a new entry. Lines starting with '#' or '//' are treated as DSL comments. The value section of an entry may be empty, which is useful for discarding "filler" phrases. Variable references may be written with an exclamation mark and a string processing directive following the variable name. Implemented directives are "lc" and "uc" for converting the value to lower and upper case, repsectively, and "ucfirst", which makes the value lower case with an upper case initial letter. A comment starting with "#/" may be used for setting debug options. If this line contains "result", the resulting DRL text is written to standard output. If it contains "steps", the effect of substituting DSL phrases by the corresponding value section modified by variable interpolation Changed files ============= DSL/drools-compiler/src/main/java/org/drools/compiler: PackageBuilder.java - make sure errors from DSL compilation are passed on DSL/drools-compiler/src/main/java/org/drools/lang/dsl: AntlrDSLMappingEntry.java - improve several regexes - fix closure counting errors DSLTokenizedMappingFile.java - preprocess DSL source: fold continuations, omit comments, get options - postprocess errors DefaultDSLMapping.java - add options DSLMapping.java - add options DefaultExpander.java - improve pattern for locating rule-when-then-end and query-end - add option support (dump full result, show replacements step-by-step) - replace values "manually" - keep variables from previous lines of condition or consequence - honour !lc, !uc !ucfirst DSL/drools-compiler/src/main/resources/org/drools/lang/dsl: DSLMap.g DSLMapWalker.g - permit empty value section - omit all comment line handling from parser - omit the useless "q" part of variable definition - catch errors Sample DSL ========== # DEBUG OPTIONS: #/ result steps [keyword][]check (that|whether)=salience 100\nwhen [when][](?:and)? [Tt]here is an?= [when][]equal to=== [when][](?:and)? [Tt]here is no=not [when][]or the code {code:\w+}=|| code == CommandCode.{code} [when][]-with the code {code:\w+}=code == CommandCode.{code} [when][]- is not a switch=eval( ! ($element instanceof Switch) ) [when][]- is not a lockable element=eval( ! ($element instanceof Lock) ) [when][]- is locked=locked == true [when][]- is (?:not\s+|un)locked=locked == false # [when][]- is not a signal=eval( ! ($element instanceof Signal) ) [when][]- (?:has no fault|is not faulty)=faulty == false # element command # [when][](?i:element command)=$command: Command( $element: element, $code: code, $operator: operator ) [when][](and)? the commanded lockable element=Lock( this == $element ) [when][](and)? the commanded element=Element( this == $element ) # route command # [when][](?i:(route set|set route) command)= $command: RouteCommand( $code: code == CommandCode.SET_ROUTE, $start: start, $goal: goal, $operator: operator ) [when][](without a|but no) matching route definition=not RouteDefinition( start == $start, goal == $goal ) [when][](and)? with a matching route definition=$def: RouteDefinition( start == $start, goal == $goal, $elements: elements ) [when][](and|but)? with a locked goal=$badel: Goal( this == $goal, locked == true ) [when][](and|but)? with a barred interdiction=$badel: Interdiction( this in ($elements), barred == true ) [when][](and|but)? with a switch locked in the wrong position= $badel: Switch( locked == true, position != ($def.getPositionOf($badel)) ) from $def.switches [when][](and|but)? with an element used in an(other)? route= Token( $badel: target memberOf ($elements) ) # route # [when][](?i:set route)=$route: Route( state == RouteState.SET, $start: start ) [when][]and its start signal=Signal( this == $start ) [when][]and all of it's track circuits are free= not ( TrackCircuit( clear != true ) from $route.trackCircuits ) ####################################### # # consequence # [then][]execute command= modify( $element )\{ execute( $code ) \} retract( $command ); [then][][Ss]end failed command message {text}=$operator.message( {text} + ": " + $element.getId() ); # [then][][Ss]end failed route message {text} with blocking element= $operator.message( {text} + " on " + $badel.getId() + " for route: " + $start.getId() + " to " + $goal.getId() ); # [then][][Ss]end failed route message {text}= $operator.message( {text} + ": " + $start.getId() + " to " + $goal.getId() ); # [then][][Dd]iscard (the)? command=retract( $command ); [then][][Ee]stablish set route= Route route = new Route( $def ); insert( route ); [then][]set the start signal to (?i:CLEAR)=modify( $start )\{ setIndication( Indication.CLEAR ) \} [then][]change the route state to proving=modify( $route )\{ setState( RouteState.PROVING ) \} Sample DSLR =========== package appl.domain.element; import appl.domain.route.*; # # element command checks # rule "command: throw-over for switch" check that there is an element command - with the code THROW_OVER and the commanded element - is not a switch then send failed command message "cannot throw over element" end rule "commmand: lock/unlock for switch, signal, goal" check that there is an element command - with the code LOCK or the code UNLOCK and the commanded element - is not a lockable element then send failed command message "cannot lock/unlock element" end rule "commmand: lock for locked switch, signal, goal" check that there is an element command - with the code LOCK and the commanded lockable element - is locked then send failed command message "cannot lock element with lock" end rule "commmand: unlock for unlocked switch, signal, goal" check that there is an element command - with the code UNLOCK and the commanded lockable element - is not locked then send failed command message "cannot unlock element without lock" end rule "command: halt for signal" check that there is an element command - with the code HALT and the commanded element - is not a signal then send failed command message "cannot halt element" end # # element command executione # rule "execute element command" when there is an element command then execute command end # # route command checks # rule "route: no definition" check whether there is a set route command but no matching route definition then send failed route message "no such route" discard the command end rule "refuse defined route if goal locked" check whether there is a set route command and with a matching route definition but with a locked goal then send failed route message "goal locked" with blocking element discard the command end rule "refuse route if interdiction on any element" check whether there is a set route command with a matching route definition but with a barred interdiction then send failed route message "goal locked" with blocking element discard the command end rule "refuse route if switch locked in wrong position" check whether there is a set route command with a matching route definition and with a switch locked in the wrong position then send failed route message "switch locked in wrong position" with blocking element discard the command end rule "refuse route if element already used by route" check whether there is a set route command with a matching route definition and with an element used in another route then send failed route message "conflicting route" with blocking element discard the command end rule "establish route" when there is a set route command with a matching route definition then establish set route discard the command end rule "set signal to drive" when there is a set route and its start signal - is not locked - is not faulty and all of it's track circuits are free then set the start signal to clear change the route state to proving end Debug Output ============ to expand: | there is an element command| matches: (?<=\s|^)(?:and)?\s+[Tt]here\s+is\s+an?(?=\s|$) result: | element command| matches: (?<=\s|^)(?i:element\s+command)(?=\s|$) result: | $command: Command( $element: element, $code: code, $operator: operator )| to expand: | - with the code THROW_OVER| matches: (?<=\s|^)-\s*with\s+the\s+code\s+(\w+)(?=\s|$) found key "code" at index 1 result: | code == CommandCode.THROW_OVER| to expand: | and the commanded element| matches: (?<=\s|^)(and)?\s+the\s+commanded\s+element(?=\s|$) result: | Element( this == $element )| to expand: | - is not a switch| matches: (?<=\s|^)-\s*is\s+not\s+a\s+switch(?=\s|$) result: | eval( ! ($element instanceof Switch) )| to expand: | send failed command message "cannot throw over element"| matches: (?<=\s|^)[Ss]end\s+failed\s+command\s+message\s+(.*?)$ found key "text" at index 1 result: | $operator.message( "cannot throw over element" + ": " + $element.getId() );| to expand: | there is an element command| matches: (?<=\s|^)(?:and)?\s+[Tt]here\s+is\s+an?(?=\s|$) result: | element command| matches: (?<=\s|^)(?i:element\s+command)(?=\s|$) result: | $command: Command( $element: element, $code: code, $operator: operator )| to expand: | - with the code LOCK or the code UNLOCK| matches: (?<=\s|^)or\s+the\s+code\s+(\w+)(?=\s|$) found key "code" at index 1 result: | - with the code LOCK || code == CommandCode.UNLOCK| matches: (?<=\s|^)-\s*with\s+the\s+code\s+(\w+)(?=\s|$) found key "code" at index 1 result: | code == CommandCode.LOCK || code == CommandCode.UNLOCK| to expand: | and the commanded element| matches: (?<=\s|^)(and)?\s+the\s+commanded\s+element(?=\s|$) result: | Element( this == $element )| to expand: | - is not a lockable element| matches: (?<=\s|^)-\s*is\s+not\s+a\s+lockable\s+element(?=\s|$) result: | eval( ! ($element instanceof Lock) )| to expand: | send failed command message "cannot lock/unlock element"| matches: (?<=\s|^)[Ss]end\s+failed\s+command\s+message\s+(.*?)$ found key "text" at index 1 result: | $operator.message( "cannot lock/unlock element" + ": " + $element.getId() );| to expand: | there is an element command| matches: (?<=\s|^)(?:and)?\s+[Tt]here\s+is\s+an?(?=\s|$) result: | element command| matches: (?<=\s|^)(?i:element\s+command)(?=\s|$) result: | $command: Command( $element: element, $code: code, $operator: operator )| to expand: | - with the code LOCK| matches: (?<=\s|^)-\s*with\s+the\s+code\s+(\w+)(?=\s|$) found key "code" at index 1 result: | code == CommandCode.LOCK| to expand: | and the commanded lockable element| matches: (?<=\s|^)(and)?\s+the\s+commanded\s+lockable\s+element(?=\s|$) result: | Lock( this == $element )| to expand: | - is locked| matches: (?<=\s|^)-\s*is\s+locked(?=\s|$) result: | locked == true| to expand: | send failed command message "cannot lock element with lock"| matches: (?<=\s|^)[Ss]end\s+failed\s+command\s+message\s+(.*?)$ found key "text" at index 1 result: | $operator.message( "cannot lock element with lock" + ": " + $element.getId() );| to expand: | there is an element command| matches: (?<=\s|^)(?:and)?\s+[Tt]here\s+is\s+an?(?=\s|$) result: | element command| matches: (?<=\s|^)(?i:element\s+command)(?=\s|$) result: | $command: Command( $element: element, $code: code, $operator: operator )| to expand: | - with the code UNLOCK| matches: (?<=\s|^)-\s*with\s+the\s+code\s+(\w+)(?=\s|$) found key "code" at index 1 result: | code == CommandCode.UNLOCK| to expand: | and the commanded lockable element| matches: (?<=\s|^)(and)?\s+the\s+commanded\s+lockable\s+element(?=\s|$) result: | Lock( this == $element )| to expand: | - is not locked| matches: (?<=\s|^)-\s*is\s+(?:not\s+|un)locked(?=\s|$) result: | locked == false| to expand: | send failed command message "cannot unlock element without lock"| matches: (?<=\s|^)[Ss]end\s+failed\s+command\s+message\s+(.*?)$ found key "text" at index 1 result: | $operator.message( "cannot unlock element without lock" + ": " + $element.getId() );| to expand: | there is an element command| matches: (?<=\s|^)(?:and)?\s+[Tt]here\s+is\s+an?(?=\s|$) result: | element command| matches: (?<=\s|^)(?i:element\s+command)(?=\s|$) result: | $command: Command( $element: element, $code: code, $operator: operator )| to expand: | - with the code HALT| matches: (?<=\s|^)-\s*with\s+the\s+code\s+(\w+)(?=\s|$) found key "code" at index 1 result: | code == CommandCode.HALT| to expand: | and the commanded element| matches: (?<=\s|^)(and)?\s+the\s+commanded\s+element(?=\s|$) result: | Element( this == $element )| to expand: | - is not a signal| matches: (?<=\s|^)-\s*is\s+not\s+a\s+signal(?=\s|$) result: | eval( ! ($element instanceof Signal) )| to expand: | send failed command message "cannot halt element"| matches: (?<=\s|^)[Ss]end\s+failed\s+command\s+message\s+(.*?)$ found key "text" at index 1 result: | $operator.message( "cannot halt element" + ": " + $element.getId() );| to expand: | there is an element command| matches: (?<=\s|^)(?:and)?\s+[Tt]here\s+is\s+an?(?=\s|$) result: | element command| matches: (?<=\s|^)(?i:element\s+command)(?=\s|$) result: | $command: Command( $element: element, $code: code, $operator: operator )| to expand: | execute command| matches: (?<=\s|^)execute\s+command(?=\s|$) result: | modify( $element ){ execute( $code ) } retract( $command );| to expand: | there is a set route command but no matching route definition| matches: (?<=\s|^)(?:and)?\s+[Tt]here\s+is\s+an?(?=\s|$) result: | set route command but no matching route definition| matches: (?<=\s|^)(?i:(route\s+set|set\s+route)\s+command)(?=\s|$) result: | $command: RouteCommand( $code: code == CommandCode.SET_ROUTE, $start: start, $goal: goal, $operator: operator ) but no matching route definition| matches: (?<=\s|^)(without\s+a|but\s+no)\s+matching\s+route\s+definition(?=\s|$) result: | $command: RouteCommand( $code: code == CommandCode.SET_ROUTE, $start: start, $goal: goal, $operator: operator ) not RouteDefinition( start == $start, goal == $goal )| to expand: | send failed route message "no such route"| matches: (?<=\s|^)[Ss]end\s+failed\s+route\s+message\s+(.*?)$ found key "text" at index 1 result: | $operator.message( "no such route" + ": " + $start.getId() + " to " + $goal.getId() );| to expand: | discard the command| matches: (?<=\s|^)[Dd]iscard\s+(the)?\s+command(?=\s|$) result: | retract( $command );| to expand: | there is a set route command | matches: (?<=\s|^)(?:and)?\s+[Tt]here\s+is\s+an?(?=\s|$) result: | set route command | matches: (?<=\s|^)(?i:(route\s+set|set\s+route)\s+command)(?=\s|$) result: | $command: RouteCommand( $code: code == CommandCode.SET_ROUTE, $start: start, $goal: goal, $operator: operator ) | to expand: | and with a matching route definition| matches: (?<=\s|^)(and)?\s+with\s+a\s+matching\s+route\s+definition(?=\s|$) result: | $def: RouteDefinition( start == $start, goal == $goal, $elements: elements )| to expand: | but with a locked goal| matches: (?<=\s|^)(and|but)?\s+with\s+a\s+locked\s+goal(?=\s|$) result: | $badel: Goal( this == $goal, locked == true )| to expand: | send failed route message "goal locked" with blocking element| matches: (?<=\s|^)[Ss]end\s+failed\s+route\s+message\s+(.*?)\s+with\s+blocking\s+element(?=\s|$) found key "text" at index 1 result: | $operator.message( "goal locked" + " on " + $badel.getId() + " for route: " + $start.getId() + " to " + $goal.getId() );| to expand: | discard the command| matches: (?<=\s|^)[Dd]iscard\s+(the)?\s+command(?=\s|$) result: | retract( $command );| to expand: | there is a set route command | matches: (?<=\s|^)(?:and)?\s+[Tt]here\s+is\s+an?(?=\s|$) result: | set route command | matches: (?<=\s|^)(?i:(route\s+set|set\s+route)\s+command)(?=\s|$) result: | $command: RouteCommand( $code: code == CommandCode.SET_ROUTE, $start: start, $goal: goal, $operator: operator ) | to expand: | with a matching route definition| matches: (?<=\s|^)(and)?\s+with\s+a\s+matching\s+route\s+definition(?=\s|$) result: |$def: RouteDefinition( start == $start, goal == $goal, $elements: elements )| to expand: | but with a barred interdiction| matches: (?<=\s|^)(and|but)?\s+with\s+a\s+barred\s+interdiction(?=\s|$) result: | $badel: Interdiction( this in ($elements), barred == true )| to expand: | send failed route message "goal locked" with blocking element| matches: (?<=\s|^)[Ss]end\s+failed\s+route\s+message\s+(.*?)\s+with\s+blocking\s+element(?=\s|$) found key "text" at index 1 result: | $operator.message( "goal locked" + " on " + $badel.getId() + " for route: " + $start.getId() + " to " + $goal.getId() );| to expand: | discard the command| matches: (?<=\s|^)[Dd]iscard\s+(the)?\s+command(?=\s|$) result: | retract( $command );| to expand: | there is a set route command | matches: (?<=\s|^)(?:and)?\s+[Tt]here\s+is\s+an?(?=\s|$) result: | set route command | matches: (?<=\s|^)(?i:(route\s+set|set\s+route)\s+command)(?=\s|$) result: | $command: RouteCommand( $code: code == CommandCode.SET_ROUTE, $start: start, $goal: goal, $operator: operator ) | to expand: | with a matching route definition| matches: (?<=\s|^)(and)?\s+with\s+a\s+matching\s+route\s+definition(?=\s|$) result: |$def: RouteDefinition( start == $start, goal == $goal, $elements: elements )| to expand: | and with a switch locked in the wrong position| matches: (?<=\s|^)(and|but)?\s+with\s+a\s+switch\s+locked\s+in\s+the\s+wrong\s+position(?=\s|$) result: | $badel: Switch( locked == true, position != ($def.getPositionOf($badel)) ) from $def.switches| to expand: | send failed route message "switch locked in wrong position" with blocking element| matches: (?<=\s|^)[Ss]end\s+failed\s+route\s+message\s+(.*?)\s+with\s+blocking\s+element(?=\s|$) found key "text" at index 1 result: | $operator.message( "switch locked in wrong position" + " on " + $badel.getId() + " for route: " + $start.getId() + " to " + $goal.getId() );| to expand: | discard the command| matches: (?<=\s|^)[Dd]iscard\s+(the)?\s+command(?=\s|$) result: | retract( $command );| to expand: | there is a set route command | matches: (?<=\s|^)(?:and)?\s+[Tt]here\s+is\s+an?(?=\s|$) result: | set route command | matches: (?<=\s|^)(?i:(route\s+set|set\s+route)\s+command)(?=\s|$) result: | $command: RouteCommand( $code: code == CommandCode.SET_ROUTE, $start: start, $goal: goal, $operator: operator ) | to expand: | with a matching route definition| matches: (?<=\s|^)(and)?\s+with\s+a\s+matching\s+route\s+definition(?=\s|$) result: |$def: RouteDefinition( start == $start, goal == $goal, $elements: elements )| to expand: | and with an element used in another route| matches: (?<=\s|^)(and|but)?\s+with\s+an\s+element\s+used\s+in\s+an(other)?\s+route(?=\s|$) result: | Token( $badel: target memberOf ($elements) )| to expand: | send failed route message "conflicting route" with blocking element| matches: (?<=\s|^)[Ss]end\s+failed\s+route\s+message\s+(.*?)\s+with\s+blocking\s+element(?=\s|$) found key "text" at index 1 result: | $operator.message( "conflicting route" + " on " + $badel.getId() + " for route: " + $start.getId() + " to " + $goal.getId() );| to expand: | discard the command| matches: (?<=\s|^)[Dd]iscard\s+(the)?\s+command(?=\s|$) result: | retract( $command );| to expand: | there is a set route command| matches: (?<=\s|^)(?:and)?\s+[Tt]here\s+is\s+an?(?=\s|$) result: | set route command| matches: (?<=\s|^)(?i:(route\s+set|set\s+route)\s+command)(?=\s|$) result: | $command: RouteCommand( $code: code == CommandCode.SET_ROUTE, $start: start, $goal: goal, $operator: operator )| to expand: | with a matching route definition| matches: (?<=\s|^)(and)?\s+with\s+a\s+matching\s+route\s+definition(?=\s|$) result: |$def: RouteDefinition( start == $start, goal == $goal, $elements: elements )| to expand: | establish set route| matches: (?<=\s|^)[Ee]stablish\s+set\s+route(?=\s|$) result: | Route route = new Route( $def ); insert( route );| to expand: | discard the command| matches: (?<=\s|^)[Dd]iscard\s+(the)?\s+command(?=\s|$) result: | retract( $command );| to expand: | there is a set route| matches: (?<=\s|^)(?:and)?\s+[Tt]here\s+is\s+an?(?=\s|$) result: | set route| matches: (?<=\s|^)(?i:set\s+route)(?=\s|$) result: | $route: Route( state == RouteState.SET, $start: start )| to expand: | and its start signal| matches: (?<=\s|^)and\s+its\s+start\s+signal(?=\s|$) result: | Signal( this == $start )| to expand: | - is not locked| matches: (?<=\s|^)-\s*is\s+(?:not\s+|un)locked(?=\s|$) result: | locked == false| to expand: | - is not faulty| matches: (?<=\s|^)-\s*(?:has\s+no\s+fault|is\s+not\s+faulty)(?=\s|$) result: | faulty == false| to expand: | and all of it's track circuits are free| matches: (?<=\s|^)and\s+all\s+of\s+it's\s+track\s+circuits\s+are\s+free(?=\s|$) result: | not ( TrackCircuit( clear != true ) from $route.trackCircuits )| to expand: | set the start signal to clear| matches: (?<=\s|^)set\s+the\s+start\s+signal\s+to\s+(?i:CLEAR)(?=\s|$) result: | modify( $start ){ setIndication( Indication.CLEAR ) }| to expand: | change the route state to proving| matches: (?<=\s|^)change\s+the\s+route\s+state\s+to\s+proving(?=\s|$) result: | modify( $route ){ setState( RouteState.PROVING ) }| === DRL xpanded from DSLR === package appl.domain.element; import appl.domain.route.*; # # element command checks # rule "command: throw-over for switch" salience 100 when $command: Command( $element: element, $code: code, $operator: operator, code == CommandCode.THROW_OVER ) Element( this == $element, eval( ! ($element instanceof Switch) ) ) then $operator.message( "cannot throw over element" + ": " + $element.getId() ); end rule "commmand: lock/unlock for switch, signal, goal" salience 100 when $command: Command( $element: element, $code: code, $operator: operator, code == CommandCode.LOCK || code == CommandCode.UNLOCK ) Element( this == $element, eval( ! ($element instanceof Lock) ) ) then $operator.message( "cannot lock/unlock element" + ": " + $element.getId() ); end rule "commmand: lock for locked switch, signal, goal" salience 100 when $command: Command( $element: element, $code: code, $operator: operator, code == CommandCode.LOCK ) Lock( this == $element, locked == true ) then $operator.message( "cannot lock element with lock" + ": " + $element.getId() ); end rule "commmand: unlock for unlocked switch, signal, goal" salience 100 when $command: Command( $element: element, $code: code, $operator: operator, code == CommandCode.UNLOCK ) Lock( this == $element, locked == false ) then $operator.message( "cannot unlock element without lock" + ": " + $element.getId() ); end rule "command: halt for signal" salience 100 when $command: Command( $element: element, $code: code, $operator: operator, code == CommandCode.HALT ) Element( this == $element, eval( ! ($element instanceof Signal) ) ) then $operator.message( "cannot halt element" + ": " + $element.getId() ); end # # element command executione # rule "execute element command" when $command: Command( $element: element, $code: code, $operator: operator ) then modify( $element ){ execute( $code ) } retract( $command ); end # # route command checks # rule "route: no definition" salience 100 when $command: RouteCommand( $code: code == CommandCode.SET_ROUTE, $start: start, $goal: goal, $operator: operator ) not RouteDefinition( start == $start, goal == $goal ) then $operator.message( "no such route" + ": " + $start.getId() + " to " + $goal.getId() ); retract( $command ); end rule "refuse defined route if goal locked" salience 100 when $command: RouteCommand( $code: code == CommandCode.SET_ROUTE, $start: start, $goal: goal, $operator: operator ) $def: RouteDefinition( start == $start, goal == $goal, $elements: elements ) $badel: Goal( this == $goal, locked == true ) then $operator.message( "goal locked" + " on " + $badel.getId() + " for route: " + $start.getId() + " to " + $goal.getId() ); retract( $command ); end rule "refuse route if interdiction on any element" salience 100 when $command: RouteCommand( $code: code == CommandCode.SET_ROUTE, $start: start, $goal: goal, $operator: operator ) $def: RouteDefinition( start == $start, goal == $goal, $elements: elements ) $badel: Interdiction( this in ($elements), barred == true ) then $operator.message( "goal locked" + " on " + $badel.getId() + " for route: " + $start.getId() + " to " + $goal.getId() ); retract( $command ); end rule "refuse route if switch locked in wrong position" salience 100 when $command: RouteCommand( $code: code == CommandCode.SET_ROUTE, $start: start, $goal: goal, $operator: operator ) $def: RouteDefinition( start == $start, goal == $goal, $elements: elements ) $badel: Switch( locked == true, position != ($def.getPositionOf($badel)) ) from $def.switches then $operator.message( "switch locked in wrong position" + " on " + $badel.getId() + " for route: " + $start.getId() + " to " + $goal.getId() ); retract( $command ); end rule "refuse route if element already used by route" salience 100 when $command: RouteCommand( $code: code == CommandCode.SET_ROUTE, $start: start, $goal: goal, $operator: operator ) $def: RouteDefinition( start == $start, goal == $goal, $elements: elements ) Token( $badel: target memberOf ($elements) ) then $operator.message( "conflicting route" + " on " + $badel.getId() + " for route: " + $start.getId() + " to " + $goal.getId() ); retract( $command ); end rule "establish route" when $command: RouteCommand( $code: code == CommandCode.SET_ROUTE, $start: start, $goal: goal, $operator: operator ) $def: RouteDefinition( start == $start, goal == $goal, $elements: elements ) then Route route = new Route( $def ); insert( route ); retract( $command ); end rule "set signal to drive" when $route: Route( state == RouteState.SET, $start: start ) Signal( this == $start, locked == false, faulty == false ) not ( TrackCircuit( clear != true ) from $route.trackCircuits ) then modify( $start ){ setIndication( Indication.CLEAR ) } modify( $route ){ setState( RouteState.PROVING ) } end =============================