diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml index ea0b66d77..6dbadf7b4 100644 --- a/.github/workflows/sonar.yml +++ b/.github/workflows/sonar.yml @@ -11,7 +11,7 @@ jobs: if: (github.repository == 'EvilBeaver/OneScript' ) && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.event.repository.full_name) steps: - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: temurin @@ -19,14 +19,14 @@ jobs: with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Cache SonarQube packages - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~\sonar\cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - name: Cache SonarQube scanner id: cache-sonar-scanner - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: .\.sonar\scanner key: ${{ runner.os }}-sonar-scanner diff --git a/Build.csproj b/Build.csproj index d7273cda1..93eb134ab 100644 --- a/Build.csproj +++ b/Build.csproj @@ -1,27 +1,43 @@ - - - 1.1.0 + 2.0.0 + + $(VersionPrefix) + $(VersionPrefix)-$(VersionSuffix) + $(BUILD_NUMBER) 0 $(MSBuildProjectDirectory)/built - $(MSBuildProjectDirectory)/src/1Script.sln + $(ArtifactsRoot)/lib + $(ArtifactsRoot)\vscode\ Release + $(MSBuildProjectDirectory)/src/1Script.sln + x86 - bin32 + Win32 - Any CPU - bin + "Any CPU" + x64 - + + + + oscript.csproj + net6.0 + + + TestApp.csproj + net6.0-windows + + + @@ -29,219 +45,313 @@ - + - - - - - - + - - - - - - + + + + + + - + + + + + + + + - $(ArtifactsRoot)/tmp - $(TempFolder)/lib - $(TempFolder)/bin - $(TempFolder)/examples - $(TempFolder)/doc - $(ArtifactsRoot)\vscode\ - $(ArtifactsRoot)/mddoc + $(MSBuildProjectDirectory)/src/ScriptEngine.NativeApi/bin/$(Configuration) - - - - - - - - - - + + + + + + + + + + + + + + + + + - - + + - - - - $(TempFolder)/bin - - - $(TempFolder)/bin32 - + + + + + + + + + + $(MSBuildProjectDirectory)/src/ScriptEngine.NativeApi/bin/$(Configuration) + - - - $(TempFolder)/bin - - - $(TempFolder)/bin32 - + + - - - - - - $(MSBuildProjectDirectory)/src/VSCode.DebugAdapter/bin/$(Configuration)/net461 - + + + - - - - - - + + - - - + + + - - - - - + + + + src/VSCode.DebugAdapter/VSCode.DebugAdapter.csproj + $(DebugAdapterDir) + - - - - - - + - - - + + + + + + + + + + + + - - + + + + + + + + + + FirstItem = Items.First().ItemSpec; + + + + + + + + + + + + + + + + + + + + + + + opm\src\cmd\opm.os + dotnet $(Runner) "$(LibDir)\$(OpmEntryPoint)" + + - + + + + + + - + + + - - + - + - - + + + + + + + + + + + + - - - - + + + + + + + - - - - - - + + + - - + + + $(MSBuildProjectDirectory)/src/Tests + - - - - + + + + - + + + + + + + + + - - - - - "$(InnoSetupPath)\iscc.exe" - $(ArtifactsRoot) - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + - - + + - - - - - - + + --version-suffix $(VersionSuffix) + - - - - - - - - - - - - - - - - - - - - - - + + - + - + + + + - - + + - + diff --git a/Build_Core.csproj b/Build_Core.csproj deleted file mode 100644 index 94e4a1c82..000000000 --- a/Build_Core.csproj +++ /dev/null @@ -1,356 +0,0 @@ - - - 2.0.0 - - $(VersionPrefix) - $(VersionPrefix)-$(VersionSuffix) - - $(BUILD_NUMBER) - 0 - - $(MSBuildProjectDirectory)/built - $(ArtifactsRoot)/lib - $(ArtifactsRoot)\vscode\ - Release - $(MSBuildProjectDirectory)/src/1Script.sln - - - - - - x86 - Win32 - - - "Any CPU" - x64 - - - - - - oscript.csproj - net6.0 - - - TestApp.csproj - net6.0-windows - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - $(MSBuildProjectDirectory)/src/ScriptEngine.NativeApi/bin/$(Configuration) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - $(MSBuildProjectDirectory)/src/ScriptEngine.NativeApi/bin/$(Configuration) - - - - - - - - - - - - - - - - - - - - - - - - src/VSCode.DebugAdapter/VSCode.DebugAdapter.csproj - $(DebugAdapterDir) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - FirstItem = Items.First().ItemSpec; - - - - - - - - - - - - - - - - - - - - - - - opm\src\cmd\opm.os - dotnet $(Runner) "$(LibDir)\$(OpmEntryPoint)" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - $(MSBuildProjectDirectory)/src/Tests - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --version-suffix $(VersionSuffix) - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Jenkinsfile b/Jenkinsfile index 4213103b5..b2a3babc7 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -5,7 +5,7 @@ pipeline { environment { VersionPrefix = '2.0.0' - VersionSuffix = 'rc3' + VersionSuffix = 'rc4' outputEnc = '65001' } @@ -58,7 +58,7 @@ pipeline { bat "chcp $outputEnc > nul\r\n\"${tool 'MSBuild'}\" src/1Script.sln /t:restore && mkdir doctool" bat "chcp $outputEnc > nul\r\n dotnet publish src/OneScriptDocumenter/OneScriptDocumenter.csproj -c Release -o doctool" - bat "chcp $outputEnc > nul\r\n\"${tool 'MSBuild'}\" Build_Core.csproj /t:CleanAll;PrepareDistributionFiles;CreateNuget" + bat "chcp $outputEnc > nul\r\n\"${tool 'MSBuild'}\" Build.csproj /t:CleanAll;PrepareDistributionFiles;CreateNuget" stash includes: 'tests, built/**', name: 'buildResults' } @@ -126,7 +126,7 @@ pipeline { deleteDir() } unstash 'buildResults' - bat "chcp $outputEnc > nul\r\n\"${tool 'MSBuild'}\" Build_Core.csproj /t:Test" + bat "chcp $outputEnc > nul\r\n\"${tool 'MSBuild'}\" Build.csproj /t:Test" junit 'tests/*.xml' } @@ -136,7 +136,7 @@ pipeline { stage('Linux testing') { agent{ docker { - image 'mcr.microsoft.com/dotnet/sdk:5.0' + image 'mcr.microsoft.com/dotnet/sdk:6.0' label 'linux' } } @@ -187,16 +187,20 @@ pipeline { unstash 'buildResults' unstash 'nativeApiSo' - bat 'xcopy output\\na-proxy\\*64.so built\\linux-64\\bin\\' + bat ''' + chcp 65001 > nul + dir output\\na-proxy + xcopy output\\na-proxy\\*64.so built\\linux-x64\\bin\\ /F + '''.stripIndent() script { if (env.BRANCH_NAME == "preview") { echo 'Building preview' - bat "chcp $outputEnc > nul\r\n\"${tool 'MSBuild'}\" Build_Core.csproj /t:PackDistributions /p:Suffix=-pre%BUILD_NUMBER%" + bat "chcp $outputEnc > nul\r\n\"${tool 'MSBuild'}\" Build.csproj /t:PackDistributions /p:Suffix=-pre%BUILD_NUMBER%" } else{ - bat "chcp $outputEnc > nul\r\n\"${tool 'MSBuild'}\" Build_Core.csproj /t:PackDistributions" + bat "chcp $outputEnc > nul\r\n\"${tool 'MSBuild'}\" Build.csproj /t:PackDistributions" } } archiveArtifacts artifacts: 'built/**', fingerprint: true @@ -213,7 +217,7 @@ pipeline { agent { label 'master' } steps { - + cleanWs() unstash 'dist' unstash 'vsix' diff --git a/README.md b/README.md index 7737b5d62..25bf3d5c1 100644 --- a/README.md +++ b/README.md @@ -47,10 +47,57 @@ OneScript позволяет создавать и выполнять текст - выполнить команду `mono ovm.exe use stable` - перезапустить терминал -Донастройка Self-Contained варианта поставки (не требующего инсталляции dotnet) +#### Донастройка Self-Contained варианта поставки (не требующего инсталляции dotnet) ``` chmod +x ./oscript xattr -d com.apple.quarantine *.dylib oscript codesign -s - ./oscript ``` + + +# Ручная локальная сборка + +## Подготовка + +Ниже приведены ссылки на дистрибутивы, однако, учтите, что ссылки могут меняться со временем и их актуальность не гарантируется. Нужен dotnet SDK и компилятор C++, скачать можно из любого места, которое нагуглится. + +* Установить [MS BuildTools](https://visualstudio.microsoft.com/ru/thank-you-downloading-visual-studio/?sku=buildtools&rel=16), при установке включить таргетинг на .net6, .net4.8, установить компилятор C++. + +## Сборка + +Запустить Developer Command Prompt (появится в меню Пуск после установки MSBuildTools или Visual Studio). Перейти в каталог репозитория OneScript. Далее приведены команды в консоли Developer Command Prompt +Сборка выполняется с помощью msbuild. Таргеты: + +* CleanAll - очистка результатов предыдущих сборок +* BuildAll - подготовить файлы для поставки +* MakeCPP;MakeFDD;MakeSCD;BuildDebugger - отдельные таргеты сборки для подготовки разных типов поставки +* PrepareDistributionFiles - сборка полных пакетов поставки (включая библиотеки) +* PackDistributions - подготовка ZIP архивов поставки +* CreateNuget - создать пакеты для публикации в NuGet + +**Параметры сборки** + +* VersionPrefix - префикс номера релиза, его основная часть, например, 2.0.0 +* VersionSuffix - суффикс номера, который обычно выступает в качестве произвольного суффикса версионирования по semver, например, beta-786 (необязателен) +* NoCppCompiler - если True - не установлен компилятор C++, в сборку не будут добавлены компоненты C++ (поддержка NativeApi) + +Все поставляемые файлы будут размещены в каталоге `built` в корне репозитория 1Script + +### Сборка содержимого дистрибутивов в отдельном каталоге + +```bat +msbuild Build.csproj /t:CleanAll;PrepareDistributionFiles +``` + +### Сборка с ручным указанием версии + +```bat +msbuild Build.csproj /t:CleanAll;PrepareDistributionFiles /p:VersionPrefix=2.0.0 +``` + +### Сборка ZIP-дистрибутивов + +```bat +msbuild Build.csproj /t:CleanAll;PrepareDistributionFiles;PackDistributions /p:VersionPrefix=2.0.0 /p:VersionSuffix=preview223 +``` \ No newline at end of file diff --git a/install/opm.ospx b/install/opm.ospx index 661b6ff2b..5fd862e52 100644 Binary files a/install/opm.ospx and b/install/opm.ospx differ diff --git a/install/package-loader.os b/install/package-loader.os index ae2b4a3ec..915c41a15 100644 --- a/install/package-loader.os +++ b/install/package-loader.os @@ -106,6 +106,9 @@ КаталогиМодулей.Добавить(ОбъединитьПути(Путь, "src", "Модули")); КаталогиМодулей.Добавить(ОбъединитьПути(Путь, "src", "Modules")); + КаталогиВК = Новый Массив; + КаталогиВК.Добавить(ОбъединитьПути(Путь, "Components")); + КаталогиВК.Добавить(ОбъединитьПути(Путь, "Компоненты")); Для Каждого мКаталог Из КаталогиКлассов Цикл @@ -118,6 +121,12 @@ ОбработатьКаталогМодулей(мКаталог, СтандартнаяОбработка, Отказ); КонецЦикла; + + Для Каждого мКаталог Из КаталогиВК Цикл + + ОбработатьКаталогВК(мКаталог, СтандартнаяОбработка, Отказ); + + КонецЦикла; КонецПроцедуры @@ -161,6 +170,75 @@ КонецПроцедуры +// По соглашению ВК должны лежать в подпапках, названных, как значения перечисления ТипПлатформы. +// Имя файла, являющегося внешней компонентой должно иметь префикс 1script_ +// Components +// net4 (фреймворк .net48 +// 1script_barcode.dll +// dotnet (.net современных версий, он же netcore) +// 1script_barcode.dll +// NativeApi +// Windows_x86 +// 1script_barcode.dll +// Windows_x86_64 +// 1script_barcode.dll +// Linux_x86_64 +// 1script_barcode.so +// остальные не поддерживаются (ЖВПР) +// +Процедура ОбработатьКаталогВК(Знач Путь, СтандартнаяОбработка, Отказ) + + СИ = Новый СистемнаяИнформация(); + МажорнаяВерсия = Лев(СИ.Версия,1); + + Если МажорнаяВерсия = "1" Тогда + ОбработатьБиблиотекиCLR(ОбъединитьПути(Путь, "net4")); + ИначеЕсли МажорнаяВерсия = "2" Тогда + ОбработатьБиблиотекиCLR(ОбъединитьПути(Путь, "dotnet")); + Иначе + Вывести("Неизвестная мажорная версия системы: " + МажорнаяВерсия); + КонецЕсли; + + ОбработатьКомпонентыNativeApi(Путь); + +КонецПроцедуры + +Процедура ОбработатьБиблиотекиCLR(Путь) + КандидатыВКомпоненты = НайтиФайлы(Путь, "1script_*.dll"); + Для Каждого Кандидат Из КандидатыВКомпоненты Цикл + Если Не Кандидат.ЭтоФайл() Тогда + Продолжить; + КонецЕсли; + + Вывести("Загружаю файл " + Кандидат.Имя); + ЗагрузитьБиблиотеку(Кандидат.ПолноеИмя); + + КонецЦикла; +КонецПроцедуры + +Процедура ОбработатьКомпонентыNativeApi(Путь) + СИ = Новый СистемнаяИнформация; + ИмяПодкаталога = ОбъединитьПути(Путь, Строка(СИ.ТипПлатформы)); + Вывести("Ищу внешние компоненты в каталоге " + Путь); + + #Если Windows Тогда + Расширение = ".dll"; + #Иначе + Расширение = ".so"; + #КонецЕсли + + КандидатыВКомпоненты = НайтиФайлы(ИмяПодкаталога, "1script_*"+Расширение); + Для Каждого Кандидат Из КандидатыВКомпоненты Цикл + Если Не Кандидат.ЭтоФайл() Тогда + Продолжить; + КонецЕсли; + + Вывести("Загружаю файл " + Кандидат.Имя); + ПодключитьВнешнююКомпоненту(Кандидат.ПолноеИмя, Кандидат.Имя, ТипВнешнейКомпоненты.Native); + + КонецЦикла; +КонецПроцедуры + Процедура ДобавитьКлассЕслиРанееНеДобавляли(ПутьФайла, ИмяКласса) Вывести("Добавляю класс, если ранее не добавляли " + ИмяКласса); Если ВыдаватьОшибкуПриЗагрузкеУжеСуществующихКлассовМодулей Тогда diff --git a/src/1Script.sln b/src/1Script.sln index 9da3cfeaf..3f8c681ea 100644 --- a/src/1Script.sln +++ b/src/1Script.sln @@ -17,9 +17,8 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D14BF321-348C-46B8-B96A-43A22BA7AC10}" ProjectSection(SolutionItems) = preProject - ..\Build.csproj = ..\Build.csproj oscommon.targets = oscommon.targets - ..\Build_Core.csproj = ..\Build_Core.csproj + ..\Build.csproj = ..\Build.csproj ..\Jenkinsfile = ..\Jenkinsfile EndProjectSection EndProject diff --git a/src/1Script_Mono.sln b/src/1Script_Mono.sln deleted file mode 100644 index f138cfbd8..000000000 --- a/src/1Script_Mono.sln +++ /dev/null @@ -1,165 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2020 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptEngine", "ScriptEngine\ScriptEngine.csproj", "{F062D1D9-D307-492A-A56B-FF3C55F8F6C0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptEngine.HostedScript", "ScriptEngine.HostedScript\ScriptEngine.HostedScript.csproj", "{F09A46BD-5737-45E7-BA60-A47C9F7821A9}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "oscript", "oscript\oscript.csproj", "{2590E2BB-CC1F-4775-80ED-451F45C9A4F1}" - ProjectSection(ProjectDependencies) = postProject - {795AA2F5-3074-4BC5-A30F-1B6354044D9B} = {795AA2F5-3074-4BC5-A30F-1B6354044D9B} - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StandaloneRunner", "StandaloneRunner\StandaloneRunner.csproj", "{795AA2F5-3074-4BC5-A30F-1B6354044D9B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D14BF321-348C-46B8-B96A-43A22BA7AC10}" - ProjectSection(SolutionItems) = preProject - oscommon.targets = oscommon.targets - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NUnitTests", "NUnitTests\NUnitTests.csproj", "{93ACC849-E7E1-4695-B59D-54B3737E48A6}" - ProjectSection(ProjectDependencies) = postProject - {2590E2BB-CC1F-4775-80ED-451F45C9A4F1} = {2590E2BB-CC1F-4775-80ED-451F45C9A4F1} - {F09A46BD-5737-45E7-BA60-A47C9F7821A9} = {F09A46BD-5737-45E7-BA60-A47C9F7821A9} - {F062D1D9-D307-492A-A56B-FF3C55F8F6C0} = {F062D1D9-D307-492A-A56B-FF3C55F8F6C0} - {795AA2F5-3074-4BC5-A30F-1B6354044D9B} = {795AA2F5-3074-4BC5-A30F-1B6354044D9B} - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Component", "Component\Component.csproj", "{B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DebugServer", "DebugServer\DebugServer.csproj", "{C979F151-AA29-47E4-B020-3039BA0986D9}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScript.DebugProtocol", "OneScript.DebugProtocol\OneScript.DebugProtocol.csproj", "{727A498F-BF50-42F8-8523-40C0B5B1B233}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "oscriptTests", "oscriptTest\oscriptTests.csproj", "{69A7869C-203C-4F09-AC3F-04E9B52AD7AB}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{91059C5B-526C-4B81-B106-99DEFF542D1F}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HTTPServices", "ASPNETHandler\HTTPServices.csproj", "{B7CD7F52-E387-490E-8F77-E1FB060401B5}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x86 = Release|x86 - ReleaseNetStandard|Any CPU = ReleaseNetStandard|Any CPU - ReleaseNetStandard|x86 = ReleaseNetStandard|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.Debug|x86.ActiveCfg = Debug|Any CPU - {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.Debug|x86.Build.0 = Debug|Any CPU - {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.Release|Any CPU.Build.0 = Release|Any CPU - {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.Release|x86.ActiveCfg = Release|Any CPU - {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.Release|x86.Build.0 = Release|Any CPU - {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.ReleaseNetStandard|Any CPU.ActiveCfg = ReleaseNetStandard|Any CPU - {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.ReleaseNetStandard|Any CPU.Build.0 = ReleaseNetStandard|Any CPU - {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.ReleaseNetStandard|x86.ActiveCfg = Release|Any CPU - {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.ReleaseNetStandard|x86.Build.0 = Release|Any CPU - {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.Debug|x86.ActiveCfg = Debug|Any CPU - {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.Debug|x86.Build.0 = Debug|Any CPU - {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.Release|Any CPU.Build.0 = Release|Any CPU - {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.Release|x86.ActiveCfg = Release|Any CPU - {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.Release|x86.Build.0 = Release|Any CPU - {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.ReleaseNetStandard|Any CPU.ActiveCfg = ReleaseNetStandard|Any CPU - {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.ReleaseNetStandard|Any CPU.Build.0 = ReleaseNetStandard|Any CPU - {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.ReleaseNetStandard|x86.ActiveCfg = Release|Any CPU - {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.ReleaseNetStandard|x86.Build.0 = Release|Any CPU - {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.Debug|Any CPU.ActiveCfg = Debug|x86 - {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.Debug|x86.ActiveCfg = Debug|x86 - {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.Debug|x86.Build.0 = Debug|x86 - {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.Release|Any CPU.ActiveCfg = Release|x86 - {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.Release|x86.ActiveCfg = Release|x86 - {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.Release|x86.Build.0 = Release|x86 - {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.ReleaseNetStandard|Any CPU.ActiveCfg = ReleaseNetStandard|x86 - {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.ReleaseNetStandard|x86.ActiveCfg = Release|x86 - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Debug|x86.ActiveCfg = Debug|x86 - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Debug|x86.Build.0 = Debug|x86 - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Release|Any CPU.Build.0 = Release|Any CPU - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Release|x86.ActiveCfg = Release|x86 - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Release|x86.Build.0 = Release|x86 - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.ReleaseNetStandard|Any CPU.ActiveCfg = ReleaseNetStandard|Any CPU - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.ReleaseNetStandard|Any CPU.Build.0 = ReleaseNetStandard|Any CPU - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.ReleaseNetStandard|x86.ActiveCfg = Release|x86 - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.Debug|x86.ActiveCfg = Debug|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.Release|Any CPU.Build.0 = Release|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.Release|x86.ActiveCfg = Release|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.ReleaseNetStandard|Any CPU.ActiveCfg = ReleaseNetStandard|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.ReleaseNetStandard|Any CPU.Build.0 = ReleaseNetStandard|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.ReleaseNetStandard|x86.ActiveCfg = Release|Any CPU - {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.Debug|x86.ActiveCfg = Debug|Any CPU - {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.Release|Any CPU.Build.0 = Release|Any CPU - {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.Release|x86.ActiveCfg = Release|Any CPU - {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.ReleaseNetStandard|Any CPU.ActiveCfg = ReleaseNetStandard|Any CPU - {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.ReleaseNetStandard|Any CPU.Build.0 = ReleaseNetStandard|Any CPU - {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.ReleaseNetStandard|x86.ActiveCfg = Release|Any CPU - {C979F151-AA29-47E4-B020-3039BA0986D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C979F151-AA29-47E4-B020-3039BA0986D9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C979F151-AA29-47E4-B020-3039BA0986D9}.Debug|x86.ActiveCfg = Debug|Any CPU - {C979F151-AA29-47E4-B020-3039BA0986D9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C979F151-AA29-47E4-B020-3039BA0986D9}.Release|Any CPU.Build.0 = Release|Any CPU - {C979F151-AA29-47E4-B020-3039BA0986D9}.Release|x86.ActiveCfg = Release|Any CPU - {C979F151-AA29-47E4-B020-3039BA0986D9}.Release|x86.Build.0 = Release|Any CPU - {C979F151-AA29-47E4-B020-3039BA0986D9}.ReleaseNetStandard|Any CPU.ActiveCfg = ReleaseNetStandard|Any CPU - {C979F151-AA29-47E4-B020-3039BA0986D9}.ReleaseNetStandard|Any CPU.Build.0 = ReleaseNetStandard|Any CPU - {C979F151-AA29-47E4-B020-3039BA0986D9}.ReleaseNetStandard|x86.ActiveCfg = Release|Any CPU - {727A498F-BF50-42F8-8523-40C0B5B1B233}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {727A498F-BF50-42F8-8523-40C0B5B1B233}.Debug|Any CPU.Build.0 = Debug|Any CPU - {727A498F-BF50-42F8-8523-40C0B5B1B233}.Debug|x86.ActiveCfg = Debug|Any CPU - {727A498F-BF50-42F8-8523-40C0B5B1B233}.Debug|x86.Build.0 = Debug|Any CPU - {727A498F-BF50-42F8-8523-40C0B5B1B233}.Release|Any CPU.ActiveCfg = Release|Any CPU - {727A498F-BF50-42F8-8523-40C0B5B1B233}.Release|Any CPU.Build.0 = Release|Any CPU - {727A498F-BF50-42F8-8523-40C0B5B1B233}.Release|x86.ActiveCfg = Release|Any CPU - {727A498F-BF50-42F8-8523-40C0B5B1B233}.Release|x86.Build.0 = Release|Any CPU - {727A498F-BF50-42F8-8523-40C0B5B1B233}.ReleaseNetStandard|Any CPU.ActiveCfg = ReleaseNetStandard|Any CPU - {727A498F-BF50-42F8-8523-40C0B5B1B233}.ReleaseNetStandard|Any CPU.Build.0 = ReleaseNetStandard|Any CPU - {727A498F-BF50-42F8-8523-40C0B5B1B233}.ReleaseNetStandard|x86.ActiveCfg = Release|Any CPU - {727A498F-BF50-42F8-8523-40C0B5B1B233}.ReleaseNetStandard|x86.Build.0 = Release|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.Debug|x86.ActiveCfg = Debug|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.Release|Any CPU.Build.0 = Release|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.Release|x86.ActiveCfg = Release|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.ReleaseNetStandard|Any CPU.ActiveCfg = ReleaseNetStandard|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.ReleaseNetStandard|Any CPU.Build.0 = ReleaseNetStandard|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.ReleaseNetStandard|x86.ActiveCfg = Release|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Debug|x86.ActiveCfg = Debug|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Debug|x86.Build.0 = Debug|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Release|Any CPU.Build.0 = Release|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Release|x86.ActiveCfg = Release|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Release|x86.Build.0 = Release|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.ReleaseNetStandard|Any CPU.ActiveCfg = Release|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.ReleaseNetStandard|Any CPU.Build.0 = Release|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.ReleaseNetStandard|x86.ActiveCfg = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB} = {91059C5B-526C-4B81-B106-99DEFF542D1F} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {A4A871EF-C5A7-478F-907E-31C69A869973} - EndGlobalSection -EndGlobal diff --git a/src/OneScript.Core/Commons/Utils.cs b/src/OneScript.Core/Commons/Utils.cs index b110f0713..1ed8a41bd 100644 --- a/src/OneScript.Core/Commons/Utils.cs +++ b/src/OneScript.Core/Commons/Utils.cs @@ -5,7 +5,9 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ using System; +using System.Collections; using System.Collections.Generic; +using System.Linq; namespace OneScript.Commons { diff --git a/src/OneScript.Core/Compilation/Binding/SymbolTable.cs b/src/OneScript.Core/Compilation/Binding/SymbolTable.cs index 7c5ae829c..3a05090cb 100644 --- a/src/OneScript.Core/Compilation/Binding/SymbolTable.cs +++ b/src/OneScript.Core/Compilation/Binding/SymbolTable.cs @@ -18,7 +18,7 @@ private struct BindingRecord public IRuntimeContextInstance target; } - private List _bindings = new List(); + private readonly List _bindings = new List(); public SymbolScope GetScope(int index) => _bindings[index].scope; diff --git a/src/OneScript.Core/Compilation/CompileTimeSymbolsProvider.cs b/src/OneScript.Core/Compilation/CompileTimeSymbolsProvider.cs index b7dff4a05..f037926fd 100644 --- a/src/OneScript.Core/Compilation/CompileTimeSymbolsProvider.cs +++ b/src/OneScript.Core/Compilation/CompileTimeSymbolsProvider.cs @@ -60,8 +60,8 @@ private static bool IsFiller(MethodInfo methodInfo) private class SymbolProvider : IModuleSymbolsProvider { - private CompileTimeSymbolsProvider _provider; - private Filler _filler; + private readonly CompileTimeSymbolsProvider _provider; + private readonly Filler _filler; public SymbolProvider(CompileTimeSymbolsProvider provider, Filler filler) { diff --git a/src/OneScript.Core/Compilation/GlobalPropertiesHolder.cs b/src/OneScript.Core/Compilation/GlobalPropertiesHolder.cs index 9a830bec7..7f85bf6e9 100644 --- a/src/OneScript.Core/Compilation/GlobalPropertiesHolder.cs +++ b/src/OneScript.Core/Compilation/GlobalPropertiesHolder.cs @@ -18,8 +18,8 @@ namespace OneScript.Compilation { internal class GlobalPropertiesHolder : IContext { - private IndexedNameValueCollection _values= new IndexedNameValueCollection(); - private List _properties = new List(); + private readonly IndexedNameValueCollection _values= new IndexedNameValueCollection(); + private readonly List _properties = new List(); public BslPropertyInfo Register(BilingualString names, BslValue value) { @@ -90,12 +90,12 @@ public BslValue GetPropValue(BslPropertyInfo property) public void CallAsProcedure(BslMethodInfo method, IReadOnlyList arguments) { - throw new ArgumentException(); + throw new InvalidOperationException(); } public BslValue CallAsFunction(BslMethodInfo method, IReadOnlyList arguments) { - throw new ArgumentException(); + throw new InvalidOperationException(); } #endregion diff --git a/src/OneScript.Core/Contexts/BslMethodInfo.cs b/src/OneScript.Core/Contexts/BslMethodInfo.cs index 220f5e896..4d82c6d12 100644 --- a/src/OneScript.Core/Contexts/BslMethodInfo.cs +++ b/src/OneScript.Core/Contexts/BslMethodInfo.cs @@ -6,8 +6,10 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using System.Collections.Generic; using System.Linq; using System.Reflection; +using OneScript.Localization; namespace OneScript.Contexts { @@ -20,7 +22,7 @@ public abstract class BslMethodInfo : MethodInfo, INameAndAliasProvider public abstract string Alias { get; } - private AnnotationHolder Annotations + protected AnnotationHolder Annotations { get { diff --git a/src/OneScript.Core/Contexts/BslScriptMethodInfo.cs b/src/OneScript.Core/Contexts/BslScriptMethodInfo.cs index 0dd00e1e4..89ede2c03 100644 --- a/src/OneScript.Core/Contexts/BslScriptMethodInfo.cs +++ b/src/OneScript.Core/Contexts/BslScriptMethodInfo.cs @@ -11,6 +11,7 @@ This Source Code Form is subject to the terms of the using System.Linq; using System.Reflection; using OneScript.Contexts.Internal; +using OneScript.Localization; namespace OneScript.Contexts { @@ -117,5 +118,12 @@ public override MethodInfo GetBaseDefinition() } public override ICustomAttributeProvider ReturnTypeCustomAttributes => throw new NotImplementedException(); + + public bool HasBslAnnotation(BilingualString name) + { + return Annotations.GetCustomAttributes(typeof(BslAnnotationAttribute), false) + .Cast() + .Any(anno => name.HasName(anno.Name)); + } } } \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/IPredefinedInterfaceChecker.cs b/src/OneScript.Core/Contexts/IPredefinedInterfaceChecker.cs new file mode 100644 index 000000000..7491fc76a --- /dev/null +++ b/src/OneScript.Core/Contexts/IPredefinedInterfaceChecker.cs @@ -0,0 +1,23 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Collections.Generic; +using OneScript.Execution; + +namespace OneScript.Contexts +{ + /// + /// Постобработчик построения модуля, регистрирующий специальные аннотации, позволяющий во время компиляции проверить + /// что модуль соответствует тому или иному интерфейсу, содержит корректные методы и пр. + /// + public interface IPredefinedInterfaceChecker + { + IEnumerable GetRegistrations(); + + public void Validate(IExecutableModule module); + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/PredefinedInterfaceRegistration.cs b/src/OneScript.Core/Contexts/PredefinedInterfaceRegistration.cs new file mode 100644 index 000000000..661c4ebbf --- /dev/null +++ b/src/OneScript.Core/Contexts/PredefinedInterfaceRegistration.cs @@ -0,0 +1,81 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Localization; + +namespace OneScript.Contexts +{ + /// + /// Опции регистрации предопределенных аннотаций + /// + public class PredefinedInterfaceRegistration : IEquatable + { + public PredefinedInterfaceRegistration( + MarkerLocation location, + BilingualString annotation, + BilingualString methodName) + { + Location = location; + Annotation = annotation; + MethodName = methodName; + } + + public MarkerLocation Location { get; } + public BilingualString Annotation { get; } + public BilingualString MethodName { get; } + + public PredefinedInterfaceRegistration(MarkerLocation location, BilingualString annotation) : this(location, annotation, null) + { + } + + /// + /// Хелпер для регистрации аннотаций модуля + /// + /// Регистрируемая аннотация + public static PredefinedInterfaceRegistration OnModule(BilingualString annotation) + { + return new PredefinedInterfaceRegistration(MarkerLocation.ModuleAnnotation, annotation); + } + + /// + /// Хелпер для регистрации аннотаций на методах + /// + /// Регистрируемая аннотация + /// Метод на котором должна быть расположена аннотация + public static PredefinedInterfaceRegistration OnMethod(BilingualString annotation, BilingualString methodName) + { + return new PredefinedInterfaceRegistration(MarkerLocation.SpecificMethodAnnotation, annotation, methodName); + } + + public bool Equals(PredefinedInterfaceRegistration other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Location == other.Location && Equals(Annotation, other.Annotation) && Equals(MethodName, other.MethodName); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((PredefinedInterfaceRegistration)obj); + } + + public override int GetHashCode() + { + return HashCode.Combine((int)Location, Annotation, MethodName); + } + } + + public enum MarkerLocation + { + ModuleAnnotation, + SpecificMethodAnnotation + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/PredefinedInterfaceResolver.cs b/src/OneScript.Core/Contexts/PredefinedInterfaceResolver.cs new file mode 100644 index 000000000..ce72f8db6 --- /dev/null +++ b/src/OneScript.Core/Contexts/PredefinedInterfaceResolver.cs @@ -0,0 +1,83 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using OneScript.Commons; +using OneScript.Execution; + +namespace OneScript.Contexts +{ + // ReSharper disable once ClassNeverInstantiated.Global + public class PredefinedInterfaceResolver + { + private readonly ISet _checkers; + + private class CheckerData : PredefinedInterfaceRegistration + { + public IPredefinedInterfaceChecker Checker { get; } + + public CheckerData( + IPredefinedInterfaceChecker checker, + PredefinedInterfaceRegistration source) + : base(source.Location, source.Annotation, source.MethodName) + { + Checker = checker; + } + } + + public PredefinedInterfaceResolver(IEnumerable checkers) + { + _checkers = MapCheckers(checkers); + } + + public void Resolve(IExecutableModule module) + { + var triggeredCheckers = new HashSet(); + + _checkers + .Where(check => !triggeredCheckers.Contains(check.Checker)) + .Where(check => + check.Location == MarkerLocation.ModuleAnnotation + && module.ModuleAttributes.Any(attr => check.Annotation.HasName(attr.Name))) + .ForEach(check => + { + triggeredCheckers.Add(check.Checker); + check.Checker.Validate(module); + }); + + foreach (var methodInfo in module.Methods.Where(m => m is BslScriptMethodInfo).Cast()) + { + _checkers + .Where(check => !triggeredCheckers.Contains(check.Checker)) + .Where(check => + check.Location == MarkerLocation.SpecificMethodAnnotation && + check.MethodName.HasName(methodInfo.Name) && + methodInfo.HasBslAnnotation(check.Annotation)) + .ForEach(check => + { + triggeredCheckers.Add(check.Checker); + check.Checker.Validate(module); + }); + } + } + + private ISet MapCheckers(IEnumerable checkers) + { + var result = new HashSet(); + + foreach (var sourceCheck in checkers) + { + sourceCheck.GetRegistrations().ForEach(x => result.Add(new CheckerData(sourceCheck, x))); + } + + return result; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Exceptions/InterfaceCheckException.cs b/src/OneScript.Core/Exceptions/InterfaceCheckException.cs new file mode 100644 index 000000000..d591996b4 --- /dev/null +++ b/src/OneScript.Core/Exceptions/InterfaceCheckException.cs @@ -0,0 +1,23 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Localization; + +namespace OneScript.Exceptions +{ + public class InterfaceCheckException : RuntimeException + { + public InterfaceCheckException(BilingualString message, Exception innerException) : base(message, innerException) + { + } + + public InterfaceCheckException(BilingualString message) : base(message) + { + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Execution/IExecutableModule.cs b/src/OneScript.Core/Execution/IExecutableModule.cs index fcd3da430..09fd80b69 100644 --- a/src/OneScript.Core/Execution/IExecutableModule.cs +++ b/src/OneScript.Core/Execution/IExecutableModule.cs @@ -5,6 +5,7 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using System; using System.Collections.Generic; using OneScript.Contexts; using OneScript.Sources; @@ -25,6 +26,8 @@ public interface IExecutableModule SourceCode Source { get; } + IDictionary Interfaces { get; } + const string BODY_METHOD_NAME = "$entry"; } } \ No newline at end of file diff --git a/src/OneScript.Core/Rcw/RcwMembersMetadataCollection.cs b/src/OneScript.Core/Rcw/RcwMembersMetadataCollection.cs index 826162b36..80386d5c5 100644 --- a/src/OneScript.Core/Rcw/RcwMembersMetadataCollection.cs +++ b/src/OneScript.Core/Rcw/RcwMembersMetadataCollection.cs @@ -14,12 +14,12 @@ namespace OneScript.Rcw public class RcwMembersMetadataCollection where T : RcwMemberMetadata { private readonly List _collection = new List(); - private readonly Dictionary _dispIds = new Dictionary(); - private readonly Dictionary _names = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + private readonly Dictionary _indexByDispId = new Dictionary(); + private readonly Dictionary _indexByName = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - public IReadOnlyDictionary DispatchIds => new ReadOnlyDictionary(_dispIds); + public IReadOnlyDictionary ByDispatchId => _indexByDispId; - public IReadOnlyDictionary Names => new ReadOnlyDictionary(_names); + public IReadOnlyDictionary ByName => _indexByName; public T this[int index] => _collection[index]; @@ -27,15 +27,28 @@ public class RcwMembersMetadataCollection where T : RcwMemberMetadata public void Add(T item) { - if (_dispIds.ContainsKey(item.DispatchId)) + if (_indexByDispId.TryGetValue(item.DispatchId, out var method)) { - _names.Add(item.Name, _dispIds[item.DispatchId]); + if (method.Name != item.Name) + { + // добавляют метод, который известен нам, с тем же dispId, но который имеет другое имя + _indexByName.Remove(method.Name); // известное нам старое имя этого dispId - инвалидируем + + // добавляемый метод поместим в индекс диспатчей и имен + _indexByDispId[item.DispatchId] = item; + _indexByName[item.Name] = item; + } + else + { + _indexByName[item.Name] = item; + } + return; } _collection.Add(item); - _dispIds.Add(item.DispatchId, item); - _names.Add(item.Name, item); + _indexByDispId.Add(item.DispatchId, item); + _indexByName.Add(item.Name, item); } public bool Contains(T item) => _collection.Contains(item); diff --git a/src/OneScript.Core/Rcw/RcwMetadata.cs b/src/OneScript.Core/Rcw/RcwMetadata.cs index e4281775d..be04a8e5b 100644 --- a/src/OneScript.Core/Rcw/RcwMetadata.cs +++ b/src/OneScript.Core/Rcw/RcwMetadata.cs @@ -128,7 +128,7 @@ private void LoadFuncs(ITypeInfo typeInfo, TYPEATTR typeAttr) private RcwPropertyMetadata GetOrAddProperty(string propName, int dispId) { - if (Properties.Names.TryGetValue(propName, out var md)) return md; + if (Properties.ByName.TryGetValue(propName, out var md)) return md; md = new RcwPropertyMetadata(propName, dispId); Properties.Add(md); diff --git a/src/OneScript.Core/Sources/EmptyModule.cs b/src/OneScript.Core/Sources/EmptyModule.cs index 66226a123..a4d23be98 100644 --- a/src/OneScript.Core/Sources/EmptyModule.cs +++ b/src/OneScript.Core/Sources/EmptyModule.cs @@ -7,6 +7,7 @@ This Source Code Form is subject to the terms of the using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using OneScript.Contexts; using OneScript.Execution; @@ -14,7 +15,7 @@ namespace OneScript.Sources { public sealed class EmptyModule : IExecutableModule { - public static IExecutableModule Instance = new EmptyModule(); + public static readonly IExecutableModule Instance = new EmptyModule(); private EmptyModule() { @@ -29,5 +30,7 @@ private EmptyModule() public IList Methods => Array.Empty(); public BslMethodInfo ModuleBody { get; } public SourceCode Source { get; } + + public IDictionary Interfaces => new ReadOnlyDictionary(new Dictionary()); } } \ No newline at end of file diff --git a/src/OneScript.Core/Types/ITypeManager.cs b/src/OneScript.Core/Types/ITypeManager.cs index 01f69ab34..79b21490a 100644 --- a/src/OneScript.Core/Types/ITypeManager.cs +++ b/src/OneScript.Core/Types/ITypeManager.cs @@ -6,6 +6,7 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using System.Collections.Generic; namespace OneScript.Types { @@ -25,5 +26,8 @@ public interface ITypeManager bool IsKnownType(Type type); bool IsKnownType(string typeName); + + IReadOnlyList RegisteredTypes(); + } } \ No newline at end of file diff --git a/src/OneScript.Language/LanguageDef.cs b/src/OneScript.Language/LanguageDef.cs index fadd827f3..ecbbb9c76 100644 --- a/src/OneScript.Language/LanguageDef.cs +++ b/src/OneScript.Language/LanguageDef.cs @@ -17,16 +17,13 @@ public static class LanguageDef static readonly Dictionary _priority = new Dictionary(); public const int MAX_OPERATION_PRIORITY = 8; - private static readonly LexemTrie _stringToToken = new LexemTrie(); + private static readonly IdentifiersTrie _stringToToken = new IdentifiersTrie(); - private static readonly LexemTrie _undefined = new LexemTrie(); - private static readonly LexemTrie _booleans = new LexemTrie(); - private static readonly LexemTrie _logicalOp = new LexemTrie(); + private static readonly IdentifiersTrie _undefined = new IdentifiersTrie(); + private static readonly IdentifiersTrie _booleans = new IdentifiersTrie(); + private static readonly IdentifiersTrie _logicalOp = new IdentifiersTrie(); - private static readonly LexemTrie _preprocRegion = new LexemTrie(); - private static readonly LexemTrie _preprocEndRegion = new LexemTrie(); - - private static readonly LexemTrie _preprocImport = new LexemTrie(); + private static readonly IdentifiersTrie _preprocImport = new IdentifiersTrie(); const int BUILTINS_INDEX = (int)Token.ByValParam; @@ -110,6 +107,7 @@ static LanguageDef() AddToken(Token.RemoveHandler, "УдалитьОбработчик", "RemoveHandler"); AddToken(Token.Async, "Асинх", "Async"); AddToken(Token.Await, "Ждать", "Await"); + AddToken(Token.Goto, "Перейти", "Goto"); #endregion @@ -216,11 +214,6 @@ static LanguageDef() #endregion - _preprocRegion.Add("Область",true); - _preprocRegion.Add("Region", true); - _preprocEndRegion.Add("КонецОбласти", true); - _preprocEndRegion.Add("EndRegion", true); - _preprocImport.Add("Использовать", true); _preprocImport.Add("Use", true); } @@ -292,7 +285,7 @@ public static bool IsUnaryOperator(Token token) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsLiteral(ref Lexem lex) + public static bool IsLiteral(in Lexem lex) { return lex.Type == LexemType.StringLiteral || lex.Type == LexemType.NumberLiteral @@ -410,18 +403,6 @@ public static bool IsLogicalOperatorString(string content) return _logicalOp.TryGetValue(content, out var nodeIsFilled) && nodeIsFilled; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsPreprocRegion(string value) - { - return _preprocRegion.TryGetValue(value, out var nodeIsFilled) && nodeIsFilled; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsPreprocEndRegion(string value) - { - return _preprocEndRegion.TryGetValue(value, out var nodeIsFilled) && nodeIsFilled; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsImportDirective(string value) { diff --git a/src/OneScript.Language/LexemTrie.cs b/src/OneScript.Language/LexemTrie.cs deleted file mode 100644 index 5263b4e15..000000000 --- a/src/OneScript.Language/LexemTrie.cs +++ /dev/null @@ -1,213 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System.Collections.Generic; -using System.Diagnostics; - -namespace OneScript.Language -{ - public class LexemTrie - { - private class TrieNode - { - public char UCase; - public char LCase; - - public TrieNode next; - public TrieNode sibling; - public T value; - } - - private static int _alphabetLength; - private TrieNode[] _alphabet; - private int _count; - - static LexemTrie() - { - var ru = "абвгдеёжзийклмнопрстуфхцчшщьыъэюя"; - var en = "abcdefghijklmnopqrstuvwxyz"; - var symbols = @"+-*/\()[].,<>=;?%0123456789"; - - var all = symbols + - ru + - ru.ToUpper() + - en + - en.ToUpper(); - - _alphabetLength = all.Length; - } - - public LexemTrie() - { - _alphabet = new TrieNode[_alphabetLength]; - } - - public int Count => _count; - - private static int GetIndex(char c) - { - var code = (int) c; - - if (code >= 1040 && code <= 1103) - { - return code - 960; - } - - if (code >= 40 && code <= 57) - { - return code - 39; - } - - if (code >= 59 && code <= 63) - { - return code - 40; - } - - if (code >= 65 && code <= 93) - { - return code - 41; - } - - if (code >= 97 && code <= 122) - { - return code - 44; - } - - switch (c) - { - case '%': - return 0; - case 'Ё': - return 79; - case 'ё': - return 144; - } - - return -1; - } - - private TrieNode GetValueNode(string key) - { - var index = GetIndex(key[0]); - if (index == -1) - return null; - - var node = _alphabet[index]; - if (node == null) - { - node = new TrieNode(); - node.LCase = char.ToLower(key[0]); - node.UCase = char.ToUpper(key[0]); - _alphabet[GetIndex(node.LCase)] = node; - _alphabet[GetIndex(node.UCase)] = node; - } - - for (int i = 1; i < key.Length; i++) - { - var current = node; - node = node.next; - if (node == null) - { - var newNode = new TrieNode(); - newNode.LCase = char.ToLower(key[i]); - newNode.UCase = char.ToUpper(key[i]); - current.next = newNode; - node = newNode; - } - else if (node.LCase != key[i] && node.UCase != key[i]) - { - var insert = node.sibling; - while (insert != null) - { - if (insert.LCase == key[i] || insert.UCase == key[i]) - { - node = insert; - break; - } - - node = insert; - insert = insert.sibling; - } - - if (insert == null) - { - var newNode = new TrieNode(); - newNode.LCase = char.ToLower(key[i]); - newNode.UCase = char.ToUpper(key[i]); - node.sibling = newNode; - node = newNode; - } - } - } - - return node; - } - - public void Add(string key, T value) - { - var node = GetValueNode(key); - Debug.Assert(node != null); - - node.value = value; - ++_count; - } - - public T Get(string key) - { - var node = FindNode(key); - if (node == null) - { - throw new KeyNotFoundException(); - } - - return node.value; - } - - private TrieNode FindNode(string key) - { - var index = GetIndex(key[0]); - if (index == -1 || _alphabet[index] == null) - return null; - - var node = _alphabet[index]; - for (int i = 1; i < key.Length; i++) - { - node = node.next; - if (node == null) - return null; - - while(node.LCase != key[i] && node.UCase != key[i]) - { - node = node.sibling; - if(node == null) - return null; - } - } - - return node; - } - - public bool TryGetValue(string key, out T value) - { - var node = FindNode(key); - if (node == null) - { - value = default(T); - return false; - } - - value = node.value; - return true; - } - - public T this[string key] - { - get => Get(key); - set => Add(key, value); - } - } -} diff --git a/src/OneScript.Language/LexicalAnalysis/AnnotationLexerState.cs b/src/OneScript.Language/LexicalAnalysis/AnnotationLexerState.cs index fb6efd65d..8bd933bc3 100644 --- a/src/OneScript.Language/LexicalAnalysis/AnnotationLexerState.cs +++ b/src/OneScript.Language/LexicalAnalysis/AnnotationLexerState.cs @@ -9,8 +9,8 @@ namespace OneScript.Language.LexicalAnalysis { public class AnnotationLexerState : LexerState { - WordLexerState _wordExtractor = new WordLexerState(); const string MESSAGE_ANNOTATION_EXPECTED = "Ожидается имя аннотации"; + private readonly WordLexerState _wordExtractor = new WordLexerState(); public override Lexem ReadNextLexem(SourceCodeIterator iterator) { diff --git a/src/OneScript.Language/LexicalAnalysis/FullSourceLexer.cs b/src/OneScript.Language/LexicalAnalysis/FullSourceLexer.cs index 72aed2aac..0fc6c88c2 100644 --- a/src/OneScript.Language/LexicalAnalysis/FullSourceLexer.cs +++ b/src/OneScript.Language/LexicalAnalysis/FullSourceLexer.cs @@ -23,6 +23,7 @@ public class FullSourceLexer : ILexer private readonly LexerState _commentState = new CommentLexerState(); private readonly LexerState _annotationState = new AnnotationLexerState(); private readonly LexerState _directiveState = new PreprocessorDirectiveLexerState(); + private readonly LexerState _labelState = new LabelLexerState(); private readonly FixedLexerState _fixedState = new FixedLexerState(); @@ -119,6 +120,10 @@ private void SelectState() { _state = _annotationState; } + else if (cs == SpecialChars.Tilde) + { + _state = _labelState; + } else { var cp = _iterator.GetErrorPosition(); diff --git a/src/OneScript.Language/LexicalAnalysis/ISourceCodeIndexer.cs b/src/OneScript.Language/LexicalAnalysis/ISourceCodeIndexer.cs index b6e5b406c..84f9521c1 100644 --- a/src/OneScript.Language/LexicalAnalysis/ISourceCodeIndexer.cs +++ b/src/OneScript.Language/LexicalAnalysis/ISourceCodeIndexer.cs @@ -9,6 +9,6 @@ namespace OneScript.Language.LexicalAnalysis { public interface ISourceCodeIndexer { - string GetCodeLine(int index); + string GetCodeLine(int lineNumber); } } diff --git a/src/OneScript.Language/LexicalAnalysis/LabelLexerState.cs b/src/OneScript.Language/LexicalAnalysis/LabelLexerState.cs new file mode 100644 index 000000000..607b53946 --- /dev/null +++ b/src/OneScript.Language/LexicalAnalysis/LabelLexerState.cs @@ -0,0 +1,60 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Diagnostics; +using OneScript.Localization; + +namespace OneScript.Language.LexicalAnalysis +{ + public class LabelLexerState : LexerState + { + private static BilingualString MESSAGE_NAME_EXPECTED = new BilingualString( + "Ожидается имя метки", + "Label name expected" + ); + + private static BilingualString INVALID_LABEL = new BilingualString( + "Неверно задана метка", + "Invalid label definition" + ); + + private readonly WordLexerState _wordExtractor = new WordLexerState(); + + public override Lexem ReadNextLexem(SourceCodeIterator iterator) + { + Debug.Assert(iterator.CurrentSymbol == SpecialChars.Tilde); + + var start = new CodeRange(iterator.CurrentLine, iterator.CurrentColumn); + iterator.MoveNext(); + if (!iterator.MoveToContent()) + throw CreateExceptionOnCurrentLine(MESSAGE_NAME_EXPECTED.ToString(), iterator); + + if (!char.IsLetter(iterator.CurrentSymbol)) + throw CreateExceptionOnCurrentLine(MESSAGE_NAME_EXPECTED.ToString(), iterator); + + var result = _wordExtractor.ReadNextLexem(iterator); + if (!LanguageDef.IsUserSymbol(result)) + { + throw CreateExceptionOnCurrentLine(INVALID_LABEL.ToString(), iterator); + } + + result.Type = LexemType.LabelRef; + if (iterator.CurrentSymbol == SpecialChars.Colon) + { + result.Type = LexemType.Label; + var tail = iterator.ReadToLineEnd(); + if (tail.Trim().Length != 0) + { + throw CreateExceptionOnCurrentLine(INVALID_LABEL.ToString(), iterator); + } + } + + result.Location = start; + return result; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/LexicalAnalysis/LexemType.cs b/src/OneScript.Language/LexicalAnalysis/LexemType.cs index 48f8c5fc6..86723c470 100644 --- a/src/OneScript.Language/LexicalAnalysis/LexemType.cs +++ b/src/OneScript.Language/LexicalAnalysis/LexemType.cs @@ -22,6 +22,8 @@ public enum LexemType PreprocessorDirective, Annotation, Comment, + Label, + LabelRef, EndOfText } } diff --git a/src/OneScript.Language/LexicalAnalysis/PreprocessorDirectiveLexerState.cs b/src/OneScript.Language/LexicalAnalysis/PreprocessorDirectiveLexerState.cs index 01070638e..95a676ffb 100644 --- a/src/OneScript.Language/LexicalAnalysis/PreprocessorDirectiveLexerState.cs +++ b/src/OneScript.Language/LexicalAnalysis/PreprocessorDirectiveLexerState.cs @@ -11,7 +11,7 @@ namespace OneScript.Language.LexicalAnalysis { public class PreprocessorDirectiveLexerState : LexerState { - WordLexerState _wordExtractor = new WordLexerState(); + private readonly WordLexerState _wordExtractor = new WordLexerState(); const string MESSAGE_DIRECTIVE_EXPECTED = "Ожидается директива препроцессора"; public override Lexem ReadNextLexem(SourceCodeIterator iterator) diff --git a/src/OneScript.Language/LexicalAnalysis/Token.cs b/src/OneScript.Language/LexicalAnalysis/Token.cs index fc3fd7824..aca7738f1 100644 --- a/src/OneScript.Language/LexicalAnalysis/Token.cs +++ b/src/OneScript.Language/LexicalAnalysis/Token.cs @@ -43,7 +43,8 @@ public enum Token RemoveHandler, Async, Await, - + Goto, + // operators Plus, Minus, diff --git a/src/OneScript.Language/Localization/BilingualString.cs b/src/OneScript.Language/Localization/BilingualString.cs index a4aaf9f01..baed18d03 100644 --- a/src/OneScript.Language/Localization/BilingualString.cs +++ b/src/OneScript.Language/Localization/BilingualString.cs @@ -5,6 +5,7 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using System; using System.Globalization; namespace OneScript.Localization @@ -55,6 +56,11 @@ public override string ToString() return Localize(Russian, English); } + public bool HasName(string name, StringComparison comparison = StringComparison.CurrentCultureIgnoreCase) + { + return string.Equals(Russian, name, comparison) || string.Equals(English, name, comparison); + } + public static bool UseRussianLocale => CultureInfo.CurrentCulture.Parent.Equals(RussianCulture); public static string Localize(string russian, string english) diff --git a/src/OneScript.Language/OneScript.Language.csproj b/src/OneScript.Language/OneScript.Language.csproj index b4a2f7bda..404476e0e 100644 --- a/src/OneScript.Language/OneScript.Language.csproj +++ b/src/OneScript.Language/OneScript.Language.csproj @@ -22,6 +22,7 @@ true false + DEBUG;TRACE diff --git a/src/OneScript.Language/Sources/SourceCode.cs b/src/OneScript.Language/Sources/SourceCode.cs index 8d2be16a1..a31e60652 100644 --- a/src/OneScript.Language/Sources/SourceCode.cs +++ b/src/OneScript.Language/Sources/SourceCode.cs @@ -35,9 +35,9 @@ public SourceCodeIterator CreateIterator() public string GetSourceCode() => _source.GetSourceCode(); - public string GetCodeLine(int index) + public string GetCodeLine(int lineNumber) { - return _indexer?.GetCodeLine(index); + return _indexer?.GetCodeLine(lineNumber); } public override string ToString() => Name ?? Location; diff --git a/src/OneScript.Language/SpecialChars.cs b/src/OneScript.Language/SpecialChars.cs index fb46c4f42..cfc96e761 100644 --- a/src/OneScript.Language/SpecialChars.cs +++ b/src/OneScript.Language/SpecialChars.cs @@ -18,6 +18,8 @@ public static class SpecialChars public const char QuestionMark = '?'; public const char Preprocessor = '#'; public const char Annotation = '&'; + public const char Tilde = '~'; + public const char Colon = ':'; public static bool IsOperatorChar(char symbol) { diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/ErrorTerminalNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/ErrorTerminalNode.cs new file mode 100644 index 000000000..a4a3c76f9 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/ErrorTerminalNode.cs @@ -0,0 +1,25 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + /// + /// Нода ошибочного синтаксиса + /// + public class ErrorTerminalNode : TerminalNode + { + public ErrorTerminalNode() : base(NodeKind.Unknown) + { + } + + public ErrorTerminalNode(Lexem lexem) : base(NodeKind.Unknown, lexem) + { + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/LabelNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/LabelNode.cs new file mode 100644 index 000000000..9721e90ce --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/LabelNode.cs @@ -0,0 +1,21 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class LabelNode : LineMarkerNode + { + public LabelNode(Lexem labelLexem) : base(labelLexem.Location, NodeKind.Label) + { + LabelName = labelLexem.Content; + } + + public string LabelName { get; } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/MethodNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/MethodNode.cs index d7de41a27..07aa269a3 100644 --- a/src/OneScript.Language/SyntaxAnalysis/AstNodes/MethodNode.cs +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/MethodNode.cs @@ -17,6 +17,8 @@ public MethodNode() : base(NodeKind.Method) { } + public bool IsAsync { get; set; } + public MethodSignatureNode Signature { get; private set; } public BslSyntaxNode MethodBody { get; private set; } diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/UnaryOperationNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/UnaryOperationNode.cs index dddcaf793..05e9b80cd 100644 --- a/src/OneScript.Language/SyntaxAnalysis/AstNodes/UnaryOperationNode.cs +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/UnaryOperationNode.cs @@ -13,7 +13,7 @@ public class UnaryOperationNode : NonTerminalNode { public Token Operation { get; } - public UnaryOperationNode(Lexem operation) : base(NodeKind.UnaryOperation) + public UnaryOperationNode(Lexem operation) : base(NodeKind.UnaryOperation, operation) { Operation = operation.Token; } diff --git a/src/OneScript.Language/SyntaxAnalysis/BslSyntaxWalker.cs b/src/OneScript.Language/SyntaxAnalysis/BslSyntaxWalker.cs index fbeae0c73..7c2b352da 100644 --- a/src/OneScript.Language/SyntaxAnalysis/BslSyntaxWalker.cs +++ b/src/OneScript.Language/SyntaxAnalysis/BslSyntaxWalker.cs @@ -51,9 +51,19 @@ private void CreateVisitors() _nodeVisitors[(int)NodeKind.RemoveHandler] = VisitHandlerOperation; _nodeVisitors[(int)NodeKind.NewObject] = (x) => VisitNewObjectCreation((NewObjectNode)x); _nodeVisitors[(int)NodeKind.Preprocessor] = (x) => VisitPreprocessorDirective((PreprocessorDirectiveNode)x); + _nodeVisitors[(int)NodeKind.Goto] = (x) => VisitGotoNode((NonTerminalNode)x); + _nodeVisitors[(int)NodeKind.Label] = (x) => VisitLabelNode((LabelNode)x); } + protected virtual void VisitGotoNode(NonTerminalNode node) + { + } + + protected virtual void VisitLabelNode(LabelNode node) + { + } + protected void SetDefaultVisitorFor(NodeKind kind, Action action) { _nodeVisitors[(int)kind] = action; @@ -158,10 +168,20 @@ protected virtual void VisitStatement(BslSyntaxNode statement) VisitGlobalProcedureCall(statement as CallNode); else if (statement.Kind == NodeKind.DereferenceOperation) VisitProcedureDereference(statement); + else if (statement.Kind == NodeKind.UnaryOperation && statement is UnaryOperationNode + { + Operation: Token.Await + } unaryOp) + VisitGlobalAwaitCall(unaryOp); else DefaultVisit(statement); } + private void VisitGlobalAwaitCall(UnaryOperationNode awaitStatement) + { + VisitStatement(awaitStatement.Children[0]); + } + protected virtual void VisitAssignment(BslSyntaxNode assignment) { var left = assignment.Children[0]; diff --git a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs index 87e99f83f..9701bb69b 100644 --- a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs +++ b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs @@ -27,6 +27,7 @@ public class DefaultBslParser private bool _isMethodsDefined; private bool _isStatementsDefined; private bool _isInFunctionScope; + private bool _isInAsyncMethod; private bool _lastDereferenceIsWritable; private readonly Stack _tokenStack = new Stack(); @@ -286,7 +287,8 @@ private void BuildMethodsSection() { if (_lastExtractedLexem.Type != LexemType.Annotation && _lastExtractedLexem.Token != Token.Procedure - && _lastExtractedLexem.Token != Token.Function) + && _lastExtractedLexem.Token != Token.Function + && _lastExtractedLexem.Token != Token.Async) { return; } @@ -301,7 +303,7 @@ private void BuildMethodsSection() while (true) { BuildAnnotations(); - if (_lastExtractedLexem.Token == Token.Procedure || _lastExtractedLexem.Token == Token.Function) + if (IsStartOfMethod(_lastExtractedLexem)) { if (!sectionExist) { @@ -324,14 +326,26 @@ private void BuildMethodsSection() } } + private static bool IsStartOfMethod(in Lexem lex) + { + return lex.Token == Token.Async || lex.Token == Token.Procedure || lex.Token == Token.Function; + } + private void BuildMethod() { - Debug.Assert(_lastExtractedLexem.Token == Token.Procedure || _lastExtractedLexem.Token == Token.Function); + Debug.Assert(IsStartOfMethod(_lastExtractedLexem)); var method = _nodeContext.AddChild(new MethodNode()); ApplyAnnotations(method); PushContext(method); + if (_lastExtractedLexem.Token == Token.Async) + { + method.IsAsync = true; + _isInAsyncMethod = true; + NextLexem(); + } + try { BuildMethodSignature(); @@ -345,6 +359,7 @@ private void BuildMethod() _isInFunctionScope = false; _inMethodScope = false; _isStatementsDefined = false; + _isInAsyncMethod = false; PopContext(); } } @@ -382,9 +397,8 @@ private void BuildMethodBody() private void BuildMethodSignature() { - var isFunction = _lastExtractedLexem.Token == Token.Function; - var signature = _nodeContext.AddChild(new MethodSignatureNode(_lastExtractedLexem)); + var isFunction = _lastExtractedLexem.Token == Token.Function; CreateChild(signature, isFunction? NodeKind.Function : NodeKind.Procedure, _lastExtractedLexem); _isInFunctionScope = isFunction; NextLexem(); @@ -471,7 +485,7 @@ private bool BuildDefaultParameterValue(NonTerminalNode param, NodeKind nodeKind NextLexem(); } - if (LanguageDef.IsLiteral(ref _lastExtractedLexem)) + if (LanguageDef.IsLiteral(_lastExtractedLexem)) { string literalText = _lastExtractedLexem.Content; if (hasSign) @@ -607,6 +621,12 @@ private void BuildCodeBatch(params Token[] endTokens) continue; } + if (_lastExtractedLexem.Type == LexemType.Label) + { + DefineLabel(_lastExtractedLexem); + continue; + } + if (_lastExtractedLexem.Type != LexemType.Identifier && _lastExtractedLexem.Token != Token.EndOfText) { AddError(LocalizedErrors.UnexpectedOperation()); @@ -628,6 +648,13 @@ private void BuildCodeBatch(params Token[] endTokens) PopStructureToken(); } + private void DefineLabel(Lexem label) + { + var node = new LabelNode(label); + _nodeContext.AddChild(node); + NextLexem(); + } + #region Statements private void BuildStatement() @@ -676,7 +703,13 @@ private void BuildComplexStructureStatement() case Token.AddHandler: case Token.RemoveHandler: BuildEventHandlerOperation(_lastExtractedLexem.Token); - break; + break; + case Token.Await: + BuildGlobalCallAwaitOperator(); + break; + case Token.Goto: + BuildGotoOperator(); + break; default: var expected = _tokenStack.Peek(); AddError(LocalizedErrors.TokenExpected(expected)); @@ -684,6 +717,64 @@ private void BuildComplexStructureStatement() } } + private void BuildGlobalCallAwaitOperator() + { + Debug.Assert(_lastExtractedLexem.Token == Token.Await); + + _nodeContext.AddChild(TerminalNode()); + } + + + private BslSyntaxNode BuildExpressionAwaitOperator(Lexem lexem) + { + Debug.Assert(_lastExtractedLexem.Token == Token.Await); + + NextLexem(); + + var argument = SelectTerminalNode(_lastExtractedLexem, false); + if (argument != default) + { + CheckAsyncMethod(); + var awaitOperator = new UnaryOperationNode(lexem); + awaitOperator.AddChild(argument); + return awaitOperator; + } + else if (!_isInAsyncMethod) + { + // это просто переменная Ждать или метод Ждать + return CallOrVariable(lexem); + } + else + { + AddError(LocalizedErrors.ExpressionSyntax()); + return new ErrorTerminalNode(_lastExtractedLexem); + } + } + + private void BuildGotoOperator() + { + var gotoNode = new NonTerminalNode(NodeKind.Goto, _lastExtractedLexem); + NextLexem(); + + if (_lastExtractedLexem.Type != LexemType.LabelRef) + { + AddError(LocalizedErrors.LabelNameExpected()); + } + + gotoNode.AddChild(new LabelNode(_lastExtractedLexem)); + NextLexem(); + + _nodeContext.AddChild(gotoNode); + } + + private void CheckAsyncMethod() + { + if (!_isInAsyncMethod) + { + AddError(LocalizedErrors.AwaitMustBeInAsyncMethod(), false); + } + } + private void BuildIfStatement() { var condition = _nodeContext.AddChild(new ConditionNode(_lastExtractedLexem)); @@ -1019,10 +1110,15 @@ private void BuildAssignment(NonTerminalNode batch) private BslSyntaxNode BuildGlobalCall(Lexem identifier) { - var target = NodeBuilder.CreateNode(NodeKind.Identifier, identifier); NextLexem(); - if (_lastExtractedLexem.Token != Token.OpenPar) + return CallOrVariable(identifier); + } + + private BslSyntaxNode CallOrVariable(Lexem identifier) + { + var target = NodeBuilder.CreateNode(NodeKind.Identifier, identifier); + if (_lastExtractedLexem.Token != Token.OpenPar) { _lastDereferenceIsWritable = true; // одиночный идентификатор } @@ -1097,7 +1193,6 @@ private void BuildCallArgument(NonTerminalNode argsList) } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private void BuildLastDefaultArg(NonTerminalNode argsList) { NextLexem(); @@ -1253,9 +1348,18 @@ private BslSyntaxNode BuildExpressionUpTo(NonTerminalNode parent, Token stopToke else { if (_lastExtractedLexem.Token == Token.EndOfText) + { AddError(LocalizedErrors.UnexpectedEof()); + } else - AddError(LocalizedErrors.ExpressionSyntax()); + { + SkipToNextStatement(new []{stopToken}); + AddError(LocalizedErrors.ExpressionSyntax(), false); + if (_lastExtractedLexem.Token == stopToken) + { + NextLexem(); + } + } node = default; } @@ -1276,7 +1380,6 @@ private void BuildOptionalExpression(NonTerminalNode parent, Token stopToken) #region Operators - [MethodImpl(MethodImplOptions.AggressiveInlining)] private static BslSyntaxNode MakeBinaryOperationNode(BslSyntaxNode firstArg, BslSyntaxNode secondArg, in Lexem lexem) { var node = new BinaryOperationNode(lexem); @@ -1285,7 +1388,6 @@ private static BslSyntaxNode MakeBinaryOperationNode(BslSyntaxNode firstArg, Bsl return node; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private BslSyntaxNode BuildParenthesis() { if (_lastExtractedLexem.Token == Token.OpenPar) @@ -1307,34 +1409,45 @@ private BslSyntaxNode BuildParenthesis() #endregion private BslSyntaxNode TerminalNode() + { + BslSyntaxNode node = SelectTerminalNode(_lastExtractedLexem, true); + if (node == default) + { + AddError(LocalizedErrors.ExpressionSyntax()); + } + + return node; + } + + private BslSyntaxNode SelectTerminalNode(in Lexem currentLexem, bool supportAwait) { BslSyntaxNode node = default; - if (LanguageDef.IsLiteral(ref _lastExtractedLexem)) + if (LanguageDef.IsLiteral(currentLexem)) { - node = NodeBuilder.CreateNode(NodeKind.Constant, _lastExtractedLexem); + node = NodeBuilder.CreateNode(NodeKind.Constant, currentLexem); NextLexem(); } - else if (LanguageDef.IsUserSymbol(in _lastExtractedLexem)) + else if (LanguageDef.IsUserSymbol(currentLexem)) { - node = BuildGlobalCall(_lastExtractedLexem); + node = BuildGlobalCall(currentLexem); } - else if(_lastExtractedLexem.Token == Token.NewObject) + else if(currentLexem.Token == Token.NewObject) { node = BuildNewObjectCreation(); } - else if (_lastExtractedLexem.Token == Token.Question) + else if (currentLexem.Token == Token.Question) { node = BuildQuestionOperator(); } - else if (LanguageDef.IsBuiltInFunction(_lastExtractedLexem.Token)) + else if (LanguageDef.IsBuiltInFunction(currentLexem.Token)) { - node = BuildGlobalCall(_lastExtractedLexem); + node = BuildGlobalCall(currentLexem); } - else + else if (supportAwait && currentLexem.Token == Token.Await) { - AddError(LocalizedErrors.ExpressionSyntax()); + node = BuildExpressionAwaitOperator(currentLexem); } - + return node; } @@ -1486,8 +1599,9 @@ private void NewObjectStaticConstructor(NonTerminalNode node) } } - #endregion - + #endregion + + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void NextLexem() { _lastExtractedLexem = _lexer.NextLexem(); diff --git a/src/OneScript.Language/SyntaxAnalysis/LocalizedErrors.cs b/src/OneScript.Language/SyntaxAnalysis/LocalizedErrors.cs index b2f8b73fe..e0b96014f 100644 --- a/src/OneScript.Language/SyntaxAnalysis/LocalizedErrors.cs +++ b/src/OneScript.Language/SyntaxAnalysis/LocalizedErrors.cs @@ -43,6 +43,9 @@ private static CodeError Create(string ru, string en, [CallerMemberName] string public static CodeError IdentifierExpected() => Create("Ожидается идентификатор", "Identifier expecting"); + + public static CodeError LabelNameExpected() + => Create("Ожидается имя метки", "Label name expected"); public static CodeError ExpressionSyntax() => Create("Ошибка в выражении", "Expression syntax error"); @@ -60,6 +63,11 @@ public static CodeError ExportedLocalVar(string varName) $"Local variable can't be exported ({varName})"); } + public static CodeError AwaitMustBeInAsyncMethod() => Create( + "Оператор Ждать (Await) может употребляться только в асинхронных процедурах или функциях", + "Operator Await can be used only in async procedures or functions" + ); + public static CodeError LiteralExpected() => Create("Ожидается константа", "Constant expected"); public static CodeError NumberExpected() => Create("Ожидается числовая константа", "Numeric constant expected"); @@ -134,5 +142,8 @@ public static CodeError DuplicateMethodDefinition(string methodName) => public static CodeError SymbolNotFound(string symbol) => Create($"Неизвестный символ: {symbol}", $"Symbol not found {symbol}"); + + public static CodeError AsyncMethodsNotSupported() => + Create("Асинхронные методы не поддерживаются", "Async methods aren't supported"); } } \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/NodeKind.cs b/src/OneScript.Language/SyntaxAnalysis/NodeKind.cs index bc76bca70..975941278 100644 --- a/src/OneScript.Language/SyntaxAnalysis/NodeKind.cs +++ b/src/OneScript.Language/SyntaxAnalysis/NodeKind.cs @@ -61,6 +61,8 @@ public enum NodeKind RemoveHandler, Preprocessor, Import, - TopLevelExpression + TopLevelExpression, + Label, + Goto } } \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/RegionDirectiveHandler.cs b/src/OneScript.Language/SyntaxAnalysis/RegionDirectiveHandler.cs index 90a05ad03..d659ce4da 100644 --- a/src/OneScript.Language/SyntaxAnalysis/RegionDirectiveHandler.cs +++ b/src/OneScript.Language/SyntaxAnalysis/RegionDirectiveHandler.cs @@ -12,8 +12,8 @@ namespace OneScript.Language.SyntaxAnalysis { public class RegionDirectiveHandler : DirectiveHandlerBase { - private readonly LexemTrie _preprocRegion = new LexemTrie(); - private readonly LexemTrie _preprocEndRegion = new LexemTrie(); + private readonly IdentifiersTrie _preprocRegion = new IdentifiersTrie(); + private readonly IdentifiersTrie _preprocEndRegion = new IdentifiersTrie(); private int _regionsNesting = 0; diff --git a/src/OneScript.Language/SyntaxAnalysis/SingleWordModuleAnnotationHandler.cs b/src/OneScript.Language/SyntaxAnalysis/SingleWordModuleAnnotationHandler.cs new file mode 100644 index 000000000..73b949967 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/SingleWordModuleAnnotationHandler.cs @@ -0,0 +1,78 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using OneScript.Language.LexicalAnalysis; +using OneScript.Language.SyntaxAnalysis.AstNodes; +using OneScript.Localization; + +namespace OneScript.Language.SyntaxAnalysis +{ + /// + /// Обработчик аннотаций модуля, состоящих из одного слова + /// + public class SingleWordModuleAnnotationHandler : ModuleAnnotationDirectiveHandler + { + private readonly ILexer _allLineContentLexer; + private readonly ISet _knownNames = new HashSet(StringComparer.CurrentCultureIgnoreCase); + + public SingleWordModuleAnnotationHandler(ISet knownNames, IErrorSink errorSink) : base(errorSink) + { + var builder = new LexerBuilder(); + builder.Detect((cs, i) => !char.IsWhiteSpace(cs)) + .HandleWith(new WordLexerState()); + + _allLineContentLexer = builder.Build(); + _knownNames = knownNames; + } + + public SingleWordModuleAnnotationHandler(ISet knownNames, IErrorSink errorSink) : base(errorSink) + { + var builder = new LexerBuilder(); + builder.Detect((cs, i) => !char.IsWhiteSpace(cs)) + .HandleWith(new WordLexerState()); + + _allLineContentLexer = builder.Build(); + + foreach (var twoNames in knownNames) + { + _knownNames.Add(twoNames.Russian); + _knownNames.Add(twoNames.English); + } + } + + protected override bool DirectiveSupported(string directive) + { + return _knownNames.Contains(directive); + } + + protected override void ParseAnnotationInternal(ref Lexem lastExtractedLexem, ILexer lexer, ParserContext parserContext) + { + var node = new AnnotationNode(NodeKind.Annotation, lastExtractedLexem); + _allLineContentLexer.Iterator = lexer.Iterator; + + parserContext.AddChild(node); + + // после ничего не должно находиться + var nextLexem = _allLineContentLexer.NextLexemOnSameLine(); + lastExtractedLexem = lexer.NextLexem(); // сдвиг основного лексера + if (nextLexem.Type != LexemType.EndOfText) + { + var err = LocalizedErrors.ExpressionSyntax(); + err.Position = new ErrorPositionInfo + { + LineNumber = node.Location.LineNumber, + ColumnNumber = node.Location.ColumnNumber, + Code = lexer.Iterator.GetCodeLine(node.Location.LineNumber), + ModuleName = lexer.Iterator.Source.Name + }; + ErrorSink.AddError(err); + } + } + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Compiler/DynamicModule.cs b/src/OneScript.Native/Compiler/DynamicModule.cs index 3c9dbe796..7d0bfb6e4 100644 --- a/src/OneScript.Native/Compiler/DynamicModule.cs +++ b/src/OneScript.Native/Compiler/DynamicModule.cs @@ -5,6 +5,7 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using System; using System.Collections.Generic; using System.Linq; using OneScript.Contexts; @@ -26,5 +27,7 @@ public class DynamicModule : IExecutableModule public BslMethodInfo ModuleBody => Methods.FirstOrDefault(x => x.Name == IExecutableModule.BODY_METHOD_NAME); public SourceCode Source { get; set; } + + public IDictionary Interfaces { get; } = new Dictionary(); } } \ No newline at end of file diff --git a/src/OneScript.Native/Compiler/ModuleCompiler.cs b/src/OneScript.Native/Compiler/ModuleCompiler.cs index 07c200a31..61acb2044 100644 --- a/src/OneScript.Native/Compiler/ModuleCompiler.cs +++ b/src/OneScript.Native/Compiler/ModuleCompiler.cs @@ -5,6 +5,7 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using System; using System.Linq; using OneScript.Compilation; using OneScript.Compilation.Binding; @@ -166,8 +167,22 @@ protected override void VisitModuleVariable(VariableDefinitionNode varNode) } } + protected override void VisitGotoNode(NonTerminalNode node) + { + throw new NotSupportedException(); + } + + protected override void VisitLabelNode(LabelNode node) + { + throw new NotSupportedException(); + } + protected override void VisitMethod(MethodNode methodNode) { + if (methodNode.IsAsync) + { + AddError(LocalizedErrors.AsyncMethodsNotSupported(), methodNode.Location); + } var methodSymbol = Symbols.GetScope(Symbols.ScopeCount - 1).Methods[methodNode.Signature.MethodName]; var methodInfo = (BslNativeMethodInfo)methodSymbol.Method; diff --git a/src/OneScript.Native/Compiler/NativeRuntimeAnnotationHandler.cs b/src/OneScript.Native/Compiler/NativeRuntimeAnnotationHandler.cs index 6ff0b9183..8cdf4ad8c 100644 --- a/src/OneScript.Native/Compiler/NativeRuntimeAnnotationHandler.cs +++ b/src/OneScript.Native/Compiler/NativeRuntimeAnnotationHandler.cs @@ -6,57 +6,28 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using System.Collections.Generic; using OneScript.Language; using OneScript.Language.LexicalAnalysis; using OneScript.Language.SyntaxAnalysis; using OneScript.Language.SyntaxAnalysis.AstNodes; +using OneScript.Localization; namespace OneScript.Native.Compiler { - public class NativeRuntimeAnnotationHandler : ModuleAnnotationDirectiveHandler + public class NativeRuntimeAnnotationHandler : SingleWordModuleAnnotationHandler { - private readonly ILexer _allLineContentLexer; - - public NativeRuntimeAnnotationHandler(IErrorSink errorSink) : base(errorSink) - { - var builder = new LexerBuilder(); - builder.Detect((cs, i) => !char.IsWhiteSpace(cs)) - .HandleWith(new WordLexerState()); - - _allLineContentLexer = builder.Build(); - } - public static string NativeDirectiveName => "native"; public static string StackRuntimeDirectiveName => "stack"; - - protected override bool DirectiveSupported(string directive) - { - return string.Equals(directive, NativeDirectiveName, StringComparison.CurrentCultureIgnoreCase) || - string.Equals(directive, StackRuntimeDirectiveName, StringComparison.CurrentCultureIgnoreCase); - } - protected override void ParseAnnotationInternal(ref Lexem lastExtractedLexem, ILexer lexer, ParserContext parserContext) + private static readonly HashSet Directives = new HashSet + { + NativeDirectiveName, + StackRuntimeDirectiveName + }; + + public NativeRuntimeAnnotationHandler(IErrorSink errorSink) : base(Directives, errorSink) { - var node = new AnnotationNode(NodeKind.Annotation, lastExtractedLexem); - _allLineContentLexer.Iterator = lexer.Iterator; - - parserContext.AddChild(node); - - // после ничего не должно находиться - var nextLexem = _allLineContentLexer.NextLexemOnSameLine(); - lastExtractedLexem = lexer.NextLexem(); // сдвиг основного лексера - if (nextLexem.Type != LexemType.EndOfText) - { - var err = LocalizedErrors.ExpressionSyntax(); - err.Position = new ErrorPositionInfo - { - LineNumber = node.Location.LineNumber, - ColumnNumber = node.Location.ColumnNumber, - Code = lexer.Iterator.GetCodeLine(node.Location.LineNumber), - ModuleName = lexer.Iterator.Source.Name - }; - ErrorSink.AddError(err); - } } } } \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/Binary/BinaryDataBuffer.cs b/src/OneScript.StandardLibrary/Binary/BinaryDataBuffer.cs index 07321e0a3..0eadcb418 100644 --- a/src/OneScript.StandardLibrary/Binary/BinaryDataBuffer.cs +++ b/src/OneScript.StandardLibrary/Binary/BinaryDataBuffer.cs @@ -11,6 +11,7 @@ This Source Code Form is subject to the terms of the using OneScript.Commons; using OneScript.Contexts; using OneScript.Exceptions; +using OneScript.StandardLibrary.Collections; using OneScript.Types; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; @@ -29,21 +30,15 @@ namespace OneScript.StandardLibrary.Binary [ContextClass("БуферДвоичныхДанных", "BinaryDataBuffer")] public class BinaryDataBuffer : AutoCollectionContext { - private bool _readOnly; - private readonly byte[] _buffer; - public BinaryDataBuffer(byte[] buffer, ByteOrderEnum byteOrder = ByteOrderEnum.LittleEndian) { - _buffer = buffer; + Bytes = buffer; ByteOrder = byteOrder; } // для операций с содержимым буфера внутри 1Script // - public byte[] Bytes - { - get { return _buffer; } - } + public byte[] Bytes { get; } /// /// Размер буфера в байтах. @@ -75,7 +70,7 @@ public override void SetIndexedValue(IValue index, IValue val) throw RuntimeException.InvalidArgumentValue(); var idx = (int)index.AsNumber(); - _buffer[idx] = (byte) value; + Bytes[idx] = (byte) value; } /// @@ -92,7 +87,7 @@ public override void SetIndexedValue(IValue index, IValue val) /// /// Число (Number) [ContextProperty("Размер", "Size")] - public long Size => _buffer.LongLength; + public long Size => Bytes.LongLength; /// /// @@ -100,11 +95,7 @@ public override void SetIndexedValue(IValue index, IValue val) /// /// Булево (Boolean) [ContextProperty("ТолькоЧтение", "ReadOnly")] - public bool ReadOnly - { - get { return _readOnly; } - - } + public bool ReadOnly { get; private set; } /// /// @@ -124,9 +115,9 @@ public void Write(int position, BinaryDataBuffer bytes, int number = 0) ThrowIfReadonly(); if (number == 0) - Array.Copy(bytes._buffer, 0, _buffer, position, bytes._buffer.Length); + Array.Copy(bytes.Bytes, 0, Bytes, position, bytes.Bytes.Length); else - Array.Copy(bytes._buffer, 0, _buffer, position, number); + Array.Copy(bytes.Bytes, 0, Bytes, position, number); } private byte[] GetBytes(T value, Converter leConverter, Converter beConverter, IValue byteOrder = null) @@ -158,7 +149,7 @@ private void CopyBytes(int position, byte[] bytes) { for (int i = 0; i < bytes.Length; i++) { - _buffer[position + i] = bytes[i]; + Bytes[position + i] = bytes[i]; } } @@ -258,9 +249,9 @@ private void WriteBitwiseOp(int position, BinaryDataBuffer buffer, int number, F try { - var bytesToCopy = (number == 0 ? buffer._buffer.Length : number); + var bytesToCopy = (number == 0 ? buffer.Bytes.Length : number); for (int i = 0; i < bytesToCopy; i++) - _buffer[i + position] = op(_buffer[i + position], buffer._buffer[i]); + Bytes[i + position] = op(Bytes[i + position], buffer.Bytes[i]); } catch (IndexOutOfRangeException) { @@ -363,7 +354,7 @@ public void WriteBitwiseXor(int position, BinaryDataBuffer bytes, int number = 0 [ContextMethod("Перевернуть", "Reverse")] public BinaryDataBuffer Reverse() { - var bytes = _buffer.Reverse().ToArray(); + var bytes = Bytes.Reverse().ToArray(); return new BinaryDataBuffer(bytes, ByteOrder); } @@ -384,7 +375,7 @@ public BinaryDataBuffer Reverse() [ContextMethod("Получить", "Get")] public int Get(int position) { - return _buffer[position]; + return Bytes[position]; } @@ -427,7 +418,7 @@ public BinaryDataBuffer GetSlice(int position, IValue number = null) public BinaryDataBuffer Read(int position, int number) { var data = new byte[number]; - Array.Copy(_buffer, position, data, 0, number); + Array.Copy(Bytes, position, data, 0, number); return new BinaryDataBuffer(data, ByteOrder); } @@ -453,7 +444,7 @@ private T FromBytes(int position, Func leConverter, Func @@ -540,11 +531,113 @@ public ulong ReadInt64(int position, IValue byteOrder = null) /// /// [ContextMethod("Разделить", "Split")] - public IValue Split(IValue separator) + public ArrayImpl Split(IValue separator) { - throw new NotImplementedException(); + var buffers = ParseParam(separator); + + // Функция поиска требует, чтобы буферы были в порядке убывания размера + buffers.Sort((a, b) => b.Bytes.LongLength.CompareTo(a.Bytes.LongLength)); + return SplitBuffer(buffers.ToArray()); + } + + private static List ParseParam(IValue separator) + { + var rawSeparator = separator?.GetRawValue(); + switch (rawSeparator) + { + case BinaryDataBuffer buffer: + return new List { CheckedBuffer(buffer) }; + + case ArrayImpl array: + { + var buffers = new List(); + + foreach (var element in array) + { + buffers.AddRange(ParseParam(element)); + } + + return buffers; + } + + default: + throw RuntimeException.InvalidArgumentType(); + } + } + + private static BinaryDataBuffer CheckedBuffer(BinaryDataBuffer buffer) + { + if (buffer.Size == 0) + { + throw RuntimeException.InvalidArgumentValue(); + } + + return buffer; + } + + private ArrayImpl SplitBuffer(BinaryDataBuffer[] splitter) + { + var result = new List(); + long start = 0; + var foundPosition = FindFirst(splitter, start); + while (foundPosition.pos != -1) + { + var length = foundPosition.pos - start; + result.Add(new BinaryDataBuffer(Copy(start, length), ByteOrder)); + start = foundPosition.pos + foundPosition.buffer.Size; + foundPosition = FindFirst(splitter, start); + } + + // хвостовой элемент + result.Add(new BinaryDataBuffer(Copy(start, Bytes.LongLength - start))); + return new ArrayImpl(result); } + /// + /// Ищет ближайшее вхождение любого из буферов. Если на одной позиции находятся два и более буфера, берется бОльший. + /// + /// Массив искомых буферов + /// Начальная позиция поиска + /// Буфер и позиция или null, если нет вхождений + private (BinaryDataBuffer buffer, long pos) FindFirst(BinaryDataBuffer[] buffers, long start) + { + var maxI = Size - buffers[buffers.Length - 1].Size; + for (var i = start; i < maxI; i++) + { + foreach (var expectedBuffer in buffers) + { + if (SubsequenceEquals(Bytes, i, expectedBuffer.Bytes)) + { + return (expectedBuffer, i); + } + } + } + + return (null, -1); + } + + private byte[] Copy(long start, long length) + { + if (length == 0) return Array.Empty(); + var partition = new byte[length]; + Array.Copy(Bytes, start, partition, 0, length); + return partition; + } + + private static bool SubsequenceEquals(byte[] sequence, long start, byte[] subsequence) + { + for (long j = 0; j < subsequence.LongLength; j++) + { + if (subsequence[j] != sequence[start + j]) + { + return false; + } + } + + return true; + } + + /// /// /// Создает копию массива. @@ -556,8 +649,8 @@ public IValue Split(IValue separator) [ContextMethod("Скопировать", "Copy")] public BinaryDataBuffer Copy() { - byte[] copy = new byte[_buffer.Length]; - Array.Copy(_buffer, copy, _buffer.Length); + byte[] copy = new byte[Bytes.Length]; + Array.Copy(Bytes, copy, Bytes.Length); return new BinaryDataBuffer(copy, ByteOrder); } @@ -575,11 +668,11 @@ public BinaryDataBuffer Copy() [ContextMethod("Соединить", "Concat")] public BinaryDataBuffer Concat(BinaryDataBuffer buffer) { - var source = buffer._buffer; - var totalLength = _buffer.Length + source.Length; + var source = buffer.Bytes; + var totalLength = Bytes.Length + source.Length; var joinedArray = new byte[totalLength]; - Array.Copy(_buffer, joinedArray, _buffer.Length); - Array.Copy(source, 0, joinedArray, _buffer.Length, source.Length); + Array.Copy(Bytes, joinedArray, Bytes.Length); + Array.Copy(source, 0, joinedArray, Bytes.Length, source.Length); return new BinaryDataBuffer(joinedArray, ByteOrder); } @@ -600,7 +693,7 @@ public void Set(int position, IValue value) { ThrowIfReadonly(); - _buffer[position] = (byte)AsUnsignedLong(value, byte.MaxValue); + Bytes[position] = (byte)AsUnsignedLong(value, byte.MaxValue); } @@ -617,25 +710,25 @@ public void Set(int position, IValue value) [ContextMethod("УстановитьТолькоЧтение", "SetReadOnly")] public void SetReadOnly() { - _readOnly = true; + ReadOnly = true; } public override int Count() { - return _buffer.Length; + return Bytes.Length; } public override IEnumerator GetEnumerator() { - for (long i = 0; i < _buffer.LongLength; i++) + for (long i = 0; i < Bytes.LongLength; i++) { - yield return ValueFactory.Create(_buffer[i]); + yield return ValueFactory.Create(Bytes[i]); } } public void ThrowIfReadonly() { - if (_readOnly) + if (ReadOnly) throw new RuntimeException("Буфер находится в режиме \"Только чтение\""); } } diff --git a/src/OneScript.StandardLibrary/Binary/GlobalBinaryData.cs b/src/OneScript.StandardLibrary/Binary/GlobalBinaryData.cs index 458cdd030..04a29e00f 100644 --- a/src/OneScript.StandardLibrary/Binary/GlobalBinaryData.cs +++ b/src/OneScript.StandardLibrary/Binary/GlobalBinaryData.cs @@ -21,7 +21,7 @@ namespace OneScript.StandardLibrary.Binary [GlobalContext(Category = "Процедуры и функции работы с двоичными данными")] public sealed class GlobalBinaryData : GlobalContextBase { - private static byte[] HexStringToByteArray(String hex) + private static byte[] HexStringToByteArray(string hex) { var newHex = System.Text.RegularExpressions.Regex.Replace(hex, @"[^0-9A-Fa-f]", ""); int numberChars = newHex.Length; @@ -130,6 +130,17 @@ private static string GetStringFromByteBuffer(byte[] buf, Encoding enc) return enc.GetString(buf, startPos, buf.Length - startPos); } + private static void CheckAndThrowIfNull(AutoContext obj) where T : AutoContext + { + if (obj == null) + throw RuntimeException.InvalidArgumentType(); + } + + private static void CheckAndThrowIfNull(AutoContext obj, int argNumber, string argName) where T : AutoContext + { + if (obj == null) + throw RuntimeException.InvalidArgumentType(argNumber, argName); + } public static IAttachableContext CreateInstance() { @@ -150,19 +161,19 @@ public static IAttachableContext CreateInstance() [ContextMethod("СоединитьДвоичныеДанные")] public BinaryDataContext ConcatenateBinaryData(ArrayImpl array) { + CheckAndThrowIfNull(array); + // Сделано на int т.к. BinaryContext.Size имеет тип int; + using var stream = new System.IO.MemoryStream(); - using (var stream = new System.IO.MemoryStream()) + foreach (var cbd in array) { - - foreach (var cbd in array) - { - byte[] buffer = ((BinaryDataContext) cbd.AsObject()).Buffer; - stream.Write(buffer, 0, buffer.Length); - } - stream.Position = 0; - return new BinaryDataContext(stream); + byte[] buffer = ((BinaryDataContext)cbd.AsObject()).Buffer; + stream.Write(buffer, 0, buffer.Length); } + stream.Position = 0; + + return new BinaryDataContext(stream); } /// @@ -174,6 +185,8 @@ public BinaryDataContext ConcatenateBinaryData(ArrayImpl array) [ContextMethod("РазделитьДвоичныеДанные")] public ArrayImpl SplitBinaryData(BinaryDataContext data, long size) { + CheckAndThrowIfNull(data, 1, nameof(data)); + if (size <= 0 || size > Int32.MaxValue) throw RuntimeException.InvalidNthArgumentValue(2); @@ -257,9 +270,10 @@ public BinaryDataBuffer GetBinaryDataBufferFromString(string str, IValue encodin [ContextMethod("ПолучитьСтрокуИзДвоичныхДанных")] public string GetStringFromBinaryData(BinaryDataContext data, IValue encoding = null) { + CheckAndThrowIfNull(data, 1, nameof(data)); + // Получаем кодировку // Из синтаксис помощника если кодировка не задана используем UTF8 - var enc = (encoding != null) ? TextEncodingEnum.GetEncoding(encoding) : Encoding.UTF8; return GetStringFromByteBuffer(data.Buffer, enc); @@ -274,6 +288,8 @@ public string GetStringFromBinaryData(BinaryDataContext data, IValue encoding = [ContextMethod("ПолучитьСтрокуИзБуфераДвоичныхДанных")] public string GetStringFromBinaryDataBuffer(BinaryDataBuffer buffer, IValue encoding = null) { + CheckAndThrowIfNull(buffer, 1, nameof(buffer)); + var enc = (encoding != null) ? TextEncodingEnum.GetEncoding(encoding) : Encoding.UTF8; return GetStringFromByteBuffer(buffer.Bytes, enc); @@ -325,6 +341,8 @@ public BinaryDataBuffer GetBinaryDataBufferFromBase64String(string str) [ContextMethod("ПолучитьBase64СтрокуИзДвоичныхДанных")] public string GetBase64StringFromBinaryData(BinaryDataContext data) { + CheckAndThrowIfNull(data, 1, nameof(data)); + return Convert.ToBase64String(data.Buffer, Base64FormattingOptions.InsertLineBreaks); } @@ -338,6 +356,8 @@ public string GetBase64StringFromBinaryData(BinaryDataContext data) [ContextMethod("ПолучитьBase64СтрокуИзБуфераДвоичныхДанных")] public string GetBase64StringFromBinaryDataBuffer(BinaryDataBuffer buffer) { + CheckAndThrowIfNull(buffer); + return Convert.ToBase64String(buffer.Bytes, Base64FormattingOptions.InsertLineBreaks); } @@ -349,6 +369,8 @@ public string GetBase64StringFromBinaryDataBuffer(BinaryDataBuffer buffer) [ContextMethod("ПолучитьДвоичныеДанныеИзBase64ДвоичныхДанных")] public BinaryDataContext GetBinaryDataFromBase64BinaryData(BinaryDataContext data) { + CheckAndThrowIfNull(data); + try { var enc = new UTF8Encoding(false,true); @@ -369,6 +391,8 @@ public BinaryDataContext GetBinaryDataFromBase64BinaryData(BinaryDataContext dat [ContextMethod("ПолучитьБуферДвоичныхДанныхИзBase64БуфераДвоичныхДанных")] public BinaryDataBuffer GetBinaryDataBufferFromBase64BinaryDataBuffer(BinaryDataBuffer buffer) { + CheckAndThrowIfNull(buffer); + try { var enc = new UTF8Encoding(false, true); @@ -391,6 +415,8 @@ public BinaryDataBuffer GetBinaryDataBufferFromBase64BinaryDataBuffer(BinaryData [ContextMethod("ПолучитьBase64ДвоичныеДанныеИзДвоичныхДанных")] public BinaryDataContext GetBase64BinaryDataFromBinaryData(BinaryDataContext data) { + CheckAndThrowIfNull(data); + var base64str = Convert.ToBase64String(data.Buffer, Base64FormattingOptions.InsertLineBreaks); return new BinaryDataContext(Encoding.ASCII.GetBytes(base64str)); } @@ -405,6 +431,8 @@ public BinaryDataContext GetBase64BinaryDataFromBinaryData(BinaryDataContext dat [ContextMethod("ПолучитьBase64БуферДвоичныхДанныхИзБуфераДвоичныхДанных")] public BinaryDataBuffer GetBase64BinaryDataBufferFromBinaryDataBuffer(BinaryDataBuffer buffer) { + CheckAndThrowIfNull(buffer); + var base64str = Convert.ToBase64String(buffer.Bytes, Base64FormattingOptions.InsertLineBreaks); return new BinaryDataBuffer(Encoding.ASCII.GetBytes(base64str)); } @@ -439,6 +467,8 @@ public BinaryDataBuffer GetBinaryDataBufferFromHexString(string hex) [ContextMethod("ПолучитьHexСтрокуИзДвоичныхДанных")] public string GetHexStringFromBinaryData(BinaryDataContext data) { + CheckAndThrowIfNull(data); + return BitConverter.ToString(data.Buffer).Replace("-",""); } @@ -450,6 +480,8 @@ public string GetHexStringFromBinaryData(BinaryDataContext data) [ContextMethod("ПолучитьHexСтрокуИзБуфераДвоичныхДанных")] public string GetHexStringFromBinaryDataBuffer(BinaryDataBuffer buffer) { + CheckAndThrowIfNull(buffer); + return BitConverter.ToString(buffer.Bytes).Replace("-",""); } @@ -461,6 +493,8 @@ public string GetHexStringFromBinaryDataBuffer(BinaryDataBuffer buffer) [ContextMethod("ПолучитьДвоичныеДанныеИзHexДвоичныхДанных")] public BinaryDataContext GetBinaryDataFromHexBinaryData(BinaryDataContext data) { + CheckAndThrowIfNull(data); + return new BinaryDataContext(HexArrayToByteArray(data.Buffer)); } @@ -472,6 +506,8 @@ public BinaryDataContext GetBinaryDataFromHexBinaryData(BinaryDataContext data) [ContextMethod("ПолучитьБуферДвоичныхДанныхИзHexБуфераДвоичныхДанных")] public BinaryDataBuffer GetBinaryDataBufferFromHexBinaryDataBuffer(BinaryDataBuffer buffer) { + CheckAndThrowIfNull(buffer); + return new BinaryDataBuffer(HexArrayToByteArray(buffer.Bytes)); } @@ -507,6 +543,8 @@ public BinaryDataBuffer GetHexBinaryDataBufferFromBinaryDataBuffer(BinaryDataBuf [ContextMethod("ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных")] public BinaryDataBuffer GetBinaryDataBufferFromBinaryData(BinaryDataContext data) { + CheckAndThrowIfNull(data); + return new BinaryDataBuffer(data.Buffer); } @@ -518,6 +556,8 @@ public BinaryDataBuffer GetBinaryDataBufferFromBinaryData(BinaryDataContext data [ContextMethod("ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных")] public BinaryDataContext GetBinaryDataFromBinaryDataBuffer(BinaryDataBuffer buffer) { + CheckAndThrowIfNull(buffer); + return new BinaryDataContext(buffer.Bytes); } diff --git a/src/OneScript.StandardLibrary/Collections/Indexes/CollectionIndex.cs b/src/OneScript.StandardLibrary/Collections/Indexes/CollectionIndex.cs new file mode 100644 index 000000000..e1ff63ab8 --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/Indexes/CollectionIndex.cs @@ -0,0 +1,134 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using ScriptEngine.Types; + +namespace OneScript.StandardLibrary.Collections.Indexes +{ + [ContextClass("ИндексКоллекции", "CollectionIndex", TypeUUID = "48D150D4-A0DA-47CA-AEA3-D4078A731C11")] + public class CollectionIndex : AutoCollectionContext + { + private static readonly TypeDescriptor _objectType = typeof(CollectionIndex).GetTypeFromClassMarkup(); + + private readonly List _fields = new List(); + private readonly IIndexCollectionSource _source; + + private readonly IDictionary> _data = + new Dictionary>(); + + public CollectionIndex(IIndexCollectionSource source, IEnumerable fields) : base(_objectType) + { + _source = source; + _fields.AddRange(fields); + } + + internal bool CanBeUsedFor(IEnumerable searchFields) + { + return _fields.Any() && _fields.ToHashSet().IsSubsetOf(searchFields.ToHashSet()); + } + + private CollectionIndexKey IndexKey(PropertyNameIndexAccessor source) + { + return CollectionIndexKey.Extract(source, _fields); + } + + public override string ToString() + { + return string.Join(", ", _fields.Select(field => _source.GetName(field))); + } + + public IEnumerable GetData(PropertyNameIndexAccessor searchCriteria) + { + var key = IndexKey(searchCriteria); + return _data.TryGetValue(key, out var filteredData) ? filteredData : new List(); + } + + internal void FieldRemoved(IValue field) + { + if (_fields.Contains(field)) + { + while (_fields.Contains(field)) _fields.Remove(field); + Rebuild(); + } + } + + internal void ElementAdded(PropertyNameIndexAccessor element) + { + var key = CollectionIndexKey.Extract(element, _fields); + if (_data.TryGetValue(key, out var list)) + { + list.Add(element); + } + else + { + _data.Add(key, new List { element}); + } + } + + internal void ElementRemoved(PropertyNameIndexAccessor element) + { + var key = CollectionIndexKey.Extract(element, _fields); + if (_data.TryGetValue(key, out var value)) + { + value.Remove(element); + } + } + + internal void Clear() + { + _data.Clear(); + } + + internal void Rebuild() + { + _data.Clear(); + foreach (var value in _source) + { + ElementAdded(value); + } + } + + public override IValue GetIndexedValue(IValue index) + { + var rawValue = index.GetRawValue(); + if (rawValue is BslNumericValue numericValue) + { + var numeric = numericValue.AsNumber(); + if (numeric >= 0 && numeric < _fields.Count) + { + + return ValueFactory.Create(_source.GetName(_fields[decimal.ToInt32(numeric)])); + } + } + throw RuntimeException.InvalidArgumentValue(); + } + + public override int Count() + { + return _fields.Count; + } + + public override IEnumerator GetEnumerator() + { + foreach (var field in _fields) + { + yield return ValueFactory.Create(_source.GetName(field)); + } + } + } +} diff --git a/src/OneScript.StandardLibrary/Collections/Indexes/CollectionIndexKey.cs b/src/OneScript.StandardLibrary/Collections/Indexes/CollectionIndexKey.cs new file mode 100644 index 000000000..e33a9090b --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/Indexes/CollectionIndexKey.cs @@ -0,0 +1,84 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Collections.Generic; +using System.Linq; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Collections.Indexes +{ + public class CollectionIndexKey + { + private readonly IDictionary _values; + private readonly int _hashCode; + + private CollectionIndexKey(IDictionary values) + { + _values = values; + _hashCode = EvalHashCode(values.Values); + } + + private static int EvalHashCode(IEnumerable values) + { + var result = 0; + foreach (var value in values) + { + result ^= value.GetHashCode(); + } + + return result; + } + + public static CollectionIndexKey Extract(PropertyNameIndexAccessor source, IEnumerable fields) + { + var values = new Dictionary(); + + foreach (var field in fields) + { + var value = source.GetIndexedValue(field); + values.TryAdd(field, value); + } + + return new CollectionIndexKey(values); + } + + public override bool Equals(object obj) + { + if (object.ReferenceEquals(this, obj)) return true; + if (obj is CollectionIndexKey casted) + { + var allKeys = CombinedKeysSet(casted); + + foreach (var key in allKeys) + { + var thisValue = _values[key]; + var otherValue = _values[key]; + + if (!thisValue.Equals(otherValue)) return false; + } + + return true; + } + + return false; + } + + public override int GetHashCode() + { + return _hashCode; + } + + private ISet CombinedKeysSet(CollectionIndexKey other) + { + var allKeys = new HashSet(); + allKeys.UnionWith(_values.Keys.ToHashSet()); + allKeys.UnionWith(other._values.Keys.ToHashSet()); + return allKeys; + } + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/Collections/Indexes/IIndexCollectionSource.cs b/src/OneScript.StandardLibrary/Collections/Indexes/IIndexCollectionSource.cs new file mode 100644 index 000000000..9ffd3d8f3 --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/Indexes/IIndexCollectionSource.cs @@ -0,0 +1,30 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Collections.Indexes +{ + public interface IIndexCollectionSource : ICollectionContext + { + /// + /// Возвращает имя поля + /// + /// Поле + /// Строка. Имя поля + string GetName(IValue field); + + /// + /// Возвращает поле по имени. + /// + /// Имя поля + /// Поле + IValue GetField(string name); + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/Collections/MapImpl.cs b/src/OneScript.StandardLibrary/Collections/MapImpl.cs index 4ba66aae1..5e0cb9956 100644 --- a/src/OneScript.StandardLibrary/Collections/MapImpl.cs +++ b/src/OneScript.StandardLibrary/Collections/MapImpl.cs @@ -73,6 +73,14 @@ internal bool ContainsKey(IValue key) return _content.ContainsKey(key); } + public IEnumerable Keys() + { + foreach (var key in _content.Keys) + { + yield return key; + } + } + #region ICollectionContext Members [ContextMethod("Вставить", "Insert")] diff --git a/src/OneScript.StandardLibrary/Collections/ValueTable/CollectionIndexes.cs b/src/OneScript.StandardLibrary/Collections/ValueTable/CollectionIndexes.cs index 090e757d8..c1f82f98c 100644 --- a/src/OneScript.StandardLibrary/Collections/ValueTable/CollectionIndexes.cs +++ b/src/OneScript.StandardLibrary/Collections/ValueTable/CollectionIndexes.cs @@ -9,7 +9,10 @@ This Source Code Form is subject to the terms of the using System.Collections.Generic; using System.Linq; using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Collections.Indexes; using OneScript.Types; +using OneScript.Values; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; using ScriptEngine.Types; @@ -22,15 +25,18 @@ public class CollectionIndexes : AutoCollectionContext _indexes = new List(); + private readonly IIndexCollectionSource _owner; - public CollectionIndexes() : base(_instanceType) + public CollectionIndexes(IIndexCollectionSource owner) : base(_instanceType) { + _owner = owner; } [ContextMethod("Добавить", "Add")] public CollectionIndex Add(string columns) { - CollectionIndex newIndex = new CollectionIndex(); + var newIndex = new CollectionIndex(_owner, BuildFieldList(_owner, columns)); + newIndex.Rebuild(); _indexes.Add(newIndex); return newIndex; } @@ -42,13 +48,9 @@ public override int Count() } [ContextMethod("Удалить", "Delete")] - public void Delete(IValue Index) + public void Delete(IValue index) { - Index = Index.GetRawValue(); - if (Index is CollectionIndex) - _indexes.Remove(Index as CollectionIndex); - else - _indexes.RemoveAt(Decimal.ToInt32(Index.AsNumber())); + _indexes.Remove(GetIndex(index)); } [ContextMethod("Очистить", "Clear")] @@ -57,6 +59,102 @@ public void Clear() _indexes.Clear(); } + public override IValue GetIndexedValue(IValue index) + { + return GetIndex(index); + } + + private CollectionIndex GetIndex(IValue index) + { + index = index.GetRawValue(); + if (index is CollectionIndex collectionIndex) + { + if (_indexes.Contains(collectionIndex)) + { + return collectionIndex; + } + throw RuntimeException.InvalidArgumentValue(); + } + + if (index is BslNumericValue numberValue) + { + var number = numberValue.AsNumber(); + if (number >= 0 && number < _indexes.Count) + { + return _indexes[decimal.ToInt32(number)]; + } + + throw RuntimeException.InvalidArgumentValue(); + } + + throw RuntimeException.InvalidArgumentType(); + } + + public void Rebuild() + { + foreach (var index in _indexes) + { + index.Rebuild(); + } + } + + internal void ClearIndexes() + { + foreach (var index in _indexes) + { + index.Clear(); + } + } + + internal void FieldRemoved(IValue field) + { + foreach (var index in _indexes) + { + index.FieldRemoved(field); + } + } + + internal void ElementAdded(PropertyNameIndexAccessor element) + { + foreach (var index in _indexes) + { + index.ElementAdded(element); + } + } + + internal void ElementRemoved(PropertyNameIndexAccessor element) + { + foreach (var index in _indexes) + { + index.ElementRemoved(element); + } + } + + public CollectionIndex FindSuitableIndex(IEnumerable searchFields) + { + return _indexes.FirstOrDefault(index => index.CanBeUsedFor(searchFields)); + } + + private static IList BuildFieldList(IIndexCollectionSource source, string fieldList) + { + var fields = new List(); + var fieldNames = fieldList.Split(','); + foreach (var fieldName in fieldNames) + { + if (!string.IsNullOrWhiteSpace(fieldName)) + { + var field = source.GetField(fieldName.Trim()); + if (field == null) + { + throw new ColumnNotFoundException(fieldName); + } + fields.Add(field); + } + } + + return fields; + } + public override IEnumerator GetEnumerator() { foreach (var item in _indexes) diff --git a/src/OneScript.StandardLibrary/Collections/ValueTable/ColumnNotFoundException.cs b/src/OneScript.StandardLibrary/Collections/ValueTable/ColumnNotFoundException.cs new file mode 100644 index 000000000..84d11bffe --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/ValueTable/ColumnNotFoundException.cs @@ -0,0 +1,29 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using OneScript.Exceptions; +using OneScript.Localization; + +namespace OneScript.StandardLibrary.Collections.ValueTable +{ + public class ColumnNotFoundException : RuntimeException + { + public ColumnNotFoundException(BilingualString message, Exception innerException) : base(message, + innerException) + { + } + + public ColumnNotFoundException(BilingualString message) : base(message) + { + } + + public ColumnNotFoundException(string columnName) : base($"Неверное имя колонки {columnName}") + { + } + + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTable.cs b/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTable.cs index eb52b0a8f..4c1239d4c 100644 --- a/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTable.cs +++ b/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTable.cs @@ -9,6 +9,7 @@ This Source Code Form is subject to the terms of the using System.Collections.Generic; using System.Linq; using OneScript.Contexts; +using OneScript.StandardLibrary.Collections.Indexes; using OneScript.Exceptions; using OneScript.Types; using ScriptEngine.Machine; @@ -21,17 +22,15 @@ namespace OneScript.StandardLibrary.Collections.ValueTable /// Представляет из себя коллекцию строк с заранее заданной структурой. /// [ContextClass("ТаблицаЗначений", "ValueTable")] - public class ValueTable : AutoCollectionContext + public class ValueTable : AutoCollectionContext, IIndexCollectionSource { - private readonly ValueTableColumnCollection _columns; private readonly List _rows; - private readonly CollectionIndexes _indexes; public ValueTable() { - _columns = new ValueTableColumnCollection(this); + Columns = new ValueTableColumnCollection(this); _rows = new List(); - _indexes = new CollectionIndexes(); + Indexes = new CollectionIndexes(this); } /// @@ -39,20 +38,14 @@ public ValueTable() /// /// КоллекцияКолонокТаблицыЗначений [ContextProperty("Колонки", "Columns")] - public ValueTableColumnCollection Columns - { - get { return _columns; } - } + public ValueTableColumnCollection Columns { get; } /// /// Коллекция индексов /// /// ИндексыКоллекции [ContextProperty("Индексы", "Indexes")] - public CollectionIndexes Indexes - { - get { return _indexes; } - } + public CollectionIndexes Indexes { get; } /// /// Количество строк в Таблице значений @@ -71,8 +64,9 @@ public override int Count() [ContextMethod("Добавить", "Add")] public ValueTableRow Add() { - ValueTableRow row = new ValueTableRow(this); + var row = new ValueTableRow(this); _rows.Add(row); + Indexes.ElementAdded(row); return row; } @@ -84,8 +78,9 @@ public ValueTableRow Add() [ContextMethod("Вставить", "Insert")] public ValueTableRow Insert(int index) { - ValueTableRow row = new ValueTableRow(this); + var row = new ValueTableRow(this); _rows.Insert(index, row); + Indexes.ElementAdded(row); return row; } @@ -99,8 +94,8 @@ public ValueTableRow Insert(int index) [ContextMethod("Удалить", "Delete")] public void Delete(IValue row) { - int index = IndexByValue(row); - + var index = IndexByValue(row); + Indexes.ElementRemoved(_rows[index]); _rows.RemoveAt(index); } @@ -120,10 +115,14 @@ public void LoadColumn(IValue values, IValue columnIndex) var row_iterator = _rows.GetEnumerator(); var array_iterator = (values as ArrayImpl).GetEnumerator(); + Indexes.ClearIndexes(); + while (row_iterator.MoveNext() && array_iterator.MoveNext()) { row_iterator.Current.Set(columnIndex, array_iterator.Current); } + + Indexes.Rebuild(); } /// @@ -138,9 +137,9 @@ public void LoadColumn(IValue values, IValue columnIndex) [ContextMethod("ВыгрузитьКолонку", "UnloadColumn")] public ArrayImpl UnloadColumn(IValue column) { - ArrayImpl result = new ArrayImpl(); + var result = new ArrayImpl(); - foreach (ValueTableRow row in _rows) + foreach (var row in _rows) { result.Add(row.Get(column)); } @@ -159,10 +158,10 @@ private List GetProcessingColumnList(string ColumnNames, bool return processing_list; } - foreach (string column_name in ColumnNames.Split(',')) + foreach (var column_name in ColumnNames.Split(',')) { - string name = column_name.Trim(); - ValueTableColumn Column = Columns.FindColumnByName(name); + var name = column_name.Trim(); + var Column = Columns.FindColumnByName(name); if (Column == null) throw WrongColumnNameException(name); @@ -173,8 +172,7 @@ private List GetProcessingColumnList(string ColumnNames, bool } else if (!EmptyListInCaseOfNull) { - foreach (ValueTableColumn Column in _columns) - processing_list.Add(Column); + processing_list.AddRange(Columns); } return processing_list; } @@ -187,14 +185,16 @@ private List GetProcessingColumnList(string ColumnNames, bool [ContextMethod("ЗаполнитьЗначения", "FillValues")] public void FillValues(IValue value, string columnNames = null) { - List processing_list = GetProcessingColumnList(columnNames); - foreach (ValueTableRow row in _rows) + var processing_list = GetProcessingColumnList(columnNames); + Indexes.ClearIndexes(); + foreach (var row in _rows) { - foreach (ValueTableColumn col in processing_list) + foreach (var col in processing_list) { row.Set(col, value); } } + Indexes.Rebuild(); } /// @@ -207,8 +207,8 @@ public int IndexOf(IValue row) { row = row.GetRawValue(); - if (row is ValueTableRow) - return _rows.IndexOf(row as ValueTableRow); + if (row is ValueTableRow tableRow) + return _rows.IndexOf(tableRow); return -1; } @@ -225,13 +225,13 @@ public int IndexOf(IValue row) [ContextMethod("Итог", "Total")] public IValue Total(IValue columnIndex) { - ValueTableColumn Column = Columns.GetColumnByIIndex(columnIndex); + var Column = Columns.GetColumnByIIndex(columnIndex); bool has_data = false; decimal Result = 0; - foreach (ValueTableRow row in _rows) + foreach (var row in _rows) { - IValue current_value = row.Get(Column); + var current_value = row.Get(Column); if (current_value.SystemType == BasicTypes.Number) { has_data = true; @@ -239,10 +239,7 @@ public IValue Total(IValue columnIndex) } } - if (has_data) - return ValueFactory.Create(Result); - - return ValueFactory.Create(); + return has_data ? ValueFactory.Create(Result) : ValueFactory.Create(); } /// @@ -255,12 +252,12 @@ public IValue Total(IValue columnIndex) [ContextMethod("Найти", "Find")] public IValue Find(IValue value, string columnNames = null) { - List processing_list = GetProcessingColumnList(columnNames); + var processing_list = GetProcessingColumnList(columnNames); foreach (ValueTableRow row in _rows) { - foreach (ValueTableColumn col in processing_list) + foreach (var col in processing_list) { - IValue current = row.Get(col); + var current = row.Get(col); if (value.Equals(current)) return row; } @@ -270,9 +267,9 @@ public IValue Find(IValue value, string columnNames = null) private bool CheckFilterCriteria(ValueTableRow Row, StructureImpl Filter) { - foreach (KeyAndValueImpl kv in Filter) + foreach (var kv in Filter) { - ValueTableColumn Column = Columns.FindColumnByName(kv.Key.AsString()); + var Column = Columns.FindColumnByName(kv.Key.AsString()); if (Column == null) throw WrongColumnNameException(kv.Key.AsString()); @@ -294,15 +291,32 @@ public ArrayImpl FindRows(StructureImpl filter) if (filter == null) throw RuntimeException.InvalidArgumentType(); - ArrayImpl Result = new ArrayImpl(); + var result = new ArrayImpl(); - foreach (ValueTableRow row in _rows) + var mapped = ColumnsMap(filter); + var suitableIndex = Indexes.FindSuitableIndex(mapped.Keys()); + var dataToScan = suitableIndex != null ? suitableIndex.GetData(mapped) : _rows; + + foreach (var element in dataToScan) { + var row = (ValueTableRow)element; if (CheckFilterCriteria(row, filter)) - Result.Add(row); + result.Add(row); } - return Result; + return result; + } + + private MapImpl ColumnsMap(StructureImpl filter) + { + var result = new MapImpl(); + foreach (var kv in filter) + { + var key = Columns.FindColumnByName(kv.Key.AsString()); + result.Insert(key, kv.Value); + } + + return result; } /// @@ -312,6 +326,7 @@ public ArrayImpl FindRows(StructureImpl filter) public void Clear() { _rows.Clear(); + Indexes.Clear(); } /// @@ -336,62 +351,72 @@ public ValueTableRow Get(int index) [ContextMethod("Свернуть", "GroupBy")] public void GroupBy(string groupColumnNames, string aggregateColumnNames = null) { - List GroupColumns = GetProcessingColumnList(groupColumnNames, true); - List AggregateColumns = GetProcessingColumnList(aggregateColumnNames, true); + var GroupColumns = GetProcessingColumnList(groupColumnNames, true); + var AggregateColumns = GetProcessingColumnList(aggregateColumnNames, true); - foreach (ValueTableColumn group_column in GroupColumns ) - if ( AggregateColumns.Find(x => x.Name==group_column.Name)!=null ) - throw ColumnsMixedException(group_column.Name); + CheckMixedColumns(GroupColumns, AggregateColumns); var uniqueRows = new Dictionary(new RowsByColumnsEqComparer(GroupColumns) ); int new_idx = 0; - foreach (ValueTableRow row in _rows) + foreach (var row in _rows) { - if (uniqueRows.ContainsKey(row)) + if (!uniqueRows.TryGetValue(row, out var destination)) { - ValueTableRow old_row = uniqueRows[row]; - - foreach (var Column in AggregateColumns) - { - IValue current = row.Get(Column); - if (current.SystemType == BasicTypes.Number) - { - decimal sum = old_row.Get(Column).AsNumber() + current.AsNumber(); - old_row.Set(Column, ValueFactory.Create(sum)); - } - } + destination = _rows[new_idx++]; + CopyRowData(row, destination, GroupColumns); + CopyRowData(row, destination, AggregateColumns); + uniqueRows.Add(destination, destination); } else { - ValueTableRow new_row = _rows[new_idx++]; - - foreach (var Column in GroupColumns) - new_row.Set(Column, row.Get(Column)); - - foreach (var Column in AggregateColumns) - if (row.Get(Column).SystemType != BasicTypes.Number) - new_row.Set(Column, ValueFactory.Create(0)); - else - new_row.Set(Column, row.Get(Column)); - - uniqueRows.Add(new_row, new_row); + AppendRowData(row, destination, AggregateColumns); } } _rows.RemoveRange(new_idx, _rows.Count()-new_idx); int i = 0; - while (i < _columns.Count()) + while (i < Columns.Count()) { - ValueTableColumn Column = _columns.FindColumnByIndex(i); - if (GroupColumns.IndexOf(Column) == -1 && AggregateColumns.IndexOf(Column) == -1) - _columns.Delete(Column); + var column = Columns.FindColumnByIndex(i); + if (GroupColumns.IndexOf(column) == -1 && AggregateColumns.IndexOf(column) == -1) + Columns.Delete(column); else ++i; } } + private void CheckMixedColumns(List groupColumns, List aggregateColumns) + { + foreach (var groupColumn in groupColumns ) + if ( aggregateColumns.Find(x => x.Name==groupColumn.Name)!=null ) + throw ColumnsMixedException(groupColumn.Name); + } + + private void CopyRowData(ValueTableRow source, ValueTableRow dest, IEnumerable columns) + { + foreach (var column in columns) + dest.Set(column, source.Get(column)); + } + + private void AppendRowData(ValueTableRow source, ValueTableRow dest, IEnumerable columns) + { + foreach (var column in columns) + { + var value1 = GetNumeric(source, column); + var value2 = GetNumeric(dest, column); + dest.Set(column, ValueFactory.Add(value1, value2)); + } + } + + private IValue GetNumeric(ValueTableRow row, ValueTableColumn column) + { + var value = row.Get(column); + if (value.SystemType == BasicTypes.Number) return value; + return ValueFactory.Create(0); + } + private class RowsByColumnsEqComparer : IEqualityComparer { private List _columns; @@ -426,9 +451,9 @@ private int IndexByValue(IValue item) int index; - if (item is ValueTableRow) + if (item is ValueTableRow row) { - index = IndexOf(item as ValueTableRow); + index = IndexOf(row); if (index == -1) throw new RuntimeException("Строка не принадлежит таблице значений"); } @@ -490,11 +515,10 @@ public void Move(IValue row, int offset) [ContextMethod("СкопироватьКолонки", "CopyColumns")] public ValueTable CopyColumns(string columnNames = null) { - ValueTable Result = new ValueTable(); - - List columns = GetProcessingColumnList(columnNames); + var Result = new ValueTable(); + var columns = GetProcessingColumnList(columnNames); - foreach (ValueTableColumn Column in columns) + foreach (var Column in columns) { Result.Columns.Add(Column.Name, Column.ValueType, Column.Title, Column.Width); } @@ -516,8 +540,8 @@ public ValueTable CopyColumns(string columnNames = null) [ContextMethod("Скопировать", "Copy")] public ValueTable Copy(IValue rows = null, string columnNames = null) { - ValueTable Result = CopyColumns(columnNames); - List columns = GetProcessingColumnList(columnNames); + var Result = CopyColumns(columnNames); + var columns = GetProcessingColumnList(columnNames); IEnumerable requestedRows; if (rows == null) @@ -544,8 +568,8 @@ public ValueTable Copy(IValue rows = null, string columnNames = null) foreach (var row in requestedRows) { - ValueTableRow new_row = Result.Add(); - foreach (ValueTableColumn Column in columns) + var new_row = Result.Add(); + foreach (var Column in columns) { new_row.Set(columnMap[Column], row.Get(Column)); } @@ -672,6 +696,11 @@ public void ChooseRow(string title = null, IValue startRow = null) throw new NotSupportedException(); } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + public override IEnumerator GetEnumerator() { foreach (var item in _rows) @@ -706,5 +735,17 @@ private static RuntimeException ColumnsMixedException(string columnName) { return new RuntimeException(string.Format("Колонка '{0}' не может одновременно быть колонкой группировки и колонкой суммирования", columnName)); } + + public string GetName(IValue field) + { + if (field is ValueTableColumn column) + return column.Name; + throw RuntimeException.InvalidArgumentType(nameof(field)); + } + + public IValue GetField(string name) + { + return Columns.FindColumnByName(name); + } } } diff --git a/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableColumn.cs b/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableColumn.cs index 5c9ce285c..3c562e0b9 100644 --- a/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableColumn.cs +++ b/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableColumn.cs @@ -26,16 +26,18 @@ public class ValueTableColumn : AutoContext private TypeDescription _valueType; private int _width; private readonly WeakReference _owner; + private readonly int _id; private static TypeDescriptor _instanceType = typeof(ValueTableColumn).GetTypeFromClassMarkup(); - public ValueTableColumn(ValueTableColumnCollection owner, string name, string title, TypeDescription type, int width) + public ValueTableColumn(ValueTableColumnCollection owner, int id, string name, string title, TypeDescription type, int width) : base(_instanceType) { _name = name; _title = title; _valueType = type ?? new TypeDescription(); _width = width; + _id = id; _owner = new WeakReference(owner); } @@ -92,5 +94,10 @@ public int Width get { return _width; } set { _width = value; } } + + public override int GetHashCode() + { + return _id; + } } } diff --git a/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableColumnCollection.cs b/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableColumnCollection.cs index 578e6b01a..2f4134b66 100644 --- a/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableColumnCollection.cs +++ b/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableColumnCollection.cs @@ -7,6 +7,7 @@ This Source Code Form is subject to the terms of the using System; using System.Collections.Generic; +using System.Linq; using OneScript.Commons; using OneScript.Contexts; using OneScript.Exceptions; @@ -27,6 +28,7 @@ public class ValueTableColumnCollection : DynamicPropertiesAccessor, ICollection private readonly List _columns = new List(); private readonly StringComparer _namesComparer = StringComparer.OrdinalIgnoreCase; private readonly ValueTable _owner; + private int maxColumnId = 0; private static readonly TypeDescriptor _objectType = typeof(ValueTableColumnCollection).GetTypeFromClassMarkup(); @@ -50,7 +52,7 @@ public ValueTableColumn Add(string name, TypeDescription type = null, string tit if (FindColumnByName(name) != null) throw new RuntimeException("Неверное имя колонки " + name); - var column = new ValueTableColumn(this, name, title, type, width); + var column = new ValueTableColumn(this, ++maxColumnId, name, title, type, width); _columns.Add(column); return column; @@ -71,7 +73,7 @@ public ValueTableColumn Insert(int index, string name, TypeDescription type = nu if (FindColumnByName(name) != null) throw new RuntimeException("Неверное имя колонки " + name); - ValueTableColumn column = new ValueTableColumn(this, name, title, type, width); + ValueTableColumn column = new ValueTableColumn(this, ++maxColumnId, name, title, type, width); _columns.Insert(index, column); return column; @@ -125,10 +127,26 @@ public void Delete(IValue column) { column = column.GetRawValue(); var vtColumn = GetColumnByIIndex(column); - _owner.ForEach(x=>x.OnOwnerColumnRemoval(vtColumn)); + _owner.ForEach((ValueTableRow x)=> + { + x.OnOwnerColumnRemoval(vtColumn); + }); + _owner.Indexes.FieldRemoved(column); _columns.Remove(vtColumn); } + /// + /// Удаляет все колонки + /// + [ContextMethod("Очистить", "Clear")] + public void Clear() + { + while (_columns.Any()) + { + Delete(_columns[0]); + } + } + public ValueTableColumn FindColumnByName(string name) { return _columns.Find(column => _namesComparer.Equals(name, column.Name)); diff --git a/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableIndex.cs b/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableIndex.cs deleted file mode 100644 index 75c4566ca..000000000 --- a/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableIndex.cs +++ /dev/null @@ -1,25 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using OneScript.Contexts; -using OneScript.Types; -using ScriptEngine.Machine.Contexts; -using ScriptEngine.Types; - -namespace OneScript.StandardLibrary.Collections.ValueTable -{ - [ContextClass("ИндексКоллекции", "CollectionIndex", TypeUUID = "48D150D4-A0DA-47CA-AEA3-D4078A731C11")] - public class CollectionIndex : ContextIValueImpl - { - private static readonly TypeDescriptor _objectType = typeof(ValueTableColumnCollection).GetTypeFromClassMarkup(); - - public CollectionIndex() : base(_objectType) - { - } - - } -} diff --git a/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableRow.cs b/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableRow.cs index 0ef5c7c06..df3f75fee 100644 --- a/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableRow.cs +++ b/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableRow.cs @@ -84,18 +84,20 @@ public IValue Get(ValueTableColumn c) public void Set(int index, IValue value) { var C = Owner().Columns.FindColumnByIndex(index); - _data[C] = C.ValueType.AdjustValue(value); + Set(C, value); } public void Set(IValue index, IValue value) { var C = Owner().Columns.GetColumnByIIndex(index); - _data[C] = C.ValueType.AdjustValue(value); + Set(C, value); } public void Set(ValueTableColumn column, IValue value) { + Owner().Indexes.ElementRemoved(this); _data[column] = column.ValueType.AdjustValue(value); + Owner().Indexes.ElementAdded(this); } public void OnOwnerColumnRemoval(IValue column) @@ -105,7 +107,7 @@ public void OnOwnerColumnRemoval(IValue column) public IEnumerator GetEnumerator() { - foreach (ValueTableColumn item in Owner().Columns) + foreach (var item in Owner().Columns) { yield return TryValue(item); } @@ -143,14 +145,14 @@ public override bool IsPropWritable(int propNum) public override IValue GetPropValue(int propNum) { - ValueTableColumn C = Owner().Columns.FindColumnByIndex(propNum); + var C = Owner().Columns.FindColumnByIndex(propNum); return TryValue(C); } public override void SetPropValue(int propNum, IValue newVal) { - ValueTableColumn C = Owner().Columns.FindColumnByIndex(propNum); - _data[C] = C.ValueType.AdjustValue(newVal); + var C = Owner().Columns.FindColumnByIndex(propNum); + Set(C, newVal); } private ValueTableColumn GetColumnByIIndex(IValue index) diff --git a/src/OneScript.StandardLibrary/DelegateAction.cs b/src/OneScript.StandardLibrary/DelegateAction.cs index 017b7844f..b668ee017 100644 --- a/src/OneScript.StandardLibrary/DelegateAction.cs +++ b/src/OneScript.StandardLibrary/DelegateAction.cs @@ -30,6 +30,7 @@ static DelegateAction() { var builder = BslMethodBuilder.Create() .DeclaringType(typeof(DelegateAction)) + .ReturnType(typeof(BslValue)) .SetNames(MethodName_Ru, MethodName_En); ExecuteMethodInfo = builder.Build(); @@ -66,10 +67,7 @@ public override int GetMethodsCount() public override BslMethodInfo GetMethodInfo(int methodNumber) { - return BslMethodBuilder.Create() - .SetNames(MethodName_Ru, MethodName_En) - .ReturnType(typeof(BslValue)) - .Build(); + return ExecuteMethodInfo; } public override void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue) diff --git a/src/OneScript.StandardLibrary/Json/JSONWriterSettings.cs b/src/OneScript.StandardLibrary/Json/JSONWriterSettings.cs index d6e9df75b..bbd82ebd6 100644 --- a/src/OneScript.StandardLibrary/Json/JSONWriterSettings.cs +++ b/src/OneScript.StandardLibrary/Json/JSONWriterSettings.cs @@ -86,7 +86,7 @@ public JSONWriterSettings(JSONLineBreakEnum NewLines = JSONLineBreakEnum.Auto, s /// Определяет, будет ли экранироваться слеш (косая черта) при записи значения. /// Значение по умолчанию: Ложь. [ScriptConstructor(Name = "По описанию параметров записи")] - public static JSONWriterSettings Constructor(IValue newLines = null, IValue paddingSymbols = null, IValue useDoubleQuotes = null, IValue escapeCharacters = null, IValue escapeAngleBrackets = null, IValue escapeLineTerminators = null, IValue escapeAmpersand = null, IValue escapeSingleQuotes = null, IValue escapeSlash = null) + public static JSONWriterSettings ParametrizedConstructor(IValue newLines = null, IValue paddingSymbols = null, IValue useDoubleQuotes = null, IValue escapeCharacters = null, IValue escapeAngleBrackets = null, IValue escapeLineTerminators = null, IValue escapeAmpersand = null, IValue escapeSingleQuotes = null, IValue escapeSlash = null) { return new JSONWriterSettings((newLines as ClrEnumValueWrapper)?.UnderlyingValue ?? JSONLineBreakEnum.None, paddingSymbols?.AsString(), diff --git a/src/OneScript.StandardLibrary/NativeApi/NativeApiComponent.cs b/src/OneScript.StandardLibrary/NativeApi/NativeApiComponent.cs index 217932042..a42f26ce6 100644 --- a/src/OneScript.StandardLibrary/NativeApi/NativeApiComponent.cs +++ b/src/OneScript.StandardLibrary/NativeApi/NativeApiComponent.cs @@ -17,9 +17,9 @@ namespace OneScript.StandardLibrary.NativeApi /// /// Экземпляр внешней компоненты Native API /// - class NativeApiComponent : NativeApiValue, IRuntimeContextInstance, IValue + class NativeApiComponent : NativeApiValue, IRuntimeContextInstance, IDisposable { - private readonly IntPtr _object; + private IntPtr _object; public override IRuntimeContextInstance AsObject() { @@ -96,11 +96,6 @@ public NativeApiComponent(object host, NativeApiLibrary library, TypeDescriptor DefineType(typeDef); } - public void Dispose() - { - try { NativeApiProxy.DestroyObject(_object); } catch (Exception) { } - } - public bool IsIndexed => true; public bool DynamicMethodSignatures => false; @@ -271,5 +266,36 @@ public void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retV } retValue = result; } + + private void ReleaseUnmanagedResources(bool isDisposing) + { + if (_object == IntPtr.Zero) + return; + + try + { + NativeApiProxy.DestroyObject(_object); + } + catch (Exception) + { + if (isDisposing) + throw; + } + finally + { + _object = IntPtr.Zero; + } + } + + public void Dispose() + { + ReleaseUnmanagedResources(true); + GC.SuppressFinalize(this); + } + + ~NativeApiComponent() + { + ReleaseUnmanagedResources(false); + } } } diff --git a/src/OneScript.StandardLibrary/NativeApi/NativeApiKernel.cs b/src/OneScript.StandardLibrary/NativeApi/NativeApiKernel.cs index 1508a5372..ad9020707 100644 --- a/src/OneScript.StandardLibrary/NativeApi/NativeApiKernel.cs +++ b/src/OneScript.StandardLibrary/NativeApi/NativeApiKernel.cs @@ -22,7 +22,7 @@ public static bool IsLinux } private const String KernelWin = "kernel32.dll"; - private const String KernelLin = "libdl.so"; + private const String KernelLin = "libdl.so.2"; public static IntPtr LoadLibrary(string filename) { diff --git a/src/OneScript.StandardLibrary/NativeApi/NativeApiLibrary.cs b/src/OneScript.StandardLibrary/NativeApi/NativeApiLibrary.cs index 43462570e..3746d382c 100644 --- a/src/OneScript.StandardLibrary/NativeApi/NativeApiLibrary.cs +++ b/src/OneScript.StandardLibrary/NativeApi/NativeApiLibrary.cs @@ -19,7 +19,7 @@ namespace OneScript.StandardLibrary.NativeApi /// Класс, ассоциированный с экземпляром библиотеки внешних компонент /// Native API и осуществляющий непосредственное создание экземпляра компоненты. /// - class NativeApiLibrary + class NativeApiLibrary : IDisposable { private delegate IntPtr GetClassNames(); @@ -47,15 +47,6 @@ public NativeApiLibrary(string filepath, string identifier, ITypeManager typeMan RegisterComponents(identifier, typeManager); } - public void Dispose() - { - foreach (var component in _components) - component.Dispose(); - if (Loaded && NativeApiKernel.FreeLibrary(Module)) - if (!String.IsNullOrEmpty(_tempfile)) - try { File.Delete(_tempfile); } catch (Exception) { } - } - public IntPtr Module { get; private set; } = IntPtr.Zero; public Boolean Loaded @@ -84,5 +75,40 @@ public IValue CreateComponent(ITypeManager typeManager, object host, String type _components.Add(component); return component; } + + private void ReleaseUnmanagedResources(bool isDisposing) + { + try + { + foreach (var component in _components) + { + component.Dispose(); + } + + if (Loaded && NativeApiKernel.FreeLibrary(Module)) + { + if (!String.IsNullOrEmpty(_tempfile)) + { + File.Delete(_tempfile); + } + } + } + catch (Exception) + { + if (isDisposing) + throw; + } + } + + public void Dispose() + { + ReleaseUnmanagedResources(true); + GC.SuppressFinalize(this); + } + + ~NativeApiLibrary() + { + ReleaseUnmanagedResources(false); + } } } diff --git a/src/OneScript.StandardLibrary/NativeApi/NativeApiProxy.cs b/src/OneScript.StandardLibrary/NativeApi/NativeApiProxy.cs index 6e20bad0b..ab7cc697b 100644 --- a/src/OneScript.StandardLibrary/NativeApi/NativeApiProxy.cs +++ b/src/OneScript.StandardLibrary/NativeApi/NativeApiProxy.cs @@ -106,33 +106,33 @@ public delegate void TGetVariant(IntPtr ptr, Int32 num [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] public delegate bool TCallAsFunc(IntPtr ptr, Int32 lMethodNum, IntPtr value, PointerDelegate response); - public static TGetClassObject GetClassObject; - public static TDestroyObject DestroyObject; - public static TCreateVariant CreateVariant; - public static TFreeVariant FreeVariant; - public static TGetNProps GetNProps; - public static TFindProp FindProp; - public static TIsPropReadable IsPropReadable; - public static TIsPropWritable IsPropWritable; - public static TGetPropName GetPropName; - public static TGetPropVal GetPropVal; - public static TSetPropVal SetPropVal; - public static TGetVariant GetVariant; - public static TSetVariantEmpty SetVariantEmpty; - public static TSetVariantBool SetVariantBool; - public static TSetVariantReal SetVariantReal; - public static TSetVariantBlob SetVariantBlob; - public static TSetVariantInt SetVariantInt; - public static TSetVariantStr SetVariantStr; - public static TGetNMethods GetNMethods; - public static TFindMethod FindMethod; - public static TGetMethodName GetMethodName; - public static TGetNParams GetNParams; - public static THasParamDefValue HasParamDefValue; - public static TGetParamDefValue GetParamDefValue; - public static THasRetVal HasRetVal; - public static TCallAsProc CallAsProc; - public static TCallAsFunc CallAsFunc; + public static readonly TGetClassObject GetClassObject; + public static readonly TDestroyObject DestroyObject; + public static readonly TCreateVariant CreateVariant; + public static readonly TFreeVariant FreeVariant; + public static readonly TGetNProps GetNProps; + public static readonly TFindProp FindProp; + public static readonly TIsPropReadable IsPropReadable; + public static readonly TIsPropWritable IsPropWritable; + public static readonly TGetPropName GetPropName; + public static readonly TGetPropVal GetPropVal; + public static readonly TSetPropVal SetPropVal; + public static readonly TGetVariant GetVariant; + public static readonly TSetVariantEmpty SetVariantEmpty; + public static readonly TSetVariantBool SetVariantBool; + public static readonly TSetVariantReal SetVariantReal; + public static readonly TSetVariantBlob SetVariantBlob; + public static readonly TSetVariantInt SetVariantInt; + public static readonly TSetVariantStr SetVariantStr; + public static readonly TGetNMethods GetNMethods; + public static readonly TFindMethod FindMethod; + public static readonly TGetMethodName GetMethodName; + public static readonly TGetNParams GetNParams; + public static readonly THasParamDefValue HasParamDefValue; + public static readonly TGetParamDefValue GetParamDefValue; + public static readonly THasRetVal HasRetVal; + public static readonly TCallAsProc CallAsProc; + public static readonly TCallAsFunc CallAsFunc; public static bool IsAvailable { get; private set; } diff --git a/src/OneScript.StandardLibrary/Net/TCPClient.cs b/src/OneScript.StandardLibrary/Net/TCPClient.cs index 346ae447f..44abe36a7 100644 --- a/src/OneScript.StandardLibrary/Net/TCPClient.cs +++ b/src/OneScript.StandardLibrary/Net/TCPClient.cs @@ -163,6 +163,16 @@ public int ReadTimeout set { _client.GetStream().ReadTimeout = value; } } + /// + /// Флаг ожидания наполнения буфера при приеме/отправке данных. По умолчанию - Ложь + /// + [ContextProperty("НеОткладывать", "NoDelay")] + public bool NoDelay + { + get { return _client.NoDelay; } + set { _client.NoDelay = value; } + } + private static Encoding GetEncodingByName(string encoding) { Encoding enc; diff --git a/src/OneScript.StandardLibrary/Processes/ArgumentsParser.cs b/src/OneScript.StandardLibrary/Processes/ArgumentsParser.cs index 88e22b283..6000d85ab 100644 --- a/src/OneScript.StandardLibrary/Processes/ArgumentsParser.cs +++ b/src/OneScript.StandardLibrary/Processes/ArgumentsParser.cs @@ -1,4 +1,4 @@ -/*---------------------------------------------------------- +/*---------------------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v.2.0. If a copy of the MPL was not distributed with this file, You can obtain one diff --git a/src/OneScript.StandardLibrary/Processes/ProcessContext.cs b/src/OneScript.StandardLibrary/Processes/ProcessContext.cs index a0ae09708..71fea5b45 100644 --- a/src/OneScript.StandardLibrary/Processes/ProcessContext.cs +++ b/src/OneScript.StandardLibrary/Processes/ProcessContext.cs @@ -8,6 +8,7 @@ This Source Code Form is subject to the terms of the using System; using System.Diagnostics; using System.Linq; +using System.Runtime.InteropServices; using OneScript.Contexts; using OneScript.Exceptions; using OneScript.StandardLibrary.Collections; @@ -274,14 +275,24 @@ public static ProcessStartInfo PrepareProcessStartupInfo(string cmdLine, string sInfo.FileName = ExtractExecutableName(cmdLine, out argsPosition); if (currentDir != null) sInfo.WorkingDirectory = currentDir; - - var arguments = argsPosition >= cmdLine.Length - ? Array.Empty() - : new ArgumentsParser(cmdLine[argsPosition..]).GetArguments(); - foreach (var argument in arguments) + // Поведение под MacOS и остальные системы различается + // Страдает обратная совместимость и неясны до конца синтаксисы. + // См. https://github.com/EvilBeaver/OneScript/issues/1299 + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + sInfo.Arguments = argsPosition >= cmdLine.Length ? "" : cmdLine.Substring(argsPosition); + } + else { - sInfo.ArgumentList.Add(argument); + var arguments = argsPosition >= cmdLine.Length + ? Array.Empty() + : new ArgumentsParser(cmdLine[argsPosition..]).GetArguments(); + + foreach (var argument in arguments) + { + sInfo.ArgumentList.Add(argument); + } } return sInfo; diff --git a/src/OneScript.StandardLibrary/Processes/ProcessOutputWrapper.cs b/src/OneScript.StandardLibrary/Processes/ProcessOutputWrapper.cs index 6cd35a45b..db67e18be 100644 --- a/src/OneScript.StandardLibrary/Processes/ProcessOutputWrapper.cs +++ b/src/OneScript.StandardLibrary/Processes/ProcessOutputWrapper.cs @@ -15,11 +15,10 @@ namespace OneScript.StandardLibrary.Processes { class ProcessOutputWrapper : TextReader { - private sys.Process _process; - private OutputVariant _variant; - private StringBuilder _buffer = new StringBuilder(4096); - private ReaderWriterLockSlim _locker; - + private readonly sys.Process _process; + private readonly OutputVariant _variant; + private readonly StringBuilder _buffer = new StringBuilder(4096); + private int _bufferIndex = 0; private bool AlreadyReading { get; set; } @@ -36,7 +35,6 @@ public ProcessOutputWrapper(sys.Process process, OutputVariant variant) { _process = process; _variant = variant; - _locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); } public void StartReading() diff --git a/src/OneScript.StandardLibrary/Reflector.cs b/src/OneScript.StandardLibrary/Reflector.cs index fdb6e3ac3..0032434d6 100644 --- a/src/OneScript.StandardLibrary/Reflector.cs +++ b/src/OneScript.StandardLibrary/Reflector.cs @@ -9,6 +9,7 @@ This Source Code Form is subject to the terms of the using System.Collections.Generic; using System.Linq; using System.Reflection; +using OneScript.Commons; using OneScript.Contexts; using OneScript.Exceptions; using OneScript.Execution; @@ -30,6 +31,13 @@ namespace OneScript.StandardLibrary [ContextClass("Рефлектор","Reflector")] public class ReflectorContext : AutoContext { + private readonly ITypeManager _typeManager; + + private ReflectorContext(ITypeManager typeManager) + { + _typeManager = typeManager; + } + /// /// Вызывает метод по его имени. /// @@ -43,8 +51,12 @@ public IValue CallMethod(IRuntimeContextInstance target, string methodName, Arra var methodIdx = target.GetMethodNumber(methodName); var methInfo = target.GetMethodInfo(methodIdx); - var argsToPass = GetArgsToPass(arguments, methInfo.GetParameters()); - + IValue[] argsToPass; + if (target.DynamicMethodSignatures) + argsToPass = arguments?.ToArray() ?? Array.Empty(); + else + argsToPass = GetArgsToPass(arguments, methInfo.GetParameters()); + IValue retValue = ValueFactory.Create(); if (methInfo.IsFunction()) { @@ -71,34 +83,39 @@ public IValue CallMethod(IRuntimeContextInstance target, string methodName, Arra private static IValue[] GetArgsToPass(ArrayImpl arguments, ParameterInfo[] parameters) { - var argsToPass = new List(); - if (arguments != null) - { - argsToPass.AddRange(arguments); - } - - if (parameters.Length < argsToPass.Count) + var argValues = arguments?.ToArray() ?? Array.Empty(); + // ArrayImpl не может (не должен!) содержать null или NotAValidValue + + if (argValues.Length > parameters.Length) throw RuntimeException.TooManyArgumentsPassed(); - for (int i = 0; i < argsToPass.Count; i++) + var argsToPass = new IValue[parameters.Length]; + + int i = 0; + for (; i < argValues.Length; i++) { if (parameters[i].IsByRef()) - argsToPass[i] = Variable.Create(argsToPass[i], $"reflectorArg{i}"); + argsToPass[i] = Variable.Create(argValues[i], ""); + else + argsToPass[i] = argValues[i]; } - while (argsToPass.Count < parameters.Length) + for (; i < parameters.Length; i++) { - argsToPass.Add(null); + if (!parameters[i].HasDefaultValue) + throw RuntimeException.TooFewArgumentsPassed(); + + // else keep null as a default value } - return argsToPass.ToArray(); + return argsToPass; } /// - /// Проверяет существование указанного метода у переданного объекта.. + /// Проверяет существование указанного метода у переданного объекта. /// /// Объект, из которого получаем таблицу методов. /// Имя метода для вызова - /// Истину, если метод существует, и Ложь в обратном случае. + /// Истина, если метод существует, и Ложь в обратном случае. [ContextMethod("МетодСуществует", "MethodExists")] public bool MethodExists(IValue target, string methodName) { @@ -198,10 +215,10 @@ private static RuntimeException NonReflectableType() } /// - /// Получает таблицу методов для переданного объекта.. + /// Получает таблицу методов для переданного объекта. /// /// Объект, из которого получаем таблицу методов. - /// Таблица значений колонками: Имя, Количество, ЭтоФункция, Аннотации + /// Таблица значений с колонками: Имя, Количество, ЭтоФункция, Аннотации, Параметры, Экспорт [ContextMethod("ПолучитьТаблицуМетодов", "GetMethodsTable")] public ValueTable GetMethodsTable(IValue target) { @@ -312,7 +329,7 @@ private static void FillMethodsTable(ValueTable result, IEnumerable - /// Получает таблицу свойств для переданного объекта.. + /// Получает таблицу свойств для переданного объекта. /// /// Объект, из которого получаем таблицу свойств. /// Включить в результат приватные поля - /// Таблица значений с колонками - Имя, Аннотации + /// Таблица значений с колонками - Имя, Аннотации, Экспорт [ContextMethod("ПолучитьТаблицуСвойств", "GetPropertiesTable")] public ValueTable GetPropertiesTable(IValue target, bool withPrivate = false) { @@ -478,10 +495,54 @@ public static Type ReflectContext(Type clrType) .Build(); } + /// + /// Возвращает все известные типы + /// + /// Структура - Условия поиска. Ключ - имя колонки, значение - искомое значение + /// + /// ТаблицаЗначений: + /// * Имя - Строка - Имя типа + /// * Значение - Тип - Тип + /// * Примитивный - Булево - Это примитивный тип + /// * Пользовательский - Булево - Это пользовательский типа + /// * Коллекция - Булево - Это коллекция + /// + [ContextMethod("ИзвестныеТипы", "KnownTypes")] + public ValueTable KnownTypes(StructureImpl filter = default) + { + var result = new ValueTable(); + + var nameColumn = result.Columns.Add("Имя", TypeDescription.StringType()); + var valueColumn = result.Columns.Add("Значение", new TypeDescription(new List() { new BslTypeValue(BasicTypes.Type) })); + var primitiveColumn = result.Columns.Add("Примитивный", TypeDescription.BooleanType()); + var userColumn = result.Columns.Add("Пользовательский", TypeDescription.BooleanType()); + var collectionColumn = result.Columns.Add("Коллекция", TypeDescription.BooleanType()); + + _typeManager.RegisteredTypes().ForEach(descriptor => + { + var row = result.Add(); + + row.Set(nameColumn, ValueFactory.Create(descriptor.ToString())); + row.Set(valueColumn, new BslTypeValue(descriptor)); + row.Set(primitiveColumn, ValueFactory.Create(descriptor.ImplementingClass.IsSubclassOf(typeof(BslPrimitiveValue)))); + row.Set(userColumn, ValueFactory.Create(descriptor.ImplementingClass == typeof(AttachedScriptsFactory))); + row.Set(collectionColumn, ValueFactory.Create( + descriptor.ImplementingClass.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICollectionContext<>)) + )); + }); + + if (filter != default) + { + result = result.Copy(filter); + } + + return result; + } + [ScriptConstructor] - public static IRuntimeContextInstance CreateNew() + public static ReflectorContext CreateNew(TypeActivationContext context) { - return new ReflectorContext(); + return new ReflectorContext(context.TypeManager); } } } diff --git a/src/OneScript.StandardLibrary/Text/TextReadImpl.cs b/src/OneScript.StandardLibrary/Text/TextReadImpl.cs index b66f5c5af..7083150a5 100644 --- a/src/OneScript.StandardLibrary/Text/TextReadImpl.cs +++ b/src/OneScript.StandardLibrary/Text/TextReadImpl.cs @@ -179,7 +179,7 @@ public static TextReadImpl Constructor (IValue input) /// Открывать файл монопольно /// ЧтениеТекста [ScriptConstructor(Name = "На основании потока или файла")] - public static TextReadImpl Constructor(IValue input, IValue encoding = null, + public static TextReadImpl ConstructorWithEncoding(IValue input, IValue encoding = null, IValue lineDelimiter = null, IValue eolDelimiter = null, IValue monopoly = null) { var reader = new TextReadImpl(); diff --git a/src/OneScript.StandardLibrary/XDTO/XDTOSerializer.cs b/src/OneScript.StandardLibrary/XDTO/XDTOSerializer.cs index 9655f91ac..d09602c57 100644 --- a/src/OneScript.StandardLibrary/XDTO/XDTOSerializer.cs +++ b/src/OneScript.StandardLibrary/XDTO/XDTOSerializer.cs @@ -22,14 +22,12 @@ namespace OneScript.StandardLibrary.XDTO [ContextClass("СериализаторXDTO", "XDTOSerializer")] public sealed class XDTOSerializer : AutoContext { - private readonly ITypeManager _typeManager; private readonly XmlGlobalFunctions _xmlGlobalFunctions; private readonly XmlNodeTypeEnum _xmlNodeEnum; - private XDTOSerializer(ITypeManager typeManager, IGlobalsManager globalsManager) + private XDTOSerializer(IGlobalsManager globalsManager) { - _typeManager = typeManager; _xmlGlobalFunctions = globalsManager.GetInstance(); _xmlNodeEnum = globalsManager.GetInstance(); } @@ -263,7 +261,7 @@ public IValue ReadXML(XmlReaderImpl xmlReader, IValue valueType = null) public static XDTOSerializer CreateInstance(TypeActivationContext context) { var globalsManager = context.Services.Resolve(); - return new XDTOSerializer(context.TypeManager, globalsManager); + return new XDTOSerializer(globalsManager); } #endregion diff --git a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSAppInfo.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSAppInfo.cs index a1b64664e..98b5e8b29 100644 --- a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSAppInfo.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSAppInfo.cs @@ -60,7 +60,7 @@ public string Source #region Methods [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] - public IXSComponent CloneComponent(bool recursive) => throw new NotImplementedException(); + public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] public void UpdateDOMElement() => throw new NotImplementedException(); diff --git a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSComplexTypeDefinition.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSComplexTypeDefinition.cs index 616f09d09..a64033baa 100644 --- a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSComplexTypeDefinition.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSComplexTypeDefinition.cs @@ -445,7 +445,7 @@ public IXSComponent Content #region Methods [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] - public IXSComponent CloneComponent(bool recursive) => throw new NotImplementedException(); + public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] public void UpdateDOMElement() => throw new NotImplementedException(); diff --git a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSDocumentation.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSDocumentation.cs index efbcebb8a..198dfe43f 100644 --- a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSDocumentation.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSDocumentation.cs @@ -67,7 +67,7 @@ public string Language #region Methods [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] - public IXSComponent CloneComponent(bool recursive) => throw new NotImplementedException(); + public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] public void UpdateDOMElement() => throw new NotImplementedException(); diff --git a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSEnumerationFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSEnumerationFacet.cs index 3a299bd23..7f9ce9831 100644 --- a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSEnumerationFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSEnumerationFacet.cs @@ -106,7 +106,7 @@ public IValue Value #region Methods [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] - public IXSComponent CloneComponent(bool recursive) => throw new NotImplementedException(); + public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] public void UpdateDOMElement() => throw new NotImplementedException(); diff --git a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSFractionDigitsFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSFractionDigitsFacet.cs index 5a7217d0b..9f806fca7 100644 --- a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSFractionDigitsFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSFractionDigitsFacet.cs @@ -96,7 +96,7 @@ public decimal Value #region Methods [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] - public IXSComponent CloneComponent(bool recursive) => throw new NotImplementedException(); + public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] public void UpdateDOMElement() => throw new NotImplementedException(); diff --git a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSInclude.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSInclude.cs index b759a6059..7deead4ed 100644 --- a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSInclude.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSInclude.cs @@ -75,7 +75,7 @@ public string SchemaLocation #region Methods [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] - public IXSComponent CloneComponent(bool recursive) => throw new NotImplementedException(); + public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] public void UpdateDOMElement() => throw new NotImplementedException(); diff --git a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSLengthFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSLengthFacet.cs index 58d9751de..c1c727bec 100644 --- a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSLengthFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSLengthFacet.cs @@ -96,7 +96,7 @@ public decimal Value #region Methods [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] - public IXSComponent CloneComponent(bool recursive) => throw new NotImplementedException(); + public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] public void UpdateDOMElement() => throw new NotImplementedException(); diff --git a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMaxExclusiveFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMaxExclusiveFacet.cs index 0d89d09b3..fe2549c67 100644 --- a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMaxExclusiveFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMaxExclusiveFacet.cs @@ -112,7 +112,7 @@ public IValue Value #region Methods [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] - public IXSComponent CloneComponent(bool recursive) => throw new NotImplementedException(); + public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] public void UpdateDOMElement() => throw new NotImplementedException(); diff --git a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMaxInclusiveFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMaxInclusiveFacet.cs index d1aab68db..a20c209c0 100644 --- a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMaxInclusiveFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMaxInclusiveFacet.cs @@ -113,7 +113,7 @@ public IValue Value #region Methods [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] - public IXSComponent CloneComponent(bool recursive) => throw new NotImplementedException(); + public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] public void UpdateDOMElement() => throw new NotImplementedException(); diff --git a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMaxLengthFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMaxLengthFacet.cs index 397c0344d..4a5a5764f 100644 --- a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMaxLengthFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMaxLengthFacet.cs @@ -96,7 +96,7 @@ public decimal Value #region Methods [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] - public IXSComponent CloneComponent(bool recursive) => throw new NotImplementedException(); + public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] public void UpdateDOMElement() => throw new NotImplementedException(); diff --git a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMinExclusiveFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMinExclusiveFacet.cs index 98a5d88b1..881b61d2a 100644 --- a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMinExclusiveFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMinExclusiveFacet.cs @@ -112,7 +112,7 @@ public IValue Value #region Methods [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] - public IXSComponent CloneComponent(bool recursive) => throw new NotImplementedException(); + public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] public void UpdateDOMElement() => throw new NotImplementedException(); diff --git a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMinInclusiveFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMinInclusiveFacet.cs index d4c83e859..4108af205 100644 --- a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMinInclusiveFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMinInclusiveFacet.cs @@ -112,7 +112,7 @@ public IValue Value #region Methods [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] - public IXSComponent CloneComponent(bool recursive) => throw new NotImplementedException(); + public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] public void UpdateDOMElement() => throw new NotImplementedException(); diff --git a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMinLengthFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMinLengthFacet.cs index 322763457..282b1422e 100644 --- a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMinLengthFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMinLengthFacet.cs @@ -96,7 +96,7 @@ public decimal Value #region Methods [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] - public IXSComponent CloneComponent(bool recursive) => throw new NotImplementedException(); + public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] public void UpdateDOMElement() => throw new NotImplementedException(); diff --git a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSModelGroupDefinition.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSModelGroupDefinition.cs index c12979632..f9c99c43c 100644 --- a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSModelGroupDefinition.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSModelGroupDefinition.cs @@ -162,7 +162,7 @@ public XMLExpandedName Reference #region Methods [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] - public IXSComponent CloneComponent(bool recursive) => throw new NotImplementedException(); + public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] public void UpdateDOMElement() => throw new NotImplementedException(); diff --git a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSPatternFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSPatternFacet.cs index 53792aa9c..56a81e02e 100644 --- a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSPatternFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSPatternFacet.cs @@ -95,7 +95,7 @@ public string Value #region Methods [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] - public IXSComponent CloneComponent(bool recursive) => throw new NotImplementedException(); + public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] public void UpdateDOMElement() => throw new NotImplementedException(); diff --git a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSSimpleTypeDefinition.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSSimpleTypeDefinition.cs index bbafd61c4..fa039c378 100644 --- a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSSimpleTypeDefinition.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSSimpleTypeDefinition.cs @@ -264,7 +264,7 @@ public XMLExpandedName ItemTypeName #region Methods [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] - public IXSComponent CloneComponent(bool recursive) => throw new NotImplementedException(); + public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] public void UpdateDOMElement() => throw new NotImplementedException(); diff --git a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSTotalDigitsFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSTotalDigitsFacet.cs index 498bae27d..dbb3190f6 100644 --- a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSTotalDigitsFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSTotalDigitsFacet.cs @@ -96,7 +96,7 @@ public decimal Value #region Methods [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] - public IXSComponent CloneComponent(bool recursive) => throw new NotImplementedException(); + public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] public void UpdateDOMElement() => throw new NotImplementedException(); diff --git a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSWhitespaceFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSWhitespaceFacet.cs index a71ce3216..02870bbb6 100644 --- a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSWhitespaceFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSWhitespaceFacet.cs @@ -131,7 +131,7 @@ public XSWhitespaceHandling Value #region Methods [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] - public IXSComponent CloneComponent(bool recursive) => throw new NotImplementedException(); + public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] public void UpdateDOMElement() => throw new NotImplementedException(); diff --git a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSWildcard.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSWildcard.cs index f5718511d..33ffd8740 100644 --- a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSWildcard.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSWildcard.cs @@ -165,7 +165,7 @@ public string LexicalNamespaceConstraint #region Methods [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] - public IXSComponent CloneComponent(bool recursive) => throw new NotImplementedException(); + public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] public void UpdateDOMElement() => throw new NotImplementedException(); diff --git a/src/OneScriptDocumenter/OneScriptDocumenter.csproj b/src/OneScriptDocumenter/OneScriptDocumenter.csproj index 225f8a3bd..b97bdd1cb 100644 --- a/src/OneScriptDocumenter/OneScriptDocumenter.csproj +++ b/src/OneScriptDocumenter/OneScriptDocumenter.csproj @@ -3,7 +3,7 @@ - netcoreapp3.1 + net6.0 Exe OneScript Libraries Documenter diff --git a/src/ScriptEngine/Compiler/CompilerFrontend.cs b/src/ScriptEngine/Compiler/CompilerFrontend.cs index 1c6fe1db8..3f1a9d965 100644 --- a/src/ScriptEngine/Compiler/CompilerFrontend.cs +++ b/src/ScriptEngine/Compiler/CompilerFrontend.cs @@ -5,13 +5,18 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ using System; +using System.Collections.Generic; +using System.Linq; +using OneScript.Commons; using OneScript.Compilation; using OneScript.Compilation.Binding; +using OneScript.Contexts; using OneScript.DependencyInjection; using OneScript.Execution; using OneScript.Language; using OneScript.Language.SyntaxAnalysis; using OneScript.Language.SyntaxAnalysis.AstNodes; +using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; namespace ScriptEngine.Compiler @@ -19,15 +24,20 @@ namespace ScriptEngine.Compiler public class CompilerFrontend : CompilerFrontendBase { private readonly IDependencyResolver _dependencyResolver; + private readonly PredefinedInterfaceResolver _interfaceResolver; private readonly CompilerBackendSelector _backendSelector; public CompilerFrontend( PreprocessorHandlers handlers, IErrorSink errorSink, IServiceContainer services, - IDependencyResolver dependencyResolver) : base(handlers, errorSink, services) + IDependencyResolver dependencyResolver, + PredefinedInterfaceResolver interfaceResolver, + IEnumerable checkers) : base(handlers, errorSink, services) { _dependencyResolver = dependencyResolver; + _interfaceResolver = interfaceResolver; + _backendSelector = services.Resolve(); _backendSelector.NativeBackendInitializer = NativeInitializer; @@ -61,7 +71,12 @@ protected override IExecutableModule CompileInternal(SymbolTable symbols, Module { var backend = _backendSelector.Select(parsedModule); backend.Symbols = symbols; - return backend.Compile(parsedModule, classType); + + var module = backend.Compile(parsedModule, classType); + + _interfaceResolver.Resolve(module); + + return module; } protected override IExecutableModule CompileExpressionInternal(SymbolTable symbols, ModuleNode parsedModule) diff --git a/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs b/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs index c37e0938b..e361f4e9f 100644 --- a/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs +++ b/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs @@ -224,9 +224,23 @@ private static string[] GetVariableNames(SymbolScope localCtx) return localCtx.Variables.Select(v => v.Name).ToArray(); } + + protected override void VisitGotoNode(NonTerminalNode node) + { + throw new NotSupportedException(); + } + + protected override void VisitLabelNode(LabelNode node) + { + throw new NotSupportedException(); + } protected override void VisitMethod(MethodNode methodNode) { + if (methodNode.IsAsync) + { + AddError(LocalizedErrors.AsyncMethodsNotSupported(), methodNode.Location); + } var signature = methodNode.Signature; var methodBuilder = NewMethod(); diff --git a/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs b/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs index ccb3981a8..39b273efd 100644 --- a/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs +++ b/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs @@ -15,6 +15,8 @@ This Source Code Form is subject to the terms of the using OneScript.Language.SyntaxAnalysis; using OneScript.Types; using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using ScriptEngine.Machine.Interfaces; namespace ScriptEngine.Hosting { @@ -59,7 +61,7 @@ public static IEngineBuilder SetDefaultOptions(this IEngineBuilder builder) services.RegisterEnumerable(); services.Register(); - + services.EnablePredefinedIterables(); services.Register(sp => { var providers = sp.ResolveEnumerable(); diff --git a/src/ScriptEngine/Machine/Contexts/AttachedScriptsFactory.cs b/src/ScriptEngine/Machine/Contexts/AttachedScriptsFactory.cs index 8412de908..5b2335cc2 100755 --- a/src/ScriptEngine/Machine/Contexts/AttachedScriptsFactory.cs +++ b/src/ScriptEngine/Machine/Contexts/AttachedScriptsFactory.cs @@ -6,6 +6,7 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; using System.Collections.Generic; +using System.Linq; using System.Text; using OneScript.Sources; using System.Security.Cryptography; @@ -16,6 +17,7 @@ This Source Code Form is subject to the terms of the using OneScript.Exceptions; using OneScript.Execution; using OneScript.Types; +using ScriptEngine.Machine.Interfaces; namespace ScriptEngine.Machine.Contexts { @@ -212,9 +214,17 @@ public static UserScriptContextInstance ScriptFactory(TypeActivationContext cont { var module = _instance._loadedModules[context.TypeName]; - var type = context.TypeManager.GetTypeByName(context.TypeName); - - var newObj = new UserScriptContextInstance(module, type, arguments); + var type = context.TypeManager.GetTypeByName(context.TypeName); + UserScriptContextInstance newObj; + if (module.GetInterface() != null) + { + newObj = new UserIterableContextInstance(module, type, arguments); + } + else + { + newObj = new UserScriptContextInstance(module, type, arguments); + } + newObj.InitOwnData(); newObj.Initialize(); diff --git a/src/ScriptEngine/Machine/Contexts/BslPredefinedAnnotation.cs b/src/ScriptEngine/Machine/Contexts/BslPredefinedAnnotation.cs new file mode 100644 index 000000000..cc14e4dbf --- /dev/null +++ b/src/ScriptEngine/Machine/Contexts/BslPredefinedAnnotation.cs @@ -0,0 +1,21 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; +using OneScript.Localization; + +namespace ScriptEngine.Machine.Contexts +{ + public static class BslPredefinedAnnotation + { + public static bool NameMatches(this BslAnnotationAttribute attribute, BilingualString names) + { + return names.HasName(attribute.Name, StringComparison.CurrentCultureIgnoreCase); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Contexts/COMWrapperContext.cs b/src/ScriptEngine/Machine/Contexts/COMWrapperContext.cs index 80f620dda..523896b67 100644 --- a/src/ScriptEngine/Machine/Contexts/COMWrapperContext.cs +++ b/src/ScriptEngine/Machine/Contexts/COMWrapperContext.cs @@ -127,7 +127,7 @@ private static bool TypeIsRuntimeCallableWrapper(Type type) protected static (object[] values, ParameterModifier[] flags) MarshalArguments(IValue[] arguments) { var values = new object[arguments.Length]; - ParameterModifier[] flagsArray = null; + ParameterModifier[] flagsArray = new ParameterModifier[1]; if (arguments.Length > 0) { var flags = new ParameterModifier(arguments.Length); @@ -137,7 +137,11 @@ protected static (object[] values, ParameterModifier[] flags) MarshalArguments(I flags[i] = arguments[i] is IVariable; } - flagsArray = new[] { flags }; + flagsArray[0] = flags; + } + else + { + flagsArray[0] = new ParameterModifier(); } return (values, flagsArray); @@ -312,7 +316,6 @@ protected virtual void Dispose(bool manualDispose) { GC.SuppressFinalize(this); } - } public void Dispose() @@ -327,14 +330,8 @@ public void Dispose() #endregion - public override bool DynamicMethodSignatures - { - get - { - return true; - } - } - + public override bool DynamicMethodSignatures => true; + [ScriptConstructor] public static COMWrapperContext Constructor(IValue[] args) { diff --git a/src/ScriptEngine/Machine/Contexts/ExceptionInfoContext.cs b/src/ScriptEngine/Machine/Contexts/ExceptionInfoContext.cs index ce73700d3..869d6d408 100644 --- a/src/ScriptEngine/Machine/Contexts/ExceptionInfoContext.cs +++ b/src/ScriptEngine/Machine/Contexts/ExceptionInfoContext.cs @@ -207,7 +207,7 @@ public static ExceptionInfoContext EmptyExceptionInfo() private class EmptyScriptException : ScriptException { - public static EmptyScriptException Instance = new EmptyScriptException(); + public static readonly EmptyScriptException Instance = new EmptyScriptException(); private EmptyScriptException() : base("") { LineNumber = 0; diff --git a/src/ScriptEngine/Machine/Contexts/ScriptedEnumeratorWrapper.cs b/src/ScriptEngine/Machine/Contexts/ScriptedEnumeratorWrapper.cs new file mode 100644 index 000000000..cf2b68783 --- /dev/null +++ b/src/ScriptEngine/Machine/Contexts/ScriptedEnumeratorWrapper.cs @@ -0,0 +1,82 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Localization; +using OneScript.Values; +using ScriptEngine.Machine.Interfaces; + +namespace ScriptEngine.Machine.Contexts +{ + public sealed class ScriptedEnumeratorWrapper : IEnumerator + { + private readonly UserScriptContextInstance _userObject; + + private BslScriptMethodInfo _moveNextMethod; + private BslScriptMethodInfo _getCurrentMethod; + private BslScriptMethodInfo _onDisposeMethod; + + public ScriptedEnumeratorWrapper(UserScriptContextInstance userObject) + { + _userObject = userObject; + CheckAndSetMethods(); + } + + private void CheckAndSetMethods() + { + var bslInterface = _userObject.Module.GetInterface() + ?? IteratorBslInterfaceChecker.CheckModule(_userObject.Module); + + _moveNextMethod = bslInterface.MoveNextMethod; + _getCurrentMethod = bslInterface.GetCurrentMethod; + _onDisposeMethod = bslInterface.OnDisposeMethod; + } + + public bool MoveNext() + { + _userObject.CallAsFunction(_moveNextMethod.DispatchId, Array.Empty(), out var result); + return result.AsBoolean(); + } + + public void Reset() + { + throw new System.NotSupportedException(); + } + + public BslValue Current + { + get + { + _userObject.CallAsFunction(_getCurrentMethod.DispatchId, Array.Empty(), out var result); + return (BslValue)result; + } + } + + object IEnumerator.Current => Current; + + public void Dispose() + { + if (_onDisposeMethod != null) + _userObject.CallAsProcedure(_onDisposeMethod.DispatchId, Array.Empty()); + } + + public static RuntimeException IncompatibleInterfaceError() + { + var error = new BilingualString( + "Итератор не соответствует интерфейсу итератора", + "Iterator doesn't match Iterator interface"); + + return new RuntimeException(error); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Contexts/UnmanagedCOMWrapperContext.cs b/src/ScriptEngine/Machine/Contexts/UnmanagedCOMWrapperContext.cs index 058ed8715..08d4d77dc 100644 --- a/src/ScriptEngine/Machine/Contexts/UnmanagedCOMWrapperContext.cs +++ b/src/ScriptEngine/Machine/Contexts/UnmanagedCOMWrapperContext.cs @@ -105,7 +105,7 @@ public override IValue GetPropValue(int propNum) var result = DispatchUtility.Invoke(Instance, dispId, null); return CreateIValue(result); } - catch (System.Reflection.TargetInvocationException e) + catch (TargetInvocationException e) { throw e.InnerException ?? e; } @@ -149,16 +149,16 @@ public override void SetPropValue(int propNum, IValue newVal) } DispatchUtility.InvokeSetProperty(Instance, dispId, argToPass); } - catch (System.Reflection.TargetInvocationException e) + catch (TargetInvocationException e) { throw e.InnerException ?? e; } } - catch (System.MissingMemberException) + catch (MissingMemberException) { throw PropertyAccessException.PropNotFoundException(prop.Name); } - catch (System.MemberAccessException) + catch (MemberAccessException) { throw PropertyAccessException.PropIsNotWritableException(prop.Name); } @@ -174,8 +174,11 @@ public override int GetMethodNumber(string name) public override BslMethodInfo GetMethodInfo(int methodNumber) { - //TODO: Доработать RcwMethodMetadata - return BslMethodBuilder.Create().Build(); + var md = _methods[methodNumber]; + return BslMethodBuilder.Create() + .Name(md.Name) + .ReturnType(md.IsFunction ?? true ? typeof(IValue) : typeof(void)) + .Build(); } private MethodSignature GetMethodDescription(int methodNumber) @@ -200,12 +203,12 @@ public override void CallAsProcedure(int methodNumber, IValue[] arguments) DispatchUtility.Invoke(Instance, dispId, argsData.values, argsData.flags); RemapOutputParams(arguments, argsData.values, argsData.flags[0], initialValues); } - catch (System.Reflection.TargetInvocationException e) + catch (TargetInvocationException e) { throw e.InnerException ?? e; } } - catch (System.MissingMemberException) + catch (MissingMemberException) { throw RuntimeException.MethodNotFoundException(method.Name); } @@ -231,12 +234,12 @@ public override void CallAsFunction(int methodNumber, IValue[] arguments, out IV RemapOutputParams(arguments, argsData.values, argsData.flags[0], initialValues); retValue = CreateIValue(result); } - catch (System.Reflection.TargetInvocationException e) + catch (TargetInvocationException e) { throw e.InnerException ?? e; } } - catch (System.MissingMemberException) + catch (MissingMemberException) { throw RuntimeException.MethodNotFoundException(method.Name); } @@ -247,7 +250,10 @@ private void RemapOutputParams(IValue[] arguments, object[] values, ParameterMod { for (int i = 0; i < arguments.Length; i++) { - if (flags[i] && !initialValues[i].Equals(values[i])) + var initialValue = initialValues[i]; + var valueAfterCall = values[i]; + + if (flags[i] && !Equals(initialValue, valueAfterCall)) { var variable = (IVariable)arguments[i]; variable.Value = CreateIValue(values[i]); @@ -257,28 +263,28 @@ private void RemapOutputParams(IValue[] arguments, object[] values, ParameterMod private bool TryFindMethod(string name, out RcwMethodMetadata md) { - if (_methods.Names.TryGetValue(name, out md)) + if (_methods.ByName.TryGetValue(name, out md)) return true; if (!DispatchUtility.TryGetDispId(Instance, name, out var dispatchId)) return false; _methods.Add(new RcwMethodMetadata(name, dispatchId, null)); - md = _methods.DispatchIds[dispatchId]; + md = _methods.ByDispatchId[dispatchId]; return true; } private bool TryFindProperty(string name, out RcwPropertyMetadata md) { - if (_props.Names.TryGetValue(name, out md)) + if (_props.ByName.TryGetValue(name, out md)) return true; if (!DispatchUtility.TryGetDispId(Instance, name, out var dispatchId)) return false; _props.Add(new RcwPropertyMetadata(name, dispatchId)); - md = _props.DispatchIds[dispatchId]; + md = _props.ByDispatchId[dispatchId]; return true; } diff --git a/src/ScriptEngine/Machine/Contexts/UserIterableContextInstance.cs b/src/ScriptEngine/Machine/Contexts/UserIterableContextInstance.cs new file mode 100644 index 000000000..bc6833aac --- /dev/null +++ b/src/ScriptEngine/Machine/Contexts/UserIterableContextInstance.cs @@ -0,0 +1,66 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Localization; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine.Interfaces; + +namespace ScriptEngine.Machine.Contexts +{ + public class UserIterableContextInstance : UserScriptContextInstance, ICollectionContext + { + private BslScriptMethodInfo _getCountMethod; + private BslScriptMethodInfo _getIteratorMethod; + public static readonly BilingualString GetIteratorTerms = new BilingualString("ПолучитьИтератор", "GetIterator"); + public static readonly BilingualString GetCountTerms = new BilingualString("Количество", "Count"); + + public UserIterableContextInstance(IExecutableModule module, TypeDescriptor asObjectOfType, IValue[] args = null) : base(module, asObjectOfType, args) + { + var methods = module.GetInterface(); + _getIteratorMethod = methods.GetIteratorMethod; + _getCountMethod = methods.GetCountMethod; + } + + public int Count() + { + if (_getCountMethod == null) + throw new RuntimeException( + new BilingualString( + "Класс не поддерживает получение количества элементов, т.к. в нем отсутствует метод "+GetCountTerms.Russian+"()", + "Class doesn't support items counting, because method "+GetCountTerms.English+"() is not defined") + ); + + CallAsFunction(_getCountMethod.DispatchId, Array.Empty(), out var ret); + + return (int)ret.AsNumber(); + } + + public IEnumerator GetEnumerator() + { + CallAsFunction(_getIteratorMethod.DispatchId, Array.Empty(), out var enumerator); + if (!(enumerator is UserScriptContextInstance userObject)) + { + throw ScriptedEnumeratorWrapper.IncompatibleInterfaceError(); + } + + return new ScriptedEnumeratorWrapper(userObject); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Contexts/UserScriptContextInstance.cs b/src/ScriptEngine/Machine/Contexts/UserScriptContextInstance.cs index 0d715d8cd..0c980cb07 100644 --- a/src/ScriptEngine/Machine/Contexts/UserScriptContextInstance.cs +++ b/src/ScriptEngine/Machine/Contexts/UserScriptContextInstance.cs @@ -14,6 +14,7 @@ This Source Code Form is subject to the terms of the using OneScript.Contexts; using OneScript.Exceptions; using OneScript.Execution; +using OneScript.Localization; using OneScript.Types; using OneScript.Values; @@ -22,14 +23,21 @@ namespace ScriptEngine.Machine.Contexts [ContextClass("Сценарий", "Script")] public class UserScriptContextInstance : ThisAwareScriptedObjectBase, IDebugPresentationAcceptor { + public static readonly BilingualString OnInstanceCreationTerms = + new BilingualString("ПриСозданииОбъекта", "OnObjectCreate"); + + public static readonly BilingualString PresentationGetProcessingTerms = + new BilingualString("ОбработкаПолученияПредставления", "PresentationGetProcessing"); + + public static readonly BilingualString RaiseEventTerms = + new BilingualString("ВызватьСобытие", "RaiseEvent"); + + private const int RAIZEEVENT_INDEX = 0; + Dictionary _ownPropertyIndexes; List _ownProperties; private Func _asStringOverride; - - private const string RAISEEVENT_RU = "ВызватьСобытие"; - private const string RAISEEVENT_EN = "RaiseEvent"; - private const int RAIZEEVENT_INDEX = 0; public IValue[] ConstructorParams { get; private set; } @@ -56,7 +64,7 @@ protected override void OnInstanceCreation() ActivateAsStringOverride(); base.OnInstanceCreation(); - var methId = GetScriptMethod("ПриСозданииОбъекта", "OnObjectCreate"); + var methId = GetScriptMethod(OnInstanceCreationTerms.Russian, OnInstanceCreationTerms.English); int constructorParamsCount = ConstructorParams.Count(); if (methId > -1) @@ -88,12 +96,12 @@ protected override void OnInstanceCreation() private void ActivateAsStringOverride() { - var methId = GetScriptMethod("ОбработкаПолученияПредставления", "PresentationGetProcessing"); + var methId = GetScriptMethod(PresentationGetProcessingTerms.Russian, PresentationGetProcessingTerms.English); if (methId == -1) _asStringOverride = base.ConvertToString; else { - var signature = GetMethodInfo(methId); + var signature = GetMethodInfo(GetOwnMethodCount()+methId); if (signature.GetParameters().Length != 2) throw new RuntimeException("Обработчик получения представления должен иметь 2 параметра"); @@ -150,13 +158,7 @@ protected override int GetOwnMethodCount() protected override int FindOwnMethod(string name) { - if (string.Equals(RAISEEVENT_EN, name, StringComparison.OrdinalIgnoreCase) - || string.Equals(RAISEEVENT_RU, name, StringComparison.OrdinalIgnoreCase)) - { - return RAIZEEVENT_INDEX; - } - - return base.FindOwnMethod(name); + return RaiseEventTerms.HasName(name) ? RAIZEEVENT_INDEX : base.FindOwnMethod(name); } protected override int FindOwnProperty(string name) @@ -210,7 +212,7 @@ private static void PrepareCompilation(CompileTimeSymbolsProvider provider, Symb private static BslMethodInfo[] GetOwnMethodsDefinition() { var methodBuilder = BslMethodBuilder.Create(); - methodBuilder.SetNames(RAISEEVENT_RU, RAISEEVENT_EN) + methodBuilder.SetNames(RaiseEventTerms.Russian, RaiseEventTerms.English) .DeclaringType(typeof(UserScriptContextInstance)); methodBuilder.NewParameter() diff --git a/src/ScriptEngine/Machine/DefaultTypeManager.cs b/src/ScriptEngine/Machine/DefaultTypeManager.cs index 3be4bd521..985ad9170 100644 --- a/src/ScriptEngine/Machine/DefaultTypeManager.cs +++ b/src/ScriptEngine/Machine/DefaultTypeManager.cs @@ -143,6 +143,11 @@ public bool IsKnownType(string typeName) return _knownTypes.Any(x => x.Name.ToUpperInvariant() == nameToUpper); } + public IReadOnlyList RegisteredTypes() + { + return _knownTypes; + } + public Type NewInstanceHandler { get diff --git a/src/ScriptEngine/Machine/Interfaces/InterfaceRegistrationExtensions.cs b/src/ScriptEngine/Machine/Interfaces/InterfaceRegistrationExtensions.cs new file mode 100644 index 000000000..a5ae0336c --- /dev/null +++ b/src/ScriptEngine/Machine/Interfaces/InterfaceRegistrationExtensions.cs @@ -0,0 +1,39 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts; +using OneScript.DependencyInjection; +using OneScript.Execution; +using OneScript.Language.SyntaxAnalysis; + +namespace ScriptEngine.Machine.Interfaces +{ + public static class InterfaceRegistrationExtensions + { + public static IServiceDefinitions EnablePredefinedIterables(this IServiceDefinitions services) + { + services.RegisterEnumerable(); + services.RegisterEnumerable(); + services.RegisterEnumerable(); + + return services; + } + + public static void AddInterface(this IExecutableModule module, T interfaceData) where T : class + { + module.Interfaces[typeof(T)] = interfaceData; + } + + public static T GetInterface(this IExecutableModule module) where T : class + { + if (!module.Interfaces.TryGetValue(typeof(T), out var value)) + return null; + + return (T)value; + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Interfaces/IterableBslInterface.cs b/src/ScriptEngine/Machine/Interfaces/IterableBslInterface.cs new file mode 100644 index 000000000..192d0d462 --- /dev/null +++ b/src/ScriptEngine/Machine/Interfaces/IterableBslInterface.cs @@ -0,0 +1,23 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts; + +namespace ScriptEngine.Machine.Interfaces +{ + public class IterableBslInterface + { + public IterableBslInterface(BslScriptMethodInfo getIteratorMethod, BslScriptMethodInfo getCountMethod) + { + GetIteratorMethod = getIteratorMethod; + GetCountMethod = getCountMethod; + } + + public BslScriptMethodInfo GetIteratorMethod { get; } + public BslScriptMethodInfo GetCountMethod { get; } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Interfaces/IterableBslInterfaceChecker.cs b/src/ScriptEngine/Machine/Interfaces/IterableBslInterfaceChecker.cs new file mode 100644 index 000000000..e649c3a68 --- /dev/null +++ b/src/ScriptEngine/Machine/Interfaces/IterableBslInterfaceChecker.cs @@ -0,0 +1,73 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Localization; +using ScriptEngine.Machine.Contexts; + +namespace ScriptEngine.Machine.Interfaces +{ + internal class IterableBslInterfaceChecker : IPredefinedInterfaceChecker + { + public static readonly BilingualString IterableAnnotation = new BilingualString("Обходимое", "Iterable"); + + public IEnumerable GetRegistrations() + { + return new[] + { + PredefinedInterfaceRegistration.OnMethod(IterableAnnotation, UserScriptContextInstance.OnInstanceCreationTerms), + PredefinedInterfaceRegistration.OnModule(IterableAnnotation) + }; + } + + public void Validate(IExecutableModule module) + { + var error = new BilingualString( + "Модуль объекта Обходимое должен содержать функцию " + UserIterableContextInstance.GetIteratorTerms.Russian + "()", + "Module of Iterable must contain function " + UserIterableContextInstance.GetIteratorTerms.English + "()"); + + BslScriptMethodInfo getIteratorMethod; + + try + { + getIteratorMethod = (BslScriptMethodInfo)module.Methods.Single(method => + UserIterableContextInstance.GetIteratorTerms.HasName(method.Name)); + } + catch (InvalidOperationException e) + { + throw new InterfaceCheckException(error, e); + } + + if (!getIteratorMethod.IsFunction() || getIteratorMethod.GetBslParameters().Length != 0) + { + throw new InterfaceCheckException(error); + } + + BslScriptMethodInfo getCountMethod = (BslScriptMethodInfo)module.Methods.FirstOrDefault(method => + UserIterableContextInstance.GetCountTerms.HasName(method.Name)); + + if (getCountMethod != null) + { + var countError = new BilingualString( + "Метод " + UserIterableContextInstance.GetCountTerms.Russian + " должен быть функцией без параметров", + "Method " + UserIterableContextInstance.GetCountTerms.English + "() must be a function without parameters"); + + if (!getCountMethod.IsFunction() || getCountMethod.GetBslParameters().Length != 0) + { + throw new InterfaceCheckException(countError); + } + } + + module.AddInterface(new IterableBslInterface(getIteratorMethod, getCountMethod)); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Interfaces/IterablesModuleAnnotationsHandler.cs b/src/ScriptEngine/Machine/Interfaces/IterablesModuleAnnotationsHandler.cs new file mode 100644 index 000000000..620d9b226 --- /dev/null +++ b/src/ScriptEngine/Machine/Interfaces/IterablesModuleAnnotationsHandler.cs @@ -0,0 +1,27 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Collections.Generic; +using OneScript.Language; +using OneScript.Language.SyntaxAnalysis; +using OneScript.Localization; + +namespace ScriptEngine.Machine.Interfaces +{ + public class IterablesModuleAnnotationsHandler : SingleWordModuleAnnotationHandler + { + private static HashSet SupportedNames = new HashSet() + { + IterableBslInterfaceChecker.IterableAnnotation, + IteratorBslInterfaceChecker.IterableAnnotation + }; + + public IterablesModuleAnnotationsHandler(IErrorSink errorSink) : base(SupportedNames, errorSink) + { + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Interfaces/IteratorBslInterface.cs b/src/ScriptEngine/Machine/Interfaces/IteratorBslInterface.cs new file mode 100644 index 000000000..e80481ef5 --- /dev/null +++ b/src/ScriptEngine/Machine/Interfaces/IteratorBslInterface.cs @@ -0,0 +1,25 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts; + +namespace ScriptEngine.Machine.Interfaces +{ + public class IteratorBslInterface + { + public IteratorBslInterface(BslScriptMethodInfo moveNextMethod, BslScriptMethodInfo getCurrentMethod, BslScriptMethodInfo onDisposeMethod) + { + MoveNextMethod = moveNextMethod; + GetCurrentMethod = getCurrentMethod; + OnDisposeMethod = onDisposeMethod; + } + + public BslScriptMethodInfo MoveNextMethod { get; } + public BslScriptMethodInfo GetCurrentMethod { get; } + public BslScriptMethodInfo OnDisposeMethod { get; } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Interfaces/IteratorBslInterfaceChecker.cs b/src/ScriptEngine/Machine/Interfaces/IteratorBslInterfaceChecker.cs new file mode 100644 index 000000000..35c3315e1 --- /dev/null +++ b/src/ScriptEngine/Machine/Interfaces/IteratorBslInterfaceChecker.cs @@ -0,0 +1,102 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Localization; +using ScriptEngine.Machine.Contexts; + +namespace ScriptEngine.Machine.Interfaces +{ + internal class IteratorBslInterfaceChecker : IPredefinedInterfaceChecker + { + public static readonly BilingualString IterableAnnotation = new BilingualString("Итератор", "Iterator"); + + private static readonly BilingualString + MoveNextMethodName = new BilingualString("Следующий", "Next"); + + private static readonly BilingualString + GetCurrentMethodName = new BilingualString("ТекущийЭлемент", "CurrentItem"); + + private static readonly BilingualString + DisposeMethodName = new BilingualString("ПриОсвобожденииОбъекта", "OnDispose"); + + public IEnumerable GetRegistrations() + { + return new[] + { + PredefinedInterfaceRegistration.OnMethod(IterableAnnotation, UserScriptContextInstance.OnInstanceCreationTerms), + PredefinedInterfaceRegistration.OnModule(IterableAnnotation) + }; + } + + public void Validate(IExecutableModule module) + { + CheckModule(module); + } + + internal static IteratorBslInterface CheckModule(IExecutableModule module) + { + var moveNextMethod = FindRequiredMethod(module, MoveNextMethodName); + var getCurrentMethod = FindRequiredMethod(module, GetCurrentMethodName); + var onDisposeMethod = FindOptionalMethod(module, DisposeMethodName); + + if (!moveNextMethod.IsFunction() || moveNextMethod.GetBslParameters().Length != 0) + { + throw MissingMethod(MoveNextMethodName); + } + + if (!getCurrentMethod.IsFunction() || getCurrentMethod.GetBslParameters().Length != 0) + { + throw MissingMethod(GetCurrentMethodName); + } + + if (onDisposeMethod != null && onDisposeMethod.GetBslParameters().Length != 0) + { + throw MissingMethod(DisposeMethodName); + } + + var bslInterface = new IteratorBslInterface(moveNextMethod, getCurrentMethod, onDisposeMethod); + module.AddInterface(bslInterface); + + return bslInterface; + } + + private static BslScriptMethodInfo FindRequiredMethod(IExecutableModule module, BilingualString names) + { + try + { + return (BslScriptMethodInfo)module.Methods.Single(m => + names.HasName(m.Name)); + } + catch (InvalidOperationException e) + { + throw MissingMethod(names, e); + } + } + + private static BslScriptMethodInfo FindOptionalMethod(IExecutableModule module,BilingualString names) + { + return (BslScriptMethodInfo)module.Methods.FirstOrDefault(m => + names.HasName(m.Name)); + + } + + private static InterfaceCheckException MissingMethod(BilingualString methodName, Exception parent = null) + { + var error = new BilingualString( + "Обязательный метод "+methodName.Russian+" отсутствует, или не соответствует интерфейсу итератора", + "Required method "+methodName.English+" is missing or doesn't match iterator interface"); + + return new InterfaceCheckException(error, parent); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/StackRuntimeModule.cs b/src/ScriptEngine/Machine/StackRuntimeModule.cs index b1bf5374a..c507fef5f 100644 --- a/src/ScriptEngine/Machine/StackRuntimeModule.cs +++ b/src/ScriptEngine/Machine/StackRuntimeModule.cs @@ -58,6 +58,8 @@ public BslMethodInfo ModuleBody public IList Code { get; } = new List(512); public SourceCode Source { get; set; } + + public IDictionary Interfaces { get; } = new Dictionary(); #endregion } diff --git a/src/Tests/OneScript.Core.Tests/ArgumentParserTests.cs b/src/Tests/OneScript.Core.Tests/ArgumentParserTests.cs index cbca74f67..247713773 100644 --- a/src/Tests/OneScript.Core.Tests/ArgumentParserTests.cs +++ b/src/Tests/OneScript.Core.Tests/ArgumentParserTests.cs @@ -1,4 +1,4 @@ -/*---------------------------------------------------------- +/*---------------------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v.2.0. If a copy of the MPL was not distributed with this file, You can obtain one diff --git a/src/Tests/OneScript.Core.Tests/PredefinedInterfacesTest.cs b/src/Tests/OneScript.Core.Tests/PredefinedInterfacesTest.cs new file mode 100644 index 000000000..453508d1b --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/PredefinedInterfacesTest.cs @@ -0,0 +1,155 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using FluentAssertions; +using Moq; +using OneScript.Contexts; +using OneScript.Execution; +using OneScript.Localization; +using Xunit; + +namespace OneScript.Core.Tests; + +public class PredefinedInterfacesTest +{ + [Fact] + public void Test_CheckerReactsOnModuleAnnotations() + { + var annotationToSearch = new BilingualString("MyAnno"); + var checker = new Mock(); + checker.Setup(x => x.GetRegistrations()).Returns(new[] + { + PredefinedInterfaceRegistration.OnModule(annotationToSearch), + }); + + var module = MockModule(mock => + { + mock.SetupGet(x => x.ModuleAttributes).Returns(new[] + { + new BslAnnotationAttribute("MyAnno") + }); + + mock.SetupGet(x => x.Methods).Returns(Array.Empty()); + }); + + checker.Setup(x => x.Validate(module)).Callback((m) => + { + m.Interfaces.Add(typeof(string), "Registered"); + }); + + var resolver = new PredefinedInterfaceResolver(new[] { checker.Object }); + resolver.Resolve(module); + + module.Interfaces.Should().Contain(KeyValuePair.Create(typeof(string), (object)"Registered")); + } + + [Fact] + public void Test_CheckerDoesNotReactsOnModuleAnnotations() + { + var annotationToSearch = new BilingualString("MyAnno"); + var checker = new Mock(); + checker.Setup(x => x.GetRegistrations()).Returns(new[] + { + PredefinedInterfaceRegistration.OnModule(annotationToSearch), + }); + + var module = MockModule(mock => + { + mock.SetupGet(x => x.ModuleAttributes).Returns(Array.Empty()); + mock.SetupGet(x => x.Methods).Returns(Array.Empty()); + }); + + checker.Setup(x => x.Validate(module)).Callback((m) => + { + m.Interfaces.Add(typeof(string), "Registered"); + }); + + var resolver = new PredefinedInterfaceResolver(new[] { checker.Object }); + resolver.Resolve(module); + + module.Interfaces.Should().BeEmpty(); + } + + [Fact] + public void Test_CheckerReactsOnlyOnceOnManyAnnotations() + { + var annotationToSearch = new BilingualString("MyAnno"); + var checker = new Mock(); + checker.Setup(x => x.GetRegistrations()).Returns(new[] + { + PredefinedInterfaceRegistration.OnModule(annotationToSearch), + PredefinedInterfaceRegistration.OnMethod(annotationToSearch, new BilingualString("MyMethod")), + }); + + var module = MockModule(mock => + { + mock.SetupGet(x => x.ModuleAttributes).Returns(new[] + { + new BslAnnotationAttribute("MyAnno") + }); + + var method = BslMethodBuilder.Create() + .Name("MyMethod") + .SetAnnotations(new[] { new BslAnnotationAttribute("MyAnno") }) + .Build(); + + mock.SetupGet(x => x.Methods).Returns(new BslMethodInfo[] { method }); + }); + + checker.Setup(x => x.Validate(module)).Verifiable(); + + var resolver = new PredefinedInterfaceResolver(new[] { checker.Object }); + resolver.Resolve(module); + + checker.Verify(x => x.Validate(It.IsAny()), Times.Once); + } + + [Fact] + public void Test_CheckerReactsOnlyOnMethodAnnotations() + { + var annotationToSearch = new BilingualString("MyAnno"); + var checker = new Mock(); + checker.Setup(x => x.GetRegistrations()).Returns(new[] + { + PredefinedInterfaceRegistration.OnMethod(annotationToSearch, new BilingualString("MyMethod")), + }); + + var module = MockModule(mock => + { + var method = BslMethodBuilder.Create() + .Name("MyMethod") + .SetAnnotations(new[] { new BslAnnotationAttribute("MyAnno") }) + .Build(); + + mock.SetupGet(x => x.Methods).Returns(new BslMethodInfo[] { method }); + }); + + checker.Setup(x => x.Validate(module)).Callback((m) => + { + m.Interfaces.Add(typeof(string), "Registered"); + }); + + var resolver = new PredefinedInterfaceResolver(new[] { checker.Object }); + resolver.Resolve(module); + + module.Interfaces.Should().Contain(KeyValuePair.Create(typeof(string), (object)"Registered")); + } + + private static IExecutableModule MockModule(Action> setup) + { + var dict = new Dictionary(); + var moduleMock = new Mock(); + moduleMock.SetupGet(x => x.Interfaces).Returns(dict); + + setup(moduleMock); + + return moduleMock.Object; + } + +} \ No newline at end of file diff --git a/src/Tests/OneScript.Language.Tests/LexerTests.cs b/src/Tests/OneScript.Language.Tests/LexerTests.cs index 0d047c52e..2f13ab36c 100644 --- a/src/Tests/OneScript.Language.Tests/LexerTests.cs +++ b/src/Tests/OneScript.Language.Tests/LexerTests.cs @@ -586,12 +586,12 @@ public void Syntax_Error_Handling() Assert.Equal("Б", lex.Content); } - [Fact(Skip = "Рефакторинг")] + [Fact] public void New_Exception_Shows_Negative_Line_And_Column() { - //var e = new ScriptException(); - //Assert.True(e.LineNumber == -1); - //Assert.True(e.ColumnNumber == -1); + var e = new ScriptException("fake"); + Assert.True(e.LineNumber == -1); + Assert.True(e.ColumnNumber == -1); } [Fact] @@ -633,6 +633,23 @@ public void Lexer_Ignores_Comments() Assert.Equal("value", lex.Content); } + [Fact] + public void Lexer_Extracts_Labels() + { + string code = "~ImALabel:\n" + + " ~LabelRef;"; + var lexer = GetLexerForCode(code); + + var lex = lexer.NextLexem(); + + Assert.Equal(LexemType.Label, lex.Type); + Assert.Equal("ImALabel", lex.Content); + + lex = lexer.NextLexem(); + Assert.Equal(LexemType.LabelRef, lex.Type); + Assert.Equal("LabelRef", lex.Content); + } + private ILexer GetLexerForCode(string code) { return new FullSourceLexer diff --git a/src/Tests/OneScript.Language.Tests/ParserTests.cs b/src/Tests/OneScript.Language.Tests/ParserTests.cs index ec6144928..41ed6f52a 100644 --- a/src/Tests/OneScript.Language.Tests/ParserTests.cs +++ b/src/Tests/OneScript.Language.Tests/ParserTests.cs @@ -1168,6 +1168,206 @@ public void TestLocalExportVar() }); } + [Fact] + public void TestAsyncProcedure() + { + var code = + @"Асинх Процедура Проц1() + Перем Переменная; + КонецПроцедуры"; + + var node = ParseModuleAndGetValidator(code); + node.Is(NodeKind.MethodsSection); + + var methodNode = node.NextChild().Is(NodeKind.Method); + + methodNode.CurrentNode.RealNode.As().IsAsync.Should().BeTrue(); + } + + [Fact] + public void TestAsyncFunction() + { + var code = + @"Асинх Функция Ф1() + Перем Переменная; + КонецФункции"; + + var node = ParseModuleAndGetValidator(code); + node.Is(NodeKind.MethodsSection); + + var methodNode = node.NextChild().Is(NodeKind.Method); + + methodNode.CurrentNode.RealNode.As().IsAsync.Should().BeTrue(); + } + + [Fact] + public void TestAwaitPriority() + { + var code = + @"Асинх Процедура Проц1() + А = Ждать Вызов().Поле; + КонецПроцедуры"; + + var node = ParseModuleAndGetValidator(code); + node.Is(NodeKind.MethodsSection); + + var method = node.NextChild().Is(NodeKind.Method); + var expression = method.DownTo(NodeKind.Assignment) + .NextChildIs(NodeKind.Identifier) + .NextChild(); + + expression.CurrentNode.RealNode.As().Operation.Should().Be(Token.Await); + expression + .NextChildIs(NodeKind.DereferenceOperation) + .NoMoreChildren(); + } + + [Fact] + public void TestAwaitMustBeInAsyncOnly() + { + var code = + @"Процедура Проц1() + Ждать Операция(); + КонецПроцедуры"; + + CatchParsingError(code, errors => + { + errors.Single().Description.Should().Contain("Await"); + }); + } + + [Fact] + public void AwaitIsNotKeywordInNonAsyncContext_Variable() + { + var code = + @"Процедура Проц1() + А = Ждать; + КонецПроцедуры"; + + var validator = ParseModuleAndGetValidator(code) + .DownTo(NodeKind.Assignment); + + validator + .NextChildIs(NodeKind.Identifier) + .NextChildIs(NodeKind.Identifier); + } + + [Fact] + public void AwaitIsNotKeywordInNonAsyncContext_Method() + { + var code = + @"Процедура Проц1() + А = Ждать(); + КонецПроцедуры"; + + var validator = ParseModuleAndGetValidator(code) + .DownTo(NodeKind.Assignment); + + validator + .NextChildIs(NodeKind.Identifier) + .NextChildIs(NodeKind.GlobalCall); + } + + [Fact] + public void AwaitIsNotKeywordInNonAsyncContext_PropertyName() + { + var code = + @"Процедура Проц1() + А = П.Ждать; + КонецПроцедуры"; + + var validator = ParseModuleAndGetValidator(code) + .DownTo(NodeKind.DereferenceOperation); + + validator + .NextChildIs(NodeKind.Identifier) + .NextChildIs(NodeKind.Identifier); + } + + [Fact] + public void AwaitIsNotKeywordInNonAsyncContext_MemberMethodName() + { + var code = + @"Процедура Проц1() + А = П.Ждать(); + КонецПроцедуры"; + + var validator = ParseModuleAndGetValidator(code) + .DownTo(NodeKind.DereferenceOperation); + + validator + .NextChildIs(NodeKind.Identifier) + .NextChildIs(NodeKind.MethodCall); + } + + [Fact] + public void Await_In_NonAsync_Expression_Fails() + { + var code = + @"Процедура Проц1() + Если Ждать ВтороеСлово Тогда + КонецЕсли; + КонецПроцедуры"; + + CatchParsingError(code, err => + { + err.Single().ErrorId.Should().Be("AwaitMustBeInAsyncMethod"); + }); + } + + [Fact] + public void AwaitRequiresExpression() + { + var code = + @"Асинх Процедура Проц1() + А = Ждать; + КонецПроцедуры"; + + CatchParsingError(code, err => err.Single().ErrorId.Should().Be("ExpressionSyntax")); + } + + [Fact] + public void DoubleAwaitIsForbidden() + { + var code = + @"Асинх Процедура Проц1() + А = Ждать Ждать Б; + КонецПроцедуры"; + + CatchParsingError(code, err => err.Single().ErrorId.Should().Be("ExpressionSyntax")); + } + + [Fact] + public void Labels_Can_Appear_In_CodeBlocks() + { + var code = + @"А = 1; + ~Метка: + Б = 2; + "; + + var validator = ParseBatchAndGetValidator(code); + + validator.NextChildIs(NodeKind.Assignment); + validator.NextChildIs(NodeKind.Label); + validator.NextChildIs(NodeKind.Assignment); + } + + [Fact] + public void Goto_Can_Appear_In_CodeBlocks() + { + var code = + @"А = 1; + Перейти ~Метка; + Б = 2; + "; + + var validator = ParseBatchAndGetValidator(code); + + validator.NextChildIs(NodeKind.Assignment); + validator.NextChildIs(NodeKind.Goto); + validator.NextChildIs(NodeKind.Assignment); + } private static void CatchParsingError(string code) { diff --git a/src/Tests/OneScript.Language.Tests/TestAstNode.cs b/src/Tests/OneScript.Language.Tests/TestAstNode.cs index 613371016..e909e7952 100644 --- a/src/Tests/OneScript.Language.Tests/TestAstNode.cs +++ b/src/Tests/OneScript.Language.Tests/TestAstNode.cs @@ -31,7 +31,7 @@ public TestAstNode(BslSyntaxNode node) BinaryOperationNode binary => binary.Operation.ToString(), UnaryOperationNode unary => unary.Operation.ToString(), PreprocessorDirectiveNode preproc => preproc.DirectiveName, - _ => Value + _ => nonTerm.ToString() }; } else if(node is TerminalNode term) @@ -39,6 +39,11 @@ public TestAstNode(BslSyntaxNode node) _childrenLazy = new Lazy>(new TestAstNode[0]); Value = term.Lexem.Content; } + else + { + _childrenLazy = new Lazy>(new TestAstNode[0]); + Value = node.ToString(); + } } public string Value { get; set; } diff --git a/src/Tests/OneScript.Language.Tests/TreeValidatorExtensions.cs b/src/Tests/OneScript.Language.Tests/TreeValidatorExtensions.cs index 93f319b0d..1b246c6cb 100644 --- a/src/Tests/OneScript.Language.Tests/TreeValidatorExtensions.cs +++ b/src/Tests/OneScript.Language.Tests/TreeValidatorExtensions.cs @@ -27,6 +27,41 @@ public static SyntaxTreeValidator Is(this SyntaxTreeValidator validator, NodeKin validator.CurrentNode.Kind.Should().Be(type); return validator; } + + public static SyntaxTreeValidator DownTo(this SyntaxTreeValidator validator, NodeKind type) + { + var node = DownTo(validator.CurrentNode, type); + if (node == null) + { + throw new Exception("No such child: " + type); + } + + return new SyntaxTreeValidator(node); + } + + private static TestAstNode DownTo(this TestAstNode node, NodeKind type) + { + if (node.Kind == type) + return node; + + if (node.Children.Count == 0) + return null; + + foreach (var childNode in node.ChildrenList) + { + if (childNode.Kind == type) + return childNode; + + if (childNode.ChildrenList.Count == 0) + continue; + + var result = DownTo(childNode, type); + if (result != null) + return result; + } + + return null; + } public static void Is(this TestAstNode node, NodeKind type) { diff --git a/src/Tests/OneScript.Language.Tests/TrieTests.cs b/src/Tests/OneScript.Language.Tests/TrieTests.cs index f6c137daa..0c476e7d0 100644 --- a/src/Tests/OneScript.Language.Tests/TrieTests.cs +++ b/src/Tests/OneScript.Language.Tests/TrieTests.cs @@ -12,9 +12,9 @@ namespace OneScript.Language.Tests public class TrieTests { [Fact] - public void LexemTrieAdd() + public void IdentifiersTrieAdd() { - var t = new LexemTrie(); + var t = new IdentifiersTrie(); t.Add("Иван", 0); t.Add("Иволга", 1); @@ -28,7 +28,7 @@ public void LexemTrieAdd() [Fact] public void Tokens() { - var t = new LexemTrie(); + var t = new IdentifiersTrie(); t.Add("иначе", 1); t.Add("иначеесли", 2); diff --git a/src/VSCode.DebugAdapter/VSCode.DebugAdapter.csproj b/src/VSCode.DebugAdapter/VSCode.DebugAdapter.csproj index cc5e71fe0..e06cd92e7 100644 --- a/src/VSCode.DebugAdapter/VSCode.DebugAdapter.csproj +++ b/src/VSCode.DebugAdapter/VSCode.DebugAdapter.csproj @@ -3,7 +3,7 @@ - net461 + net48 Exe VSCode.DebugAdapter.Program Debug;Release;LinuxDebug diff --git a/tests/ValueTableIndex.os b/tests/ValueTableIndex.os new file mode 100644 index 000000000..eb46d3fbf --- /dev/null +++ b/tests/ValueTableIndex.os @@ -0,0 +1,245 @@ +/////////////////////////////////////////////////////////////////////// +// +// Тест проверки работы таблицы значений +// +// +/////////////////////////////////////////////////////////////////////// + +Перем юТест; + +//////////////////////////////////////////////////////////////////// +// Программный интерфейс + +Функция Версия() Экспорт + Возврат "0.2"; +КонецФункции + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + юТест = ЮнитТестирование; + ВсеТесты = Новый Массив; + + ВсеТесты.Добавить("ТестДолжен_СоздатьТаблицуЗначенийСИндексами"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьСтроковоеПредставлениеИндекса"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПоискСИндексом"); + ВсеТесты.Добавить("ТестДолжен_ПоведениеИндексовПриУдаленииКолонок"); + ВсеТесты.Добавить("ТестДолжен_ПоведениеИндексовПриИзмененииЗначенийИндексированныхПолей"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьВыбросИсключений"); + + Возврат ВсеТесты; + +КонецФункции + +Процедура ТестДолжен_СоздатьТаблицуЗначенийСИндексами() Экспорт + + Т = Новый ТаблицаЗначений; + Т.Колонки.Добавить("К1"); + Т.Колонки.Добавить("К2"); + Т.Колонки.Добавить("К3"); + + И1 = Т.Индексы.Добавить("К1"); + И2 = Т.Индексы.Добавить("К1,К2"); + И3 = Т.Индексы.Добавить("К1,К2,К3"); + + юТест.ПроверитьРавенство(Т.Индексы.Количество(), 3); + + Т.Индексы.Удалить(Т.Индексы[0]); + юТест.ПроверитьРавенство(Т.Индексы.Количество(), 2); + + Т.Колонки.К1.Имя = "К_1"; + юТест.ПроверитьРавенство(Т.Индексы[0][0], "К_1"); + + Для Каждого мИндекс Из Т.Индексы Цикл + + Для Каждого мИмяКолонки Из мИндекс Цикл + + Колонка = Т.Колонки.Найти(мИмяКолонки); + юТест.ПроверитьНеравенство(Колонка, Неопределено, "Не найдена колонка " + мИмяКолонки); + + КонецЦикла; + + КонецЦикла; + + Т.Индексы.Очистить(); + юТест.ПроверитьРавенство(Т.Индексы.Количество(), 0); + + ИндексСОдинаковымиПолями = Т.Индексы.Добавить("К2, К2, К2"); + юТест.ПроверитьРавенство(ИндексСОдинаковымиПолями[0], "К2"); + юТест.ПроверитьРавенство(ИндексСОдинаковымиПолями[1], "К2"); + юТест.ПроверитьРавенство(ИндексСОдинаковымиПолями[2], "К2"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьСтроковоеПредставлениеИндекса() Экспорт + + Т = Новый ТаблицаЗначений; + Т.Колонки.Добавить("К1"); + Т.Колонки.Добавить("К2"); + + И1 = Т.Индексы.Добавить("К1"); + И2 = Т.Индексы.Добавить("К1,К2"); + И3 = Т.Индексы.Добавить("К1,К2,К2"); + И4 = Т.Индексы.Добавить("К2,К1,К2"); + + юТест.ПроверитьРавенство(Строка(И1), "К1"); + юТест.ПроверитьРавенство(Строка(И2), "К1, К2"); + юТест.ПроверитьРавенство(Строка(И3), "К1, К2, К2"); + юТест.ПроверитьРавенство(Строка(И4), "К2, К1, К2"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПоискСИндексом() Экспорт + + Т = Новый ТаблицаЗначений; + Т.Колонки.Добавить("К1"); + Т.Колонки.Добавить("К2"); + Т.Индексы.Добавить("К1"); + Т.Индексы.Добавить("К2,К1"); + + НекоеБольшоеЧисло = 1000; // не такое уж большое, чтобы тесты проходили быстрее + Для Инд = 1 По НекоеБольшоеЧисло Цикл + + НоваяСтрока = Т.Добавить(); + НоваяСтрока.К1 = НекоеБольшоеЧисло; + НоваяСтрока.К2 = Инд; + НоваяСтрока.К1 = Инд; + + КонецЦикла; + + НайтиИПроверить(Т, НекоеБольшоеЧисло, "Поиск по индексированному полю"); + + Т.Колонки.К1.Имя = "ДругоеИмя"; + НайтиИПроверить(Т, НекоеБольшоеЧисло, "Переименовали проиндексированную колонку"); + юТест.ПроверитьРавенство(Т.Индексы[0][0], "ДругоеИмя"); + + Т.Колонки.ДругоеИмя.Имя = "К1"; + НайтиИПроверить(Т, НекоеБольшоеЧисло, "Вернули имя"); + + Т.Свернуть("К2, К1"); + НайтиИПроверить(Т, НекоеБольшоеЧисло, "Свернули 0 раз"); + + Т.Свернуть("К2", "К1"); + НайтиИПроверить(Т, НекоеБольшоеЧисло, "Свернули 1 раз"); + + Т.Свернуть("К1", "К2"); + НайтиИПроверить(Т, НекоеБольшоеЧисло, "Свернули 2 раз"); + + Т.Свернуть("К1"); + НайтиИПроверить(Т, НекоеБольшоеЧисло, "Свернули 3 раз"); + +КонецПроцедуры + +Процедура ТестДолжен_ПоведениеИндексовПриУдаленииКолонок() Экспорт + + Т = Новый ТаблицаЗначений; + Т.Колонки.Добавить("К1"); + Т.Добавить().К1 = 1; + Т.Добавить().К1 = 2; + Т.Добавить().К1 = 3; + + Т.Индексы.Добавить("К1"); + юТест.ПроверитьРавенство(Строка(Т.Индексы[0]), "К1"); + + Т.Колонки.Очистить(); + юТест.ПроверитьРавенство(Строка(Т.Индексы[0]), ""); // Индекс не удаляется, состав полей пустой + + Т.Колонки.Добавить("К1"); + Т[0].К1 = 3; + Т[1].К1 = 2; + Т[2].К1 = 1; + + СтруктураПоиска = Новый Структура("К1", 1); + НайденныеСтроки = Т.НайтиСтроки(СтруктураПоиска); + юТест.ПроверитьРавенство(НайденныеСтроки.Количество(), 1); + юТест.ПроверитьРавенство(НайденныеСтроки[0], Т[2]); + +КонецПроцедуры + +Процедура ТестДолжен_ПоведениеИндексовПриИзмененииЗначенийИндексированныхПолей() Экспорт + + Т = Новый ТаблицаЗначений; + Т.Колонки.Добавить("К1"); + Т.Колонки.Добавить("К2"); + Т.Индексы.Добавить("К2"); + Т.Индексы.Добавить("К2, К1"); + Т.Индексы.Добавить("К1"); + Т.Добавить().К1 = 1; + Т.Добавить().К1 = 2; + Т.Добавить().К1 = 3; + + Т[0].К1 = 3; + Т[1].К1 = 2; + Т[2].К1 = 1; + + СтруктураПоиска = Новый Структура("К1", 1); + НайденныеСтроки = Т.НайтиСтроки(СтруктураПоиска); + юТест.ПроверитьРавенство(НайденныеСтроки.Количество(), 1); + юТест.ПроверитьРавенство(НайденныеСтроки[0], Т[2]); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьВыбросИсключений() Экспорт + + Т = Новый ТаблицаЗначений; + Т.Колонки.Добавить("К1"); + Т.Колонки.Добавить("К2"); + Т.Индексы.Добавить("К2"); + Т.Индексы.Добавить("К2, К1"); + Т.Индексы.Добавить("К1"); + + БылоИсключение = Ложь; + Попытка + Индекс = Т.Индексы[4]; + Исключение + БылоИсключение = Истина; + КонецПопытки; + юТест.ПроверитьИстину(БылоИсключение, "Доступ за пределы диапазона"); + + БылоИсключение = Ложь; + Попытка + Индекс = Т.Индексы[-1]; + Исключение + БылоИсключение = Истина; + КонецПопытки; + юТест.ПроверитьИстину(БылоИсключение, "Доступ за пределы диапазона"); + + Индекс = Т.Индексы[0]; + Т.Индексы.Удалить(Индекс); + БылоИсключение = Ложь; + Попытка + Т.Индексы.Удалить(Индекс); + Исключение + БылоИсключение = Истина; + КонецПопытки; + юТест.ПроверитьИстину(БылоИсключение, "Удаление несуществующего индекса"); + + БылоИсключение = Ложь; + Попытка + Т.Индексы[0] = Индекс; + Исключение + БылоИсключение = Истина; + КонецПопытки; + юТест.ПроверитьИстину(БылоИсключение, "Запись по индексу"); + + БылоИсключение = Ложь; + Попытка + Т.Индексы.Добавить("НесуществующееПоле"); + Исключение + БылоИсключение = Истина; + КонецПопытки; + юТест.ПроверитьИстину(БылоИсключение, "Индекс по несуществующему полю"); + +КонецПроцедуры + +Процедура НайтиИПроверить(Знач Таблица, Знач ИскомоеЗначение, Знач Пояснение) + + // в проверяемом тесте индексированное поле всегда первое + ИндексированноеПоле = Таблица.Колонки[0].Имя; + + Отбор = Новый Структура(ИндексированноеПоле, ИскомоеЗначение); + ПоискПоИндексу = Таблица.НайтиСтроки(Отбор); + + юТест.ПроверитьРавенство(ПоискПоИндексу.Количество(), 1, Пояснение); + юТест.ПроверитьРавенство(ПоискПоИндексу[0][ИндексированноеПоле], ИскомоеЗначение, Пояснение); + +КонецПроцедуры diff --git a/tests/binary-objects.os b/tests/binary-objects.os index 130fc076a..aa5bc3a01 100644 --- a/tests/binary-objects.os +++ b/tests/binary-objects.os @@ -11,6 +11,7 @@ ВсеТесты = Новый Массив; ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоСоздаетсяБуферДвоичныхДанных"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьРазделениеБуфераДвоичныхДанныхНесколькимиБуферами"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоМожноЗаписатьБайты"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоМожноПрочитатьБайты"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоМожноЗаписатьПрочитатьЦелое16"); @@ -30,6 +31,8 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоМетодОткрытьПотокДляЧтенияВозвращаетПотокТолькоДляЧтения"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоХешированиеРаботаетСПотоком"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоХешированиеРаботаетСДвоичнымиДанными"); + + ВсеТесты.Добавить("ТестДолжен_ПроверитьРазделениеБуфераДвоичныхДанныхОднимБуфером"); Возврат ВсеТесты; @@ -351,3 +354,58 @@ НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеширование.ХешСумма))); КонецПроцедуры + +Процедура ТестДолжен_ПроверитьРазделениеБуфераДвоичныхДанныхОднимБуфером() Экспорт + + Подстроки = Новый Массив; + Подстроки.Добавить("Часть1"); + Подстроки.Добавить("Часть2"); + Подстроки.Добавить("Часть3"); + РазделительТекстом = "123"; + + ТестовыйБуфер = ПолучитьБуферДвоичныхДанныхИзСтроки(СтрСоединить(Подстроки, РазделительТекстом)); + Разделитель = ПолучитьБуферДвоичныхДанныхИзСтроки(РазделительТекстом); + + РазделенныеДанные = ТестовыйБуфер.Разделить(Разделитель); + + юТест.ПроверитьРавенство(РазделенныеДанные.Количество(), 3, "два разделителя - три элемента"); + + Для Инд = 0 По Подстроки.ВГраница() Цикл + + СтрокаИзБуфера = ПолучитьСтрокуИзБуфераДвоичныхДанных(РазделенныеДанные[Инд]); + юТест.ПроверитьРавенство(СтрокаИзБуфера, Подстроки[Инд], "сравнение строк после разделения буфера"); + + КонецЦикла; + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьРазделениеБуфераДвоичныхДанныхНесколькимиБуферами() Экспорт + + Подстроки = Новый Массив; + Подстроки.Добавить("Часть1"); + Подстроки.Добавить("Часть2"); + Подстроки.Добавить("Часть3"); + Подстроки.Добавить("Часть4"); + Подстроки.Добавить("Часть5"); + + ТестоваяСтрока = "Часть1\R\R\NЧасть2\R\NЧасть3\NЧасть4\RЧасть5"; + + ТестовыйБуфер = ПолучитьБуферДвоичныхДанныхИзСтроки(ТестоваяСтрока); + Разделители = Новый Массив; + Разделители.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("\N")); + Разделители.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("\R\N")); + Разделители.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("\R")); + Разделители.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("\R\R\N")); + + РазделенныеДанные = ТестовыйБуфер.Разделить(Разделители); + + юТест.ПроверитьРавенство(РазделенныеДанные.Количество(), 5); + + Для Инд = 0 По РазделенныеДанные.ВГраница() Цикл + + СтрокаИзБуфера = ПолучитьСтрокуИзБуфераДвоичныхДанных(РазделенныеДанные[Инд]); + юТест.ПроверитьРавенство(СтрокаИзБуфера, Подстроки[Инд], "сравнение строк после разделения буфера"); + + КонецЦикла; + +КонецПроцедуры diff --git a/tests/iterators/annotated_iterator.os b/tests/iterators/annotated_iterator.os new file mode 100644 index 000000000..b27a2b63a --- /dev/null +++ b/tests/iterators/annotated_iterator.os @@ -0,0 +1,24 @@ + +Перем Индекс; +Перем Коллекция; +Перем Размер; + +&Итератор +Процедура ПриСозданииОбъекта(пКоллекция) + Коллекция = пКоллекция; + Индекс = 0; + Размер = Коллекция.Количество(); +КонецПроцедуры + +Функция Следующий() + Если Индекс < Размер-1 Тогда + Индекс = Индекс + 1; + Возврат Истина; + Иначе + Возврат Ложь; + КонецЕсли; +КонецФункции + +Функция ТекущийЭлемент() + Возврат Коллекция.Получить(Индекс); +КонецФункции \ No newline at end of file diff --git a/tests/iterators/collection.os b/tests/iterators/collection.os new file mode 100644 index 000000000..25709f27e --- /dev/null +++ b/tests/iterators/collection.os @@ -0,0 +1,17 @@ +#Обходимое + +Функция ПолучитьИтератор() + Возврат Новый ИтераторКоллекции(ЭтотОбъект); +КонецФункции + +Функция Количество() Экспорт + Возврат 10; +КонецФункции + +Функция Получить(Индекс) Экспорт + Если Индекс < 10 тогда + Возврат Индекс; + КонецЕсли; + + ВызватьИсключение "Индекс выходит за границы коллекции"; +КонецФункции \ No newline at end of file diff --git a/tests/iterators/collection_method_style.os b/tests/iterators/collection_method_style.os new file mode 100644 index 000000000..e55a747dd --- /dev/null +++ b/tests/iterators/collection_method_style.os @@ -0,0 +1,20 @@ + +&Обходимое +Процедура ПриСозданииОбъекта() +КонецПроцедуры + +Функция ПолучитьИтератор() + Возврат Новый ИтераторСАннотацией(ЭтотОбъект); +КонецФункции + +Функция Количество() Экспорт + Возврат 10; +КонецФункции + +Функция Получить(Индекс) Экспорт + Если Индекс < 10 тогда + Возврат Индекс; + КонецЕсли; + + ВызватьИсключение "Индекс выходит за границы коллекции"; +КонецФункции \ No newline at end of file diff --git a/tests/iterators/iterator.os b/tests/iterators/iterator.os new file mode 100644 index 000000000..7868e21dc --- /dev/null +++ b/tests/iterators/iterator.os @@ -0,0 +1,23 @@ + +Перем Индекс; +Перем Коллекция; +Перем Размер; + +Процедура ПриСозданииОбъекта(пКоллекция) + Коллекция = пКоллекция; + Индекс = 0; + Размер = Коллекция.Количество(); +КонецПроцедуры + +Функция Следующий() + Если Индекс < Размер-1 Тогда + Индекс = Индекс + 1; + Возврат Истина; + Иначе + Возврат Ложь; + КонецЕсли; +КонецФункции + +Функция ТекущийЭлемент() + Возврат Коллекция.Получить(Индекс); +КонецФункции \ No newline at end of file diff --git a/tests/iterators/test-iterators.os b/tests/iterators/test-iterators.os new file mode 100644 index 000000000..a54de631a --- /dev/null +++ b/tests/iterators/test-iterators.os @@ -0,0 +1,57 @@ +Перем юТест; + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + юТест = ЮнитТестирование; + + ВсеТесты = Новый Массив; + ВсеТесты.Добавить("ТестДолжен_ОбойтиКоллекцию"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоМодулиСАннотациейКонструктораРаботают"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоИтераторПроверяетсяПриКомпиляции"); + + Возврат ВсеТесты; + +КонецФункции + +Процедура ТестДолжен_ОбойтиКоллекцию() Экспорт + + ТекКаталог = ТекущийСценарий().Каталог; + + ПодключитьСценарий(ОбъединитьПути(ТекКаталог, "iterator.os"), "ИтераторКоллекции"); + ПодключитьСценарий(ОбъединитьПути(ТекКаталог, "collection.os"), "Коллекция"); + + Коллекция = Новый Коллекция; + + Индекс = 1; + Для Каждого Элемент Из Коллекция цикл + юТест.ПроверитьРавенство(Индекс, Элемент); + Индекс = Индекс + 1; + КонецЦикла; + + юТест.ПроверитьРавенство(10, Индекс); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЧтоМодулиСАннотациейКонструктораРаботают() Экспорт + + ТекКаталог = ТекущийСценарий().Каталог; + + ПодключитьСценарий(ОбъединитьПути(ТекКаталог, "collection_method_style.os"), "КоллекцияСАннотациейМетода"); + ПодключитьСценарий(ОбъединитьПути(ТекКаталог, "annotated_iterator.os"), "ИтераторСАннотацией"); + + Коллекция = Новый КоллекцияСАннотациейМетода; + + Индекс = 1; + Для Каждого Элемент Из Коллекция цикл + юТест.ПроверитьРавенство(Индекс, Элемент); + Индекс = Индекс + 1; + КонецЦикла; + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЧтоИтераторПроверяетсяПриКомпиляции() Экспорт + + Скрипт = "#Итератор + |Процедура Привет() КонецПроцедуры"; + + юТест.ПроверитьКодСОшибкой(Скрипт, "Обязательный метод Следующий отсутствует"); +КонецПроцедуры \ No newline at end of file diff --git a/tests/native-api.os b/tests/native-api.os index 1f538d95c..bfebd457f 100644 --- a/tests/native-api.os +++ b/tests/native-api.os @@ -21,8 +21,6 @@ ЭтоWindows = Истина; #ИначеЕсли Linux Тогда ЭтоWindows = Ложь; - Каталог = ТекущийСценарий().Каталог + "/native-api/"; - ЗапуститьПриложение(Каталог + "build.sh", Каталог, Истина); #Иначе Возврат Новый Массив; // под макос не тестируем #КонецЕсли diff --git a/tests/reflector.os b/tests/reflector.os index beb8820c7..7973d1ca4 100644 --- a/tests/reflector.os +++ b/tests/reflector.os @@ -24,6 +24,9 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьМетод_ПолучитьТаблицуСвойств"); ВсеТесты.Добавить("ТестДолжен_ПроверитьВызовМетодаСПараметрамиПоУмолчанию"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьВызовМетодаБезПараметровПоУмолчанию"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПередачуПараметровПоУмолчанию"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьМетодСуществуетДляТипа"); ВсеТесты.Добавить("ТестДолжен_ПроверитьПолучитьТаблицуМетодовДляТипа"); ВсеТесты.Добавить("ТестДолжен_ПроверитьПолучитьТаблицуСвойствДляСистемногоТипа"); @@ -41,6 +44,15 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьРефлексиюДля_ЭтотОбъект"); ВсеТесты.Добавить("ТестДолжен_ПроверитьПустыеАннотации"); ВсеТесты.Добавить("ТестДолжен_ПроверитьПриватныеПоля"); + + ВсеТесты.Добавить("ТестДолжен_ПроверитьИзвестныеТипы"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИзвестныеТипы_СОтборомКоллекция"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИзвестныеТипы_СОтборомПримитивный"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИзвестныеТипы_СОтборомПользовательский"); + +#Если Windows Тогда + ВсеТесты.Добавить("ТестДолжен_ПроверитьВызовМетодаComОбъекта"); +#КонецЕсли Возврат ВсеТесты; КонецФункции @@ -215,7 +227,7 @@ ТаблицаМетодов = Рефлектор.ПолучитьТаблицуМетодов(Пример); юТест.ПроверитьРавенство(Строка(ТипЗнч(ТаблицаМетодов)), "ТаблицаЗначений", "ТаблицаМетодов"); - юТест.ПроверитьРавенство(6, ТаблицаМетодов.Количество(), "ТаблицаМетодов.Количество()"); + юТест.ПроверитьРавенство(7, ТаблицаМетодов.Количество(), "ТаблицаМетодов.Количество()"); Метод0 = ТаблицаМетодов.Найти("ВызватьМетод", "Имя"); юТест.ПроверитьНеРавенство(Неопределено, Метод0, "Метод0"); @@ -415,6 +427,47 @@ КонецФункции +Процедура ТестДолжен_ПроверитьВызовМетодаБезПараметровПоУмолчанию() Экспорт + Рефлектор = Новый Рефлектор; + + Массив = Новый Массив(1); + Арг = Новый Массив; + Арг.Добавить(0); + + Ошибка = Ложь; + Попытка + Рефлектор.ВызватьМетод(Массив, "Установить", Арг); + Ч = Массив[0]; + Ошибка = Истина; + Исключение + Сообщение = ОписаниеОшибки(); + Если СтрНайти(Сообщение,"NullReferenceException") <> 0 Тогда + Ошибка = Истина; + КонецЕсли; + КонецПопытки; + + Если Ошибка Тогда + юТест.ТестПровален("Вызов рефлектора с недостаточным количеством параметров без умолчания"); + КонецЕсли; + +КонецПроцедуры + +Процедура ТестПередачиПараметровПоУмолчанию(Пар1, Пар2="п2") Экспорт + юТест.ПроверитьРавенство(Пар1, "п1", "Тест передачи параметров: параметр 1"); + юТест.ПроверитьРавенство(Пар2, "п2", "Тест передачи параметров: параметр 2"); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПередачуПараметровПоУмолчанию() Экспорт + Рефлектор = Новый Рефлектор; + + Массив = Новый Массив(1); + Арг = Новый Массив; + Арг.Добавить("п1"); + Рефлектор.ВызватьМетод(ЭтотОбъект, "ТестПередачиПараметровПоУмолчанию", Арг); + +КонецПроцедуры + + Процедура ТестДолжен_ПроверитьМетодСуществуетДляТипа() Экспорт Рефлектор = Новый Рефлектор; @@ -667,4 +720,165 @@ юТест.ПроверитьНеравенство(0, ТаблицаМетодов.Количество()); юТест.ПроверитьНеравенство(0, ТаблицаСвойств.Количество()); -КонецПроцедуры \ No newline at end of file +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИзвестныеТипы() Экспорт + + ПолучитьОбъектДляПроверки("МойКлассныйТип"); + + Рефлектор = Новый Рефлектор; + + ИзвестныеТипы = Рефлектор.ИзвестныеТипы(); + + ПроверитьТипИзвестныхТипов(ИзвестныеТипы); + + Для Каждого ИзвестныйТип Из ИзвестныеТипы Цикл + ПроверитьСтрокуИзвестныхТипов(ИзвестныйТип); + КонецЦикла; + + ОписаниеТипаЧисло = ИзвестныеТипы.Найти(Тип("Число")); + + юТест.ПроверитьЗаполненность(ОписаниеТипаЧисло); + юТест.ПроверитьТип(ОписаниеТипаЧисло, "СтрокаТаблицыЗначений"); + юТест.ПроверитьИстину(ОписаниеТипаЧисло.Примитивный); + юТест.ПроверитьЛожь(ОписаниеТипаЧисло.Пользовательский); + юТест.ПроверитьЛожь(ОписаниеТипаЧисло.Коллекция); + + ОписаниеТипаМассив = ИзвестныеТипы.Найти(Тип("Массив")); + + юТест.ПроверитьЗаполненность(ОписаниеТипаМассив); + юТест.ПроверитьТип(ОписаниеТипаМассив, "СтрокаТаблицыЗначений"); + юТест.ПроверитьЛожь(ОписаниеТипаМассив.Примитивный); + юТест.ПроверитьЛожь(ОписаниеТипаМассив.Пользовательский); + юТест.ПроверитьИстину(ОписаниеТипаМассив.Коллекция); + + ОписаниеТипаМойКлассныйТип = ИзвестныеТипы.Найти(Тип("МойКлассныйТип")); + + юТест.ПроверитьЗаполненность(ОписаниеТипаМойКлассныйТип); + юТест.ПроверитьТип(ОписаниеТипаМойКлассныйТип, "СтрокаТаблицыЗначений"); + юТест.ПроверитьЛожь(ОписаниеТипаМойКлассныйТип.Примитивный); + юТест.ПроверитьИстину(ОписаниеТипаМойКлассныйТип.Пользовательский); + юТест.ПроверитьЛожь(ОписаниеТипаМойКлассныйТип.Коллекция); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИзвестныеТипы_СОтборомКоллекция() Экспорт + + ПолучитьОбъектДляПроверки("МойКлассныйТип"); + + Рефлектор = Новый Рефлектор; + + Отбор = Новый Структура("Коллекция", Истина); + + ИзвестныеТипы = Рефлектор.ИзвестныеТипы(Отбор); + + ПроверитьТипИзвестныхТипов(ИзвестныеТипы); + + Для Каждого ИзвестныйТип Из ИзвестныеТипы Цикл + ПроверитьСтрокуИзвестныхТипов(ИзвестныйТип); + + юТест.ПроверитьИстину(ИзвестныйТип.Коллекция); + юТест.ПроверитьЛожь(ИзвестныйТип.Примитивный); + юТест.ПроверитьЛожь(ИзвестныйТип.Пользовательский); + + КонецЦикла; + + ОписаниеТипаМассив = ИзвестныеТипы.Найти(Тип("Массив")); + + юТест.ПроверитьЗаполненность(ОписаниеТипаМассив); + юТест.ПроверитьТип(ОписаниеТипаМассив, "СтрокаТаблицыЗначений"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИзвестныеТипы_СОтборомПримитивный() Экспорт + + ПолучитьОбъектДляПроверки("МойКлассныйТип"); + + Рефлектор = Новый Рефлектор; + + Отбор = Новый Структура("Примитивный", Истина); + + ИзвестныеТипы = Рефлектор.ИзвестныеТипы(Отбор); + + ПроверитьТипИзвестныхТипов(ИзвестныеТипы); + + Для Каждого ИзвестныйТип Из ИзвестныеТипы Цикл + + ПроверитьСтрокуИзвестныхТипов(ИзвестныйТип); + + юТест.ПроверитьЛожь(ИзвестныйТип.Коллекция); + юТест.ПроверитьИстину(ИзвестныйТип.Примитивный); + юТест.ПроверитьЛожь(ИзвестныйТип.Пользовательский); + + КонецЦикла; + + ОписаниеТипаЧисло = ИзвестныеТипы.Найти(Тип("Число")); + + юТест.ПроверитьЗаполненность(ОписаниеТипаЧисло); + юТест.ПроверитьТип(ОписаниеТипаЧисло, "СтрокаТаблицыЗначений"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИзвестныеТипы_СОтборомПользовательский() Экспорт + + ПолучитьОбъектДляПроверки("МойКлассныйТип"); + + Рефлектор = Новый Рефлектор; + + Отбор = Новый Структура("Пользовательский", Истина); + + ИзвестныеТипы = Рефлектор.ИзвестныеТипы(Отбор); + + ПроверитьТипИзвестныхТипов(ИзвестныеТипы); + + Для Каждого ИзвестныйТип Из ИзвестныеТипы Цикл + + ПроверитьСтрокуИзвестныхТипов(ИзвестныйТип); + + юТест.ПроверитьЛожь(ИзвестныйТип.Коллекция); + юТест.ПроверитьЛожь(ИзвестныйТип.Примитивный); + юТест.ПроверитьИстину(ИзвестныйТип.Пользовательский); + + КонецЦикла; + + ОписаниеТипаМойКлассныйТип = ИзвестныеТипы.Найти(Тип("МойКлассныйТип")); + + юТест.ПроверитьЗаполненность(ОписаниеТипаМойКлассныйТип); + юТест.ПроверитьТип(ОписаниеТипаМойКлассныйТип, "СтрокаТаблицыЗначений"); + +КонецПроцедуры + +Процедура ПроверитьТипИзвестныхТипов(ИзвестныеТипы) + + юТест.ПроверитьТип(ИзвестныеТипы, "ТаблицаЗначений"); + юТест.ПроверитьНеРавенство(ИзвестныеТипы.Количество(), 0); + + юТест.ПроверитьРавенство(ИзвестныеТипы.Колонки.Количество(), 5); + юТест.ПроверитьЗаполненность(ИзвестныеТипы.Колонки.Найти("Имя")); + юТест.ПроверитьЗаполненность(ИзвестныеТипы.Колонки.Найти("Значение")); + юТест.ПроверитьЗаполненность(ИзвестныеТипы.Колонки.Найти("Примитивный")); + юТест.ПроверитьЗаполненность(ИзвестныеТипы.Колонки.Найти("Пользовательский")); + юТест.ПроверитьЗаполненность(ИзвестныеТипы.Колонки.Найти("Коллекция")); + +КонецПроцедуры + +Процедура ПроверитьСтрокуИзвестныхТипов(ИзвестныйТип) + + юТест.ПроверитьТип(ИзвестныйТип.Имя, "Строка"); + юТест.ПроверитьТип(ИзвестныйТип.Значение, "Тип"); + юТест.ПроверитьТип(ИзвестныйТип.Примитивный, "Булево"); + юТест.ПроверитьТип(ИзвестныйТип.Пользовательский, "Булево"); + юТест.ПроверитьТип(ИзвестныйТип.Коллекция, "Булево"); + + юТест.ПроверитьЗаполненность(ИзвестныйТип.Имя); + юТест.ПроверитьЗаполненность(ИзвестныйТип.Значение); + +КонецПроцедуры + +#Если Windows Тогда +Процедура ТестДолжен_ПроверитьВызовМетодаComОбъекта() Экспорт + FSO = ПолучитьCOMОбъект("", "Scripting.FileSystemObject"); + Рез = FSO.DriveExists("C"); + юТест.ПроверитьТип(Рез, "Булево"); +КонецПроцедуры +#КонецЕсли diff --git a/tests/testdata/magic-object.os b/tests/testdata/magic-object.os index 08ebe52e4..a4bd36228 100644 --- a/tests/testdata/magic-object.os +++ b/tests/testdata/magic-object.os @@ -1,6 +1,10 @@ Перем ПроизвольноеПредставление Экспорт; Перем ЗначПоУмолчанию Экспорт; +Процедура ПриСозданииОбъекта(Прм1, Прм2, Прм3, Прм4 = 4) + ЗначПоУмолчанию = Прм4; +КонецПроцедуры + Процедура ОбработкаПолученияПредставления(Строка, СтандартнаяОбработка) Если ПроизвольноеПредставление = Неопределено Тогда @@ -12,6 +16,3 @@ КонецПроцедуры -Процедура ПриСозданииОбъекта(Прм1, Прм2, Прм3, Прм4 = 4) - ЗначПоУмолчанию = Прм4; -КонецПроцедуры