86 Commits

Author SHA1 Message Date
Bartłomiej Pluta
aca227ac5e Create virtual environment with pipenv 2019-09-06 13:48:28 +02:00
Bartłomiej Pluta
7ec967a014 Merge branch 'add-polyphony' 2019-09-06 13:46:02 +02:00
Bartłomiej Pluta
aca6e6bb55 Create tools for compiling waves 2019-07-30 16:51:35 +02:00
Bartłomiej Pluta
a7de7f0279 Optimise time of generating overtones 2019-07-30 15:12:32 +02:00
Bartłomiej Pluta
7e55fe4c1a Add 'plot' function and remove deprecated 'percent' type 2019-07-30 13:59:18 +02:00
Bartłomiej Pluta
8abae7c2ff Add decay to synthetiser 2019-07-29 22:11:30 +02:00
Bartłomiej Pluta
07f08b0557 Add support for integers in passing overtones to synth 2019-07-29 17:59:38 +02:00
Bartłomiej Pluta
13b069dc7d Add support for non-quoted (identifier) map keys being used as string 2019-07-29 17:53:41 +02:00
Bartłomiej Pluta
73ea88d8d9 Overload synth function to accept notes without config object 2019-07-29 17:19:41 +02:00
Bartłomiej Pluta
a5875425fc Enable passing config map to synth function 2019-07-28 21:20:11 +02:00
Bartłomiej Pluta
0dcf5287e1 Add polyphony AND add overtones do synthesed tones 2019-07-28 19:48:39 +02:00
Bartłomiej Pluta
75dcacce67 Merge branch 'add-float-type' 2019-07-28 09:32:31 +02:00
Bartłomiej Pluta
70687ddc02 Fix relation operators between floats and integers 2019-07-27 12:52:30 +02:00
Bartłomiej Pluta
d802c58eee Add Float function to convert strings and integers to floats 2019-07-27 12:48:17 +02:00
Bartłomiej Pluta
c9a3fc070b Add Integer function to convert strings and floats to integers 2019-07-26 20:59:41 +02:00
Bartłomiej Pluta
b126f83824 Enable basic support for evaluating float types 2019-07-25 13:37:31 +02:00
Bartłomiej Pluta
6dc503ba86 Enable parsing float types 2019-07-25 13:07:53 +02:00
Bartłomiej Pluta
6222dccaac Improve float type tokenizer 2019-07-25 13:02:33 +02:00
Bartłomiej Pluta
0657214aa3 Create tokenizer for float type 2019-07-25 12:51:48 +02:00
Bartłomiej Pluta
3feec0839b Fix scope leakage after exit function 2019-07-16 23:59:34 +02:00
Bartłomiej Pluta
56ca69246d Merge branch 'add-filtering-clause-to-loop-operator' 2019-07-16 13:25:22 +02:00
Bartłomiej Pluta
5a2508e804 Move parenthesed expression to atom 2019-07-16 10:23:30 +02:00
Bartłomiej Pluta
ea28ab6235 Fix leaking scope of function to outer scope 2019-07-16 10:18:00 +02:00
Bartłomiej Pluta
6e9e252b86 Create 'read' function 2019-07-15 23:54:21 +02:00
Bartłomiej Pluta
17ef5be057 Remove unnecessary functions from module 2019-07-15 21:10:05 +02:00
Bartłomiej Pluta
44e63ed18d Add optional filtering expression to loop operator 2019-07-15 20:43:33 +02:00
Bartłomiej Pluta
83c7b92741 Merge branch 'optional-function-args' 2019-07-15 20:26:48 +02:00
Bartłomiej Pluta
79a7b8bb1d Add optional semicolon at the end of statements and move loop parser from factor to expression (change precedence) 2019-07-15 20:17:40 +02:00
Bartłomiej Pluta
2737139962 Clear main 2019-07-14 00:13:05 +02:00
Bartłomiej Pluta
c5435e66ff Enable checking matching optional arguments with declared types 2019-07-14 00:12:14 +02:00
Bartłomiej Pluta
460deb4981 Create evaluators for optional arguments in function and method definitions 2019-07-13 23:52:15 +02:00
Bartłomiej Pluta
69bac69946 Fix checking signature 2019-07-13 23:49:33 +02:00
Bartłomiej Pluta
6bd8046346 Enable parser to handle optional arguments 2019-07-13 23:08:35 +02:00
Bartłomiej Pluta
e70b5fa71a Add 'optional' matcher 2019-07-13 23:08:17 +02:00
Bartłomiej Pluta
44d234d36a Move semitones, transpose and interval functions to standard library 2019-07-13 22:07:49 +02:00
Bartłomiej Pluta
78ea26ea08 Add evaluators for logic operators 'and' and 'or' 2019-07-13 21:35:00 +02:00
Bartłomiej Pluta
b6983df2d3 Add 'source' to RuntimeException 2019-07-13 15:06:53 +02:00
Bartłomiej Pluta
86cf5d01f3 Add 'throw' statement 2019-07-13 14:48:58 +02:00
Bartłomiej Pluta
a07b226edb Remove 'synth' method and fix mapping string to note pitch 2019-07-13 13:17:59 +02:00
Bartłomiej Pluta
9ae9da089b Fix return statement 2019-07-13 10:32:16 +02:00
Bartłomiej Pluta
4f2058eaac Move some functions to standard library 2019-07-13 10:21:08 +02:00
Bartłomiej Pluta
a68f870037 Merge branch 'left-associative-ops' 2019-07-12 23:27:32 +02:00
Bartłomiej Pluta
526412068f Fix 'range' function 2019-07-12 23:25:52 +02:00
Bartłomiej Pluta
439765f442 Move 'flat' function definition to standard library 2019-07-12 23:12:09 +02:00
Bartłomiej Pluta
4c03ca2f86 Fix loop statement 2019-07-12 22:55:46 +02:00
Bartłomiej Pluta
033d864b0f Create evaluator for extend statement 2019-07-12 22:31:14 +02:00
Bartłomiej Pluta
41f385de09 Fix return statement 2019-07-12 22:25:26 +02:00
Bartłomiej Pluta
5512f808f8 Create evaluator for custom functions 2019-07-12 22:19:12 +02:00
Bartłomiej Pluta
cc569b5f19 Create import statement evaluator and update standard library 2019-07-12 22:08:37 +02:00
Bartłomiej Pluta
b31e17d176 Create if-else statement evaluator 2019-07-12 21:58:16 +02:00
Bartłomiej Pluta
95e6a5f95d Create evaluator for relation operators 2019-07-12 21:26:06 +02:00
Bartłomiej Pluta
99dd8bd46e Improve evaluator for sum (concatenating lists and maps) 2019-07-12 21:02:09 +02:00
Bartłomiej Pluta
dc3387c685 Create evaluator for sum (sum, subtraction and string concatenation) 2019-07-12 20:56:42 +02:00
Bartłomiej Pluta
1094c071fb Create evaluator for product (both multiplying and dividing) 2019-07-12 20:45:23 +02:00
Bartłomiej Pluta
13a6dedba6 Create working PoC of loop statement 2019-07-12 20:36:56 +02:00
Bartłomiej Pluta
94128d9f21 Create evaluator for identifier 2019-07-12 19:57:11 +02:00
Bartłomiej Pluta
65fccda989 Create evaluator for power operator 2019-07-12 19:54:29 +02:00
Bartłomiej Pluta
35eb38076f Create evaluator for not operator 2019-07-12 19:45:54 +02:00
Bartłomiej Pluta
a1273896e4 Create evaluator for access operator 2019-07-12 19:40:52 +02:00
Bartłomiej Pluta
94666aca79 Create evaluator for minus operator 2019-07-12 19:21:42 +02:00
Bartłomiej Pluta
6d1351e4a0 Create evaluators for lists and maps 2019-07-12 19:07:12 +02:00
Bartłomiej Pluta
1563045de1 Create evaluator for function call and basic atoms 2019-07-12 18:59:59 +02:00
Bartłomiej Pluta
4394c9a8db Add assertions to map and list items 2019-07-12 18:28:12 +02:00
Bartłomiej Pluta
2bf25da2fa Fix reporting syntax error when parsing only map 2019-07-12 17:55:24 +02:00
Bartłomiej Pluta
df4d737676 Fix oneOf() to reset input cursor after each parser execution 2019-07-12 17:44:12 +02:00
Bartłomiej Pluta
fe8dca4d2c Create asserts for expression and identifier 2019-07-12 17:20:38 +02:00
Bartłomiej Pluta
386c89502a Improve assertions on if/else statement 2019-07-12 17:14:40 +02:00
Bartłomiej Pluta
0c72203551 Create assertions on extend statements 2019-07-12 17:10:47 +02:00
Bartłomiej Pluta
0aad7e52dd Create assertions on import and function definition parsers 2019-07-12 17:00:44 +02:00
Bartłomiej Pluta
b711b6a582 Improve a little bit string tokenizer 2019-07-12 16:52:26 +02:00
Bartłomiej Pluta
c7e90b9fbd Add 'assertExpected' to Parser.oneOf() method 2019-07-12 16:49:25 +02:00
Bartłomiej Pluta
0435bc776e Rename Chain to Unit 2019-07-12 16:00:39 +02:00
Bartłomiej Pluta
916c8c69ef Move list and map parsers to atom 2019-07-12 15:11:36 +02:00
Bartłomiej Pluta
e43b0ad725 Move parentheses in factor before power operator ('**') 2019-07-12 14:51:12 +02:00
Bartłomiej Pluta
1a09a73c91 Improve integer parser 2019-07-12 14:30:49 +02:00
Bartłomiej Pluta
ac8b46b077 Improve 'extend' statement 2019-07-12 14:19:21 +02:00
Bartłomiej Pluta
b7192ea52b Add optional 'as' operator to loop 2019-07-12 14:12:55 +02:00
Bartłomiej Pluta
0cefcd282b Improve minus operator 2019-07-12 00:51:37 +02:00
Bartłomiej Pluta
28f32ea3d0 Perform cleaning code 2019-07-12 00:42:51 +02:00
Bartłomiej Pluta
e71bffcf5d Create 'return' statement 2019-07-12 00:36:26 +02:00
Bartłomiej Pluta
ee91dbec8a Add support for 'extend' statement 2019-07-12 00:28:33 +02:00
Bartłomiej Pluta
f459873574 Rename terminalParser() to terminal() 2019-07-12 00:18:53 +02:00
Bartłomiej Pluta
eb28976704 Enable support for custom functions definition 2019-07-12 00:15:28 +02:00
Bartłomiej Pluta
af3cb7027a Add TypeParser (handling types list - specifiers etc.) 2019-07-11 23:36:52 +02:00
Bartłomiej Pluta
261530eb10 Clean code 2019-07-11 19:51:47 +02:00
Bartłomiej Pluta
5a1d568e8e Refactor ExpressionParser 2019-07-11 19:43:18 +02:00
107 changed files with 2238 additions and 1184 deletions

3
.gitignore vendored
View File

@@ -1,3 +1,4 @@
__pycache__/ __pycache__/
*.mus *.mus
.idea/* .idea/*
venv/

15
Pipfile Normal file
View File

@@ -0,0 +1,15 @@
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
[packages]
sounddevice = "*"
soundfile = "*"
numpy = "*"
matplotlib = "*"
[requires]
python_version = "3.7"

180
Pipfile.lock generated Normal file
View File

@@ -0,0 +1,180 @@
{
"_meta": {
"hash": {
"sha256": "92b0c636f6ac9be9b32aa25b1cdc96dcbe85602d5c68a2084dc39d25392540b6"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.7"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"cffi": {
"hashes": [
"sha256:041c81822e9f84b1d9c401182e174996f0bae9991f33725d059b771744290774",
"sha256:046ef9a22f5d3eed06334d01b1e836977eeef500d9b78e9ef693f9380ad0b83d",
"sha256:066bc4c7895c91812eff46f4b1c285220947d4aa46fa0a2651ff85f2afae9c90",
"sha256:066c7ff148ae33040c01058662d6752fd73fbc8e64787229ea8498c7d7f4041b",
"sha256:2444d0c61f03dcd26dbf7600cf64354376ee579acad77aef459e34efcb438c63",
"sha256:300832850b8f7967e278870c5d51e3819b9aad8f0a2c8dbe39ab11f119237f45",
"sha256:34c77afe85b6b9e967bd8154e3855e847b70ca42043db6ad17f26899a3df1b25",
"sha256:46de5fa00f7ac09f020729148ff632819649b3e05a007d286242c4882f7b1dc3",
"sha256:4aa8ee7ba27c472d429b980c51e714a24f47ca296d53f4d7868075b175866f4b",
"sha256:4d0004eb4351e35ed950c14c11e734182591465a33e960a4ab5e8d4f04d72647",
"sha256:4e3d3f31a1e202b0f5a35ba3bc4eb41e2fc2b11c1eff38b362de710bcffb5016",
"sha256:50bec6d35e6b1aaeb17f7c4e2b9374ebf95a8975d57863546fa83e8d31bdb8c4",
"sha256:55cad9a6df1e2a1d62063f79d0881a414a906a6962bc160ac968cc03ed3efcfb",
"sha256:5662ad4e4e84f1eaa8efce5da695c5d2e229c563f9d5ce5b0113f71321bcf753",
"sha256:59b4dc008f98fc6ee2bb4fd7fc786a8d70000d058c2bbe2698275bc53a8d3fa7",
"sha256:73e1ffefe05e4ccd7bcea61af76f36077b914f92b76f95ccf00b0c1b9186f3f9",
"sha256:a1f0fd46eba2d71ce1589f7e50a9e2ffaeb739fb2c11e8192aa2b45d5f6cc41f",
"sha256:a2e85dc204556657661051ff4bab75a84e968669765c8a2cd425918699c3d0e8",
"sha256:a5457d47dfff24882a21492e5815f891c0ca35fefae8aa742c6c263dac16ef1f",
"sha256:a8dccd61d52a8dae4a825cdbb7735da530179fea472903eb871a5513b5abbfdc",
"sha256:ae61af521ed676cf16ae94f30fe202781a38d7178b6b4ab622e4eec8cefaff42",
"sha256:b012a5edb48288f77a63dba0840c92d0504aa215612da4541b7b42d849bc83a3",
"sha256:d2c5cfa536227f57f97c92ac30c8109688ace8fa4ac086d19d0af47d134e2909",
"sha256:d42b5796e20aacc9d15e66befb7a345454eef794fdb0737d1af593447c6c8f45",
"sha256:dee54f5d30d775f525894d67b1495625dd9322945e7fee00731952e0368ff42d",
"sha256:e070535507bd6aa07124258171be2ee8dfc19119c28ca94c9dfb7efd23564512",
"sha256:e1ff2748c84d97b065cc95429814cdba39bcbd77c9c85c89344b317dc0d9cbff",
"sha256:ed851c75d1e0e043cbf5ca9a8e1b13c4c90f3fbd863dacb01c0808e2b5204201"
],
"version": "==1.12.3"
},
"cycler": {
"hashes": [
"sha256:1d8a5ae1ff6c5cf9b93e8811e581232ad8920aeec647c37316ceac982b08cb2d",
"sha256:cd7b2d1018258d7247a71425e9f26463dfb444d411c39569972f4ce586b0c9d8"
],
"version": "==0.10.0"
},
"kiwisolver": {
"hashes": [
"sha256:05b5b061e09f60f56244adc885c4a7867da25ca387376b02c1efc29cc16bcd0f",
"sha256:26f4fbd6f5e1dabff70a9ba0d2c4bd30761086454aa30dddc5b52764ee4852b7",
"sha256:3b2378ad387f49cbb328205bda569b9f87288d6bc1bf4cd683c34523a2341efe",
"sha256:400599c0fe58d21522cae0e8b22318e09d9729451b17ee61ba8e1e7c0346565c",
"sha256:47b8cb81a7d18dbaf4fed6a61c3cecdb5adec7b4ac292bddb0d016d57e8507d5",
"sha256:53eaed412477c836e1b9522c19858a8557d6e595077830146182225613b11a75",
"sha256:58e626e1f7dfbb620d08d457325a4cdac65d1809680009f46bf41eaf74ad0187",
"sha256:5a52e1b006bfa5be04fe4debbcdd2688432a9af4b207a3f429c74ad625022641",
"sha256:5c7ca4e449ac9f99b3b9d4693debb1d6d237d1542dd6a56b3305fe8a9620f883",
"sha256:682e54f0ce8f45981878756d7203fd01e188cc6c8b2c5e2cf03675390b4534d5",
"sha256:79bfb2f0bd7cbf9ea256612c9523367e5ec51d7cd616ae20ca2c90f575d839a2",
"sha256:7f4dd50874177d2bb060d74769210f3bce1af87a8c7cf5b37d032ebf94f0aca3",
"sha256:8944a16020c07b682df861207b7e0efcd2f46c7488619cb55f65882279119389",
"sha256:8aa7009437640beb2768bfd06da049bad0df85f47ff18426261acecd1cf00897",
"sha256:939f36f21a8c571686eb491acfffa9c7f1ac345087281b412d63ea39ca14ec4a",
"sha256:9733b7f64bd9f807832d673355f79703f81f0b3e52bfce420fc00d8cb28c6a6c",
"sha256:a02f6c3e229d0b7220bd74600e9351e18bc0c361b05f29adae0d10599ae0e326",
"sha256:a0c0a9f06872330d0dd31b45607197caab3c22777600e88031bfe66799e70bb0",
"sha256:acc4df99308111585121db217681f1ce0eecb48d3a828a2f9bbf9773f4937e9e",
"sha256:b64916959e4ae0ac78af7c3e8cef4becee0c0e9694ad477b4c6b3a536de6a544",
"sha256:d3fcf0819dc3fea58be1fd1ca390851bdb719a549850e708ed858503ff25d995",
"sha256:d52e3b1868a4e8fd18b5cb15055c76820df514e26aa84cc02f593d99fef6707f",
"sha256:db1a5d3cc4ae943d674718d6c47d2d82488ddd94b93b9e12d24aabdbfe48caee",
"sha256:e3a21a720791712ed721c7b95d433e036134de6f18c77dbe96119eaf7aa08004",
"sha256:e8bf074363ce2babeb4764d94f8e65efd22e6a7c74860a4f05a6947afc020ff2",
"sha256:f16814a4a96dc04bf1da7d53ee8d5b1d6decfc1a92a63349bb15d37b6a263dd9",
"sha256:f2b22153870ca5cf2ab9c940d7bc38e8e9089fa0f7e5856ea195e1cf4ff43d5a",
"sha256:f790f8b3dff3d53453de6a7b7ddd173d2e020fb160baff578d578065b108a05f"
],
"version": "==1.1.0"
},
"matplotlib": {
"hashes": [
"sha256:1febd22afe1489b13c6749ea059d392c03261b2950d1d45c17e3aed812080c93",
"sha256:31a30d03f39528c79f3a592857be62a08595dec4ac034978ecd0f814fa0eec2d",
"sha256:4442ce720907f67a79d45de9ada47be81ce17e6c2f448b3c64765af93f6829c9",
"sha256:796edbd1182cbffa7e1e7a97f1e141f875a8501ba8dd834269ae3cd45a8c976f",
"sha256:934e6243df7165aad097572abf5b6003c77c9b6c480c3c4de6f2ef1b5fdd4ec0",
"sha256:bab9d848dbf1517bc58d1f486772e99919b19efef5dd8596d4b26f9f5ee08b6b",
"sha256:c1fe1e6cdaa53f11f088b7470c2056c0df7d80ee4858dadf6cbe433fcba4323b",
"sha256:e5b8aeca9276a3a988caebe9f08366ed519fff98f77c6df5b64d7603d0e42e36",
"sha256:ec6bd0a6a58df3628ff269978f4a4b924a0d371ad8ce1f8e2b635b99e482877a"
],
"index": "pypi",
"version": "==3.1.1"
},
"numpy": {
"hashes": [
"sha256:03f2ebcbffcce2dec8860633b89a93e80c6a239d21a77ae8b241450dc21e8c35",
"sha256:078c8025da5ab9e8657edc9c2a1e9642e06e953bc7baa2e65c1aa9d9dfb7e98b",
"sha256:0fbfa98c5d5c3c6489cc1e852ec94395d51f35d9ebe70c6850e47f465038cdf4",
"sha256:1c841033f4fe6801648180c3033c45b3235a8bbd09bc7249010f99ea27bb6790",
"sha256:2c0984a01ddd0aeec89f0ce46ef21d64761048cd76c0074d0658c91f9131f154",
"sha256:4c166dcb0fff7cb3c0bbc682dfb5061852a2547efb6222e043a7932828c08fb5",
"sha256:8c2d98d0623bd63fb883b65256c00454d5f53127a5a7bcdaa8bdc582814e8cb4",
"sha256:8cb4b6ae45aad6d26712a1ce0a3f2556c5e1484867f9649e03496e45d6a5eba4",
"sha256:93050e73c446c82065b7410221b07682e475ac51887cd9368227a5d944afae80",
"sha256:a3f6b3024f8826d8b1490e6e2a9b99e841cd2c375791b1df62991bd8f4c00b89",
"sha256:bede70fd8699695363f39e86c1e869b2c8b74fb5ef135a67b9e1eeebff50322a",
"sha256:c304b2221f33489cd15a915237a84cdfe9420d7e4d4828c78a0820f9d990395c",
"sha256:f11331530f0eff69a758d62c2461cd98cdc2eae0147279d8fc86e0464eb7e8ca",
"sha256:fa5f2a8ef1e07ba258dc07d4dd246de23ef4ab920ae0f3fa2a1cc5e90f0f1888",
"sha256:fb6178b0488b0ce6a54bc4accbdf5225e937383586555604155d64773f6beb2b",
"sha256:fd5e830d4dc31658d61a6452cd3e842213594d8c15578cdae6829e36ad9c0930"
],
"index": "pypi",
"version": "==1.17.1"
},
"pycparser": {
"hashes": [
"sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3"
],
"version": "==2.19"
},
"pyparsing": {
"hashes": [
"sha256:6f98a7b9397e206d78cc01df10131398f1c8b8510a2f4d97d9abd82e1aacdd80",
"sha256:d9338df12903bbf5d65a0e4e87c2161968b10d2e489652bb47001d82a9b028b4"
],
"version": "==2.4.2"
},
"python-dateutil": {
"hashes": [
"sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb",
"sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e"
],
"version": "==2.8.0"
},
"six": {
"hashes": [
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
],
"version": "==1.12.0"
},
"sounddevice": {
"hashes": [
"sha256:8d0571349f9a438a97f2c69da760f195cf5ddf2351072199cc1dfede4785a207",
"sha256:a0dda9dea9a5038ec0dafee4cdebd6d9a09086405d4833a7e3f6b18c109f89f3",
"sha256:a71cd3daabc8cb13e0ec9a29e7d4ba9b707386f3c81b3501b20f64209bcaece7",
"sha256:b038315e3497e01b1cb22630bc9b5025fcfad40e5ef7c125a9d565befa7220e0",
"sha256:c340d0a68945439571a13b287bd11b2d2884b61cd47cc5de8356d88221edf580"
],
"index": "pypi",
"version": "==0.3.13"
},
"soundfile": {
"hashes": [
"sha256:181393f8daac38bda997c4284b760bcd726dc0e847f620020d7bf2d3d538a985",
"sha256:3f90971bb1884575ada9a0731e878c91cc1ffde14a778f0857deaa2fd04849ac",
"sha256:637f6218c867b8cae80f6989634a0813b416b3e6132480d056e6e5a89a921571",
"sha256:7fe04272cbfb94ab648acb0db31ace0800f5a4cf46e9d45b4d71ddb15abce1a0",
"sha256:80a3d2ed37199ffa7066c097d7b82b8413922e22870b62b54dc264bf0169f4c2"
],
"index": "pypi",
"version": "==0.10.2"
}
},
"develop": {}
}

2
os_requirements.txt Normal file
View File

@@ -0,0 +1,2 @@
You have to have installed `PortAudio` in order to use app.

View File

@@ -27,6 +27,10 @@ class IntegerLiteral(Atom):
pass pass
class FloatLiteral(Atom):
pass
class StringLiteral(Atom): class StringLiteral(Atom):
pass pass
@@ -44,50 +48,61 @@ class TypeLiteral(Atom):
def IntegerParser(input): def IntegerParser(input):
return Parser.oneOf( return Parser.terminal(TokenType.INTEGER, createNode=IntegerLiteral.withValue)(input)
Parser.terminalParser(TokenType.INTEGER, lambda val, pos: IntegerLiteral.withValue(int(val), pos)),
Parser.allOf(
Parser.terminalParser(TokenType.MINUS), def FloatParser(input):
Parser.terminalParser(TokenType.INTEGER, lambda val, pos: IntegerLiteral.withValue(int(val), pos)), return Parser.terminal(TokenType.FLOAT, createNode=FloatLiteral.withValue)(input)
createNode=lambda minus, integer: IntegerLiteral.withValue(-integer.value, minus.pos),
name="negative integer"
)
)(input)
def StringParser(input): def StringParser(input):
return Parser.terminalParser(TokenType.STRING, createNode=StringLiteral.withValue)(input) return Parser.terminal(TokenType.STRING, createNode=StringLiteral.withValue)(input)
def NoteParser(input): def NoteParser(input):
return Parser.terminalParser(TokenType.NOTE, createNode=NoteLiteral.withValue)(input) return Parser.terminal(TokenType.NOTE, createNode=NoteLiteral.withValue)(input)
def BoolParser(input): def BoolParser(input):
return Parser.terminalParser(TokenType.BOOL, createNode=BoolLiteral.withValue)(input) return Parser.terminal(TokenType.BOOL, createNode=BoolLiteral.withValue)(input)
def TypeParser(input): def TypeLiteralParser(input):
return Parser.terminalParser(TokenType.TYPE, createNode=TypeLiteral.withValue)(input) return Parser.terminal(TokenType.TYPE, createNode=TypeLiteral.withValue)(input)
def LiteralParser(input): def LiteralParser(input):
return Parser.oneOf( return Parser.oneOf(
IntegerParser, IntegerParser,
FloatParser,
StringParser, StringParser,
NoteParser, NoteParser,
BoolParser, BoolParser,
TypeParser, TypeLiteralParser,
name="literal" name="literal"
)(input) )(input)
def AtomParser(input): def AtomParser(input):
from smnp.ast.node.identifier import IdentifierParser from smnp.ast.node.identifier import IdentifierParser
from smnp.ast.node.list import ListParser
from smnp.ast.node.map import MapParser
from smnp.ast.node.expression import ExpressionParser
parentheses = Parser.allOf(
Parser.terminal(TokenType.OPEN_PAREN),
Parser.doAssert(ExpressionParser, "expression"),
Parser.terminal(TokenType.CLOSE_PAREN),
createNode=lambda open, expr, close: expr,
name="grouping parentheses"
)
return Parser.oneOf( return Parser.oneOf(
parentheses,
LiteralParser, LiteralParser,
IdentifierParser, IdentifierParser,
ListParser,
MapParser,
name="atom" name="atom"
)(input) )(input)

View File

@@ -9,11 +9,9 @@ class Block(Node):
def BlockParser(input): def BlockParser(input):
parser = Parser.loop( return Parser.loop(
Parser.terminalParser(TokenType.OPEN_CURLY), Parser.terminal(TokenType.OPEN_CURLY),
Parser.doAssert(StatementParser, f"statement or '{TokenType.CLOSE_CURLY.key}'"), Parser.doAssert(StatementParser, f"statement or '{TokenType.CLOSE_CURLY.key}'"),
Parser.terminalParser(TokenType.CLOSE_CURLY), Parser.terminal(TokenType.CLOSE_CURLY),
createNode=lambda open, statements, close: Block.withChildren(statements, open.pos) createNode=lambda open, statements, close: Block.withChildren(statements, open.pos)
) )(input)
return Parser(parser, "block", [parser])(input)

View File

@@ -1,21 +0,0 @@
from smnp.ast.node.atom import AtomParser
from smnp.ast.node.list import ListParser
from smnp.ast.node.map import MapParser
from smnp.ast.node.operator import BinaryOperator
from smnp.ast.node.valuable import Valuable
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
class Chain(Valuable):
pass
itemParser = Parser.oneOf(
ListParser,
MapParser,
AtomParser,
)
ChainParser = Parser.leftAssociativeOperatorParser(itemParser, [TokenType.DOT], itemParser,
lambda left, op, right: Chain.withValue(BinaryOperator.withValues(left, op, right)))

View File

@@ -1,4 +1,4 @@
from smnp.ast.node.expression import MaxPrecedenceExpressionParser from smnp.ast.node.expression import ExpressionParser
from smnp.ast.node.model import Node from smnp.ast.node.model import Node
from smnp.ast.node.none import NoneNode from smnp.ast.node.none import NoneNode
from smnp.ast.node.statement import StatementParser from smnp.ast.node.statement import StatementParser
@@ -46,23 +46,23 @@ class IfElse(Node):
def IfElseStatementParser(input): def IfElseStatementParser(input):
ifStatementParser = Parser.allOf( ifStatementParser = Parser.allOf(
Parser.terminalParser(TokenType.IF), Parser.terminal(TokenType.IF),
Parser.terminalParser(TokenType.OPEN_PAREN), Parser.terminal(TokenType.OPEN_PAREN),
MaxPrecedenceExpressionParser, ExpressionParser,
Parser.terminalParser(TokenType.CLOSE_PAREN), Parser.terminal(TokenType.CLOSE_PAREN),
StatementParser, StatementParser,
createNode=lambda _, __, condition, ___, ifStatement: IfElse.createNode(ifStatement, condition), createNode=lambda _, __, condition, ___, ifStatement: IfElse.createNode(ifStatement, condition),
name="if statement" name="if statement"
) )
ifElseStatementParser = Parser.allOf( ifElseStatementParser = Parser.allOf(
Parser.terminalParser(TokenType.IF), Parser.terminal(TokenType.IF),
Parser.terminalParser(TokenType.OPEN_PAREN), Parser.terminal(TokenType.OPEN_PAREN, doAssert=True),
MaxPrecedenceExpressionParser, Parser.doAssert(ExpressionParser, "expression"),
Parser.terminalParser(TokenType.CLOSE_PAREN), Parser.terminal(TokenType.CLOSE_PAREN, doAssert=True),
StatementParser, Parser.doAssert(StatementParser, "statement"),
Parser.terminalParser(TokenType.ELSE), Parser.terminal(TokenType.ELSE),
StatementParser, Parser.doAssert(StatementParser, "statement"),
createNode=lambda _, __, condition, ___, ifStatement, ____, elseStatement: IfElse.createNode(ifStatement, condition, elseStatement), createNode=lambda _, __, condition, ___, ifStatement, ____, elseStatement: IfElse.createNode(ifStatement, condition, elseStatement),
name="if-else statement" name="if-else statement"
) )

View File

@@ -1,63 +1,128 @@
from smnp.ast.node.operator import BinaryOperator from smnp.ast.node.model import Node
from smnp.ast.node.none import NoneNode
from smnp.ast.node.operator import BinaryOperator, Operator
from smnp.ast.node.term import TermParser from smnp.ast.node.term import TermParser
from smnp.ast.node.valuable import Valuable
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
from smnp.token.type import TokenType from smnp.token.type import TokenType
class Expression(Valuable): class Sum(BinaryOperator):
pass pass
def ExpressionParser(input): class Relation(BinaryOperator):
return Parser.leftAssociativeOperatorParser( pass
class And(BinaryOperator):
pass
class Or(BinaryOperator):
pass
class Loop(BinaryOperator):
def __init__(self, pos):
super().__init__(pos)
self.children.extend([NoneNode(), NoneNode()])
@property
def parameters(self):
return self[3]
@parameters.setter
def parameters(self, value):
self[3] = value
@property
def filter(self):
return self[4]
@filter.setter
def filter(self, value):
self[4] = value
@classmethod
def loop(cls, left, parameters, operator, right, filter):
node = cls(left.pos)
node.left = left
node.parameters = parameters
node.operator = operator
node.right = right
node.filter = filter
return node
class LoopParameters(Node):
pass
def ExpressionWithoutLoopParser(input):
expr1 = Parser.leftAssociativeOperatorParser(
TermParser, TermParser,
[TokenType.PLUS, TokenType.MINUS], [TokenType.PLUS, TokenType.MINUS],
TermParser, TermParser,
lambda left, op, right: Expression.withValue(BinaryOperator.withValues(left, op, right)) lambda left, op, right: Sum.withValues(left, op, right)
)(input) )
expr2 = Parser.leftAssociativeOperatorParser(
expr1,
[TokenType.RELATION, TokenType.OPEN_ANGLE, TokenType.CLOSE_ANGLE],
expr1,
lambda left, op, right: Relation.withValues(left, op, right)
)
def Expression2Parser(input): expr3 = Parser.leftAssociativeOperatorParser(
return Parser.leftAssociativeOperatorParser( expr2,
ExpressionParser,
[TokenType.RELATION],
ExpressionParser,
lambda left, op, right: Expression.withValue(BinaryOperator.withValues(left, op, right))
)(input)
def Expression3Parser(input):
return Parser.leftAssociativeOperatorParser(
Expression2Parser,
[TokenType.AND], [TokenType.AND],
Expression2Parser, expr2,
lambda left, op, right: Expression.withValue(BinaryOperator.withValues(left, op, right)) lambda left, op, right: And.withValues(left, op, right)
)(input) )
return Parser.leftAssociativeOperatorParser(
def Expression4Parser(input): expr3,
from smnp.ast.node.condition import IfElse
exprParser = Parser.leftAssociativeOperatorParser(
Expression3Parser,
[TokenType.OR], [TokenType.OR],
Expression3Parser, expr3,
lambda left, op, right: Expression.withValue(BinaryOperator.withValues(left, op, right)) lambda left, op, right: Or.withValues(left, op, right)
)
ifElseExpression = Parser.allOf(
exprParser,
Parser.terminalParser(TokenType.IF),
Expression4Parser,
Parser.terminalParser(TokenType.ELSE),
Expression4Parser,
createNode=lambda ifNode, _, condition, __, elseNode: IfElse.createNode(ifNode, condition, elseNode)
)
return Parser.oneOf(
ifElseExpression,
exprParser,
)(input) )(input)
MaxPrecedenceExpressionParser = Expression4Parser def LoopParser(input):
from smnp.ast.node.identifier import IdentifierLiteralParser
from smnp.ast.node.iterable import abstractIterableParser
from smnp.ast.node.statement import StatementParser
loopParameters = Parser.allOf(
Parser.terminal(TokenType.AS),
Parser.oneOf(
Parser.wrap(IdentifierLiteralParser, lambda id: LoopParameters.withChildren([id], id.pos)),
abstractIterableParser(LoopParameters, TokenType.OPEN_PAREN, TokenType.CLOSE_PAREN, IdentifierLiteralParser)
),
createNode=lambda asKeyword, parameters: parameters,
name="loop parameters"
)
loopFilter = Parser.allOf(
Parser.terminal(TokenType.PERCENT),
Parser.doAssert(ExpressionWithoutLoopParser, "filter as bool expression"),
createNode=lambda percent, expr: expr,
name="loop filter"
)
return Parser.allOf(
ExpressionWithoutLoopParser,
Parser.optional(loopParameters),
Parser.terminal(TokenType.DASH, createNode=Operator.withValue),
StatementParser,
Parser.optional(loopFilter),
createNode=Loop.loop,
name="dash-loop"
)(input)
def ExpressionParser(input):
return Parser.oneOf(
LoopParser,
ExpressionWithoutLoopParser
)(input)

View File

@@ -1,70 +1,88 @@
# from smnp.ast.node.block import BlockNode from smnp.ast.node.block import Block
# from smnp.ast.node.function import FunctionDefinitionNode from smnp.ast.node.function import FunctionDefinitionParser
# from smnp.ast.node.identifier import IdentifierNode from smnp.ast.node.identifier import IdentifierLiteralParser
# from smnp.ast.node.none import NoneNode from smnp.ast.node.model import Node
# from smnp.ast.node.statement import StatementNode from smnp.ast.node.none import NoneNode
# from smnp.ast.node.type import TypeNode from smnp.ast.node.type import TypeParser
# from smnp.ast.parser import Parser from smnp.ast.parser import Parser
# from smnp.token.type import TokenType from smnp.token.type import TokenType
#
#
# class ExtendNode(StatementNode): class Extend(Node):
# def __init__(self, pos): def __init__(self, pos):
# super().__init__(pos) super().__init__(pos)
# self.children = [NoneNode(), NoneNode(), NoneNode()] self.children = [NoneNode(), NoneNode(), NoneNode()]
#
# @property @property
# def type(self): def type(self):
# return self[0] return self[0]
#
# @type.setter @type.setter
# def type(self, value): def type(self, value):
# self[0] = value self[0] = value
#
# @property @property
# def variable(self): def variable(self):
# return self[1] return self[1]
#
# @variable.setter @variable.setter
# def variable(self, value): def variable(self, value):
# self[1] = value self[1] = value
#
# @property @property
# def methods(self): def methods(self):
# return self[2] return self[2]
#
# @methods.setter @methods.setter
# def methods(self, value): def methods(self, value):
# self[2] = value self[2] = value
#
# @classmethod @classmethod
# def _parse(cls, input): def withValues(cls, pos, type, variable, methods):
# def createNode(extend, type, asKeyword, variable, methods): node = cls(pos)
# node = ExtendNode(extend.pos) node.type = type
# node.type = type node.variable = variable
# node.variable = variable node.methods = methods
# node.methods = methods return node
# return node
#
# return Parser.allOf( def ExtendParser(input):
# Parser.terminalParser(TokenType.EXTEND),
# Parser.doAssert(TypeNode.parse, "type being extended"), simpleExtend = Parser.allOf(
# Parser.terminalParser(TokenType.AS, doAssert=True), Parser.terminal(TokenType.EXTEND),
# Parser.doAssert(IdentifierNode.identifierParser(), "variable name"), TypeParser,
# Parser.doAssert(cls._methodsDeclarationsParser(), "methods declarations"), Parser.terminal(TokenType.AS),
# createNode=createNode IdentifierLiteralParser,
# )(input) Parser.terminal(TokenType.WITH),
# Parser.doAssert(Parser.wrap(FunctionDefinitionParser, lambda method: Block.withChildren([ method ], method.pos)), "method definition"),
# @classmethod createNode=lambda extend, type, _, variable, __, methods: Extend.withValues(extend.pos, type, variable, methods),
# def _methodsDeclarationsParser(cls): name="simple extend"
# def createNode(openBracket, items, closeBracket): )
# node = BlockNode(openBracket.pos)
# node.children = items multiExtend = Parser.allOf(
# return node Parser.terminal(TokenType.EXTEND),
# Parser.doAssert(TypeParser, "type being extended"),
# return Parser.loop( Parser.terminal(TokenType.AS, doAssert=True),
# Parser.terminalParser(TokenType.OPEN_CURLY), Parser.doAssert(IdentifierLiteralParser, "variable name"),
# Parser.doAssert(FunctionDefinitionNode.parse, f"method declaration or '{TokenType.CLOSE_CURLY.key}'"), Parser.doAssert(MethodsDeclarationParser, f"block with methods definitions or '{TokenType.WITH.key}' keyword"),
# Parser.terminalParser(TokenType.CLOSE_CURLY), createNode=lambda extend, type, _, variable, methods: Extend.withValues(extend.pos, type, variable, methods),
# createNode=createNode name="multiple extend"
# ) )
return Parser.oneOf(
simpleExtend,
multiExtend,
name="extend"
)(input)
def MethodsDeclarationParser(input):
return Parser.loop(
Parser.terminal(TokenType.OPEN_CURLY),
Parser.doAssert(FunctionDefinitionParser, f"method definition or '{TokenType.CLOSE_CURLY.key}'"),
Parser.terminal(TokenType.CLOSE_CURLY),
createNode=lambda open, methods, close: Block.withChildren(methods, open.pos),
name="methods block"
)(input)

View File

@@ -1,64 +1,34 @@
from smnp.ast.node.chain import ChainParser
from smnp.ast.node.operator import BinaryOperator, Operator, UnaryOperator from smnp.ast.node.operator import BinaryOperator, Operator, UnaryOperator
from smnp.ast.node.valuable import Valuable from smnp.ast.node.unit import UnitParser
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
from smnp.token.type import TokenType from smnp.token.type import TokenType
class Factor(Valuable):
pass
class NotOperator(UnaryOperator): class NotOperator(UnaryOperator):
pass pass
class Loop(BinaryOperator):
class Power(BinaryOperator):
pass pass
def FactorParser(input): def FactorParser(input):
from smnp.ast.node.expression import MaxPrecedenceExpressionParser
from smnp.ast.node.statement import StatementParser
powerFactor = Parser.leftAssociativeOperatorParser( powerFactor = Parser.leftAssociativeOperatorParser(
ChainParser, UnitParser,
[TokenType.DOUBLE_ASTERISK], [TokenType.DOUBLE_ASTERISK],
ChainParser, UnitParser,
lambda left, op, right: Factor.withValue(BinaryOperator.withValues(left, op, right)), lambda left, op, right: Power.withValues(left, op, right),
name="power operator" name="power operator"
) )
exprFactor = Parser.allOf(
Parser.terminalParser(TokenType.OPEN_PAREN),
MaxPrecedenceExpressionParser,
Parser.terminalParser(TokenType.CLOSE_PAREN),
createNode=lambda open, expr, close: expr,
name="grouping parentheses"
)
factorParser = Parser.oneOf(
powerFactor,
exprFactor,
name="basic factor"
)
notOperator = Parser.allOf( notOperator = Parser.allOf(
Parser.terminalParser(TokenType.NOT, Operator.withValue), Parser.terminal(TokenType.NOT, Operator.withValue),
factorParser, powerFactor,
createNode=NotOperator.withValues, createNode=NotOperator.withValues,
name="not" name="not"
) )
loopFactor = Parser.allOf(
factorParser,
Parser.terminalParser(TokenType.DASH, createNode=Operator.withValue),
StatementParser,
createNode=Loop.withValues,
name="dash-loop"
)
return Parser.oneOf( return Parser.oneOf(
loopFactor,
notOperator, notOperator,
factorParser powerFactor,
name="factor"
)(input) )(input)

View File

@@ -1,128 +1,165 @@
# from smnp.ast.node.block import BlockNode from smnp.ast.node.block import BlockParser
# from smnp.ast.node.expression import ExpressionNode from smnp.ast.node.expression import ExpressionParser
# from smnp.ast.node.identifier import IdentifierNode from smnp.ast.node.identifier import IdentifierLiteralParser
# from smnp.ast.node.iterable import abstractIterableParser from smnp.ast.node.iterable import abstractIterableParser
# from smnp.ast.node.model import Node from smnp.ast.node.model import Node
# from smnp.ast.node.none import NoneNode from smnp.ast.node.none import NoneNode
# from smnp.ast.node.statement import StatementNode from smnp.ast.node.type import TypeParser, Type
# from smnp.ast.node.type import TypeNode, TypeSpecifier from smnp.ast.parser import Parser
# from smnp.ast.parser import Parser from smnp.token.type import TokenType
# from smnp.token.type import TokenType
#
# class ArgumentsDeclaration(Node):
# class ArgumentsDeclarationNode(Node): pass
#
# @classmethod
# def _parse(cls, input): class Argument(Node):
# raise RuntimeError("This class is not supposed to be automatically called")
# def __init__(self, pos):
# super().__init__(pos)
# class VarargNode(Node): self.children = [NoneNode(), NoneNode(), False, NoneNode()]
# pass
# @property
# def type(self):
# class ArgumentDefinitionNode(ExpressionNode): return self[0]
# def __init__(self, pos):
# super().__init__(pos)
# self.children.extend([NoneNode(), False]) @type.setter
# def type(self, value):
# @property self[0] = value
# def type(self):
# return self[0]
# @property
# @type.setter def variable(self):
# def type(self, value): return self[1]
# self[0] = value
#
# @property @variable.setter
# def variable(self): def variable(self, value):
# return self[1] self[1] = value
#
# @variable.setter
# def variable(self, value): @property
# self[1] = value def vararg(self):
# return self[2]
# @property
# def vararg(self):
# return self[2] @vararg.setter
# def vararg(self, value):
# @vararg.setter self[2] = value
# def vararg(self, value):
# self[2] = value
# @property
# def optionalValue(self):
# @classmethod return self[3]
# def parser(cls):
# def createNode(type, variable, dots): @optionalValue.setter
# node = ArgumentDefinitionNode(type.pos) def optionalValue(self, value):
# node.type = type self[3] = value
# node.variable = variable
# node.vararg = isinstance(dots, VarargNode) class VarargNode(Node):
# return node pass
#
# return Parser.allOf(
# Parser.optional(Parser.oneOf( class FunctionDefinition(Node):
# TypeNode.parse, def __init__(self, pos):
# TypeSpecifier.parse super().__init__(pos)
# )), self.children = [NoneNode(), NoneNode(), NoneNode()]
# Parser.doAssert(IdentifierNode.identifierParser(), "variable name"),
# Parser.optional(Parser.terminalParser(TokenType.DOTS, lambda val, pos: VarargNode(pos))), @property
# createNode=createNode def name(self):
# ) return self[0]
#
# @classmethod @name.setter
# def _parse(cls, input): def name(self, value):
# #TODO self[0] = value
# raise RuntimeError("Not implemented yet. There is still required work to correctly build AST related to IdentifierNode")
# @property
# def arguments(self):
# return self[1]
# class FunctionDefinitionNode(StatementNode):
# def __init__(self, pos): @arguments.setter
# super().__init__(pos) def arguments(self, value):
# self.children = [NoneNode(), NoneNode(), NoneNode()] self[1] = value
#
# @property @property
# def name(self): def body(self):
# return self[0] return self[2]
#
# @name.setter @body.setter
# def name(self, value): def body(self, value):
# self[0] = value self[2] = value
#
# @property @classmethod
# def arguments(self): def withValues(cls, name, arguments, body):
# return self[1] node = cls(name.pos)
# node.name = name
# @arguments.setter node.arguments = arguments
# def arguments(self, value): node.body = body
# self[1] = value return node
#
# @property
# def body(self): def RegularArgumentParser(input):
# return self[2] def createNode(type, variable, vararg):
# pos = type.pos if isinstance(type, Type) else variable.pos
# @body.setter node = Argument(pos)
# def body(self, value): node.type = type
# self[2] = value node.variable = variable
# node.vararg = vararg is True
# @classmethod return node
# def _parse(cls, input):
# def createNode(function, name, arguments, body): return Parser.allOf(
# node = FunctionDefinitionNode(function.pos) Parser.optional(TypeParser),
# node.name = name Parser.doAssert(IdentifierLiteralParser, "argument name"),
# node.arguments = arguments Parser.optional(Parser.terminal(TokenType.DOTS, lambda val, pos: True)),
# node.body = body createNode=createNode,
# return node name="regular function argument"
# )(input)
# return Parser.allOf(
# Parser.terminalParser(TokenType.FUNCTION),
# Parser.doAssert(IdentifierNode.identifierParser(), "function name"), def OptionalArgumentParser(input):
# Parser.doAssert(cls._argumentsDeclarationParser(), "arguments list"), def createNode(type, variable, _, optional):
# Parser.doAssert(BlockNode.parse, "function body"), pos = type.pos if isinstance(type, Type) else variable.pos
# createNode=createNode node = Argument(pos)
# )(input) node.type = type
# node.variable = variable
# @staticmethod node.optionalValue = optional
# def _argumentsDeclarationParser(): return node
# return abstractIterableParser(ArgumentsDeclarationNode, TokenType.OPEN_PAREN, TokenType.CLOSE_PAREN, ArgumentDefinitionNode.parser())
return Parser.allOf(
Parser.optional(TypeParser),
Parser.doAssert(IdentifierLiteralParser, "argument name"),
Parser.terminal(TokenType.ASSIGN),
Parser.doAssert(ExpressionParser, "expression"),
createNode=createNode,
name="optional function argument"
)(input)
def ArgumentParser(input):
return Parser.oneOf(
OptionalArgumentParser,
RegularArgumentParser,
name="function argument"
)(input)
def ArgumentsDeclarationParser(input):
return abstractIterableParser(
ArgumentsDeclaration,
TokenType.OPEN_PAREN,
TokenType.CLOSE_PAREN,
Parser.doAssert(ArgumentParser, "function/method argument")
)(input)
def FunctionDefinitionParser(input):
return Parser.allOf(
Parser.terminal(TokenType.FUNCTION),
Parser.doAssert(IdentifierLiteralParser, "function/method name"),
Parser.doAssert(ArgumentsDeclarationParser, "function/method arguments"),
Parser.doAssert(BlockParser, "function/method body"),
createNode=lambda _, name, args, body: FunctionDefinition.withValues(name, args, body),
name="function definition"
)(input)

View File

@@ -1,5 +1,5 @@
from smnp.ast.node.atom import Atom from smnp.ast.node.atom import Atom
from smnp.ast.node.expression import MaxPrecedenceExpressionParser from smnp.ast.node.expression import ExpressionParser
from smnp.ast.node.iterable import abstractIterableParser from smnp.ast.node.iterable import abstractIterableParser
from smnp.ast.node.model import Node from smnp.ast.node.model import Node
from smnp.ast.node.none import NoneNode from smnp.ast.node.none import NoneNode
@@ -49,24 +49,26 @@ class Assignment(BinaryOperator):
pass pass
def IdentifierParser(input): def IdentifierLiteralParser(input):
identifierLiteralParser = Parser.terminalParser(TokenType.IDENTIFIER, createNode=Identifier.withValue) return Parser.terminal(TokenType.IDENTIFIER, createNode=Identifier.withValue)(input)
def IdentifierParser(input):
functionCallParser = Parser.allOf( functionCallParser = Parser.allOf(
identifierLiteralParser, IdentifierLiteralParser,
abstractIterableParser(ArgumentsList, TokenType.OPEN_PAREN, TokenType.CLOSE_PAREN, MaxPrecedenceExpressionParser), abstractIterableParser(ArgumentsList, TokenType.OPEN_PAREN, TokenType.CLOSE_PAREN, Parser.doAssert(ExpressionParser, "expression")),
createNode=lambda name, arguments: FunctionCall.withChildren(name, arguments) createNode=lambda name, arguments: FunctionCall.withChildren(name, arguments)
) )
assignmentParser = Parser.allOf( assignmentParser = Parser.allOf(
identifierLiteralParser, IdentifierLiteralParser,
Parser.terminalParser(TokenType.ASSIGN, createNode=Operator.withValue), Parser.terminal(TokenType.ASSIGN, createNode=Operator.withValue),
MaxPrecedenceExpressionParser, Parser.doAssert(ExpressionParser, "expression"),
createNode=lambda identifier, assign, expr: Assignment.withValues(identifier, assign, expr) createNode=lambda identifier, assign, expr: Assignment.withValues(identifier, assign, expr)
) )
return Parser.oneOf( return Parser.oneOf(
assignmentParser, assignmentParser,
functionCallParser, functionCallParser,
identifierLiteralParser IdentifierLiteralParser
)(input) )(input)

View File

@@ -1,11 +1,3 @@
# from smnp.ast.node.identifier import Identifier
# from smnp.ast.node.model import Node
# from smnp.ast.node.none import NoneNode
# from smnp.ast.node.string import StringLiteralNode
# from smnp.ast.node.type import TypeNode
# from smnp.ast.parser import Parser
# from smnp.token.type import TokenType
#
from smnp.ast.node.atom import StringParser from smnp.ast.node.atom import StringParser
from smnp.ast.node.model import Node from smnp.ast.node.model import Node
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
@@ -34,8 +26,8 @@ class Import(Node):
def ImportParser(input): def ImportParser(input):
return Parser.allOf( return Parser.allOf(
Parser.terminalParser(TokenType.IMPORT), Parser.terminal(TokenType.IMPORT),
StringParser, Parser.doAssert(StringParser, "import source as string"),
createNode=lambda imp, source: Import.withValue(source), createNode=lambda imp, source: Import.withValue(source),
name="import" name="import"
)(input) )(input)

View File

@@ -45,8 +45,8 @@ def abstractIterableParser(iterableNodeType, openTokenType, closeTokenType, item
return node return node
return Parser.allOf( return Parser.allOf(
Parser.terminalParser(openTokenType), Parser.terminal(openTokenType),
Parser.terminalParser(closeTokenType), Parser.terminal(closeTokenType),
createNode=createNode createNode=createNode
)(input) )(input)
@@ -58,7 +58,7 @@ def abstractIterableParser(iterableNodeType, openTokenType, closeTokenType, item
return node return node
return Parser.allOf( return Parser.allOf(
Parser.terminalParser(openTokenType), Parser.terminal(openTokenType),
itemParser, itemParser,
abstractIterableTailParser, abstractIterableTailParser,
createNode=createNode createNode=createNode
@@ -78,14 +78,14 @@ def abstractIterableParser(iterableNodeType, openTokenType, closeTokenType, item
return node return node
return Parser.allOf( return Parser.allOf(
Parser.terminalParser(TokenType.COMMA, doAssert=True), Parser.doAssert(Parser.terminal(TokenType.COMMA), f"'{TokenType.COMMA.key}' or '{closeTokenType.key}'"),
itemParser, itemParser,
abstractIterableTailParser, abstractIterableTailParser,
createNode=createNode createNode=createNode
)(input) )(input)
def closeIterable(input): def closeIterable(input):
return Parser.terminalParser(closeTokenType)(input) return Parser.terminal(closeTokenType)(input)
return toFlatDesiredNode(iterableNodeType, abstractIterableParser) return toFlatDesiredNode(iterableNodeType, abstractIterableParser)

View File

@@ -1,5 +1,6 @@
from smnp.ast.node.iterable import abstractIterableParser from smnp.ast.node.iterable import abstractIterableParser
from smnp.ast.node.model import Node from smnp.ast.node.model import Node
from smnp.ast.parser import Parser
from smnp.token.type import TokenType from smnp.token.type import TokenType
@@ -8,6 +9,11 @@ class List(Node):
def ListParser(input): def ListParser(input):
from smnp.ast.node.expression import MaxPrecedenceExpressionParser from smnp.ast.node.expression import ExpressionParser
return abstractIterableParser(List, TokenType.OPEN_SQUARE, TokenType.CLOSE_SQUARE,
MaxPrecedenceExpressionParser)(input) return abstractIterableParser(
List,
TokenType.OPEN_SQUARE,
TokenType.CLOSE_SQUARE,
Parser.doAssert(ExpressionParser, "expression")
)(input)

View File

@@ -1,4 +1,5 @@
from smnp.ast.node.atom import LiteralParser from smnp.ast.node.atom import LiteralParser
from smnp.ast.node.identifier import IdentifierLiteralParser
from smnp.ast.node.iterable import abstractIterableParser from smnp.ast.node.iterable import abstractIterableParser
from smnp.ast.node.model import Node from smnp.ast.node.model import Node
from smnp.ast.node.operator import BinaryOperator, Operator from smnp.ast.node.operator import BinaryOperator, Operator
@@ -30,17 +31,24 @@ class Map(Node):
def MapParser(input): def MapParser(input):
from smnp.ast.node.expression import MaxPrecedenceExpressionParser from smnp.ast.node.expression import ExpressionParser
keyParser = LiteralParser keyParser = Parser.oneOf(
valueParser = MaxPrecedenceExpressionParser LiteralParser,
IdentifierLiteralParser
)
valueParser = ExpressionParser
mapEntryParser = Parser.allOf( mapEntryParser = Parser.allOf(
keyParser, keyParser,
Parser.terminalParser(TokenType.ARROW, createNode=Operator.withValue), Parser.terminal(TokenType.ARROW, createNode=Operator.withValue),
valueParser, Parser.doAssert(valueParser, "expression"),
createNode=MapEntry.withValues createNode=MapEntry.withValues
) )
mapParser = abstractIterableParser(Map, TokenType.OPEN_CURLY, TokenType.CLOSE_CURLY, mapEntryParser) return abstractIterableParser(
Map,
TokenType.OPEN_CURLY,
TokenType.CLOSE_CURLY,
mapEntryParser
)(input)
return Parser(mapParser, "map", [mapParser])(input)

View File

@@ -28,21 +28,6 @@ class Node:
def pop(self, index): def pop(self, index):
return self.children.pop(index) return self.children.pop(index)
@classmethod
def _parse(cls, input):
pass
@classmethod
def parse(cls, input):
result = cls._parse(input)
if result is None:
return ParseResult.FAIL()
if not isinstance(result, ParseResult):
raise RuntimeError(f"_parse() method of '{cls.__name__}' class haven't returned ParseResult object")
return result
def print(self): def print(self):
self._print(first=True) self._print(first=True)

View File

@@ -68,68 +68,7 @@ class BinaryOperator(Node):
node.right = right node.right = right
return node return node
#
# class LeftAssociativeOperatorNode(ExpressionNode):
# def __init__(self, pos):
# super().__init__(pos)
# self.children = [NoneNode(), NoneNode(), NoneNode()]
#
# @property
# def left(self):
# return self[0]
#
# @left.setter
# def left(self, value):
# self[0] = value
#
# @property
# def operator(self):
# return self[1]
#
# @operator.setter
# def operator(self, value):
# self[1] = value
#
# @property
# def right(self):
# return self[2]
#
# @right.setter
# def right(self, value):
# self[2] = value
#
# @classmethod
# def _parse(cls, input):
# def createNode(left, operator, right):
# node = LeftAssociativeOperatorNode(right.pos)
# node.left = left
# node.operator = operator
# node.right = right
# return node
#
# return Parser.leftAssociativeOperatorParser(
# cls._lhsParser(),
# TokenType.DOT,
# cls._rhsParser(),
# createNode=createNode
# )(input)
#
# @classmethod
# def _lhsParser(cls):
# raise RuntimeError(f"LHS parser is not implemented in {cls.__name__}")
#
# @staticmethod
# def _rhsParser():
# from smnp.ast.node.identifier import IdentifierNode
#
# return Parser.oneOf(
# # TODO!!!
# IdentifierNode._lhsParser(),
# IdentifierNode._functionCallParser(),
# exception=lambda input: SyntaxException(f"Expected property name or method call, found '{input.current().rawValue}'", input.currentPos())
# )
#
#
class Operator(Node): class Operator(Node):
def __init__(self, pos): def __init__(self, pos):
super().__init__(pos) super().__init__(pos)

View File

@@ -1,44 +1,36 @@
from smnp.ast.node.extend import ExtendParser
from smnp.ast.node.function import FunctionDefinitionParser
from smnp.ast.node.imports import ImportParser from smnp.ast.node.imports import ImportParser
from smnp.ast.node.model import Node, ParseResult from smnp.ast.node.model import Node, ParseResult
from smnp.ast.node.statement import StatementParser from smnp.ast.node.statement import StatementParser
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
from smnp.error.syntax import SyntaxException
class Program(Node): class Program(Node):
def __init__(self): def __init__(self):
super().__init__((-1, -1)) super().__init__((-1, -1))
def parse(input): def ProgramParser(input):
root = Program() def parse(input):
while input.hasCurrent(): root = Program()
result = Parser.oneOf(
# Start Symbol # Start Symbol
startSymbolParser = Parser.oneOf(
ImportParser, ImportParser,
FunctionDefinitionParser,
ExtendParser,
StatementParser, StatementParser,
exception=RuntimeError("Nie znam tego wyrazenia") exception=lambda inp: SyntaxException(f"Invalid statement: {inp.currentToEndOfLine()}", inp.current().pos),
)(input) name="start symbol"
)
if result.result: while input.hasCurrent():
root.append(result.node) result = startSymbolParser(input)
return ParseResult.OK(root) if result.result:
root.append(result.node)
ProgramParser = Parser(parse, name="program") return ParseResult.OK(root)
# @classmethod
# def _parse(cls, input): return Parser(parse, name="program")(input)
# def parseToken(input):
# return Parser.oneOf(
# FunctionDefinitionNode.parse,
# ExtendNode.parse,
# ExpressionNode.parse,
# ImportNode.parse,
# StatementNode.parse,
# exception = SyntaxException(f"Invalid statement: {input.currentToEndOfLine()}", input.current().pos)
# )(input)
#
# root = Program()
# while input.hasCurrent():
# result = parseToken(input)
# if result.result:
# root.append(result.node)
# return ParseResult.OK(root)

View File

@@ -1,32 +1,17 @@
# from smnp.ast.node.expression import ExpressionNode from smnp.ast.node.expression import ExpressionParser
# from smnp.ast.node.none import NoneNode from smnp.ast.node.valuable import Valuable
# from smnp.ast.node.statement import StatementNode from smnp.ast.parser import Parser
# from smnp.ast.parser import Parser from smnp.token.type import TokenType
# from smnp.token.type import TokenType
#
# class Return(Valuable):
# class ReturnNode(StatementNode): pass
# def __init__(self, pos):
# super().__init__(pos)
# self.children.append(NoneNode()) def ReturnParser(input):
# return Parser.allOf(
# @property Parser.terminal(TokenType.RETURN),
# def value(self): Parser.optional(ExpressionParser),
# return self[0] createNode=lambda ret, val: Return.withValue(val, ret.pos),
# name="return"
# @value.setter )(input)
# def value(self, value):
# self[0] = value
#
# @classmethod
# def _parse(cls, input):
# def createNode(ret, value):
# node = ReturnNode(ret.pos)
# node.value = value
# return node
#
# return Parser.allOf(
# Parser.terminalParser(TokenType.RETURN),
# Parser.doAssert(ExpressionNode.parse, "expression"),
# createNode=createNode
# )(input)

View File

@@ -1,6 +1,6 @@
from smnp.ast.node.expression import MaxPrecedenceExpressionParser
from smnp.ast.node.model import Node from smnp.ast.node.model import Node
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
from smnp.token.type import TokenType
class Statement(Node): class Statement(Node):
@@ -10,25 +10,28 @@ class Statement(Node):
def StatementParser(input): def StatementParser(input):
from smnp.ast.node.block import BlockParser from smnp.ast.node.block import BlockParser
from smnp.ast.node.condition import IfElseStatementParser from smnp.ast.node.condition import IfElseStatementParser
from smnp.ast.node.expression import ExpressionParser
from smnp.ast.node.ret import ReturnParser
from smnp.ast.node.throw import ThrowParser
parser = Parser.oneOf( return withSemicolon(
IfElseStatementParser, Parser.oneOf(
BlockParser, IfElseStatementParser,
MaxPrecedenceExpressionParser ExpressionParser, # Must be above BlockParser because of Map's syntax with curly braces
BlockParser,
ReturnParser,
ThrowParser,
name="statement"
), optional=True)(input)
def withSemicolon(parser, optional=False, doAssert=False):
semicolonParser = Parser.optional(Parser.terminal(TokenType.SEMICOLON)) if optional else Parser.terminal(
TokenType.SEMICOLON, doAssert=doAssert)
return Parser.allOf(
parser,
semicolonParser,
createNode=lambda stmt, semicolon: stmt,
name="semicolon" + "?" if optional else ""
) )
return Parser(parser, "statement", parser)(input)
# class StatementNode(Node):
#
# @classmethod
# def _parse(cls, input):
# from smnp.ast.node.block import BlockNode
# from smnp.ast.node.expression import ExpressionNode
# from smnp.ast.node.ret import ReturnNode
#
# return Parser.oneOf(
# ExpressionNode.parse,
# BlockNode.parse,
# ReturnNode.parse,
# )(input)

View File

@@ -1,12 +1,17 @@
from smnp.ast.node.factor import FactorParser from smnp.ast.node.factor import FactorParser
from smnp.ast.node.operator import BinaryOperator from smnp.ast.node.operator import BinaryOperator
from smnp.ast.node.valuable import Valuable
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
from smnp.token.type import TokenType from smnp.token.type import TokenType
class Term(Valuable): class Product(BinaryOperator):
pass pass
TermParser = Parser.leftAssociativeOperatorParser(FactorParser, [TokenType.ASTERISK, TokenType.SLASH], FactorParser,
lambda left, op, right: Term.withValue(BinaryOperator.withValues(left, op, right))) def TermParser(input):
return Parser.leftAssociativeOperatorParser(
FactorParser,
[TokenType.ASTERISK, TokenType.SLASH],
FactorParser,
lambda left, op, right: Product.withValues(left, op, right)
)(input)

17
smnp/ast/node/throw.py Normal file
View File

@@ -0,0 +1,17 @@
from smnp.ast.node.expression import ExpressionParser
from smnp.ast.node.valuable import Valuable
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
class Throw(Valuable):
pass
def ThrowParser(input):
return Parser.allOf(
Parser.terminal(TokenType.THROW),
Parser.doAssert(ExpressionParser, "error message as string"),
createNode=lambda throw, message: Throw.withValue(message, throw.pos),
name="throw"
)(input)

View File

@@ -1,63 +1,67 @@
# from smnp.ast.node.iterable import abstractIterableParser from smnp.ast.node.atom import TypeLiteralParser
# from smnp.ast.node.model import Node from smnp.ast.node.iterable import abstractIterableParser
# from smnp.ast.node.operator import LeftAssociativeOperatorNode from smnp.ast.node.model import Node
# from smnp.ast.parser import Parser from smnp.ast.node.none import NoneNode
# from smnp.token.type import TokenType from smnp.ast.parser import Parser
# from smnp.type.model import Type from smnp.token.type import TokenType
#
#
# class TypeSpecifier(Node): class Type(Node):
# def __init__(self, pos):
# @classmethod super().__init__(pos)
# def _parse(cls, input): self.children = [NoneNode(), NoneNode()]
# return abstractIterableParser(TypeSpecifier, TokenType.OPEN_ANGLE, TokenType.CLOSE_ANGLE,
# Parser.doAssert(cls._specifierItem(), "type"))(input) @property
# def type(self):
# @classmethod return self[0]
# def _specifierItem(cls):
# return Parser.oneOf( @type.setter
# TypeNode.parse, def type(self, value):
# cls.parse self[0] = value
# )
# @property
# class TypeSpecifiers(Node): def specifiers(self):
# pass return self[1]
#
# @specifiers.setter
# class TypeNode(LeftAssociativeOperatorNode): def specifiers(self, value):
# def __init__(self, pos): self[1] = value
# super().__init__(pos)
# @classmethod
# @property def withValues(cls, pos, type, specifiers=NoneNode()):
# def type(self): node = cls(pos)
# return self[0] node.type = type
# node.specifiers = specifiers
# @type.setter return node
# def type(self, value):
# self[0] = value
# class TypesList(Node):
# @property pass
# def specifiers(self):
# return self[1]
# def TypesListParser(input):
# @specifiers.setter return abstractIterableParser(
# def specifiers(self, value): TypesList,
# self[1] = value TokenType.OPEN_ANGLE,
# TokenType.CLOSE_ANGLE,
# @classmethod TypeParser
# def _parse(cls, input): )(input)
# def createNode(type, specifiers):
# node = TypeNode(type.pos)
# node.type = Type[type.value.upper()] class TypeSpecifiers(Node):
# node.specifiers = specifiers pass
# return node
#
# return Parser.allOf( def TypeParser(input):
# cls._rawTypeParser(), typeWithSpecifier = Parser.allOf(
# Parser.many(TypeSpecifier.parse, lambda specifiers, pos: TypeSpecifiers.withChildren(specifiers, pos)), TypeLiteralParser,
# createNode=createNode Parser.many(TypesListParser, createNode=TypeSpecifiers.withChildren),
# )(input) createNode=lambda type, specifiers: Type.withValues(type.pos, type, specifiers),
# name="type with specifiers?"
# @classmethod )
# def _rawTypeParser(cls):
# return Parser.terminalParser(TokenType.TYPE, lambda val, pos: TypeNode.withValue(val, pos)) return Parser.oneOf(
typeWithSpecifier,
TypesListParser,
name="mult. types or type with specifier"
)(input)

36
smnp/ast/node/unit.py Normal file
View File

@@ -0,0 +1,36 @@
from smnp.ast.node.atom import AtomParser
from smnp.ast.node.operator import BinaryOperator, UnaryOperator, Operator
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
class MinusOperator(UnaryOperator):
pass
class Access(BinaryOperator):
pass
def UnitParser(input):
minusOperator = Parser.allOf(
Parser.terminal(TokenType.MINUS, createNode=Operator.withValue),
Parser.doAssert(AtomParser, "atom"),
createNode=MinusOperator.withValues,
name="minus"
)
atom2 = Parser.oneOf(
minusOperator,
AtomParser,
name="atom2"
)
return Parser.leftAssociativeOperatorParser(
atom2,
[TokenType.DOT],
Parser.doAssert(atom2, "atom"),
createNode=lambda left, op, right: Access.withValues(left, op, right),
name="unit"
)(input)

View File

@@ -16,7 +16,7 @@ class Valuable(Node):
self[0] = value self[0] = value
@classmethod @classmethod
def withValue(cls, value): def withValue(cls, value, pos=None):
node = cls(value.pos) node = cls(value.pos if pos is None else pos)
node.value = value node.value = value
return node return node

View File

@@ -41,7 +41,7 @@ class Parser:
# a -> A # a -> A
@staticmethod @staticmethod
def terminalParser(expectedType, createNode=None, doAssert=False): def terminal(expectedType, createNode=None, doAssert=False):
def provideNode(value, pos): def provideNode(value, pos):
if createNode is None: if createNode is None:
return IgnoredNode(pos) return IgnoredNode(pos)
@@ -62,13 +62,18 @@ class Parser:
# oneOf -> a | b | c | ... # oneOf -> a | b | c | ...
@staticmethod @staticmethod
def oneOf(*parsers, exception=None, name="or"): def oneOf(*parsers, assertExpected=None, exception=None, name="or"):
def combinedParser(input): def combinedParser(input):
snap = input.snapshot() snap = input.snapshot()
for parser in parsers: for parser in parsers:
value = parser(input) value = parser(input)
if value.result: if value.result:
return value return value
input.reset(snap)
if assertExpected is not None:
found = f", found '{input.current().rawValue}'" if input.hasCurrent() else ""
raise SyntaxException(f"Expected {assertExpected}{found}", input.currentPos())
if exception is not None: if exception is not None:
if callable(exception): if callable(exception):
@@ -76,7 +81,6 @@ class Parser:
else: else:
raise exception raise exception
input.reset(snap) input.reset(snap)
return ParseResult.FAIL() return ParseResult.FAIL()
@@ -140,7 +144,7 @@ class Parser:
@staticmethod @staticmethod
def oneOfTerminals(*tokenTypes, createNode=None): def oneOfTerminals(*tokenTypes, createNode=None):
return Parser.oneOf(*[ Parser.terminalParser(expectedType, createNode=createNode) for expectedType in tokenTypes ], name='|'.join([t.value for t in tokenTypes])) return Parser.oneOf(*[Parser.terminal(expectedType, createNode=createNode) for expectedType in tokenTypes], name='|'.join([t.value for t in tokenTypes]))
# loop -> start item* end # loop -> start item* end
@staticmethod @staticmethod
@@ -207,3 +211,14 @@ class Parser:
return ParseResult.OK(createNode(results, pos) if len(results) > 0 else NoneNode()) return ParseResult.OK(createNode(results, pos) if len(results) > 0 else NoneNode())
return Parser(parse, name, parsers=[parser]) return Parser(parse, name, parsers=[parser])
@staticmethod
def wrap(parser, createNode):
def parse(input):
result = parser(input)
if result.result:
return ParseResult.OK(createNode(result.node))
return result
return parse

View File

@@ -10,6 +10,9 @@ class Sound:
def play(self): def play(self):
sd.play(self.data, self.fs, blocking=True) sd.play(self.data, self.fs, blocking=True)
def __eq__(self, other):
return self.file == other.file and self.data == other.data
def __str__(self): def __str__(self):
return f"sound[{self.file}]" return f"sound[{self.file}]"

View File

@@ -1,17 +1,19 @@
from smnp.error.function import FunctionNotFoundException, MethodNotFoundException, IllegalFunctionInvocationException from smnp.error.function import FunctionNotFoundException, MethodNotFoundException, IllegalFunctionInvocationException
from smnp.error.runtime import RuntimeException from smnp.error.runtime import RuntimeException
from smnp.function.tools import argsTypesToString from smnp.function.tools import argsTypesToString
from smnp.runtime.evaluators.function import BodyEvaluator from smnp.runtime.evaluators.function import BodyEvaluator, Return
from smnp.type.model import Type
class Environment(): class Environment():
def __init__(self, scopes, functions, methods): def __init__(self, scopes, functions, methods, source):
self.scopes = scopes self.scopes = scopes
self.functions = functions self.functions = functions
self.methods = methods self.methods = methods
self.customFunctions = [] self.customFunctions = []
self.customMethods = [] self.customMethods = []
self.callStack = [] self.callStack = []
self.source = source
def invokeMethod(self, object, name, args): def invokeMethod(self, object, name, args):
builtinMethodResult = self._invokeBuiltinMethod(object, name, args) builtinMethodResult = self._invokeBuiltinMethod(object, name, args)
@@ -38,10 +40,15 @@ class Environment():
if method.typeSignature.check([object])[0] and method.name == name: #Todo sprawdzic sygnature typu if method.typeSignature.check([object])[0] and method.name == name: #Todo sprawdzic sygnature typu
signatureCheckresult = method.signature.check(args) signatureCheckresult = method.signature.check(args)
if signatureCheckresult[0]: if signatureCheckresult[0]:
self.scopes.append({argName: argValue for argName, argValue in zip(method.arguments, list(signatureCheckresult[1:]))}) self.scopes.append(method.defaultArgs)
self.scopes[-1].update({argName: argValue for argName, argValue in zip(method.arguments, list(signatureCheckresult[1:]))})
self.scopes[-1][method.alias] = object self.scopes[-1][method.alias] = object
self.callStack.append(CallStackItem(name)) self.callStack.append(CallStackItem(name))
result = BodyEvaluator.evaluate(method.body, self).value # TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult result = Type.void()
try:
BodyEvaluator.evaluate(method.body, self).value # TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
except Return as r:
result = r.value
self.callStack.pop(-1) self.callStack.pop(-1)
self.scopes.pop(-1) self.scopes.pop(-1)
return (True, result) return (True, result)
@@ -74,20 +81,27 @@ class Environment():
if function.name == name: if function.name == name:
signatureCheckresult = function.signature.check(args) signatureCheckresult = function.signature.check(args)
if signatureCheckresult[0]: if signatureCheckresult[0]:
self.scopes.append({ argName: argValue for argName, argValue in zip(function.arguments, list(signatureCheckresult[1:])) }) self.appendScope(function.defaultArgs)
appendedScopeIndex = len(self.scopes)-1
self.scopes[-1].update({ argName: argValue for argName, argValue in zip(function.arguments, list(signatureCheckresult[1:])) })
self.callStack.append(CallStackItem(name)) self.callStack.append(CallStackItem(name))
result = BodyEvaluator.evaluate(function.body, self).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult result = Type.void()
try:
BodyEvaluator.evaluate(function.body, self).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
except Return as r:
result = r.value
self.callStack.pop(-1) self.callStack.pop(-1)
self.scopes.pop(-1) self.popScope(mergeVariables=False)
self.removeScopesAfter(appendedScopeIndex)
return (True, result) return (True, result)
raise IllegalFunctionInvocationException(f"{function.name}{function.signature.string}", f"{name}{argsTypesToString(args)}") raise IllegalFunctionInvocationException(f"{function.name}{function.signature.string}", f"{name}{argsTypesToString(args)}")
return (False, None) return (False, None)
def addCustomFunction(self, name, signature, arguments, body): def addCustomFunction(self, name, signature, arguments, body, defaultArguments):
if len([fun for fun in self.functions + self.customFunctions if fun.name == name]) > 0: if len([fun for fun in self.functions + self.customFunctions if fun.name == name]) > 0:
raise RuntimeException(f"Cannot redeclare function '{name}'", None) raise RuntimeException(f"Cannot redeclare function '{name}'", None)
self.customFunctions.append(CustomFunction(name, signature, arguments, body)) self.customFunctions.append(CustomFunction(name, signature, arguments, body, defaultArguments))
# TODO: # TODO:
# There is still problem with checking existing of generic types, like lists: # There is still problem with checking existing of generic types, like lists:
@@ -98,14 +112,14 @@ class Environment():
# function foo() { return 2 } # function foo() { return 2 }
# } # }
# Then calling [1, 2, 3, 4].foo() will produce 1, when the second method is more suitable # Then calling [1, 2, 3, 4].foo() will produce 1, when the second method is more suitable
def addCustomMethod(self, typeSignature, alias, name, signature, arguments, body): def addCustomMethod(self, typeSignature, alias, name, signature, arguments, body, defaultArguments):
if len([m for m in self.methods if m.name == name and m.signature.matchers[0] == typeSignature.matchers[0]]) > 0: if len([m for m in self.methods if m.name == name and m.signature.matchers[0] == typeSignature.matchers[0]]) > 0:
raise RuntimeException(f"Cannot redeclare method '{name}' for type '{typeSignature.matchers[0]}'", None) raise RuntimeException(f"Cannot redeclare method '{name}' for type '{typeSignature.matchers[0]}'", None)
if len([m for m in self.customMethods if m.name == name and m.typeSignature.matchers[0] == typeSignature.matchers[0]]) > 0: if len([m for m in self.customMethods if m.name == name and m.typeSignature.matchers[0] == typeSignature.matchers[0]]) > 0:
raise RuntimeException(f"Cannot redeclare method '{name}' for type '{typeSignature.matchers[0]}'", None) raise RuntimeException(f"Cannot redeclare method '{name}' for type '{typeSignature.matchers[0]}'", None)
self.customMethods.append(CustomMethod(typeSignature, alias, name, signature, arguments, body)) self.customMethods.append(CustomMethod(typeSignature, alias, name, signature, arguments, body, defaultArguments))
def findVariable(self, name, type=None, pos=None): def findVariable(self, name, type=None, pos=None):
for scope in reversed(self.scopes): for scope in reversed(self.scopes):
@@ -128,6 +142,20 @@ class Environment():
else: else:
return scope return scope
def appendScope(self, variables=None):
if variables is None:
variables = {}
self.scopes.append(variables)
def popScope(self, mergeVariables=True):
lastScope = self.scopes.pop(-1)
if mergeVariables:
self.scopes[-1].update(lastScope)
def removeScopesAfter(self, index):
del self.scopes[index:]
def scopesToString(self): def scopesToString(self):
return "Scopes:\n" + ("\n".join([ f" [{i}]: {scope}" for i, scope in enumerate(self.scopes) ])) return "Scopes:\n" + ("\n".join([ f" [{i}]: {scope}" for i, scope in enumerate(self.scopes) ]))
@@ -162,22 +190,23 @@ class Environment():
class CallStackItem: class CallStackItem:
def __init__(self, function): def __init__(self, function):
self.function = function self.function = function
self.value = None
class CustomFunction: class CustomFunction:
def __init__(self, name, signature, arguments, body): def __init__(self, name, signature, arguments, body, defaultArgs):
self.name = name self.name = name
self.signature = signature self.signature = signature
self.arguments = arguments self.arguments = arguments
self.body = body self.body = body
self.defaultArgs = defaultArgs
class CustomMethod: class CustomMethod:
def __init__(self, typeSignature, alias, name, signature, arguments, body): def __init__(self, typeSignature, alias, name, signature, arguments, body, defaultArgs):
self.typeSignature = typeSignature self.typeSignature = typeSignature
self.alias = alias self.alias = alias
self.name = name self.name = name
self.signature = signature self.signature = signature
self.arguments = arguments self.arguments = arguments
self.body = body self.body = body
self.defaultArgs = defaultArgs

View File

@@ -1,7 +1,3 @@
from smnp.environment.environment import Environment
from smnp.module import functions, methods
def createEnvironment(): def createEnvironment():
return Environment([{}], functions, methods) return

View File

@@ -2,6 +2,7 @@ class SmnpException(Exception):
def __init__(self, msg, pos): def __init__(self, msg, pos):
self.msg = msg self.msg = msg
self.pos = pos self.pos = pos
self.file = None
def _title(self): def _title(self):
pass pass
@@ -10,7 +11,10 @@ class SmnpException(Exception):
return "" return ""
def _position(self): def _position(self):
return "" if self.pos is None else f" [line {self.pos[0]+1}, col {self.pos[1]+1}]" return "" if self.pos is None else f"[line {self.pos[0]+1}, col {self.pos[1]+1}]"
def _file(self):
return "" if self.file is None else f"File: {self.file}"
def message(self): def message(self):
return f"{self._title()}{self._position()}:\n{self.msg}\n{self._postMessage()}" return f"{self._title()}\n{self._file()} {self._position()}\n\n{self.msg}\n{self._postMessage()}"

12
smnp/error/custom.py Normal file
View File

@@ -0,0 +1,12 @@
from smnp.error.runtime import RuntimeException
class CustomException(RuntimeException):
def __init__(self, message, pos):
super().__init__(message, pos)
def _title(self):
return "Execution Error"
def _postMessage(self):
return "\n" + self.environment.callStackToString() if len(self.environment.callStack) > 0 else ""

View File

@@ -12,6 +12,9 @@ class Signature:
def varargSignature(varargMatcher, *basicSignature, wrapVarargInValue=False): def varargSignature(varargMatcher, *basicSignature, wrapVarargInValue=False):
def check(args): def check(args):
if any([ matcher.optional for matcher in [ varargMatcher, *basicSignature ]]):
raise RuntimeError("Vararg signature can't have optional arguments")
if len(basicSignature) > len(args): if len(basicSignature) > len(args):
return doesNotMatchVararg(basicSignature) return doesNotMatchVararg(basicSignature)
@@ -38,7 +41,7 @@ def doesNotMatchVararg(basicSignature):
def signature(*signature): def signature(*signature):
def check(args): def check(args):
if len(signature) != len(args): if len(args) > len(signature) or len(args) < len([ matcher for matcher in signature if not matcher.optional ]):
return doesNotMatch(signature) return doesNotMatch(signature)
for s, a in zip(signature, args): for s, a in zip(signature, args):
@@ -52,6 +55,12 @@ def signature(*signature):
return Signature(check, string, signature) return Signature(check, string, signature)
def optional(matcher):
matcher.optional = True
matcher.string += "?"
return matcher
def doesNotMatch(sign): def doesNotMatch(sign):
return (False, *[None for n in sign]) return (False, *[None for n in sign])

View File

@@ -5,7 +5,5 @@ from smnp.program.interpreter import Interpreter
def loadStandardLibrary(): def loadStandardLibrary():
mainSource = resource_string('smnp.library.code', 'main.mus').decode("utf-8") mainSource = resource_string('smnp.library.code', 'main.mus').decode("utf-8")
boolSource = resource_string('smnp.library.code', 'bool.mus').decode("utf-8") env = Interpreter.interpretString(mainSource, "<stdlib>")
env = Interpreter.interpretString(mainSource) return env
return Interpreter.interpretString(boolSource, baseEnvironment=env)

View File

@@ -1,14 +1,14 @@
import sys import sys
from smnp.error.base import SmnpException from smnp.error.base import SmnpException
from smnp.library.loader import loadStandardLibrary
from smnp.program.interpreter import Interpreter from smnp.program.interpreter import Interpreter
def main(): def main():
try: try:
#stdLibraryEnv = loadStandardLibrary() stdLibraryEnv = loadStandardLibrary()
Interpreter.interpretFile(sys.argv[1], printTokens=True, printAst=True, execute=False, baseEnvironment=None) Interpreter.interpretFile(sys.argv[1], printTokens=False, printAst=False, execute=True, baseEnvironment=stdLibraryEnv)
#draft()
except SmnpException as e: except SmnpException as e:
print(e.message()) print(e.message())

View File

@@ -1,4 +1,4 @@
from smnp.module import system, mic, note, iterable, sound, synth, string, util from smnp.module import system, mic, note, iterable, sound, synth, string, util, integer, float
functions = [ *system.functions, *mic.functions, *note.functions, *iterable.functions, *sound.functions, *synth.functions, *string.functions, *util.functions ] functions = [ *system.functions, *mic.functions, *note.functions, *iterable.functions, *sound.functions, *synth.functions, *string.functions, *util.functions, *integer.functions, *float.functions ]
methods = [ *system.methods, *mic.methods, *note.methods, *iterable.methods, *sound.methods, *synth.methods, *string.functions, *util.methods ] methods = [ *system.methods, *mic.methods, *note.methods, *iterable.methods, *sound.methods, *synth.methods, *string.methods, *util.methods, *integer.methods, *float.methods ]

View File

@@ -0,0 +1,4 @@
from smnp.module.float.function import float
functions = [ float.function ]
methods = []

View File

View File

@@ -0,0 +1,26 @@
from smnp.function.model import CombinedFunction, Function
from smnp.function.signature import signature
from smnp.type.model import Type
from smnp.type.signature.matcher.type import ofType
_signature1 = signature(ofType(Type.INTEGER))
def _function1(env, value):
return Type.float(float(value.value))
_signature2 = signature(ofType(Type.STRING))
def _function2(env, value):
return Type.float(float(value.value))
_signature3 = signature(ofType(Type.FLOAT))
def _function3(env, value):
return value
function = CombinedFunction(
'Float',
Function(_signature1, _function1),
Function(_signature2, _function2),
Function(_signature3, _function3),
)

View File

@@ -0,0 +1,4 @@
from smnp.module.integer.function import integer
functions = [ integer.function ]
methods = []

View File

View File

@@ -0,0 +1,25 @@
from smnp.function.model import CombinedFunction, Function
from smnp.function.signature import signature
from smnp.type.model import Type
from smnp.type.signature.matcher.type import ofType
_signature1 = signature(ofType(Type.FLOAT))
def _function1(env, value):
return Type.integer(int(value.value))
_signature2 = signature(ofType(Type.STRING))
def _function2(env, value):
return Type.integer(int(value.value))
_signature3 = signature(ofType(Type.INTEGER))
def _function3(env, value):
return value
function = CombinedFunction(
'Integer',
Function(_signature1, _function1),
Function(_signature2, _function2),
Function(_signature3, _function3),
)

View File

@@ -1,4 +1,4 @@
from smnp.module.iterable.function import combine, flat, map, range, get from smnp.module.iterable.function import map, get
functions = [ combine.function, flat.function, map.function, range.function ] functions = [ map.function ]
methods = [ get.function ] methods = [ get.function ]

View File

@@ -1,18 +0,0 @@
from functools import reduce
from smnp.function.model import Function
from smnp.function.signature import varargSignature
from smnp.type.model import Type
from smnp.type.signature.matcher.type import ofTypes
_signature = varargSignature(ofTypes(Type.LIST))
def _function(env, vararg):
if len(vararg) == 1:
return vararg[0]
combined = reduce(lambda x, y: x.value + y.value, vararg)
return Type.list(combined)
function = Function(_signature, _function, 'combine')

View File

@@ -1,23 +0,0 @@
from smnp.function.model import Function
from smnp.function.signature import varargSignature
from smnp.type.model import Type
from smnp.type.signature.matcher.type import allTypes
_signature = varargSignature(allTypes())
def _function(env, vararg):
return Type.list(doFlat(vararg, [])).decompose()
def doFlat(input, output=None):
if output is None:
output = []
for item in input:
if item.type == Type.LIST:
doFlat(item.value, output)
else:
output.append(item)
return output
function = Function(_signature, _function, 'flat')

View File

@@ -1,36 +0,0 @@
from smnp.function.model import CombinedFunction, Function
from smnp.function.signature import signature
from smnp.note.model import Note
from smnp.type.model import Type
from smnp.type.signature.matcher.type import ofType
_signature1 = signature(ofType(Type.INTEGER))
def _function1(env, upper):
return Type.list(list(range(upper.value + 1)))
_signature2 = signature(ofType(Type.INTEGER), ofType(Type.INTEGER))
def _function2(env, lower, upper):
return Type.list(list(range(lower.value, upper.value + 1)))
_signature3 = signature(ofType(Type.INTEGER), ofType(Type.INTEGER), ofType(Type.INTEGER))
def _function3(env, lower, upper, step):
return Type.list(list(range(lower.value, upper.value + 1, step.value)))
_signature4 = signature(ofType(Type.NOTE), ofType(Type.NOTE))
def _function4(env, lower, upper):
return Type.list([Type.note(n) for n in Note.range(lower.value, upper.value)])
# TODO
# signature5 = range(note lower, note upper, integer step) OR step = "diatonic" | "chromatic" | "augmented" | "diminish"
function = CombinedFunction(
'range',
Function(_signature1, _function1),
Function(_signature2, _function2),
Function(_signature3, _function3),
Function(_signature4, _function4),
)

View File

@@ -1,4 +1,4 @@
from smnp.module.note.function import tuplet, transpose, semitones, octave, duration, interval from smnp.module.note.function import note
functions = [ semitones.function, interval.function, transpose.function, tuplet.function ] functions = [ note.function ]
methods = [ duration.function, octave.function ] methods = []

View File

@@ -1,11 +0,0 @@
from smnp.function.model import Function
from smnp.function.signature import signature
from smnp.type.model import Type
from smnp.type.signature.matcher.type import ofType
_signature = signature(ofType(Type.NOTE), ofType(Type.INTEGER))
def _function(env, note, duration):
return Type.note(note.value.withDuration(duration.value))
function = Function(_signature, _function, 'withDuration')

View File

@@ -1,27 +0,0 @@
from smnp.function.model import Function, CombinedFunction
from smnp.function.signature import varargSignature
from smnp.note.interval import intervalToString
from smnp.note.model import Note
from smnp.type.model import Type
from smnp.type.signature.matcher.list import listOf
from smnp.type.signature.matcher.type import ofTypes
_signature1 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER))
def _function1(env, vararg):
withoutPauses = [note.value for note in vararg if note.type == Type.NOTE]
if len(withoutPauses) < 2:
return Type.list([])
semitones = [Note.checkInterval(withoutPauses[i-1], withoutPauses[i]) for i in range(1, len(withoutPauses))]
return Type.list([Type.string(intervalToString(s)) for s in semitones]).decompose()
_signature2 = varargSignature(listOf(Type.NOTE, Type.INTEGER))
def _function2(env, vararg):
return Type.list([_function1(env, arg.value) for arg in vararg]).decompose()
function = CombinedFunction(
'interval',
Function(_signature1, _function1),
Function(_signature2, _function2)
)

View File

@@ -0,0 +1,11 @@
from smnp.function.model import Function
from smnp.function.signature import signature
from smnp.note.model import Note
from smnp.type.model import Type
from smnp.type.signature.matcher.type import ofType
_signature = signature(ofType(Type.STRING), ofType(Type.INTEGER), ofType(Type.INTEGER), ofType(Type.BOOL))
def _function(env, note, octave, duration, dot):
return Type.note(Note(note.value, octave.value, duration.value, dot.value))
function = Function(_signature, _function, 'Note')

View File

@@ -1,11 +0,0 @@
from smnp.function.model import Function
from smnp.function.signature import signature
from smnp.type.model import Type
from smnp.type.signature.matcher.type import ofType
_signature = signature(ofType(Type.NOTE), ofType(Type.INTEGER))
def _function(env, note, octave):
return Type.note(note.value.withOctave(octave.value))
function = Function(_signature, _function, 'withOctave')

View File

@@ -1,25 +0,0 @@
from smnp.function.model import Function, CombinedFunction
from smnp.function.signature import varargSignature
from smnp.note.model import Note
from smnp.type.model import Type
from smnp.type.signature.matcher.list import listOf
from smnp.type.signature.matcher.type import ofTypes
_signature1 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER))
def _function1(env, vararg):
withoutPauses = [note.value for note in vararg if note.type == Type.NOTE]
if len(withoutPauses) < 2:
return Type.list([])
return Type.list([Type.integer(Note.checkInterval(withoutPauses[i-1], withoutPauses[i])) for i in range(1, len(withoutPauses))]).decompose()
_signature2 = varargSignature(listOf(Type.NOTE, Type.INTEGER))
def _function2(env, vararg):
return Type.list([_function1(env, arg.value) for arg in vararg]).decompose()
function = CombinedFunction(
"semitones",
Function(_signature1, _function1),
Function(_signature2, _function2),
)

View File

@@ -1,22 +0,0 @@
from smnp.function.model import CombinedFunction, Function
from smnp.function.signature import varargSignature
from smnp.type.model import Type
from smnp.type.signature.matcher.list import listOf
from smnp.type.signature.matcher.type import ofTypes
_signature1 = varargSignature(ofTypes(Type.INTEGER, Type.NOTE), ofTypes(Type.INTEGER))
def _function1(env, value, vararg):
transposed = [Type.note(arg.value.transpose(value.value)) if arg.type == Type.NOTE else arg for arg in vararg]
return Type.list(transposed).decompose()
_signature2 = varargSignature(listOf(Type.INTEGER, Type.NOTE), ofTypes(Type.INTEGER))
def _function2(env, value, vararg):
return Type.list([_function1(env, value, arg.value) for arg in vararg]).decompose()
function = CombinedFunction(
'transpose',
Function(_signature1, _function1),
Function(_signature2, _function2)
)

View File

@@ -1,23 +0,0 @@
from smnp.function.model import CombinedFunction, Function
from smnp.function.signature import signature, varargSignature
from smnp.type.model import Type
from smnp.type.signature.matcher.list import listOf
from smnp.type.signature.matcher.type import ofTypes
_signature1 = varargSignature(ofTypes(Type.NOTE), ofTypes(Type.INTEGER), ofTypes(Type.INTEGER))
def _function1(env, n, m, vararg):
t = [Type.note(arg.value.withDuration(int(arg.value.duration * n.value / m.value))) for arg in vararg]
return Type.list(t).decompose()
_signature2 = signature(ofTypes(Type.INTEGER), ofTypes(Type.INTEGER), listOf(Type.NOTE))
def _function2(env, n, m, notes):
return _function1(env, n, m, notes.value)
function = CombinedFunction(
'tuplet',
Function(_signature1, _function1),
Function(_signature2, _function2)
)

View File

@@ -1,4 +1,4 @@
from smnp.module.string.function import concat from smnp.module.string.function import stringify
functions = [ concat.function ] functions = []
methods = [] methods = [ stringify.function ]

View File

@@ -1,11 +0,0 @@
from smnp.function.model import Function
from smnp.function.signature import varargSignature
from smnp.type.model import Type
from smnp.type.signature.matcher.type import ofType
_signature = varargSignature(ofType(Type.STRING))
def _function(env, vararg):
return Type.string("".join([ arg.value for arg in vararg ]))
function = Function(_signature, _function, 'concat')

View File

@@ -0,0 +1,10 @@
from smnp.function.model import Function
from smnp.function.signature import signature
from smnp.type.model import Type
from smnp.type.signature.matcher.type import allTypes
_signature = signature(allTypes())
def _function(env, object):
return Type.string(object.stringify())
function = Function(_signature, _function, 'toString')

View File

@@ -1,4 +1,4 @@
from smnp.module.synth.function import synth, pause from smnp.module.synth.function import synth, pause, plot, compile
functions = [ synth.function, pause.function ] functions = [ synth.function, pause.function, plot.function, compile.function ]
methods = [] methods = []

View File

@@ -0,0 +1,141 @@
from smnp.error.runtime import RuntimeException
from smnp.function.model import Function, CombinedFunction
from smnp.function.signature import varargSignature
from smnp.module.synth.lib.wave import compilePolyphony
from smnp.type.model import Type
from smnp.type.signature.matcher.list import listOf
from smnp.type.signature.matcher.type import ofTypes, ofType
DEFAULT_BPM = 120
DEFAULT_OVERTONES = [0.4, 0.3, 0.1, 0.1, 0.1]
DEFAULT_DECAY = 4
DEFAULT_ATTACK = 100
def getBpm(config):
key = Type.string("bpm")
if key in config.value:
bpm = config.value[key]
if bpm.type != Type.INTEGER or bpm.value <= 0:
raise RuntimeException("The 'bpm' property must be positive integer", None)
return bpm.value
return DEFAULT_BPM
def getOvertones(config):
key = Type.string("overtones")
if key in config.value:
overtones = config.value[key]
rawOvertones = [overtone.value for overtone in overtones.value]
if overtones.type != Type.LIST or not all(overtone.type in [Type.FLOAT, Type.INTEGER] for overtone in overtones.value):
raise RuntimeException("The 'overtones' property must be list of floats", None)
if len(rawOvertones) < 1:
raise RuntimeException("The 'overtones' property must contain one overtone at least", None)
if any(overtone < 0 for overtone in rawOvertones):
raise RuntimeException("The 'overtones' property mustn't contain negative values", None)
if sum(rawOvertones) > 1.0:
raise RuntimeException("The 'overtones' property must contain overtones which sum is not greater than 1.0", None)
return rawOvertones
return DEFAULT_OVERTONES
def getDecay(config):
key = Type.string("decay")
if key in config.value:
decay = config.value[key]
if not decay.type in [Type.INTEGER, Type.FLOAT] or decay.value < 0:
raise RuntimeException("The 'decay' property must be non-negative integer or float", None)
return decay.value
return DEFAULT_DECAY
def getAttack(config):
key = Type.string("attack")
if key in config.value:
attack = config.value[key]
if not attack.type in [Type.INTEGER, Type.FLOAT] or attack.value < 0:
raise RuntimeException("The 'attack' property must be non-negative integer or float", None)
return attack.value
return DEFAULT_ATTACK
class Config:
def __init__(self, bpm, overtones, decay, attack):
self.bpm = bpm
self.overtones = overtones
self.decay = decay
self.attack = attack
@staticmethod
def default():
return Config(DEFAULT_BPM, DEFAULT_OVERTONES, DEFAULT_DECAY, DEFAULT_ATTACK)
_signature1 = varargSignature(listOf(Type.NOTE, Type.INTEGER))
def _function1(env, notes):
return Type.list([Type.float(float(m)) for m in __function1(notes)])
def __function1(notes):
return compilePolyphony([note.value for note in notes], Config.default())
_signature2 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER))
def _function2(env, notes):
return Type.list([Type.float(float(m)) for m in __function2(notes)])
def __function2(notes):
return compilePolyphony([ notes ], Config.default())
_signature3 = varargSignature(listOf(Type.NOTE, Type.INTEGER), ofType(Type.MAP))
def _function3(env, config, notes):
return Type.list([ Type.float(float(m)) for m in __function3(config, notes) ])
def __function3(config, notes):
rawNotes = [note.value for note in notes]
bpm = getBpm(config)
overtones = getOvertones(config)
decay = getDecay(config)
attack = getAttack(config)
return compilePolyphony(rawNotes, Config(bpm, overtones, decay, attack))
_signature4 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER), ofType(Type.MAP))
def _function4(env, config, notes):
return Type.list([ Type.float(float(m)) for m in __function4(config, notes) ])
def __function4(config, notes):
bpm = getBpm(config)
overtones = getOvertones(config)
decay = getDecay(config)
attack = getAttack(config)
return compilePolyphony([ notes ], Config(bpm, overtones, decay, attack))
function = CombinedFunction(
'wave',
Function(_signature1, _function1),
Function(_signature2, _function2),
Function(_signature3, _function3),
Function(_signature4, _function4),
)

View File

@@ -1,13 +1,13 @@
from smnp.function.model import Function from smnp.function.model import Function
from smnp.function.signature import signature from smnp.function.signature import signature
from smnp.module.synth.lib import player from smnp.module.synth.lib.wave import pause
from smnp.type.model import Type from smnp.type.model import Type
from smnp.type.signature.matcher.type import ofTypes from smnp.type.signature.matcher.type import ofTypes
_signature = signature(ofTypes(Type.INTEGER)) _signature = signature(ofTypes(Type.INTEGER))
def _function(env, value): def _function(env, value):
bpm = env.findVariable('bpm') bpm = env.findVariable('bpm')
player.pause(value.value, bpm.value) pause(value.value, bpm.value)
function = Function(_signature, _function, 'pause') function = Function(_signature, _function, 'pause')

View File

@@ -0,0 +1,13 @@
from smnp.function.model import Function
from smnp.function.signature import signature
from smnp.module.synth.lib.wave import plot
from smnp.type.model import Type
from smnp.type.signature.matcher.list import listOf
_signature = signature(listOf(Type.FLOAT))
def _function(env, wave):
rawWave = [ m.value for m in wave.value ]
plot(rawWave)
function = Function(_signature, _function, 'plotWave')

View File

@@ -1,25 +1,47 @@
from smnp.function.model import CombinedFunction, Function from smnp.function.model import Function, CombinedFunction
from smnp.function.signature import varargSignature from smnp.function.signature import varargSignature
from smnp.module.synth.lib.player import playNotes from smnp.module.synth.function import compile
from smnp.module.synth.lib.wave import play
from smnp.type.model import Type from smnp.type.model import Type
from smnp.type.signature.matcher.list import listOf from smnp.type.signature.matcher.list import listOf
from smnp.type.signature.matcher.type import ofTypes from smnp.type.signature.matcher.type import ofTypes, ofType
_signature1 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER)) _signature1 = varargSignature(listOf(Type.NOTE, Type.INTEGER))
def _function1(env, vararg): def _function1(env, notes):
notes = [arg.value for arg in vararg] wave = compile.__function1(notes)
bpm = env.findVariable('bpm') play(wave)
playNotes(notes, bpm.value)
_signature2 = varargSignature(listOf(Type.NOTE, Type.INTEGER)) _signature2 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER))
def _function2(env, vararg): def _function2(env, notes):
for arg in vararg: wave = compile.__function2(notes)
_function1(env, arg.value) play(wave)
_signature3 = varargSignature(listOf(Type.NOTE, Type.INTEGER), ofType(Type.MAP))
def _function3(env, config, notes):
wave = compile.__function3(config, notes)
play(wave)
_signature4 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER), ofType(Type.MAP))
def _function4(env, config, notes):
wave = compile.__function4(config, notes)
play(wave)
_signature5 = varargSignature(listOf(Type.FLOAT))
def _function5(env, waves):
for wave in waves:
rawWave = [m.value for m in wave.value]
play(rawWave)
function = CombinedFunction( function = CombinedFunction(
'synth', 'synth',
Function(_signature1, _function1), Function(_signature1, _function1),
Function(_signature2, _function2) Function(_signature2, _function2),
) Function(_signature3, _function3),
Function(_signature4, _function4),
Function(_signature5, _function5)
)

View File

@@ -1,24 +0,0 @@
import time
from smnp.module.synth.lib.wave import sine
from smnp.note.model import Note
def playNotes(notes, bpm):
for note in notes:
{
Note: play,
int: pause
}[type(note)](note, bpm)
def play(note, bpm):
frequency = note.toFrequency()
duration = 60 * 4 / note.duration / bpm
duration *= 1.5 if note.dot else 1
sine(frequency, duration)
def pause(value, bpm):
time.sleep(60 * 4 / value / bpm)

View File

@@ -1,12 +1,78 @@
import time import time
import matplotlib.pyplot as plt
import numpy as np import numpy as np
import sounddevice as sd import sounddevice as sd
from smnp.type.model import Type
FS = 44100 FS = 44100
def pause(value, bpm):
time.sleep(60 * 4 / value / bpm)
def plot(wave):
X = np.arange(len(wave))
plt.plot(X, wave)
plt.show()
def play(wave):
sd.play(wave)
time.sleep(len(wave) / FS)
def compilePolyphony(notes, config):
compiledLines = [1 / len(notes) * compileNotes(line, config) for line in notes]
return sum(adjustSize(compiledLines))
def adjustSize(compiledLines):
maxSize = max(len(line) for line in compiledLines)
return [np.concatenate([line, np.zeros(maxSize - len(line))]) for line in compiledLines]
def compileNotes(notes, config):
dispatcher = {
Type.NOTE: lambda note, overtones: sineForNote(note.value, config),
Type.INTEGER: lambda note, overtones: silenceForPause(note.value, config)
}
return np.concatenate([dispatcher[note.type](note, config) for note in notes])
def sineForNote(note, config):
frequency = note.toFrequency()
duration = 60 * 4 / note.duration / config.bpm
duration *= 1.5 if note.dot else 1
return sound(frequency, duration, config)
def sound(frequency, duration, config):
return attack(decay(sum(overtone * sine((i+1) * frequency, duration) for i, overtone in enumerate(config.overtones) if overtone > 0), config), config)
def decay(wave, config):
magnitude = np.exp(-config.decay/len(wave) * np.arange(len(wave)))
return magnitude * wave
def attack(wave, config):
magnitude = -np.exp(-config.attack / len(wave) * np.arange(len(wave)))+1 \
if config.attack > 0 \
else np.ones(len(wave))
return magnitude * wave
def sine(frequency, duration): def sine(frequency, duration):
samples = (np.sin(2*np.pi*np.arange(FS*duration)*frequency/FS)).astype(np.float32) return (np.sin(2 * np.pi * np.arange(FS * duration) * frequency / FS)).astype(np.float32)
sd.play(samples, FS)
time.sleep(duration)
def silenceForPause(value, config):
duration = 60 * 4 / value / config.bpm
return np.zeros(int(FS * duration))

View File

@@ -1,4 +1,4 @@
from smnp.module.system.function import sleep, display, debug, exit, type from smnp.module.system.function import sleep, display, displayln, debug, exit, type, read
functions = [ debug.function, display.function, exit.function, sleep.function, type.function ] functions = [ debug.function, display.function, displayln.function, exit.function, sleep.function, type.function, read.function ]
methods = [] methods = []

View File

@@ -4,7 +4,7 @@ from smnp.type.signature.matcher.type import allTypes
_signature = varargSignature(allTypes()) _signature = varargSignature(allTypes())
def _function(env, vararg): def _function(env, vararg):
print("".join([arg.stringify() for arg in vararg])) print("".join([arg.stringify() for arg in vararg]), end="")
function = Function(_signature, _function, 'print') function = Function(_signature, _function, 'print')

View File

@@ -0,0 +1,10 @@
from smnp.function.model import Function
from smnp.function.signature import varargSignature
from smnp.type.signature.matcher.type import allTypes
_signature = varargSignature(allTypes())
def _function(env, vararg):
print("".join([arg.stringify() for arg in vararg]))
function = Function(_signature, _function, 'println')

View File

@@ -1,3 +1,72 @@
from smnp.error.runtime import RuntimeException
from smnp.function.model import CombinedFunction, Function
from smnp.function.signature import signature
from smnp.token.tokenizers.bool import boolTokenizer
from smnp.token.tokenizers.note import noteTokenizer
from smnp.type.model import Type
from smnp.type.signature.matcher.type import ofType
_signature1 = signature()
def _function1(env):
value = input()
return Type.string(value)
_signature2 = signature(ofType(Type.STRING))
def _function2(env, prompt):
print(prompt.value, end="")
value = input()
return Type.string(value)
_signature3 = signature(ofType(Type.TYPE))
def _function3(env, type):
value = input()
return getValueAccordingToType(value, type)
def getValueAccordingToType(value, type):
try:
if type.value == Type.STRING:
return Type.string(value)
if type.value == Type.INTEGER:
return Type.integer(int(value))
if type.value == Type.BOOL:
consumedChars, token = boolTokenizer(value, 0, 0)
if consumedChars > 0:
return Type.bool(token.value)
return ValueError()
if type.value == Type.NOTE:
consumedChars, token = noteTokenizer(value, 0, 0)
if consumedChars > 0:
return Type.note(token.value)
raise ValueError()
raise RuntimeException(f"Type {type.value.name.lower()} is not suuported", None)
except ValueError:
raise RuntimeException(f"Invalid value '{value}' for type {type.value.name.lower()}", None)
_signature4 = signature(ofType(Type.STRING), ofType(Type.TYPE))
def _function4(env, prompt, type):
print(prompt.value, end="")
value = input()
return getValueAccordingToType(value, type)
function = CombinedFunction(
'read',
Function(_signature1, _function1),
Function(_signature2, _function2),
Function(_signature3, _function3),
Function(_signature4, _function4)
)
# TODO read function # TODO read function
# def read(args, env): # def read(args, env):

View File

@@ -1,29 +1,12 @@
import random as r import random
from smnp.error.function import IllegalArgumentException from smnp.function.model import Function
from smnp.function.model import Function, CombinedFunction from smnp.function.signature import signature
from smnp.function.signature import varargSignature
from smnp.type.model import Type from smnp.type.model import Type
from smnp.type.signature.matcher.list import listMatches from smnp.type.signature.matcher.type import ofType
from smnp.type.signature.matcher.type import ofTypes
_signature = signature(ofType(Type.INTEGER), ofType(Type.INTEGER))
def _function(env, min, max):
return Type.integer(random.randint(min.value, max.value))
def forType(t): function = Function(_signature, _function, 'rand')
_signature = varargSignature(listMatches(ofTypes(Type.PERCENT), ofTypes(t)))
def _function(env, vararg):
choice = r.random()
acc = 0
if sum(arg.value[0].value for arg in vararg) != 1.0:
raise IllegalArgumentException("Sum of all percentage values must be equal 100%")
for arg in vararg:
percent, item = arg.value
acc += percent.value
if choice <= acc:
return item
return Function(_signature, _function)
function = CombinedFunction('random', *[ forType(t) for t in Type if t != Type.VOID ])
#TODO: sample

View File

@@ -16,7 +16,7 @@ class NotePitch(Enum):
A = 9 A = 9
AIS = 10 AIS = 10
H = 11 H = 11
def toFrequency(self): def toFrequency(self):
return { return {
NotePitch.C: 16.35, NotePitch.C: 16.35,
@@ -32,19 +32,23 @@ class NotePitch(Enum):
NotePitch.AIS: 29.17, NotePitch.AIS: 29.17,
NotePitch.H: 30.87 NotePitch.H: 30.87
}[self] }[self]
def __str__(self): def __str__(self):
return self.name return self.name
def __repr__(self): def __repr__(self):
return self.__str__() return self.__str__()
@staticmethod @staticmethod
def toPitch(string): def toPitch(string):
try: if string.lower() in stringToPitch:
return stringToPitch[string.lower()] return stringToPitch[string.lower()]
except KeyError as e:
raise NoteException(f"Note '{string}' does not exist") if string.upper() in NotePitch.__members__:
return NotePitch[string.upper()]
raise NoteException(f"Note '{string}' does not exist")
stringToPitch = { stringToPitch = {
'cb': NotePitch.H, 'cb': NotePitch.H,
@@ -67,4 +71,4 @@ stringToPitch = {
'a#': NotePitch.AIS, 'a#': NotePitch.AIS,
'b': NotePitch.AIS, 'b': NotePitch.AIS,
'h': NotePitch.H 'h': NotePitch.H
} }

View File

@@ -1,25 +1,42 @@
from smnp.ast.parser import parse from smnp.ast.parser import parse
#from smnp.environment.factory import createEnvironment from smnp.environment.environment import Environment
from smnp.error.runtime import RuntimeException from smnp.error.runtime import RuntimeException
from smnp.module import functions, methods
from smnp.program.FileReader import readLines from smnp.program.FileReader import readLines
from smnp.runtime.evaluator import evaluate
from smnp.token.tokenizer import tokenize from smnp.token.tokenizer import tokenize
class Interpreter: class Interpreter:
@staticmethod @staticmethod
def interpretString(string, printTokens=False, printAst=False, execute=True, baseEnvironment=None): def interpretString(string, source, printTokens=False, printAst=False, execute=True, baseEnvironment=None):
return Interpreter._interpret(string.splitlines(), printTokens, printAst, execute, baseEnvironment) return Interpreter._interpret(
string.splitlines(),
source,
printTokens,
printAst,
execute,
baseEnvironment,
)
@staticmethod @staticmethod
def interpretFile(file, printTokens=False, printAst=False, execute=True, baseEnvironment=None): def interpretFile(file, printTokens=False, printAst=False, execute=True, baseEnvironment=None, source=None):
return Interpreter._interpret(readLines(file), printTokens, printAst, execute, baseEnvironment) return Interpreter._interpret(
readLines(file),
source if source is not None else file,
printTokens,
printAst,
execute,
baseEnvironment,
)
@staticmethod @staticmethod
def _interpret(lines, printTokens=False, printAst=False, execute=True, baseEnvironment=None): def _interpret(lines, source, printTokens=False, printAst=False, execute=True, baseEnvironment=None):
#environment = createEnvironment() environment = Environment([{}], functions, methods, source=source)
#if baseEnvironment is not None:
# environment.extend(baseEnvironment) if baseEnvironment is not None:
environment.extend(baseEnvironment)
try: try:
tokens = tokenize(lines) tokens = tokenize(lines)
@@ -30,10 +47,11 @@ class Interpreter:
if printAst: if printAst:
ast.print() ast.print()
#if execute: if execute:
# evaluate(ast, environment) evaluate(ast, environment)
#return environment return environment
except RuntimeException as e: except RuntimeException as e:
#e.environment = environment e.environment = environment
e.file = environment.source
raise e raise e

View File

@@ -1,9 +1,11 @@
from smnp.ast.node.block import BlockNode from smnp.ast.node.block import Block
from smnp.ast.node.extend import ExtendNode from smnp.ast.node.condition import IfElse
from smnp.ast.node.function import FunctionDefinitionNode from smnp.ast.node.extend import Extend
from smnp.ast.node.imports import ImportNode from smnp.ast.node.function import FunctionDefinition
from smnp.ast.node.imports import Import
from smnp.ast.node.program import Program from smnp.ast.node.program import Program
from smnp.ast.node.ret import ReturnNode from smnp.ast.node.ret import Return
from smnp.ast.node.throw import Throw
from smnp.error.runtime import RuntimeException from smnp.error.runtime import RuntimeException
from smnp.type.model import Type from smnp.type.model import Type
@@ -69,19 +71,28 @@ class EvaluationResult():
def evaluate(node, environment): def evaluate(node, environment):
from smnp.runtime.evaluators.program import ProgramEvaluator from smnp.runtime.evaluators.program import ProgramEvaluator
from smnp.runtime.evaluators.expression import expressionEvaluator from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.runtime.evaluators.condition import IfElseStatementEvaluator
from smnp.runtime.evaluators.function import FunctionDefinitionEvaluator
from smnp.runtime.evaluators.extend import ExtendEvaluator
from smnp.runtime.evaluators.block import BlockEvaluator from smnp.runtime.evaluators.block import BlockEvaluator
from smnp.runtime.evaluators.imports import ImportEvaluator from smnp.runtime.evaluators.imports import ImportEvaluator
from smnp.runtime.evaluators.function import FunctionDefinitionEvaluator
from smnp.runtime.evaluators.function import ReturnEvaluator from smnp.runtime.evaluators.function import ReturnEvaluator
from smnp.runtime.evaluators.extend import ExtendEvaluator
from smnp.runtime.evaluators.throw import ThrowEvaluator
result = Evaluator.oneOf( result = Evaluator.oneOf(
Evaluator.forNodes(ProgramEvaluator.evaluate, Program), Evaluator.forNodes(ProgramEvaluator.evaluate, Program),
Evaluator.forNodes(ImportEvaluator.evaluate, ImportNode), Evaluator.forNodes(IfElseStatementEvaluator.evaluate, IfElse),
Evaluator.forNodes(FunctionDefinitionEvaluator.evaluate, FunctionDefinitionNode), Evaluator.forNodes(BlockEvaluator.evaluate, Block),
Evaluator.forNodes(ExtendEvaluator.evaluate, ExtendNode), Evaluator.forNodes(ImportEvaluator.evaluate, Import),
Evaluator.forNodes(BlockEvaluator.evaluate, BlockNode), Evaluator.forNodes(FunctionDefinitionEvaluator.evaluate, FunctionDefinition),
Evaluator.forNodes(ReturnEvaluator.evaluate, ReturnNode), Evaluator.forNodes(ReturnEvaluator.evaluate, Return),
Evaluator.forNodes(ExtendEvaluator.evaluate, Extend),
Evaluator.forNodes(ThrowEvaluator.evaluate, Throw),
#Evaluator.forNodes(ImportEvaluator.evaluate, ImportNode),
#Evaluator.forNodes(FunctionDefinitionEvaluator.evaluate, FunctionDefinitionNode),
#Evaluator.forNodes(ExtendEvaluator.evaluate, ExtendNode),
#Evaluator.forNodes(BlockEvaluator.evaluate, BlockNode),
#Evaluator.forNodes(ReturnEvaluator.evaluate, ReturnNode),
expressionEvaluator() expressionEvaluator()
)(node, environment) )(node, environment)

View File

@@ -1,5 +1,4 @@
from smnp.ast.node.identifier import Identifier from smnp.ast.node.identifier import Identifier, FunctionCall
from smnp.ast.node.invocation import FunctionCallNode
from smnp.error.runtime import RuntimeException from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import expressionEvaluator from smnp.runtime.evaluators.expression import expressionEvaluator
@@ -20,9 +19,9 @@ class AccessEvaluator(Evaluator):
except KeyError: except KeyError:
raise RuntimeException(f"Unknown property '{right.value}' of type '{left.type.name.lower()}'", right.pos) raise RuntimeException(f"Unknown property '{right.value}' of type '{left.type.name.lower()}'", right.pos)
if type(right) == FunctionCallNode: if type(right) == FunctionCall:
try: try:
arguments = abstractIterableEvaluator(expressionEvaluator(True))(right.arguments, environment) arguments = abstractIterableEvaluator(expressionEvaluator(doAssert=True))(right.arguments, environment)
return environment.invokeMethod(left, right.name.value, arguments) return environment.invokeMethod(left, right.name.value, arguments)
except RuntimeException as e: except RuntimeException as e:
raise updatePos(e, right) raise updatePos(e, right)

View File

@@ -1,4 +1,3 @@
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import expressionEvaluator from smnp.runtime.evaluators.expression import expressionEvaluator
@@ -7,16 +6,9 @@ class AssignmentEvaluator(Evaluator):
@classmethod @classmethod
def evaluator(cls, node, environment): def evaluator(cls, node, environment):
target = node.target.value target = node.left.value
if target.startswith("_"): value = expressionEvaluator(doAssert=True)(node.right, environment).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
raise RuntimeException("Declaration and assignation variables with names starting with '_' is not allowed", node.target.pos) environment.scopes[-1][target] = value
value = expressionEvaluator(doAssert=True)(node.value, environment).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
scopeOfExistingVariable = environment.findVariableScope(target)
if scopeOfExistingVariable is None:
environment.scopes[-1][target] = value
else:
scopeOfExistingVariable[target] = value
return value return value

View File

@@ -1,104 +0,0 @@
from smnp.ast.node.identifier import Identifier
from smnp.runtime.evaluator import evaluate, Evaluator, EvaluationResult
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.type.model import Type
class AsteriskEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
iterator = expressionEvaluator(doAssert=True)(node.iterator, environment).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
return Evaluator.oneOf(
cls._numberIteratorAsteriskEvaluator(iterator),
cls._listIteratorAsteriskEvaluator(iterator),
cls._mapIteratorAsteriskEvaluator(iterator)
)(node, environment).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
@classmethod
def _numberIteratorAsteriskEvaluator(cls, evaluatedIterator):
def evaluator(node, environment):
if evaluatedIterator.type == Type.INTEGER:
results = []
automaticVariable = cls._automaticNamedVariable(node.iterator, environment, "_")
for i in range(evaluatedIterator.value):
environment.scopes[-1][automaticVariable] = Type.integer(i + 1)
result = evaluate(node.statement, environment).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
if result is None or result.type == Type.VOID:
results = None
if results is not None:
results.append(result)
del environment.scopes[-1][automaticVariable]
return EvaluationResult.OK(Type.list(results).decompose() if results is not None else Type.void())
return EvaluationResult.FAIL()
return evaluator
@classmethod
def _automaticNamedVariable(cls, iteratorNode, environment, prefix=''):
if type(iteratorNode) == Identifier:
return cls._automaticVariableName(environment, prefix, iteratorNode.value, False)
else:
return cls._automaticVariableName(environment, prefix, '', True)
@classmethod
def _automaticVariableName(cls, environment, prefix='', suffix='', startWithNumber=False):
number = 1 if startWithNumber else ''
variableName = lambda x: f"{prefix}{x}{suffix}"
while environment.findVariableScope(variableName(number)) is not None:
if number == '':
number = 1
else:
number += 1
return variableName(number)
@classmethod
def _listIteratorAsteriskEvaluator(cls, evaluatedIterator):
def evaluator(node, environment):
if evaluatedIterator.type == Type.LIST:
results = []
automaticVariableKey = cls._automaticNamedVariable(node.iterator, environment, "_")
automaticVariableValue = cls._automaticNamedVariable(node.iterator, environment, "__")
for i, v in enumerate(evaluatedIterator.value):
environment.scopes[-1][automaticVariableKey] = Type.integer(i + 1)
environment.scopes[-1][automaticVariableValue] = v
result = evaluate(node.statement, environment).value # TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
if result is not None and result.type != Type.VOID:
results.append(result)
del environment.scopes[-1][automaticVariableKey]
del environment.scopes[-1][automaticVariableValue]
return EvaluationResult.OK(Type.list(results).decompose())
return EvaluationResult.FAIL()
return evaluator
@classmethod
def _mapIteratorAsteriskEvaluator(cls, evaluatedIterator):
def evaluator(node, environment):
if evaluatedIterator.type == Type.MAP:
results = []
automaticVariableKey = cls._automaticNamedVariable(node.iterator, environment, "_")
automaticVariableValue = cls._automaticNamedVariable(node.iterator, environment, "__")
for k, v in evaluatedIterator.value.items():
environment.scopes[-1][automaticVariableKey] = k
environment.scopes[-1][automaticVariableValue] = v
result = evaluate(node.statement, environment).value # TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
if result is not None and result.type != Type.VOID:
results.append(result)
del environment.scopes[-1][automaticVariableKey]
del environment.scopes[-1][automaticVariableValue]
return EvaluationResult.OK(Type.list(results).decompose())
return EvaluationResult.FAIL()
return evaluator

View File

@@ -0,0 +1,99 @@
from smnp.ast.node.atom import StringLiteral, IntegerLiteral, NoteLiteral, BoolLiteral, TypeLiteral, FloatLiteral
from smnp.ast.node.identifier import Identifier
from smnp.ast.node.list import List
from smnp.ast.node.map import Map
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator, EvaluationResult
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.runtime.evaluators.float import FloatEvaluator
from smnp.runtime.evaluators.iterable import abstractIterableEvaluator
from smnp.runtime.tools.error import updatePos
from smnp.type.model import Type
class IntegerEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
return Type.integer(node.value)
class StringEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
return Type.string(node.value)
class NoteEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
return Type.note(node.value)
class BoolEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
return Type.bool(node.value)
class TypeEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
return Type.type(node.value)
class ListEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
list = abstractIterableEvaluator(expressionEvaluator(doAssert=True))(node, environment)
return Type.list(list)
class MapEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
map = {}
keyEvaluator = Evaluator.oneOf(
Evaluator.forNodes(lambda node, environment: EvaluationResult.OK(Type.string(node.value)), Identifier),
expressionEvaluator(doAssert=True)
)
for entry in node.children:
key = keyEvaluator(entry.key, environment).value
if key in map:
raise RuntimeException(f"Duplicated key '{key.stringify()}' found in map", entry.pos)
map[key] = expressionEvaluator(doAssert=True)(entry.value, environment).value
return Type.map(map)
class IdentifierEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
try:
return environment.findVariable(node.value)
except RuntimeException as e:
raise updatePos(e, node)
class AtomEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
return Evaluator.oneOf(
Evaluator.forNodes(StringEvaluator.evaluate, StringLiteral),
Evaluator.forNodes(IntegerEvaluator.evaluate, IntegerLiteral),
Evaluator.forNodes(FloatEvaluator.evaluate, FloatLiteral),
Evaluator.forNodes(NoteEvaluator.evaluate, NoteLiteral),
Evaluator.forNodes(BoolEvaluator.evaluate, BoolLiteral),
Evaluator.forNodes(TypeEvaluator.evaluate, TypeLiteral),
Evaluator.forNodes(IdentifierEvaluator.evaluate, Identifier),
Evaluator.forNodes(ListEvaluator.evaluate, List),
Evaluator.forNodes(MapEvaluator.evaluate, Map)
)(node, environment).value

View File

@@ -5,12 +5,12 @@ class BlockEvaluator(Evaluator):
@classmethod @classmethod
def evaluator(cls, node, environment): def evaluator(cls, node, environment):
environment.scopes.append({}) environment.appendScope()
for child in node.children: for child in node.children:
evaluate(child, environment) evaluate(child, environment)
environment.scopes.pop(-1) environment.popScope()
# #
# def evaluateBlock(block, environment): # def evaluateBlock(block, environment):

View File

@@ -0,0 +1,35 @@
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator, evaluate
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.type.model import Type
class IfElseEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
condition = expressionEvaluator(doAssert=True)(node.condition, environment).value
if condition.type != Type.BOOL:
raise RuntimeException(f"Only {Type.BOOL.name.lower()} types can be used as conditions in conditional expression", node.condition.pos)
if condition.value:
return expressionEvaluator(doAssert=True)(node.ifNode, environment).value
else:
return expressionEvaluator(doAssert=True)(node.elseNode, environment).value
class IfElseStatementEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
condition = expressionEvaluator(doAssert=True)(node.condition, environment).value
if condition.type != Type.BOOL:
raise RuntimeException(
f"Only {Type.BOOL.name.lower()} types can be used as conditions in conditional expression", node.condition.pos)
if condition.value:
evaluate(node.ifNode, environment)
else:
evaluate(node.elseNode, environment)

View File

@@ -1,15 +1,9 @@
from smnp.ast.node.assignment import AssignmentNode from smnp.ast.node.condition import IfElse
from smnp.ast.node.asterisk import AsteriskNode from smnp.ast.node.expression import Sum, Relation, And, Or, Loop
from smnp.ast.node.bool import BoolLiteralNode from smnp.ast.node.factor import NotOperator, Power
from smnp.ast.node.identifier import Identifier from smnp.ast.node.identifier import FunctionCall, Assignment
from smnp.ast.node.integer import IntegerLiteralNode from smnp.ast.node.term import Product
from smnp.ast.node.invocation import FunctionCallNode from smnp.ast.node.unit import MinusOperator, Access
from smnp.ast.node.list import List
from smnp.ast.node.map import MapNode
from smnp.ast.node.note import NoteLiteralNode
from smnp.ast.node.operator import LeftAssociativeOperatorNode
from smnp.ast.node.string import StringLiteralNode
from smnp.ast.node.type import TypeNode
from smnp.error.runtime import RuntimeException from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator from smnp.runtime.evaluator import Evaluator
from smnp.type.model import Type from smnp.type.model import Type
@@ -17,38 +11,54 @@ from smnp.type.model import Type
def expressionEvaluator(doAssert=False): def expressionEvaluator(doAssert=False):
def evaluateExpression(node, environment): def evaluateExpression(node, environment):
from smnp.runtime.evaluators.string import StringEvaluator from smnp.runtime.evaluators.function import FunctionCallEvaluator
from smnp.runtime.evaluators.integer import IntegerEvaluator from smnp.runtime.evaluators.minus import MinusEvaluator
from smnp.runtime.evaluators.note import NoteEvaluator from smnp.runtime.evaluators.atom import AtomEvaluator
from smnp.runtime.evaluators.identifier import IdentifierEvaluator from smnp.runtime.evaluators.access import AccessEvaluator
from smnp.runtime.evaluators.list import ListEvaluator from smnp.runtime.evaluators.negation import NotEvaluator
from smnp.runtime.evaluators.function import FunctionCallEvaluator from smnp.runtime.evaluators.power import PowerEvaluator
from smnp.runtime.evaluators.loop import LoopEvaluator
from smnp.runtime.evaluators.assignment import AssignmentEvaluator
from smnp.runtime.evaluators.product import ProductEvaluator
from smnp.runtime.evaluators.access import AccessEvaluator from smnp.runtime.evaluators.sum import SumEvaluator
from smnp.runtime.evaluators.assignment import AssignmentEvaluator from smnp.runtime.evaluators.relation import RelationEvaluator
from smnp.runtime.evaluators.asterisk import AsteriskEvaluator from smnp.runtime.evaluators.condition import IfElseEvaluator
from smnp.runtime.evaluators.map import MapEvaluator from smnp.runtime.evaluators.logic import AndEvaluator
from smnp.runtime.evaluators.type import TypeEvaluator from smnp.runtime.evaluators.logic import OrEvaluator
from smnp.runtime.evaluators.bool import BoolEvaluator result = Evaluator.oneOf(
result = Evaluator.oneOf( Evaluator.forNodes(FunctionCallEvaluator.evaluate, FunctionCall),
Evaluator.forNodes(FunctionCallEvaluator.evaluate, FunctionCallNode), Evaluator.forNodes(MinusEvaluator.evaluate, MinusOperator),
Evaluator.forNodes(StringEvaluator.evaluate, StringLiteralNode), Evaluator.forNodes(AccessEvaluator.evaluate, Access),
Evaluator.forNodes(IntegerEvaluator.evaluate, IntegerLiteralNode), Evaluator.forNodes(NotEvaluator.evaluate, NotOperator),
Evaluator.forNodes(NoteEvaluator.evaluate, NoteLiteralNode), Evaluator.forNodes(PowerEvaluator.evaluate, Power),
Evaluator.forNodes(BoolEvaluator.evaluate, BoolLiteralNode), Evaluator.forNodes(LoopEvaluator.evaluate, Loop),
Evaluator.forNodes(TypeEvaluator.evaluate, TypeNode), Evaluator.forNodes(AssignmentEvaluator.evaluate, Assignment),
Evaluator.forNodes(IdentifierEvaluator.evaluate, Identifier), Evaluator.forNodes(ProductEvaluator.evaluate, Product),
Evaluator.forNodes(ListEvaluator.evaluate, List), Evaluator.forNodes(SumEvaluator.evaluate, Sum),
Evaluator.forNodes(AccessEvaluator.evaluate, LeftAssociativeOperatorNode), Evaluator.forNodes(RelationEvaluator.evaluate, Relation),
Evaluator.forNodes(AssignmentEvaluator.evaluate, AssignmentNode), Evaluator.forNodes(IfElseEvaluator.evaluate, IfElse),
Evaluator.forNodes(AsteriskEvaluator.evaluate, AsteriskNode), Evaluator.forNodes(AndEvaluator.evaluate, And),
Evaluator.forNodes(MapEvaluator.evaluate, MapNode) Evaluator.forNodes(OrEvaluator.evaluate, Or),
)(node, environment) AtomEvaluator.evaluate
)(node, environment)
if doAssert and result.result and result.value.type == Type.VOID: if doAssert and result.result and result.value.type == Type.VOID:
raise RuntimeException(f"Expected expression", node.pos) raise RuntimeException(f"Expected expression", node.pos)
return result
return result
return evaluateExpression return evaluateExpression
def expressionEvaluatorWithMatcher(matcher, exceptionProvider, doAssert=True):
def evaluate(node, environment):
value = expressionEvaluator(doAssert=doAssert)(node, environment).value
if not matcher.match(value):
raise exceptionProvider(value)
return value
return evaluate

View File

@@ -17,11 +17,11 @@ class ExtendEvaluator(Evaluator):
@classmethod @classmethod
def _typeToMethodSignature(cls, node): def _typeToMethodSignature(cls, node):
if type(node.specifiers) == NoneNode: if type(node.specifiers) == NoneNode:
return signature(ofType(node.type)) return signature(ofType(node.type.value))
elif node.type == Type.LIST: elif node.type.value == Type.LIST:
return signature(listSpecifier(node.specifiers[0])) return signature(listSpecifier(node.specifiers[0]))
elif node.type == Type.MAP: elif node.type.value == Type.MAP:
return signature(mapSpecifier(node.specifiers[0], node.specifiers[1])) return signature(mapSpecifier(node.specifiers[0], node.specifiers[1]))
@classmethod @classmethod
@@ -32,8 +32,8 @@ class ExtendEvaluator(Evaluator):
@classmethod @classmethod
def _evaluateMethodDefinition(cls, node, environment, type, variable): def _evaluateMethodDefinition(cls, node, environment, type, variable):
name = node.name.value name = node.name.value
signature = argumentsNodeToMethodSignature(node.arguments) defaultArguments, signature = argumentsNodeToMethodSignature(node.arguments, environment)
arguments = [arg.variable.value for arg in node.arguments] arguments = [arg.variable.value for arg in node.arguments]
body = node.body body = node.body
environment.addCustomMethod(type, variable, name, signature, arguments, body) environment.addCustomMethod(type, variable, name, signature, arguments, body, defaultArguments)

View File

@@ -0,0 +1,9 @@
from smnp.runtime.evaluator import Evaluator
from smnp.type.model import Type
class FloatEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
return Type.float(node.value)

View File

@@ -4,6 +4,7 @@ from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.runtime.evaluators.iterable import abstractIterableEvaluator from smnp.runtime.evaluators.iterable import abstractIterableEvaluator
from smnp.runtime.tools.error import updatePos from smnp.runtime.tools.error import updatePos
from smnp.runtime.tools.signature import argumentsNodeToMethodSignature from smnp.runtime.tools.signature import argumentsNodeToMethodSignature
from smnp.type.model import Type
class FunctionCallEvaluator(Evaluator): class FunctionCallEvaluator(Evaluator):
@@ -24,10 +25,10 @@ class FunctionDefinitionEvaluator(Evaluator):
def evaluator(cls, node, environment): def evaluator(cls, node, environment):
try: try:
name = node.name.value name = node.name.value
signature = argumentsNodeToMethodSignature(node.arguments) defaultArguments, signature = argumentsNodeToMethodSignature(node.arguments, environment)
arguments = [ arg.variable.value for arg in node.arguments ] arguments = [ arg.variable.value for arg in node.arguments ]
body = node.body body = node.body
environment.addCustomFunction(name, signature, arguments, body) environment.addCustomFunction(name, signature, arguments, body, defaultArguments)
except RuntimeException as e: except RuntimeException as e:
raise updatePos(e, node) raise updatePos(e, node)
@@ -38,8 +39,6 @@ class BodyEvaluator(Evaluator):
def evaluator(cls, node, environment): def evaluator(cls, node, environment):
for child in node.children: for child in node.children:
evaluate(child, environment) evaluate(child, environment)
if environment.callStack[-1].value is not None:
return environment.callStack[-1].value
class ReturnEvaluator(Evaluator): class ReturnEvaluator(Evaluator):
@@ -47,7 +46,21 @@ class ReturnEvaluator(Evaluator):
@classmethod @classmethod
def evaluator(cls, node, environment): def evaluator(cls, node, environment):
if len(environment.callStack) > 0: if len(environment.callStack) > 0:
returnValue = expressionEvaluator(doAssert=True)(node.value, environment) returnValue = expressionEvaluator()(node.value, environment).value
environment.callStack[-1].value = returnValue.value raise Return(returnValue)
# Disclaimer
# Exception system usage to control program execution flow is really bad idea.
# However because of lack of 'goto' instruction equivalent in Python
# there is to need to use some mechanism to break function execution on 'return' statement
# and immediately go to Environment's method 'invokeFunction()' or 'invokeMethod()',
# which can handle value that came with exception and return it to code being executed.
else: else:
raise RuntimeException("Cannot use 'return' statement outside a function or method", node.pos, environment) raise RuntimeException("Cannot use 'return' statement outside a function or method", node.pos, environment)
class Return(Exception):
def __init__(self, value):
if value is None:
value = Type.void()
self.value = value

View File

@@ -1,4 +1,3 @@
from smnp.ast.node.none import NoneNode
from smnp.program.interpreter import Interpreter from smnp.program.interpreter import Interpreter
from smnp.runtime.evaluator import Evaluator from smnp.runtime.evaluator import Evaluator
@@ -7,13 +6,6 @@ class ImportEvaluator(Evaluator):
@classmethod @classmethod
def evaluator(cls, node, environment): def evaluator(cls, node, environment):
if type(node.type) == NoneNode:
cls._evaluateCodeImport(node, environment)
else:
raise RuntimeError("Importing types is not implemented yet")
@classmethod
def _evaluateCodeImport(cls, node, environment):
source = node.source source = node.source
newEnvironment = Interpreter.interpretFile(source.value) newEnvironment = Interpreter.interpretFile(source.value, baseEnvironment=environment)
environment.extend(newEnvironment) environment.extend(newEnvironment)

View File

@@ -0,0 +1,21 @@
from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.type.model import Type
class AndEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
left = expressionEvaluator(doAssert=True)(node.left, environment).value
right = expressionEvaluator(doAssert=True)(node.right, environment).value
return Type.bool(left.value and right.value)
class OrEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
left = expressionEvaluator(doAssert=True)(node.left, environment).value
right = expressionEvaluator(doAssert=True)(node.right, environment).value
return Type.bool(left.value or right.value)

View File

@@ -0,0 +1,121 @@
from smnp.ast.node.none import NoneNode
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator, evaluate
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.type.model import Type
class LoopEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
iterator = expressionEvaluator(doAssert=True)(node.left, environment).value
parameters = [ identifier.value for identifier in node.parameters ] if type(node.parameters) != NoneNode() else []
try:
environment.appendScope()
output = {
Type.INTEGER: cls.numberEvaluator,
Type.BOOL: cls.boolEvaluator,
Type.LIST: cls.listEvaluator,
Type.MAP: cls.mapEvaluator
}[iterator.type](node, environment, iterator, parameters, node.filter)
environment.popScope()
except KeyError:
raise RuntimeException(f"The {iterator.type.name.lower()} type cannot stand as an iterator for loop statement", node.left.pos)
return Type.list(output)
@classmethod
def numberEvaluator(cls, node, environment, evaluatedIterator, parameters, filter):
output = []
if len(parameters) > 1:
raise RuntimeException(f"Loop with numeric iterator can handle only one parameter", node.parameters.pos)
for i in range(evaluatedIterator.value):
if len(parameters) > 0:
environment.scopes[-1][parameters[0]] = Type.integer(i)
if cls.doFilter(filter, environment):
output.append(evaluate(node.right, environment).value)
return output
@classmethod
def doFilter(cls, filter, environment):
if type(filter) is not NoneNode:
evaluation = expressionEvaluator(doAssert=True)(filter, environment).value
if evaluation.type != Type.BOOL:
raise RuntimeException(f"Expected {Type.BOOL.name.lower()} as filter expression, found {evaluation.type.name.lower()}", filter.pos)
return evaluation.value
return True
@classmethod
def boolEvaluator(cls, node, environment, evaluatedIterator, parameters, filter):
output = []
if len(parameters) > 0:
raise RuntimeException(f"Loop with logic iterator can't' handle any parameters", node.parameters.pos)
condition = evaluatedIterator
while condition.value:
if cls.doFilter(filter, environment):
output.append(evaluate(node.right, environment).value)
condition = expressionEvaluator(doAssert=True)(node.left, environment).value
return output
@classmethod
def listEvaluator(cls, node, environment, evaluatedIterator, parameters, filter):
output = []
if len(parameters) > 2:
raise RuntimeException(f"Loop with list iterator can handle only two parameters", node.parameters.pos)
for i, value in enumerate(evaluatedIterator.value):
if len(parameters) == 1:
environment.scopes[-1][parameters[0]] = value
if len(parameters) == 2:
environment.scopes[-1][parameters[0]] = Type.integer(i)
environment.scopes[-1][parameters[1]] = value
if cls.doFilter(filter, environment):
output.append(evaluate(node.right, environment).value)
return output
@classmethod
def mapEvaluator(cls, node, environment, evaluatedIterator, parameters, filter):
output = []
if len(parameters) > 3:
raise RuntimeException(f"Loop with map iterator can handle only three parameters", node.parameters.pos)
i = 0
for key, value in evaluatedIterator.value.items():
if len(parameters) == 1:
environment.scopes[-1][parameters[0]] = value
if len(parameters) == 2:
environment.scopes[-1][parameters[0]] = key
environment.scopes[-1][parameters[1]] = value
if len(parameters) == 3:
environment.scopes[-1][parameters[0]] = Type.integer(i)
environment.scopes[-1][parameters[1]] = key
environment.scopes[-1][parameters[2]] = value
i += 1
if cls.doFilter(filter, environment):
output.append(evaluate(node.right, environment).value)
return output

View File

@@ -1,19 +0,0 @@
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.type.model import Type
class MapEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
map = {}
exprEvaluator = expressionEvaluator(doAssert=True)
for entry in node.children:
key = exprEvaluator(entry.key, environment).value
if key in map:
raise RuntimeException(f"Duplicated key '{key.stringify()}' found in map", entry.pos)
map[key] = exprEvaluator(entry.value, environment).value
return Type.map(map)

View File

@@ -0,0 +1,35 @@
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator, evaluate
from smnp.type.model import Type
class MinusEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
value = evaluate(node.value, environment).value
try:
return {
Type.INTEGER: cls.evaluateForInteger,
Type.FLOAT: cls.evaluateForFloat,
Type.STRING: cls.evaluateForString,
Type.LIST: cls.evaluateForList
}[value.type](value.value)
except KeyError:
raise RuntimeException(f"Type {value.type.name.lower()} does not support '{node.operator.value}' operator", node.pos)
@classmethod
def evaluateForInteger(cls, value):
return Type.integer(-value)
@classmethod
def evaluateForFloat(cls, value):
return Type.float(-value)
@classmethod
def evaluateForString(cls, value):
return Type.string(value[::-1])
@classmethod
def evaluateForList(cls, value):
return Type.list(value[::-1])

View File

@@ -0,0 +1,16 @@
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.type.model import Type
class NotEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
value = expressionEvaluator(doAssert=True)(node.value, environment).value
if value.type != Type.BOOL:
raise RuntimeException(f"Operator '{node.operator.value}' is supported only by {Type.BOOL.name.lower()} type", node.value.pos)
return Type.bool(not value.value)

View File

@@ -0,0 +1,21 @@
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.type.model import Type
class PowerEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
left = expressionEvaluator(doAssert=True)(node.left, environment).value
right = expressionEvaluator(doAssert=True)(node.right, environment).value
supportedTypes = [Type.INTEGER, Type.FLOAT]
if not left.type in supportedTypes:
raise RuntimeException(f"Operator '{node.operator.value}' is supported only by {Type.INTEGER.name.lower()} type", node.left.pos)
if not right.type in supportedTypes:
raise RuntimeException(f"Operator '{node.operator.value}' is supported only by {[t.name.lower() for t in supportedTypes]} type", node.right.pos)
return Type.integer(int(left.value ** right.value))

View File

@@ -0,0 +1,45 @@
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.type.model import Type
class ProductEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
left = expressionEvaluator(doAssert=True)(node.left, environment).value
right = expressionEvaluator(doAssert=True)(node.right, environment).value
supportedTypes = [Type.INTEGER, Type.FLOAT]
if not left.type in supportedTypes:
raise RuntimeException(
f"Operator '{node.operator.value}' is supported only by {[t.name.lower() for t in supportedTypes]} type", node.left.pos)
if not right.type in supportedTypes:
raise RuntimeException(
f"Operator '{node.operator.value}' is supported only by {[t.name.lower() for t in supportedTypes]} type", node.right.pos)
if node.operator.value == "*":
return getProperTypeProvider(left.value * right.value)
if node.operator.value == "/":
if right.value == 0:
raise RuntimeException("Attempt to divide by 0", node.right.pos)
value = left.value / right.value
if left.type == right.type == Type.INTEGER and int(value) == value:
return Type.integer(int(value))
return getProperTypeProvider(value)
raise RuntimeError("This line should never be reached")
def getProperTypeProvider(value):
return {
int: lambda v: Type.integer(v),
float: lambda v: Type.float(v)
}[type(value)](value)

View File

@@ -0,0 +1,52 @@
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.type.model import Type
class RelationEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
left = expressionEvaluator(doAssert=True)(node.left, environment).value
right = expressionEvaluator(doAssert=True)(node.right, environment).value
if node.operator.value == "==":
return cls.equalOperatorEvaluator(left, node.operator, right)
if node.operator.value == "!=":
return cls.notEqualOperatorEvaluator(left, node.operator, right)
return cls.otherRelationOperatorsEvaluator(left, node.operator, right)
@classmethod
def equalOperatorEvaluator(cls, left, operator, right):
if left.type in [Type.INTEGER, Type.FLOAT] and right.type in [Type.INTEGER, Type.FLOAT]:
return Type.bool(left.value == right.value)
return Type.bool(left.type == right.type and left.value == right.value)
@classmethod
def notEqualOperatorEvaluator(cls, left, operator, right):
if left.type in [Type.INTEGER, Type.FLOAT] and right.type in [Type.INTEGER, Type.FLOAT]:
return Type.bool(left.value != right.value)
return Type.bool(left.type != right.type or left.value != right.value)
@classmethod
def otherRelationOperatorsEvaluator(cls, left, operator, right):
if left.type in [Type.INTEGER, Type.FLOAT] and right.type in [Type.INTEGER, Type.FLOAT]:
if operator.value == ">":
return Type.bool(left.value > right.value)
if operator.value == ">=":
return Type.bool(left.value >= right.value)
if operator.value == "<":
return Type.bool(left.value < right.value)
if operator.value == "<=":
return Type.bool(left.value <= right.value)
raise RuntimeException(f"Operator {operator.value} is not supported by {left.type.name.lower()} and {right.type.name.lower()} types", operator.pos)

View File

@@ -0,0 +1,75 @@
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.type.model import Type
class SumEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
left = expressionEvaluator(doAssert=True)(node.left, environment).value
right = expressionEvaluator(doAssert=True)(node.right, environment).value
if left.type in [Type.INTEGER, Type.FLOAT] and right.type in [Type.INTEGER, Type.FLOAT]:
return cls.numberEvaluator(left, node.operator, right)
if left.type == right.type == Type.STRING:
return cls.stringEvaluator(left, node.operator, right)
if left.type == right.type == Type.LIST:
return cls.listEvaluator(left, node.operator, right)
if left.type == right.type == Type.MAP:
return cls.mapEvaluator(left, node.operator, right)
raise RuntimeException(
f"Operator {node.operator.value} is not supported by {left.type.name.lower()} and {right.type.name.lower()} types",
node.operator.pos)
@classmethod
def numberEvaluator(cls, left, operator, right):
if operator.value == "+":
return getProperTypeProvider(left.value + right.value)
if operator.value == "-":
return getProperTypeProvider(left.value - right.value)
raise RuntimeError("This line should never be reached")
@classmethod
def stringEvaluator(cls, left, operator, right):
if operator.value == "+":
return Type.string(left.value + right.value)
if operator.value == "-":
raise RuntimeException(f"Operator {operator.value} is not supported by string types", operator.pos)
raise RuntimeError("This line should never be reached")
@classmethod
def listEvaluator(cls, left, operator, right):
if operator.value == "+":
return Type.list(left.value + right.value)
if operator.value == "-":
raise RuntimeException(f"Operator {operator.value} is not supported by list types", operator.pos)
raise RuntimeError("This line should never be reached")
@classmethod
def mapEvaluator(cls, left, operator, right):
if operator.value == "+":
return Type.map({**left.value, **right.value})
if operator.value == "-":
raise RuntimeException(f"Operator {operator.value} is not supported by map types", operator.pos)
raise RuntimeError("This line should never be reached")
def getProperTypeProvider(value):
return {
int: lambda v: Type.integer(v),
float: lambda v: Type.float(v)
}[type(value)](value)

View File

@@ -0,0 +1,17 @@
from smnp.error.custom import CustomException
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.type.model import Type
class ThrowEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
string = expressionEvaluator(doAssert=True)(node.value, environment).value
if string.type != Type.STRING:
raise RuntimeException(f"Only {Type.STRING.name.lower()} types can be thrown", node.value.pos)
raise CustomException(string.value, node.pos)

View File

@@ -0,0 +1,34 @@
from smnp.ast.node.identifier import Identifier, FunctionCall
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.runtime.evaluators.iterable import abstractIterableEvaluator
from smnp.runtime.tools.error import updatePos
class UnitEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
return
class AccessEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
left = expressionEvaluator(doAssert=True)(node.left, environment).value # TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
right = node.right
if type(right) == Identifier:
try:
return left.properties[right.value]
except KeyError:
raise RuntimeException(f"Unknown property '{right.value}' of type '{left.type.name.lower()}'", right.pos)
if type(right) == FunctionCall:
try:
arguments = abstractIterableEvaluator(expressionEvaluator(doAssert=True))(right.arguments, environment)
return environment.invokeMethod(left, right.name.value, arguments)
except RuntimeException as e:
raise updatePos(e, right)

View File

@@ -1,7 +1,9 @@
from smnp.ast.node import type as ast
from smnp.ast.node.none import NoneNode from smnp.ast.node.none import NoneNode
from smnp.ast.node.type import TypeNode, TypeSpecifier from smnp.ast.node.type import TypesList
from smnp.error.runtime import RuntimeException from smnp.error.runtime import RuntimeException
from smnp.function.signature import varargSignature, signature from smnp.function.signature import varargSignature, signature, optional
from smnp.runtime.evaluators.expression import expressionEvaluator, expressionEvaluatorWithMatcher
from smnp.runtime.tools.error import updatePos from smnp.runtime.tools.error import updatePos
from smnp.type.model import Type from smnp.type.model import Type
from smnp.type.signature.matcher.list import listOfMatchers from smnp.type.signature.matcher.list import listOfMatchers
@@ -9,31 +11,54 @@ from smnp.type.signature.matcher.map import mapOfMatchers
from smnp.type.signature.matcher.type import allTypes, oneOf, ofType from smnp.type.signature.matcher.type import allTypes, oneOf, ofType
def argumentsNodeToMethodSignature(node): def evaluateDefaultArguments(node, environment):
defaultValues = { arg.variable.value: expressionEvaluator(doAssert=True)(arg.optionalValue, environment).value for arg in node.children if type(arg.optionalValue) != NoneNode }
return defaultValues
def argumentsNodeToMethodSignature(node, environment):
try: try:
sign = [] sign = []
vararg = None vararg = None
argumentsCount = len(node.children) argumentsCount = len(node.children)
checkPositionOfOptionalArguments(node)
defaultArgs = {}
for i, child in enumerate(node.children): for i, child in enumerate(node.children):
matchers = { matchers = {
TypeNode: (lambda c: c.type, typeMatcher), ast.Type: (lambda c: c.type, typeMatcher),
NoneNode: (lambda c: c.type, lambda c: allTypes()), NoneNode: (lambda c: c.type, lambda c: allTypes()),
TypeSpecifier: (lambda c: c, multipleTypeMatcher) TypesList: (lambda c: c, multipleTypeMatcher)
} }
evaluatedMatcher = matchers[type(child.type)][1](matchers[type(child.type)][0](child)) evaluatedMatcher = matchers[type(child.type)][1](matchers[type(child.type)][0](child))
if child.vararg: if child.vararg:
if i != argumentsCount - 1: if i != argumentsCount - 1:
raise RuntimeException("Vararg must be the last argument in signature", child.pos) raise RuntimeException("Vararg must be the last argument in signature", child.pos)
vararg = evaluatedMatcher vararg = evaluatedMatcher
else: else:
if type(child.optionalValue) != NoneNode:
defaultArgs[child.variable.value] = expressionEvaluatorWithMatcher(
evaluatedMatcher,
exceptionProvider=lambda value: RuntimeException(
f"Value '{value.stringify()}' doesn't match declared type: {evaluatedMatcher.string}", child.optionalValue.pos)
)(child.optionalValue, environment)
evaluatedMatcher = optional(evaluatedMatcher)
sign.append(evaluatedMatcher) sign.append(evaluatedMatcher)
return defaultArgs, (varargSignature(vararg, *sign, wrapVarargInValue=True) if vararg is not None else signature(*sign))
return varargSignature(vararg, *sign, wrapVarargInValue=True) if vararg is not None else signature(*sign)
except RuntimeException as e: except RuntimeException as e:
raise updatePos(e, node) raise updatePos(e, node)
def checkPositionOfOptionalArguments(node):
firstOptional = next((i for i, v in enumerate(node.children) if type(v.optionalValue) != NoneNode), None) #next(filter(lambda arg: type(arg.optionalValue) != NoneNode, node.children), None)
if firstOptional is not None:
regularAfterOptional = next((i for i, v in enumerate(node.children[firstOptional:]) if type(v.optionalValue) == NoneNode), None)
if regularAfterOptional is not None:
raise RuntimeException(f"Optional arguments should be declared at the end of the arguments list", node.children[regularAfterOptional].pos)
def multipleTypeMatcher(typeNode): def multipleTypeMatcher(typeNode):
subSignature = [] subSignature = []
@@ -49,10 +74,10 @@ def multipleTypeMatcher(typeNode):
def typeMatcher(typeNode): def typeMatcher(typeNode):
if type(typeNode.specifiers) == NoneNode: if type(typeNode.specifiers) == NoneNode:
return ofType(typeNode.type) return ofType(typeNode.type.value)
elif typeNode.type == Type.LIST and len(typeNode.specifiers) == 1: elif typeNode.type.value == Type.LIST and len(typeNode.specifiers) == 1:
return listSpecifier(typeNode.specifiers[0]) return listSpecifier(typeNode.specifiers[0])
elif typeNode.type == Type.MAP and len(typeNode.specifiers) == 2: elif typeNode.type.value == Type.MAP and len(typeNode.specifiers) == 2:
return mapSpecifier(typeNode.specifiers[0], typeNode.specifiers[1]) return mapSpecifier(typeNode.specifiers[0], typeNode.specifiers[1])
raise RuntimeException("Unknown type", typeNode.pos) # Todo: Improve pointing position raise RuntimeException("Unknown type", typeNode.pos) # Todo: Improve pointing position

View File

@@ -2,13 +2,14 @@ from smnp.error.syntax import SyntaxException
from smnp.token.model import TokenList from smnp.token.model import TokenList
from smnp.token.tokenizers.bool import boolTokenizer from smnp.token.tokenizers.bool import boolTokenizer
from smnp.token.tokenizers.comment import commentTokenizer from smnp.token.tokenizers.comment import commentTokenizer
from smnp.token.tokenizers.float import floatTokenizer
from smnp.token.tokenizers.identifier import identifierTokenizer from smnp.token.tokenizers.identifier import identifierTokenizer
from smnp.token.tokenizers.keyword import typeTokenizer from smnp.token.tokenizers.keyword import typeTokenizer
from smnp.token.tokenizers.note import noteTokenizer from smnp.token.tokenizers.note import noteTokenizer
from smnp.token.tokenizers.relation import relationOperatorTokenizer from smnp.token.tokenizers.relation import relationOperatorTokenizer
from smnp.token.tokenizers.string import stringTokenizer from smnp.token.tokenizers.string import stringTokenizer
from smnp.token.tokenizers.whitespace import whitespacesTokenizer from smnp.token.tokenizers.whitespace import whitespacesTokenizer
from smnp.token.tools import defaultTokenizer, separated, regexPatternTokenizer from smnp.token.tools import defaultTokenizer, separated, regexPatternTokenizer, mapValue
from smnp.token.type import TokenType from smnp.token.type import TokenType
tokenizers = ( tokenizers = (
@@ -27,7 +28,9 @@ tokenizers = (
defaultTokenizer(TokenType.CLOSE_SQUARE), defaultTokenizer(TokenType.CLOSE_SQUARE),
defaultTokenizer(TokenType.OPEN_ANGLE), defaultTokenizer(TokenType.OPEN_ANGLE),
defaultTokenizer(TokenType.CLOSE_ANGLE), defaultTokenizer(TokenType.CLOSE_ANGLE),
defaultTokenizer(TokenType.SEMICOLON),
defaultTokenizer(TokenType.ASTERISK), defaultTokenizer(TokenType.ASTERISK),
defaultTokenizer(TokenType.PERCENT),
defaultTokenizer(TokenType.ASSIGN), defaultTokenizer(TokenType.ASSIGN),
defaultTokenizer(TokenType.COMMA), defaultTokenizer(TokenType.COMMA),
defaultTokenizer(TokenType.SLASH), defaultTokenizer(TokenType.SLASH),
@@ -39,7 +42,8 @@ tokenizers = (
defaultTokenizer(TokenType.DOT), defaultTokenizer(TokenType.DOT),
# Types # Types
separated(regexPatternTokenizer(TokenType.INTEGER, r'\d')), separated(floatTokenizer),
mapValue(separated(regexPatternTokenizer(TokenType.INTEGER, r'\d')), int),
stringTokenizer, stringTokenizer,
noteTokenizer, noteTokenizer,
boolTokenizer, boolTokenizer,
@@ -50,7 +54,9 @@ tokenizers = (
separated(defaultTokenizer(TokenType.RETURN)), separated(defaultTokenizer(TokenType.RETURN)),
separated(defaultTokenizer(TokenType.EXTEND)), separated(defaultTokenizer(TokenType.EXTEND)),
separated(defaultTokenizer(TokenType.IMPORT)), separated(defaultTokenizer(TokenType.IMPORT)),
separated(defaultTokenizer(TokenType.THROW)),
separated(defaultTokenizer(TokenType.FROM)), separated(defaultTokenizer(TokenType.FROM)),
separated(defaultTokenizer(TokenType.WITH)),
separated(defaultTokenizer(TokenType.ELSE)), separated(defaultTokenizer(TokenType.ELSE)),
separated(defaultTokenizer(TokenType.AND)), separated(defaultTokenizer(TokenType.AND)),
separated(defaultTokenizer(TokenType.NOT)), separated(defaultTokenizer(TokenType.NOT)),

View File

@@ -0,0 +1,17 @@
from smnp.token.model import Token
from smnp.token.tools import regexPatternTokenizer, keywordTokenizer, allOf
from smnp.token.type import TokenType
def createToken(pos, beforeDot, dot, afterDot):
rawValue = f"{beforeDot.value}.{afterDot.value}"
value = float(rawValue)
return Token(TokenType.FLOAT, value, pos, rawValue)
floatTokenizer = allOf(
regexPatternTokenizer(TokenType.INTEGER, r'\d'),
keywordTokenizer(None, "."),
regexPatternTokenizer(TokenType.INTEGER, r'\d'),
createToken=createToken
)

View File

@@ -3,7 +3,7 @@ from smnp.token.type import TokenType
from smnp.type.model import Type from smnp.type.model import Type
typeTokenizer = separated(keywordsTokenizer(TokenType.TYPE, *[type.name.lower() for type in Type])) typeTokenizer = separated(keywordsTokenizer(TokenType.TYPE, *[type.name.lower() for type in Type], mapKeyword=lambda value: Type[value.upper()]))

View File

@@ -14,7 +14,7 @@ def noteTokenizer(input, current, line):
rawValue = '' rawValue = ''
if input[current] == '@': if input[current] == '@':
rawValue += input[current+consumedChars] rawValue += input[current+consumedChars]
consumedChars += 1 consumedChars += 1 # TODO: Check if next item does even exist
if input[current+consumedChars] in ('C', 'c', 'D', 'd', 'E', 'e', 'F', 'f', 'G', 'g', 'A', 'a', 'H', 'h', 'B', 'b'): if input[current+consumedChars] in ('C', 'c', 'D', 'd', 'E', 'e', 'F', 'f', 'G', 'g', 'A', 'a', 'H', 'h', 'B', 'b'):
rawValue += input[current + consumedChars] rawValue += input[current + consumedChars]
notePitch = input[current+consumedChars] notePitch = input[current+consumedChars]

Some files were not shown because too many files have changed in this diff Show More