├── .gitignore ├── .paket └── Paket.Restore.targets ├── LICENSE ├── README.md ├── build.fsx ├── compiler.sln ├── fake.cmd ├── fake.sh ├── paket.dependencies ├── paket.lock └── src └── HastiLang.Core ├── BaseTypes.fs ├── ErrorExtensions.fs ├── HastiLang.Core.fsproj ├── HigherPipes.fs ├── IdeUtils.fs ├── IndentationParser.fs ├── InterpreterStates.fs ├── MSILCompiler.fs ├── ParserConfig.fs ├── ParserConfigs.fs ├── ParserUtils.fs ├── Parsers.fs └── paket.references /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | /.ionide/symbolCache.db 332 | -------------------------------------------------------------------------------- /.paket/Paket.Restore.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 8 | 9 | $(MSBuildVersion) 10 | 15.0.0 11 | false 12 | true 13 | 14 | true 15 | $(MSBuildThisFileDirectory) 16 | $(MSBuildThisFileDirectory)..\ 17 | $(PaketRootPath)paket-files\paket.restore.cached 18 | $(PaketRootPath)paket.lock 19 | classic 20 | proj 21 | assembly 22 | native 23 | /Library/Frameworks/Mono.framework/Commands/mono 24 | mono 25 | 26 | 27 | $(PaketRootPath)paket.bootstrapper.exe 28 | $(PaketToolsPath)paket.bootstrapper.exe 29 | $([System.IO.Path]::GetDirectoryName("$(PaketBootStrapperExePath)"))\ 30 | 31 | 32 | 33 | 34 | $(PaketRootPath)paket.exe 35 | $(PaketToolsPath)paket.exe 36 | $(PaketToolsPath)paket.exe 37 | $(_PaketBootStrapperExeDir)paket.exe 38 | paket.exe 39 | 40 | 41 | $(PaketRootPath)paket 42 | $(PaketToolsPath)paket 43 | $(PaketToolsPath)paket 44 | 45 | 46 | $(PaketRootPath)paket.exe 47 | $(PaketToolsPath)paket.exe 48 | 49 | 50 | $(PaketBootStrapperExeDir)paket.exe 51 | 52 | 53 | paket 54 | 55 | 56 | <_PaketExeExtension>$([System.IO.Path]::GetExtension("$(PaketExePath)")) 57 | dotnet "$(PaketExePath)" 58 | $(MonoPath) --runtime=v4.0.30319 "$(PaketExePath)" 59 | "$(PaketExePath)" 60 | 61 | 62 | "$(PaketBootStrapperExePath)" 63 | $(MonoPath) --runtime=v4.0.30319 "$(PaketBootStrapperExePath)" 64 | 65 | 66 | 67 | 68 | true 69 | true 70 | 71 | 72 | True 73 | 74 | $(BaseIntermediateOutputPath.TrimEnd('\').TrimEnd('\/')) 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | true 89 | $(NoWarn);NU1603;NU1604;NU1605;NU1608 90 | false 91 | true 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | $([System.IO.File]::ReadAllText('$(PaketRestoreCacheFile)')) 101 | 102 | 103 | 104 | 105 | 106 | 108 | $([System.Text.RegularExpressions.Regex]::Split(`%(Identity)`, `": "`)[0].Replace(`"`, ``).Replace(` `, ``)) 109 | $([System.Text.RegularExpressions.Regex]::Split(`%(Identity)`, `": "`)[1].Replace(`"`, ``).Replace(` `, ``)) 110 | 111 | 112 | 113 | 114 | %(PaketRestoreCachedKeyValue.Value) 115 | %(PaketRestoreCachedKeyValue.Value) 116 | 117 | 118 | 119 | 120 | true 121 | false 122 | true 123 | 124 | 125 | 129 | 130 | true 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | $(PaketIntermediateOutputPath)\$(MSBuildProjectFile).paket.references.cached 150 | 151 | $(MSBuildProjectFullPath).paket.references 152 | 153 | $(MSBuildProjectDirectory)\$(MSBuildProjectName).paket.references 154 | 155 | $(MSBuildProjectDirectory)\paket.references 156 | 157 | false 158 | true 159 | true 160 | references-file-or-cache-not-found 161 | 162 | 163 | 164 | 165 | $([System.IO.File]::ReadAllText('$(PaketReferencesCachedFilePath)')) 166 | $([System.IO.File]::ReadAllText('$(PaketOriginalReferencesFilePath)')) 167 | references-file 168 | false 169 | 170 | 171 | 172 | 173 | false 174 | 175 | 176 | 177 | 178 | true 179 | target-framework '$(TargetFramework)' or '$(TargetFrameworks)' files @(PaketResolvedFilePaths) 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | false 191 | true 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',').Length) 203 | $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[0]) 204 | $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[1]) 205 | $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[4]) 206 | $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[5]) 207 | 208 | 209 | %(PaketReferencesFileLinesInfo.PackageVersion) 210 | All 211 | runtime 212 | runtime 213 | true 214 | true 215 | 216 | 217 | 218 | 219 | $(PaketIntermediateOutputPath)/$(MSBuildProjectFile).paket.clitools 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | $([System.String]::Copy('%(PaketCliToolFileLines.Identity)').Split(',')[0]) 229 | $([System.String]::Copy('%(PaketCliToolFileLines.Identity)').Split(',')[1]) 230 | 231 | 232 | %(PaketCliToolFileLinesInfo.PackageVersion) 233 | 234 | 235 | 236 | 240 | 241 | 242 | 243 | 244 | 245 | false 246 | 247 | 248 | 249 | 250 | 251 | <_NuspecFilesNewLocation Include="$(PaketIntermediateOutputPath)\$(Configuration)\*.nuspec"/> 252 | 253 | 254 | 255 | 256 | 257 | $(MSBuildProjectDirectory)/$(MSBuildProjectFile) 258 | true 259 | false 260 | true 261 | false 262 | true 263 | false 264 | true 265 | false 266 | true 267 | $(PaketIntermediateOutputPath)\$(Configuration) 268 | $(PaketIntermediateOutputPath) 269 | 270 | 271 | 272 | <_NuspecFiles Include="$(AdjustedNuspecOutputPath)\*.$(PackageVersion.Split(`+`)[0]).nuspec"/> 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 330 | 331 | 374 | 375 | 417 | 418 | 459 | 460 | 461 | 462 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Hasti Language 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # کامپایلر 4 | این مخزن محل توسعه کامپایلر زبان برنامه نویسی هستی است 5 | 6 | این پروژه صرفا تحقیقاتی است و به هدف استفاده خارج از محیط آموزشی ساخته نشده است 7 |
8 | 9 | 10 | **This is only for research and not intended to be used** 11 | -------------------------------------------------------------------------------- /build.fsx: -------------------------------------------------------------------------------- 1 | #load ".fake/build.fsx/intellisense.fsx" 2 | open Fake.Core 3 | open Fake.DotNet 4 | open Fake.IO 5 | open Fake.IO.FileSystemOperators 6 | open Fake.IO.Globbing.Operators 7 | open Fake.Core.TargetOperators 8 | 9 | Target.create "Clean" (fun _ -> 10 | !! "src/**/bin" 11 | ++ "src/**/obj" 12 | |> Shell.cleanDirs 13 | ) 14 | 15 | Target.create "Build" (fun _ -> 16 | !! "src/**/*.*proj" 17 | |> Seq.iter (DotNet.build id) 18 | ) 19 | 20 | Target.create "All" ignore 21 | 22 | "Clean" 23 | ==> "Build" 24 | ==> "All" 25 | 26 | Target.runOrDefault "All" 27 | -------------------------------------------------------------------------------- /compiler.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".paket", ".paket", "{1CE5F572-30E7-4244-B600-753CC413A8E6}" 7 | ProjectSection(SolutionItems) = preProject 8 | paket.dependencies = paket.dependencies 9 | EndProjectSection 10 | EndProject 11 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{FC93A659-4436-4A37-AC5E-0EFF1B66A619}" 12 | EndProject 13 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "HastiLang.Core", "src\HastiLang.Core\HastiLang.Core.fsproj", "{898D5D40-411C-4D1D-AAB4-B4FFC8835BE8}" 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|Any CPU = Debug|Any CPU 18 | Debug|x64 = Debug|x64 19 | Debug|x86 = Debug|x86 20 | Release|Any CPU = Release|Any CPU 21 | Release|x64 = Release|x64 22 | Release|x86 = Release|x86 23 | EndGlobalSection 24 | GlobalSection(SolutionProperties) = preSolution 25 | HideSolutionNode = FALSE 26 | EndGlobalSection 27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 28 | {898D5D40-411C-4D1D-AAB4-B4FFC8835BE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {898D5D40-411C-4D1D-AAB4-B4FFC8835BE8}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {898D5D40-411C-4D1D-AAB4-B4FFC8835BE8}.Debug|x64.ActiveCfg = Debug|Any CPU 31 | {898D5D40-411C-4D1D-AAB4-B4FFC8835BE8}.Debug|x64.Build.0 = Debug|Any CPU 32 | {898D5D40-411C-4D1D-AAB4-B4FFC8835BE8}.Debug|x86.ActiveCfg = Debug|Any CPU 33 | {898D5D40-411C-4D1D-AAB4-B4FFC8835BE8}.Debug|x86.Build.0 = Debug|Any CPU 34 | {898D5D40-411C-4D1D-AAB4-B4FFC8835BE8}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {898D5D40-411C-4D1D-AAB4-B4FFC8835BE8}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {898D5D40-411C-4D1D-AAB4-B4FFC8835BE8}.Release|x64.ActiveCfg = Release|Any CPU 37 | {898D5D40-411C-4D1D-AAB4-B4FFC8835BE8}.Release|x64.Build.0 = Release|Any CPU 38 | {898D5D40-411C-4D1D-AAB4-B4FFC8835BE8}.Release|x86.ActiveCfg = Release|Any CPU 39 | {898D5D40-411C-4D1D-AAB4-B4FFC8835BE8}.Release|x86.Build.0 = Release|Any CPU 40 | EndGlobalSection 41 | GlobalSection(NestedProjects) = preSolution 42 | {898D5D40-411C-4D1D-AAB4-B4FFC8835BE8} = {FC93A659-4436-4A37-AC5E-0EFF1B66A619} 43 | EndGlobalSection 44 | EndGlobal 45 | -------------------------------------------------------------------------------- /fake.cmd: -------------------------------------------------------------------------------- 1 | SET TOOL_PATH=.fake 2 | 3 | IF NOT EXIST "%TOOL_PATH%\fake.exe" ( 4 | dotnet tool install fake-cli --tool-path ./%TOOL_PATH% 5 | ) 6 | 7 | "%TOOL_PATH%/fake.exe" %* -------------------------------------------------------------------------------- /fake.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | set -o pipefail 5 | 6 | # liberated from https://stackoverflow.com/a/18443300/433393 7 | realpath() { 8 | OURPWD=$PWD 9 | cd "$(dirname "$1")" 10 | LINK=$(readlink "$(basename "$1")") 11 | while [ "$LINK" ]; do 12 | cd "$(dirname "$LINK")" 13 | LINK=$(readlink "$(basename "$1")") 14 | done 15 | REALPATH="$PWD/$(basename "$1")" 16 | cd "$OURPWD" 17 | echo "$REALPATH" 18 | } 19 | 20 | TOOL_PATH=$(realpath .fake) 21 | FAKE="$TOOL_PATH"/fake 22 | 23 | if ! [ -e "$FAKE" ] 24 | then 25 | dotnet tool install fake-cli --tool-path "$TOOL_PATH" 26 | fi 27 | "$FAKE" "$@" 28 | -------------------------------------------------------------------------------- /paket.dependencies: -------------------------------------------------------------------------------- 1 | source https://www.nuget.org/api/v2 2 | nuget FSharp.Core 3 | nuget FParsec 4 | 5 | // [ FAKE GROUP ] 6 | group Build 7 | storage none 8 | source https://api.nuget.org/v3/index.json 9 | nuget Fake.DotNet.Cli 10 | nuget Fake.IO.FileSystem 11 | nuget Fake.Core.Target 12 | -------------------------------------------------------------------------------- /src/HastiLang.Core/BaseTypes.fs: -------------------------------------------------------------------------------- 1 | module BaseTypes 2 | 3 | type HstAtomicTypeName = TypeName of string 4 | and HstIdentifier = HstIdentifier of string 5 | and HstUnit = HstUnit of unit 6 | 7 | and HstType = 8 | | TypeAlias of string * HstTypeSig 9 | | RecordType of HstAtomicTypeName * (HstIdentifier * HstType) list 10 | | SumType of (HstAtomicTypeName * HstType) list 11 | 12 | and HstTypeSig = 13 | | Atomic of HstAtomicTypeName 14 | | Chain of HstAtomicTypeName list 15 | 16 | and HstBinding = 17 | { identifier : HstIdentifier 18 | args : HstIdentifier list option 19 | bindingType : HstTypeSig 20 | exps : HstStatement list } 21 | 22 | and HstIfStmt = 23 | { cond : HstExpression 24 | thendo : HstStatement list } 25 | 26 | and HstIfElseStmt = 27 | { cond : HstExpression 28 | thendo: HstStatement list 29 | elsedo : HstStatement list } 30 | 31 | and HstExpression = 32 | | ExpValue of HstValue 33 | | ExpOpCall of HstOperatorCall 34 | | BindingExpr of HstIdentifier 35 | 36 | and HstStatement = 37 | | IfStmt of HstIfStmt 38 | | IfElseStmt of HstIfElseStmt 39 | | TypeDef of HstType 40 | | Binding of HstBinding 41 | | Value of HstValue 42 | | OperatorCall of HstOperatorCall 43 | | PrintStatement of HstExpression 44 | | Loop of HstLoop 45 | | FuncCall of HstFuncCall 46 | 47 | and HstFuncCall = 48 | { id : HstIdentifier 49 | callArgs : HstCallArg list } 50 | 51 | and HstLoop = 52 | { fromIndex : HstNumber 53 | toIndex : HstNumber 54 | boundIdf : HstIdentifier 55 | body : HstStatement list } 56 | 57 | and HstValue = 58 | | Number of HstNumber 59 | | String of HstString 60 | | RecordInstance of HstRecordInstance 61 | | Boolean of HstBoolean 62 | | Unit 63 | 64 | and HstNumber = 65 | | Float of float 66 | | Integer of int 67 | 68 | and HstCallArg = 69 | | CallValue of HstValue 70 | | CallIdf of HstIdentifier 71 | 72 | and HstString = string 73 | 74 | and HstRecordInstance = HstAtomicTypeName * Map 75 | 76 | and HstBoolean = HstTrue | HstFalse 77 | 78 | and HstOperator = Operator of HstString 79 | 80 | and HstOperatorCall = 81 | { op : HstOperator 82 | exp1 : HstExpression 83 | exp2 : HstExpression } -------------------------------------------------------------------------------- /src/HastiLang.Core/ErrorExtensions.fs: -------------------------------------------------------------------------------- 1 | module ErrorExtensions 2 | open FParsec.Internals 3 | open FParsec 4 | 5 | let rep (a:string) b (text:string) = text.Replace(a, b) 6 | 7 | let toPersian (text:string) = 8 | text 9 | |> rep "The error occured at the end of the line" "خطایی در آخر خط رخ داده است" 10 | |> rep "Expecting:" "انتظار:" 11 | |> rep "Note:" "یادداشت:" 12 | |> rep "Unexpected:" "غیر‌منتظره:" 13 | |> rep " or " " یا " 14 | |> rep " and " " و " 15 | |> rep ", " "، " 16 | |> rep " could not be parsed because: " " نتوانست تجزیه شود به دلیل: " 17 | |> rep "The error occurred at the end of the input stream." "خطایی در انتهای ورودی رخ داده است." 18 | |> rep "Error in Ln:" "خطا در خط:" 19 | |> rep "Col:" "ستون:" 20 | |> rep "end of input" "پایان ورودی" 21 | |> rep "or" "یا" -------------------------------------------------------------------------------- /src/HastiLang.Core/HastiLang.Core.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/HastiLang.Core/HigherPipes.fs: -------------------------------------------------------------------------------- 1 | module HigherPipes 2 | 3 | open FParsec 4 | 5 | let pipe6 p1 p2 p3 p4 p5 p6 f = 6 | pipe4 p1 p2 p3 (tuple3 p4 p5 p6) 7 | (fun x1 x2 x3 (x4, x5, x6) -> f x1 x2 x3 x4 x5 x6) 8 | 9 | let pipe7 p1 p2 p3 p4 p5 p6 p7 f = 10 | pipe4 p1 p2 p3 (tuple4 p4 p5 p6 p7) 11 | (fun x1 x2 x3 (x4, x5, x6, x7) -> f x1 x2 x3 x4 x5 x6 x7) -------------------------------------------------------------------------------- /src/HastiLang.Core/IdeUtils.fs: -------------------------------------------------------------------------------- 1 | namespace HastiLang.Core.Ide 2 | 3 | open Parsers 4 | open BaseTypes 5 | 6 | module IdeUtils = 7 | open Parsers 8 | let RunParser = Parsers.test 9 | 10 | 11 | module CodeRunner = 12 | open InterpreterStates 13 | open System.Collections.Generic 14 | 15 | let rec eval (expr:HstExpression) (session:Session) = 16 | let inline checkEq a b = if a = b then Ok (Boolean HstTrue) else Ok (Boolean HstFalse) 17 | match expr with 18 | | ExpValue a -> Ok a 19 | | BindingExpr a -> 20 | if session.bindings.ContainsKey a then 21 | let instr = session.bindings.[a].exps |> List.last 22 | match instr with 23 | | Value x -> Ok x 24 | | _ -> Error "خطا در پایان بدنه انقیاد" 25 | else Error "انقیادی با نام مورد نظر وجود نداشت" 26 | | ExpOpCall c -> 27 | match c.op with 28 | | Operator "+" -> 29 | match (eval c.exp1 session, eval c.exp2 session) with 30 | | (Ok (Number j), Ok (Number k)) -> 31 | match (j, k ) with 32 | | (Float m , Float n) -> m + n |> (Float >> Number >> Ok) 33 | | (Integer m, Integer n) -> m + n |> (Integer >> Number >> Ok) 34 | | (Float m, Integer n) -> m + (float n) |> (Float >> Number >> Ok) 35 | | (Integer m, Float n) -> (float m) + n |> (Float >> Number >> Ok) 36 | | (Ok (String s1), Ok (String s2)) -> s1 + s2 |> (String >> Ok) 37 | | _ -> Error "مقدار جمع غیر قابل محاسبه است" 38 | 39 | | Operator "*" -> 40 | match (eval c.exp1 session, eval c.exp2 session ) with 41 | | (Ok (Number j), Ok (Number k)) -> 42 | match (j, k ) with 43 | | (Float m , Float n) -> m * n |> (Float >> Number >> Ok) 44 | | (Integer m, Integer n) -> m * n |> (Integer >> Number >> Ok) 45 | | (Float m, Integer n) -> m * (float n) |> (Float >> Number >> Ok) 46 | | (Integer m, Float n) -> (float m) * n |> (Float >> Number >> Ok) 47 | | Ok (Number j), Ok (String s) | Ok (String s), Ok (Number j) -> 48 | match j with 49 | | Float f -> String.replicate (int f) s |> (String >> Ok) 50 | | Integer i -> String.replicate i s |> (String >> Ok) 51 | 52 | | _ -> Error "مقدار ضرب غیر قابل محاسبه است" 53 | 54 | | Operator "%" -> 55 | match (eval c.exp1 session, eval c.exp2 session ) with 56 | | (Ok (Number j), Ok (Number k)) -> 57 | match (j, k ) with 58 | | (Float m , Float n) -> m % n |> (Float >> Number >> Ok) 59 | | (Integer m, Integer n) -> m % n |> (Integer >> Number >> Ok) 60 | | (Float m, Integer n) -> m % (float n) |> (Float >> Number >> Ok) 61 | | (Integer m, Float n) -> (float m) % n |> (Float >> Number >> Ok) 62 | | _ -> Error "مقدار باقی مانده غیر قابل محاسبه است" 63 | 64 | | Operator "==" -> 65 | match (eval c.exp1 session, eval c.exp2 session) with 66 | | (Ok (Number j), Ok (Number k)) -> 67 | match (j, k ) with 68 | | (Float m , Float n) -> checkEq m n 69 | | (Integer m, Integer n) -> checkEq m n 70 | | (Float m, Integer n) -> Error "نوع صحیح با اعشاری هم خوانی ندارد" 71 | | (Integer m, Float n) -> Error "نوع صحیح با اعشاری هم خوانی ندارد" 72 | | (Ok (String s1), Ok (String s2)) -> checkEq s1 s2 73 | | (Ok (Boolean b1), Ok (Boolean b2)) -> checkEq b1 b2 74 | | _ -> Error "برابری غیر قابل محاسبه است" 75 | 76 | | _ -> Error "اپراتور تعریف نشده" 77 | 78 | let evalBool (expr:HstExpression) (session:Session) = 79 | match eval expr session with 80 | | Ok (Boolean hb) -> match hb with HstTrue -> Ok true | HstFalse -> Ok false 81 | | Error s -> Error (sprintf "%s (باید یک عبارت با نتیجه بولی قرار بگیرد))" s) 82 | | _ -> Error "باید یک عبارت با نتیجه بولی قرار بگیرد" 83 | 84 | 85 | let createSession() = 86 | { 87 | definedTypes = Dictionary() 88 | bindings = Dictionary() 89 | } 90 | 91 | let statementPrint (stmt:HstExpression) session = 92 | match eval stmt session with 93 | | Ok x -> 94 | match x with 95 | | Number x -> x.ToString() 96 | | String x -> x 97 | | Boolean x -> if x = HstTrue then "درست" else "غلط" 98 | | RecordInstance _ -> "نوع:رکورد" 99 | | Unit -> "واحد" 100 | | Error e -> sprintf "خطا در محاسبه (%s)" e 101 | 102 | let rec exec (stmts:HstStatement list) (prints:List) (session:Session) = 103 | for s in stmts do 104 | match s with 105 | | PrintStatement p -> prints.Add (statementPrint p session) 106 | | Binding b -> session.bindings.Add (b.identifier, b) 107 | | IfStmt ifs -> 108 | match evalBool ifs.cond session with 109 | | Ok true -> (exec ifs.thendo prints session) |> ignore 110 | | Error x -> prints.Add x 111 | | _ -> () 112 | | IfElseStmt ifs -> 113 | match evalBool ifs.cond session with 114 | | Ok true -> (exec ifs.thendo prints session) |> ignore 115 | | Ok false -> (exec ifs.elsedo prints session) |> ignore 116 | | Error x -> prints.Add x 117 | | Loop x -> let lpFunc = loopExecutator x.body prints session x.boundIdf 118 | match (x.fromIndex, x.toIndex) with 119 | | (Integer a, Integer b) -> lpFunc a b 120 | | (Float a, Integer b) -> lpFunc (int a) b 121 | | (Integer a, Float b) -> lpFunc a (int b) 122 | | (Float a, Float b) -> lpFunc (int a) (int b) 123 | | FuncCall f -> let funcBinding = session.bindings.[f.id] 124 | execFunc funcBinding prints session f.callArgs 125 | | _ -> () 126 | 127 | prints 128 | 129 | and loopExecutator body prints session idf a b = 130 | let hasIdf = session.bindings.ContainsKey idf 131 | let oldBinding = if hasIdf then Some (session.bindings.[idf]) else None 132 | if hasIdf then session.bindings.Remove idf |> ignore 133 | let createValueBinding idf a = { identifier = idf 134 | args = None 135 | bindingType = Atomic (TypeName "عدد") 136 | exps = [a |> (Integer >> Number >> Value)] 137 | } 138 | session.bindings.Add (idf, createValueBinding idf a) 139 | for i = a to b do 140 | let newValue = createValueBinding idf i 141 | session.bindings.[idf] <- newValue 142 | (exec body prints session) |> ignore 143 | 144 | match oldBinding with 145 | | None -> session.bindings.Remove idf |> ignore 146 | | Some x -> session.bindings.[idf] <- x 147 | 148 | and execFunc funcBinding prints session (callArgs:HstCallArg list) = 149 | match funcBinding.args with 150 | | None -> prints.Add "آرگومانی برای اجرای تابع پاس داده نشده بود" 151 | | Some ar when ar.Length <> callArgs.Length -> prints.Add "آزگومان های تابع با تعداد آرگومان های درخواستی مطابقت نداشت. در این نسخه اعمال جزیی پشتیبانی نمیشود" 152 | | Some binds -> 153 | let zipArgsAndIdfs = binds |> List.zip callArgs 154 | let createSessionByCallArgs() = 155 | let bs = Dictionary() 156 | zipArgsAndIdfs |> List.iter (fun (arg, idf) -> 157 | match arg with 158 | | CallValue x -> bs.Add (idf, { identifier = idf 159 | args = None 160 | bindingType = Atomic (TypeName "Dynamic") 161 | exps = [Value x] }) 162 | | CallIdf a -> 163 | if session.bindings.ContainsKey a then 164 | let instr = session.bindings.[a].exps |> List.last 165 | match instr with 166 | | Value x -> bs.Add (idf, { identifier = idf 167 | args = None 168 | bindingType = Atomic (TypeName "Dynamic") 169 | exps = [Value x] }) 170 | | _ -> prints.Add "خطا در پایان بدنه انقیاد" 171 | else prints.Add "انقیادی با نام مورد نظر وجود نداشت" 172 | 173 | ) 174 | { definedTypes = Dictionary() 175 | bindings = bs} 176 | 177 | let tempSession = createSessionByCallArgs() 178 | 179 | exec funcBinding.exps prints tempSession |> ignore -------------------------------------------------------------------------------- /src/HastiLang.Core/IndentationParser.fs: -------------------------------------------------------------------------------- 1 | module IndentationParserWithBacktracking 2 | 3 | open FParsec 4 | open ParserConfigs 5 | 6 | let skipIndentation (stream: CharStream) = 7 | let mutable indentation = stream.SkipNewlineThenWhitespace(tabStopDistance, false) 8 | while stream.Peek() = '#' do 9 | stream.SkipRestOfLine(false) // skip comment 10 | indentation <- stream.SkipNewlineThenWhitespace(tabStopDistance, false) 11 | indentation 12 | 13 | let indented p : Parser<_> = 14 | fun stream -> 15 | let state = stream.State 16 | let indentation = skipIndentation stream 17 | let expectedIndentation = stream.UserState.Indentation 18 | if indentation < expectedIndentation || stream.IsEndOfStream then 19 | stream.BacktrackTo(state) 20 | Reply(Error, NoErrorMessages) 21 | elif indentation = expectedIndentation then 22 | p stream 23 | else // indentation > expectedIndentation 24 | Reply(Error, messageError "فاصله گذاری نامناسب") 25 | 26 | let indentedBlock p = 27 | Inline.Many(stateFromFirstElement = (fun x -> [x]), 28 | foldState = (fun xs x -> x::xs), 29 | resultFromState = List.rev, 30 | firstElementParser = p, 31 | elementParser = indented p) 32 | 33 | let indentedMany1 (p: Parser<'t>) label : Parser<'t list> = 34 | let indentedBlock = indentedBlock p 35 | fun stream -> 36 | let oldIndentation = stream.UserState.Indentation 37 | let indentation = skipIndentation stream 38 | if indentation <= oldIndentation then 39 | Reply(Error, expected (if indentation < 0 then "خط جدید" else "فاصله " + label)) 40 | else 41 | stream.UserState <- {stream.UserState with Indentation = indentation} 42 | let reply = indentedBlock stream 43 | if reply.Status = Ok then 44 | stream.UserState <- {stream.UserState with Indentation = oldIndentation} 45 | reply -------------------------------------------------------------------------------- /src/HastiLang.Core/InterpreterStates.fs: -------------------------------------------------------------------------------- 1 | module InterpreterStates 2 | 3 | open BaseTypes 4 | open System.Collections.Generic 5 | 6 | type Session = { 7 | definedTypes : Dictionary 8 | bindings : Dictionary 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/HastiLang.Core/MSILCompiler.fs: -------------------------------------------------------------------------------- 1 | module MSILCompiler 2 | 3 | /// To be done -------------------------------------------------------------------------------- /src/HastiLang.Core/ParserConfig.fs: -------------------------------------------------------------------------------- 1 | module ParserConfig 2 | open FParsec 3 | 4 | let tabStopDistance = 8 // must be a power of 2 5 | 6 | module IndentationParserWithBacktracking = 7 | type UserState = 8 | {Indentation: int} 9 | with 10 | static member Create() = {Indentation = -1} 11 | 12 | type CharStream = CharStream 13 | type Parser<'t> = Parser<'t, UserState> 14 | 15 | let skipIndentation (stream: CharStream) = 16 | let mutable indentation = stream.SkipNewlineThenWhitespace(tabStopDistance, false) 17 | while stream.Peek() = '#' do 18 | stream.SkipRestOfLine(false) // skip comment 19 | indentation <- stream.SkipNewlineThenWhitespace(tabStopDistance, false) 20 | indentation 21 | 22 | let indented p : Parser<_> = 23 | fun stream -> 24 | let state = stream.State 25 | let indentation = skipIndentation stream 26 | let expectedIndentation = stream.UserState.Indentation 27 | if indentation < expectedIndentation || stream.IsEndOfStream then 28 | stream.BacktrackTo(state) 29 | Reply(Error, NoErrorMessages) 30 | elif indentation = expectedIndentation then 31 | p stream 32 | else // indentation > expectedIndentation 33 | Reply(Error, messageError "wrong indentation") 34 | 35 | let indentedBlock p = 36 | Inline.Many(stateFromFirstElement = (fun x -> [x]), 37 | foldState = (fun xs x -> x::xs), 38 | resultFromState = List.rev, 39 | firstElementParser = p, 40 | elementParser = indented p) 41 | 42 | let indentedMany1 (p: Parser<'t>) label : Parser<'t list> = 43 | let indentedBlock = indentedBlock p 44 | fun stream -> 45 | let oldIndentation = stream.UserState.Indentation 46 | let indentation = skipIndentation stream 47 | if indentation <= oldIndentation then 48 | Reply(Error, expected (if indentation < 0 then "newline" else "indented " + label)) 49 | else 50 | stream.UserState <- {stream.UserState with Indentation = indentation} 51 | let reply = indentedBlock stream 52 | if reply.Status = Ok then 53 | stream.UserState <- {stream.UserState with Indentation = oldIndentation} 54 | reply 55 | 56 | module IndentationParserWithoutBacktracking = 57 | type LastParsedIndentation() = 58 | [] 59 | val mutable Value: int32 60 | [] 61 | val mutable EndIndex: int64 62 | 63 | type UserState = 64 | {Indentation: int 65 | // We put LastParsedIndentation into the UserState so that we 66 | // can conveniently use a separate instance for each stream. 67 | // The members of the LastParsedIndentation instance will be mutated 68 | // directly and hence won't be affected by any stream backtracking. 69 | LastParsedIndentation: LastParsedIndentation} 70 | with 71 | static member Create() = {Indentation = -1 72 | LastParsedIndentation = LastParsedIndentation(EndIndex = -1L)} 73 | 74 | type CharStream = CharStream 75 | type Parser<'t> = Parser<'t, UserState> 76 | 77 | // If this function is called at the same index in the stream 78 | // where the function previously stopped, then the previously 79 | // returned indentation will be returned again. 80 | // This way we can avoid backtracking at the end of indented blocks. 81 | let skipIndentation (stream: CharStream) = 82 | let lastParsedIndentation = stream.UserState.LastParsedIndentation 83 | if lastParsedIndentation.EndIndex = stream.Index then 84 | lastParsedIndentation.Value 85 | else 86 | let mutable indentation = stream.SkipNewlineThenWhitespace(tabStopDistance, false) 87 | while stream.Peek() = '#' do 88 | stream.SkipRestOfLine(false) // skip comment 89 | indentation <- stream.SkipNewlineThenWhitespace(tabStopDistance, false) 90 | lastParsedIndentation.EndIndex <- stream.Index 91 | lastParsedIndentation.Value <- indentation 92 | indentation 93 | 94 | let indentedMany1 (p: Parser<'t>) label : Parser<'t list> = 95 | fun stream -> 96 | let oldIndentation = stream.UserState.Indentation 97 | let indentation = skipIndentation stream 98 | if indentation <= oldIndentation then 99 | Reply(Error, expected (if indentation < 0 then "newline" else "indented " + label)) 100 | else 101 | stream.UserState <- {stream.UserState with Indentation = indentation} 102 | let results = ResizeArray() 103 | let mutable stateTag = stream.StateTag 104 | let mutable reply = p stream // parse the first element 105 | let mutable newIndentation = 0 106 | while reply.Status = Ok 107 | && (results.Add(reply.Result) 108 | newIndentation <- skipIndentation stream 109 | newIndentation = indentation) 110 | do 111 | stateTag <- stream.StateTag 112 | reply <- p stream 113 | if reply.Status = Ok 114 | || (stream.IsEndOfStream && results.Count > 0 && stream.StateTag = stateTag) 115 | then 116 | if newIndentation < indentation || stream.IsEndOfStream then 117 | stream.UserState <- {stream.UserState with Indentation = oldIndentation} 118 | Reply(List.ofSeq results) 119 | else 120 | Reply(Error, messageError "wrong indentation") 121 | else // p failed 122 | Reply(reply.Status, reply.Error) -------------------------------------------------------------------------------- /src/HastiLang.Core/ParserConfigs.fs: -------------------------------------------------------------------------------- 1 | module ParserConfigs 2 | open FParsec 3 | 4 | let tabStopDistance = 8 // must be a power of 2 5 | 6 | 7 | type IndentationState = 8 | {Indentation: int} 9 | with 10 | static member Create() = {Indentation = -1} 11 | 12 | type CharStream = CharStream 13 | type Parser<'t> = Parser<'t, IndentationState> 14 | -------------------------------------------------------------------------------- /src/HastiLang.Core/ParserUtils.fs: -------------------------------------------------------------------------------- 1 | module ParserUtils 2 | 3 | open FParsec 4 | open ParserConfigs 5 | 6 | let achoice (ps:Parser<'a, 'u> seq) = choice [for x in ps -> attempt x] -------------------------------------------------------------------------------- /src/HastiLang.Core/Parsers.fs: -------------------------------------------------------------------------------- 1 | module Parsers 2 | open System 3 | open FParsec 4 | open ErrorExtensions 5 | open BaseTypes 6 | 7 | open ParserConfigs 8 | 9 | module Signs = 10 | let eq = "=" 11 | let colon = ":" 12 | let lightArrow = "->" 13 | let oPara = "(" 14 | let cPara = ")" 15 | 16 | let availableOpChars = ['+'; '='; '$'; '|'; '>'; '<'; '*'; '/'; '%'] 17 | 18 | module ParserAliases = 19 | let str = pstring 20 | let isBlank = fun c -> c = ' ' || c = '\t' 21 | let ws = skipMany1SatisfyL isBlank "فاصله" 22 | 23 | 24 | module SignParsers = 25 | open ParserAliases 26 | open Signs 27 | let peq : Parser<_> = str eq 28 | let sigc : Parser<_> = str ":" .>> ws 29 | let plight : Parser<_> = str lightArrow .>> ws 30 | let poPara : Parser<_> = str oPara 31 | let pcPara : Parser<_> = str cPara 32 | 33 | module Keywords = 34 | let use' = "استفاده" 35 | let type' = "نوع" 36 | let with' = "با" 37 | let if' = "اگه" 38 | let then' = "اونوقت" 39 | let else' = "وگرنه" 40 | let true' = "درست" 41 | let false' = "غلط" 42 | 43 | let from' = "از" 44 | let ta' = "تا" 45 | 46 | let print' = "بنویس" 47 | 48 | let keywords = [ 49 | use'; type'; with'; if'; then'; else'; true'; false'; print'; 50 | from'; ta'; 51 | ] 52 | 53 | 54 | module KeywordParsers = 55 | open ParserAliases 56 | open Keywords 57 | let keyword str = pstring str //>>? nextCharSatisfiesNot (fun c -> isLetter c || isDigit c) str 58 | 59 | let pkw_use : Parser<_> = keyword use' 60 | let pkw_type : Parser<_> = keyword type' 61 | let pkw_with : Parser<_> = keyword with' 62 | let pkw_if : Parser<_> = keyword if' .>>? ws 63 | let pkw_then : Parser<_> = keyword then' 64 | let pkw_else : Parser<_> = keyword else' 65 | 66 | let pkw_from : Parser<_> = keyword from' 67 | let pkw_ta : Parser<_> = keyword ta' 68 | 69 | let pkw_true : Parser<_> = stringReturn true' HstTrue 70 | let pkw_false : Parser<_> = stringReturn false' HstFalse 71 | 72 | let pkw_print : Parser<_> = keyword print' 73 | 74 | module ReserveParsers = 75 | open Keywords 76 | open ParserAliases 77 | let notReserved : Parser<_> = 78 | let strWs x = str x .>> ws 79 | keywords |> List.map (strWs >> notFollowedBy) |> List.reduce (>>?) 80 | 81 | 82 | let idf = notReserved >>? identifier (IdentifierOptions()) .>> ws "شناساگر" 83 | let idfn = notReserved >>? identifier (IdentifierOptions()) "شناساگر" 84 | 85 | module LiteralParsers = 86 | open KeywordParsers 87 | open ParserAliases 88 | open ReserveParsers 89 | /// Parses a string literal 90 | let stringLiteral : Parser<_> = 91 | let normalCharSnippet = many1Satisfy (fun c -> c <> '/' && c <> '"') 92 | let specialChars = anyOf "/نرت\"" "یکی از کاراکتر های ویژه «ن» یا «ر» یا «ت» انتظار میرفت" 93 | let escapedChar = pstring "/" >>. (specialChars |>> function 94 | | 'ن' -> "\n" 95 | | 'ر' -> "\r" 96 | | 'ت' -> "\t" 97 | | c -> string c) .>> (pstring "/" "انتهای کاراکتر ویژه باید با «/» تمام شود") 98 | between (pstring "\"") (pstring "\"") 99 | (manyStrings (normalCharSnippet <|> escapedChar)) "رشته متنی" 100 | 101 | let numLiteral : Parser = (attempt pint32 "عدد صحیح" |>> Integer) <|> 102 | (attempt pfloat "عدد اعشاری" |>> Float) 103 | 104 | let precordInstance : Parser<_> = str "رکورد" 105 | let pbool = (attempt pkw_true <|> attempt pkw_false) "مقدار بولی" 106 | 107 | let punit = stringReturn "()" Unit 108 | 109 | let pliteral : Parser = (attempt stringLiteral |>> String) <|> 110 | (attempt numLiteral |>> Number) <|> 111 | (attempt pbool |>> Boolean) <|> 112 | (attempt punit) 113 | 114 | 115 | let poperator : Parser = many1Satisfy (fun c -> Signs.availableOpChars |> List.contains c) |>> Operator 116 | 117 | 118 | module LanguageConstructParsers = 119 | open SignParsers 120 | open KeywordParsers 121 | open LiteralParsers 122 | open ParserAliases 123 | open BaseTypes 124 | open ReserveParsers 125 | open IndentationParserWithBacktracking 126 | open ParserUtils 127 | 128 | let comment : Parser<_> = pstring "#" >>. skipRestOfLine false 129 | let wsEol = skipManySatisfy isBlank >>. optional comment 130 | let owsEol = optional wsEol 131 | let nl : Parser<_> = newline 132 | let indNewline = indentedMany1 nl "خط بندی جدید" 133 | 134 | let ptypesig : Parser = sepBy1 idf plight |>> 135 | fun x -> if x.Length = 1 then Atomic (TypeName x.[0]) 136 | else (x |> List.map TypeName) |> Chain 137 | 138 | 139 | 140 | 141 | let pexpr, pexprRef = createParserForwardedToRef() 142 | let pparaExpr, pparaExprRef = createParserForwardedToRef() 143 | let value = pliteral |>> Value 144 | 145 | let valueExpr = pliteral |>> ExpValue 146 | 147 | let bindingExpr = idfn |>> (HstIdentifier >> BindingExpr) 148 | 149 | let valOrBindExpr = attempt valueExpr <|> attempt bindingExpr 150 | 151 | let opCall = 152 | pipe3 (valOrBindExpr .>>? ws) 153 | (poperator .>>? ws) 154 | (pexpr) 155 | 156 | (fun exp1 op exp2 -> { op = op; exp1 = exp1; exp2 = exp2}) 157 | 158 | let opCallExpr = opCall |>> ExpOpCall 159 | 160 | let paraOpCallExpr = poPara >>? opCallExpr .>>? pcPara 161 | 162 | do pexprRef := achoice [ 163 | paraOpCallExpr 164 | opCallExpr 165 | valueExpr 166 | bindingExpr 167 | ] 168 | 169 | let pstatement, pstatementRef = createParserForwardedToRef() 170 | let indentedStatements, indentedStatementsRef = createParserForwardedToRef() 171 | 172 | 173 | 174 | let ifStmt = 175 | pipe2 (pkw_if >>? pexpr .>>? ws .>>? pkw_then .>>? wsEol) 176 | (indentedStatements .>>? wsEol) 177 | 178 | (fun cond thn -> IfStmt { cond = cond; thendo = thn }) "عبارت شرطی" 179 | 180 | let ifStmtInline = 181 | pipe2 (pkw_if >>? pexpr) 182 | (ws >>? pkw_then >>? ws >>? pstatement) 183 | 184 | (fun cond thn -> IfStmt { cond = cond; thendo = [thn] }) "عبارت شرطی" 185 | 186 | let ifElseStmt = 187 | pipe3 (pkw_if >>? pexpr .>>? ws .>>? pkw_then .>>? wsEol) 188 | (indentedStatements .>>? wsEol) 189 | (indented pkw_else >>? indentedStatements .>>? wsEol) 190 | 191 | (fun cond thn els -> IfElseStmt { cond = cond; thendo = thn; elsedo = els }) "عبارت شرطی" 192 | 193 | let ifElseStmtInline = 194 | pipe3 (pkw_if >>? pexpr) 195 | (ws >>? pkw_then >>? ws >>? pstatement) 196 | (ws >>? pkw_else >>? ws >>? pstatement) 197 | 198 | (fun cond thn els -> IfElseStmt { cond = cond; thendo = [thn]; elsedo = [els] }) "عبارت شرطی" 199 | 200 | let binding = 201 | pipe3 (many1 idf .>>? sigc) 202 | (ptypesig .>>? peq .>>? wsEol) 203 | (indentedStatements) 204 | 205 | (fun id tsig body -> Binding { 206 | identifier = HstIdentifier id.[0] 207 | args = if id.Length > 1 then Some (id.[1..] |> List.map HstIdentifier) else None 208 | bindingType = tsig 209 | exps = body 210 | }) 211 | 212 | let basicBinding = 213 | pipe3 (many1 idf .>>? sigc) 214 | (ptypesig .>>? peq .>>? ws) 215 | (pstatement .>>? wsEol) 216 | 217 | (fun id tsig stmt -> Binding { 218 | identifier = HstIdentifier id.[0] 219 | args = if id.Length > 1 then Some (id.[1..] |> List.map HstIdentifier) else None 220 | bindingType = tsig 221 | exps = [stmt] 222 | }) 223 | 224 | let ploop = 225 | pipe4 (pkw_from >>? ws >>? idf .>>? peq .>>? ws) 226 | (numLiteral .>>? ws) 227 | (pkw_ta >>? ws >>? numLiteral .>>? ws .>>? pkw_then .>>? wsEol) 228 | (indentedStatements .>>? wsEol) 229 | 230 | (fun idf from' to' body -> { boundIdf = HstIdentifier idf; fromIndex = from'; toIndex = to'; body = body }) |>> Loop 231 | 232 | let ploopInline = 233 | pipe4 (pkw_from >>? ws >>? idf .>>? peq .>>? ws) 234 | (numLiteral .>>? ws) 235 | (pkw_ta >>? ws >>? numLiteral .>>? ws .>>? pkw_then .>>? ws) 236 | (pstatement) 237 | 238 | (fun idf from' to' stmt -> { boundIdf = HstIdentifier idf ; fromIndex = from'; toIndex = to'; body = [stmt] }) |>> Loop 239 | 240 | 241 | let typedef = 242 | pipe2 (idf .>>? sigc) 243 | pkw_type 244 | 245 | (fun id _ -> TypeAlias (id, (TypeName "x") |> Atomic) |> TypeDef) 246 | 247 | let pcallArg = attempt (pliteral |>> CallValue) <|> attempt (idfn |>> (HstIdentifier >> CallIdf)) 248 | 249 | let funcCall = 250 | pipe2 idfn 251 | (many1 (ws >>? pcallArg)) 252 | 253 | (fun idf callArgs -> { id = HstIdentifier idf; callArgs = callArgs }) |>> FuncCall 254 | 255 | let printStmt = (pkw_print >>? ws >>? pexpr) |>> PrintStatement 256 | 257 | do pstatementRef := achoice [ 258 | ifElseStmt 259 | ifElseStmtInline 260 | ifStmt 261 | ifStmtInline 262 | value 263 | typedef 264 | binding 265 | basicBinding 266 | printStmt 267 | ploop 268 | ploopInline 269 | funcCall 270 | ] 271 | 272 | do indentedStatementsRef := indentedMany1 pstatement "عبارت" 273 | 274 | let parastatement = optional poPara >>? pstatement .>>? optional pcPara 275 | 276 | 277 | let document = indentedStatements .>> spaces .>> eof 278 | 279 | open FParsec.Error 280 | 281 | let longestMathRuleSplit (err:string) = 282 | err.Split([|"The parser backtracked after:"|], StringSplitOptions.None) 283 | |> Array.last 284 | 285 | type CompilerAnswer() = class end 286 | 287 | type CompilerSuccess(result) = 288 | inherit CompilerAnswer() 289 | member __.Result : HstStatement list = result 290 | 291 | type CompilerFailure(result:string) = 292 | inherit CompilerAnswer() 293 | member __.Error = result 294 | 295 | let test str = 296 | match runParserOnString LanguageConstructParsers.document (IndentationState.Create()) "" (str) with 297 | | Success(result, _, _) -> CompilerSuccess result :> CompilerAnswer 298 | | Failure(errorMsg, err, _) -> CompilerFailure (errorMsg |> toPersian |> longestMathRuleSplit) :> CompilerAnswer 299 | 300 | -------------------------------------------------------------------------------- /src/HastiLang.Core/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | FParsec --------------------------------------------------------------------------------