Compare commits
89 Commits
new-parser
...
add-cli
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24bcb25bee | ||
|
|
83ef3f6888 | ||
|
|
3061dae723 | ||
|
|
aca227ac5e | ||
|
|
7ec967a014 | ||
|
|
aca6e6bb55 | ||
|
|
a7de7f0279 | ||
|
|
7e55fe4c1a | ||
|
|
8abae7c2ff | ||
|
|
07f08b0557 | ||
|
|
13b069dc7d | ||
|
|
73ea88d8d9 | ||
|
|
a5875425fc | ||
|
|
0dcf5287e1 | ||
|
|
75dcacce67 | ||
|
|
70687ddc02 | ||
|
|
d802c58eee | ||
|
|
c9a3fc070b | ||
|
|
b126f83824 | ||
|
|
6dc503ba86 | ||
|
|
6222dccaac | ||
|
|
0657214aa3 | ||
|
|
3feec0839b | ||
|
|
56ca69246d | ||
|
|
5a2508e804 | ||
|
|
ea28ab6235 | ||
|
|
6e9e252b86 | ||
|
|
17ef5be057 | ||
|
|
44e63ed18d | ||
|
|
83c7b92741 | ||
|
|
79a7b8bb1d | ||
|
|
2737139962 | ||
|
|
c5435e66ff | ||
|
|
460deb4981 | ||
|
|
69bac69946 | ||
|
|
6bd8046346 | ||
|
|
e70b5fa71a | ||
|
|
44d234d36a | ||
|
|
78ea26ea08 | ||
|
|
b6983df2d3 | ||
|
|
86cf5d01f3 | ||
|
|
a07b226edb | ||
|
|
9ae9da089b | ||
|
|
4f2058eaac | ||
|
|
a68f870037 | ||
|
|
526412068f | ||
|
|
439765f442 | ||
|
|
4c03ca2f86 | ||
|
|
033d864b0f | ||
|
|
41f385de09 | ||
|
|
5512f808f8 | ||
|
|
cc569b5f19 | ||
|
|
b31e17d176 | ||
|
|
95e6a5f95d | ||
|
|
99dd8bd46e | ||
|
|
dc3387c685 | ||
|
|
1094c071fb | ||
|
|
13a6dedba6 | ||
|
|
94128d9f21 | ||
|
|
65fccda989 | ||
|
|
35eb38076f | ||
|
|
a1273896e4 | ||
|
|
94666aca79 | ||
|
|
6d1351e4a0 | ||
|
|
1563045de1 | ||
|
|
4394c9a8db | ||
|
|
2bf25da2fa | ||
|
|
df4d737676 | ||
|
|
fe8dca4d2c | ||
|
|
386c89502a | ||
|
|
0c72203551 | ||
|
|
0aad7e52dd | ||
|
|
b711b6a582 | ||
|
|
c7e90b9fbd | ||
|
|
0435bc776e | ||
|
|
916c8c69ef | ||
|
|
e43b0ad725 | ||
|
|
1a09a73c91 | ||
|
|
ac8b46b077 | ||
|
|
b7192ea52b | ||
|
|
0cefcd282b | ||
|
|
28f32ea3d0 | ||
|
|
e71bffcf5d | ||
|
|
ee91dbec8a | ||
|
|
f459873574 | ||
|
|
eb28976704 | ||
|
|
af3cb7027a | ||
|
|
261530eb10 | ||
|
|
5a1d568e8e |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
*.mus
|
*.mus
|
||||||
.idea/*
|
.idea/*
|
||||||
|
venv/
|
||||||
|
|||||||
15
Pipfile
Normal file
15
Pipfile
Normal 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
180
Pipfile.lock
generated
Normal 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
2
os_requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
You have to have installed `PortAudio` in order to use app.
|
||||||
|
|
||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
|
||||||
|
|||||||
@@ -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)))
|
|
||||||
|
|
||||||
@@ -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"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
|
||||||
|
|||||||
@@ -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)
|
|
||||||
@@ -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)
|
|
||||||
@@ -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
17
smnp/ast/node/throw.py
Normal 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)
|
||||||
@@ -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
36
smnp/ast/node/unit.py
Normal 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)
|
||||||
|
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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}]"
|
||||||
|
|
||||||
|
|||||||
0
smnp/cli/__init__.py
Normal file
0
smnp/cli/__init__.py
Normal file
23
smnp/cli/parser.py
Normal file
23
smnp/cli/parser.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import argparse
|
||||||
|
|
||||||
|
VERSION = "0.1"
|
||||||
|
DESCRIPTION = """
|
||||||
|
Simple Music Notation Processor is a command line tool enabling you to do some music stuff using custom domain-specific language.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class CliParser(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.parser = argparse.ArgumentParser(description=DESCRIPTION)
|
||||||
|
self.parser.add_argument('file', nargs='*', help='a file containing SMNP code')
|
||||||
|
self.parser.add_argument('-c', '--code', action='append', default=[], type=str, help='a string with SMNP code')
|
||||||
|
self.parser.add_argument('-m', '--mic', action='store_true', help='test microphone level')
|
||||||
|
self.parser.add_argument('-C', '--config', type=argparse.FileType('-r'), help='a file containing settings (not implemented yet)')
|
||||||
|
self.parser.add_argument('-p', '--params', action='append', help='pass arguments to program (not implemented yet)')
|
||||||
|
self.parser.add_argument('-v', '--version', action='version')
|
||||||
|
self.parser.add_argument('--tokens', action='store_true', help='print tokens of parsed code')
|
||||||
|
self.parser.add_argument('--ast', action='store_true', help='print abstract syntax tree of parsed code')
|
||||||
|
self.parser.add_argument('--dry-run', action='store_true', help='don\'t execute passed code')
|
||||||
|
self.parser.version = VERSION
|
||||||
|
|
||||||
|
def parse(self):
|
||||||
|
return self.parser.parse_args()
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
12
smnp/error/custom.py
Normal 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 ""
|
||||||
@@ -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])
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
|
||||||
|
|
||||||
|
|||||||
31
smnp/main.py
31
smnp/main.py
@@ -1,17 +1,38 @@
|
|||||||
import sys
|
from smnp.cli.parser import CliParser
|
||||||
|
|
||||||
from smnp.error.base import SmnpException
|
from smnp.error.base import SmnpException
|
||||||
|
from smnp.library.loader import loadStandardLibrary
|
||||||
|
from smnp.module.mic.lib.detector.noise import NoiseDetector
|
||||||
from smnp.program.interpreter import Interpreter
|
from smnp.program.interpreter import Interpreter
|
||||||
|
|
||||||
|
|
||||||
|
def interpretFile(args, file):
|
||||||
|
stdLibraryEnv = loadStandardLibrary() if not args.dry_run else None
|
||||||
|
Interpreter.interpretFile(file, printTokens=args.tokens, printAst=args.ast, execute=not args.dry_run, baseEnvironment=stdLibraryEnv)
|
||||||
|
|
||||||
|
|
||||||
|
def interpretString(args, string):
|
||||||
|
stdLibraryEnv = loadStandardLibrary() if not args.dry_run else None
|
||||||
|
Interpreter.interpretString(string, printTokens=args.tokens, printAst=args.ast, execute=not args.dry_run, baseEnvironment=stdLibraryEnv, source='<cli>')
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
try:
|
try:
|
||||||
#stdLibraryEnv = loadStandardLibrary()
|
parser = CliParser()
|
||||||
Interpreter.interpretFile(sys.argv[1], printTokens=True, printAst=True, execute=False, baseEnvironment=None)
|
args = parser.parse()
|
||||||
#draft()
|
|
||||||
|
if args.mic:
|
||||||
|
nd = NoiseDetector()
|
||||||
|
nd.test()
|
||||||
|
|
||||||
|
for code in args.code:
|
||||||
|
interpretString(args, code)
|
||||||
|
|
||||||
|
for file in args.file:
|
||||||
|
interpretFile(args, file)
|
||||||
|
|
||||||
except SmnpException as e:
|
except SmnpException as e:
|
||||||
print(e.message())
|
print(e.message())
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("Program interrupted")
|
print("Program interrupted")
|
||||||
|
|
||||||
|
|||||||
@@ -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 ]
|
||||||
4
smnp/module/float/__init__.py
Normal file
4
smnp/module/float/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
from smnp.module.float.function import float
|
||||||
|
|
||||||
|
functions = [ float.function ]
|
||||||
|
methods = []
|
||||||
0
smnp/module/float/function/__init__.py
Normal file
0
smnp/module/float/function/__init__.py
Normal file
26
smnp/module/float/function/float.py
Normal file
26
smnp/module/float/function/float.py
Normal 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),
|
||||||
|
)
|
||||||
4
smnp/module/integer/__init__.py
Normal file
4
smnp/module/integer/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
from smnp.module.integer.function import integer
|
||||||
|
|
||||||
|
functions = [ integer.function ]
|
||||||
|
methods = []
|
||||||
0
smnp/module/integer/function/__init__.py
Normal file
0
smnp/module/integer/function/__init__.py
Normal file
25
smnp/module/integer/function/integer.py
Normal file
25
smnp/module/integer/function/integer.py
Normal 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),
|
||||||
|
)
|
||||||
@@ -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 ]
|
||||||
@@ -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')
|
|
||||||
|
|
||||||
@@ -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')
|
|
||||||
@@ -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),
|
|
||||||
)
|
|
||||||
@@ -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 = []
|
||||||
@@ -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')
|
|
||||||
@@ -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)
|
|
||||||
)
|
|
||||||
11
smnp/module/note/function/note.py
Normal file
11
smnp/module/note/function/note.py
Normal 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')
|
||||||
@@ -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')
|
|
||||||
@@ -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),
|
|
||||||
)
|
|
||||||
@@ -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)
|
|
||||||
)
|
|
||||||
@@ -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)
|
|
||||||
)
|
|
||||||
@@ -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 ]
|
||||||
@@ -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')
|
|
||||||
10
smnp/module/string/function/stringify.py
Normal file
10
smnp/module/string/function/stringify.py
Normal 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')
|
||||||
@@ -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 = []
|
||||||
157
smnp/module/synth/function/compile.py
Normal file
157
smnp/module/synth/function/compile.py
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
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
|
||||||
|
DEFAULT_TUNING = 440
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
def getTuning(config):
|
||||||
|
key = Type.string("tuning")
|
||||||
|
if key in config.value:
|
||||||
|
tuning = config.value[key]
|
||||||
|
if not tuning.type in [Type.INTEGER, Type.FLOAT] or tuning.value < 0:
|
||||||
|
raise RuntimeException("The 'tuning' property must be non-negative integer or float", None)
|
||||||
|
|
||||||
|
return tuning.value
|
||||||
|
|
||||||
|
return DEFAULT_TUNING
|
||||||
|
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
def __init__(self, bpm, overtones, decay, attack, tuning):
|
||||||
|
self.bpm = bpm
|
||||||
|
self.overtones = overtones
|
||||||
|
self.decay = decay
|
||||||
|
self.attack = attack
|
||||||
|
self.tuning = tuning
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def default():
|
||||||
|
return Config(DEFAULT_BPM, DEFAULT_OVERTONES, DEFAULT_DECAY, DEFAULT_ATTACK, DEFAULT_TUNING)
|
||||||
|
|
||||||
|
|
||||||
|
_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)
|
||||||
|
tuning = getTuning(config)
|
||||||
|
|
||||||
|
return compilePolyphony(rawNotes, Config(bpm, overtones, decay, attack, tuning))
|
||||||
|
|
||||||
|
|
||||||
|
_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)
|
||||||
|
tuning = getTuning(config)
|
||||||
|
|
||||||
|
return compilePolyphony([ notes ], Config(bpm, overtones, decay, attack, tuning))
|
||||||
|
|
||||||
|
|
||||||
|
function = CombinedFunction(
|
||||||
|
'wave',
|
||||||
|
Function(_signature1, _function1),
|
||||||
|
Function(_signature2, _function2),
|
||||||
|
Function(_signature3, _function3),
|
||||||
|
Function(_signature4, _function4),
|
||||||
|
)
|
||||||
@@ -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')
|
||||||
13
smnp/module/synth/function/plot.py
Normal file
13
smnp/module/synth/function/plot.py
Normal 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')
|
||||||
@@ -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)
|
||||||
)
|
)
|
||||||
@@ -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)
|
|
||||||
|
|
||||||
@@ -1,12 +1,80 @@
|
|||||||
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.note.pitch import Tuning
|
||||||
|
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):
|
||||||
|
tuning = Tuning(config.tuning)
|
||||||
|
compiledLines = [1 / len(notes) * compileNotes(line, config, tuning) 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, tuning):
|
||||||
|
dispatcher = {
|
||||||
|
Type.NOTE: lambda note, overtones: sineForNote(note.value, config, tuning),
|
||||||
|
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, tuning):
|
||||||
|
frequency = note.toFrequency(tuning)
|
||||||
|
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))
|
||||||
|
|||||||
@@ -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 = []
|
||||||
@@ -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')
|
||||||
10
smnp/module/system/function/displayln.py
Normal file
10
smnp/module/system/function/displayln.py
Normal 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')
|
||||||
@@ -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):
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -16,8 +16,8 @@ class Note:
|
|||||||
self.duration = duration
|
self.duration = duration
|
||||||
self.dot = dot
|
self.dot = dot
|
||||||
|
|
||||||
def toFrequency(self):
|
def toFrequency(self, tuning):
|
||||||
return self.note.toFrequency() * 2 ** self.octave
|
return tuning[self.note] * 2 ** self.octave
|
||||||
|
|
||||||
def transpose(self, interval):
|
def transpose(self, interval):
|
||||||
origIntRepr = self._intRepr()
|
origIntRepr = self._intRepr()
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from enum import Enum
|
|||||||
|
|
||||||
from smnp.error.note import NoteException
|
from smnp.error.note import NoteException
|
||||||
|
|
||||||
|
_semitone = 2**(1/12)
|
||||||
|
|
||||||
class NotePitch(Enum):
|
class NotePitch(Enum):
|
||||||
C = 0
|
C = 0
|
||||||
@@ -17,21 +18,21 @@ class NotePitch(Enum):
|
|||||||
AIS = 10
|
AIS = 10
|
||||||
H = 11
|
H = 11
|
||||||
|
|
||||||
def toFrequency(self):
|
# def toFrequency(self):
|
||||||
return {
|
# return {
|
||||||
NotePitch.C: 16.35,
|
# NotePitch.C: 16.35,
|
||||||
NotePitch.CIS: 17.32,
|
# NotePitch.CIS: 17.32,
|
||||||
NotePitch.D: 18.35,
|
# NotePitch.D: 18.35,
|
||||||
NotePitch.DIS: 19.45,
|
# NotePitch.DIS: 19.45,
|
||||||
NotePitch.E: 20.60,
|
# NotePitch.E: 20.60,
|
||||||
NotePitch.F: 21.83,
|
# NotePitch.F: 21.83,
|
||||||
NotePitch.FIS: 23.12,
|
# NotePitch.FIS: 23.12,
|
||||||
NotePitch.G: 24.50,
|
# NotePitch.G: 24.50,
|
||||||
NotePitch.GIS: 25.96,
|
# NotePitch.GIS: 25.96,
|
||||||
NotePitch.A: 27.50,
|
# NotePitch.A: 27.50,
|
||||||
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
|
||||||
@@ -41,10 +42,22 @@ class NotePitch(Enum):
|
|||||||
|
|
||||||
@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")
|
||||||
|
|
||||||
|
|
||||||
|
class Tuning(object):
|
||||||
|
def __init__(self, a4=440):
|
||||||
|
self.tuning = { value: a4/_semitone**(57-i) for i, value in enumerate(NotePitch.__members__) }
|
||||||
|
|
||||||
|
def __getitem__(self, item):
|
||||||
|
return self.tuning[str(item)]
|
||||||
|
|
||||||
|
|
||||||
stringToPitch = {
|
stringToPitch = {
|
||||||
'cb': NotePitch.H,
|
'cb': NotePitch.H,
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
99
smnp/runtime/evaluators/atom.py
Normal file
99
smnp/runtime/evaluators/atom.py
Normal 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
|
||||||
@@ -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):
|
||||||
|
|||||||
35
smnp/runtime/evaluators/condition.py
Normal file
35
smnp/runtime/evaluators/condition.py
Normal 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)
|
||||||
@@ -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
|
||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
9
smnp/runtime/evaluators/float.py
Normal file
9
smnp/runtime/evaluators/float.py
Normal 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)
|
||||||
@@ -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
|
||||||
@@ -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)
|
||||||
21
smnp/runtime/evaluators/logic.py
Normal file
21
smnp/runtime/evaluators/logic.py
Normal 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)
|
||||||
121
smnp/runtime/evaluators/loop.py
Normal file
121
smnp/runtime/evaluators/loop.py
Normal 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
|
||||||
@@ -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)
|
|
||||||
35
smnp/runtime/evaluators/minus.py
Normal file
35
smnp/runtime/evaluators/minus.py
Normal 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])
|
||||||
16
smnp/runtime/evaluators/negation.py
Normal file
16
smnp/runtime/evaluators/negation.py
Normal 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)
|
||||||
21
smnp/runtime/evaluators/power.py
Normal file
21
smnp/runtime/evaluators/power.py
Normal 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))
|
||||||
45
smnp/runtime/evaluators/product.py
Normal file
45
smnp/runtime/evaluators/product.py
Normal 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)
|
||||||
|
|
||||||
52
smnp/runtime/evaluators/relation.py
Normal file
52
smnp/runtime/evaluators/relation.py
Normal 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)
|
||||||
|
|
||||||
75
smnp/runtime/evaluators/sum.py
Normal file
75
smnp/runtime/evaluators/sum.py
Normal 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)
|
||||||
17
smnp/runtime/evaluators/throw.py
Normal file
17
smnp/runtime/evaluators/throw.py
Normal 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)
|
||||||
34
smnp/runtime/evaluators/unit.py
Normal file
34
smnp/runtime/evaluators/unit.py
Normal 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)
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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)),
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user