├── .gitignore
├── .project
├── .pydevproject
├── .settings
├── org.deved.antlride.core.prefs
└── org.eclipse.core.resources.prefs
├── License.txt
├── README.rst
├── README.txt
├── images
├── action.jpg
├── agendaGroup.jpg
├── attributeActrion.jpg
├── attributeStmt.jpg
├── classConstraint.jpg
├── condition.jpg
├── forgetAction.jpg
├── gittip.png
├── haltAction.jpg
├── learnAction.jpg
├── modifyAction.jpg
├── ruleStmt.jpg
├── simpleStmt.jpg
├── then.jpg
└── when.jpg
├── intellect
├── Callable.py
├── IO.py
├── Intellect.py
├── Node.py
├── PolicyLexer.py
├── PolicyTokenSource.py
├── __init__.py
├── examples
│ ├── __init__.py
│ ├── bahBahBlackSheep
│ │ ├── BagOfWool.py
│ │ ├── BlackSheep.py
│ │ ├── BuyOrder.py
│ │ ├── Example.py
│ │ ├── __init__.py
│ │ └── rulesets
│ │ │ ├── __init__.py
│ │ │ └── example.policy
│ └── testing
│ │ ├── ClassA.py
│ │ ├── ClassCandD.py
│ │ ├── ExerciseGrammar.py
│ │ ├── ExerciseIntellect.py
│ │ ├── Test.py
│ │ ├── __init__.py
│ │ ├── rulesets
│ │ ├── __init__.py
│ │ ├── test_a.policy
│ │ ├── test_b.policy
│ │ ├── test_c.policy
│ │ ├── test_d.policy
│ │ ├── test_e.policy
│ │ └── test_f.policy
│ │ └── subModule
│ │ ├── ClassB.py
│ │ └── __init__.py
├── grammar
│ ├── Policy.g
│ ├── Policy.tokens
│ ├── PolicyLexer.py
│ ├── PolicyParser.py
│ └── __init__.py
└── reflection.py
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.py[co]
2 |
3 | # Packages
4 | *.egg
5 | *.egg-info
6 | dist
7 | build
8 | eggs
9 | parts
10 | bin
11 | var
12 | sdist
13 | develop-eggs
14 | .installed.cfg
15 |
16 | # Installer logs
17 | pip-log.txt
18 |
19 | # Unit test / coverage reports
20 | .coverage
21 | .tox
22 |
23 | #Translations
24 | *.mo
25 |
26 | #Mr Developer
27 | .mr.developer.cfg
28 |
29 | /Intellect.egg-info
30 | /dist
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | Intellect
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.dltk.core.scriptbuilder
10 |
11 |
12 |
13 |
14 | org.python.pydev.PyDevBuilder
15 |
16 |
17 |
18 |
19 |
20 | org.python.pydev.pythonNature
21 | org.deved.antlride.core.nature
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.pydevproject:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Default
6 | python 2.7
7 |
8 | /Intellect
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.settings/org.deved.antlride.core.prefs:
--------------------------------------------------------------------------------
1 | antlr_core_builder_Xconversiontimeout=1000
2 | antlr_core_builder_Xdfaverbose=false
3 | antlr_core_builder_Xm=4
4 | antlr_core_builder_Xmaxdfaedges=65534
5 | antlr_core_builder_Xnocollapse=false
6 | antlr_core_builder_Xnomergestopstates=false
7 | antlr_core_builder_Xnoprune=false
8 | antlr_core_builder_dfa=false
9 | antlr_core_builder_include_stacktrace_on_internal_errors=false
10 | antlr_core_builder_max_number_of_problems_reported_per_grammar=25
11 | antlr_core_builder_nfa=false
12 | antlr_core_builder_report=false
13 | antlr_core_builder_runtime=3.1.3
14 | antlr_core_code_generator_XdbgST=false
15 | antlr_core_code_generator_append_java_package_to_out_folder=false
16 | antlr_core_code_generator_debug=false
17 | antlr_core_code_generator_max_memory=0
18 | antlr_core_code_generator_out_folder=antlr-generated
19 | antlr_core_code_generator_out_option=g
20 | antlr_core_code_generator_profile=false
21 | antlr_core_code_generator_trace=false
22 | antlr_core_code_generator_x_max_switch_case_labels=300
23 | antlr_core_code_generator_x_min_switch_alts=3
24 | antlr_core_mark_generated_resources_as_derived=true
25 | eclipse.preferences.version=1
26 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | encoding//intellect/Callable.py=utf-8
3 | encoding//intellect/IO.py=utf-8
4 | encoding//intellect/Intellect.py=utf-8
5 | encoding//intellect/Node.py=utf-8
6 | encoding//intellect/PolicyLexer.py=utf-8
7 | encoding//intellect/PolicyTokenSource.py=utf-8
8 | encoding//intellect/examples/bahBahBlackSheep/Example.py=utf-8
9 | encoding//intellect/examples/testing/ExerciseGrammar.py=utf-8
10 | encoding//intellect/examples/testing/ExerciseIntellect.py=utf-8
11 | encoding/setup.py=utf-8
12 |
--------------------------------------------------------------------------------
/License.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011, The MITRE Corporation
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 | 1. Redistributions of source code must retain the above copyright
7 | notice, this list of conditions and the following disclaimer.
8 | 2. Redistributions in binary form must reproduce the above copyright
9 | notice, this list of conditions and the following disclaimer in the
10 | documentation and/or other materials provided with the distribution.
11 | 3. All advertising materials mentioning features or use of this software
12 | must display the following acknowledgement:
13 | This product includes software developed by the author.
14 | 4. Neither the name of the author nor the
15 | names of its contributors may be used to endorse or promote products
16 | derived from this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
19 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | Intellect
2 | =========
3 |
4 | :Info: Intellect is a Domain-specific language and Rules Engine for Python.
5 |
6 | :Author: Michael Joseph Walsh
7 |
8 | 1. What is Intellect
9 | --------------------
10 |
11 | Intellect is a DSL ("Domain-Specific Language") and Rule Engine for Python
12 | I authored for expressing policies to orchestrate and control a dynamic
13 | network defense cyber-security platform being researched in The
14 | MITRE Corporation's Innovation Program.
15 |
16 | The rules engine provides an intellect, a form of artificial intelligence,
17 | a faculty of reasoning and understanding objectively over a working memory.
18 | The memory retains knowledge relevant to the system, and a set of rules
19 | authored in the DSL that describe a necessary behavior to achieve some
20 | goal. Each rule has an optional condition, and a suite of one or more
21 | actions. These actions either further direct the behavior of the system,
22 | and/or further inform the system. The engine starts with some facts,
23 | truths known about past or present circumstances, and uses rules to infer
24 | more facts. These facts fire more rules, that infer more facts and so
25 | on.
26 |
27 | For the platform in the Innovation Program, the network defender uses
28 | the DSL to confer policy, how the platform is to respond to network
29 | events mounted over covert network channels, but there are no direct
30 | ties written into the language nor the rule engine to cyber security
31 | and thus the system in its entirety can be more broadly used in
32 | other domains.
33 |
34 | 2. TODOS
35 | --------
36 |
37 | There are number of improvements I would like to work for future releases:
38 |
39 | * Add Support for Multiple Rule Conditions.
40 | * Move to an ANTLR4 based parser. The ANTLR Runtime for Python dependency was left behind with the the release of ANTL4. Luckily, n ANTLR 4 runtime for both Python 2 and 3 target is being tackled by Eric Vergnaud and is taking `shape `_. The last I looked all execParser and execLexer tests passed. Woot!
41 | * Support Python 3.
42 |
43 | Please help support these efforts.
44 |
45 | .. image:: https://github.com/nemonik/Intellect/raw/master/images/gittip.png
46 | :target: https://www.gittip.com/nemonik/
47 |
48 | 3. Intellect In The News
49 | ---------------------
50 |
51 | The September 2013 issue, Volume 37 of Elsevier's "Computers and Security" contains a journal article entitled "`Active cyber defense with denial and deception: A cyber-wargame experiment `_" describing a computer network security use case for Intellect.
52 |
53 | 4. Installation
54 | ---------------
55 |
56 | * To install via `setuptools `_ use ``easy_install -U Intellect``
57 | * To install via `pip `_ use ``pip install Intellect``
58 | * To install via `pypm `_ use ``pypm install intellect``
59 | * Or download the latest source from `Master `_ or the most recent tagged release `Tagged Releases `_, unpack, and run ``python setup.py install``
60 |
61 | 5. Dependencies
62 | ---------------
63 |
64 | * `ANTLR3 Python Runtime `_ that will contrain you to Python 2.x. In the past I've noted here that "Python 3 at present is not supported, because ANTLR3 appears not to support Python 3." Well, that's not exactly true, there is a Python3 runtime, I just wasn't aware of it nor have I worked with it. It can be found here `ANTLR3 Python3 Runtime `_
65 | * Python itself, if you don't already have it. I've tested the code on Python 2.7.1 and 2.7.2., but will likely work on any and all Python 2.x versions.
66 |
67 | 6. Source Code Contributions
68 | ----------------------------
69 |
70 | The source code is available under the BSD 4-clause license. If you have ideas,
71 | code, bug reports, or fixes you would like to contribute please do so.
72 |
73 | Bugs and feature requests can be filed at `Github `_.
74 |
75 | 7. Background
76 | -------------
77 |
78 | Many production rule system implementations have been open-sourced, such as
79 | JBoss Drools, Rools, Jess, Lisa, et cetera. If you're familiar with the
80 | Drools syntax, Intellect's syntax should look familiar. (I'm not saying it
81 | is based on it, because it is not entirely, but I found as I was working
82 | the syntax I would check with Drools and if made sense to push in the
83 | direction of Drools, this is what I did.) The aforementioned implementations
84 | are available for other languages for expressing production rules, but it is
85 | my belief that Python is under-represented, and as such it was my thought the
86 | language and rule engine could benefit from being open sourced, and so I put
87 | a request in.
88 |
89 | The MITRE Corporation granted release August 4, 2011.
90 |
91 | Thus, releasing the domain-specific language (DSL) and Rule Engine to Open
92 | Source in the hopes doing so will extend its use and increase its chances
93 | for possible adoption, while at the same time mature the project with more
94 | interested eyeballs being placed on it.
95 |
96 | Starting out, it was initially assumed the aforementioned platform would
97 | be integrated with the best Open Source rules engine available for
98 | Python as there are countless implementation for Ruby, Java, and Perl,
99 | but surprisingly I found none fitting the project's needs. This led to
100 | the thought of inventing one; simply typing the keywords "python rules
101 | engine" into Google though will return to you the advice "to not invent
102 | yet another rules language", but instead you are advised to "just write
103 | your rules in Python, import them, and execute them." The basis for this
104 | advice can be coalesced down to doing so otherwise does not fit with the
105 | "Python Philosophy." At the time, I did not believe this to be true, nor
106 | fully contextualized, and yet admittedly, I had not yet authored a line
107 | of Python code (Yes, you're looking at my first Python program. So,
108 | please give me a break.) nor used ANTLR3 prior to this effort. Looking
109 | back, I firmly believe the act of inventing a rules engine and abstracting it
110 | behind a nomenclature that describes and illuminates a specific domain is
111 | the best way for in case of aforementioned platform the network defender
112 | to think about the problem. Like I said though the DSL and rules engine
113 | could be used for anything needing a "production rule system".
114 |
115 | As there were no rules engines available for Python fitting the platforms
116 | needs, a policy language and naive forward chaining rules engine were built
117 | from scratch. The policy language's grammar is based on a subset of Python
118 | language syntax. The policy DSL is parsed and lexed with the help of the
119 | ANTLR3 Parse Generator and Runtime for Python.
120 |
121 |
122 | 8. Facts (Data being reasoned over)
123 | -----------------------------------
124 |
125 | The interpreter, the rules engine, and the remainder of the code such as
126 | objects for conferring discrete network conditions, referred to as "facts",
127 | are also authored in Python. Python's approach to the object-oriented programming
128 | paradigm, where objects consist of data fields and methods, did not easily
129 | lend itself to describing "facts". Because the data fields of a Python object
130 | referred to syntactically as "attributes" can and often are set on an
131 | instance of a class, they will not exist prior to a class's instantiation.
132 | In order for a rules engine to work, it must be able to fully introspect an
133 | object instance representing a condition. This proves to be very difficult
134 | unless the property decorator with its two attributes, "getter" and "setter",
135 | introduced in Python 2.6, are adopted and formally used for authoring these objects.
136 | Coincidentally, the use of the "Getter/Setter Pattern" used frequently in
137 | Java is singularly frowned upon in the Python developer community with the
138 | cheer of "Python is not Java".
139 |
140 | So, you will need to author your facts as Python object's who attributes
141 | are formally denoted as properties like so for the attributes you would like to
142 | reason over::
143 |
144 | class ClassA(object):
145 | '''
146 | An example fact
147 | '''
148 |
149 | def __init__(self, property0 = None, property1 = None):
150 | '''
151 | ClassA initializer
152 | '''
153 | self._property0 = property0
154 |
155 | @property
156 | def property0(self):
157 | return self._property0
158 |
159 | @property0.setter
160 | def property0(self, value):
161 | self._property0 = value
162 |
163 | 9. The Policy DSL
164 | -----------------
165 |
166 | Example with policy files can be found at the path `intellect/examples `_.
167 | Policy files must follow the Policy grammar as define in `intellect/grammar/Policy.g `_.
168 | The rest of this section documents the grammar of policy domain-specific language.
169 |
170 | 9.1 Import Statements (``ImportStmts``)
171 | ---------------------------------------
172 |
173 | Import statements basically follow Python's with a few limitations. For
174 | example, The wild card form of import is not supported for the reasons
175 | elaborated `here `_
176 | and follow the Python 2.7.2 grammar. ``ImportStmt`` statements exist only at the same
177 | level of ``ruleStmt`` statements as per the grammar, and are typically at the top of a
178 | policy file, but are not limited to. In fact, if you break up your policy across several
179 | files the last imported as class or module wins as the one being named.
180 |
181 | .. _9.2:
182 |
183 | 9.2 Attribute Statements (``attribute``)
184 | ----------------------------------------
185 |
186 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/attributeStmt.jpg
187 |
188 | The syntax diagram for a ``attributeStmt``.
189 |
190 | ``attributeStmt`` statements are expressions used to create policy attributes, a form of
191 | globals, that are accessible from rules.
192 |
193 | For example, a policy could be written::
194 |
195 | import logging
196 |
197 | first_sum = 0
198 | second_sum = 0
199 |
200 | rule "set both first_sum and second_sum to 1":
201 | agenda-group "test_d"
202 | then:
203 | attribute (first_sum, second_sum) = (1,1)
204 | log("first_sum is {0}".format(first_sum), "example", logging.DEBUG)
205 | log("second_sum is {0}".format(second_sum), "example", logging.DEBUG)
206 |
207 | rule "add 2":
208 | agenda-group "test_d"
209 | then:
210 | attribute first_sum += 2
211 | attribute second_sum += 2
212 | log("first_sum is {0}".format(first_sum), "example", logging.DEBUG)
213 | log("second_sum is {0}".format(second_sum), "example", logging.DEBUG)
214 |
215 | rule "add 3":
216 | agenda-group "test_d"
217 | then:
218 | attribute first_sum += 3
219 | attribute second_sum += 3
220 | log("first_sum is {0}".format(first_sum), "example", logging.DEBUG)
221 | log("second_sum is {0}".format(second_sum), "example", logging.DEBUG)
222 |
223 | rule "add 4":
224 | agenda-group "test_d"
225 | then:
226 | attribute first_sum += 4
227 | attribute second_sum += 4
228 | log("first_sum is {0}".format(first_sum), "example", logging.DEBUG)
229 | log("second_sum is {0}".format(second_sum), "example", logging.DEBUG)
230 | halt
231 |
232 | rule "should never get here":
233 | agenda-group "test_d"
234 | then:
235 | log("Then how did I get here?", "example", logging.DEBUG)
236 |
237 | containing the two ``atributeStmt`` statements::
238 |
239 | first_sum = 0
240 | second_sum = 0
241 |
242 | The following rules will increment these two attributes using ``attributeAction``
243 | statements.
244 |
245 | Code to exercise this policy would look like so::
246 |
247 | class MyIntellect(Intellect):
248 | pass
249 |
250 | if __name__ == "__main__":
251 |
252 | # set up logging for the example
253 | logger = logging.getLogger('example')
254 | logger.setLevel(logging.DEBUG)
255 |
256 | consoleHandler = logging.StreamHandler(stream=sys.stdout)
257 | consoleHandler.setFormatter(logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s%(message)s'))
258 | logger.addHandler(consoleHandler)
259 |
260 | myIntellect = MyIntellect()
261 |
262 | policy_d = myIntellect.learn(Intellect.local_file_uri("./rulesets/test_d.policy"))
263 |
264 | myIntellect.reason(["test_d"])
265 |
266 | and the logging output from the execution of the above would be::
267 |
268 | 2011-10-04 23:56:51,681 example DEBUG __main__.MyIntellect :: first_sum is 1
269 | 2011-10-04 23:56:51,682 example DEBUG __main__.MyIntellect :: second_sum is 1
270 | 2011-10-04 23:56:51,683 example DEBUG __main__.MyIntellect :: first_sum is 3
271 | 2011-10-04 23:56:51,683 example DEBUG __main__.MyIntellect :: second_sum is 3
272 | 2011-10-04 23:56:51,685 example DEBUG __main__.MyIntellect :: first_sum is 6
273 | 2011-10-04 23:56:51,685 example DEBUG __main__.MyIntellect :: second_sum is 6
274 | 2011-10-04 23:56:51,687 example DEBUG __main__.MyIntellect :: first_sum is 10
275 | 2011-10-04 23:56:51,687 example DEBUG __main__.MyIntellect :: second_sum is 10
276 |
277 | See section 9.3.3.1.2_ ``attributeAction`` for another example.
278 |
279 | 9.3 Rule Statements (``ruleStmt``)
280 | ----------------------------------
281 |
282 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/ruleStmt.jpg
283 |
284 | The syntax diagram for a ``ruleStmt``.
285 |
286 | A rule statement at its simplest looks like so::
287 |
288 | rule "print":
289 | then:
290 | print("hello world!!!!")
291 |
292 | The rule ``"print"`` will always activate and output ``hello world!!!!`` to the
293 | ``sys.stdout``.
294 |
295 | A rule will always have an identifier (``id``) in either a ``NAME`` or ``STRING``
296 | token form following Python's naming and ``String`` conventions.
297 |
298 | Generally, a rule will have both a ``when`` portion containing the condition
299 | of the rule, as of now a ``ruleCondition``, and an ``action`` described by the
300 | ``then`` portion. The ``action`` can be thought of in Python-terms as having more
301 | specifically a suite of one ore more actions.
302 |
303 | Depending on the evaluation of ``condition``, facts in knowledge will be matched
304 | and then operated over in the action of the rule.
305 |
306 | Such as in the rule ``"delete those that don't match"``, all facts in knowledge
307 | of type ``ClassD`` who's ``property1`` value is either a ``1`` or ``2`` or ``3``
308 | will be deleted in action of the rule.
309 |
310 | ::
311 |
312 | from intellect.testing.ClassCandD import ClassD
313 |
314 | rule "delete those that don't match":
315 | when:
316 | not $bar := ClassD(property1 in [1,2,3])
317 | then:
318 | delete $bar
319 |
320 | 9.3.1 ``agenda-group`` rule property
321 | ------------------------------------
322 |
323 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/agendaGroup.jpg
324 |
325 | The syntax diagram for a ``agendaGroup``.
326 |
327 | Optionally, a rule may have an ``agenda-group`` property that allows it to be
328 | grouped in to agenda groups, and fired on an agenda.
329 |
330 | See sections 9.2_ ``attribute`` and 9.3.3.1.2_ ``attributeAction`` for examples
331 | of the use of this property.
332 |
333 | 9.3.2 When
334 | ----------
335 |
336 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/when.jpg
337 |
338 | The syntax diagram for a ``when``.
339 |
340 | If present in rule, it defines the condition on which the rule will be activated.
341 |
342 | 9.3.2.1 Rule Condition (``condition``)
343 | --------------------------------------
344 |
345 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/condition.jpg
346 |
347 | The syntax diagram for a ``condition``.
348 |
349 | A rule may have an optional condition, a boolean evaluation, on the state of objects
350 | in knowledge defined by a Class Constraint (``classConstraint``), and may be
351 | optionally prepended with ``exists`` as follows::
352 |
353 | rule rule_c:
354 | when:
355 | exists $classB := ClassB(property1.startswith("apple") and property2>5 and test.greaterThanTen(property2) and aMethod() == "a")
356 | then:
357 | print( "matches" + " exist" )
358 | a = 1
359 | b = 2
360 | c = a + b
361 | print(c)
362 | test.helloworld()
363 | # call MyIntellect's bar method as it is decorated as callable
364 | bar()
365 |
366 | and thus the action will be called once if there are any object in memory matching
367 | the condition. The action statements ``modify`` and ``delete`` may not be used in
368 | the action if ``exists`` prepends the ``classContraint``.
369 |
370 | Currently, the DSL only supports a single ``classConstraint``, but work is ongoing
371 | to support more than one.
372 |
373 | 9.3.2.1.1 A Class Constraint (``classConstraint``)
374 | --------------------------------------------------
375 |
376 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/classConstraint.jpg
377 |
378 | The syntax diagram for a ``classConsraint``.
379 |
380 | A ``classContraint`` defines how an objects in knowledge will be matched. It defines an
381 | ``OBJECTBINDING``, the Python name of the object's class and the optional ``constraint``
382 | by which objects will be matched in knowledge.
383 |
384 | The ``OBJECTBINDING`` is a ``NAME`` token following Python's naming convention prepended
385 | with a dollar-sign (``$``).
386 |
387 | As in the case of the Rule Condition example::
388 |
389 | exists $classB := ClassB(property1.startswith("apple") and property2>5 and test.greaterThanTen(property2) and aMethod() == "a")
390 |
391 |
392 | ``$classB`` is the ``OBJECTBINDING`` that binds the matches of facts of type
393 | ``ClassB`` in knowledge matching the ``constraint``.
394 |
395 | An ``OBJECTBINDING`` can be further used in the action of the rule, but not in the
396 | case where the ``condition`` is prepended with ``exists`` as in the example.
397 |
398 | 9.3.2.1.2 A Constraint
399 | ----------------------
400 |
401 | A ``constraint`` follows the same basic ``and``, ``or``, and ``not`` grammar that Python
402 | follows.
403 |
404 | As in the case of the Rule Condition example::
405 |
406 | exists $classB := ClassB(property1.startswith("apple") and property2>5 and test.greaterThanTen(property2) and aMethod() == "a")
407 |
408 | All ``ClassB`` type facts are matched in knowledge that have ``property1`` attributes
409 | that ``startwith`` ``apple``, and ``property2`` attributes greater than ``5`` before
410 | evaluated in hand with ``exist`` statement. More on the rest of the constraint follows
411 | in the sections below.
412 |
413 | 9.3.2.1.2.1 Using Regular Expressions
414 | -------------------------------------
415 |
416 | You can also use regular expressions in constraint by simply importing the
417 | regular expression library straight from Python and then using like so as
418 | in the case of the Rule Condition example::
419 |
420 | $classB := ClassB( re.search(r"\bapple\b", property1)!=None and property2>5 and test.greaterThanTen(property2) and aMethod() == "a")
421 |
422 | The regular expression ``r"\bapple\b"`` search is performed on ``property1`` of
423 | objects of type ``ClassB`` in knowledge.
424 |
425 | 9.3.2.1.2.2 Using Methods
426 | -------------------------
427 |
428 | To rewrite a complicated ``constraint``:
429 | ````````````````````````````````````````
430 |
431 | If you are writing a very complicated ``constraint`` consider moving the
432 | evaluation necessary for the ``constraint`` into a method of fact being
433 | reasoned over to increase readability.
434 |
435 | As in the case of the Rule Condition example, it could be rewritten to::
436 |
437 | $classB := ClassB(property1ContainsTheStrApple() and property2>5 and test.greaterThanTen(property2) and aMethod() == "a")
438 |
439 | If you were to add the method to ClassB::
440 |
441 | def property1ContainsTheStrApple()
442 | return re.search(r"\bapple\b", property1) != None
443 |
444 | Of a class and/or instance:
445 | ```````````````````````````
446 |
447 | This example, also demonstrates how the ``test`` module function ``greaterThanTen``
448 | can be messaged the instance's ``property2`` attribute and the function's return
449 | evaluated, and a call to the instance's ``aMethod`` method can be evaluated for
450 | a return of ``"a"``.
451 |
452 | 9.3.3 Then
453 | ----------
454 |
455 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/then.jpg
456 |
457 | The syntax diagram for a ``then``.
458 |
459 | Is used to define the suite of one-or-more ``action`` statements to be called
460 | firing the rule, when the rule is said to be activated.
461 |
462 | 9.3.3.1 Rule Action (Suite of Actions)
463 | --------------------------------------
464 |
465 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/action.jpg
466 |
467 | The syntax diagram for an ``action``.
468 |
469 | Rules may have a suite of one or more actions used in process of doing something,
470 | typically to achieve an aim.
471 |
472 | 9.3.3.1.1 Simple Statements (``simpleStmt``)
473 | --------------------------------------------
474 |
475 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/simpleStmt.jpg
476 |
477 | The syntax diagram for a ``simpleStmt``.
478 |
479 | ``simpleStmts`` are supported actions of a rule, and so one can do the following::
480 |
481 | rule rule_c:
482 | when:
483 | exists $classB := ClassB(property1.startswith("apple") and property2>5 and test.greaterThanTen(property2) and aMethod() == "a")
484 | then:
485 | print("matches" + " exist")
486 | a = 1
487 | b = 2
488 | c = a + b
489 | print(c)
490 | test.helloworld()
491 | bar()
492 |
493 | The ``simpleStmt`` in the action will be executed if any facts in knowledge
494 | exist matching the condition.
495 |
496 | To keep the policy files from turning into just another Python script you
497 | will want to keep as little code out of the suite of actions and thus the policy
498 | file was possible... You will want to focus on using ``modify``, ``delete``,
499 | ``insert``, ``halt`` before heavily using large amounts of simple statements. This
500 | is why ``action`` supports a limited Python grammar. ``if``, ``for``, ``while`` etc
501 | are not supported, only Python's ``expressionStmt`` statements are supported.
502 |
503 | .. _9.3.3.1.2:
504 |
505 | 9.3.3.1.2 ``attributeAction``
506 | -----------------------------
507 |
508 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/attributeStmt.jpg
509 |
510 | The syntax diagram for a ``attributeStmt``.
511 |
512 | ``attributeAction`` actions are used to create, delete, or modify a policy
513 | attribute.
514 |
515 | For example::
516 |
517 | i = 0
518 |
519 | rule rule_e:
520 | agenda-group "1"
521 | then:
522 | attribute i = i + 1
523 | print i
524 |
525 | rule rule_f:
526 | agenda-group "2"
527 | then:
528 | attribute i = i + 1
529 | print i
530 |
531 | rule rule_g:
532 | agenda-group "3"
533 | then:
534 | attribute i = i + 1
535 | print i
536 |
537 | rule rule_h:
538 | agenda-group "4"
539 | then:
540 | # the 'i' variable is scoped to then portion of the rule
541 | i = 0
542 | print i
543 |
544 | rule rule_i:
545 | agenda-group "5"
546 | then:
547 | attribute i += 1
548 | print i
549 | # the 'i' variable is scoped to then portion of the rule
550 | i = 0
551 |
552 | rule rule_j:
553 | agenda-group "6"
554 | then:
555 | attribute i += 1
556 | print i
557 |
558 | If the rules engine is instructed to reason seeking to activate
559 | rules on agenda in the order describe by the Python list
560 | ``["1", "2", "3", "4", "5", "6"]`` like so::
561 |
562 | class MyIntellect(Intellect):
563 | pass
564 |
565 | if __name__ == "__main__":
566 |
567 | myIntellect = MyIntellect()
568 |
569 | policy_c = myIntellect.learn(Intellect.local_file_uri("./rulesets/test_c.policy"))
570 |
571 | myIntellect.reason(["1", "2", "3", "4", "5", "6"])
572 |
573 | The following output will result::
574 |
575 | 1
576 | 2
577 | 3
578 | 0
579 | 4
580 | 5
581 |
582 | When firing ``rule_e`` the policy attribute ``i`` will be incremented by a value
583 | of ``1``, and print ``1``, same with ``rule_f`` and ``rule_g``, but ``rule_h``
584 | prints 0. The reason for this is the ``i`` variable is scoped to ``then`` portion
585 | of the rule. ``Rule_i`` further illustrates scoping: the policy attribute ``i``
586 | is further incremented by ``1`` and is printed, and then a variable ``i`` scoped to
587 | ``then`` portion of the rule initialized to ``0``, but this has no impact on
588 | the policy attribute ``i`` for when ``rule_j`` action is executed firing the rule
589 | the value of ``6`` is printed.
590 |
591 | 9.3.3.1.3 ``learn`` action
592 | --------------------------
593 |
594 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/learnAction.jpg
595 | :scale: 50 %
596 |
597 | The syntax diagram for a ``learnAction``.
598 |
599 | A rule entitled ``"Time to buy new sheep?"`` might look like the following::
600 |
601 | rule "Time to buy new sheep?":
602 | when:
603 | $buyOrder := BuyOrder( )
604 | then:
605 | print( "Buying a new sheep." )
606 | modify $buyOrder:
607 | count = $buyOrder.count - 1
608 | learn BlackSheep()
609 |
610 | The rule above illustrates the use of a ``learn`` action to learn/insert
611 | a ``BlackSheep`` fact. The same rule can also be written as the following
612 | using ``insert``::
613 |
614 | rule "Time to buy new sheep?":
615 | when:
616 | $buyOrder := BuyOrder( )
617 | then:
618 | print( "Buying a new sheep." )
619 | modify $buyOrder:
620 | count = $buyOrder.count - 1
621 | insert BlackSheep()
622 |
623 | 9.3.3.1.4 ``forget`` action
624 | ---------------------------
625 |
626 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/forgetAction.jpg
627 |
628 | The syntax diagram for a ``forgetAction``.
629 |
630 |
631 | A rule entitled ``"Remove empty buy orders"`` might look like the following::
632 |
633 | rule "Remove empty buy orders":
634 | when:
635 | $buyOrder := BuyOrder( count == 0 )
636 | then:
637 | forget $buyOrder
638 |
639 |
640 | The rule above illustrates the use of a ``forget`` action to forget/delete
641 | each match returned by the rule's condition. The same rule can also be written
642 | as the following using ``delete``::
643 |
644 | rule "Remove empty buy orders":
645 | when:
646 | $buyOrder := BuyOrder( count == 0 )
647 | then:
648 | delete $buyOrder
649 |
650 | Note: cannot be used in conjunction with ``exists``.
651 |
652 | 9.3.3.1.5 ``modify`` action
653 | ---------------------------
654 |
655 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/modifyAction.jpg
656 |
657 | The syntax diagram for a ``modifyAction``.
658 |
659 | The following rule::
660 |
661 | rule "Time to buy new sheep?":
662 | when:
663 | $buyOrder := BuyOrder( )
664 | then:
665 | print( "Buying a new sheep." )
666 | modify $buyOrder:
667 | count = $buyOrder.count - 1
668 | learn BlackSheep()
669 |
670 |
671 | illustrates the use of a ``modify`` action to modify each ``BuyOrder`` match
672 | returned by the rule's condition. Cannot be used in conjunction with ``exists``
673 | rule conditions. The ``modify`` action can also be used to chain rules, what
674 | you do is modify the fact (toggle a boolean property, set a property's value,
675 | etc) and then use this property to evaluate in the proceeding rule.
676 |
677 |
678 | 9.3.3.1.6 ``halt`` action
679 | -------------------------
680 |
681 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/haltAction.jpg
682 |
683 | The syntax diagram for a ``haltAction``.
684 |
685 | The following rule::
686 |
687 | rule "End policy":
688 | then:
689 | log("Finished reasoning over policy.", "example", logging.DEBUG)
690 | halt
691 |
692 | illustrates the use of a ``halt`` action to tell the rules engine to halt
693 | reasoning over the policy.
694 |
695 | 10. Creating and using a Rules Engine with a single policy
696 | ---------------------------------------------------------
697 |
698 | At its simplest a rules engine can be created and used like so::
699 |
700 | import sys, logging
701 |
702 | from intellect.Intellect import Intellect
703 | from intellect.Intellect import Callable
704 |
705 | # set up logging
706 | logging.basicConfig(level=logging.DEBUG,
707 | format='%(asctime)s %(name)-12s%(levelname)-8s%(message)s', stream=sys.stdout)
708 |
709 | intellect = Intellect()
710 |
711 | policy_a = intellect.learn(Intellect.local_file_uri("../rulesets/test_a.policy"))
712 |
713 | intellect.reason()
714 |
715 | intellect.forget_all()
716 |
717 |
718 | It may be preferable for you to sub-class ``intellect.Intellect.Intellect`` class in
719 | order to add ``@Callable`` decorated methods that will in turn permit these methods
720 | to be called from the action of the rule.
721 |
722 | For example, ``MyIntellect`` is created to sub-class ``Intellect``::
723 |
724 | import sys, logging
725 |
726 | from intellect.Intellect import Intellect
727 | from intellect.Intellect import Callable
728 |
729 | class MyIntellect(Intellect):
730 |
731 | @Callable
732 | def bar(self):
733 | self.log(logging.DEBUG, ">>>>>>>>>>>>>> called MyIntellect's bar method as it was decorated as callable.")
734 |
735 | if __name__ == "__main__":
736 |
737 | # set up logging
738 | logging.basicConfig(level=logging.DEBUG,
739 | format='%(asctime)s %(name)-12s%(levelname)-8s%(message)s',
740 | #filename="rules.log")
741 | stream=sys.stdout)
742 |
743 | print "*"*80
744 | print """create an instance of MyIntellect extending Intellect, create some facts, and exercise the MyIntellect's ability to learn and forget"""
745 | print "*"*80
746 |
747 | myIntellect = MyIntellect()
748 |
749 | policy_a = myIntellect.learn(Intellect.local_file_uri("../rulesets/test_a.policy"))
750 |
751 | myIntellect.reason()
752 |
753 | myIntellect.forget_all()
754 |
755 |
756 | The policy could then be authored, where the ``MyIntellect`` class's ``bar`` method
757 | is called for matches to the rule condition, like so::
758 |
759 | from intellect.testing.subModule.ClassB import ClassB
760 | import intellect.testing.Test as Test
761 | import logging
762 |
763 | fruits_of_interest = ["apple", "grape", "mellon", "pear"]
764 | count = 5
765 |
766 | rule rule_a:
767 | agenda-group test_a
768 | when:
769 | $classB := ClassB( property1 in fruits_of_interest and property2>count )
770 | then:
771 | # mark the 'ClassB' matches in memory as modified
772 | modify $classB:
773 | property1 = $classB.property1 + " pie"
774 | modified = True
775 | # increment the match's 'property2' value by 1000
776 | property2 = $classB.property2 + 1000
777 |
778 | attribute count = $classB.property2
779 | print "count = {0}".format( count )
780 |
781 | # call MyIntellect's bar method as it is decorated as callable
782 | bar()
783 | log(logging.DEBUG, "rule_a fired")
784 |
--------------------------------------------------------------------------------
/README.txt:
--------------------------------------------------------------------------------
1 | Intellect
2 | =========
3 |
4 | :Info: Intellect is a Domain-specific language and Rules Engine for Python.
5 |
6 | :Author: Michael Joseph Walsh
7 |
8 | 1. What is Intellect
9 | --------------------
10 |
11 | Intellect is a DSL ("Domain-Specific Language") and Rule Engine for Python
12 | I authored for expressing policies to orchestrate and control a dynamic
13 | network defense cyber-security platform being researched in The
14 | MITRE Corporation's Innovation Program.
15 |
16 | The rules engine provides an intellect, a form of artificial intelligence,
17 | a faculty of reasoning and understanding objectively over a working memory.
18 | The memory retains knowledge relevant to the system, and a set of rules
19 | authored in the DSL that describe a necessary behavior to achieve some
20 | goal. Each rule has an optional condition, and a suite of one or more
21 | actions. These actions either further direct the behavior of the system,
22 | and/or further inform the system. The engine starts with some facts,
23 | truths known about past or present circumstances, and uses rules to infer
24 | more facts. These facts fire more rules, that infer more facts and so
25 | on.
26 |
27 | For the platform in the Innovation Program, the network defender uses
28 | the DSL to confer policy, how the platform is to respond to network
29 | events mounted over covert network channels, but there are no direct
30 | ties written into the language nor the rule engine to cyber security
31 | and thus the system in its entirety can be more broadly used in
32 | other domains.
33 |
34 | 2. TODOS
35 | --------
36 |
37 | There are number of improvements I would like to work for future releases:
38 |
39 | * Add Support for Multiple Rule Conditions.
40 | * The ANTLR Runtime for Python dependency was left behind with the the release of ANTL4, I would like to port the present Runtime for Java to Python and use.
41 | * Support Python 3.
42 |
43 | Please help support these efforts.
44 |
45 | .. image:: https://github.com/nemonik/Intellect/raw/master/images/gittip.png
46 | :target: https://www.gittip.com/nemonik/
47 |
48 | 3. Intellect In The News
49 | ---------------------
50 |
51 | The September 2013 issue, Volume 37 of Elsevier's "Computers and Security" contains a journal article entitled "`Active cyber defense with denial and deception: A cyber-wargame experiment `_" describing a computer network security use case for Intellect.
52 |
53 | 4. Installation
54 | ---------------
55 |
56 | * To install via `setuptools `_ use ``easy_install -U Intellect``
57 | * To install via `pip `_ use ``pip install Intellect``
58 | * To install via `pypm `_ use ``pypm install intellect``
59 | * Or download the latest source from `Master `_ or the most recent tagged release `Tagged Releases `_, unpack, and run ``python setup.py install``
60 |
61 | 5. Dependencies
62 | ---------------
63 |
64 | * ANTLR3 Python Runtime 3.1.3 (Will contrain you to Python 2.x.)
65 | * Python itself, if you don't already have it. I've tested the code on Python 2.7.1 and 2.7.2., but will likely work on any and all Python 2.x versions. Python 3 at prestent is not supported, because ANTLR3 appears not to support Python 3.
66 |
67 | 6. Source Code Contributions
68 | ----------------------------
69 |
70 | The source code is available under the BSD 4-clause license. If you have ideas,
71 | code, bug reports, or fixes you would like to contribute please do so.
72 |
73 | Bugs and feature requests can be filed at `Github `_.
74 |
75 | 7. Background
76 | -------------
77 |
78 | Many production rule system implementations have been open-sourced, such as
79 | JBoss Drools, Rools, Jess, Lisa, et cetera. If you're familiar with the
80 | Drools syntax, Intellect's syntax should look familiar. (I'm not saying it
81 | is based on it, because it is not entirely, but I found as I was working
82 | the syntax I would check with Drools and if made sense to push in the
83 | direction of Drools, this is what I did.) The aforementioned implementations
84 | are available for other languages for expressing production rules, but it is
85 | my belief that Python is under-represented, and as such it was my thought the
86 | language and rule engine could benefit from being open sourced, and so I put
87 | a request in.
88 |
89 | The MITRE Corporation granted release August 4, 2011.
90 |
91 | Thus, releasing the domain-specific language (DSL) and Rule Engine to Open
92 | Source in the hopes doing so will extend its use and increase its chances
93 | for possible adoption, while at the same time mature the project with more
94 | interested eyeballs being placed on it.
95 |
96 | Starting out, it was initially assumed the aforementioned platform would
97 | be integrated with the best Open Source rules engine available for
98 | Python as there are countless implementation for Ruby, Java, and Perl,
99 | but surprisingly I found none fitting the project's needs. This led to
100 | the thought of inventing one; simply typing the keywords "python rules
101 | engine" into Google though will return to you the advice "to not invent
102 | yet another rules language", but instead you are advised to "just write
103 | your rules in Python, import them, and execute them." The basis for this
104 | advice can be coalesced down to doing so otherwise does not fit with the
105 | "Python Philosophy." At the time, I did not believe this to be true, nor
106 | fully contextualized, and yet admittedly, I had not yet authored a line
107 | of Python code (Yes, you're looking at my first Python program. So,
108 | please give me a break.) nor used ANTLR3 prior to this effort. Looking
109 | back, I firmly believe the act of inventing a rules engine and abstracting it
110 | behind a nomenclature that describes and illuminates a specific domain is
111 | the best way for in case of aforementioned platform the network defender
112 | to think about the problem. Like I said though the DSL and rules engine
113 | could be used for anything needing a "production rule system".
114 |
115 | As there were no rules engines available for Python fitting the platforms
116 | needs, a policy language and naive forward chaining rules engine were built
117 | from scratch. The policy language's grammar is based on a subset of Python
118 | language syntax. The policy DSL is parsed and lexed with the help of the
119 | ANTLR3 Parse Generator and Runtime for Python.
120 |
121 |
122 | 8. Facts (Data being reasoned over)
123 | -----------------------------------
124 |
125 | The interpreter, the rules engine, and the remainder of the code such as
126 | objects for conferring discrete network conditions, referred to as "facts",
127 | are also authored in Python. Python's approach to the object-oriented programming
128 | paradigm, where objects consist of data fields and methods, did not easily
129 | lend itself to describing "facts". Because the data fields of a Python object
130 | referred to syntactically as "attributes" can and often are set on an
131 | instance of a class, they will not exist prior to a class's instantiation.
132 | In order for a rules engine to work, it must be able to fully introspect an
133 | object instance representing a condition. This proves to be very difficult
134 | unless the property decorator with its two attributes, "getter" and "setter",
135 | introduced in Python 2.6, are adopted and formally used for authoring these objects.
136 | Coincidentally, the use of the "Getter/Setter Pattern" used frequently in
137 | Java is singularly frowned upon in the Python developer community with the
138 | cheer of "Python is not Java".
139 |
140 | So, you will need to author your facts as Python object's who attributes
141 | are formally denoted as properties like so for the attributes you would like to
142 | reason over::
143 |
144 | class ClassA(object):
145 | '''
146 | An example fact
147 | '''
148 |
149 | def __init__(self, property0 = None, property1 = None):
150 | '''
151 | ClassA initializer
152 | '''
153 | self._property0 = property0
154 |
155 | @property
156 | def property0(self):
157 | return self._property0
158 |
159 | @property0.setter
160 | def property0(self, value):
161 | self._property0 = value
162 |
163 | 9. The Policy DSL
164 | -----------------
165 |
166 | Example with policy files can be found at the path `intellect/examples `_.
167 | Policy files must follow the Policy grammar as define in `intellect/grammar/Policy.g `_.
168 | The rest of this section documents the grammar of policy domain-specific language.
169 |
170 | 9.1 Import Statements (``ImportStmts``)
171 | ---------------------------------------
172 |
173 | Import statements basically follow Python's with a few limitations. For
174 | example, The wild card form of import is not supported for the reasons
175 | elaborated `here `_
176 | and follow the Python 2.7.2 grammar. ``ImportStmt`` statements exist only at the same
177 | level of ``ruleStmt`` statements as per the grammar, and are typically at the top of a
178 | policy file, but are not limited to. In fact, if you break up your policy across several
179 | files the last imported as class or module wins as the one being named.
180 |
181 | .. _9.2:
182 |
183 | 9.2 Attribute Statements (``attribute``)
184 | ----------------------------------------
185 |
186 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/attributeStmt.jpg
187 |
188 | The syntax diagram for a ``attributeStmt``.
189 |
190 | ``attributeStmt`` statements are expressions used to create policy attributes, a form of
191 | globals, that are accessible from rules.
192 |
193 | For example, a policy could be written::
194 |
195 | import logging
196 |
197 | first_sum = 0
198 | second_sum = 0
199 |
200 | rule "set both first_sum and second_sum to 1":
201 | agenda-group "test_d"
202 | then:
203 | attribute (first_sum, second_sum) = (1,1)
204 | log("first_sum is {0}".format(first_sum), "example", logging.DEBUG)
205 | log("second_sum is {0}".format(second_sum), "example", logging.DEBUG)
206 |
207 | rule "add 2":
208 | agenda-group "test_d"
209 | then:
210 | attribute first_sum += 2
211 | attribute second_sum += 2
212 | log("first_sum is {0}".format(first_sum), "example", logging.DEBUG)
213 | log("second_sum is {0}".format(second_sum), "example", logging.DEBUG)
214 |
215 | rule "add 3":
216 | agenda-group "test_d"
217 | then:
218 | attribute first_sum += 3
219 | attribute second_sum += 3
220 | log("first_sum is {0}".format(first_sum), "example", logging.DEBUG)
221 | log("second_sum is {0}".format(second_sum), "example", logging.DEBUG)
222 |
223 | rule "add 4":
224 | agenda-group "test_d"
225 | then:
226 | attribute first_sum += 4
227 | attribute second_sum += 4
228 | log("first_sum is {0}".format(first_sum), "example", logging.DEBUG)
229 | log("second_sum is {0}".format(second_sum), "example", logging.DEBUG)
230 | halt
231 |
232 | rule "should never get here":
233 | agenda-group "test_d"
234 | then:
235 | log("Then how did I get here?", "example", logging.DEBUG)
236 |
237 | containing the two ``atributeStmt`` statements::
238 |
239 | first_sum = 0
240 | second_sum = 0
241 |
242 | The following rules will increment these two attributes using ``attributeAction``
243 | statements.
244 |
245 | Code to exercise this policy would look like so::
246 |
247 | class MyIntellect(Intellect):
248 | pass
249 |
250 | if __name__ == "__main__":
251 |
252 | # set up logging for the example
253 | logger = logging.getLogger('example')
254 | logger.setLevel(logging.DEBUG)
255 |
256 | consoleHandler = logging.StreamHandler(stream=sys.stdout)
257 | consoleHandler.setFormatter(logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s%(message)s'))
258 | logger.addHandler(consoleHandler)
259 |
260 | myIntellect = MyIntellect()
261 |
262 | policy_d = myIntellect.learn(Intellect.local_file_uri("./rulesets/test_d.policy"))
263 |
264 | myIntellect.reason(["test_d"])
265 |
266 | and the logging output from the execution of the above would be::
267 |
268 | 2011-10-04 23:56:51,681 example DEBUG __main__.MyIntellect :: first_sum is 1
269 | 2011-10-04 23:56:51,682 example DEBUG __main__.MyIntellect :: second_sum is 1
270 | 2011-10-04 23:56:51,683 example DEBUG __main__.MyIntellect :: first_sum is 3
271 | 2011-10-04 23:56:51,683 example DEBUG __main__.MyIntellect :: second_sum is 3
272 | 2011-10-04 23:56:51,685 example DEBUG __main__.MyIntellect :: first_sum is 6
273 | 2011-10-04 23:56:51,685 example DEBUG __main__.MyIntellect :: second_sum is 6
274 | 2011-10-04 23:56:51,687 example DEBUG __main__.MyIntellect :: first_sum is 10
275 | 2011-10-04 23:56:51,687 example DEBUG __main__.MyIntellect :: second_sum is 10
276 |
277 | See section 9.3.3.1.2_ ``attributeAction`` for another example.
278 |
279 | 9.3 Rule Statements (``ruleStmt``)
280 | ----------------------------------
281 |
282 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/ruleStmt.jpg
283 |
284 | The syntax diagram for a ``ruleStmt``.
285 |
286 | A rule statement at its simplest looks like so::
287 |
288 | rule "print":
289 | then:
290 | print("hello world!!!!")
291 |
292 | The rule ``"print"`` will always activate and output ``hello world!!!!`` to the
293 | ``sys.stdout``.
294 |
295 | A rule will always have an identifier (``id``) in either a ``NAME`` or ``STRING``
296 | token form following Python's naming and ``String`` conventions.
297 |
298 | Generally, a rule will have both a ``when`` portion containing the condition
299 | of the rule, as of now a ``ruleCondition``, and an ``action`` described by the
300 | ``then`` portion. The ``action`` can be thought of in Python-terms as having more
301 | specifically a suite of one ore more actions.
302 |
303 | Depending on the evaluation of ``condition``, facts in knowledge will be matched
304 | and then operated over in the action of the rule.
305 |
306 | Such as in the rule ``"delete those that don't match"``, all facts in knowledge
307 | of type ``ClassD`` who's ``property1`` value is either a ``1`` or ``2`` or ``3``
308 | will be deleted in action of the rule.
309 |
310 | ::
311 |
312 | from intellect.testing.ClassCandD import ClassD
313 |
314 | rule "delete those that don't match":
315 | when:
316 | not $bar := ClassD(property1 in [1,2,3])
317 | then:
318 | delete $bar
319 |
320 | 9.3.1 ``agenda-group`` rule property
321 | ------------------------------------
322 |
323 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/agendaGroup.jpg
324 |
325 | The syntax diagram for a ``agendaGroup``.
326 |
327 | Optionally, a rule may have an ``agenda-group`` property that allows it to be
328 | grouped in to agenda groups, and fired on an agenda.
329 |
330 | See sections 9.2_ ``attribute`` and 9.3.3.1.2_ ``attributeAction`` for examples
331 | of the use of this property.
332 |
333 | 9.3.2 When
334 | ----------
335 |
336 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/when.jpg
337 |
338 | The syntax diagram for a ``when``.
339 |
340 | If present in rule, it defines the condition on which the rule will be activated.
341 |
342 | 9.3.2.1 Rule Condition (``condition``)
343 | --------------------------------------
344 |
345 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/condition.jpg
346 |
347 | The syntax diagram for a ``condition``.
348 |
349 | A rule may have an optional condition, a boolean evaluation, on the state of objects
350 | in knowledge defined by a Class Constraint (``classConstraint``), and may be
351 | optionally prepended with ``exists`` as follows::
352 |
353 | rule rule_c:
354 | when:
355 | exists $classB := ClassB(property1.startswith("apple") and property2>5 and test.greaterThanTen(property2) and aMethod() == "a")
356 | then:
357 | print( "matches" + " exist" )
358 | a = 1
359 | b = 2
360 | c = a + b
361 | print(c)
362 | test.helloworld()
363 | # call MyIntellect's bar method as it is decorated as callable
364 | bar()
365 |
366 | and thus the action will be called once if there are any object in memory matching
367 | the condition. The action statements ``modify`` and ``delete`` may not be used in
368 | the action if ``exists`` prepends the ``classContraint``.
369 |
370 | Currently, the DSL only supports a single ``classConstraint``, but work is ongoing
371 | to support more than one.
372 |
373 | 9.3.2.1.1 A Class Constraint (``classConstraint``)
374 | --------------------------------------------------
375 |
376 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/classConstraint.jpg
377 |
378 | The syntax diagram for a ``classConsraint``.
379 |
380 | A ``classContraint`` defines how an objects in knowledge will be matched. It defines an
381 | ``OBJECTBINDING``, the Python name of the object's class and the optional ``constraint``
382 | by which objects will be matched in knowledge.
383 |
384 | The ``OBJECTBINDING`` is a ``NAME`` token following Python's naming convention prepended
385 | with a dollar-sign (``$``).
386 |
387 | As in the case of the Rule Condition example::
388 |
389 | exists $classB := ClassB(property1.startswith("apple") and property2>5 and test.greaterThanTen(property2) and aMethod() == "a")
390 |
391 |
392 | ``$classB`` is the ``OBJECTBINDING`` that binds the matches of facts of type
393 | ``ClassB`` in knowledge matching the ``constraint``.
394 |
395 | An ``OBJECTBINDING`` can be further used in the action of the rule, but not in the
396 | case where the ``condition`` is prepended with ``exists`` as in the example.
397 |
398 | 9.3.2.1.2 A Constraint
399 | ----------------------
400 |
401 | A ``constraint`` follows the same basic ``and``, ``or``, and ``not`` grammar that Python
402 | follows.
403 |
404 | As in the case of the Rule Condition example::
405 |
406 | exists $classB := ClassB(property1.startswith("apple") and property2>5 and test.greaterThanTen(property2) and aMethod() == "a")
407 |
408 | All ``ClassB`` type facts are matched in knowledge that have ``property1`` attributes
409 | that ``startwith`` ``apple``, and ``property2`` attributes greater than ``5`` before
410 | evaluated in hand with ``exist`` statement. More on the rest of the constraint follows
411 | in the sections below.
412 |
413 | 9.3.2.1.2.1 Using Regular Expressions
414 | -------------------------------------
415 |
416 | You can also use regular expressions in constraint by simply importing the
417 | regular expression library straight from Python and then using like so as
418 | in the case of the Rule Condition example::
419 |
420 | $classB := ClassB( re.search(r"\bapple\b", property1)!=None and property2>5 and test.greaterThanTen(property2) and aMethod() == "a")
421 |
422 | The regular expression ``r"\bapple\b"`` search is performed on ``property1`` of
423 | objects of type ``ClassB`` in knowledge.
424 |
425 | 9.3.2.1.2.2 Using Methods
426 | -------------------------
427 |
428 | To rewrite a complicated ``constraint``:
429 | ````````````````````````````````````````
430 |
431 | If you are writing a very complicated ``constraint`` consider moving the
432 | evaluation necessary for the ``constraint`` into a method of fact being
433 | reasoned over to increase readability.
434 |
435 | As in the case of the Rule Condition example, it could be rewritten to::
436 |
437 | $classB := ClassB(property1ContainsTheStrApple() and property2>5 and test.greaterThanTen(property2) and aMethod() == "a")
438 |
439 | If you were to add the method to ClassB::
440 |
441 | def property1ContainsTheStrApple()
442 | return re.search(r"\bapple\b", property1) != None
443 |
444 | Of a class and/or instance:
445 | ```````````````````````````
446 |
447 | This example, also demonstrates how the ``test`` module function ``greaterThanTen``
448 | can be messaged the instance's ``property2`` attribute and the function's return
449 | evaluated, and a call to the instance's ``aMethod`` method can be evaluated for
450 | a return of ``"a"``.
451 |
452 | 9.3.3 Then
453 | ----------
454 |
455 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/then.jpg
456 |
457 | The syntax diagram for a ``then``.
458 |
459 | Is used to define the suite of one-or-more ``action`` statements to be called
460 | firing the rule, when the rule is said to be activated.
461 |
462 | 9.3.3.1 Rule Action (Suite of Actions)
463 | --------------------------------------
464 |
465 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/action.jpg
466 |
467 | The syntax diagram for an ``action``.
468 |
469 | Rules may have a suite of one or more actions used in process of doing something,
470 | typically to achieve an aim.
471 |
472 | 9.3.3.1.1 Simple Statements (``simpleStmt``)
473 | --------------------------------------------
474 |
475 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/simpleStmt.jpg
476 |
477 | The syntax diagram for a ``simpleStmt``.
478 |
479 | ``simpleStmts`` are supported actions of a rule, and so one can do the following::
480 |
481 | rule rule_c:
482 | when:
483 | exists $classB := ClassB(property1.startswith("apple") and property2>5 and test.greaterThanTen(property2) and aMethod() == "a")
484 | then:
485 | print("matches" + " exist")
486 | a = 1
487 | b = 2
488 | c = a + b
489 | print(c)
490 | test.helloworld()
491 | bar()
492 |
493 | The ``simpleStmt`` in the action will be executed if any facts in knowledge
494 | exist matching the condition.
495 |
496 | To keep the policy files from turning into just another Python script you
497 | will want to keep as little code out of the suite of actions and thus the policy
498 | file was possible... You will want to focus on using ``modify``, ``delete``,
499 | ``insert``, ``halt`` before heavily using large amounts of simple statements. This
500 | is why ``action`` supports a limited Python grammar. ``if``, ``for``, ``while`` etc
501 | are not supported, only Python's ``expressionStmt`` statements are supported.
502 |
503 | .. _9.3.3.1.2:
504 |
505 | 9.3.3.1.2 ``attributeAction``
506 | -----------------------------
507 |
508 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/attributeStmt.jpg
509 |
510 | The syntax diagram for a ``attributeStmt``.
511 |
512 | ``attributeAction`` actions are used to create, delete, or modify a policy
513 | attribute.
514 |
515 | For example::
516 |
517 | i = 0
518 |
519 | rule rule_e:
520 | agenda-group "1"
521 | then:
522 | attribute i = i + 1
523 | print i
524 |
525 | rule rule_f:
526 | agenda-group "2"
527 | then:
528 | attribute i = i + 1
529 | print i
530 |
531 | rule rule_g:
532 | agenda-group "3"
533 | then:
534 | attribute i = i + 1
535 | print i
536 |
537 | rule rule_h:
538 | agenda-group "4"
539 | then:
540 | # the 'i' variable is scoped to then portion of the rule
541 | i = 0
542 | print i
543 |
544 | rule rule_i:
545 | agenda-group "5"
546 | then:
547 | attribute i += 1
548 | print i
549 | # the 'i' variable is scoped to then portion of the rule
550 | i = 0
551 |
552 | rule rule_j:
553 | agenda-group "6"
554 | then:
555 | attribute i += 1
556 | print i
557 |
558 | If the rules engine is instructed to reason seeking to activate
559 | rules on agenda in the order describe by the Python list
560 | ``["1", "2", "3", "4", "5", "6"]`` like so::
561 |
562 | class MyIntellect(Intellect):
563 | pass
564 |
565 | if __name__ == "__main__":
566 |
567 | myIntellect = MyIntellect()
568 |
569 | policy_c = myIntellect.learn(Intellect.local_file_uri("./rulesets/test_c.policy"))
570 |
571 | myIntellect.reason(["1", "2", "3", "4", "5", "6"])
572 |
573 | The following output will result::
574 |
575 | 1
576 | 2
577 | 3
578 | 0
579 | 4
580 | 5
581 |
582 | When firing ``rule_e`` the policy attribute ``i`` will be incremented by a value
583 | of ``1``, and print ``1``, same with ``rule_f`` and ``rule_g``, but ``rule_h``
584 | prints 0. The reason for this is the ``i`` variable is scoped to ``then`` portion
585 | of the rule. ``Rule_i`` further illustrates scoping: the policy attribute ``i``
586 | is further incremented by ``1`` and is printed, and then a variable ``i`` scoped to
587 | ``then`` portion of the rule initialized to ``0``, but this has no impact on
588 | the policy attribute ``i`` for when ``rule_j`` action is executed firing the rule
589 | the value of ``6`` is printed.
590 |
591 | 9.3.3.1.3 ``learn`` action
592 | --------------------------
593 |
594 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/learnAction.jpg
595 | :scale: 50 %
596 |
597 | The syntax diagram for a ``learnAction``.
598 |
599 | A rule entitled ``"Time to buy new sheep?"`` might look like the following::
600 |
601 | rule "Time to buy new sheep?":
602 | when:
603 | $buyOrder := BuyOrder( )
604 | then:
605 | print( "Buying a new sheep." )
606 | modify $buyOrder:
607 | count = $buyOrder.count - 1
608 | learn BlackSheep()
609 |
610 | The rule above illustrates the use of a ``learn`` action to learn/insert
611 | a ``BlackSheep`` fact. The same rule can also be written as the following
612 | using ``insert``::
613 |
614 | rule "Time to buy new sheep?":
615 | when:
616 | $buyOrder := BuyOrder( )
617 | then:
618 | print( "Buying a new sheep." )
619 | modify $buyOrder:
620 | count = $buyOrder.count - 1
621 | insert BlackSheep()
622 |
623 | 9.3.3.1.4 ``forget`` action
624 | ---------------------------
625 |
626 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/forgetAction.jpg
627 |
628 | The syntax diagram for a ``forgetAction``.
629 |
630 |
631 | A rule entitled ``"Remove empty buy orders"`` might look like the following::
632 |
633 | rule "Remove empty buy orders":
634 | when:
635 | $buyOrder := BuyOrder( count == 0 )
636 | then:
637 | forget $buyOrder
638 |
639 |
640 | The rule above illustrates the use of a ``forget`` action to forget/delete
641 | each match returned by the rule's condition. The same rule can also be written
642 | as the following using ``delete``::
643 |
644 | rule "Remove empty buy orders":
645 | when:
646 | $buyOrder := BuyOrder( count == 0 )
647 | then:
648 | delete $buyOrder
649 |
650 | Note: cannot be used in conjunction with ``exists``.
651 |
652 | 9.3.3.1.5 ``modify`` action
653 | ---------------------------
654 |
655 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/modifyAction.jpg
656 |
657 | The syntax diagram for a ``modifyAction``.
658 |
659 | The following rule::
660 |
661 | rule "Time to buy new sheep?":
662 | when:
663 | $buyOrder := BuyOrder( )
664 | then:
665 | print( "Buying a new sheep." )
666 | modify $buyOrder:
667 | count = $buyOrder.count - 1
668 | learn BlackSheep()
669 |
670 |
671 | illustrates the use of a ``modify`` action to modify each ``BuyOrder`` match
672 | returned by the rule's condition. Cannot be used in conjunction with ``exists``
673 | rule conditions. The ``modify`` action can also be used to chain rules, what
674 | you do is modify the fact (toggle a boolean property, set a property's value,
675 | etc) and then use this property to evaluate in the proceeding rule.
676 |
677 |
678 | 9.3.3.1.6 ``halt`` action
679 | -------------------------
680 |
681 | .. figure:: https://github.com/nemonik/Intellect/raw/master/images/haltAction.jpg
682 |
683 | The syntax diagram for a ``haltAction``.
684 |
685 | The following rule::
686 |
687 | rule "End policy":
688 | then:
689 | log("Finished reasoning over policy.", "example", logging.DEBUG)
690 | halt
691 |
692 | illustrates the use of a ``halt`` action to tell the rules engine to halt
693 | reasoning over the policy.
694 |
695 | 10. Creating and using a Rules Engine with a single policy
696 | ---------------------------------------------------------
697 |
698 | At its simplest a rules engine can be created and used like so::
699 |
700 | import sys, logging
701 |
702 | from intellect.Intellect import Intellect
703 | from intellect.Intellect import Callable
704 |
705 | # set up logging
706 | logging.basicConfig(level=logging.DEBUG,
707 | format='%(asctime)s %(name)-12s%(levelname)-8s%(message)s', stream=sys.stdout)
708 |
709 | intellect = Intellect()
710 |
711 | policy_a = intellect.learn(Intellect.local_file_uri("../rulesets/test_a.policy"))
712 |
713 | intellect.reason()
714 |
715 | intellect.forget_all()
716 |
717 |
718 | It may be preferable for you to sub-class ``intellect.Intellect.Intellect`` class in
719 | order to add ``@Callable`` decorated methods that will in turn permit these methods
720 | to be called from the action of the rule.
721 |
722 | For example, ``MyIntellect`` is created to sub-class ``Intellect``::
723 |
724 | import sys, logging
725 |
726 | from intellect.Intellect import Intellect
727 | from intellect.Intellect import Callable
728 |
729 | class MyIntellect(Intellect):
730 |
731 | @Callable
732 | def bar(self):
733 | self.log(logging.DEBUG, ">>>>>>>>>>>>>> called MyIntellect's bar method as it was decorated as callable.")
734 |
735 | if __name__ == "__main__":
736 |
737 | # set up logging
738 | logging.basicConfig(level=logging.DEBUG,
739 | format='%(asctime)s %(name)-12s%(levelname)-8s%(message)s',
740 | #filename="rules.log")
741 | stream=sys.stdout)
742 |
743 | print "*"*80
744 | print """create an instance of MyIntellect extending Intellect, create some facts, and exercise the MyIntellect's ability to learn and forget"""
745 | print "*"*80
746 |
747 | myIntellect = MyIntellect()
748 |
749 | policy_a = myIntellect.learn(Intellect.local_file_uri("../rulesets/test_a.policy"))
750 |
751 | myIntellect.reason()
752 |
753 | myIntellect.forget_all()
754 |
755 |
756 | The policy could then be authored, where the ``MyIntellect`` class's ``bar`` method
757 | is called for matches to the rule condition, like so::
758 |
759 | from intellect.testing.subModule.ClassB import ClassB
760 | import intellect.testing.Test as Test
761 | import logging
762 |
763 | fruits_of_interest = ["apple", "grape", "mellon", "pear"]
764 | count = 5
765 |
766 | rule rule_a:
767 | agenda-group test_a
768 | when:
769 | $classB := ClassB( property1 in fruits_of_interest and property2>count )
770 | then:
771 | # mark the 'ClassB' matches in memory as modified
772 | modify $classB:
773 | property1 = $classB.property1 + " pie"
774 | modified = True
775 | # increment the match's 'property2' value by 1000
776 | property2 = $classB.property2 + 1000
777 |
778 | attribute count = $classB.property2
779 | print "count = {0}".format( count )
780 |
781 | # call MyIntellect's bar method as it is decorated as callable
782 | bar()
783 | log(logging.DEBUG, "rule_a fired")
784 |
--------------------------------------------------------------------------------
/images/action.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nemonik/Intellect/4fdc9eb00f874900a4353333e1026cf7f4d1dba7/images/action.jpg
--------------------------------------------------------------------------------
/images/agendaGroup.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nemonik/Intellect/4fdc9eb00f874900a4353333e1026cf7f4d1dba7/images/agendaGroup.jpg
--------------------------------------------------------------------------------
/images/attributeActrion.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nemonik/Intellect/4fdc9eb00f874900a4353333e1026cf7f4d1dba7/images/attributeActrion.jpg
--------------------------------------------------------------------------------
/images/attributeStmt.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nemonik/Intellect/4fdc9eb00f874900a4353333e1026cf7f4d1dba7/images/attributeStmt.jpg
--------------------------------------------------------------------------------
/images/classConstraint.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nemonik/Intellect/4fdc9eb00f874900a4353333e1026cf7f4d1dba7/images/classConstraint.jpg
--------------------------------------------------------------------------------
/images/condition.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nemonik/Intellect/4fdc9eb00f874900a4353333e1026cf7f4d1dba7/images/condition.jpg
--------------------------------------------------------------------------------
/images/forgetAction.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nemonik/Intellect/4fdc9eb00f874900a4353333e1026cf7f4d1dba7/images/forgetAction.jpg
--------------------------------------------------------------------------------
/images/gittip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nemonik/Intellect/4fdc9eb00f874900a4353333e1026cf7f4d1dba7/images/gittip.png
--------------------------------------------------------------------------------
/images/haltAction.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nemonik/Intellect/4fdc9eb00f874900a4353333e1026cf7f4d1dba7/images/haltAction.jpg
--------------------------------------------------------------------------------
/images/learnAction.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nemonik/Intellect/4fdc9eb00f874900a4353333e1026cf7f4d1dba7/images/learnAction.jpg
--------------------------------------------------------------------------------
/images/modifyAction.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nemonik/Intellect/4fdc9eb00f874900a4353333e1026cf7f4d1dba7/images/modifyAction.jpg
--------------------------------------------------------------------------------
/images/ruleStmt.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nemonik/Intellect/4fdc9eb00f874900a4353333e1026cf7f4d1dba7/images/ruleStmt.jpg
--------------------------------------------------------------------------------
/images/simpleStmt.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nemonik/Intellect/4fdc9eb00f874900a4353333e1026cf7f4d1dba7/images/simpleStmt.jpg
--------------------------------------------------------------------------------
/images/then.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nemonik/Intellect/4fdc9eb00f874900a4353333e1026cf7f4d1dba7/images/then.jpg
--------------------------------------------------------------------------------
/images/when.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nemonik/Intellect/4fdc9eb00f874900a4353333e1026cf7f4d1dba7/images/when.jpg
--------------------------------------------------------------------------------
/intellect/Callable.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/python
2 | # coding=utf-8
3 | '''
4 | Copyright (c) 2011, The MITRE Corporation.
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without
8 | modification, are permitted provided that the following conditions are met:
9 | 1. Redistributions of source code must retain the above copyright
10 | notice, this list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright
12 | notice, this list of conditions and the following disclaimer in the
13 | documentation and/or other materials provided with the distribution.
14 | 3. All advertising materials mentioning features or use of this software
15 | must display the following acknowledgement:
16 | This product includes software developed by the author.
17 | 4. Neither the name of the author nor the
18 | names of its contributors may be used to endorse or promote products
19 | derived from this software without specific prior written permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
22 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | '''
32 |
33 |
34 | class Callable(object):
35 |
36 | '''
37 | A decorator used to confer to Policy object what
38 | methods are callable.
39 | '''
40 |
41 | def __init__(self, method):
42 | '''
43 | Callable Initializer
44 | '''
45 |
46 | self.__method = method
47 |
48 | def __call__(self, *args):
49 | '''
50 | Wrapper to the callable method
51 | '''
52 |
53 | return self.__method(*args)
54 |
55 | def __get__(self, obj, obj_type):
56 | """
57 | PFM to get self of method decorated
58 | """
59 |
60 | if obj is None:
61 | return self
62 |
63 | new_method = self.__method.__get__(obj, obj_type)
64 |
65 | return self.__class__(new_method)
66 |
--------------------------------------------------------------------------------
/intellect/IO.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/python
2 | # coding=utf-8
3 | """
4 | Copyright (c) 2011, The MITRE Corporation.
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without
8 | modification, are permitted provided that the following conditions are met:
9 | 1. Redistributions of source code must retain the above copyright
10 | notice, this list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright
12 | notice, this list of conditions and the following disclaimer in the
13 | documentation and/or other materials provided with the distribution.
14 | 3. All advertising materials mentioning features or use of this software
15 | must display the following acknowledgement:
16 | This product includes software developed by the author.
17 | 4. Neither the name of the author nor the
18 | names of its contributors may be used to endorse or promote products
19 | derived from this software without specific prior written permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
22 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | """
32 |
33 | import os
34 | import sys
35 | import StringIO
36 |
37 |
38 | class RedirectStdError(object):
39 |
40 | def __init__(self):
41 | self._stderr = StringIO.StringIO()
42 |
43 | def __enter__(self):
44 | self.save_stderr = sys.stderr
45 | self.save_stderr.flush()
46 | sys.stderr = self._stderr
47 |
48 | return sys.stderr
49 |
50 | def __exit__(
51 | self,
52 | exc_type,
53 | exc_value,
54 | traceback,
55 | ):
56 | self._stderr.flush()
57 | sys.stderr = self.save_stderr
58 |
59 |
60 | class RedirectStdOut(object):
61 |
62 | def __init__(self):
63 | self._stdout = StringIO.StringIO()
64 |
65 | def __enter__(self):
66 | self.save_stdout = sys.stdout
67 | self.save_stdout.flush()
68 | sys.stdout = self._stdout
69 |
70 | return sys.stdout
71 |
72 | def __exit__(
73 | self,
74 | exc_type,
75 | exc_value,
76 | traceback,
77 | ):
78 | self._stdout.flush()
79 | sys.stdout = self.save_stdout
80 |
--------------------------------------------------------------------------------
/intellect/Intellect.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/python
2 | # coding=utf-8
3 | """
4 | Copyright (c) 2011, The MITRE Corporation.
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without
8 | modification, are permitted provided that the following conditions are met:
9 | 1. Redistributions of source code must retain the above copyright
10 | notice, this list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright
12 | notice, this list of conditions and the following disclaimer in the
13 | documentation and/or other materials provided with the distribution.
14 | 3. All advertising materials mentioning features or use of this software
15 | must display the following acknowledgement:
16 | This product includes software developed by the author.
17 | 4. Neither the name of the author nor the
18 | names of its contributors may be used to endorse or promote products
19 | derived from this software without specific prior written permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
22 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | """
32 |
33 | import logging
34 | import sys
35 | import urllib
36 | import urllib2
37 | import os
38 | import errno
39 |
40 | from urlparse import urlparse
41 |
42 | from antlr3 import CommonTokenStream, ANTLRStringStream
43 |
44 | from intellect.grammar.PolicyParser import PolicyParser
45 | from intellect.PolicyLexer import PolicyLexer
46 | from intellect.Node import Policy
47 | from intellect.Node import File
48 | from intellect.PolicyTokenSource import PolicyTokenSource
49 | from intellect.Callable import Callable
50 | from intellect.IO import RedirectStdError
51 |
52 |
53 |
54 | class Intellect(object):
55 | '''
56 | Rules engine.
57 | '''
58 |
59 |
60 | def __init__(self):
61 | '''
62 | Intellect initializer.
63 | '''
64 |
65 | # initialize list to hold learned objects
66 | self.knowledge = []
67 |
68 | # initialize the hold the combined sum of the policy files
69 | # as a single policy.
70 | self.policy = Policy()
71 |
72 |
73 | @property
74 | def knowledge(self):
75 | '''
76 | Returns the intellect's knowledge either a list of objects or an
77 | empty list.
78 | '''
79 | return self._knowledge
80 |
81 |
82 | @knowledge.setter
83 | def knowledge(self, one_or_more_objects):
84 | '''
85 | Keeper of knowledge.
86 |
87 | Holds facts aka objects in a list.
88 |
89 | Args:
90 | one_or_more_objects: Either a single facts or policy file or
91 | a list of facts and/or policy files.
92 | '''
93 |
94 | try:
95 | self.knowledge
96 | except AttributeError:
97 | self._knowledge = []
98 |
99 | if not isinstance(one_or_more_objects, list):
100 | self.knowledge.append(one_or_more_objects)
101 | elif one_or_more_objects != []:
102 | self.knowledge.extend(one_or_more_objects)
103 |
104 |
105 | @property
106 | def policy(self):
107 | '''
108 | Getter for the intellect's Policy object.
109 | '''
110 | return self._policy
111 |
112 |
113 | @policy.setter
114 | def policy(self, value):
115 | '''
116 | Setter for the intellect Policy object
117 |
118 | Args:
119 | value: a Policy object
120 | '''
121 | self._policy = value
122 |
123 |
124 | @staticmethod
125 | def local_file_uri(file_path):
126 | '''
127 | Helper/Utility method to take file system paths and return a file URI for use
128 | with learn, and learn_policy methods
129 |
130 | Args:
131 | file_path: The file path to the policy
132 |
133 | Returns:
134 | an equivalent file URI
135 | '''
136 | if os.path.isfile(file_path):
137 | try:
138 | with open(file_path):
139 | pass
140 |
141 | except IOError:
142 | # Permission denied, cannot read file.
143 | raise IOError(errno.EACCES, "Permission denied to policy locate at: {0}".format(file_path))
144 |
145 | else:
146 | # Cannot find file.
147 | raise IOError(errno.ENOENT, "Cannot find policy located at: {0}".format(file_path))
148 |
149 | full_path = urllib.pathname2url(os.path.abspath(file_path))
150 | if file_path.startswith("file://"):
151 | return full_path
152 |
153 | else:
154 | return "file://" + full_path
155 |
156 |
157 | @staticmethod
158 | def policy_from(urlstring):
159 | '''
160 | Helper/Utility method to retrieve a policy from a URL
161 |
162 | Uses proxies from environment.
163 |
164 | Args:
165 | urlstring: The URL to the policy file.
166 |
167 | Returns:
168 | The text of the policy.
169 | '''
170 |
171 | response = urllib2.urlopen(urlstring)
172 | response.headers['content-type'] = 'text/plain; charset=utf8'
173 | content = response.read()
174 | return content
175 |
176 |
177 | def learn(self, identifier):
178 | '''
179 | Learns an object fact, or learns a policy by messaging learn_policy
180 | method with the 'identifier' as a policy URL or the text of the policy,
181 | if the identifier is a string.
182 |
183 | Args:
184 | fact: an object or policy file/string to learn. Typically objects have
185 | annotated properties and/or methods that return values.
186 |
187 | Raises:
188 | ValueError: The fact or policy already exists in knowledge.
189 | TypeError: Raised when parameter 'identifier' is a NoneType.
190 | '''
191 | if identifier:
192 | if isinstance(identifier, basestring):
193 | return self.learn_policy(identifier)
194 | elif self.knowledge.count(identifier) == 0:
195 | self.knowledge.append(identifier)
196 | self.log("Learned: {0}:{1}".format(type(identifier), identifier.__dict__))
197 | else:
198 | raise ValueError("{0}:{1} already exists in knowledge.".format(type(identifier), identifier.__dict__))
199 | else:
200 | raise TypeError("parameter 'identifier' cannot be a NoneType.")
201 |
202 |
203 | def learn_policy(self, identifier):
204 | '''
205 | Learn a policy file.
206 |
207 | Args:
208 | identifier: a string, either a URL to a policy file or the text of the policy itself.
209 | Keep in mind a policy can be comprised of more than one policy file (a file containing
210 | valid policy DSL) or string containing policy DSL. This way you break your rule set,
211 | imports, and policy attributes across any number of files. See reason-method for more.
212 |
213 |
214 | Returns:
215 | The resulting File Node.
216 |
217 | Raises:
218 | ValueError: if the policy already exists in knowledge.
219 | TypeError: if parameter 'identifier' is a NoneType, or is not a string representing
220 | either a file path to a policy or the text of the policy itself.
221 | '''
222 |
223 | is_file = False
224 |
225 | if identifier:
226 | if isinstance(identifier, basestring):
227 | if urlparse(identifier).scheme:
228 |
229 | # Treat 'identifier' as an URL
230 | self.log("Learning policy from URL: {0}".format(identifier))
231 | stream = ANTLRStringStream(Intellect.policy_from(identifier))
232 | is_file = True
233 | else:
234 |
235 | #Treat 'identifier' as policy string
236 | self.log("Learning policy from string")
237 | stream = ANTLRStringStream(identifier)
238 |
239 | lexer = PolicyLexer(stream)
240 | tokens = CommonTokenStream(lexer)
241 | tokens.discardOffChannelTokens = True
242 | indented_source = PolicyTokenSource(tokens)
243 | tokens = CommonTokenStream(indented_source)
244 | parser = PolicyParser(tokens)
245 |
246 | with RedirectStdError() as stderr:
247 | try:
248 | # ANTL3 may raise an exception, and doing so the stderror
249 | # will not be printed hiding the underlying problem. GRRR!!!!
250 | file_node = parser.file()
251 | except Exception as e:
252 | if stderr.getvalue().rstrip() != "":
253 | trace = sys.exc_info()[2]
254 | raise Exception(stderr.getvalue().rstrip()), None, trace
255 | else:
256 | raise e
257 |
258 | # The ANTLR3 Recognizer class prints a number of ANTLR3 Exceptions to
259 | # stderr vice throwing an exception, because it will try to recover and
260 | # continue parsing.
261 | #
262 | # In the case of NoViableAltException, I've chosen to raise an
263 | # exception.
264 | #
265 | # Otherwise, all the other error message that Recognizer writes to
266 | # stderr will be returned for the benefit of the policy author.
267 | if stderr.getvalue().rstrip() != "":
268 | # check for stderror msg indicating an NoViableAltException occured.
269 | # if did raise an exception with the stderror message.
270 | if "no viable alternative at input" in stderr.getvalue().rstrip():
271 | raise Exception("Error parsing policy: {0}\n{1}".format(identifier, stderr.getvalue().rstrip()))
272 | else:
273 | print >> sys.stderr, stderr.getvalue().rstrip()
274 |
275 | # set path attribute
276 | file_node.path = identifier if is_file else None
277 |
278 | # associate the path to all descendants
279 | file_node.set_file_on_descendants(file_node, file_node)
280 |
281 | try:
282 | # determine if the policy already exists in knowledge
283 | self.policy.files.index(file_node)
284 | raise ValueError("Policy already exists in knowledge: {0}".format(identifier))
285 | except:
286 | pass
287 |
288 | # store add the policy file to the policy
289 | self.policy.append_child(file_node)
290 |
291 | self.log("learned a policy file")
292 |
293 | return file_node
294 |
295 | else:
296 | raise TypeError("parameter 'identifier' must be a string, either a file path to a policy or the text of the policy itself")
297 |
298 | else:
299 | raise TypeError("parameter 'identifier' cannot be a NoneType.")
300 |
301 |
302 | def learn_fact(self, identifier):
303 | '''
304 | Wrapper for 'learn' method
305 | '''
306 | self.learn(identifier)
307 |
308 |
309 | def forget(self, identifier):
310 | '''
311 | Forgets an id() of a fact or policy, or a string representing the path
312 | of a policy, or a fact.
313 |
314 | Args:
315 | identifier: is an id() of a fact or policy, or a string
316 | representing the URL of a policy, or a fact.
317 |
318 | Raises:
319 | ValueError: Raised when parameter 'identifier' has the right
320 | type but an inappropriate value.
321 | TypeError: Raised when parameter 'identifier' is a NoneType.
322 | '''
323 | if identifier:
324 | if isinstance(identifier, int):
325 | # remove the fact from the knowledge
326 | for index, fact in enumerate(self.knowledge):
327 | if identifier == id(fact):
328 | self.log("Forgetting fact with id: {0} of type: {1} from knowledge. fact.__dict__: {2}".format(identifier, type(fact), fact.__dict__))
329 | self.knowledge.remove(fact)
330 | return
331 | # fact doesn't exist in memory, attempt to remove a policy file/String
332 | # from knowledge with this identifier
333 | for index, policy_file in self.policy.files:
334 | if identifier == id(policy_file):
335 | self.log("Forgetting policy loaded from file path : {0}".format(identifier.path))
336 | self.policy.files.remove(policy_file)
337 | return
338 | # neither fact nor policy so raise an exception
339 | raise ValueError("Fact with id: {0} is not in knowledge".format(identifier))
340 | elif isinstance(identifier, basestring):
341 | # remove the policy policy file from knowledge
342 | try:
343 | for fileIndex, policy_file in enumerate(self.policy.files):
344 | if policy_file.path == identifier:
345 | self.policy.files.pop(fileIndex)
346 |
347 | self.log("Forgetting policy loaded from file path : {0}".format(identifier))
348 | except KeyError:
349 | raise ValueError("Policy for file path: {0} is not in knowledge".format(identifier))
350 | elif isinstance(identifier, File):
351 | try:
352 | index = self.policy.files.index(identifier)
353 | self.policy.files.pop(index)
354 | self.log("Forgetting policy loaded from file path : {0}".format(identifier.path))
355 | except:
356 | raise ValueError("Policy: {0} not in knowledge".format(identifier.path))
357 | else:
358 | try:
359 | self.knowledge.remove(identifier)
360 | self.log("Forgetting fact: {0}".format(identifier))
361 | except:
362 | raise ValueError("Fact: {0} is not in knowledge".format(identifier))
363 | else:
364 | raise TypeError("Parameter 'identifier' cannot be a NoneType.")
365 |
366 |
367 | def forget_fact(self, identifier):
368 | '''
369 | Wrapper for 'forget' method
370 |
371 | Args:
372 | identifier: is the id() of the fact, or the fact itself to forget
373 |
374 | Raises:
375 | See forget-method 'raises'.
376 | '''
377 | self.forget(identifier)
378 |
379 |
380 | def forget_policy(self, identifier):
381 | '''
382 | Wrapper for 'forget' method
383 |
384 | Args:
385 | identifier: is the either the path to the policy to forget, or
386 | the Policy object itself.
387 |
388 | Raises:
389 | TypeError: Raised when 'identifier' is not a basestring or Policy
390 | object.
391 | Also, see forget-method 'raises'.
392 | '''
393 | if isinstance(identifier, (basestring, File)):
394 | self.forget(identifier)
395 | else:
396 | raise TypeError("Parameter 'identifier': {0} was neither a path to the policy to forget, or a Policy object.".format(identifier))
397 |
398 |
399 | def forget_all(self):
400 | '''
401 | Forgets all facts in knowledge and the present policy
402 | '''
403 | self.knowledge = []
404 | self.policy = Policy()
405 |
406 | self.log("forgot all")
407 |
408 |
409 | def reason(self, agenda=None):
410 | '''
411 | Reasons across the facts in knowledge applying the policy.
412 |
413 | Args:
414 | agenda: is either the default of None or a list of agenda-group identifiers.
415 | If a rule is created with no agenda group attribute then the group will
416 | be associated with "MAIN" agenda group. If the 'agenda' attribute remains
417 | the default of None, then only the "MAIN" agenda group will fire.
418 |
419 | rule "flood the torpedo tubes":
420 | agenda group "firing sequence"
421 | when:
422 | ...
423 | then:
424 | ...
425 |
426 | So, in the scenario above an agenda may look like:
427 |
428 | agenda = ["targeting sequence", "firing sequence", "after firing sequence" ]
429 |
430 | First, all the rules associated with the "targeting sequence" agenda
431 | group will fire, then those associated with the "firing sequence"
432 | group...
433 |
434 | Note, any cumulative changes that occur to policy attributes are
435 | passed onto individual agenda groups.
436 |
437 | Remember, whatever is loaded last wins in terms of imports. At present rule
438 | names are not evaluated. So, it doesn't matter to the interpreter if you have
439 | two or more rules named the same, each will be evaluated. Attributes and import
440 | statement are evaluated top to bottom. Imports are evaluated first, then
441 | attributes, then rule statements.
442 |
443 | Raises:
444 | Any exceptions raised by the combined policy as it is evaluated will
445 | be raised here.
446 | '''
447 |
448 | #update the policy with the present Intellect
449 | self.policy.intellect = self
450 |
451 | # eval the policy using the described agenda
452 | self.policy.eval(agenda)
453 |
454 |
455 | @Callable
456 | def log(self, msg, name="intellect", level=logging.DEBUG):
457 | '''
458 | Logs at the 'level' for the messaged 'msg'
459 |
460 | Args:
461 | name: the name of the logger
462 | level: must be either logging.DEBUG, logging.INFO, logging.WARNING,
463 | logging.ERROR, logging.CRITICAL
464 | msg: message string
465 |
466 | Raises:
467 | ValueError: Raised when it receives an 'level' that is not either
468 | logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, or
469 | logging.CRITICAL.
470 | '''
471 |
472 | if level not in [logging.DEBUG, logging.INFO, logging.WARNING,
473 | logging.ERROR, logging.CRITICAL]:
474 | raise ValueError("A value of '{0}' for 'level' is invalid, must be either logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL".format(level))
475 |
476 | logging.getLogger(name).log(level, "{0}.{1} :: {2}".format(self.__class__.__module__, self.__class__.__name__, msg))
477 |
--------------------------------------------------------------------------------
/intellect/PolicyLexer.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/python
2 | # coding=utf-8
3 | """
4 | Copyright (c) 2011, The MITRE Corporation.
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without
8 | modification, are permitted provided that the following conditions are met:
9 | 1. Redistributions of source code must retain the above copyright
10 | notice, this list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright
12 | notice, this list of conditions and the following disclaimer in the
13 | documentation and/or other materials provided with the distribution.
14 | 3. All advertising materials mentioning features or use of this software
15 | must display the following acknowledgement:
16 | This product includes software developed by the author.
17 | 4. Neither the name of the author nor the
18 | names of its contributors may be used to endorse or promote products
19 | derived from this software without specific prior written permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
22 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | """
32 |
33 | from intellect.grammar.PolicyLexer import PolicyLexer as AutoGeneratedLexer
34 |
35 |
36 | class PolicyLexer(AutoGeneratedLexer):
37 |
38 | def nextToken(self):
39 | self.startPosition = self.getCharPositionInLine()
40 | return AutoGeneratedLexer.nextToken(self)
41 |
42 |
--------------------------------------------------------------------------------
/intellect/PolicyTokenSource.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/python
2 | # coding=utf-8
3 | """
4 | Copyright (c) 2004 Terence Parr and Loring Craymer
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without
8 | modification, are permitted provided that the following conditions
9 | are met:
10 | 1. Redistributions of source code must retain the above copyright
11 | notice, this list of conditions and the following disclaimer.
12 | 2. Redistributions in binary form must reproduce the above copyright
13 | notice, this list of conditions and the following disclaimer in the
14 | documentation and/or other materials provided with the distribution.
15 | 3. The name of the author may not be used to endorse or promote products
16 | derived from this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 | """
29 |
30 | from antlr3.tokens import ClassicToken, EOF
31 | from antlr3.recognizers import TokenSource
32 |
33 | from intellect.grammar.PolicyParser import INDENT, DEDENT
34 | from intellect.grammar.PolicyLexer import NEWLINE, LEADING_WS
35 |
36 |
37 | class PolicyTokenSource(TokenSource):
38 |
39 | """
40 | Python does not explicitly provide begin and end nesting signals.
41 | Rather, the indentation level indicates when you begin and end.
42 | This is an interesting lexical problem because multiple DEDENT
43 | tokens should be sent to the parser sometimes without a corresponding
44 | input symbol! Consider the following example:
45 |
46 | a=1
47 | if a>1:
48 | print a
49 | b=3
50 |
51 | Here the "b" token on the left edge signals that a DEDENT is needed
52 | after the "print a \n" and before the "b". The sequence should be
53 |
54 | ... 1 COLON NEWLINE INDENT PRINT a NEWLINE DEDENT b ASSIGN 3 ...
55 |
56 | For more examples, see the big comment at the bottom of this file.
57 |
58 | This TokenStream normally just passes tokens through to the parser.
59 | Upon NEWLINE token from the lexer, however, an INDENT or DEDENT token
60 | may need to be sent to the parser. The NEWLINE is the trigger for
61 | this class to do it's job. NEWLINE is saved and then the first token
62 | of the next line is examined. If non-leading-whitespace token,
63 | then check against stack for indent vs dedent. If LEADING_WS, then
64 | the column of the next non-whitespace token will dictate indent vs
65 | dedent. The column of the next real token is number of spaces
66 | in the LEADING_WS token + 1 (to move past the whitespace). The
67 | lexer grammar must set the text of the LEADING_WS token to be
68 | the proper number of spaces (and do tab conversion etc...).
69 |
70 | A stack of column numbers is tracked and used to detect changes
71 | in indent level from one token to the next.
72 |
73 | A queue of tokens is built up to hold multiple DEDENT tokens that
74 | are generated. Before asking the lexer for another token via
75 | nextToken(), the queue is flushed first one token at a time.
76 |
77 | Terence Parr and Loring Craymer
78 | February 2004
79 | """
80 |
81 | FIRST_CHAR_POSITION = 0
82 |
83 | def __init__(self, stream):
84 |
85 | # The stack of indent levels (column numbers)
86 | # "state" of indent level is FIRST_CHAR_POSITION
87 |
88 | self.indentStack = [self.FIRST_CHAR_POSITION]
89 |
90 | # The queue of tokens
91 |
92 | self.tokens = []
93 |
94 | # We pull real tokens from this lexer
95 |
96 | self.stream = stream
97 |
98 | self.lastTokenAddedIndex = -1
99 |
100 | def nextToken(self):
101 | """
102 | From http://www.python.org/doc/2.2.3/ref/indentation.html
103 |
104 | Before the first line of the file is read, a single zero is
105 | pushed on the stack; this will never be popped off again. The
106 | numbers pushed on the stack will always be strictly increasing
107 | from bottom to top. At the beginning of each logical line, the
108 | line's indentation level is compared to the top of the
109 | stack. If it is equal, nothing happens. If it is larger, it is
110 | pushed on the stack, and one INDENT token is generated. If it
111 | is smaller, it must be one of the numbers occurring on the
112 | stack; all numbers on the stack that are larger are popped
113 | off, and for each number popped off a DEDENT token is
114 | generated. At the end of the file, a DEDENT token is generated
115 | for each number remaining on the stack that is larger than
116 | zero.
117 |
118 | I use char position in line 0..n-1 instead.
119 |
120 | The DEDENTS possibly needed at EOF are gracefully handled by forcing
121 | EOF to have char pos 0 even though with UNIX it's hard to get EOF
122 | at a non left edge.
123 | """
124 |
125 | # if something in queue, just remove and return it
126 |
127 | if len(self.tokens) > 0:
128 | t = self.tokens.pop(0)
129 | return t
130 |
131 | self.insertImaginaryIndentDedentTokens()
132 |
133 | return self.nextToken()
134 |
135 | def insertImaginaryIndentDedentTokens(self):
136 | t = self.stream.LT(1)
137 | self.stream.consume()
138 |
139 | # if not a NEWLINE, doesn't signal indent/dedent work; just enqueue
140 |
141 | if t.getType() != NEWLINE:
142 | hiddenTokens = \
143 | self.stream.getTokens(self.lastTokenAddedIndex + 1,
144 | t.getTokenIndex() - 1)
145 |
146 | if hiddenTokens is not None:
147 | self.tokens.extend(hiddenTokens)
148 |
149 | self.lastTokenAddedIndex = t.getTokenIndex()
150 | self.tokens.append(t)
151 | return
152 |
153 | # save NEWLINE in the queue
154 | # print "found newline: "+str(t)+" stack is "+self.stackString()
155 |
156 | hiddenTokens = self.stream.getTokens(self.lastTokenAddedIndex
157 | + 1, t.getTokenIndex() - 1)
158 |
159 | if hiddenTokens is not None:
160 | self.tokens.extend(hiddenTokens)
161 |
162 | self.lastTokenAddedIndex = t.getTokenIndex()
163 | self.tokens.append(t)
164 |
165 | # grab first token of next line
166 |
167 | t = self.stream.LT(1)
168 | self.stream.consume()
169 |
170 | # handle hidden tokens
171 |
172 | hiddenTokens = self.stream.getTokens(self.lastTokenAddedIndex
173 | + 1, t.getTokenIndex() - 1)
174 |
175 | if hiddenTokens is not None:
176 | self.tokens.extend(hiddenTokens)
177 |
178 | self.lastTokenAddedIndex = t.getTokenIndex()
179 |
180 | # compute cpos as the char pos of next non-WS token in line
181 |
182 | cpos = t.getCharPositionInLine() # column dictates indent/dedent
183 |
184 | if t.getType() == EOF:
185 | cpos = -1 # pretend EOF always happens at left edge
186 | elif t.getType() == LEADING_WS:
187 | cpos = len(t.getText())
188 |
189 | # print "next token is: "+str(t)
190 |
191 | # compare to last indent level
192 |
193 | lastIndent = self.indentStack[-1]
194 |
195 | # print "cpos, lastIndent = "+str(cpos)+", "+str(lastIndent)
196 |
197 | if cpos > lastIndent: # they indented; track and gen INDENT
198 | self.indentStack.append(cpos)
199 |
200 | # print self.indentStack
201 | # print "push("+str(cpos)+"): "+self.stackString()
202 |
203 | indent = ClassicToken(INDENT, '')
204 | indent.setCharPositionInLine(t.getCharPositionInLine())
205 | indent.setLine(t.getLine())
206 | self.tokens.append(indent)
207 | elif cpos < lastIndent:
208 |
209 | # they dedented
210 | # how far back did we dedent?
211 |
212 | prevIndex = self.findPreviousIndent(cpos)
213 |
214 | # print "dedented; prevIndex of cpos="+str(cpos)+" is "+str(prevIndex)
215 |
216 | # generate DEDENTs for each indent level we backed up over
217 |
218 | while len(self.indentStack) > prevIndex + 1:
219 | dedent = ClassicToken(DEDENT, '')
220 | dedent.setCharPositionInLine(t.getCharPositionInLine())
221 | dedent.setLine(t.getLine())
222 | self.tokens.append(dedent)
223 |
224 | self.indentStack.pop(-1) # pop those off indent level
225 |
226 | # print self.indentStack
227 |
228 | if t.getType() != LEADING_WS: # discard WS
229 | self.tokens.append(t)
230 |
231 | # T O K E N S T A C K M E T H O D S
232 |
233 | def findPreviousIndent(self, i):
234 | '''
235 | Return the index on stack of previous indent level == i else -1
236 | '''
237 |
238 | for (j, pos) in reversed(list(enumerate(self.indentStack))):
239 | if pos == i:
240 | return j
241 |
242 | return self.FIRST_CHAR_POSITION
243 |
244 | def stackString(self):
245 | return ' '.join([str(i) for i in reversed(self.indentStack)])
246 |
--------------------------------------------------------------------------------
/intellect/__init__.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/python
2 | # coding=utf-8
3 | """
4 | Copyright (c) 2011, The MITRE Corporation.
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without
8 | modification, are permitted provided that the following conditions are met:
9 | 1. Redistributions of source code must retain the above copyright
10 | notice, this list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright
12 | notice, this list of conditions and the following disclaimer in the
13 | documentation and/or other materials provided with the distribution.
14 | 3. All advertising materials mentioning features or use of this software
15 | must display the following acknowledgement:
16 | This product includes software developed by the author.
17 | 4. Neither the name of the author nor the
18 | names of its contributors may be used to endorse or promote products
19 | derived from this software without specific prior written permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
22 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | """
32 |
33 | __author__ = "Michael Joseph Walsh"
34 |
35 | VERSION = (1, 4, 9, 0)
36 |
--------------------------------------------------------------------------------
/intellect/examples/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2011, The MITRE Corporation.
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 | 1. Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright
10 | notice, this list of conditions and the following disclaimer in the
11 | documentation and/or other materials provided with the distribution.
12 | 3. All advertising materials mentioning features or use of this software
13 | must display the following acknowledgement:
14 | This product includes software developed by the author.
15 | 4. Neither the name of the author nor the
16 | names of its contributors may be used to endorse or promote products
17 | derived from this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | """
--------------------------------------------------------------------------------
/intellect/examples/bahBahBlackSheep/BagOfWool.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2011, Michael Joseph Walsh.
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 | 1. Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright
10 | notice, this list of conditions and the following disclaimer in the
11 | documentation and/or other materials provided with the distribution.
12 | 3. All advertising materials mentioning features or use of this software
13 | must display the following acknowledgement:
14 | This product includes software developed by the author.
15 | 4. Neither the name of the author nor the
16 | names of its contributors may be used to endorse or promote products
17 | derived from this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | """
30 |
31 | '''
32 | Created on Aug 29, 2011
33 |
34 | @author: Michael Joseph Walsh
35 | '''
36 |
37 |
38 | class BagOfWool(object):
39 | '''
40 | Used to signify a bag of wool
41 | '''
42 |
43 |
44 | def __init__(self):
45 | '''
46 | BagsOfWool Initializer
47 | '''
48 |
--------------------------------------------------------------------------------
/intellect/examples/bahBahBlackSheep/BlackSheep.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2011, Michael Jospeh Walsh.
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 | 1. Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright
10 | notice, this list of conditions and the following disclaimer in the
11 | documentation and/or other materials provided with the distribution.
12 | 3. All advertising materials mentioning features or use of this software
13 | must display the following acknowledgement:
14 | This product includes software developed by the author.
15 | 4. Neither the name of the author nor the
16 | names of its contributors may be used to endorse or promote products
17 | derived from this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | """
30 |
31 | '''
32 | Created on Aug 18, 2011
33 |
34 | @author: Michael Joseph Walsh
35 | '''
36 |
37 | import logging, time
38 | import thread, random
39 | from threading import Lock
40 |
41 | from intellect.examples.bahBahBlackSheep.BagOfWool import BagOfWool
42 |
43 | def grow_wool(sheep):
44 | while True:
45 |
46 | time.sleep(random.randint(2, 5))
47 |
48 | logging.getLogger("example").debug("{0}: Grew a bag of wool.".format(sheep.name))
49 | sheep.bags_of_wool.append(BagOfWool())
50 |
51 | if len(sheep.bags_of_wool) == 3:
52 | logging.getLogger("example").debug("{0}: Waiting around for retirement.".format(sheep.name))
53 | break
54 |
55 | class BlackSheep():
56 | '''
57 | Used to signify a black sheep
58 | '''
59 |
60 | number = 0
61 |
62 | def __init__(self):
63 | '''
64 | BlackSheep Initializer
65 | '''
66 | self.bags_of_wool = []
67 | BlackSheep.number = BlackSheep.number + 1
68 | self.name = "Sheep #{0}".format(BlackSheep.number)
69 |
70 | logging.getLogger("example").debug("Creating {0}.".format(self.name))
71 |
72 | self.lock = Lock()
73 | thread.start_new_thread(grow_wool, (self,))
74 |
75 |
76 | @property
77 | def name(self):
78 | return self._name
79 |
80 | @name.setter
81 | def name(self, value):
82 | self._name = value
83 |
84 | @property
85 | def bags_of_wool(self):
86 | return self._bags_of_wool
87 |
88 | @bags_of_wool.setter
89 | def bags_of_wool(self, value):
90 | self.lock.acquire()
91 | self._bags_of_wool = value
92 | self.lock.release()
93 |
94 |
95 | if __name__ == '__main__':
96 | sheep = BlackSheep()
97 |
98 | while True:
99 | time.sleep(5)
100 | print len(sheep.bags_of_wool)
101 |
102 | if len(sheep.bags_of_wool) == 3:
103 | break
104 |
--------------------------------------------------------------------------------
/intellect/examples/bahBahBlackSheep/BuyOrder.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2011, Michael Joseph Walsh.
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 | 1. Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright
10 | notice, this list of conditions and the following disclaimer in the
11 | documentation and/or other materials provided with the distribution.
12 | 3. All advertising materials mentioning features or use of this software
13 | must display the following acknowledgement:
14 | This product includes software developed by the author.
15 | 4. Neither the name of the author nor the
16 | names of its contributors may be used to endorse or promote products
17 | derived from this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | """
30 |
31 | '''
32 | Created on Aug 18, 2011
33 |
34 | @author: Michael Joseph Walsh
35 | '''
36 |
37 | import logging
38 |
39 | class BuyOrder(object):
40 | '''
41 | Used to signify a buy order
42 | '''
43 |
44 | def __init__(self, count = 1):
45 | '''
46 | BuyOrder Initializer
47 | '''
48 | logging.getLogger("example").debug("Creating buy order for {0} sheep.".format(count))
49 |
50 | self.count = count
51 |
52 | @property
53 | def count(self):
54 | return self._count
55 |
56 | @count.setter
57 | def count(self, value):
58 | self._count = value
--------------------------------------------------------------------------------
/intellect/examples/bahBahBlackSheep/Example.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | """
4 | Copyright (c) 2011, Michael Joseph Walsh.
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without
8 | modification, are permitted provided that the following conditions are met:
9 | 1. Redistributions of source code must retain the above copyright
10 | notice, this list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright
12 | notice, this list of conditions and the following disclaimer in the
13 | documentation and/or other materials provided with the distribution.
14 | 3. All advertising materials mentioning features or use of this software
15 | must display the following acknowledgement:
16 | This product includes software developed by the author.
17 | 4. Neither the name of the author nor the
18 | names of its contributors may be used to endorse or promote products
19 | derived from this software without specific prior written permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
22 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | """
32 |
33 | '''
34 | Created on Aug 17, 2011
35 |
36 | @author: Michael Joseph Walsh
37 | '''
38 |
39 | import sys, logging, time, random
40 |
41 | from intellect.Intellect import Intellect
42 |
43 | from intellect.examples.bahBahBlackSheep.BuyOrder import BuyOrder
44 |
45 | class MyIntellect(Intellect):
46 | pass
47 |
48 |
49 | if __name__ == '__main__':
50 |
51 | # tune down logging inside Intellect
52 | logger = logging.getLogger('intellect')
53 | logger.setLevel(logging.ERROR)
54 | consoleHandler = logging.StreamHandler(stream=sys.stdout)
55 | consoleHandler.setFormatter(logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s%(message)s'))
56 | logger.addHandler(consoleHandler)
57 |
58 | # set up logging for the example
59 | logger = logging.getLogger('example')
60 | logger.setLevel(logging.DEBUG)
61 |
62 | consoleHandler = logging.StreamHandler(stream=sys.stdout)
63 | consoleHandler.setFormatter(logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s%(message)s'))
64 | logger.addHandler(consoleHandler)
65 |
66 | logging.getLogger("example").debug("Creating reasoning engine.")
67 | myIntellect = MyIntellect()
68 |
69 | logging.getLogger("example").debug("Asking the engine to learn my policy.")
70 | policy = myIntellect.learn(myIntellect.local_file_uri("./rulesets/example.policy"))
71 |
72 | #print myIntellect.policy.str_tree("semantic model:")
73 |
74 | max_buy_orders_to_start = input('Provide the maximum number possible buy orders to start with: ')
75 |
76 | buy_order_to_start = random.randint(1, max_buy_orders_to_start)
77 |
78 | logging.getLogger("example").debug("Asking the engine to learn a BuyOrder for {0} sheep.".format(buy_order_to_start))
79 | myIntellect.learn(BuyOrder(buy_order_to_start))
80 |
81 | myIntellect.reason()
82 |
83 | while True:
84 | logging.getLogger("example").debug("{0} in knowledge.".format(myIntellect.knowledge))
85 | time.sleep(5)
86 | logging.getLogger("example").debug("Messaging reasoning engine to reason.")
87 | myIntellect.reason()
88 |
--------------------------------------------------------------------------------
/intellect/examples/bahBahBlackSheep/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2011, Michael Joseph Walsh.
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 | 1. Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright
10 | notice, this list of conditions and the following disclaimer in the
11 | documentation and/or other materials provided with the distribution.
12 | 3. All advertising materials mentioning features or use of this software
13 | must display the following acknowledgement:
14 | This product includes software developed by the author.
15 | 4. Neither the name of the author nor the
16 | names of its contributors may be used to endorse or promote products
17 | derived from this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | """
--------------------------------------------------------------------------------
/intellect/examples/bahBahBlackSheep/rulesets/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2011, The MITRE Corporation.
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 | 1. Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright
10 | notice, this list of conditions and the following disclaimer in the
11 | documentation and/or other materials provided with the distribution.
12 | 3. All advertising materials mentioning features or use of this software
13 | must display the following acknowledgement:
14 | This product includes software developed by the author.
15 | 4. Neither the name of the author nor the
16 | names of its contributors may be used to endorse or promote products
17 | derived from this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | """
--------------------------------------------------------------------------------
/intellect/examples/bahBahBlackSheep/rulesets/example.policy:
--------------------------------------------------------------------------------
1 | from intellect.examples.bahBahBlackSheep.BlackSheep import BlackSheep
2 | from intellect.examples.bahBahBlackSheep.BuyOrder import BuyOrder
3 | import logging
4 | import random
5 |
6 | '''
7 | Ruleset based on the nursery rhyme...
8 | '''
9 |
10 | rule "Start policy":
11 | then:
12 | log("Policy is being reasoned over.", "example", logging.DEBUG)
13 |
14 | rule "Baa, baa, black sheep, Have you any wool?":
15 | when:
16 | exists BlackSheep()
17 | then:
18 | print( "Me: Baa, baa, black sheep, Have you any wool?" )
19 |
20 | rule "Have you no bags full?":
21 | when:
22 | $sheep := BlackSheep( len(bags_of_wool) == 0 )
23 | then:
24 | print( "Sheep {0}: No, sir, No, sir.".format( $sheep.name ) )
25 |
26 | rule "At least 1 bag full?":
27 | when:
28 | $sheep := BlackSheep( len(bags_of_wool) >= 1 )
29 | then:
30 | print( "{0}: Yes, sir, yes, sir,".format( $sheep.name ) )
31 | print( "{0}: {1} bags full".format( $sheep.name, len($sheep.bags_of_wool) ) )
32 | print( "{0}: One for my master,".format( $sheep.name ) )
33 |
34 | rule "At least 2 bags full?":
35 | when:
36 | $sheep := BlackSheep( len(bags_of_wool) >= 2 )
37 | then:
38 | print("{0}: And one for my dame,".format( $sheep.name ) )
39 |
40 | rule "3 bags full?":
41 | when:
42 | $sheep := BlackSheep( len(bags_of_wool) == 3 )
43 | then:
44 | print( "{0}: and one for the little boy".format( $sheep.name ) )
45 | print( "{0}: Who lives down the lane.".format( $sheep.name ) )
46 |
47 | rule "Retire the sheep and insert a buy order":
48 | when:
49 | $sheep := BlackSheep( len(bags_of_wool) == 3 )
50 | then:
51 | print( "Retiring sheep {0}.".format( $sheep.name ) )
52 | forget $sheep
53 | insert BuyOrder(random.randint(1, 1))
54 |
55 | rule "Time to buy new sheep?":
56 | when:
57 | $buyOrder := BuyOrder( )
58 | then:
59 | modify $buyOrder:
60 | count = $buyOrder.count - 1
61 | learn BlackSheep()
62 |
63 | rule "Remove empty buy orders":
64 | when:
65 | $buyOrder := BuyOrder( count == 0 )
66 | then:
67 | forget $buyOrder
68 |
69 | rule "End policy":
70 | then:
71 | log("Finished reasoning over policy.", "example", logging.DEBUG)
72 | halt
73 |
--------------------------------------------------------------------------------
/intellect/examples/testing/ClassA.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2011, The MITRE Corporation.
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 | 1. Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright
10 | notice, this list of conditions and the following disclaimer in the
11 | documentation and/or other materials provided with the distribution.
12 | 3. All advertising materials mentioning features or use of this software
13 | must display the following acknowledgement:
14 | This product includes software developed by the author.
15 | 4. Neither the name of the author nor the
16 | names of its contributors may be used to endorse or promote products
17 | derived from this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | """
30 |
31 | """
32 | ClassA
33 |
34 | Description: Intellect test fact
35 |
36 | Initial Version: Feb 2, 2011
37 |
38 | @author: Michael Joseph Walsh
39 | """
40 |
41 | class ClassA(object):
42 | '''
43 | An example fact
44 | '''
45 |
46 | globalInClassA_1 = "a global"
47 | globalInClassA_2 = "another global"
48 |
49 | def __init__(self, property0 = None, property1 = None):
50 | '''
51 | ClassA initializer
52 | '''
53 | self.attribute1 = "attribute1's value"
54 | self.__hiddenAttribute1 = "super secret hidden attribute. nah!"
55 |
56 | self.property0 = property0
57 | self.property1 = property1
58 |
59 | print "created an instance of ClassA"
60 |
61 | @property
62 | def property0(self):
63 | return self._property0
64 |
65 | @property0.setter
66 | def property0(self, value):
67 | self._property0 = value
68 |
69 | @property
70 | def property1(self):
71 | return self._property1
72 |
73 | @property1.setter
74 | def property1(self, value):
75 | self._property1 = value
76 |
77 | def someMethod(self):
78 | print("someMethod called")
79 |
80 | @staticmethod
81 | def classAStaticMethod(self):
82 | print("classAStaticMethod called")
83 |
84 | def classASomeOtherMethod(self):
85 | print("classASomeOtherMethd called")
86 |
87 | def __classAHiddenMethod(self):
88 | print("classAHiddenMethod called")
--------------------------------------------------------------------------------
/intellect/examples/testing/ClassCandD.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2011, The MITRE Corporation.
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 | 1. Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright
10 | notice, this list of conditions and the following disclaimer in the
11 | documentation and/or other materials provided with the distribution.
12 | 3. All advertising materials mentioning features or use of this software
13 | must display the following acknowledgement:
14 | This product includes software developed by the author.
15 | 4. Neither the name of the author nor the
16 | names of its contributors may be used to endorse or promote products
17 | derived from this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | """
30 |
31 | """
32 | ClassCandD
33 |
34 | Description: Intellect test facts
35 |
36 | Initial Version: Feb 2, 2011
37 |
38 | @author: Michael Joseph Walsh
39 | """
40 |
41 | class ClassC(object):
42 | '''
43 | An example fact.
44 | '''
45 |
46 | globalInClassC="a global only in ClassC"
47 |
48 | def __init__(self, property1 = None, property2 = None):
49 | '''
50 | ClassC initializer
51 | '''
52 | self.property1 = property1
53 | self.property2 = property2
54 |
55 | def someMethod(self):
56 | print("someMethod called")
57 |
58 | @property
59 | def property1(self):
60 | return self._property1
61 |
62 | @property1.setter
63 | def property1(self, value):
64 | self._property1 = value
65 |
66 | @property1.deleter
67 | def property1(self):
68 | del self._property1
69 |
70 | @property
71 | def property2(self):
72 | return self._property2
73 |
74 | @property2.setter
75 | def property2(self, value):
76 | self._property2 = value
77 |
78 | @property2.deleter
79 | def property2(self):
80 | del self._property2
81 |
82 |
83 | class ClassD(object):
84 | '''
85 | An example fact.
86 | '''
87 |
88 | globalInClassD="a global only in ClassD"
89 |
90 | def __init__(self, property1 = None):
91 | '''
92 | ClassD initializer
93 | '''
94 | self.property1 = property1
95 |
96 | def someMethod(self):
97 | print("someMethod called")
98 |
99 | @property
100 | def property1(self):
101 | return self._property1
102 |
103 | @property1.setter
104 | def property1(self, value):
105 | self._property1 = value
106 |
107 | @property1.deleter
108 | def property1(self):
109 | del self._property1
--------------------------------------------------------------------------------
/intellect/examples/testing/ExerciseGrammar.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | """
4 | Copyright (c) 2011, The MITRE Corporation.
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without
8 | modification, are permitted provided that the following conditions are met:
9 | 1. Redistributions of source code must retain the above copyright
10 | notice, this list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright
12 | notice, this list of conditions and the following disclaimer in the
13 | documentation and/or other materials provided with the distribution.
14 | 3. All advertising materials mentioning features or use of this software
15 | must display the following acknowledgement:
16 | This product includes software developed by the author.
17 | 4. Neither the name of the author nor the
18 | names of its contributors may be used to endorse or promote products
19 | derived from this software without specific prior written permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
22 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | """
32 |
33 | """
34 | ExerciseIntellect
35 |
36 | Description: Exercises the Intellect grammar
37 |
38 | Initial Version: Dec 29, 2011
39 |
40 | @author: Michael Joseph Walsh
41 | """
42 | import sys, traceback, logging
43 |
44 | from intellect.Intellect import Intellect
45 | from intellect.examples.testing.ClassA import ClassA
46 |
47 | if __name__ == "__main__":
48 |
49 | # tune down logging inside Intellect
50 | logger = logging.getLogger('intellect')
51 | logger.setLevel(logging.DEBUG) # change this to ERROR for less output
52 | consoleHandler = logging.StreamHandler(stream=sys.stdout)
53 | consoleHandler.setFormatter(logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s%(message)s'))
54 | logger.addHandler(consoleHandler)
55 |
56 | # set up logging for the example
57 | logger = logging.getLogger('example')
58 | logger.setLevel(logging.DEBUG)
59 |
60 | consoleHandler = logging.StreamHandler(stream=sys.stdout)
61 | consoleHandler.setFormatter(logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s%(message)s'))
62 | logger.addHandler(consoleHandler)
63 |
64 | print "*"*80
65 | print """create an instance of MyIntellect extending Intellect, create some facts, and exercise the grammar"""
66 | print "*"*80
67 |
68 | try:
69 | myIntellect = Intellect()
70 |
71 | policy_a = myIntellect.learn(Intellect.local_file_uri("./rulesets/test_f.policy"))
72 |
73 | myIntellect.learn(ClassA(property1="apple"))
74 | #myIntellect.learn(ClassA( property1="pear"))
75 | #myIntellect.learn(ClassA( property1="grape"))
76 |
77 | #logger.debug("reasoning over policy w/ objects in memory")
78 |
79 | myIntellect.reason()
80 | except Exception as e:
81 | traceback.print_exc(limit=sys.getrecursionlimit(), file=sys.stdout)
82 |
--------------------------------------------------------------------------------
/intellect/examples/testing/ExerciseIntellect.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | """
4 | Copyright (c) 2011, The MITRE Corporation.
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without
8 | modification, are permitted provided that the following conditions are met:
9 | 1. Redistributions of source code must retain the above copyright
10 | notice, this list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright
12 | notice, this list of conditions and the following disclaimer in the
13 | documentation and/or other materials provided with the distribution.
14 | 3. All advertising materials mentioning features or use of this software
15 | must display the following acknowledgement:
16 | This product includes software developed by the author.
17 | 4. Neither the name of the author nor the
18 | names of its contributors may be used to endorse or promote products
19 | derived from this software without specific prior written permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
22 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | """
32 |
33 | """
34 | ExerciseIntellect
35 |
36 | Description: Exercises the Intellect object
37 |
38 | Initial Version: Feb 9, 2011
39 |
40 | @author: Michael Joseph Walsh
41 | """
42 |
43 | import sys, traceback, logging
44 |
45 | from intellect.Intellect import Intellect
46 | from intellect.Intellect import Callable
47 |
48 |
49 | class MyIntellect(Intellect):
50 |
51 | @Callable
52 | def bar(self):
53 | self.log(">>>>>>>>>>>>>> called MyIntellect's bar method as it was decorated as callable.")
54 |
55 |
56 | if __name__ == "__main__":
57 |
58 | try:
59 | # tune down logging inside Intellect
60 | logger = logging.getLogger('intellect')
61 | logger.setLevel(logging.DEBUG) # change this to ERROR for less output
62 | consoleHandler = logging.StreamHandler(stream=sys.stdout)
63 | consoleHandler.setFormatter(logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s%(message)s'))
64 | logger.addHandler(consoleHandler)
65 |
66 | # set up logging for the example
67 | logger = logging.getLogger('example')
68 | logger.setLevel(logging.DEBUG)
69 |
70 | consoleHandler = logging.StreamHandler(stream=sys.stdout)
71 | consoleHandler.setFormatter(logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s%(message)s'))
72 | logger.addHandler(consoleHandler)
73 |
74 | print "*"*80
75 | print """create an instance of MyIntellect extending Intellect, create some facts, and exercise the MyIntellect's ability to learn and forget"""
76 | print "*"*80
77 |
78 | myIntellect = MyIntellect()
79 |
80 | try:
81 | policy_bogus = myIntellect.learn(Intellect.local_file_uri("./rulesets/doesnt_exist.policy"))
82 | except IOError as e:
83 | exc_type, exc_value, exc_traceback = sys.exc_info()
84 | traceback.print_exception(exc_type, exc_value, exc_traceback)
85 |
86 | policy_a = myIntellect.learn(Intellect.local_file_uri("./rulesets/test_a.policy"))
87 | policy_d = myIntellect.learn(Intellect.local_file_uri("./rulesets/test_d.policy"))
88 |
89 | myIntellect.reason(["test_d", "test_a"])
90 |
91 | myIntellect.forget_all()
92 |
93 | from intellect.examples.testing.subModule.ClassB import ClassB
94 |
95 | # keep an identifier (b1) around to a ClassB, to demonstrate that a rule
96 | # can modify the object and the change is reflected in b1
97 | b = ClassB(property1="apple", property2=11)
98 | myIntellect.learn(b)
99 |
100 | b = ClassB(property1="pear", property2=11)
101 | myIntellect.learn(b)
102 |
103 | # learn policy as a string
104 | policy_a = myIntellect.learn("""
105 | from intellect.examples.testing.subModule.ClassB import ClassB
106 | import intellect.examples.testing.Test as Test
107 | import logging
108 |
109 | fruits_of_interest = ["apple", "grape", "mellon", "pear"]
110 | count = 5
111 |
112 | rule rule_a:
113 | agenda-group test_a
114 | when:
115 | $classB := ClassB( property1 in fruits_of_interest and property2>count )
116 | then:
117 | # mark the 'ClassB' matches in memory as modified
118 | modify $classB:
119 | property1 = $classB.property1 + " pie"
120 | modified = True
121 | # increment the matche's 'property2' value by 1000
122 | property2 = $classB.property2 + 1000
123 | attribute count = $classB.property2
124 | print "count = {0}".format( count )
125 | # call MyIntellect's bar method as it is decorated as callable
126 | bar()
127 | log("rule_a fired")
128 |
129 | rule rule_b:
130 | agenda-group test_a
131 | then:
132 | print "count = {0}".format( count )
133 | insert ClassB("water melon")
134 | # call MyIntellect's bar method as it is decorated as callable
135 | bar()
136 | log("rule_b fired")
137 |
138 | rule rule_c:
139 | # on the MAIN agenda-group
140 | then:
141 | log("rule_c fired")
142 |
143 | rule rule_d:
144 | agenda-group test_a
145 | then:
146 | attribute foo = "foo bar"
147 | """)
148 |
149 | policy_b = myIntellect.learn(Intellect.local_file_uri("./rulesets/test_b.policy"))
150 | #print policy.str_tree()
151 | #print str(policy_a)
152 |
153 | for policy_file_paths in myIntellect.policy.file_paths:
154 | print "----------------- path: {0}".format(policy_file_paths)
155 |
156 | myIntellect.forget(policy_b)
157 |
158 | for policy_file_paths in myIntellect.policy.file_paths:
159 | print "----------------- path: {0}".format(policy_file_paths)
160 |
161 | policy_b = myIntellect.learn(Intellect.local_file_uri("./rulesets/test_b.policy"))
162 |
163 | for policy_file_paths in myIntellect.policy.file_paths:
164 | print "----------------- path: {0}".format(policy_file_paths)
165 |
166 | myIntellect.forget(Intellect.local_file_uri("./rulesets/test_b.policy"))
167 |
168 | for policy_file_paths in myIntellect.policy.file_paths:
169 | print "----------------- path: {0}".format(policy_file_paths)
170 |
171 | policy_b = myIntellect.learn(Intellect.local_file_uri("./rulesets/test_b.policy"))
172 |
173 | for policy_file_paths in myIntellect.policy.file_paths:
174 | print "----------------- path: {0}".format(policy_file_paths)
175 |
176 | print "*"*80
177 | print "message MyIntellect to reason over the facts in knowledge"
178 | print "*"*80
179 |
180 | myIntellect.reason(["test_a"])
181 |
182 | print "*"*80
183 | print "facts in knowledge after applying policies"
184 | print "*"*80
185 |
186 | print myIntellect.knowledge
187 |
188 | for fact in myIntellect.knowledge:
189 | print "type: {0}, __dict__: {1}".format(type(fact), fact.__dict__)
190 |
191 | print "*"*80
192 | print "forget all, learn a policy from a string"
193 | print "*"*80
194 |
195 | myIntellect.forget_all()
196 |
197 | policy = myIntellect.learn("""rule rule_inline:
198 | then:
199 | a = 1
200 | b = 2
201 | c = a + b
202 | print("{0} + {1} = {2}".format(a, b, c))
203 | """)
204 |
205 | myIntellect.reason()
206 |
207 | print "*"*80
208 | print 'forget all, learn test_c.policy, reason over ["1", "2", "3", "4", "5", "6"]'
209 | print "*"*80
210 |
211 | myIntellect.forget_all()
212 |
213 | policy_c = myIntellect.learn(Intellect.local_file_uri("./rulesets/test_c.policy"))
214 |
215 | myIntellect.reason(["1", "2", "3", "4", "5", "6"])
216 |
217 | myIntellect.forget_all()
218 |
219 | try:
220 | policy_bogus = myIntellect.learn(Intellect.local_file_uri("./rulesets/test_e.policy"))
221 | except Exception as e:
222 | print >> sys.stderr, "./rulesets/test_e.policy doesn't contain any actual policy text. exception trace follows:"
223 | exc_type, exc_value, exc_traceback = sys.exc_info()
224 | traceback.print_exception(exc_type, exc_value, exc_traceback)
225 | except Exception as e:
226 | traceback.print_exc(limit=sys.getrecursionlimit(), file=sys.stdout)
227 |
--------------------------------------------------------------------------------
/intellect/examples/testing/Test.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2011, The MITRE Corporation.
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 | 1. Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright
10 | notice, this list of conditions and the following disclaimer in the
11 | documentation and/or other materials provided with the distribution.
12 | 3. All advertising materials mentioning features or use of this software
13 | must display the following acknowledgement:
14 | This product includes software developed by the author.
15 | 4. Neither the name of the author nor the
16 | names of its contributors may be used to endorse or promote products
17 | derived from this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | """
30 |
31 | """
32 | test
33 |
34 | Description: Test module
35 |
36 | Initial Version: Feb 23, 2011
37 |
38 | @author: Michael Joseph Walsh
39 | """
40 | def helloworld():
41 | """
42 | Returns "hello world" annd prints "returning 'hello world'" to the
43 | sys.stdout
44 | """
45 | print "returning 'hello world'"
46 | return "hello world"
47 |
48 | def greaterThanTen(n):
49 | """
50 | Returns True if 'n' is greater than 10
51 | """
52 | return n>10
53 |
54 |
55 | class MyClass(object):
56 |
57 | def __init__(self):
58 | self._globals = {}
59 |
60 | @property
61 | def globals(self):
62 | return self._globals
63 |
64 | @globals.setter
65 | def globals(self, value):
66 | self._globals = value
67 |
68 |
69 | a = MyClass()
70 |
71 | locals = {}
72 |
73 | exec("a = 1" ,a.globals, locals)
74 |
75 | print "globals = {0}".format([g for g in a.globals if not g.startswith("__")])
76 | print "locals = {0}".format(locals)
77 |
78 | exec("a += 1", a.globals, locals)
79 |
80 | print "globals = {0}".format([g for g in a.globals if not g.startswith("__")])
81 | print "locals = {0}".format(locals)
82 |
83 | a.globals["b"] = 5
84 |
85 | print "globals = {0}".format([g for g in a.globals if not g.startswith("__")])
86 | print "locals = {0}".format(locals)
87 |
88 | exec("global b;b += 1", a.globals, locals)
--------------------------------------------------------------------------------
/intellect/examples/testing/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2011, The MITRE Corporation.
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 | 1. Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright
10 | notice, this list of conditions and the following disclaimer in the
11 | documentation and/or other materials provided with the distribution.
12 | 3. All advertising materials mentioning features or use of this software
13 | must display the following acknowledgement:
14 | This product includes software developed by the author.
15 | 4. Neither the name of the author nor the
16 | names of its contributors may be used to endorse or promote products
17 | derived from this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | """
--------------------------------------------------------------------------------
/intellect/examples/testing/rulesets/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2011, The MITRE Corporation.
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 | 1. Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright
10 | notice, this list of conditions and the following disclaimer in the
11 | documentation and/or other materials provided with the distribution.
12 | 3. All advertising materials mentioning features or use of this software
13 | must display the following acknowledgement:
14 | This product includes software developed by the author.
15 | 4. Neither the name of the author nor the
16 | names of its contributors may be used to endorse or promote products
17 | derived from this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | """
--------------------------------------------------------------------------------
/intellect/examples/testing/rulesets/test_a.policy:
--------------------------------------------------------------------------------
1 | from intellect.examples.testing.subModule.ClassB import ClassB
2 | import intellect.examples.testing.Test as Test
3 | import logging
4 |
5 | fruits_of_interest = ["apple", "grape", "mellon", "pear"]
6 | count = 5
7 |
8 | rule rule_a:
9 | agenda-group test_a
10 | when:
11 | $classB := ClassB( property1 in fruits_of_interest and property2>count )
12 | then:
13 | # mark the 'ClassB' matches in memory as modified
14 | modify $classB:
15 | property1 = $classB.property1 + " pie"
16 | modified = True
17 | # increment the matche's 'property2' value by 1000
18 | property2 = $classB.property2 + 1000
19 | attribute count = $classB.property2
20 | print "count = {0}".format( count )
21 | # call MyIntellect's bar method as it is decorated as callable
22 | bar()
23 | log("rule_a fired")
24 |
25 | rule rule_b:
26 | agenda-group test_a
27 | then:
28 | print "count = {0}".format( count )
29 | insert ClassB("water melon")
30 | # call MyIntellect's bar method as it is decorated as callable
31 | bar()
32 | log("rule_b fired")
33 |
34 | rule rule_c:
35 | # on the MAIN agenda-group
36 | then:
37 | log("rule_c fired")
38 |
39 | rule rule_d:
40 | agenda-group test_a
41 | then:
42 | attribute foo = "foo bar"
43 |
--------------------------------------------------------------------------------
/intellect/examples/testing/rulesets/test_b.policy:
--------------------------------------------------------------------------------
1 | from intellect.examples.testing.subModule.ClassB import ClassB
2 |
3 | rule rule_e:
4 | agenda-group test_a
5 | then:
6 | print foo
7 |
8 | rule rule_f:
9 | agenda-group test_a
10 | then:
11 | halt
12 |
13 | rule rule_g:
14 | agenda-group test_a
15 | then:
16 | halt
17 |
18 |
--------------------------------------------------------------------------------
/intellect/examples/testing/rulesets/test_c.policy:
--------------------------------------------------------------------------------
1 | i = 0
2 |
3 | rule rule_e:
4 | agenda-group "1"
5 | then:
6 | attribute i = i + 1
7 | print i
8 | halt
9 |
10 | rule rule_f:
11 | agenda-group "2"
12 | then:
13 | attribute i = i + 1
14 | print i
15 |
16 | rule rule_g:
17 | agenda-group "3"
18 | then:
19 | attribute i = i + 1
20 | print i
21 |
22 | rule rule_h:
23 | agenda-group "4"
24 | then:
25 | # the 'i' variable is scoped to then portion of the rule
26 | i = 0
27 | print i
28 |
29 | rule rule_i:
30 | agenda-group "5"
31 | then:
32 | attribute i += 1
33 | print i
34 | # the 'i' variable is scoped to then portion of the rule
35 | i = 0
36 |
37 | rule rule_j:
38 | agenda-group "6"
39 | then:
40 | attribute i += 1
41 | print i
42 |
--------------------------------------------------------------------------------
/intellect/examples/testing/rulesets/test_d.policy:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | first_sum = 0
4 | second_sum = 0
5 |
6 | rule "set both first_sum and second_sum to 1":
7 | agenda-group "test_d"
8 | then:
9 | attribute (first_sum, second_sum) = (1,1)
10 | log("first_sum is {0}".format(first_sum), "example", logging.DEBUG)
11 | log("second_sum is {0}".format(second_sum), "example", logging.DEBUG)
12 |
13 | rule "add 2":
14 | agenda-group "test_d"
15 | then:
16 | attribute first_sum += 2
17 | attribute second_sum += 2
18 | log("first_sum is {0}".format(first_sum), "example", logging.DEBUG)
19 | log("second_sum is {0}".format(second_sum), "example", logging.DEBUG)
20 |
21 | rule "add 3":
22 | agenda-group "test_d"
23 | then:
24 | attribute first_sum += 3
25 | attribute second_sum += 3
26 | log("first_sum is {0}".format(first_sum), "example", logging.DEBUG)
27 | log("second_sum is {0}".format(second_sum), "example", logging.DEBUG)
28 |
29 | rule "add 4":
30 | agenda-group "test_d"
31 | then:
32 | attribute first_sum += 4
33 | attribute second_sum += 4
34 | log("first_sum is {0}".format(first_sum), "example", logging.DEBUG)
35 | log("second_sum is {0}".format(second_sum), "example", logging.DEBUG)
36 | halt
37 |
38 | rule "should never get here":
39 | agenda-group "test_d"
40 | then:
41 | log("Then how did I get here?", "example", logging.DEBUG)
42 |
--------------------------------------------------------------------------------
/intellect/examples/testing/rulesets/test_e.policy:
--------------------------------------------------------------------------------
1 | The limerick* packs laughs anatomical
2 | *(pronounced "lim'rick" to preserve meter)
3 | In space that is quite economical,
4 | But the good ones I've seen
5 | So seldom are clean,
6 | And the clean ones so seldom are comical.
--------------------------------------------------------------------------------
/intellect/examples/testing/rulesets/test_f.policy:
--------------------------------------------------------------------------------
1 | from intellect.examples.testing.ClassA import ClassA
2 |
3 | rule rule_a:
4 | when:
5 | $classA := ClassA( property1 == "apple" )
6 | then:
7 | log("rule_a fired")
8 |
--------------------------------------------------------------------------------
/intellect/examples/testing/subModule/ClassB.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2011, The MITRE Corporation.
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 | 1. Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright
10 | notice, this list of conditions and the following disclaimer in the
11 | documentation and/or other materials provided with the distribution.
12 | 3. All advertising materials mentioning features or use of this software
13 | must display the following acknowledgement:
14 | This product includes software developed by the author.
15 | 4. Neither the name of the author nor the
16 | names of its contributors may be used to endorse or promote products
17 | derived from this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | """
30 |
31 | """
32 | ClassB
33 |
34 | Description: Intellect test fact
35 |
36 | Initial Version: Feb 2, 2011
37 |
38 | @author: Michael Joseph Walsh
39 | """
40 |
41 | from intellect.examples.testing.ClassA import ClassA
42 |
43 | class ClassB(ClassA):
44 | '''
45 | An example fact
46 | '''
47 |
48 | globalInClassB="a global only in class B"
49 |
50 | def __init__(self, property0 = None, property1 = None, property2 = None):
51 | '''
52 | ClassB initializer
53 | '''
54 | self.attribute2 = "attribute2's value"
55 | self.__hiddenAttribute2 = "another super secret hidden attribute. nah!"
56 |
57 | self.property0 = property0
58 | self.property1 = property1
59 | self.property2 = property2
60 | self.modified = False
61 |
62 | print "created an instance of ClassB"
63 |
64 | @property
65 | def property2(self):
66 | return self._property2
67 |
68 | @property2.setter
69 | def property2(self, value):
70 | print "setting property2 to {0}".format(value)
71 | self._property2 = value
72 |
73 | @property
74 | def modified(self):
75 | return self._modified
76 |
77 | @modified.setter
78 | def modified(self, value):
79 | print "setting modified to {0}".format(value)
80 | self._modified = value
81 |
82 | def aMethod(self):
83 | return "a"
84 |
85 | def trueValue(self):
86 | return True
87 |
88 | @staticmethod
89 | def classBStaticMethod(self):
90 | # and here
91 | print("classBStatciMethod called")
92 |
93 | def classBSomeOtherMethod(self):
94 | # and here
95 | print("classBSomeOtherMethod called")
96 |
97 | def __classBHiddenMethod(self):
98 | # something goes here
99 | print("classBHiddenMethod called")
--------------------------------------------------------------------------------
/intellect/examples/testing/subModule/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2011, The MITRE Corporation.
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 | 1. Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright
10 | notice, this list of conditions and the following disclaimer in the
11 | documentation and/or other materials provided with the distribution.
12 | 3. All advertising materials mentioning features or use of this software
13 | must display the following acknowledgement:
14 | This product includes software developed by the author.
15 | 4. Neither the name of the author nor the
16 | names of its contributors may be used to endorse or promote products
17 | derived from this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | """
--------------------------------------------------------------------------------
/intellect/grammar/Policy.g:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2011, The MITRE Corporation.
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 | 1. Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright
10 | notice, this list of conditions and the following disclaimer in the
11 | documentation and/or other materials provided with the distribution.
12 | 3. All advertising materials mentioning features or use of this software
13 | must display the following acknowledgement:
14 | This product includes software developed by the author.
15 | 4. Neither the name of the author nor the
16 | names of its contributors may be used to endorse or promote products
17 | derived from this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | */
30 | /**
31 | * @author: Michael Joseph Walsh
32 | *
33 | * A policy grammar derived from the ANTLR3 Python 2.3.3 Grammar authored by
34 | * Terence Parr and Loring Craymer.
35 | */
36 |
37 | grammar Policy;
38 |
39 | options {
40 | language = Python;
41 | }
42 |
43 | tokens {
44 | INDENT;
45 | DEDENT;
46 | }
47 |
48 | @header {
49 | from intellect.Node import *
50 | }
51 |
52 | @lexer::init {
53 | self.implicitLineJoiningLevel = 0
54 | self.startPosition = -1
55 | }
56 |
57 | //
58 | // P A R S E R R U L E S
59 | //
60 |
61 | file returns [object] // returns a policy File object
62 | @init{ $object = File() }
63 | : ( NEWLINE | statement { $object.append_child( $statement.object ) } )+
64 | | EOF
65 | ;
66 |
67 | statement returns [object] // returns a Statment object
68 | : importStmt { $object = Statement( $importStmt.object, $importStmt.object.line, $importStmt.object.column ) }
69 | | attributeStmt { $object = Statement( $attributeStmt.object ) }
70 | | ruleStmt { $object = Statement( $ruleStmt.object, $ruleStmt.object.line, $ruleStmt.object.column ) }
71 | ;
72 |
73 | importStmt returns [object] // returns a ImportStmt object
74 | : importName { $object = ImportStmt( children = $importName.object, line = $importName.object.line ) }
75 | | importFrom { $object = ImportStmt( children = $importFrom.object, line = $importFrom.object.line ) }
76 | ;
77 |
78 | importName returns [object] // returns an ImportName object
79 | : IMPORT dottedAsNames { $object = ImportName( children = [$IMPORT.text, $dottedAsNames.object], line = $IMPORT.getLine() ) } NEWLINE
80 | ;
81 |
82 | importFrom returns [object] // returns an ImportFrom object
83 | : FROM { $object = ImportFrom( children = $FROM.text, line = $FROM.getLine() ) } dottedName { $object.append_child( $dottedName.object ) } IMPORT { $object.append_child( $IMPORT.text ) }
84 | ( importAsNames1=importAsNames { object.append_child( $importAsNames1.object ) }
85 | | LPAREN importAsNames2=importAsNames RPAREN { $object.append_children( [$LPAREN.text, $importAsNames2.object, $RPAREN.text] ) }
86 | ) NEWLINE
87 | ;
88 |
89 | importAsNames returns [object]
90 | : importAsName1=importAsName { $object=ImportAsNames( $importAsName1.object ) }
91 | ( COMMA importAsName2=importAsName { $object.append_children( [",", $importAsName2.object] ) } )* ( COMMA { $object.append_child( "," ) } )?
92 | ;
93 |
94 | importAsName returns [object]
95 | : name1=NAME { $object=ImportAsName( $name1.text ) } ( AS name2=NAME { $object.append_children( [$AS.text, $name2.text] ) } )?
96 | ;
97 |
98 | dottedAsNames returns [object]
99 | : dottedAsName1=dottedAsName { $object = DottedAsNames( $dottedAsName1.object ) }
100 | ( COMMA dottedAsName2=dottedAsName { object.append_children( [$COMMA.text, $dottedAsName2.object] ) } )*
101 | ;
102 |
103 | dottedAsName returns [object]
104 | : dottedName { $object = DottedAsName( $dottedName.object ) } ( AS NAME { $object.append_children( [$AS.text, $NAME.text] ) } )?
105 | ;
106 |
107 | dottedName returns [object] // returns a DottedName object
108 | : name1=NAME { $object = DottedName( $name1.text ) }
109 | ( DOT name2=NAME { $object.append_children( [$DOT.text, $name2.text] ) } )*
110 | ;
111 |
112 | attributeStmt returns [object] // returns an AttributeStmt object.
113 | : expressionStmt { $object = AttributeStmt( $expressionStmt.object ) }
114 | ;
115 |
116 | ruleStmt returns [object] // returns a RuleStmt object.
117 | : RULE id COLON NEWLINE { $object = RuleStmt( [ $RULE.text, $id.object, $COLON.text ], $RULE.getLine(), $RULE.getCharPositionInLine() ) }
118 | INDENT ( ruleAttribute { $object.append_child( $ruleAttribute.object ) } )*
119 | ( when { $object.append_child( $when.object ) } )?
120 | then { $object.append_child( $then.object ) } DEDENT
121 | ;
122 |
123 | id returns [object] // returns an Id object
124 | : NAME { $object = Id( $NAME.text ) }
125 | | STRING { $object = Id( $STRING.text ) }
126 | ;
127 |
128 | ruleAttribute returns [object] // returns a RuleAttribute object
129 | : agendaGroup { $object = RuleAttribute( $agendaGroup.object, $agendaGroup.object.line, $agendaGroup.object.column ) }
130 | ;
131 |
132 | agendaGroup returns [object] // returns a RuleGroup
133 | : AGENDAGROUP id NEWLINE { $object = AgendaGroup( [ $AGENDAGROUP.text, $id.object ], $AGENDAGROUP.getLine(), $AGENDAGROUP.getCharPositionInLine() ) }
134 | ;
135 |
136 | when returns [object] //returns a When object
137 | : WHEN COLON NEWLINE { $object = When( [$WHEN.text, $COLON.text], $WHEN.getLine(), $WHEN.getCharPositionInLine() ) }
138 | INDENT ( ruleCondition { $object.append_child( $ruleCondition.object ) } )? DEDENT
139 | ;
140 |
141 | then returns [object] //return a Then object
142 | : THEN COLON NEWLINE { $object = Then( [$THEN.text, $COLON.text], $THEN.getLine(), $THEN.getCharPositionInLine() ) }
143 | INDENT ( action { $object.append_child( $action.object ) } )+ DEDENT
144 | ;
145 |
146 | ruleCondition returns [object] // returns a NotCondition object
147 | : notCondition NEWLINE { $object = RuleCondition($notCondition.object) }
148 | ;
149 |
150 | notCondition returns [object] // returns a NotCondition object
151 | @init{ $object = NotCondition() }
152 | : ( NOT { $object.append_child( $NOT.text ) } )* condition { $object.append_child( $condition.object ) }
153 | ;
154 |
155 | condition returns [object] // returns a Condition object
156 | @init{ $object = Condition() }
157 | : ( EXISTS { $object.append_child( $EXISTS.text ) } )? classConstraint { $object.append_child( $classConstraint.object ) }
158 | ;
159 |
160 | classConstraint returns [object] // returns ClassConstraint object
161 | @init{ $object = ClassConstraint() }
162 | : ( OBJECTBINDING ASSIGNEQUAL { $object.append_children( [ $OBJECTBINDING.text, $ASSIGNEQUAL.text] ) } )?
163 | NAME LPAREN { $object.append_children( [$NAME.text, $LPAREN.text] ); } ( constraint { $object.append_child( $constraint.object ) } )? RPAREN { $object.append_child( $RPAREN.text ) }
164 | ;
165 |
166 | action returns [object] // returns an Action object
167 | : simpleStmt { $object = Action( $simpleStmt.object, $simpleStmt.object.line, $simpleStmt.object.column ) }
168 | | attributeAction { $object = Action( $attributeAction.object, $attributeAction.object.line, $attributeAction.object.column ) }
169 | | learnAction { $object = Action( $learnAction.object, $learnAction.object.line, $learnAction.object.column ) }
170 | | forgetAction { $object = Action( $forgetAction.object, $forgetAction.object.line, $forgetAction.object.column ) }
171 | | modifyAction { $object = Action( $modifyAction.object, $modifyAction.object.line, $modifyAction.object.column ) }
172 | | haltAction { $object = Action( $haltAction.object, $haltAction.object.line, $haltAction.object.column ) }
173 | ;
174 |
175 | simpleStmt returns [object] // returns a SimpleStmt object
176 | : expressionStmt { $object = SimpleStmt( $expressionStmt.object ) }
177 | | printStmt { $object = SimpleStmt( $printStmt.object, $printStmt.object.line, $printStmt.object.column ) }
178 | ;
179 |
180 | attributeAction returns [object] // returns a AttributeAction object
181 | : ATTRIBUTE expressionStmt { $object = AttributeAction( [ $ATTRIBUTE.text, $expressionStmt.object ] , $ATTRIBUTE.getLine(), $ATTRIBUTE.getCharPositionInLine() ) }
182 | ;
183 |
184 | printStmt returns [object] // returns a PrintStmt object
185 | : PRINT { $object = PrintStmt( $PRINT.text, $PRINT.getLine(), $PRINT.getCharPositionInLine() ) }
186 | ( comparisonList1=comparisonList { $object.append_child( $comparisonList1.object ) }
187 | | RIGHTSHIFT comparisonList2=comparisonList { object.append_children( [$RIGHTSHIFT.text, $comparisonList2.object] ) } )? NEWLINE
188 | ;
189 |
190 | forgetAction returns [object] // returns a ForgetAction object
191 | : ( FORGET { $object = ForgetAction( $FORGET.text, $FORGET.getLine(), $FORGET.getCharPositionInLine() ) }
192 | | DELETE { $object = ForgetAction( $DELETE.text, $DELETE.getLine(), $DELETE.getCharPositionInLine() ) } )
193 | OBJECTBINDING { $object.append_child( $OBJECTBINDING.text ) } NEWLINE
194 | ;
195 |
196 | learnAction returns [object] // returns an LearnActions object
197 | : ( LEARN { $object = LearnAction( $LEARN.text, $LEARN.getLine(), $LEARN.getCharPositionInLine() ) }
198 | | INSERT { $object = LearnAction( $INSERT.text, $INSERT.getLine(), $INSERT.getCharPositionInLine() ) } )
199 | NAME LPAREN { $object.append_children( [$NAME.text, $LPAREN.text] ) }
200 | ( argumentList { $object.append_child( $argumentList.object ) } )? RPAREN { $object.append_child( $RPAREN.text ) } NEWLINE
201 | ;
202 |
203 | modifyAction returns [object] // returns a ModifyAction object
204 | : MODIFY OBJECTBINDING COLON NEWLINE { $object = ModifyAction( [$MODIFY.text, $OBJECTBINDING.text, $COLON.text], $MODIFY.getLine(), $MODIFY.getCharPositionInLine() ) }
205 | INDENT ( propertyAssignment { $object.append_child( $propertyAssignment.object ) } )+ DEDENT
206 | ;
207 |
208 | haltAction returns [object] // returns a HaltAction object
209 | : HALT { $object = HaltAction( $HALT.text, $HALT.getLine(), $HALT.getCharPositionInLine() ) } NEWLINE
210 | ;
211 |
212 | propertyAssignment returns [object] // returns a PropertyAssignment object
213 | : NAME assignment constraint {$object = PropertyAssignment( [$NAME.text, $assignment.object, $constraint.object] ) } NEWLINE
214 | ;
215 |
216 | expressionStmt returns [object] // returns a ExpressionStmt object
217 | : comparisonList1=comparisonList { $object = ExpressionStmt( $comparisonList1.object ) }
218 | ( assignment comparisonList2=comparisonList { $object.append_children( [$assignment.object, $comparisonList2.object] ) } )? NEWLINE
219 | ;
220 |
221 | assignment returns [object] // returns a Assign object
222 | : ASSIGN { $object = Assignment( $ASSIGN.text ) }
223 | | PLUSEQUAL { $object = Assignment( $PLUSEQUAL.text ) }
224 | | MINUSEQUAL { $object = Assignment( $MINUSEQUAL.text ) }
225 | | STAREQUAL { $object = Assignment( $STAREQUAL.text ) }
226 | | SLASHEQUAL { $object = Assignment( $SLASHEQUAL.text ) }
227 | | PERCENTEQUAL { $object = Assignment( $PERCENTEQUAL.text ) }
228 | | AMPEREQUAL { $object = Assignment( $AMPEREQUAL.text ) }
229 | | VBAREQUAL { $object = Assignment( $VBAREQUAL.text ) }
230 | | CIRCUMFLEXEQUAL { $object = Assignment( $CIRCUMFLEXEQUAL.text ) }
231 | | LEFTSHIFTEQUAL { $object = Assignment( $LEFTSHIFTEQUAL.text ) }
232 | | RIGHTSHIFTEQUAL { $object = Assignment( $RIGHTSHIFTEQUAL.text ) }
233 | | DOUBLESTAREQUAL { $object = Assignment( $DOUBLESTAREQUAL.text ) }
234 | | DOUBLESLASHEQUAL { $object = Assignment( $DOUBLESLASHEQUAL.text ) }
235 | ;
236 |
237 | constraint returns [object] // returns a Contraint object
238 | : orConstraint { $object = Constraint( $orConstraint.object ) }
239 | ;
240 |
241 | orConstraint returns [object] // returns an OrContraint object
242 | : constraint1=andConstraint { $object = OrConstraint( $constraint1.object ) }
243 | ( OR constraint2=andConstraint { $object.append_children( [$OR.text, $constraint2.object] ) } )*
244 | ;
245 |
246 | andConstraint returns [object] // returns an AndConstraint object
247 | : constraint1=notConstraint {$object = AndConstraint( $constraint1.object )}
248 | ( AND constraint2=notConstraint { $object.append_children( [$AND.text, $constraint2.object] ) } )*
249 | ;
250 |
251 | notConstraint returns [object] // returns a NotConstraint object
252 | @init{ $object = NotConstraint() }
253 | : ( NOT { $object.append_child( $NOT.text ) } )* comparison { $object.append_child( $comparison.object ) }
254 | ;
255 |
256 | comparison returns [object] // returns a Comparison object
257 | : expression1=expression { $object = Comparison( $expression1.object ) }
258 | ( comparisonOperation expression2=expression { $object.append_children( [ $comparisonOperation.object, $expression2.object] ) } )*
259 | ;
260 |
261 | comparisonOperation returns [object] // returns a ComparisonOperation object
262 | : ( LESS { $object = ComparisonOperation( $LESS.text ) }
263 | | GREATER { $object = ComparisonOperation( $GREATER.text ) }
264 | | EQUAL { $object = ComparisonOperation( $EQUAL.text ) }
265 | | GREATEREQUAL { $object = ComparisonOperation( $GREATEREQUAL.text ) }
266 | | LESSEQUAL { $object = ComparisonOperation( $LESSEQUAL.text ) }
267 | | ALT_NOTEQUAL { $object = ComparisonOperation( $ALT_NOTEQUAL.text ) }
268 | | NOTEQUAL { $object = ComparisonOperation( $NOTEQUAL.text ) }
269 | | IN { $object = ComparisonOperation( "in" ) }
270 | | NOT IN { $object = ComparisonOperation( "not in" ) }
271 | | IS { $object = ComparisonOperation( "is" ) }
272 | | IS NOT { $object = ComparisonOperation( "is not" ) } )
273 | ;
274 |
275 | expression returns [object] // returns Exrpession object
276 | : bitwiseOrExpr { $object = Expression( $bitwiseOrExpr.object ) }
277 | ;
278 |
279 | bitwiseOrExpr returns [object] // returns a BitwiseOrExpr object
280 | : expr1=bitwiseXorExpr { $object = BitwiseOrExpr( $expr1.object ) }
281 | ( VBAR expr2=bitwiseXorExpr { $object.append_children( [$VBAR.text, $expr2.object] ) } )*
282 | ;
283 |
284 | bitwiseXorExpr returns [object] // returns a BitwiseXorExpr object
285 | : expr1=bitwiseAndExpr { $object = BitwiseXorExpr( $expr1.object ) }
286 | ( CIRCUMFLEX expr2=bitwiseAndExpr { $object.append_children( [$CIRCUMFLEX.text, $expr2.object] ) } )*
287 | ;
288 |
289 | bitwiseAndExpr returns [object] // returns a BitwiseAndExpr object
290 | : expr1=shiftExpr { $object = BitwiseAndExpr( $expr1.object ) }
291 | ( AMPER expr2=shiftExpr { $object.append_children( [$AMPER.text, $expr2.object] ) } )*
292 | ;
293 |
294 | shiftExpr returns [object] // returns a ShiftExpr object
295 | : expr1=arithExpr { $object = ShiftExpr( $expr1.object ) }
296 | ( ( LEFTSHIFT { $object.append_child( $LEFTSHIFT.text ) } | RIGHTSHIFT { $object.append_child( $RIGHTSHIFT.text ) } )
297 | expr2=arithExpr { $object.append_child( $expr2.object ) } )*
298 | ;
299 |
300 | arithExpr returns [object] // returns a ArithExpr object
301 | : term1=term { $object = ArithExpr( $term1.object ) }
302 | ( ( PLUS { $object.append_child( $PLUS.text ) } | MINUS { $object.append_child( $MINUS.text ) } )
303 | term2=term { $object.append_child( $term2.object ) } )*
304 | ;
305 |
306 | term returns [object] // returns a Term object
307 | : factor1=factor {$object = Term( $factor1.object ) }
308 | ( (STAR { $object.append_child( $STAR.text ) } | SLASH { $object.append_child( $SLASH.text ) }
309 | | PERCENT { $object.append_child( $PERCENT.text ) } | DOUBLESLASH { $object.append_child( $DOUBLESLASH.text ) } )
310 | factor2=factor { $object.append_child( $factor2.object ) } )*
311 | ;
312 |
313 | factor returns [object] // returns a Factor object
314 | : PLUS factor1=factor { $object = Factor( [$PLUS.text, $factor1.object] ) }
315 | | MINUS factor2=factor { $object = Factor( [$MINUS.text, $factor2.object] ) }
316 | | TILDE factor3=factor { $object = Factor( [$TILDE.text, $factor3.object] ) }
317 | | power { $object = Factor( $power.object ) }
318 | ;
319 |
320 | power returns [object] // returns a Power object
321 | : atom { $object = Power( $atom.object ) }
322 | ( trailer { $object.append_child( $trailer.object ) } )*
323 | (options {greedy=true;}:DOUBLESTAR factor { $object.append_children( [$DOUBLESTAR.text, $factor.object] ) } )?
324 | ;
325 |
326 | atom returns [object] // returns a Atom object
327 | : LPAREN {$object = Atom( $LPAREN.text ) }
328 | ( comparisonList1=comparisonList { $object.append_child( $comparisonList1.object ) } )?
329 | RPAREN { $object.append_child( $RPAREN.text ) }
330 | | LBRACK {$object = Atom( $LBRACK.text ) }
331 | ( listmaker { $object.append_child( $listmaker.object ) } )?
332 | RBRACK { $object.append_child( $RBRACK.text ) }
333 | | LCURLY {$object = Atom( $LCURLY.text ) }
334 | ( dictmaker { $object.append_child( $dictmaker.object ) } )?
335 | RCURLY { $object.append_child( $RCURLY.text ) }
336 | // | BACKQUOTE comparisonList2=comparisonList BACKQUOTE { $object = Atom( ["`" , $comparisonList2.object, "`"] ) } Removed, used repr(contraint) instead
337 | | NAME {$object = Atom( $NAME.text ) }
338 | | OBJECTBINDING {$object = Atom( $OBJECTBINDING.text ) }
339 | | INT {$object = Atom( $INT.text ) }
340 | | LONGINT {$object = Atom( $LONGINT.text ) }
341 | | FLOAT {$object = Atom( $FLOAT.text ) }
342 | | COMPLEX {$object = Atom( $COMPLEX.text ) }
343 | | {$object = Atom() } ( STRING { $object.append_child( $STRING.text ) } )+
344 | ;
345 |
346 | listmaker returns [object] // returns a ListMaker object
347 | : constraint1=constraint {$object = ListMaker( $constraint1.object ) }
348 | (options {greedy=true;}:COMMA constraint2=constraint { $object.append_children( [ ",", $constraint2.object] ) } )*
349 | ( COMMA { $object.append_child( "," ) } )?
350 | ;
351 |
352 | comparisonList returns [object] // returns a ComparisonList object
353 | : constraint1=constraint { $object = ComparisonList( $constraint1.object ) }
354 | (options {k=2;}:COMMA constraint2=constraint { $object.append_children( [",", $constraint2.object] ) } )*
355 | ( COMMA { $object.append_child( "," ) } )?
356 | ;
357 |
358 | trailer returns [object] // returns a Trailer object
359 | : LPAREN {$object = Trailer( $LPAREN.text ) } (argumentList { $object.append_child( $argumentList.object ) } )? RPAREN { $object.append_child( $RPAREN.text ) }
360 | | LBRACK {$object = Trailer( $LBRACK.text ) } (constraint { $object.append_child( $constraint.object ) } )? RBRACK { $object.append_child( $RBRACK.text ) }
361 | | DOT NAME { $object = Trailer( [$DOT.text, $NAME.text] ) }
362 | ;
363 |
364 | expressionList returns [object] // returns an ExpressionList object
365 | : expression1=expression { $object = ExpressionList( $expression1.object ) }
366 | ( options {k=2;}: COMMA expression2=expression { $object.append_children( [ ",", $expression2.object ] ) } )*
367 | ( COMMA { $object.append_child( "," ) } )?
368 | ;
369 |
370 | dictmaker returns [object] // returns a DictMaker object
371 | : constraint1=constraint { $object = DictMaker( $constraint1.object ) }
372 | COLON constraint2=constraint { $object.append_children( [":", $constraint2.object] ) }
373 | (options {k=2;}: COMMA constraint3=constraint COLON constraint4=constraint { $object.append_children( [",", $constraint3.object, ":", $constraint4.object] ) } )*
374 | ( COMMA { $object.append_child( "," ) } )?
375 | ;
376 |
377 | argumentList returns [object] // returns an ArgumentList object
378 | : constraint1=constraint { $object = ArgumentList( $constraint1.object ) }
379 | ( COMMA constraint2=constraint { $object.append_children( [",", $constraint2.object] ) } )*
380 | ( COMMA { $object.append_child( "," ) } )?
381 | ;
382 |
383 | //
384 | // L E X E R
385 | //
386 |
387 | LPAREN
388 | : '(' {self.implicitLineJoiningLevel += 1}
389 | ;
390 |
391 | RPAREN
392 | : ')' {self.implicitLineJoiningLevel -= 1}
393 | ;
394 |
395 | LBRACK
396 | : '[' {self.implicitLineJoiningLevel += 1}
397 | ;
398 |
399 | RBRACK
400 | : ']' {self.implicitLineJoiningLevel -= 1}
401 | ;
402 |
403 | LCURLY
404 | : '{' {self.implicitLineJoiningLevel += 1}
405 | ;
406 |
407 | RCURLY
408 | : '}' {self.implicitLineJoiningLevel -= 1}
409 | ;
410 |
411 | COLON
412 | : ':'
413 | ;
414 |
415 | COMMA
416 | : ','
417 | ;
418 |
419 | DOT
420 | : '.'
421 | ;
422 |
423 | SEMI
424 | : ';'
425 | ;
426 |
427 | PLUS
428 | : '+'
429 | ;
430 |
431 | MINUS
432 | : '-'
433 | ;
434 |
435 | STAR
436 | : '*'
437 | ;
438 |
439 | DOLLAR
440 | : '$'
441 | ;
442 |
443 | SLASH
444 | : '/'
445 | ;
446 |
447 | VBAR
448 | : '|'
449 | ;
450 |
451 | AMPER
452 | : '&'
453 | ;
454 |
455 | LESS
456 | : '<'
457 | ;
458 |
459 | GREATER
460 | : '>'
461 | ;
462 |
463 | ASSIGN
464 | : '='
465 | ;
466 |
467 | PERCENT
468 | : '%'
469 | ;
470 |
471 | BACKQUOTE
472 | : '`'
473 | ;
474 |
475 | CIRCUMFLEX
476 | : '^'
477 | ;
478 |
479 | TILDE
480 | : '~'
481 | ;
482 |
483 | EQUAL
484 | : '=='
485 | ;
486 |
487 | ASSIGNEQUAL
488 | : ':='
489 | ;
490 |
491 | NOTEQUAL
492 | : '!='
493 | ;
494 |
495 | ALT_NOTEQUAL
496 | : '<>'
497 | ;
498 |
499 | LESSEQUAL
500 | : '<='
501 | ;
502 |
503 | LEFTSHIFT
504 | : '<<'
505 | ;
506 |
507 | GREATEREQUAL
508 | : '>='
509 | ;
510 |
511 | RIGHTSHIFT
512 | : '>>'
513 | ;
514 |
515 | PLUSEQUAL
516 | : '+='
517 | ;
518 |
519 | MINUSEQUAL
520 | : '-='
521 | ;
522 |
523 | DOUBLESTAR
524 | : '**'
525 | ;
526 |
527 | STAREQUAL
528 | : '*='
529 | ;
530 |
531 | DOUBLESLASH
532 | : '//'
533 | ;
534 |
535 | SLASHEQUAL
536 | : '/='
537 | ;
538 |
539 | VBAREQUAL
540 | : '|='
541 | ;
542 |
543 | PERCENTEQUAL
544 | : '%='
545 | ;
546 |
547 | AMPEREQUAL
548 | : '&='
549 | ;
550 |
551 | CIRCUMFLEXEQUAL
552 | : '^='
553 | ;
554 |
555 | LEFTSHIFTEQUAL
556 | : '<<='
557 | ;
558 |
559 | RIGHTSHIFTEQUAL
560 | : '>>='
561 | ;
562 |
563 | DOUBLESTAREQUAL
564 | : '**='
565 | ;
566 |
567 | DOUBLESLASHEQUAL
568 | : '//='
569 | ;
570 |
571 | GLOBAL
572 | : 'global'
573 | ;
574 |
575 | ATTRIBUTE
576 | : 'attribute'
577 | ;
578 |
579 | RULE
580 | : 'rule'
581 | ;
582 |
583 | AGENDAGROUP
584 | : 'agenda-group'
585 | ;
586 |
587 | WHEN
588 | : 'when'
589 | ;
590 |
591 | EXISTS
592 | : 'exists'
593 | ;
594 |
595 | THEN
596 | : 'then'
597 | ;
598 |
599 | MODIFY
600 | : 'modify'
601 | ;
602 |
603 | INSERT
604 | : 'insert'
605 | ;
606 |
607 | LEARN
608 | : 'learn'
609 | ;
610 |
611 | DELETE
612 | : 'delete'
613 | ;
614 |
615 | FORGET
616 | : 'forget'
617 | ;
618 |
619 | HALT
620 | : 'halt'
621 | ;
622 |
623 | PRINT
624 | : 'print'
625 | ;
626 |
627 | IMPORT
628 | : 'import'
629 | ;
630 |
631 | FROM
632 | : 'from'
633 | ;
634 |
635 | AND
636 | : 'and'
637 | ;
638 |
639 | OR
640 | : 'or'
641 | ;
642 |
643 | IN
644 | : 'in'
645 | ;
646 |
647 | IS
648 | : 'is'
649 | ;
650 |
651 | AS
652 | : 'as'
653 | ;
654 |
655 | NOT
656 | : 'not'
657 | ;
658 |
659 | fragment
660 | LETTER
661 | : ('a'..'z' | 'A'..'Z')
662 | ;
663 |
664 | fragment
665 | DIGIT
666 | : ('0'.. '9')
667 | ;
668 |
669 | FLOAT
670 | : '.' DIGIT+ (EXPONENT)?
671 | | DIGIT+ '.' EXPONENT
672 | | DIGIT+ ('.' (DIGIT+ (EXPONENT)?)? | EXPONENT)
673 | ;
674 |
675 | LONGINT
676 | : INT ('l'|'L')
677 | ;
678 |
679 | fragment
680 | EXPONENT
681 | : ('e' | 'E') ( '+' | '-' )? DIGIT+
682 | ;
683 |
684 | INT
685 | : '0' ('x' | 'X') ( DIGIT | 'a' .. 'f' | 'A' .. 'F' )+
686 | | '0' DIGIT*
687 | | '1'..'9' DIGIT*
688 | ;
689 |
690 | COMPLEX
691 | : INT ('j'|'J')
692 | | FLOAT ('j'|'J')
693 | ;
694 |
695 | NAME
696 | : ( LETTER | '_') ( LETTER | '_' | DIGIT )*
697 | ;
698 |
699 | OBJECTBINDING
700 | : DOLLAR NAME
701 | ;
702 |
703 | STRING
704 | : ('r'|'u'|'ur')?
705 | ( '\'\'\'' (options {greedy=false;}:TRIAPOS)* '\'\'\''
706 | | '"""' (options {greedy=false;}:TRIQUOTE)* '"""'
707 | | '"' (ESC|~('\\'|'\n'|'"'))* '"'
708 | | '\'' (ESC|~('\\'|'\n'|'\''))* '\''
709 | )
710 | ;
711 |
712 | fragment
713 | TRIQUOTE
714 | : '"'? '"'? (ESC|~('\\'|'"'))+
715 | ;
716 |
717 | fragment
718 | TRIAPOS
719 | : '\''? '\''? (ESC|~('\\'|'\''))+
720 | ;
721 |
722 | fragment
723 | ESC
724 | : '\\' .
725 | ;
726 |
727 | CONTINUED_LINE
728 | : '\\' ( '\r' )? '\n' ( ' ' | '\t' )* { $channel=HIDDEN }
729 | ( newline=NEWLINE { self.emit( ClassicToken( NEWLINE, newline.text ) ) }
730 | |
731 | )
732 | ;
733 |
734 | NEWLINE
735 | : ( ( '\u000C' )? ( '\r' )? '\n' )+ {
736 | if self.startPosition == 0 or self.implicitLineJoiningLevel > 0:
737 | $channel=HIDDEN
738 | }
739 | ;
740 |
741 | WS
742 | : { self.startPosition > 0 }? => ( ' ' | '\t' )+ { $channel=HIDDEN }
743 | ;
744 |
745 | LEADING_WS
746 | @init { spaces = 0 }
747 | : { self.startPosition == 0 }? =>
748 | ( {self.implicitLineJoiningLevel > 0}? ( ' ' | '\t' )+ { $channel=HIDDEN }
749 | | ( ' ' { spaces += 1 }
750 | | '\t' {
751 | spaces += 8
752 | spaces -= (spaces \% 8)
753 | }
754 | )+ { self.emit(ClassicToken(LEADING_WS, ' '*spaces)) }
755 | ( ('\r')? '\n' {
756 | if self._state.token is not None:
757 | self._state.token.setChannel(99)
758 | else:
759 | $channel=HIDDEN
760 | }
761 | )*
762 | )
763 | ;
764 |
765 | COMMENT
766 | @init { $channel=HIDDEN }
767 | : { self.startPosition == 0 }? => (' '|'\t')* '#' (~'\n')* '\n'+
768 | | { self.startPosition > 0 }? => '#' (~'\n')*
769 | ;
770 |
--------------------------------------------------------------------------------
/intellect/grammar/Policy.tokens:
--------------------------------------------------------------------------------
1 | SLASHEQUAL=38
2 | BACKQUOTE=80
3 | EXPONENT=84
4 | STAR=64
5 | CIRCUMFLEXEQUAL=42
6 | LETTER=82
7 | TRIAPOS=85
8 | GREATEREQUAL=52
9 | COMPLEX=77
10 | ASSIGNEQUAL=24
11 | NOT=21
12 | NOTEQUAL=55
13 | LEADING_WS=90
14 | MINUSEQUAL=36
15 | VBAR=58
16 | RPAREN=10
17 | IMPORT=7
18 | NAME=12
19 | GREATER=50
20 | INSERT=31
21 | DOUBLESTAREQUAL=45
22 | LESS=49
23 | COMMENT=91
24 | RBRACK=71
25 | RULE=15
26 | LCURLY=72
27 | INT=74
28 | DELETE=29
29 | RIGHTSHIFT=27
30 | DOUBLESLASHEQUAL=46
31 | WS=89
32 | VBAREQUAL=41
33 | OR=47
34 | LONGINT=75
35 | FORGET=28
36 | FROM=8
37 | PERCENTEQUAL=39
38 | LESSEQUAL=53
39 | DOLLAR=79
40 | MODIFY=32
41 | DOUBLESLASH=67
42 | LBRACK=70
43 | CONTINUED_LINE=88
44 | OBJECTBINDING=23
45 | DOUBLESTAR=69
46 | HALT=33
47 | ESC=87
48 | ATTRIBUTE=25
49 | DEDENT=5
50 | FLOAT=76
51 | RIGHTSHIFTEQUAL=44
52 | AND=48
53 | LEARN=30
54 | INDENT=4
55 | LPAREN=9
56 | PLUSEQUAL=35
57 | AS=13
58 | SLASH=65
59 | THEN=20
60 | IN=56
61 | COMMA=11
62 | IS=57
63 | AMPER=60
64 | EQUAL=51
65 | TILDE=68
66 | LEFTSHIFTEQUAL=43
67 | LEFTSHIFT=61
68 | PLUS=62
69 | EXISTS=22
70 | DIGIT=83
71 | DOT=14
72 | AGENDAGROUP=18
73 | PERCENT=66
74 | MINUS=63
75 | SEMI=78
76 | PRINT=26
77 | COLON=16
78 | TRIQUOTE=86
79 | AMPEREQUAL=40
80 | NEWLINE=6
81 | WHEN=19
82 | RCURLY=73
83 | ASSIGN=34
84 | GLOBAL=81
85 | STAREQUAL=37
86 | CIRCUMFLEX=59
87 | STRING=17
88 | ALT_NOTEQUAL=54
89 |
--------------------------------------------------------------------------------
/intellect/grammar/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2011, The MITRE Corporation.
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 | 1. Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright
10 | notice, this list of conditions and the following disclaimer in the
11 | documentation and/or other materials provided with the distribution.
12 | 3. All advertising materials mentioning features or use of this software
13 | must display the following acknowledgement:
14 | This product includes software developed by the author.
15 | 4. Neither the name of the author nor the
16 | names of its contributors may be used to endorse or promote products
17 | derived from this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | """
--------------------------------------------------------------------------------
/intellect/reflection.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/python
2 | # coding=utf-8
3 | """
4 | Copyright (c) 2011, The MITRE Corporation.
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without
8 | modification, are permitted provided that the following conditions are met:
9 | 1. Redistributions of source code must retain the above copyright
10 | notice, this list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright
12 | notice, this list of conditions and the following disclaimer in the
13 | documentation and/or other materials provided with the distribution.
14 | 3. All advertising materials mentioning features or use of this software
15 | must display the following acknowledgement:
16 | This product includes software developed by the author.
17 | 4. Neither the name of the author nor the
18 | names of its contributors may be used to endorse or promote products
19 | derived from this software without specific prior written permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
22 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | """
32 |
33 | """
34 | reflection
35 |
36 | Description: Functions for performing introspection
37 |
38 | Initial Version: Feb 1, 2011
39 |
40 | @author: Michael Joseph Walsh
41 | """
42 |
43 | import inspect, os, sys, types, logging
44 |
45 | FUNCTION = "function"
46 | BUILTIN_FUNCTION = "built-in function"
47 | INSTANCE_METHOD = "instance method"
48 | CLASS_METHOD = classmethod
49 | STATIC_METHOD = staticmethod
50 | PROPERTY = property
51 | DATA = "data"
52 |
53 | def log(msg, name="intellect", level=logging.DEBUG):
54 | '''
55 | Logs at the 'level' for the messaged 'msg'
56 |
57 | Args:
58 | name: the name of the logger
59 | level: must be either logging.DEBUG, logging.INFO, logging.WARNING,
60 | logging.ERROR, logging.CRITICAL
61 | msg: message string
62 | '''
63 |
64 | if level not in [logging.DEBUG, logging.INFO, logging.WARNING,
65 | logging.ERROR, logging.CRITICAL]:
66 | raise ValueError("'level' must be either logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL")
67 |
68 | logging.getLogger(name).log(level, "{0} :: {1}".format(__name__, msg))
69 |
70 |
71 | def is_method(klazz, name):
72 | '''
73 | does this class have this method?
74 | '''
75 | return (inspect_class_for_attribute(klazz, name)[0] in [INSTANCE_METHOD, CLASS_METHOD, STATIC_METHOD])
76 |
77 |
78 | def has_attribute(klazz, name):
79 | '''
80 | does this class have this attribute in other words does it have this
81 | (instance/class/static) method, property, or global?
82 | '''
83 | return (inspect_class_for_attribute(klazz, name)) != (None, None, None)
84 |
85 |
86 | def is_instance_method(klazz, name):
87 | '''
88 | does this class have this method?
89 | '''
90 | return (inspect(klazz, name)[0] is INSTANCE_METHOD)
91 |
92 |
93 | def is_class_method(klazz, name):
94 | '''
95 | does this class have this class method?
96 | '''
97 | return (inspect_class_for_attribute(klazz, name)[0] is CLASS_METHOD)
98 |
99 |
100 | def is_static_method(klazz, name):
101 | '''
102 | does this class have this static method?
103 | '''
104 | return (inspect_class_for_attribute(klazz, name)[0] is STATIC_METHOD)
105 |
106 |
107 | def is_property(klazz, name):
108 | '''
109 | does this class have this property?
110 | '''
111 | return (inspect_class_for_attribute(klazz, name)[0] is PROPERTY)
112 |
113 |
114 | def is_data(klazz, name):
115 | '''
116 | does this class have this global or attribute?
117 | '''
118 | return (inspect_class_for_attribute(object, name)[0] is DATA)
119 |
120 |
121 | def inspect_class_for_attribute(klazz, name):
122 | '''
123 | For a given class inspects it for the existence of named instance/class/static method,
124 | property, and data element (global or attribute) attribute; and returns in tuple form
125 | (kind, obj, homeClass)
126 | '''
127 |
128 | if inspect.isclass(klazz):
129 | if name in dir(klazz):
130 | if name in klazz.__dict__:
131 | obj = klazz.__dict__[name]
132 | else:
133 | obj = getattr(klazz, name)
134 |
135 | homeClass = getattr(obj, "__objclass__", None)
136 |
137 | if homeClass is None:
138 | for base in inspect.getmro(klazz):
139 | if name in base.__dict__:
140 | homeClass = base
141 | break
142 |
143 | if ((homeClass is not None) and (name in homeClass.__dict__)):
144 | obj = homeClass.__dict__[name]
145 | obj_via_getattr = getattr(klazz, name)
146 |
147 | if isinstance(obj, staticmethod):
148 | kind = staticmethod
149 | elif isinstance(obj, classmethod):
150 | kind = CLASS_METHOD
151 | elif isinstance(obj, property):
152 | kind = PROPERTY
153 | elif (inspect.ismethod(obj_via_getattr) or
154 | inspect.ismethoddescriptor(obj_via_getattr)):
155 | kind = INSTANCE_METHOD
156 | else:
157 | kind = DATA
158 |
159 | return (kind, obj, homeClass)
160 | else:
161 | return (None, None, None)
162 | else:
163 | raise TypeError("parameter 'klazz' must a class object, not an instance of a class.")
164 |
165 |
166 | def inspect_module_for_attribute(module, name):
167 | '''
168 | For a given module inspects it for the existence of named built-in,
169 | functions, and data attributes; and returns in tuple form
170 | (kind, obj)
171 | '''
172 | if inspect.ismodule(module):
173 |
174 | names = dir(module)
175 |
176 | if name in names:
177 |
178 | if name in module.__dict__:
179 | obj = module.__dict__[name]
180 | else:
181 | obj = getattr(module, name)
182 |
183 | if isinstance(obj, types.FunctionType):
184 | kind = FUNCTION
185 | elif isinstance(obj, types.BuiltinFunctionType):
186 | kind = BUILTIN_FUNCTION
187 | else:
188 | kind = DATA
189 |
190 | return (kind, obj)
191 | else:
192 | return (None, None, None)
193 | else:
194 | raise TypeError("parameter 'module' must be a Module, not an instance of a class, nor a Class.")
195 |
196 |
197 | def for_class_list(klazz, type):
198 | '''
199 | A wrapper to for_object_list-method for evaluating just Class-type objects
200 | '''
201 | if inspect.isclass(klazz):
202 | return for_object_list(klazz, type)
203 | else:
204 | raise TypeError("parameter 'klazz' was not a Class, but an instance object.")
205 |
206 |
207 | def for_object_list(object, type):
208 | '''
209 | For a given object inspects it for the existence of named instance/class/static method,
210 | property, and data element (global or attribute) attribute; and returns in tuple form
211 | (kind, obj, homeClass)
212 | '''
213 | value = []
214 | names = dir(object)
215 |
216 | for name in names:
217 | if name in object.__dict__:
218 | obj = object.__dict__[name]
219 | else:
220 | obj = getattr(object, name)
221 |
222 | homeClass = getattr(obj, "__objclass__", None)
223 |
224 | if homeClass is None:
225 | for base in inspect.getmro(object):
226 | if name in base.__dict__:
227 | homeClass = base
228 | break
229 |
230 | if ((homeClass is not None) and (name in homeClass.__dict__)):
231 | obj = homeClass.__dict__[name]
232 | obj_via_getattr = getattr(object, name)
233 |
234 | if isinstance(obj, staticmethod):
235 | kind = staticmethod
236 | elif isinstance(obj, classmethod):
237 | kind = CLASS_METHOD
238 | elif isinstance(obj, property):
239 | kind = PROPERTY
240 | elif (inspect.ismethod(obj_via_getattr) or
241 | inspect.ismethoddescriptor(obj_via_getattr)):
242 | kind = INSTANCE_METHOD
243 | else:
244 | kind = DATA
245 |
246 | print name, kind, obj
247 |
248 | if (kind is type):
249 | value.append(name)
250 |
251 | return value
252 |
253 |
254 | def for_module_list(module, type):
255 | '''
256 | For a given module list either the built-in or data attributes.
257 |
258 | Example:
259 |
260 | for_module_list(__builtins__, BUILTIN)
261 |
262 | Will return a list of all the Python interpreter built-in functions
263 |
264 | '''
265 |
266 | value = []
267 |
268 | if inspect.ismodule(module):
269 | names = dir(module)
270 |
271 | for name in names:
272 |
273 | if name in module.__dict__:
274 | obj = module.__dict__[name]
275 | else:
276 | obj = getattr(module, name)
277 |
278 | if isinstance(obj, types.FunctionType):
279 | kind = FUNCTION
280 | elif isinstance(obj, types.BuiltinFunctionType):
281 | kind = BUILTIN_FUNCTION
282 | else:
283 | kind = DATA
284 |
285 | if (kind is type):
286 | value.append(name)
287 |
288 | return value
289 |
290 |
291 | def class_from_string(className, policy):
292 | """
293 | Returns class object
294 |
295 | Args:
296 | className: A string holding either the class identifier or local name.
297 | policy: Policy providing ImportFrom objects to inspect.
298 |
299 | Raises:
300 | SyntaxError, if the class wasn't declared in a fromImport statement in the policy file
301 | """
302 |
303 | identifier = ""
304 | dottedName = ""
305 |
306 | importFromClass = class_from_str("intellect.Node.ImportFrom")
307 |
308 | importFroms = [importStmt.importStmt for importStmt in policy.importStmts if isinstance(importStmt.importStmt, importFromClass)]
309 |
310 | if not importFroms:
311 | raise SyntaxError("{0} was not declared in a importFrom statement in the policy file: '{1}'".format(className, policy.path))
312 | else:
313 | for importFrom in reversed(importFroms):
314 | for importAsName in reversed(importFrom.importAsNames.importAsNames):
315 | if importAsName.localName is not None and importAsName.localName == className:
316 | # finding a match matching the localname for the className,
317 | # hold the class's dottedName and identifier for later use
318 | matchedImportFrom = importFrom # used for raising SyntaxError
319 | dottedName = importFrom.dottedName
320 | identifier = importAsName.identifier
321 | break
322 | elif importAsName.localName is None and importAsName.identifier == className:
323 | # finding a match matching the localname for the className,
324 | # hold the class's dottedName and identifier for later use
325 | matchedImportFrom = importFrom # used for raising SyntaxError
326 | dottedName = importFrom.dottedName
327 | identifier = importAsName.identifier
328 | break
329 |
330 | if identifier:
331 | break
332 |
333 | if not identifier:
334 | # the className was not imported, raise a SyntaxError
335 | # TODO: include the line to assist the policy author
336 | raise SyntaxError("{0} was not declared in a fromImport statement in the policy file: '{1}'".format(className, policy.path))
337 | else:
338 | # otherwise return the class
339 |
340 | try:
341 | module = __import__(str(dottedName), globals(), locals(), [identifier])
342 | except ImportError as detail:
343 | raise SyntaxError("{0} at line: {1} in policy file: {2}".format(detail, matchedImportFrom.line, policy.path))
344 |
345 | try:
346 | klazz = getattr(module, identifier)
347 | except AttributeError:
348 | raise SyntaxError("'{0}' does not exist in module imported from at line: {1} in policy file: '{2}'".format(identifier, matchedImportFrom.line, policy.path))
349 |
350 | # return the class
351 | log("returning {0} for '{1}'".format(klazz, className))
352 |
353 | return klazz
354 |
355 |
356 | def module_from_string(moduleName, policy):
357 | """
358 | Returns module object
359 |
360 | Args:
361 | module: A string holding either the module identifier or local name.
362 | policy: Policy providing ImportFrom objects to inspect.
363 |
364 | Raises:
365 | SyntaxError, if the module wasn't declared in a importName statement in the policy file
366 | """
367 |
368 | dottedName = ""
369 |
370 | importNameClass = class_from_str("intellect.Node.ImportName")
371 |
372 | importNames = [importStmt.importStmt for importStmt in policy.importStmts if isinstance(importStmt.importStmt, importNameClass)]
373 |
374 | if not importNames:
375 | raise SyntaxError("{0} was not declared in a importName statement in the policy file: '{1}'".format(moduleName, policy.path))
376 | else:
377 | for importName in importNames:
378 |
379 | # first check all the localNames
380 | for dottedAsName in importName.dottedAsNames.dottedAsNames:
381 | if dottedAsName.localName is not None and dottedAsName.localName == moduleName:
382 | # finding a match matching the localname for the moduleName,
383 | # hold the module's dottedName for later use
384 | matchedImportName = importName # used for raising SyntaxError
385 | dottedName = dottedAsName.dottedName
386 | break
387 | elif dottedAsName.dottedName == moduleName:
388 | # finding a matching identifier,
389 | # hold the class's dottedName and identifier for later use
390 | matchedImportName = importName # used for raising PolicyException
391 | dottedName = moduleName
392 | break
393 |
394 | if dottedName:
395 | break
396 |
397 | if not dottedName:
398 | # the className was not imported, raise a SyntaxError
399 | # TODO: include the line to assist the policy author
400 | raise SyntaxError("{0} was not declared in a importName statement in the policy file: '{1}'".format(moduleName, policy.path))
401 | else:
402 | # otherwise return the module
403 | try:
404 | # return the package object
405 | module = __import__(str(dottedName))
406 | # then dynamically load the module from the package
407 | components = str(dottedName).split('.')
408 | for comp in components[1:]:
409 | module = getattr(module, comp)
410 |
411 | except ImportError as detail:
412 | raise SyntaxError("{0} at line: {1} in policy file: '{2}'".format(detail, matchedImportName.line, policy.path))
413 |
414 | # returning the module, not the package...
415 | log("returning a {0}".format(module))
416 |
417 | return module
418 |
419 |
420 | def module_from_str(name):
421 | '''
422 | Returns a Module object from dottedName.identifier str such as
423 | 'intellect.reflection'.
424 | '''
425 | module = sys.modules[name]
426 |
427 | log("returning {0} for {1}".format(module, name))
428 | return module
429 |
430 |
431 | def class_from_str(name):
432 | '''
433 | Returns a Class object from dottedName.identifier str such as
434 | 'intellect.Intellect.Intellect'.
435 | '''
436 |
437 | dottedName, identifier = name.rsplit('.', 1)
438 | module = __import__(str(dottedName), globals(), locals(), [identifier])
439 | klazz = getattr(module, identifier)
440 |
441 | log("returning {0} for {1}".format(klazz, name))
442 | return klazz
443 |
444 |
445 | def is_instance(instance, klazz):
446 | '''
447 | If the python interpreter is running a module as the main program,
448 | instances of the classes define in the same module will be instances
449 | of the scope (__main__) instead of what is expected.
450 |
451 | So, more work is needed to determine if the instance is of type klazz
452 |
453 | Returns True or False
454 |
455 | Args:
456 | instance: An object instance to introspect
457 | klazz: a class object.
458 | '''
459 |
460 | log("instance = {0}".format(instance))
461 | log("klazz = {0}".format(klazz))
462 |
463 | value = isinstance(instance, klazz)
464 |
465 | log("value = {0}".format(value))
466 |
467 | if (not value):
468 | path = sys.modules[instance.__module__].__file__.rsplit(".", 1)[0]
469 | pathComponents = path.split(os.sep)
470 | moduleName = pathComponents[len(pathComponents) - 1]
471 |
472 | del pathComponents[len(pathComponents) - 1:]
473 |
474 | path = "".join(path.rsplit(moduleName, 1)).rstrip(os.sep)
475 |
476 | while True:
477 | if os.path.exists(path + os.sep + "__init__.py"):
478 | log("{0} has a __init__.py file".format(path))
479 | moduleComponent = pathComponents[len(pathComponents) - 1]
480 | moduleName = moduleComponent + "." + moduleName
481 | path = "".join(path.rsplit(moduleComponent, 1)).rstrip(os.sep)
482 |
483 | del pathComponents[len(pathComponents) - 1:]
484 | else:
485 | log("{0} doesn't have a __init__.py file".format(path))
486 | break
487 |
488 | value = ((moduleName + '.' + instance.__class__.__name__) == (klazz.__module__ + '.' + klazz.__name__))
489 |
490 | log("value = {0}".format(value))
491 |
492 | return value
493 |
494 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | '''
4 | Copyright (c) 2011, Michael Joseph Walsh
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without
8 | modification, are permitted provided that the following conditions are met:
9 | 1. Redistributions of source code must retain the above copyright
10 | notice, this list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright
12 | notice, this list of conditions and the following disclaimer in the
13 | documentation and/or other materials provided with the distribution.
14 | 3. All advertising materials mentioning features or use of this software
15 | must display the following acknowledgement:
16 | This product includes software developed by the author.
17 | 4. Neither the name of the author nor the
18 | names of its contributors may be used to endorse or promote products
19 | derived from this software without specific prior written permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
22 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | '''
32 |
33 | import os
34 | from setuptools import setup, find_packages
35 |
36 | DESCRIPTION = 'A Domain-specific language and Rules Engine for Python'
37 |
38 | if os.path.exists("README.rst"):
39 | long_description = open(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'README.rst')).read()
40 | else:
41 | long_description = "See http://pypi.python.org/pypi/Intellect"
42 |
43 | def get_version(version_tuple):
44 | version = '%s.%s' % (version_tuple[0], version_tuple[1])
45 | if version_tuple[2]:
46 | version = '%s.%s' % (version, version_tuple[2])
47 | if version_tuple[3]:
48 | version = '%s.%s' % (version, version_tuple[3])
49 | return version
50 |
51 | def fullsplit(path, result=None):
52 | """
53 | Split a pathname into components (the opposite of os.path.join) in a
54 | platform-neutral way.
55 | """
56 | if result is None:
57 | result = []
58 | head, tail = os.path.split(path)
59 | if head == '':
60 | return [tail] + result
61 | if head == path:
62 | return result
63 | return fullsplit(head, [tail] + result)
64 |
65 | init = os.path.join(os.path.dirname(__file__), 'intellect', '__init__.py')
66 | version_line = filter(lambda l: l.startswith('VERSION'), open(init))[0]
67 | VERSION = get_version(eval(version_line.split('=')[-1]))
68 | print VERSION
69 |
70 | CLASSIFIERS = [
71 | 'Development Status :: 4 - Beta',
72 | 'Intended Audience :: Developers',
73 | 'License :: OSI Approved :: BSD License',
74 | 'Operating System :: OS Independent',
75 | 'Programming Language :: Python :: 2.6',
76 | 'Programming Language :: Python :: 2.7',
77 | "Topic :: Software Development :: Libraries :: Python Modules"
78 | ]
79 |
80 | packages, data_files = [], []
81 | root_dir = os.path.dirname(__file__)
82 | if root_dir != '':
83 | os.chdir(root_dir)
84 |
85 | intellect_dir = 'intellect'
86 |
87 | for dirpath, dirnames, filenames in os.walk(intellect_dir):
88 | for i, dirname in enumerate(dirnames):
89 | if dirname.startswith('.'): del dirnames[i]
90 | if '__init__.py' in filenames:
91 | packages.append('.'.join(fullsplit(dirpath)))
92 | elif filenames:
93 | data_files.append([dirpath, [os.path.join(dirpath, f) for f in filenames]])
94 |
95 | setup(name="Intellect",
96 | version=VERSION,
97 | description=DESCRIPTION,
98 | long_description=long_description,
99 | author='Michael Joseph Walsh',
100 | author_email='github.com{nospam}nemonik.com',
101 | url='http://github.com/nemonik/Intellect',
102 | classifiers=CLASSIFIERS,
103 | keywords='intellect rules engine dsl policy',
104 | license='BSD, 4-clause license',
105 | packages=packages,
106 | package_data={'': ['*.g', '*.tokens', '*.policy']},
107 | include_package_data=True,
108 | install_requires=['antlr_python_runtime>=3.1.3'])
109 |
--------------------------------------------------------------------------------