{"id":17652190,"url":"https://github.com/impworks/lens","last_synced_at":"2025-04-15T22:51:31.362Z","repository":{"id":4711375,"uuid":"5859223","full_name":"impworks/lens","owner":"impworks","description":"Language for Embeddable .NET Scripting","archived":false,"fork":false,"pushed_at":"2020-09-21T17:01:11.000Z","size":5967,"stargazers_count":92,"open_issues_count":23,"forks_count":9,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-03-29T02:12:18.672Z","etag":null,"topics":["compiler","dotnet","dotnetcore","embeddable","language","scripting-language"],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/impworks.png","metadata":{"files":{"readme":"README.RU.md","changelog":null,"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":"2012-09-18T16:28:01.000Z","updated_at":"2024-11-29T12:38:57.000Z","dependencies_parsed_at":"2022-09-13T23:51:25.026Z","dependency_job_id":null,"html_url":"https://github.com/impworks/lens","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/impworks%2Flens","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/impworks%2Flens/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/impworks%2Flens/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/impworks%2Flens/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/impworks","download_url":"https://codeload.github.com/impworks/lens/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248881864,"owners_count":21176933,"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","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":["compiler","dotnet","dotnetcore","embeddable","language","scripting-language"],"created_at":"2024-10-23T11:46:08.387Z","updated_at":"2025-04-15T22:51:31.335Z","avatar_url":"https://github.com/impworks.png","language":"C#","readme":"# LENS\n\n## 1. Коротко о главном\n\n\n* Встраиваемый скриптовый язык\n* Платформа .NET\n* Функциональная парадигма, статическая типизация\n* Функции - объекты первого рода, алгебраические типы\n* Взаимодействие со сборками .NET\n\n## 2. Синтаксис и возможности\n\nБлоки выделяются отступами, выражения разделяются переводом строки.\nРазмер отступов не важен, однако разрешается использование только пробелов.\n\n### 2.1. Типы данных\n\nВ интерпретатор встроена поддержка следующих типов, аналогичных C#:\n\n* `unit` - он же `void`\n* `object`\n* `bool`\n* `int`\n* `long`\n* `float`\n* `double`\n* `decimal`\n* `string`\n* `char`\n\n### 2.2. Объявление констант и переменных\n\nИзменяемые переменные объявляются ключевым словом `var`, константы - `let`.\nЗначение констант не обязательно должно быть константой во время компиляции.\nКонстанты нельзя использовать слева от знака присваивания или передавать по ссылке.\n\n```\nvar a = 1\nlet b = 2\n```\n\nПри объявлении переменной обычно ей задается начальное значение. Если оно неизвестно,\nнеобходимо явно указать тип:\n\n```\nvar c, d: int\nс = 3\n```\n\n### 2.3. Операторы\n\nВ языке объявлены следующие операторы, перечисленные в порядке приоритета:\n\n1. Возведение в степень (`**`)\n2. Умножение (`*`), деление (`/`), получение остатка от деления (`%`)\n3. Сложение (`+`), вычитание (`-`)\n4. Сдвиг влево (`\u003c:`), сдвиг вправо (`:\u003e`), проверка на null (`??`)\n5. Сравнение (`==`, `\u003c\u003e`, `\u003c`, `\u003e`, `\u003c=`, `\u003e=`)\n6. Логические операции (`\u0026\u0026`, `||`, `^^`)\n7. Битовые операции (`\u0026`, `|`, `^`)\n\nОператор сложения также используется для конкатенации строк. Если у объекта есть переопределенный\nоператор, он будет использован.\n\n### 2.4. Записи\n\nЗапись - то же самое, что структура. Объект, имеющий только поля. Без методов\nи модификаторов доступа. Объявляется ключевым словом `record`:\n\n```csharp\nrecord Student\n    Name : string\n    Age : int\n```\n        \nВсе поля структуры являются публичными.\n\nСтруктуры могут быть рекурсивными, т.е. включать в себя элементы собственного\nтипа.\n\n### 2.5. Алгебраические типы\n\nОбъявляются ключевым словом `type` и перечислением возможных ярлыков типа. К\nкаждому ярлыку может быть прикреплена метка с помощью ключевого слова `of`:\n\n```csharp\ntype Suit\n    Hearts\n    Clubs\n    Spades\n    Diamonds\n\ntype Card\n    Ace of Suit\n    King of Suit\n    Queen of Suit\n    Jack of Suit\n    ValueCard of Tuple\u003cSuit, int\u003e\n```\n\nЯрлыки должны быть глобально уникальными идентификаторами в контексте скрипта,\nпоскольку они же являются статическими конструкторами:\n\n```csharp\nlet jack = Jack Hearts\nlet two = ValueCard new (Diamonds; 2)\n```\n\n### 2.6. Функции\n\nФункции объявляются в теле программы ключевым словом `fun`:\n\n```csharp\nfun negate:int (x:int) -\u003e -x\n\nfun hypo:int (a:int b:int) -\u003e\n    let sq1 = a * a\n    let sq2 = b * b\n    Math::Sqrt (sq1 + sq2)\n```\n        \nПосле названия функции идет ее тип, после - список параметров с типами в скобках.\n\nКаждая функция имеет свое пространство имен. Переменные, объявленные в глобальной\nобласти видимости, _не доступны_ внутри функций.\n\n#### 2.61. Аргументы функции\n\nПосле слова `fun` идет название функции и тип возвращаемого значения,\nа потом ее аргументы с указанием типа. Если у функции не объявлено\nни одного параметра, она будет принимать тип `unit` для вызова.\nЛитералом `unit` является выражение `()`.\n\nКлючевое слово `unit` является внутренним именованием типа. Его нельзя использовать\nдля описания типа аргумента функции, в качестве generic-параметра другого типа и в\nоператорах `default` и `typeof`.\n\n#### 2.6.2. Возвращаемое значение функции\n\nЛюбая функция должна возвращать значение. Возвращаемым значением является последнее\nвыражение тела функции. Если последнее выражение - управляющая конструкция или вызов\nфункции типа `void`, функция возвращает тип `unit`.\n\nЕсли функция не должна возвращать никакого значения, а ее последнее выражение не является\n`void`, следует использоаать литерал `()`.\n\n#### 2.6.3 Вызов функции\n\nФункция вызывается, когда ей передаются все требуемые параметры. Для того, чтобы\nвызвать функцию без параметров, ей нужно передать параметр типа `unit` - пара скобок `()`.\n\n```csharp\nfun sum:int (a:int b:int c:int) -\u003e a + b + c\nfun getTen:int -\u003e 10\n\nlet five = sum 1 1 3\nlet ten = getTen ()\n```\n\nПри вызове функции можно использовать только литералы и имена переменных. Любые более сложные\nвыражения должны быть взяты в скобки.\n\n```csharp\nfun sum:double (a:double b:double) -\u003e a + b\n\nlet sum = sqrt sin 1        // sqrt(sin, 1) - wtf?\nlet sum = sqrt (sin 1)      // компилируется\nlet someData = sum (sin 1) (cos 2)\n```\n    \n#### 2.6.4. Передача аргумента по ссылке\n\nАргумент в функцию можно передать по ссылке. Для этого как в объявлении, так и при вызове\nследует использовать модификатор `ref`:\n\n```csharp\nfun test:bool (str:ref string) -\u003e\n    if str.Length \u003e 100 then\n        str = str.Substring 0 100\n        true\n    else\n        false\n        \nvar a = \"hello world\"\nvar b = \"test\"\nprintln (test ref a) // true\nprintln (test ref b) // false\n```\n    \nПосле `ref` может использоваться:\n\n* Имя переменной, объявленной с помощью `var`\n* Имя аргумента текущей функции\n* Обращение к полю\n* Обращение к индексу массива\n \nНе может быть использовано:\n\n* Литерал, выражение или имя константы, объявленной с помощью `let`\n* Обращение к свойству\n* Обращение к индексу объекта с переопределенным индексатором\n\n#### 2.6.5. Анонимные функции\n\nАнонимные функции могут быть объявлены (практически) в любом месте программы.\nПомимо отсутствия имени они отличаются от именованных функций следующими моментами:\n\n1. Анонимная функция замыкает переменные и константы из внешней области видимости.\n2. Тип анонимной функции выводится автоматически, поскольку она не может быть рекурсивной.\n\nАнонимная функция может быть описана следующим образом:\n\n    let sum = (a:int b:int) -\u003e a + b\n    let getTen = -\u003e sum 5 5\n    let addFive = (a:int) -\u003e\n        let b = 5\n        sum a b // то же самое, что sum 5\n        \nКак видно из следующего примера, оператор `-\u003e` разделяет параметры функции и\nее тело. Даже если параметров нет, `-\u003e` все равно необходимо указывать.\n\nТипы аргументов анонимной функции можно не указывать, если они могут быть однозначно\nвыведены из места ее применения, например:\n\n* При передаче анонимной функции в качестве параметра метода или конструктора\n* При присвоении в поле, свойство, элемента массива или уже существующую переменную\n* При использовании оператора приведения типов\n* При использовании оператора композиции функций\n\n#### 2.6.6. Чистые функции и мемоизация\n\nПри объявлении именованной функции ее можно пометить модификатором `pure`. Это\nозначает, что при равных входных параметрах ее результат всегда будет\nодинаковым. В таком случае при первом вызове ее результат будет закеширован,\nа при повторных вызовах будет использоваться именно этот кеш, а сама функция\nне будет повторно вызвана.\n\nЧистота функции не проверяется компилятором. Фактическое наличие побочных\nэффектов остается на совести программиста.\n\n#### 2.6.7. Порядок объявления и вызова функций\n\nПорядок не играет роли. Рекурсивные вызовы допустимы без какого-либо явного\nуказания (например, в F# требуется модификатор `rec`), взаимная рекурсия\nтакже допустима.\n\n#### 2.6.8. Оператор передачи значения\n\nДля передачи значения в функцию может быть использован оператор `\u003c|`.\nОднако этот оператор будет полезен, если аргументы не умещаются на одной строке, или\nесли требуется передать многострочное выражение.\n\nОператор `\u003c|` требует увеличения отступа относительно выражения, к которому он применяется.\n\n```csharp\nsomefx\n    \u003c| value1\n    \u003c| (a b) -\u003e\n        let sum = a + b\n        sum * sum\n    \u003c| s -\u003e log s\n```\n        \n#### 2.6.9. Оператор передачи контекста\n\nДля вызова функций по цепочке, особенно со сложными аргументами, удобно использовать\nоператор передачи контекста, аналогичный точке. Он позволяет размещать длинное\nвыражение на нескольких строках.\n\n```csharp\nsomeData\n    |\u003e Where (a -\u003e a.Value \u003e 10)\n    |\u003e Select (a -\u003e a.Value ** 2)\n    |\u003e Sum ()\n```\n\n#### 2.6.10 Переменное число аргументов в функции\n\nМожно объявить функцию, которая будет принимать переменное число аргументов и\nупаковывать их в массив. Для этого необходимо указать модификатор типа с троточием\nу последнего аргумента:\n\n```csharp\nfun count:int (x:object...) -\u003e\n    x.Length\n    \nlet three = count 1 2 3\nlet five = count true \"test\" 1.3 3.7 three\n```\n\nКак и в C#, данный аргумент должен быть последним в списке.\n\n### 2.7. Ключевые слова и конструкции\n\n#### 2.7.1. Создание объектов\n\nНовые объекты создаются с помощью ключевого слова `new`:\n\n```csharp\nlet tuple = new Tuple\u003cstring, int\u003e \"hello\" 2\n```\n\n#### 2.7.2. Условие\n\nУсловие записывается с помощью блока if / else:\n    \n```csharp\nlet a = if 1 \u003e 2 then 3 else 4\n```\n\nВыражение может также использоваться по правую сторону от знака присваивания,\nесли указаны обе ветки (`if` и `else`). Если блок `else` не используется, конструкция\n`if` всегда возвращает тип `unit`.\n\n#### 2.7.3. Цикл\n\nЦикл записывается с помощью блока `while`:\n\n```csharp\nvar a = 0\nwhile a \u003c 10 do\n    Console::WriteLine \"{0} loop iteration\" a\n    a = a + 1\n```\n\nЦикл `while` всегда возвращает значение последнего выражения в теле цикла.\nЕсли цикл не был выполнен ни одного раза, будет возвращено выражение `default(T)`.\n\n#### 2.7.4. try-catch\n\nБлоки `try-catch` записываются следующим образом:\n\n```csharp\ntry\n    doSomethingHorrible()\ncatch ex:WebException\n    notify \"web exception\" ex.Message\ncatch ex:DivideByZeroException\n    notify \"whoops!\"\ncatch\n    notify \"something weird has happened\"\n```\n\nПосле блока `try` также может идти блок `finally`, который выполняется всегда\nпри выходе из области видимости, возникало ли исключение или нет:\n\n```csharp\ntry\n    doSomething ()\ncatch\n    notify \"something happened\"\nfinally\n    freeResources ()\n```\n\nБлок `try-catch` всегда возвращает `unit`.\n\n#### 2.7.5. use\n\nКлючевое слово `use` открывает пространство имен, добавляя объявленные в нем\nклассы в глобальное:\n\n```csharp\nuse System.Text.RegularExpressions\nlet rx = new Regex \"[a-z]{2}\"\n```\n\n#### 2.7.6. using\n\nКлючевое слово `using` позволяет объявить блок, которым ограничен интервал жизни\nресурса, реализуюшего интерфейс `IDisposable`:\n\n```csharp\nusing fs = (new FileStream \"file.txt\" FileMode::Create) do\n    fs.WriteByte 1\n```\n\n#### 2.7.7. Приведение и проверка типов\n\nДля приведения типов используется оператор `as`. В отличие от C#, он кидает\n`InvalidCastException` в случае неудачи, а не возвращает `null`. Может быть\nиспользован на любых типах, в том числе `int` / `string` / `bool` / `object`.\n\nДля проверки того, является ли объект экземпляром некоторого класса, используется\nоператор `is`. Он возвращает `bool`.\n\n### 2.8. Создание структур данных\n\nВ языке есть поддержка для упрощенного создания заранее инициализированных\nколлекций разного типа. Для этого используется специальный синтаксис оператора `new`.\n\nДанный синтаксис используется только в том случае, если количество элементов\nзаранее известно и оно отлично от нуля. Для объявления пустых структур данных\nследует пользоваться их классическими конструкторами. Объявить пустой массив можно\nс помощью `System.Array.CreateInstance(...)`. Возможно, следует добавить для этого\nслучая generic-метод.\n\nТип коллекции выводится автоматически из типов аргументов. Для этого в коллекции должен\nприсутствовать хотя бы один элемент, отличный от null.\n\nКлючи `Dictionary` проверяются более строго - они не могут иметь значение `null` и их тип\nдолжен совпадать в точности.\n\n#### 2.8.1. Массивы\n\n```csharp\n// int[]\nlet ints = new [1; 2; 3]\n```\n\n#### 2.8.2. Списки\n\n```csharp\n// System.Collections.Generic.List\u003cint\u003e\nlet ints = new [[1; 2; 3]]\n```\n\n#### 2.8.3 Словари\n\n```csharp\n// System.Collections.Generic.Dictionary\u003cstring, int\u003e\nlet dict = new { \"hello\" =\u003e 1; \"world\" =\u003e 2 }\n```\n\n#### 2.8.4 Кортежи\n\n```csharp\n// System.Tuple\u003cint, string, object\u003e\nlet t = new (1, \"hello world\", new object())\n```\n\nВ кортеже должно быть от 1 до 7 элементов. Кортежи неограниченной длины, возможно,\nбудут поддерживаться в следующей версии.\n\n### 2.9 Функциональные возможности\n\n#### 2.9.1 Приведение делегатов\n\nАнонимные функции по умолчанию являются выражениями типа `Func\u003c\u003e` или `Action\u003c\u003e`, в зависимости\nот того, возвращают ли они некое значение или их последнее выражение имеет тип `unit`.\n\nДля того, чтобы передать анонимную функцию в качестве параметра с иным типом, можно\nиспользовать приведение типов. Для этого типы принимаемых и возвращаемых значений должны\nв точности соответствовать:\n\n```csharp\nlet filter = (x:int) -\u003e x % 2 == 0\nlet data = (Enumerable::Range 1 100).ToArray ()\nlet even = Array::FindAll data (filter as Predicate\u003cint\u003e)\n```\n\n#### 2.9.2 Частичное применение\n\nНа основе одной функции можно создать другую, передав ей часть параметров, а вместо недостающих\nуказав специальный идентификатор `_`. Результирующая функция будет принимать оставшиеся параметры:\n\n```csharp\nfun add:int (x:int y:int) -\u003e x + y\nlet add2 = add 2 _\nlet alsoAdd2 = add _ 2\n\nlet three = add2 1 // int(3)\n```\n\nПри наличии перегруженных вариантов функции явно указанные аргументы должны однозначно\nидентифицировать функцию, в противном случае возникнет ошибка неоднозначности:\n\n```csharp\nfun repeat:str (value:int    count:int) -\u003e string::Join \"\" (new [value] * count)\nfun repeat:str (value:string count:int) -\u003e string::Join \"\" (new [value] * count)\n\nlet repeat2 = repeat _ 2 // error: both functions match\n```\n\nЧастичное применение работает как с функциями, так и с конструкторами.\n\n#### 2.9.3 Композиция функций\n\nС помощью оператора композиции можно создавать новые функции из существующих, используя\nрезультат одной функции в качестве аргумента для другой:\n\n```csharp\nlet parse = (x:string) -\u003e Convert::ToInt32 x\nlet inc = (x:int) -\u003e x + 1\n\nlet compound = parse :\u003e inc\nprintln (compound \"2\") // 3\n```\n\nФункция справа от оператора `:\u003e` должна иметь строго 1 параметр, совпадающий с типом\nвозвращаемого значения функции слева. В этом случае удобно использовать частичное применение:\n\n```csharp\nlet add = (x:int y:int) -\u003e x + y\nlet compound = parse :\u003e add _ 1\n```\n\n### 2.10. Сопоставление с образцом\n\nСопоставление с образцом позволяет разбирать произвольные структуры данных и извлекать из\nних необходимые значения, наподобие того, как регулярные выражения работают со строками.\nДля описания списка шаблонов используется блок `match`:\n\n```csharp\nmatch x with\n    case 1 then \"one\"\n    case 2 then \"two\"\n    case _ then \"other number\"\n```\n\nПравила применяются последовательно, пока не найдется удовлетворяющее - тогда будет возвращено\nвыражение результата, указанное после `then`. Возвращаемым типом является наиболее близкий общий тип,\nподходящий ко всем указанным выражениям результата. Если ни одно правило не подошло, будет\nвозвращено значение по умолчанию для данного типа (`default T`).\n\n#### 2.10.1. Типы правил\n\n##### 2.10.1.1. Литерал\n\nВ качестве образца можно использовать литералы встроенных типов - `int`, `string`, `bool` и т.д.\nТакже допустим литерал `null`.\n\n##### 2.10.1.2. Захват имени\n\nЕсли в качестве образца указан идентификатор, значение сохраняется в переменную с таким названием,\nкоторая может быть использована в дополнительных проверках и при возвращении результата.\n\nИдентификатор `_` (одно нижнее подчеркивание) не сохраняет значение и может быть использован\nнесколько раз.\n\nПосле идентификатора можно явно указать тип: тогда правило совпадет только в том случае, если объект\nявляется экземпляром данного типа.\n\n```csharp\nmatch getException () with\n    case ex:ArgumentException then \"Invalid arg\"\n    case ex:DivideByZeroException then \"Division by zero\"\n    case _ then \"Something went wrong\"\n```\n\n##### 2.10.1.3. Диапазон\n\nЧисловое значение можно проверить на принадлежность к диапазону: `case 1..5`.\nОбе границы диапазона включаются.\n\n##### 2.10.1.4. Кортежи\n\nКортежи можно разбить на индивидуальные значения, к каждому из которых применяется свое\nвложенное правило:\n\n```csharp\nlet tuple = new (1; 2; \"test\"; \"bla\")\nmatch tuple with\n    case (x; y; str; _) then fmt \"{0} = {1}\" str (x + y) // test = 3\n```\n\n##### 2.10.1.5. Массивы и последовательности\n\nМассивы (`T[]`), списки (`List\u003cT\u003e`) и последовательности (`IEnumerable\u003cT\u003e`) можно разбить\nна элементы:\n\n```csharp\nmatch array with\n    case [] then \"empty\"\n    case [x] then fmt \"one item: {0}\" x\n    case [x; y] then fmt \"two items: {0} and {1}\" x y\n    case [_; _; _] then \"three items\"\n    case _ then \"more than 3 items\"\n```\n\nК одному из идентификаторов можно применить префикс-многоточие. Тогда этот идентификатор\nзахватит не один элемент, а вложенную последовательность из нуля и более элементов:\n\n```\nfun length:int (array:object[]) -\u003e\n    match array with\n        case [] then 0\n        case [_] then 1\n        case [_; ...x] then 1 + (length x)\n```\n\nДля массива и `IList\u003cT\u003e` подмножество может быть любым элементом, тип подмножества - `T[]`.\nДля остальных случаем - только последний элемент, тип - `IEnumerable\u003cT\u003e`.\n\n##### 2.10.1.6. Записи\n\nДля объявленных в скрипте структур можно применить вложенные правила для каждого поля:\n\n```\nrecord Point\n    X : int\n    Y : int\n\nfun describe:string (pt:Point) -\u003e\n    match pt with\n        case Point(X = 0; Y = 0) then \"Zero\"\n        case Point(X = 0) | Point(Y = 0) then \"half-zero\"\n        case _ then \"Just a point\"\n```\n\nПоля, для которых проверки не указаны, могут иметь любые значения.\n\n##### 2.10.1.7. Алгебраические типы\n\nДля объявленных в скрипте типов можно проверить значение ярлыка:\n\n```\ntype Expr\n    IntExpr of int\n    StringExpr of string\n    AddExpr of Tuple\u003cExpr, Expr\u003e\n    SubExpr of Tuple\u003cExpr, Expr\u003e\n\nfun describe:string (expr:Expr) -\u003e\n    match expr with\n        case IntExpr of x then fmt \"Int({0})\" x\n        case StringExpr of x then fmt \"Str({0})\" x\n        case AddExpr of (x; y) then fmt \"{0} + {1}\" (describe x) (describe y)\n        case SubExpr of (x; y) then fmt \"{0} - {1}\" (describe x) (describe y)\n```\n\nДля типов без ярлыка следует использовать явное указание типа (см. 2.10.1.1).\n\n##### 2.10.1.8. KeyValue\n\nДля элементов словаря можно использовать особый синтаксис: `case key =\u003e value`.\n\n##### 2.10.1.9. Регулярные выражения\n\nСтроку можно сопоставить с регулярным выражением:\n\n```\nmatch \"String\" with\n    case #^[a-z]+$# then \"lower\"\n    case #^[A-Z]+$# then \"upper\"\n    case #^[a-z]+$#i then \"mix\"\n```\n\nДопустимы следующующие модификаторы в любом порядке:\n\n* `i = RegexOptions.IgnoreCase`\n* `m = RegexOptions.Multiline`\n* `s = RegexOptions.Singleline`\n* `c = RegexOptions.CultureInvariant`\n\nИменованные группы автоматически извлекаются в одноименные переменные:\n\n```\nmatch \"My name is John\" with\n    case #^My name is (?\u003cname\u003e\\w+)$#i then fmt \"Hello, {0}\" name\n```\n\nПо умолчанию, тип извлеченных переменных - `string`. Для удобства значения можно\nавтоматически сконвертировать в любой тип `T`, если для него объявлен статический\nметод: `bool T.TryParse(string value, out T result)`. Для этого тип указывается\nчерез двоеточие после имени группы. Если метод `TryParse` возвращает `false`,\nправило не применяется.\n\n```\nmatch \"I have 2 cookies\" with\n    case #^I have (?\u003ccount:int\u003e\\d+) cookies$# then fmt \"Twice as much will be {0}\" (count * 2)\n\n// Result: \"Twice as much will be 4\"\n```\n\n#### 2.10.2. Альтернативные правила\n\nМожно указать несколько правил, разделенных вертикальной чертой - тогда\nдостаточно совпасть хотя бы одному из них:\n\n```\nmatch number with\n    case 1 | 2 | 3 then \"one, two or three\"\n    case _ then \"other number\"\n```\n\nЕсли хотя бы одно правило захватывает какое-либо имя, такое же имя с таким же типом\nдолжно быть захвачено во всех альтернативных правилах. Порядок захвата не важен.\nИменованные группы в регулярных выражениях учитываются, а специальное имя `_` - нет.\n\n#### 2.10.3 Проверки `when`\n\nКаждое выражение `case` может содержать дополнительную проверку - выражение, которое\nдолжно вернуть `true`, чтобы правило совпало. Для этого применяется ключевое слово `when`:\n\n```\nmatch x with\n    case y when y % 2 == 0 then \"even\"\n    case _ then \"odd\"\n```\n\n## 3. Встраиваемость\n\nТехнически, интерпретатор реализован в виде сборки .NET, которую\nпрограммист может подключить к своей программе, чтобы добавить в нее\nподдержку скриптового языка.\n\nСборка содержит основной класс интерпретатора. Схема работы программиста с\nинтерпретатором следующая:\n\n1. Добавить в проект ссылку на сборки LENS\n2. Создать объект интерпретатора\n3. Зарегистрировать в интерпретаторе свои типы, функции и свойства\n4. Передать интерпретатору текст исполняемой программы\n\nРезультатом является объект типа `Func\u003cobject\u003e`, позволяющий исполнять скрипт многократно\nбез необходимости перекомпиляции.\n  \nПримерный код этого взаимодействия на языке C# представлен ниже:\n\n```csharp\npublic void Run()\n{\n    var source = \"a = 1 + 2\";\n    var a = 0;\n\n    var compiler = new LensCompiler();\n    compiler.RegisterProperty(\"a\", () =\u003e a, newA =\u003e a = newA);\n    \n    try\n    {\n        var fx = compiler.Compile(source);\n        fx();\n\n        Console.WriteLine(\"Success: {0}\", a);\n    }\n    catch (LensCompilerException ex)\n    {\n        Console.WriteLine(\"Error: {0}\", ex.FullMessage);\n    }\n}\n```\n\n## 4. Дополнительные возможности\n\n* Поддержка переопределенных операторов\n* Раскрутка констант во время компиляции\n* Сохранение сгенерированной сборки в виде исполняемого файла\n* Возможность отключать поиск extension-методов для ускорения компиляции\n\n## 5. Ограничения\n\n### 5.1. Планы на будущее\n\nСписок планируемых возможностей для следующих версий доступен в виде задач на Github:\nhttps://github.com/impworks/lens/issues\n\n### 5.2. Сознательные ограничения\n\nПоскольку LENS является встраиваемым языком, в нем не будет вещей, присущих\nклассическим языкам программирования, как то:\n\n* Создание полноценных классов с методами\n* Модификаторы доступа\n* Объявление интерфейсов\n* Управляющие конструкции, прерывающие поток выполнения: `return`, `break`, `continue`\n","funding_links":[],"categories":["Uncategorized"],"sub_categories":["Uncategorized"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fimpworks%2Flens","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fimpworks%2Flens","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fimpworks%2Flens/lists"}