{"id":19645892,"url":"https://github.com/vvaltchev/mylang","last_synced_at":"2025-10-30T03:38:34.919Z","repository":{"id":49277647,"uuid":"317324357","full_name":"vvaltchev/mylang","owner":"vvaltchev","description":"A simple programming language inspired by Python, JavaScript and C","archived":false,"fork":false,"pushed_at":"2024-06-04T05:07:45.000Z","size":487,"stargazers_count":35,"open_issues_count":0,"forks_count":6,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-28T15:39:26.558Z","etag":null,"topics":["const-evaluation","cplusplus-17","educational-programming-language","interpreter","lambdas","python-like"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vvaltchev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-11-30T19:26:51.000Z","updated_at":"2025-04-03T08:30:27.000Z","dependencies_parsed_at":"2024-11-11T14:38:12.271Z","dependency_job_id":"36d042db-7910-48ee-bdae-ae43e00b5701","html_url":"https://github.com/vvaltchev/mylang","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/vvaltchev/mylang","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vvaltchev%2Fmylang","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vvaltchev%2Fmylang/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vvaltchev%2Fmylang/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vvaltchev%2Fmylang/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vvaltchev","download_url":"https://codeload.github.com/vvaltchev/mylang/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vvaltchev%2Fmylang/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281739021,"owners_count":26553092,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-30T02:00:06.501Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["const-evaluation","cplusplus-17","educational-programming-language","interpreter","lambdas","python-like"],"created_at":"2024-11-11T14:35:47.074Z","updated_at":"2025-10-30T03:38:34.902Z","avatar_url":"https://github.com/vvaltchev.png","language":"C++","readme":"# MyLang\n\n![Linux](https://github.com/vvaltchev/mylang/workflows/Linux/badge.svg)\n![Windows](https://github.com/vvaltchev/mylang/workflows/Windows/badge.svg)\n![MacOSX](https://github.com/vvaltchev/mylang/workflows/MacOSX/badge.svg)\n![Coverage](https://github.com/vvaltchev/mylang/workflows/Coverage/badge.svg)\n[![codecov](https://codecov.io/gh/vvaltchev/mylang/branch/master/graph/badge.svg?token=B3L5Z6T6PR)](https://codecov.io/gh/vvaltchev/mylang)\n\n## What is MyLang?\n\nMyLang is a simple educational programming language inspired by `Python`,\n`JavaScript`, and `C`, written as a personal challenge, in a short time,\nmostly to have fun writing a *recursive descent parser* and explore the\nworld of interpreters. Don't expect a full-blown scripting language with\nlibraries and frameworks ready for production use. However, `MyLang` has\na minimal set of builtins and, it *could* be used for practical purposes\nas well.\n\n## Contents\n\n  * [Maintainance](#maintainance)\n    * [Building MyLang](#building-mylang)\n    * [Testing MyLang](#testing-mylang)\n  * [Syntax](#syntax)\n    * [Core concepts](#core-concepts)\n    * [Declaring variables](#declaring-variables)\n    * [Declaring constants](#declaring-constants)\n    * [Type system](#type-system)\n    * [Conditional statements](#conditional-statements)\n      - [Const evaluation](#const-evaluation-of-conditional-statements)\n    * [Classic loop statements](#classic-loop-statements)\n    * [The foreach loop](#the-foreach-loop)\n      - [The \"indexed\" keyword](#extra-features-the-indexed-keyword)\n      - [Array expansion](#extra-features-array-expansion)\n    * [Functions and lambdas](#functions-and-lambdas)\n      - [Lambda captures](#lambda-captures)\n      - [Calling functions during const-evaluation](#calling-functions-during-const-evaluation)\n      - [Pure functions](#pure-functions)\n    * [Exceptions](#exceptions)\n      - [Custom exceptions](#custom-exceptions)\n      - [Re-throwing an exception](#re-throwing-an-exception)\n      - [The finally clause](#the-finally-clause)\n  * [Builtins](#builtins)\n    * [Const builtins](#const-builtins)\n      - [General builtins](#general-builtins)\n      - [Array builtins](#array-builtins)\n      - [Dictionary builtins](#dictionary-builtins)\n      - [String builtins](#string-builtins)\n      - [Generic-container builtins](#generic-container-builtins)\n      - [Numeric builtins](#numeric-builtins)\n      - [Numeric constants](#numeric-constants)\n    * [Non-const builtins](#non-const-builtins)\n      - [Misc builtins](#non-const-misc-builtins)\n      - [Array builtins](#non-const-array-builtins)\n      - [Generic-container builtins](#non-const-generic-container-builtins)\n      - [Numeric builtins](#non-const-numeric-builtins)\n      - [I/O builtins](#non-const-io-builtins)\n\n\n## Maintainance\n\n### Building MyLang\n\nMyLang is written in *portable* C++17: at the moment, the project has no\ndependencies other than the standard C++ library. To build it, if you have\n`GNU make` installed, just run:\n\n```\n$ make -j\n```\n\nOtherwise, just pass all the .cpp files to your compiler and add the `src/`\ndirectory to the include search path. One of the nicest things about *not*\nhaving dependecies is that there's no need for a *build system* for one-time\nbuilds.\n\n#### Out-of-tree builds\n\nJust pass the `BUILD_DIR` option to `make`:\n\n```\n$ make -j BUILD_DIR=other_build_directory\n```\n\n### Testing MyLang\n\nIf you want to run MyLang's tests as well, you need to just compile with TESTS=1\nand disable the optimizations with OPT=0, for a better debugging experience:\n\n```\n$ make -j TESTS=1 OPT=0\n```\n\nThen, run all the tests with:\n\n```\n$ ./build/mylang -rt\n```\n\nIt's worth noticing that, while test frameworks like [GoogleTest] and [Boost.Test]\nare infinitely much more powerful and flexible than the trivial test engine we have\nin `src/tests.cpp`, they are *external* dependencies. The less dependencies, the\nbetter, right? :-)\n\n[GoogleTest]: https://github.com/google/googletest\n[Boost.Test]: https://www.boost.org/doc/libs/1_75_0/libs/test/doc/html/index.html\n\n## Syntax\n\nThe shortest way to describe `MyLang` is: *a C-looking dynamic python-ish\nlanguage*. Probably, the fastest way to learn this language is to check\nout the scripts in the `samples/` directory while taking a look at the short\ndocumentation below.\n\n### Core concepts\n\n`MyLang` is a dynamic duck-typing language, like `Python`. If you know `Python`\nand you're willing to use `{ }` braces, you'll be automatically able to use it.\nNo surprises. Strings are immutable like in `Python`, arrays can be defined using\n`[ ]` like in `Python`, and dictionaries can be defined using `{ }`, as well.\nThe language also supports array-slices using the same `[start:end]` syntax used\nby `Python`.\n\nSaid that, `MyLang` differs from `Python` and other scripting languages in several\naspects:\n\n  - There's support for *parse-time* constants declared using `const`.\n\n  - All variables *must be* declared using `var`.\n\n  - Variables have a scope like in `C`. Shadowing is supported when a variable\n    is explicitly re-declared using `var`, in a nested block.\n\n  - All expression statements must end with `;` like in `C`, `C++`, and `Java`.\n\n  - The keywords `true` and `false` exist, but there's no `boolean` type. Like in C,\n    `0` is false, everything else is `true`. However, strings, arrays, and dictionaries\n    have a boolean value, exactly like in `Python` (e.g. an empty array is considered\n    `false`). The `true` builtin is just an alias for the integer `1`.\n\n  - The assignment operator `=` can be used like in `C`, inside expressions, but\n    there's no such thing as the comma operator, because of the array-expansion\n    feature.\n\n  - MyLang supports both the classic `for` loop and an explicit `foreach` loop.\n\n  - MyLang does not support custom types, at the moment. However, dictionaries\n    support a nice syntactic sugar: in addition to the main syntax `d[\"key\"]`,\n    for string keys the syntax `d.key` is supported as well.\n\n### Declaring variables\n\nVariables are always declared with `var` and live in the scope they've been declared\n(while being visible in nested scopes). For example:\n\n```C#\n# Variable declared in the global scope\nvar a = 42;\n\n{\n    var b = 12;\n    # Here we can see both `a` and `b`\n    print(a, b);\n}\n\n# But here we cannot see `b`.\n```\n\nIt's possible to declare multiple variables using the following familiar syntax:\n\n```C#\nvar a,b,c;\n```\n\nBut there's a caveat, probably the only \"surprising\" feature of `MyLang`:\ninitializing variables doesn't work like in C. Consider the following\nstatement:\n\n```C#\nvar a,b,c = 42;\n```\n\nIn this case, instead of just declaring `a` and `b` and initializing to `c` to 42,\nwe're initializing *all* the three variables to the value 42. To initialize each\nvariable to a different value, use the array-expansion syntax:\n\n```C#\nvar a,b,c = [1,2,3];\n```\n\n### Declaring constants\n\nConstants are declared in a similar way as variables *but* they cannot be shadowed\nin nested scopes. For example:\n\n```C#\nconst c = 42;\n\n{\n    # That's not allowed\n    const c = 1;\n\n    # That's not allowed as well\n    var c = 99;\n}\n```\n\nIn `MyLang` constants are evaluated at *parse-time*, in a similar fashion to `C++`'s\n*constexpr* declarations (but there we talk about *compile time*). While initializing\na `const`, any kind of literal can be used in addition to the whole set of *const*\nbuiltins. For example:\n\n```C#\nconst val = sum([1,2,3]);\nconst x = \"hello\" + \" world\" + \" \" + join([\"a\",\"b\",\"c\"], \",\");\n```\n\nTo understand how exactly a constant has been evaluated, run the interpreter\nwith the `-s` option, to dump the *abstract syntax tree* before running the script.\nFor the example above:\n\n```\n$ cat \u003e t\n    const val = sum([1,2,3]);\n    const x = \"hello\" + \" world\" + \" \" + join([\"a\",\"b\",\"c\"], \",\");\n$ ./build/mylang t\n$ ./build/mylang -s t\nSyntax tree\n--------------------------\nBlock(\n)\n--------------------------\n```\n\nSurprised? Well, constants other than arrays and dictionaries are not even\ninstantiated as variables. They just don't exist at *runtime*. Let's add a\nstatement using `x`:\n\n```\n$ cat \u003e\u003e t\n    print(x);\n$ cat t\n    const val = sum([1,2,3]);\n    const x = \"hello\" + \" world\" + \" \" + join([\"a\",\"b\",\"c\"], \",\");\n    print(x);\n$ ./build/mylang -s t\nSyntax tree\n--------------------------\nBlock(\n  CallExpr(\n    Id(\"print\")\n    ExprList(\n      \"hello world a,b,c\"\n    )\n  )\n)\n--------------------------\nhello world a,b,c\n```\n\nNow, everything should make sense. Almost the same thing happens with arrays\nand dictionaries with the exception that the latter ones are instanted as at\nruntime as well, in order to avoid having potentially huge literals everywhere.\nConsider the following example:\n\n```\n$ ./build/mylang -s -e 'const ar=range(4); const s=ar[2:]; print(ar, s, s[0]);'\nSyntax tree\n--------------------------\nBlock(\n  ConstDecl(\n    Id(\"ar\")\n    Op '='\n    LiteralArray(\n      Int(0)\n      Int(1)\n      Int(2)\n      Int(3)\n    )\n  )\n  ConstDecl(\n    Id(\"s\")\n    Op '='\n    LiteralArray(\n      Int(2)\n      Int(3)\n    )\n  )\n  CallExpr(\n    Id(\"print\")\n    ExprList(\n      Id(\"ar\")\n      Id(\"s\")\n      Int(2)\n    )\n  )\n)\n--------------------------\n[0, 1, 2, 3] [2, 3] 2\n```\n\nAs you can see, the *slice* operation has been evaluated at *parse-time* while\ninitializing the constant `s`, but both the arrays exist at runtime as well.\nThe subscript operations on const expressions, instead, are converted to\nliterals. That looks like a good trade-off for performance: small values like\nintegers, floats, and strings are converted to literals during the *const evaluation*,\nwhile arrays and dictionaries (potentially big) are left as read-only symbols at\nruntime, but still allowing some operations on them (like `[index]` and `len(arr)`)\nto be const-evaluated.\n\n### Type system\n\n`MyLang` supports, at the moment, only the following (builtin) types:\n\n  * **None**\n    The type of `none`, the equivalent of Python's `None`. Variables just declared\n    without having a value assigned to them, have value `none` (e.g. `var x;`).\n    The same applies to functions that don't have a return value. Also, it's\n    used as a special value by builtins like `find()`, in case of failure.\n\n  * **Integer**\n    A *signed* pointer-size integer (e.g. `3`).\n\n  * **Float**\n    A floating-point number (e.g. `1.23`). Internally, it's a long double.\n\n  * **String**\n    A string like \"hello\". Strings are immutable and support slices (e.g.\n    `s[3:5]` or `s[3:]` or `s[-2:]`, having the same meaning as in `Python`).\n\n  * **Array**\n    A mutable type for arrays and tuples (e.g. `[1,2,3]`). It can contain items\n    of different type and it supports writable slices. Array slices behave like\n    copies while, under the hood, they use copy-on-write techniques.\n\n  * **Dictionary**\n    Dictionaries are hash-maps defined using `Python`'s syntax: `{\"a\": 3, \"b\": 4}`.\n    Elements are accessed with the familiar syntax `d[\"key-string\"]` or `d[23]`, can\n    be looked-up with `find()` and deleted with `erase()`. At the moment, only strings,\n    integers, and floats can be used as keys of a dictionary. **Perks**: identifier-like\n    string-keys can be accessed also with the \"member of\" syntax: `d.key`.\n\n  * **Function**\n    Both standalone functions and lambdas have the same object type and can be passed\n    around like any other object (see below). But, only lambdas can have a capture list.\n    Regular functions cannot be executed during const-evaluation, while `pure` functions\n    can. Pure functions can only see consts and their arguments.\n\n  * **Exception**\n    The only type of objects that can be thrown. To create them, use the `exception()`\n    builtin or its shortcut `ex()`.\n\n### Conditional statements\n\nConditional statements work exactly like in `C`. The syntax is:\n\n```C#\nif (conditionExpr) {\n    # Then block\n} else {\n    # Else block\n}\n```\n\nAnd the `{ }` braces can be omitted like in `C`, in the case of single-statement\nblocks. `conditionExpr` can be any expression, for example: `(a=3)+b \u003e= c \u0026\u0026 !d`.\n\n### Const evaluation of conditional statements\n\nWhen `conditionExpr` is an expression that can be const-evaluated, the whole\nif-statement is replaced by the true-branch, while the false-branch is\ndiscarded. For example, consider the following script:\n\n```C#\nconst a = 3;\nconst b = 4;\n\nif (a \u003c b) {\n    print(\"yes\");\n} else {\n    print(\"no\");\n}\n```\n\nNot only it always prints \"yes\", but it does not even need to check anything before\ndoing that. Check the abstract syntax tree:\n\n```\n$ ./build/mylang -s t\nSyntax tree\n--------------------------\nBlock(\n  Block(\n    CallExpr(\n      Id(\"print\")\n      ExprList(\n        \"yes\"\n      )\n    )\n  )\n)\n--------------------------\nyes\n```\n\n### Classic loop statements\n\n`MyLang` supports the classic `while` and `for` loops.\n\n```C#\nwhile (condition) {\n\n    # body\n\n    if (something)\n        break;\n\n    if (something_else)\n        continue;\n}\n\nfor (var i = 0; i \u003c 10; i += 1) {\n\n    # body\n\n    if (something)\n        break;\n\n    if (something_else)\n        continue;\n}\n```\n\nHere, the `{ }` braces can be omitted as in the case above. There are only\na few difference from `C` worth pointing out:\n\n  - The `++` and `--` operators do not exist in `MyLang`, at the moment.\n\n  - To declare multiple variables, use the syntax: `var a, b = [3,4];` or\n    just `var a,b,c,d = 0;` if you want all the variables to have the same\n    initial value.\n\n  - To increase the value of multiple variables use the syntax:\n    `a, b += [1, 2]`. In the extremely rare and complex cases when in the\n    *increment* statement of the for-loop we need to assign to each variable\n    a new variable using different expressions, take advantage of the\n    expansion syntax in assignment: `i, j = [i+2, my_next(i, j*3)]`.\n\n### The foreach loop\n\n`MyLang` supports `foreach` loops using a pretty familiar syntax:\n\n```C#\nvar arr = [1,2,3];\n\nforeach (var e in arr) {\n    print(\"elem:\", e);\n}\n```\n\nForeach loops can be used for arrays, strings, and dictionaries.\nFor example, iterating through each `\u003ckey, value\u003e` pair in a dictionary\nis easy as:\n\n```C#\nvar d = { \"a\": 3, \"b\": 10, \"c\": 42 };\n\nforeach (var k, v in d) {\n    print(k + \" =\u003e \" + str(v));\n}\n```\n\nTo iterate only through each key, just use `var k in d` instead.\n\n#### Extra features: the \"indexed\" keyword\n\n`MyLang` supports enumeration in foreach loops as well. Check the following\nexample:\n\n```C#\n\nvar arr = [\"a\", \"b\", \"c\"];\n\nforeach (var i, elem in indexed arr) {\n    print(\"elem[\"+str(i)+\"] = \"+elem);\n}\n```\nIn other words, when the name of the container is preceded by the keyword\n`indexed`, the first variable gets assigned a progressive number at each iteration.\n\n#### Extra features: array expansion\n\nWhile iterating through an array of small fixed-size arrays (think about tuples),\nit's possible to directly expand those \"tuples\" in the foreach loop:\n\n```C#\nvar arr = [\n    [ \"hello\", 42 ],\n    [ \"world\", 11 ]\n];\n\nforeach (var name, value in arr) {\n    print(name, value);\n}\n\n# This is a shortcut for:\n\nforeach (var elem in arr) {\n\n    # regular array expansion\n    var name, value = elem;\n    print(name, value);\n}\n\n# Which is a shortcut for:\n\nforeach (var elem in arr) {\n\n    var name = elem[0];\n    var value = elem[1];\n\n    print(name, value);\n}\n```\n\n### Functions and lambdas\n\nDeclaring a function is simple as:\n\n```C#\nfunc add(x, y) {\n  return x+y;\n}\n```\n\nBut several shortcuts are supported as well. For example, in the case of\nsingle-statement functions like the one above, the following syntax can be\nused:\n\n```C#\nfunc add(x, y) =\u003e x + y;\n```\n\nAlso, while it's a good practice to always write `()` for parameter-less\nfunctions, they're actually optional in this language:\n\n```C#\nfunc do_something { print(\"hello\"); }\n```\n\nFunctions are treated as regular symbols in `MyLang` and there're no substantial\ndifferences between standalone functions and lambdas in this language. For example,\nwe can declare the `add` function (above) as a lambda this way:\n\n```C#\nvar add = func (x, y) =\u003e x + y;\n```\n\nNote: when creating function objects in expressions, we're not allowed to assign\nthem a name.\n\n#### Lambda captures\n\nLambdas support a capture list as well, but implicit captures are not supported,\nin order to enforce clarity. Of course, lambdas can be returned as any other object.\nFor example:\n\n```C#\nfunc create_adder_func(val) =\u003e\n  func [val] (x) =\u003e x + val;\n\nvar f = create_adder_func(5);\n\nprint(f(1));  # Will print 6\nprint(f(10)); # Will print 15\n```\n\nLambdas with captures have a *state*, as anyone would expect.\nConsider the following script:\n\n```C#\nfunc gen_counter(val) =\u003e func [val] {\n    val += 1;\n    return val;\n};\n\nvar c1 = gen_counter(5);\n\nfor (var i = 0; i \u003c 3; i += 1)\n  print(\"c1:\", c1());\n\n# Clone the `c1` lambda object as `c2`: now it will have\n# its own state, indipendent from `c1`.\nvar c2 = clone(c1);\nprint();\n\nfor (var i = 0; i \u003c 3; i += 1)\n  print(\"c2:\", c2());\n\nprint();\n\nfor (var i = 0; i \u003c 3; i += 1)\n  print(\"c1:\", c1());\n```\n\nIt generates the output:\n\n```\nc1: 6\nc1: 7\nc1: 8\n\nc2: 9\nc2: 10\nc2: 11\n\nc1: 9\nc1: 10\nc1: 11\n```\n\n### Calling functions during const-evaluation\n\nRegular user-defined function objects (including lambdas) are not considered\n`const` and, therefore, *cannot* be run during const-evaluation. That's a pretty\nstrong limitation. Consider the following example:\n\n```C#\nconst people = [\n  [\"jack\", 3],\n  [\"alice\", 11],\n  [\"mario\", 42],\n  [\"bob\", 38]\n];\n\nconst sorted_people = sort(people, func(a, y) =\u003e a[0] \u003c b[0]);\n```\n\nIn this case, the script *cannot* create the `sorted_people` const array.\nBecause we passed a function object to the const `sort()` builtin, we'll\nget an `ExpressionIsNotConstEx` error. Sure, if `sorted_people` were\ndeclared as `var`, the script would run, but the array won't be const anymore\nand, we won't be able to benefit from any *parse-time* optimization. Therefore,\nwhile the `sort()` builtin can be called during const-evaluation, when it has a\ncustom `compare func` parameter, that's not possible anymore.\n\n### Pure functions\n\nTo overcome the just-described limitation, `MyLang` has a special syntax for\n*pure* functions. When a function is declared with the `pure` keyword preceding\n`func`, the interpreter treats it in a special way: it *can* be called anytime,\n*both* during const evaluation and during runtime *but* the function cannot\nsee global variables, nor capture anything: it can only use constants and the\nvalue of its parameters: that's exactly what we need during const evaluation.\nFor example, to generate `sorted_people` during const evaluation it's enough to\nwrite:\n\n```C#\nconst sorted_people = sort(people, pure func(a, b) =\u003e a[0] \u003c b[0]);\n```\n\nPure functions can be defined as standalone functions and *can* be used with\nnon-const parameters as well. Therefore, if a function can be declared as `pure`,\nit should always be declared that way. For example, consider the following\nscript:\n\n```C#\npure func add2(x) =\u003e x + 2;\n\nvar non_const = 25;\n\nprint(add2(non_const));\nprint(add2(5));\n```\n\nThe abstract syntax tree that language's engine will use at runtime will be:\n\n```\n$ ./build/mylang -s t\nSyntax tree\n--------------------------\nBlock(\n  FuncDeclStmt(\n    Id(\"add2\")\n    \u003cNoCaptures\u003e\n    IdList(\n      Id(\"x\")\n    )\n    Expr04(\n      Id(\"x\")\n      Op '+'\n      Int(2)\n    )\n  )\n  VarDecl(\n    Id(\"non_const\")\n    Op '='\n    Int(25)\n  )\n  CallExpr(\n    Id(\"print\")\n    ExprList(\n      CallExpr(\n        Id(\"add2\")\n        ExprList(\n          Id(\"non_const\")\n        )\n      )\n    )\n  )\n  CallExpr(\n    Id(\"print\")\n    ExprList(\n      Int(7)\n    )\n  )\n)\n--------------------------\n27\n7\n```\n\nAs you can see, in the first case an actual function call happens because\n`non_const` is not a constant, while in the second case it's AS IF we passed\na literal integer to `print()`.\n\n### Exceptions\n\nLike for other constructs, `MyLang` has an exception handling similar to\n`Python`'s, but using a syntax similar to `C++`. The basic construct is\nthe `try-catch` statement. Let's see an example:\n\n```C#\ntry {\n\n    var input_str = \"blah\";\n    var a = int(input_str);\n\n} catch (TypeErrorEx) {\n    print(\"Cannot convert the string to integer\");\n}\n```\n\nNote: if an exception is generated by constant expressions (e.g. `int(\"blah\")`),\nduring the const-evaluation, the error will be reported directly, bypassing any\nexception handling logic. The reason for that is to enforce *early failure*.\n\nMultiple `catch` statements are allowed as well:\n\n```C#\ntry {\n    # body\n} catch (TypeErrorEx) {\n    # error handling\n} catch (DivisionByZeroEx) {\n    # error handling\n}\n```\n\nAnd, in case several exceptions can be handled in with the same code,\na shorter syntax can be used as well:\n\n```C#\ntry {\n\n    # body\n\n} catch (TypeErrorEx, DivisionByZeroEx as e) {\n\n    # error handling\n    print(e);\n\n} catch (OutOfBoundsEx) {\n    # error handling\n}\n```\n\nExceptions might contain data, but none of the built-in exceptions\ncurrently do. The list of builtin *runtime* exceptions that can be\ncaught with `try-catch` blocks is:\n\n  * DivisionByZeroEx\n  * AssertionFailureEx\n  * NotLValueEx\n  * TypeErrorEx\n  * NotCallableEx\n  * OutOfBoundsEx\n  * CannotOpenFileEx\n\nOther exceptions like `SyntaxErrorEx` cannot be caught, instead.\nIt's also possible in `MyLang` to catch ANY exception use a catch-anything\nblock:\n\n```C#\ntry {\n\n    # body\n\n} catch {\n\n    # Something went wrong.\n}\n```\n\n### Custom exceptions\n\nThis language does not support custom types, at the moment. Therefore,\nit's not possible to *throw* any kind of object like in some other languages.\nTo throw an exception, it's necessary to use the special built-in function\n`exception()` or its shortcut, `ex()`. Consider the following example:\n\n```C#\ntry {\n\n    throw ex(\"MyError\", 1234);\n\n} catch (MyError as e) {\n\n    print(\"Got MyError, data:\", exdata(e));\n}\n```\n\nAs intuition suggests, with `ex()` we've created and later thrown an exception\nobject called `MyError`, having `1234` as payload data. Later, in the `catch` block,\nwe caught the exception and, we extracted the payload data using the `exdata()` builtin.\n\nIn case a given exception doesn't need to have a payload, it's possible to just\nsave the result of `ex()` in a variable and throw it later using a probably more\npleasant syntax:\n\n```C#\nvar MyError = ex(\"MyError\");\nthrow MyError;\n```\n\n### Re-throwing an exception\n\n`MyLang` supports re-throwing an exception in the body of catch statements\nusing the dedicated `rethrow` keyword:\n\n```C#\ntry {\n\n    do_something();\n\n} catch {\n\n    print(\"Something went wrong!!\");\n    rethrow;\n}\n```\n\n### The finally clause\n\nIn some cases, it might be necessary to do some clean-up, after executing\na block of code that might throw an exception. For these cases, `MyLang`\nsupports the well known `finally` clause, which works exactly as in `C#`:\n\n```C#\ntry {\n\n    step1_might_throw();\n    step2_might_throw();\n    step3_might_throw();\n    step4_might_throw();\n\n} catch (TypeErrorEx) {\n\n    # some error handling\n\n} finally {\n\n    # clean-up\n}\n```\n\nIt's worth noting that `try-finally` constructs (without any `catch` clause) are\nallowed as well.\n\n## Builtins\n\n### Const builtins\nThe following built-in functions will be evaluated during *parse-time* when\nconst arguments are passed to them.\n\n### General builtins\n\n#### `defined(symbol)`\nCheck if `symbol` is defined. Returns 1 if the symbol is defined, 0 otherwise.\n\n#### `len(container)`\nReturn the number of elements in the given container.\n\n#### `str(value, [decimal_digits])`\nConvert the given value to a string. If `value` is a float, the 2nd parameter\nindicates the desired number of decimal digits in the output string.\n\n#### `int(value)`\nConvert the given string to an integer. If the value is a float, it will be\ntrucated. If the value is a string, it will be parsed and converted to an integer,\nif possible. If the value is already an integer, it will be returned as-it-is.\n\n#### `float(value)`\nConvert the given value to float. If the value is an integer, it will be\nconverted to a floating-point number. If the value is a string, it will parsed\nand converted to float, if possible. If the value is already a float, it will be\nreturned as-it-is.\n\n#### `clone(obj)`\nClone the given object. Useful for non-trivial objects as arrays,\ndictionaries and lambda with captures.\n\n#### `type(value)`\nReturn the name of the type of the given value in string-form.\nUseful for debugging.\n\n#### `hash(value)`\nReturn the hash value used by dictionaries internally when `value` is used\nas a key. At the moment, only integers, floats and strings support `hash()`.\n\n### Array builtins\n\n#### `array(N)`\nReturn an array of `none` values with `N` elements.\n\n#### `top(array)`\nReturn the last element of the array. This is an alias for `array[-1]`.\nIt is useful when a given array is used as a stack, in combination with\nother builtins like `push()` and `pop()`.\n\n#### `range(n, [end, [step]])`\nWhen only one parameter is provided, it returns an array with numbers\nfrom 0 to `n`. When `end` is passed to the function, the array goes from\n`n` to `end-1`. When `step` is passed too, the array goes from `n` to\n`end-step` with each element being `step` bigger than the previous. `step`\ncan be negative as well. This is equivalent to `Python 2.x`'s range() function.\nIn `Python 3.x`, this is equivalent to: `list(range(...))`. Warning: while\nit might look pretty in foreach loops, that's typically not a good idea\nbecause it returns a whole array, not a generator object like in `Python 3.x`.\nTherefore, for small ranges is fine, but for larger ranges it's better to\nuse the classic for-loop.\n\n#### `sort(array, [compare_func])`\nSorts the given array *in-place* and returns the same array. Optionally,\nit supports a `compare_func` parameter: when passed, it's used to compare\nany two elements and it's supposed to return the logical value of `a \u003c b`.\n\nNote: while `sort()` works in-place, it still can be used to sort arrays\nwithout altering them and to sort const arrays as well: in the first case,\nit's possible by calling it as `sort(clone(arr))` and storing its return\nvalue to a new variable, while in the second case (const arrays), not even\n`clone()` is required: in case of const arrays, it will just sort and return\na clone of the given array.\n\n#### `rev_sort(array, [compare_func])`\nBehaves exactly like `sort()`, but sorts the array in descending order.\n\n#### `reverse(array)`\nReverse the given array in-place and returns it. Like `sort()`, if the given\nargument is const, it will be cloned before reversing. Therefore, it can be\nused during const-evaluation.\n\n#### `sum(array, [key_func])`\nApply the `+` operator sequentially to all elements in the given array and\nreturn the result. In case the optional argument `key_func` is passed to `sum()`,\nthe operator `+` is applied to the result of `key_func(elem)`, for each element\ninstead.\n\n### Dictionary builtins\n\n#### `keys(dictionary)`\nReturn an array containing all the keys of the given dictionary.\n\n#### `values(dictionary)`\nReturn an array containing all the values of the given dictionary.\n\n#### `kvpairs(dictionary)`\nReturn the contents of the given dictionary, in the form of an array of\n[key, value] arrays.\n\n#### `dict(array)`\nBuild a dictionary from an array of [key, value] arrays. This is the counter-part\nfunction of `kvpairs()`.\n\n### String builtins\n\n#### `split(string, delim)`\nSplit the given string by the given delimiter. Returns an array.\n\n#### `join(array_of_strings, delim)`\nJoin the given array of strings with the given delimiter. Returns a string.\n\n#### `ord(string)`\nReturn the numeric value of the given 1-char string.\nNote: in MyLang chars are 8-bit wide and there's no Unicode support,\nto keep this educational project smaller and simpler.\n\n#### `chr(num)`\nReturn a 1-char string containing the string representation of the given\nnumber in the range [0, 255]. Note: in MyLang chars are 8-bit wide and there's\nno Unicode support, to keep this educational project smaller and simpler.\n\n#### `splitlines(string)`\nSplit the given string, line by line. Returns an array. It's different\nfrom `split(string, \"\\n\")` because it handles multiple types of line ending\nsequences.\n\n#### `lpad(string, n, [char])`\nAdd left-padding to the given string to make it long `n` chars.\nIf len(string) \u003e= n, return the input string as it is. If a 3rd argument\nis passed to `lpad()`, it will be used as padding-character. By default,\nthe padding character is space.\n\n#### `rpad(string, n, [char])`\nThe counter-part of `lpad()`: pad the string on the right.\n\n#### `lstrip(string)`\nReturn a slice of the given string skipping any leading whitespace.\n\n#### `rstrip(string)`\nReturn a slice of the given string skipping any trailing whitespace.\n\n#### `strip(string)`\nReturn a slice of the given string skipping any leading or trailing\nwhitespace.\n\n#### `startswith(string, sub_string)`\nReturn true (1) if the given string starts with the given sub_string and\nfalse (0), otherwise.\n\n#### `endswith(string, sub_string)`\nReturn true (1) if the given string ends with the given sub_string and\nfalse (0), otherwise.\n\n\n#### Generic-container builtins\n\n#### `find(container, what, [key_func])`\nGeneric *find* function working with strings, arrays, and dictionaries.\nWhen `container` is a string, it returns the index of the first occurrence\nof the `what` substring in `container` or `none`.\n\nWhen `container` is an array, it returns the index of the first element equal\nto `what`. Also, when `container` is an array, a 3rd parameter (`key_func`) is\nsupported: it's a function object accepting a value (element of the array) and\nreturning the value that must be compared to `what`. It's useful when we're\nsearching something in an array of composite elements (e.g. tuples).\n\nWhen `container` is a dictionary, it returns the value associated with the\ngiven key (`what`) or `none` otherwise.\n\n#### `map(func, container)`\nMap each element in `container` through `func(elem)` and return the resulting\narray. For example, the following identity holds:\n\n```\nmap(func(x) =\u003e x+1, [1, 2, 3]) == [2, 3, 4]\n```\n\nIn case the container is a dictionary, `func` is required to accept two arguments,\na key and a value, but the result will still be an array. For example:\n\n```\nmap(func(k, v) =\u003e [k, v+1], {\"a\": 3, \"b\": 4}) == [[\"a\",4],[\"b\",5]]\n```\n\n#### `filter(func, container)`\nFilter the elements of `container` through `func(elem)` and return a container\nof the same type. For example:\n\n```\nfilter(func(x) =\u003e x \u003e 3, [1, 2, 3, 4, 5]) == [4, 5]\n```\n\nIn case the container is a dictionary, `func` is required to accept two parameters,\na key and a value, but the behavior will be semantically the same (a dictionary will\nbe returned).\n\n### Numeric builtins\n\n#### `abs(num)`\nReturn the absolute value of the given number.\n\n#### `min(a, b, [c, [...]])`\nReturn the smallest value among the ones passed to it.\n\n#### `min(array)`\nReturn the smallest value among the ones in the given array.\n\n#### `max(a, b, [c, [...]])`\nReturn the largest value among the ones passed to it.\n\n#### `max(array)`\nReturn the largest value among the ones in the given array.\n\n#### `exp(x)`\nReturn e^x.\n\n#### `exp2(x)`\nReturn 2^x.\n\n#### `log(x)`\nReturn the natural logarithm of `x`.\n\n#### `log2(x)`\nReturn the base-2 logarithm of `x`.\n\n#### `log10(x)`\nReturn the base-10 logarithm of `x`.\n\n#### `sqrt(x)`\nReturn the square root of `x`.\n\n#### `cbrt(x)`\nReturn the cube root of `x`.\n\n#### `pow(x, y)`\nReturn x^y.\n\n#### `sin(x)`\nReturn `sin(x)`.\n\n#### `cos(x)`\nReturn `cos(x)`.\n\n#### `tan(x)`\nReturn `tan(x)`.\n\n#### `asin(x)`\nReturn the arc sine of `x`.\n\n#### `acos(x)`\nReturn the arc cosine of `x`.\n\n#### `atan(x)`\nReturn the arc tangent of `x`.\n\n#### `ceil(x)`\nReturn the smallest integral value that is not less than `x`.\n\n#### `floor(x)`\nReturn the largest integral value that is not greater than `x`.\n\n#### `trunc(x)`\nReturn the rounded integer value of `x` as float.\n\n#### `isinf(x)`\nReturn true if `x` is `inf` or `-inf`.\n\n#### `isfinite(x)`\nReturn true if `x` is a finite value.\n\n#### `isnormal(x)`\nReturn true if `x` is a normal floating-point number.\n\n#### `isnan(x)`\nReturn true if `x` is \"Not a Number\".\n\n#### `round(x, [precision])`\nRound `x` to the nearest integer or to a floating-point number with\n`precision` digits.\n\n### Numeric constants\n\n Constant name         | Value\n-----------------------|-------------------------\n math_e                | Euler's number\n math_log2e            | log2(e)\n math_log10e           | log10(e)\n math_ln2              | log(2)\n math_ln10             | log(10)\n math_pi               | pi\n math_pi2              | pi/2\n math_pi4              | pi/4\n math_1_pi             | 1/pi\n math_2_pi             | 2/pi\n math_2_sqrt_pi        | 2/sqrt(pi)\n math_sqrt2            | sqrt(2)\n math_1_sqrt2          | 1/sqrt(2)\n nan                   | Not a Number\n inf                   | Infinity\n eps                   | Floating-point's epsilon\n\n\n### Non-const builtins\nThe following built-in functions will *not* be evaluated during *parse-time*,\nno matter if const arguments are passed to them or not.\n\n### Non-const misc builtins\n\n#### `assert(expr)`\nCheck `expr` and throw AssertionFailureEx if it's false.\n\n#### `exit(code)`\nExit the program with the given numeric code\n\n#### `intptr(symbol)`\nGet the internal shared object pointer referred by `symbol`.\nIt's currently used in tests to check if two array slices refer internally\nto the same object.\n\n#### `undef(symbol)`\nUndefine the given symbol from the current scope. Return true if the given\nsymbol was actually defined in the given scope.\n\n#### `exception(type_string, [payload_data])`\nCreate an exception object having the given type plus the optional\npayload data.\n\n#### `ex(type_string, [data])`\nA shortcut for `exception()`.\n\n#### `exdata(ex)`\nGet the payload data from the given exception object.\n\n### Non-const array builtins\n\n#### `append(array, value)`\nAppend `value` to the given array.\n\n#### `push(array, value)`\nAn alias for `append()`. Useful for symmetry when used with `pop()`.\n\n#### `pop(array)`\nPop (and return) the last element from the given array.\n\n### Non-const generic-container builtins\n\n#### `erase(container, key_or_index)`\nErase the element at the given index or the element indexed by the given key\nfrom the given container (array or dictionary). Return true (1) if the key\nexisted. For arrays, it always returns true or throws `OutOfBoundsEx` in\ncase of an invalid index.\n\n#### `insert(container, key_or_index, value)`\nInsert the given value in the given container at the given index or key,\ndepending on the type of the container. As `erase()`, it returns true if\nthe insertion was successful, in the case `container` is a dictionary.\nIn the case `container` was an array, always return true or throws\n`OutOfBoundsEx`.\n\n### Non-const numeric builtins\n\n#### `rand(a, b)`\nGenerate a random integer in the range [a, b].\n\n#### `randf(a, b)`\nGenerate a random floating-point number in the range [a, b].\n\n### Non-const I/O builtins\n\n#### `print(a, [b, [c, [...]]])`\nWrite to the standard output the string-versions of the given\narguments, separated by a single space and terminated by a line ending.\n\n#### `readln()`\nRead a single line from the standard input.\n\n#### `writeln(string)`\nWrite the given string to the standard output, plus a line ending\nsequence.\n\n#### `read([filename])`\nIf no arguments were given to `read()`, it returns the whole data in the\nstandard input. Otherwise, read the whole given file and returns it as a\nstring.\n\n#### `write(string, [filename])`\nWrite the given string to the standard output as it is, without a line ending.\nIf the optional parameter `filename` is given, write the given string to the\ngiven file, as it is.\n\n#### `readlines([filename])`\nSimilar to `read()`, but read line by line and return an array.\n\n#### `writelines(array_of_strings, [filename])`\nSimilar to `write()`, but accept an array of strings and write them one\nper line.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvvaltchev%2Fmylang","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvvaltchev%2Fmylang","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvvaltchev%2Fmylang/lists"}