{"id":20182314,"url":"https://github.com/ywangd/midle","last_synced_at":"2026-02-17T01:01:29.127Z","repository":{"id":18978890,"uuid":"22200101","full_name":"ywangd/midle","owner":"ywangd","description":"Mini IDL Evaluator for IDL statements and expressions","archived":false,"fork":false,"pushed_at":"2014-10-21T13:33:07.000Z","size":340,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-05T19:54:19.977Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"IDL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ywangd.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-07-24T05:23:47.000Z","updated_at":"2018-11-03T16:43:06.000Z","dependencies_parsed_at":"2022-09-17T01:23:31.909Z","dependency_job_id":null,"html_url":"https://github.com/ywangd/midle","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/ywangd/midle","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ywangd%2Fmidle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ywangd%2Fmidle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ywangd%2Fmidle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ywangd%2Fmidle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ywangd","download_url":"https://codeload.github.com/ywangd/midle/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ywangd%2Fmidle/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29528233,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-17T00:57:22.232Z","status":"ssl_error","status_checked_at":"2026-02-17T00:54:25.811Z","response_time":115,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-14T02:38:14.627Z","updated_at":"2026-02-17T01:01:29.013Z","avatar_url":"https://github.com/ywangd.png","language":"IDL","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MIDLE - A Mini IDL Evaluator in IDL\nEvaluate IDL statements and expressions without `EXECUTE`, i.e. virtual machine\nsafe. It can be an alternative to `EXECUTE` in many cases.\n\nMIDLE implements its own parser and evaluates IDL statements and expressions\nwithout resorting to the power of `EXECUTE`. It even adds [additional language\nfeatures](#added-features) such as syntax for HASH and LIST literals, higher\nlevel array concatenation, bettering support for chaining function/method calls\nand subscripts. \n\nMIDLE is however not without limitations. Some limitations are due to the limit\nof IDL language itself, notably output arguments and object property access\n(object method calls are OK). Others are deliberately set by design to meet the\nscope of MIDLE, notably procedure/function creation and limited support of\nprogram control constructs. Please refer to [Missing Features](#missing-features) \nsection for details.\n\nMIDLE requires IDL 8.0 or up (8.3 is recommended).\n\n## Motivation\nThe original motivation came from the wish to use a file containing a list of\nIDL assignments as a configuration/input file to IDL packages. This, however,\nindicates `EXECUTE` has to be used to process the file, which is not ideal due\nto the fact that the IDL virtual machine does not allow `EXECUTE`.\n\nMIDLE is designed to fill the gap. It initially focused only on the\nright-hand-side of the `=` sign, i.e. the expression and was later decided to\nsupport full assignment statements as well as procedure calls. It now has a\nbroader scope than what it was originated from. For an example, it is possible \nto use MIDLE as a simple scripting engine to GUI applications running in IDL\nvirtual machine. This opens the possibility to script the application using IDL\nscripts. It can also exposes some powers of the underlying IDL language to end\nusers who do not own IDL licenses.\n\nIt is also worth to note that MIDLE is not envisioned to be fast or memory\nefficient (at least not now). For situations where `EXECUTE` is needed, you\nare hardly looking for performance anyway (if you are, you may want to\nre-think the design of the program). As a semi-replacement of `EXECUTE`, MIDLE is\nmeant to be flexible and it is pretty good at its own job. In addition, MIDLE\ndoes not re-compile any routines, which means it could be faster than `EXECUTE`\nin some situations.\n\n## Installation\n1. Get the code from the git repo:\n\n    ```Bash\n    $ git clone git@github.com:ywangd/midle.git\n    ```\n\n2. Add the `src` folder to your IDL path. This can be done by either setup the\n   `IDL_PATH` environment variable, or use the `preference` menu in IDE, or some\n   other ways your prefer.\n\nIDL 8.0 or up is required as 8.0 features (e.g. `!NULL`, `Hash`, `List`, `isa`)\nare used extensively. Version 8.3 is recommended for the new `Dictionary` class.\nA drop-in replacement of the `Dictionary` class is provided with the package so\nMIDLE can run with 8.0, 8.1 and 8.2. If you have access to IDL 8.3, remove\n`dictionary.pro` to ensure the built-in `Dictionary` class is used.\n\n## Usage\nThe basic usage is to simply pass a string of IDL statement or expression to\n`midle`. Here is the classic Hello World example:\n```IDL\nprint, midle('\"Hello, World!\"')\n```\nA procedure interface is also available if the return value is of no interest.\n```IDL\nmidle, 'print, \"Hello, World!\"'\n```\n\nAn array of strings can be passed to MIDLE and they will be evaluated in order:\n```IDL\nmidle, ['print, \"STAR\"', 'print, \"WARS\"']\n```\n\nMultiple statements can be written in one line and separated by `\u0026`:\n```IDL\nmidle, 'print, \"STAR\" \u0026 print, \"WARS\"'\n```\n\n\nMIDLE also takes a filename and evaluates its content line by line. If `input1`\ncontains following content:\n```IDL\n; This line is a comment\nprint, 'A long time ago ', $\n    'in a galaxy far, far away ...'\nprint, 'STAR'  ; trailing comments \nprint, 'WARS'\n```\n\nNote that comments and line continuation are both permitted. To evaluate the\nfile, specify the filename and set the `file` keyword.\n```IDL\nmidle, 'input1', /file\n```\n\nAn input file can optionally contain other input files by using the `@`\ndirective. For an example, the content of file `input2` is as follows:\n```IDL\nprint, 'before input1'\n@input1\nprint, 'after input1'\n```\n\nThe file inclusion can be nested and MIDLE will recursively expand all file\ninclusions. The file to be included can be given as relative path as shown above\nor as a full qualified path such as `@/home/user/input1`. If file name or path\ncontains whitespace, double or single quotes can be used as `@\"/home/user/input\n1\"`. Note that expansion of included files is done in a pre-process step and is\nnot a function of the parser. This also means the `@` directive can only be used\nin file input.\n\n\nA second argument can be used to pass values to variables in the expression.\nThis argument, called `env` variable for the rest of the document, is the\nruntime environment for variable lookup. It can be either a structure or a hash\nlike object. \n```IDL\nenv = {num: 50}\nprint, midle('indgen(2,3,4, start=num)', env)\n\nenv = hash('x', 42)\nprint, midle('indgen(x)', env)\n```\n\nProcedure call is supported. Note that a procedure call always returns the\n`!NULL` value if the function version of MIDLE is used.\n```IDL\nprint, midle('plot, indgen(50, start=100), /ynozero')\n```\nAlternatively, the procedure version can be used:\n```IDL\nmidle, 'plot, indgen(50, start=100), /ynozero'\n```\n\n\nAlmost all arithmetic, logical, bitwise, relational, matrix, ternary operators\nare supported. The exceptions are compound operators such as `++`, `+=` and\npointer de-reference `*pointer`.\n```IDL\nprint, midle('-2.2 - 2 mod ((42. + 22) ^ 2 \u003e 3 - 4.2) * 2.2 / 2.4')\n\nenv = {x: 42}\nprint, midle('x eq 42 ? indgen(5, start=x) : indgen(5)', env)\n```\n\nAssignments are supported. Note that all variable creation and modification is\ndone against the `env` variable, not to the scope where MIDLE is called (unlike\n`EXECUTE`). \nFor an example, the following code creates a variable `x` in the `env` variable,\nif the `env` variable itself is not defined, it will be created by MIDLE.\n```IDL\nprint, midle('x = 42', env)\nprint, env.x  ; output 42\n\nprint, midle('h = Hash()', env)\nprint, midle('h[\"a\"] = indgen(3,4,5)', env)\nprint, midle('h[\"a\", 0, 1, 2] = 420', env)\nprint, (env.a)[0,1,2]  ; output 420\n```\n\nMIDLE has limited support of program control constructs.\n```IDL\nmidle, 'a = 1 \u0026 if a eq 1 then print, \"True\" else print, \"False\"'\nmidle, 'for i = 0, 9 do print, i'\nmidle, 'foreach i, indgen(10) do print, i'\n```\n\nCompound statements are also supported.\n```IDL\nmidle, ['if counter gt 0 then begin', $\n        '    for i = 1, counter do begin', $\n        '        if i mod 7 eq 0 then continue', $\n        '        if i mod 2 eq 0 then print, \"Even\", i else print, \"Odd\", i', $\n        '        if i gt 40 then break', $\n        '    endfor', $\n        'endif else begin', $\n        '    print, \"no loop required\"', $\n        'endelse'], $\n        {counter: 42}\n```\n\nList literal is written by list the items inside a pair of parenthesis:\n```IDL\nprint, midle('(\"this\", \"is\", \"a\", \"list\", \"literal\", 42)')\n```\n\nHash literal is written in a way similar to IDL structure, but prefixing a\nletter `h` before the left curly bracket. In addition, the keys must be either\nstring or number.\n```IDL\nprint, midle('h{\"x\": 42, \"y\": 22, \"description\": \"This is a hash literal\"}')\n```\n\nNested list and hash:\n```IDL\nprint, midle('(4.2d3, \"String\", \"A Hash next\", h{\"x\": 4.2, \"y\": 2.2})')\n```\n\nHigher level array concatenation:\n```IDL\nenv = {a: indgen(6,5,4,3,2), b: indgen(6,5,4,3,2, start=720)}\nhelp, midle('[ [[[[a]]]], [[[[b]]]] ]', env)  ; concatenate on the 5th dimension\n```\n\nFunction/method calls and subscripts can be chained without the need of extra\npairs of parenthesis (as expected in other modern languages): \n```IDL\nprint, midle('indgen(10)[0:*:2]')\nprint, midle('list(indgen(3,4,5,6)[*,0:3:2,4,*][2,*,0,0:5:2], /extract).count()')\n```\n\nSubscripts and dot notations can be chained for the left-hand-side variable of\nassignment statements as well. This allows direct assignment to list items\nwhere the list itself is inside an array:\n```IDL\nmidle, 'b = [(1,2,3), (4,5,6), h{}, (7,8,9)]', env\nmidle, 'b[1][1] = 42', env\nmidle, 'b[2][\"st\"] = {x: indgen(3,4,5), y: 42, z: indgen(3,5)}', env\nmidle, 'b[2][\"st\"].x[1,2,3] = 99', env\n```\n\nThe `ast` output keyword can be used to obtain the Abstract Syntax Tree object\nof the given statements/expressions.\n```IDL\nprint, midle('-2.2 - 2 mod 42. + 22 ^ 2 \u003e 3 - 4.2 * 2.2 / 2.4', ast=ast)\n```\n\nYou can print the `ast` object (`print, ast`) to get a hierarchical view of the\nsyntax tree.\n```\nSTMTLIST\n  +-- BINOP 'T_SUB'\n        +-- BINOP 'T_MAX'\n              +-- BINOP 'T_ADD'\n                    +-- BINOP 'T_SUB'\n                          +-- UNARYOP 'T_SUB'\n                                +-- NUMBER '2.2'\n                          +-- BINOP 'T_MOD'\n                                +-- NUMBER '2'\n                                +-- NUMBER '42.'\n                    +-- BINOP 'T_EXP'\n                          +-- NUMBER '22'\n                          +-- NUMBER '2'\n              +-- NUMBER '3'\n        +-- BINOP 'T_DIV'\n              +-- BINOP 'T_MUL'\n                    +-- NUMBER '4.2'\n                    +-- NUMBER '2.2'\n              +-- NUMBER '2.4'\n```\n\nYou can also evaluate the `ast` object to get the result again.\n```IDL\nprint, ast.eval()\n```\n\nThe `error` output keyword can be used to check whether the call to MIDLE is\nsuccessful. It will be `!NULL` if the call succeeds. Or it will be a string\ncontaining the error message.\n```IDL\nprint, midle('42 + a', error=error)  ; varaible a is undefined\n```\nThe content of `error` is now `MIDLE_RUNTIME_ERR - Undefined variable: a`.\n\nA GUI is also provided to facilitate the use of MIDLE as a script engine. It is\nenvisioned that a host GUI application would call the MIDLE GUI to provide a\nconsole for editing/running IDL scripts that controls the host application. A\nsample usage is something like the follows:\n```IDL\nmidlegui, env=HOST_ENV, group_leader=HOST_APP_ID\n```\n\nAn demo of such an application can be found inside the `demo` folder. \n- Startup the demo\n- Click to access the MIDLE console\n- File -\u003e Open to load `script_01` inside the `demo` folder\n- Run -\u003e Run (or press **F5**) to watch the boat sails while the sun is setting.\n- An animated GIF file, `demo.gif`, is also saved.\n\n## \u003ca name=\"missing-features\"\u003e\u003c/a\u003eMissing Features\nThe missing features fall into two broad categories. The first category is *By\nDesign* which means they are deliberately left out to narrow the scope of MIDLE\nso it can focus on more important tasks. The second category is a result of\nIDL's own limitations and cannot be technically circumvented (please let me\nknow if there are ways to add them). \n\n### By Design\n- Limited support for program control constructs. Currently only support \n  `IF...THEN...ELSE`, `FOR`, `FOREACH`, `CONTINUE`, `BREAK`.\n- No support for routine definitions, i.e. `PRO`, `FUNCTION` (`EXECUTE` does not\n  support them either).\n- Hex and Oct literals are not supported\n- Only anonymous structure is supported. This means no named structure, no\n  inheritance.\n- Compound operators are not supported, i.e. `++`, `--`, `+=`, `-=`, etc.\n- Pointer de-reference is not supported, i.e. `*pointer`\n- The `-\u003e` operator is not supported as most times it can be replaced by `.`\n- Creation and assignment of system variables are not supported (their values\n  are readable)\n- Parenthesis over assignment statement are not supported, i.e. `x = (y = 42)`\n- Variable name cannot have the `$` character\n\n### By IDL's limitations\n- Output positional and keyword arguments are not supported (I really wanted to\n  support these. But it is just impossible in IDL.)\n- Object property access using dot notation, i.e. `object.property` are not\n  supported\n    * The dot notation can be used with Hash like object to get the value by\n      using the property's name as the key, i.e. `someHash.x` is equal to\n      `someHash['x']`\n- Up to nine positional arguments and unlimited input keyword arguments are\n  supported\n- Cannot obtain values for user defined system variables (built-in system\n  variables are OK)\n\n\n## \u003ca name=\"added-features\"\u003e\u003c/a\u003eAdded Features\nValid MIDLE expressions are mostly valid IDL expression as well. However, there\nare a few exceptions due to MIDLE's added features. User can easily choose to\nnot use them or use parenthesis to enforce IDL's default behaviours. Many of the\nadded features are inspired by the [Python](https://www.python.org/) language.\n\n### List literal\nList literals can be written the same way as tuples in Python, i.e.\n`(42,\"xyz\")` is equal to `list(42,\"xyz\")`. Note a trailing comma is needed to\ncreate a single element list, i.e. `(42,)` creates a single-element list, while\n`(42)` is just a scalar number 42. A pair of empty parenthesis, `()`, creates an\nempty list.\n\n### Hash literal\nHash literals can be written using a variant of structure literal by prefixing a\nletter `h` to the left curly bracket, i.e. `h{}` creates an empty Hash and is\nequal to `hash()`. The keys to hash literals must be either string or number,\ni.e.  `h{\"x\": 4.2, 5: \"y\"}` is equal to `hash(\"x\", 4.2, 5, \"y\")`.\n\n### Higher level of array concatenation\nIDL only supports up to three level of brackets for performing array\nconcatenation while an array can have up to eight dimensions. This means `[\n[[a]], [[b]] ]` is legal in IDL for concatenating array on the third dimension,\nwhile `[ [[[a]]], [[[b]]] ]` yields an error while trying concatenating on the\nfourth dimension. \n\nThis limitation is lifted in MIDLE. Up to eight level of brackets is now\nsupported. MIDLE delegates the array concatenation task to an utility program\ncalled `arrayconcat`, which can also be used as standalone program.\n\n### Better support for chaining function/method calls and subscripts\nParenthesis are often required in IDL if you want to chain a few calls and\nsubscripts. For an example, say we have a hash variable `h` as `hash('x',3,\n'y',5,'z',3)`, the expression `h.where(3)[0]` is illegal in IDL. To chain the\nmethod call and subscript, an extra pair of parenthesis has to be used like\n`(h.where(3))[0]`. Now let's try `h.where(3).count()`. Apparently it is illegal\nin IDL. However even after adding extra pair of parenthesis,\n`(h.where(3)).count()`, the expression is still invalid (error message is\n`Subscripts are not allowed with object properties`).\n\nWith MIDLE, it is legal to write chaining calls and subscripts without the\neye-irritating parenthesis. It is more in line with other modern languages like\nPython.\n\n### Associativity of the power operator\nThe power operator, `^`, has right associativity in MIDLE, while it has left\nassociativity in IDL. This means an expression of `2^3^2` equals to `2^(3^2)`\nin MIDLE, but equals to `(2^3)^2` in IDL. \nI think it is more common for this operator to have right associativity, as in\nFORTRAN and Python. If you don't like this behaviour, use parenthesis to enforce\nassociativity.\n\n## Documentation\n[IDLdoc](https://github.com/mgalloy/idldoc) is used for code level documentation\n(not much yet). The documentation can be generated by running `midledoc` in the\n`docs` folder (`mg_src_root` from [mglib](https://github.com/mgalloy/mglib) is\nrequired).\n\n## Testing\n[mgunit](https://github.com/mgalloy/mgunit) is used for testing. Add the `test`\nfolder in your IDL path and run `mgunit, 'midle_ut'` (`mg_src_root` is required\nas well). The unit test file has some more example ussages of MIDLE.\n\n\n## Known issues\n* File inclusion leads to incorrect line number for error report. \n\n## Contributing\n- Check any open issues or open a new issue to start discussions about your\n  ideas of features and/or bugs\n- Fork the repository, make changes, and send pull requests\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fywangd%2Fmidle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fywangd%2Fmidle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fywangd%2Fmidle/lists"}