├── .github └── workflows │ ├── documentation.yml │ └── main.yml ├── .gitignore ├── LICENSE ├── README.md ├── docs ├── .gitignore ├── api │ └── index.md ├── articles │ ├── getting-started.md │ ├── index.md │ ├── interop.md │ └── toc.yml ├── docfx.json ├── index.md ├── templates │ ├── discordfx │ │ ├── layout │ │ │ └── _master.tmpl │ │ ├── partials │ │ │ ├── footer.tmpl.partial │ │ │ ├── head.tmpl.partial │ │ │ ├── li.tmpl.partial │ │ │ ├── logo.tmpl.partial │ │ │ ├── navbar.tmpl.partial │ │ │ ├── scripts.tmpl.partial │ │ │ └── toc.tmpl.partial │ │ └── styles │ │ │ ├── colors.css │ │ │ ├── discord.css │ │ │ ├── down-arrow.svg │ │ │ ├── jquery.twbsPagination.js │ │ │ ├── main.css │ │ │ ├── main.js │ │ │ └── url.min.js │ └── material │ │ ├── partials │ │ └── head.tmpl.partial │ │ └── styles │ │ └── main.css └── toc.yml └── src ├── Alex.MoLang.sln ├── Examples └── MolangSharp.Examples.Basic │ ├── BaseEntity.cs │ ├── MolangSharp.Examples.Basic.csproj │ └── Program.cs ├── Extensibility └── MolangSharp.Json.Newtonsoft │ ├── MolangExpressionConverter.cs │ └── MolangSharp.Json.Newtonsoft.csproj ├── MolangSharp.Tests ├── BinaryOpTest.cs ├── EqualityTest.cs ├── InteropOptimizationVisitorTest.cs ├── InteropTest.cs ├── MathOptimizationVisitorTest.cs ├── MathTest.cs ├── MolangSharp.Tests.csproj ├── PerformanceTest.cs └── ScriptTest.cs └── MolangSharp ├── Attributes ├── MoFunctionAttribute.cs ├── MoObservableAttribute.cs └── MoPropertyAttribute.cs ├── MolangSharp.csproj ├── Parser ├── Exceptions │ └── MoLangParserException.cs ├── Expression.cs ├── ExpressionMeta.cs ├── ExpressionTraverser.cs ├── ExpressionVisitor.cs ├── Expressions │ ├── ArrayAccessExpression.cs │ ├── AssignExpression.cs │ ├── BinaryOp │ │ ├── ArrowExpression.cs │ │ ├── BooleanAndExpressio.cs │ │ ├── BooleanOrExpression.cs │ │ ├── CoalesceExpression.cs │ │ ├── DivideExpression.cs │ │ ├── EqualExpression.cs │ │ ├── GreaterExpression.cs │ │ ├── GreaterOrEqualExpression.cs │ │ ├── MinusExpression.cs │ │ ├── NotEqualExpression.cs │ │ ├── PlusExpression.cs │ │ ├── PowExpression.cs │ │ ├── SmallerExpression.cs │ │ └── SmallerOrEqualExpression.cs │ ├── BinaryOpExpression.cs │ ├── BooleanExpression.cs │ ├── BooleanNotExpression.cs │ ├── BreakExpression.cs │ ├── ContinueExpression.cs │ ├── ForEachExpression.cs │ ├── FuncCallExpression.cs │ ├── LoopExpression.cs │ ├── NameExpression.cs │ ├── NumberExpression.cs │ ├── ReturnExpression.cs │ ├── ScriptExpression.cs │ ├── StatementExpression.cs │ ├── StringExpression.cs │ ├── TernaryExpression.cs │ ├── ThisExpression.cs │ ├── UnaryMinusExpression.cs │ └── UnaryPlusExpression.cs ├── IExpression.cs ├── IExpressionVisitor.cs ├── InfixParselet.cs ├── MoLangParser.cs ├── Parselet │ ├── ArrayAccessParselet.cs │ ├── AssignParselet.cs │ ├── BooleanNotParselet.cs │ ├── BooleanParselet.cs │ ├── BracketScopeParselet.cs │ ├── BreakParselet.cs │ ├── ContinueParselet.cs │ ├── FloatParselet.cs │ ├── ForEachParselet.cs │ ├── GenericBinaryOpParselet.cs │ ├── GroupParselet.cs │ ├── LoopParselet.cs │ ├── NameParselet.cs │ ├── NumberParselet.cs │ ├── ReturnParselet.cs │ ├── StringParselet.cs │ ├── TernaryParselet.cs │ ├── ThisParselet.cs │ ├── UnaryMinusParselet.cs │ └── UnaryPlusParselet.cs ├── Precedence.cs ├── PrefixParselet.cs ├── Tokenizer │ ├── ITokenIterator.cs │ ├── Token.cs │ ├── TokenIterator.cs │ ├── TokenPosition.cs │ └── TokenType.cs └── Visitors │ ├── ExpressionConnectingVisitor.cs │ ├── FindingVisitor.cs │ ├── FirstFindingVisitor.cs │ ├── InteropOptimization │ ├── InteropOptimizationVisitor.cs │ └── InteropOptimizationVisitorOptions.cs │ └── MathOptimizationVisitor.cs ├── Runtime ├── Exceptions │ └── MoLangRuntimeException.cs ├── MoLangEnvironment.cs ├── MoLangMath.cs ├── MoLangRuntime.cs ├── MoLangRuntimeConfiguration.cs ├── MoParams.cs ├── MoScope.cs ├── Struct │ ├── ArrayStruct.cs │ ├── ContextStruct.cs │ ├── IMoStruct.cs │ ├── Interop │ │ ├── FieldAccessor.cs │ │ ├── PropertyAccessor.cs │ │ ├── PropertyCache.cs │ │ └── ValueAccessor.cs │ ├── InteropStruct.cs │ ├── QueryStruct.cs │ └── VariableStruct.cs └── Value │ ├── DoubleValue.cs │ ├── MoValue.cs │ └── StringValue.cs └── Utils └── MoPath.cs /.github/workflows/documentation.yml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | 3 | on: 4 | push: 5 | branches-ignore: 6 | - gh-pages 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | with: 14 | submodules: 'recursive' 15 | 16 | - name: Setup .NET 17 | uses: actions/setup-dotnet@v1.8.0 18 | with: 19 | dotnet-version: 6.0.x 20 | 21 | - name: Restore Packages 22 | run: | 23 | dotnet restore src/MolangSharp/MolangSharp.csproj 24 | 25 | - name: Build 26 | run: | 27 | dotnet build --configuration Release --no-restore src/MolangSharp/MolangSharp.csproj 28 | 29 | - name: Build Documentation 30 | uses: nikeee/docfx-action@master 31 | with: 32 | args: docs/docfx.json 33 | 34 | - name: Publish Documentation on GitHub Pages 35 | uses: peaceiris/actions-gh-pages@v3 36 | with: 37 | github_token: ${{ secrets.GITHUB_TOKEN }} 38 | publish_dir: docs/_site -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | branches: main 6 | 7 | jobs: 8 | test: 9 | name: Unit Tests 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | with: 14 | submodules: 'recursive' 15 | 16 | - name: Setup .NET 17 | uses: actions/setup-dotnet@v1.8.0 18 | with: 19 | dotnet-version: 6.0.x 20 | 21 | - name: Install dependencies 22 | run: dotnet restore 23 | working-directory: src/MolangSharp.Tests 24 | 25 | - name: Build 26 | run: dotnet build --configuration Release --no-restore 27 | working-directory: src/MolangSharp.Tests 28 | 29 | - name: Test 30 | run: dotnet test --no-restore --verbosity normal 31 | working-directory: src/MolangSharp.Tests 32 | 33 | nuget: 34 | name: Publish to NuGET 35 | runs-on: ubuntu-latest 36 | needs: [test] 37 | steps: 38 | - uses: actions/checkout@v2 39 | with: 40 | submodules: 'recursive' 41 | fetch-depth: 0 42 | 43 | - name: Setup .NET 44 | uses: actions/setup-dotnet@v1.8.0 45 | with: 46 | dotnet-version: 6.0.x 47 | 48 | - name: Install dependencies 49 | run: dotnet restore src/MolangSharp/MolangSharp.csproj 50 | 51 | - name: Build 52 | run: dotnet build --configuration Release --no-restore src/MolangSharp/MolangSharp.csproj 53 | 54 | - name: Publish 55 | run: | 56 | dotnet pack -c Release /p:PackageVersion=$(date +%Y.%m.%d.%H%M%S) src/MolangSharp/MolangSharp.csproj -o . 57 | dotnet nuget push *.nupkg -s https://api.nuget.org/v3/index.json -k ${{secrets.NUGET_PUBLISH_KEY}} --skip-duplicate 58 | -------------------------------------------------------------------------------- /.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 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MolangSharp 2 | [![Nuget](https://buildstats.info/nuget/Alex.MoLang)](https://www.nuget.org/packages/Alex.MoLang/) ![Build Status](https://img.shields.io/github/workflow/status/ConcreteMC/MolangSharp/Publish/main) 3 | [![Discord](https://img.shields.io/discord/433462740451852292.svg?color=7289da&label=Discord&logo=discord&style=flat-square)](https://discord.gg/huvW4AyW5N) 4 | 5 | A Molang Parser & Runtime for C# 6 | 7 | Getting Started 8 | ------------ 9 | See the [documentation here](https://concretemc.github.io/MolangSharp/articles/getting-started.html). 10 | 11 | Awesome Repositories 12 | ------------------------ 13 | * [Alex](https://github.com/ConcreteMC/Alex) - A Minecraft (Java & Bedrock Edition) client written in C# 14 | * [MoLang](https://github.com/bedrockk/MoLang) - The project this is based on, a Molang runtime for Java 15 | 16 | Other resources 17 | --------------- 18 | * [Bedrock.dev](https://bedrock.dev/) - Provides some documentation on MoLang 19 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | ############### 2 | # folder # 3 | ############### 4 | /**/DROP/ 5 | /**/TEMP/ 6 | /**/packages/ 7 | /**/bin/ 8 | /**/obj/ 9 | _site 10 | -------------------------------------------------------------------------------- /docs/api/index.md: -------------------------------------------------------------------------------- 1 | # MoLangSharp API Documentation 2 | 3 | Written documentation is work in progress, in the meantime there is generated code documentation. See the top menu. 4 | -------------------------------------------------------------------------------- /docs/articles/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ## Basic example 4 | ```c# 5 | using ConcreteMC.MolangSharp.Parser; 6 | using ConcreteMC.MolangSharp.Runtime; 7 | 8 | //Initialize the runtime 9 | MoLangRuntime runtime = new MoLangRuntime(); 10 | 11 | //Parse our MoLang expression 12 | var parsed = MoLangParser.Parse("10 * 45.0"); 13 | 14 | //Execute the expression 15 | var result = runtime.Execute(parsed); 16 | Console.WriteLine($"Result={result.AsDouble()}"); 17 | ``` 18 | 19 | ### Output 20 | ```c# 21 | Result=450 22 | ``` 23 | 24 | ## More examples 25 | Check out the Examples folder in the Github repository: 26 | [https://github.com/ConcreteMC/MolangSharp/tree/main/src/examples](https://github.com/ConcreteMC/MolangSharp/tree/main/src/examples) -------------------------------------------------------------------------------- /docs/articles/index.md: -------------------------------------------------------------------------------- 1 | # Articles 2 | -------------------------------------------------------------------------------- /docs/articles/interop.md: -------------------------------------------------------------------------------- 1 | # Interoperability 2 | MolangSharp has the ability to expose class properties, fields & methods to the runtime. 3 | 4 | ## Defining a property 5 | In the following example we will show howto expose a property or field to MoLang. 6 | 7 | ```c# 8 | [MoProperty("myProperty")] 9 | public double MyProperty { get; set; } 10 | 11 | //The following property only has a GET accessor and will be readonly 12 | [MoProperty("myReadOnlyProperty")] 13 | public double ReadonlyProperty { get; } 14 | 15 | //This will expose the field to MoLang 16 | [MoProperty("myField")] 17 | public readonly double MyField; 18 | ``` 19 | 20 | ## Defining a function/method 21 | In the following example we will show howto expose a method to MoLang. 22 | ```c# 23 | [MoFunction("add")] 24 | public double Add(double a, double b) 25 | { 26 | return a + b; 27 | } 28 | ``` 29 | 30 | ## Example class 31 | This example will show how to expose our custom MyMath class to MoLang 32 | 33 | ### The class 34 | This is the class we'll be exposing to MoLang 35 | ```c# 36 | public class MyMath 37 | { 38 | [MoFunction("sin")] 39 | public double Sin(double value) => Math.Sin(value * (Math.PI / 180d)); 40 | 41 | [MoFunction("asin")] 42 | public double Asin(double value) => Math.Asin(value * (Math.PI / 180d)); 43 | 44 | [MoFunction("cos")] 45 | public double Cos(double value) => Math.Cos(value * (Math.PI / 180d)); 46 | 47 | [MoFunction("acos")] 48 | public double Acos(double value) => Math.Acos(value * (Math.PI / 180d)); 49 | } 50 | ``` 51 | 52 | ### Making the class available to the runtime 53 | ```c# 54 | MoLangEnvironment environment = new MoLangEnvironment(); 55 | environment.Structs.Add("custom_math", new InteropStruct(new MyMath())); 56 | MoLangRuntime runtime = new MoLangRuntime(environment); 57 | ``` 58 | 59 | This would expose the MyMath class to the runtime instance created in the example. The following example would now be valid. 60 | ``` 61 | custom_math.sin(12.0); 62 | ``` -------------------------------------------------------------------------------- /docs/articles/toc.yml: -------------------------------------------------------------------------------- 1 | - name: Home 2 | href: index.md 3 | 4 | - name: Getting Started 5 | href: getting-started.md 6 | 7 | - name: Interoperability 8 | href: interop.md -------------------------------------------------------------------------------- /docs/docfx.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": [ 3 | { 4 | "src": [ 5 | { 6 | "src": "../", 7 | "files": [ 8 | "src/MolangSharp/MolangSharp.csproj" 9 | ] 10 | } 11 | ], 12 | "dest": "api", 13 | "disableGitFeatures": false, 14 | "disableDefaultFilter": false 15 | } 16 | ], 17 | "build": { 18 | "content": [ 19 | { 20 | "files": [ 21 | "api/**.yml", 22 | "api/index.md" 23 | ] 24 | }, 25 | { 26 | "files": [ 27 | "articles/**.md", 28 | "articles/**/toc.yml", 29 | "toc.yml", 30 | "*.md" 31 | ] 32 | } 33 | ], 34 | "resource": [ 35 | { 36 | "files": [ 37 | "images/**" 38 | ] 39 | } 40 | ], 41 | "overwrite": [ 42 | { 43 | "files": [ 44 | "apidoc/**.md" 45 | ], 46 | "exclude": [ 47 | "obj/**", 48 | "_site/**" 49 | ] 50 | } 51 | ], 52 | "dest": "_site", 53 | "globalMetadataFiles": [], 54 | "fileMetadataFiles": [], 55 | "template": ["default", "templates/discordfx"], 56 | "globalMetadata": { 57 | "_enableSearch": "true" 58 | }, 59 | "postProcessors": ["ExtractSearchIndex"], 60 | "markdownEngineName": "markdig", 61 | "noLangKeyword": false, 62 | "keepFileLink": false, 63 | "cleanupCacheHistory": false, 64 | "disableGitFeatures": false 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # MoLangSharp Documentation 2 | 3 | Written documentation is work in progress, in the meantime there is generated code documentation. See the top menu. 4 | -------------------------------------------------------------------------------- /docs/templates/discordfx/layout/_master.tmpl: -------------------------------------------------------------------------------- 1 | {{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} 2 | {{!include(/^styles/.*/)}} 3 | {{!include(/^fonts/.*/)}} 4 | {{!include(favicon.ico)}} 5 | {{!include(logo.svg)}} 6 | {{!include(search-stopwords.json)}} 7 | 8 | 9 | 10 | 11 | {{>partials/head}} 12 | 13 | 14 |
15 | 16 | 17 | 20 | 21 | 22 | {{>partials/logo}} 23 | 24 |
25 | 26 |
27 | 28 |
29 | 30 |
31 | 32 |
33 | 34 | {{>partials/navbar}} 35 | 36 |
37 | 38 | {{^_disableToc}} 39 | {{>partials/toc}} 40 | {{/_disableToc}} 41 | 42 |
43 | 44 | {{>partials/footer}} 45 | 46 |
47 | 48 |
49 | 50 |
51 | 52 | {{^_disableBreadcrumb}} 53 | {{>partials/breadcrumb}} 54 | {{/_disableBreadcrumb}} 55 | 56 |
57 | {{!body}} 58 |
59 | 60 |
61 |
62 |
63 | 64 | {{>partials/scripts}} 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /docs/templates/discordfx/partials/footer.tmpl.partial: -------------------------------------------------------------------------------- 1 |
2 | {{{_appFooter}}} 3 | {{^_appFooter}}Generated by DocFX{{/_appFooter}} 4 |
-------------------------------------------------------------------------------- /docs/templates/discordfx/partials/head.tmpl.partial: -------------------------------------------------------------------------------- 1 | {{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} 2 | 3 | 4 | 5 | 6 | {{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}} 7 | 8 | 9 | 10 | {{#_description}}{{/_description}} 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | {{#_noindex}}{{/_noindex}} 20 | {{#_enableSearch}}{{/_enableSearch}} 21 | {{#_enableNewTab}}{{/_enableNewTab}} 22 | 23 | -------------------------------------------------------------------------------- /docs/templates/discordfx/partials/li.tmpl.partial: -------------------------------------------------------------------------------- 1 | {{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} 2 | 3 |
    4 | {{#items}} 5 | {{^dropdown}} 6 |
  • 7 | {{^leaf}} 8 | 9 | {{/leaf}} 10 | {{#topicHref}} 11 | {{name}} 12 | {{/topicHref}} 13 | {{^topicHref}} 14 | {{{name}}} 15 | {{/topicHref}} 16 | 17 | {{^leaf}} 18 | {{>partials/li}} 19 | {{/leaf}} 20 |
  • 21 | {{/dropdown}} 22 | {{#dropdown}} 23 |
  • 24 | {{name}} 25 |
      26 | {{>partials/dd-li}} 27 |
    28 |
  • 29 | {{/dropdown}} 30 | {{/items}} 31 |
32 | -------------------------------------------------------------------------------- /docs/templates/discordfx/partials/logo.tmpl.partial: -------------------------------------------------------------------------------- 1 | {{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} 2 | 3 | 4 | {{_appName}} 5 | {{_appName}} 6 | -------------------------------------------------------------------------------- /docs/templates/discordfx/partials/navbar.tmpl.partial: -------------------------------------------------------------------------------- 1 | {{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} 2 | 3 | 4 | 5 |
6 | {{>partials/logo}} 7 | 8 |
9 | 10 |
11 | 12 |
13 | 14 | -------------------------------------------------------------------------------- /docs/templates/discordfx/partials/scripts.tmpl.partial: -------------------------------------------------------------------------------- 1 | {{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /docs/templates/discordfx/partials/toc.tmpl.partial: -------------------------------------------------------------------------------- 1 | {{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} 2 | 3 |
4 |
5 |
6 | -------------------------------------------------------------------------------- /docs/templates/discordfx/styles/colors.css: -------------------------------------------------------------------------------- 1 | /* Color schemes */ 2 | :root 3 | { 4 | /* General */ 5 | --main-bg-color: #36393f; 6 | --footer-bg-color: rgba(0,0,0,.4); 7 | --table-strip-bg-color: #121315; 8 | --table-header-bg-color: #202225;; 9 | --table-header-color: hsla(0,0%,100%,.8); 10 | --table-header-border-color: #040405; 11 | 12 | /* Text */ 13 | --text-color: #dcddde; 14 | --link-color: #00b0f4; 15 | --link-hover-color: #4bd5ff; 16 | --link-active-color: #fff; 17 | --link-active-bg-color: #7289da; 18 | --h3-color: #ffffff85; 19 | --h4-color: #ffffffeb; 20 | --h5-color: #ffffffd1; 21 | 22 | /* Topbar */ 23 | --topbar-bg-color: #18191c; 24 | 25 | /* Button */ 26 | --button-color: #747f8d; 27 | 28 | /* Sidebar */ 29 | --separator-color: #4f545c; 30 | --sidebar-bg-color: #2f3136; 31 | --sidebar-item-color: #b9bbbe; 32 | --sidebar-item-2nd-color: hsla(0,0%,100%,.35); 33 | --sidebar-item-3rd-color: hsla(0,0%,100%,.25); 34 | 35 | /* Scrollbar */ 36 | --scrollbar-bg-color: transparent; 37 | --scrollbar-thumb-bg-color: rgba(0,0,0,.4); 38 | --scrollbar-thumb-border-color: transparent; 39 | 40 | /* Alerts and Blocks */ 41 | --alert-info-border-color: rgba(114,137,218,.5); 42 | --alert-info-bg-color: rgba(114,137,218,.1); 43 | 44 | --alert-warning-border-color: rgba(250,166,26,.5); 45 | --alert-warning-bg-color: rgba(250,166,26,.1); 46 | 47 | --alert-danger-border-color: rgba(240,71,71,.5); 48 | --alert-danger-bg-color: rgba(240,71,71,.1); 49 | 50 | --alert-tip-border-color: rgba(255,255,255,.5); 51 | --alert-tip-bg-color: rgba(255,255,255,.1); 52 | 53 | --blockquote-border-color: rgba(255,255,255,.5); 54 | --blockquote-bg-color: rgba(255,255,255,.1); 55 | 56 | --breadcrumb-bg-color: #2f3136; 57 | 58 | /* Code Higlighting */ 59 | --code-bg-color: #18191c; 60 | --code-color: #8790A3; 61 | --code-keyword-color: #569cd6; 62 | --code-comment-color: #57a64a; 63 | --code-macro-color: #beb7ff; 64 | --code-string-color: #d69d85; 65 | --code-string-escape-color: #ffd68f; 66 | --code-field-color: #c8c8c8; 67 | --code-function-color: #dcdcaa; 68 | --code-control-color: #d8a0df; 69 | --code-class-color: #4ec9b0; 70 | --code-number-color: #b5cea8; 71 | --code-params-color: #9a9a9a; 72 | --code-breakpoint-color: #8c2f2f; 73 | } 74 | -------------------------------------------------------------------------------- /docs/templates/discordfx/styles/down-arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/templates/discordfx/styles/main.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ConcreteMC/MolangSharp/f181d498ec7a1e2325fb55656dcb4e59a86d1fba/docs/templates/discordfx/styles/main.css -------------------------------------------------------------------------------- /docs/templates/discordfx/styles/main.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information. 2 | 3 | function toggleMenu() { 4 | 5 | var x = document.getElementById("sidebar"); 6 | var b = document.getElementById("blackout"); 7 | 8 | if (x.style.left === "0px") 9 | { 10 | x.style.left = "-350px"; 11 | b.classList.remove("showThat"); 12 | b.classList.add("hideThat"); 13 | } 14 | else 15 | { 16 | x.style.left = "0px"; 17 | b.classList.remove("hideThat"); 18 | b.classList.add("showThat"); 19 | } 20 | } -------------------------------------------------------------------------------- /docs/templates/discordfx/styles/url.min.js: -------------------------------------------------------------------------------- 1 | /*! url - v1.8.6 - 2013-11-22 */window.url=function(){function a(a){return!isNaN(parseFloat(a))&&isFinite(a)}return function(b,c){var d=c||window.location.toString();if(!b)return d;b=b.toString(),"//"===d.substring(0,2)?d="http:"+d:1===d.split("://").length&&(d="http://"+d),c=d.split("/");var e={auth:""},f=c[2].split("@");1===f.length?f=f[0].split(":"):(e.auth=f[0],f=f[1].split(":")),e.protocol=c[0],e.hostname=f[0],e.port=f[1]||("https"===e.protocol.split(":")[0].toLowerCase()?"443":"80"),e.pathname=(c.length>3?"/":"")+c.slice(3,c.length).join("/").split("?")[0].split("#")[0];var g=e.pathname;"/"===g.charAt(g.length-1)&&(g=g.substring(0,g.length-1));var h=e.hostname,i=h.split("."),j=g.split("/");if("hostname"===b)return h;if("domain"===b)return/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/.test(h)?h:i.slice(-2).join(".");if("sub"===b)return i.slice(0,i.length-2).join(".");if("port"===b)return e.port;if("protocol"===b)return e.protocol.split(":")[0];if("auth"===b)return e.auth;if("user"===b)return e.auth.split(":")[0];if("pass"===b)return e.auth.split(":")[1]||"";if("path"===b)return e.pathname;if("."===b.charAt(0)){if(b=b.substring(1),a(b))return b=parseInt(b,10),i[0>b?i.length+b:b-1]||""}else{if(a(b))return b=parseInt(b,10),j[0>b?j.length+b:b]||"";if("file"===b)return j.slice(-1)[0];if("filename"===b)return j.slice(-1)[0].split(".")[0];if("fileext"===b)return j.slice(-1)[0].split(".")[1]||"";if("?"===b.charAt(0)||"#"===b.charAt(0)){var k=d,l=null;if("?"===b.charAt(0)?k=(k.split("?")[1]||"").split("#")[0]:"#"===b.charAt(0)&&(k=k.split("#")[1]||""),!b.charAt(1))return k;b=b.substring(1),k=k.split("&");for(var m=0,n=k.length;n>m;m++)if(l=k[m].split("="),l[0]===b)return l[1]||"";return null}}return""}}(),"undefined"!=typeof jQuery&&jQuery.extend({url:function(a,b){return window.url(a,b)}}); -------------------------------------------------------------------------------- /docs/templates/material/partials/head.tmpl.partial: -------------------------------------------------------------------------------- 1 | {{!Copyright (c) Oscar Vasquez. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} 2 | 3 | 4 | 5 | 6 | {{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}} 7 | 8 | 9 | 10 | {{#_description}}{{/_description}} 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | {{#_noindex}}{{/_noindex}} 19 | {{#_enableSearch}}{{/_enableSearch}} 20 | {{#_enableNewTab}}{{/_enableNewTab}} 21 | -------------------------------------------------------------------------------- /docs/templates/material/styles/main.css: -------------------------------------------------------------------------------- 1 | /* COLOR VARIABLES*/ 2 | :root { 3 | --header-bg-color: #0d47a1; 4 | --header-ft-color: #fff; 5 | --highlight-light: #5e92f3; 6 | --highlight-dark: #003c8f; 7 | --font-color: #34393e; 8 | --custom-box-shadow: 0 1px 2px 0 rgba(61, 65, 68, 0.06), 0 1px 3px 1px rgba(61, 65, 68, 0.16); 9 | } 10 | 11 | body { 12 | color: var(--font-color); 13 | font-family: "Roboto", sans-serif; 14 | line-height: 1.5; 15 | font-size: 16px; 16 | -ms-text-size-adjust: 100%; 17 | -webkit-text-size-adjust: 100%; 18 | word-wrap: break-word; 19 | } 20 | 21 | /* HIGHLIGHT COLOR */ 22 | 23 | button, 24 | a { 25 | color: var(--highlight-dark); 26 | cursor: pointer; 27 | } 28 | 29 | button:hover, 30 | button:focus, 31 | a:hover, 32 | a:focus { 33 | color: var(--highlight-light); 34 | text-decoration: none; 35 | } 36 | 37 | .toc .nav > li.active > a { 38 | color: var(--highlight-dark); 39 | } 40 | 41 | .toc .nav > li.active > a:hover, 42 | .toc .nav > li.active > a:focus { 43 | color: var(--highlight-light); 44 | } 45 | 46 | .pagination > .active > a { 47 | background-color: var(--header-bg-color); 48 | border-color: var(--header-bg-color); 49 | } 50 | 51 | .pagination > .active > a, 52 | .pagination > .active > a:focus, 53 | .pagination > .active > a:hover, 54 | .pagination > .active > span, 55 | .pagination > .active > span:focus, 56 | .pagination > .active > span:hover { 57 | background-color: var(--highlight-light); 58 | border-color: var(--highlight-light); 59 | } 60 | 61 | /* HEADINGS */ 62 | 63 | h1 { 64 | font-weight: 600; 65 | font-size: 32px; 66 | } 67 | 68 | h2 { 69 | font-weight: 600; 70 | font-size: 24px; 71 | line-height: 1.8; 72 | } 73 | 74 | h3 { 75 | font-weight: 600; 76 | font-size: 20px; 77 | line-height: 1.8; 78 | } 79 | 80 | h5 { 81 | font-size: 14px; 82 | padding: 10px 0px; 83 | } 84 | 85 | article h1, 86 | article h2, 87 | article h3, 88 | article h4 { 89 | margin-top: 35px; 90 | margin-bottom: 15px; 91 | } 92 | 93 | article h4 { 94 | padding-bottom: 8px; 95 | border-bottom: 2px solid #ddd; 96 | } 97 | 98 | /* NAVBAR */ 99 | 100 | .navbar-brand > img { 101 | color: var(--header-ft-color); 102 | } 103 | 104 | .navbar { 105 | border: none; 106 | /* Both navbars use box-shadow */ 107 | -webkit-box-shadow: var(--custom-box-shadow); 108 | -moz-box-shadow: var(--custom-box-shadow); 109 | box-shadow: var(--custom-box-shadow); 110 | } 111 | 112 | .subnav { 113 | border-top: 1px solid #ddd; 114 | background-color: #fff; 115 | } 116 | 117 | .navbar-inverse { 118 | background-color: var(--header-bg-color); 119 | z-index: 100; 120 | } 121 | 122 | .navbar-inverse .navbar-nav > li > a, 123 | .navbar-inverse .navbar-text { 124 | color: var(--header-ft-color); 125 | background-color: var(--header-bg-color); 126 | border-bottom: 3px solid transparent; 127 | padding-bottom: 12px; 128 | } 129 | 130 | .navbar-inverse .navbar-nav > li > a:focus, 131 | .navbar-inverse .navbar-nav > li > a:hover { 132 | color: var(--header-ft-color); 133 | background-color: var(--header-bg-color); 134 | border-bottom: 3px solid white; 135 | } 136 | 137 | .navbar-inverse .navbar-nav > .active > a, 138 | .navbar-inverse .navbar-nav > .active > a:focus, 139 | .navbar-inverse .navbar-nav > .active > a:hover { 140 | color: var(--header-ft-color); 141 | background-color: var(--header-bg-color); 142 | border-bottom: 3px solid white; 143 | } 144 | 145 | .navbar-form .form-control { 146 | border: none; 147 | border-radius: 20px; 148 | } 149 | 150 | /* SIDEBAR */ 151 | 152 | .toc .level1 > li { 153 | font-weight: 400; 154 | } 155 | 156 | .toc .nav > li > a { 157 | color: var(--font-color); 158 | } 159 | 160 | .sidefilter { 161 | background-color: #fff; 162 | border-left: none; 163 | border-right: none; 164 | } 165 | 166 | .sidefilter { 167 | background-color: #fff; 168 | border-left: none; 169 | border-right: none; 170 | } 171 | 172 | .toc-filter { 173 | padding: 10px; 174 | margin: 0; 175 | } 176 | 177 | .toc-filter > input { 178 | border: 2px solid #ddd; 179 | border-radius: 20px; 180 | } 181 | 182 | .toc-filter > .filter-icon { 183 | display: none; 184 | } 185 | 186 | .sidetoc > .toc { 187 | background-color: #fff; 188 | overflow-x: hidden; 189 | } 190 | 191 | .sidetoc { 192 | background-color: #fff; 193 | border: none; 194 | } 195 | 196 | /* ALERTS */ 197 | 198 | .alert { 199 | padding: 0px 0px 5px 0px; 200 | color: inherit; 201 | background-color: inherit; 202 | border: none; 203 | box-shadow: var(--custom-box-shadow); 204 | } 205 | 206 | .alert > p { 207 | margin-bottom: 0; 208 | padding: 5px 10px; 209 | } 210 | 211 | .alert > ul { 212 | margin-bottom: 0; 213 | padding: 5px 40px; 214 | } 215 | 216 | .alert > h5 { 217 | padding: 10px 15px; 218 | margin-top: 0; 219 | text-transform: uppercase; 220 | font-weight: bold; 221 | border-radius: 4px 4px 0 0; 222 | } 223 | 224 | .alert-info > h5 { 225 | color: #1976d2; 226 | border-bottom: 4px solid #1976d2; 227 | background-color: #e3f2fd; 228 | } 229 | 230 | .alert-warning > h5 { 231 | color: #f57f17; 232 | border-bottom: 4px solid #f57f17; 233 | background-color: #fff3e0; 234 | } 235 | 236 | .alert-danger > h5 { 237 | color: #d32f2f; 238 | border-bottom: 4px solid #d32f2f; 239 | background-color: #ffebee; 240 | } 241 | 242 | /* CODE HIGHLIGHT */ 243 | pre { 244 | padding: 9.5px; 245 | margin: 0 0 10px; 246 | font-size: 13px; 247 | word-break: break-all; 248 | word-wrap: break-word; 249 | background-color: #fffaef; 250 | border-radius: 4px; 251 | border: none; 252 | box-shadow: var(--custom-box-shadow); 253 | } 254 | -------------------------------------------------------------------------------- /docs/toc.yml: -------------------------------------------------------------------------------- 1 | - name: Articles 2 | href: articles/ 3 | homepage: articles/index.md 4 | 5 | - name: Api Documentation 6 | href: api/ 7 | homepage: api/index.md 8 | -------------------------------------------------------------------------------- /src/Alex.MoLang.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MolangSharp", "MolangSharp\MolangSharp.csproj", "{8A34F8E0-CAB4-4D73-A7B4-8977EF3A7DA3}" 4 | EndProject 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MolangSharp.Tests", "MolangSharp.Tests\MolangSharp.Tests.csproj", "{3FF89B16-9D85-4E87-83C7-D937F2ED7F5B}" 6 | EndProject 7 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{D73569B7-CCA2-458B-98CE-CC7531D414FA}" 8 | EndProject 9 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MolangSharp.Examples.Basic", "Examples\MolangSharp.Examples.Basic\MolangSharp.Examples.Basic.csproj", "{30FD4BFD-0792-495D-A6F6-600713011C77}" 10 | EndProject 11 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensibility", "Extensibility", "{EC0FCA2E-4326-4F6A-9210-684F90267012}" 12 | EndProject 13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MolangSharp.Json.Newtonsoft", "Extensibility\MolangSharp.Json.Newtonsoft\MolangSharp.Json.Newtonsoft.csproj", "{D5E15128-EB40-4263-B9C9-9544F3ACA4CB}" 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|Any CPU = Debug|Any CPU 18 | Release|Any CPU = Release|Any CPU 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {8A34F8E0-CAB4-4D73-A7B4-8977EF3A7DA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {8A34F8E0-CAB4-4D73-A7B4-8977EF3A7DA3}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {8A34F8E0-CAB4-4D73-A7B4-8977EF3A7DA3}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {8A34F8E0-CAB4-4D73-A7B4-8977EF3A7DA3}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {3FF89B16-9D85-4E87-83C7-D937F2ED7F5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {3FF89B16-9D85-4E87-83C7-D937F2ED7F5B}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {3FF89B16-9D85-4E87-83C7-D937F2ED7F5B}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {3FF89B16-9D85-4E87-83C7-D937F2ED7F5B}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {30FD4BFD-0792-495D-A6F6-600713011C77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {30FD4BFD-0792-495D-A6F6-600713011C77}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {30FD4BFD-0792-495D-A6F6-600713011C77}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {30FD4BFD-0792-495D-A6F6-600713011C77}.Release|Any CPU.Build.0 = Release|Any CPU 33 | {D5E15128-EB40-4263-B9C9-9544F3ACA4CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {D5E15128-EB40-4263-B9C9-9544F3ACA4CB}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {D5E15128-EB40-4263-B9C9-9544F3ACA4CB}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {D5E15128-EB40-4263-B9C9-9544F3ACA4CB}.Release|Any CPU.Build.0 = Release|Any CPU 37 | EndGlobalSection 38 | GlobalSection(NestedProjects) = preSolution 39 | {30FD4BFD-0792-495D-A6F6-600713011C77} = {D73569B7-CCA2-458B-98CE-CC7531D414FA} 40 | {D5E15128-EB40-4263-B9C9-9544F3ACA4CB} = {EC0FCA2E-4326-4F6A-9210-684F90267012} 41 | EndGlobalSection 42 | EndGlobal 43 | -------------------------------------------------------------------------------- /src/Examples/MolangSharp.Examples.Basic/BaseEntity.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using ConcreteMC.MolangSharp.Attributes; 3 | using ConcreteMC.MolangSharp.Runtime; 4 | using ConcreteMC.MolangSharp.Runtime.Struct; 5 | 6 | namespace MolangSharp.Examples.Basic; 7 | 8 | public class BaseEntity : MoLangEnvironment 9 | { 10 | private Stopwatch _stopwatch = Stopwatch.StartNew(); 11 | public BaseEntity() 12 | { 13 | Structs["query"] = new InteropStruct(this); 14 | } 15 | 16 | [MoProperty("life_time")] 17 | public double LifeTime => _stopwatch.Elapsed.TotalSeconds; 18 | } -------------------------------------------------------------------------------- /src/Examples/MolangSharp.Examples.Basic/MolangSharp.Examples.Basic.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Examples/MolangSharp.Examples.Basic/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Globalization; 3 | using ConcreteMC.MolangSharp.Parser; 4 | using ConcreteMC.MolangSharp.Runtime; 5 | using MolangSharp.Examples.Basic; 6 | 7 | Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; 8 | Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; 9 | 10 | //Create a new Entity which will use Interop to allow for MoLang->C# communication 11 | var myEntity = new BaseEntity(); 12 | 13 | //Initialize the runtime 14 | MoLangRuntime runtime = new MoLangRuntime(myEntity); 15 | 16 | //Parse our MoLang expression 17 | var parsed = MoLangParser.Parse("math.cos(query.life_time * 60) * -45.0"); 18 | 19 | Stopwatch sw = Stopwatch.StartNew(); 20 | while (true) 21 | { 22 | var value = runtime.Execute(parsed); 23 | Console.WriteLine($"[{sw.Elapsed.ToString("mm\\:ss\\.fff")}] Rotation: {value.AsDouble():F3}"); 24 | Thread.Sleep(100); 25 | } -------------------------------------------------------------------------------- /src/Extensibility/MolangSharp.Json.Newtonsoft/MolangExpressionConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ConcreteMC.MolangSharp.Parser; 3 | using ConcreteMC.MolangSharp.Parser.Expressions; 4 | using Newtonsoft.Json; 5 | using Newtonsoft.Json.Linq; 6 | 7 | namespace MolangSharp.Json.Newtonsoft 8 | { 9 | /// 10 | /// Implements a JsonConverter for reading 11 | /// 12 | public class MolangExpressionConverter : JsonConverter 13 | { 14 | public MolangExpressionConverter() 15 | { 16 | 17 | } 18 | 19 | public override bool CanWrite => false; 20 | 21 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 22 | { 23 | throw new NotSupportedException(); 24 | } 25 | 26 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, 27 | JsonSerializer serializer) 28 | { 29 | JToken token = JToken.Load(reader); 30 | if (token.Type == JTokenType.String) 31 | { 32 | string molang = token.Value(); 33 | return MoLangParser.Parse(molang); 34 | } 35 | else if (token.Type == JTokenType.Integer) 36 | { 37 | return new NumberExpression(token.Value()); 38 | } 39 | else if (token.Type == JTokenType.Float) 40 | { 41 | return new NumberExpression(token.Value()); 42 | } 43 | else if (token.Type == JTokenType.Boolean) 44 | { 45 | return new BooleanExpression(token.Value()); 46 | } 47 | 48 | return existingValue; 49 | } 50 | 51 | public override bool CanConvert(Type objectType) 52 | { 53 | return typeof(IExpression).IsAssignableFrom(objectType); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/Extensibility/MolangSharp.Json.Newtonsoft/MolangSharp.Json.Newtonsoft.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1; netcoreapp6.0 5 | disable 6 | disable 7 | true 8 | ConcreteMC.MolangSharp.Json.Newtonsoft 9 | ConcreteMC.MolangSharp.Json.Newtonsoft 10 | ConcreteMC.MolangSharp.Json.Newtonsoft 11 | Implements a JsonConverter for Molang expressions 12 | Kenny van Vulpen 13 | Kenny van Vulpen 14 | Copyright Kenny van Vulpen 2019-2022 15 | Minecraft Molang MCPE MoLang bedrock mojang alex json newtonsoft newtonsoft.json 16 | 17 | git 18 | https://github.com/ConcreteMC/MolangSharp.git 19 | https://github.com/ConcreteMC/MolangSharp 20 | 21 | 1.0.0 22 | 23 | - Initial release 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/MolangSharp.Tests/BinaryOpTest.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Parser; 2 | using ConcreteMC.MolangSharp.Runtime; 3 | using ConcreteMC.MolangSharp.Runtime.Value; 4 | using ConcreteMC.MolangSharp.Utils; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | 7 | namespace ConcreteMC.MolangSharp.Tests; 8 | 9 | [TestClass] 10 | public class BinaryOpTest 11 | { 12 | private MoLangRuntime _runtime; 13 | 14 | [TestInitialize] 15 | public void Setup() 16 | { 17 | _runtime = new MoLangRuntime(); 18 | } 19 | 20 | [TestMethod] 21 | public void ZeroReturnsFalse() 22 | { 23 | var parsed = MoLangParser.Parse("return 0 ?? 'test'"); 24 | var result = _runtime.Execute(parsed); 25 | 26 | Assert.AreEqual(result.AsString(), "test"); 27 | } 28 | 29 | [TestMethod] 30 | public void ZeroVariableReturnsFalse() 31 | { 32 | _runtime.Environment.SetValue(new MoPath("variable.lastNumber"), new DoubleValue(0)); 33 | var parsed = MoLangParser.Parse("return variable.lastNumber ?? 'test'"); 34 | var result = _runtime.Execute(parsed); 35 | 36 | Assert.AreEqual(result.AsString(), "test"); 37 | } 38 | } -------------------------------------------------------------------------------- /src/MolangSharp.Tests/EqualityTest.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Parser; 2 | using ConcreteMC.MolangSharp.Runtime; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace ConcreteMC.MolangSharp.Tests; 6 | 7 | [TestClass] 8 | public class EqualityTest 9 | { 10 | private MoLangRuntime _runtime; 11 | 12 | [TestInitialize] 13 | public void Setup() 14 | { 15 | _runtime = new MoLangRuntime(); 16 | } 17 | 18 | [TestMethod("Break Execution (Bigger or Equal)")] 19 | public void BiggerOrEqualTest() 20 | { 21 | var parsed = MoLangParser.Parse(@" 22 | t.a = 1; 23 | loop(10, { 24 | t.a = t.a + 1; 25 | t.a >= 5 ? break : continue; 26 | }); 27 | return t.a; 28 | "); 29 | 30 | Assert.IsNotNull(parsed); 31 | 32 | var result = _runtime.Execute(parsed); 33 | Assert.AreEqual(5, result.AsDouble()); 34 | } 35 | 36 | [TestMethod("Break Execution (Smaller or Equal)")] 37 | public void SmallerOrEqualTest() 38 | { 39 | var parsed = MoLangParser.Parse(@" 40 | t.a = 10; 41 | loop(10, { 42 | t.a = t.a - 1; 43 | t.a <= 5 ? break : continue; 44 | }); 45 | return t.a; 46 | "); 47 | 48 | Assert.IsNotNull(parsed); 49 | 50 | var result = _runtime.Execute(parsed); 51 | Assert.AreEqual(5, result.AsDouble()); 52 | } 53 | 54 | [TestMethod("Break Execution (Equal)")] 55 | public void EqualityOperatorTest() 56 | { 57 | var parsed = MoLangParser.Parse(@" 58 | t.a = 10; 59 | loop(10, { 60 | t.a = t.a - 1; 61 | t.a == 5 ? break : continue; 62 | }); 63 | return t.a; 64 | "); 65 | 66 | Assert.IsNotNull(parsed); 67 | 68 | var result = _runtime.Execute(parsed); 69 | Assert.AreEqual(5, result.AsDouble()); 70 | } 71 | } -------------------------------------------------------------------------------- /src/MolangSharp.Tests/InteropOptimizationVisitorTest.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Attributes; 2 | using ConcreteMC.MolangSharp.Parser; 3 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 4 | using ConcreteMC.MolangSharp.Parser.Visitors.InteropOptimization; 5 | using ConcreteMC.MolangSharp.Runtime; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | 8 | namespace ConcreteMC.MolangSharp.Tests; 9 | 10 | [TestClass] 11 | public class InteropOptimizationVisitorTest 12 | { 13 | [TestMethod] 14 | public void VerifyFunctionCallOptimization() 15 | { 16 | var entity = new TestEntity(); 17 | var parser = new MoLangParser(new TokenIterator("query.getHealth()")); 18 | parser.ExpressionTraverser.Visitors.Add(new InteropOptimizationVisitor(new InteropOptimizationVisitorOptions() 19 | { 20 | InteropEntries = new [] 21 | { 22 | new InteropEntry("query", entity) 23 | } 24 | })); 25 | 26 | var expression = parser.Parse(); 27 | Assert.IsInstanceOfType(expression, typeof(OptimizedFunctionCallExpression)); 28 | 29 | var runtime = new MoLangRuntime(); 30 | var result = runtime.Execute(expression); 31 | Assert.IsNotNull(result); 32 | Assert.AreEqual(20, result.AsDouble()); 33 | } 34 | 35 | [TestMethod] 36 | public void VerifyPropertyAccessOptimization() 37 | { 38 | var entity = new TestEntity(); 39 | var parser = new MoLangParser(new TokenIterator("query.health")); 40 | parser.ExpressionTraverser.Visitors.Add(new InteropOptimizationVisitor(new InteropOptimizationVisitorOptions() 41 | { 42 | InteropEntries = new [] 43 | { 44 | new InteropEntry("query", entity) 45 | } 46 | })); 47 | 48 | var expression = parser.Parse(); 49 | Assert.IsInstanceOfType(expression, typeof(OptimizedNameExpression)); 50 | 51 | var runtime = new MoLangRuntime(); 52 | var result = runtime.Execute(expression); 53 | Assert.IsNotNull(result); 54 | Assert.AreEqual(20, result.AsDouble()); 55 | } 56 | 57 | [TestMethod] 58 | public void VerifyPropertyAssignOptimization() 59 | { 60 | var entity = new TestEntity(); 61 | var parser = new MoLangParser(new TokenIterator("query.health = 21")); 62 | parser.ExpressionTraverser.Visitors.Add(new InteropOptimizationVisitor(new InteropOptimizationVisitorOptions() 63 | { 64 | InteropEntries = new [] 65 | { 66 | new InteropEntry("query", entity) 67 | } 68 | })); 69 | 70 | var expression = parser.Parse(); 71 | Assert.IsInstanceOfType(expression, typeof(OptimizedAssignExpression)); 72 | 73 | var runtime = new MoLangRuntime(); 74 | var result = runtime.Execute(expression); 75 | Assert.IsNotNull(result); 76 | Assert.AreEqual(21, entity.Health); 77 | } 78 | 79 | private class TestEntity 80 | { 81 | [MoProperty("health")] 82 | public double Health { get; set; } 83 | 84 | public TestEntity() 85 | { 86 | Health = 20; 87 | } 88 | 89 | [MoFunction("getHealth")] 90 | public double GetHealth() 91 | { 92 | return Health; 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /src/MolangSharp.Tests/InteropTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ConcreteMC.MolangSharp.Attributes; 3 | using ConcreteMC.MolangSharp.Parser; 4 | using ConcreteMC.MolangSharp.Runtime; 5 | using ConcreteMC.MolangSharp.Runtime.Struct; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | 8 | namespace ConcreteMC.MolangSharp.Tests; 9 | 10 | [TestClass] 11 | public class InteropTest 12 | { 13 | [TestInitialize] 14 | public void Init() 15 | { 16 | //Init static 17 | var a = MoLangMath.Library; 18 | } 19 | 20 | [TestMethod] 21 | public void PropertyRead() 22 | { 23 | var expression = MoLangParser.Parse("query.life_time"); 24 | 25 | var expected = Environment.TickCount * 3.5d; 26 | MoLangRuntime runtime = new MoLangRuntime(); 27 | 28 | runtime.Environment.Structs.TryAdd( 29 | "query", new InteropStruct(new TestClass(expected))); 30 | 31 | var result = runtime.Execute(expression); 32 | Assert.AreEqual(expected, result.AsDouble()); 33 | } 34 | 35 | [TestMethod] 36 | public void PropertyWrite() 37 | { 38 | var expression = MoLangParser.Parse("query.life_time = 5"); 39 | 40 | MoLangRuntime runtime = new MoLangRuntime(); 41 | 42 | var testStruct = new TestClass(Environment.TickCount); 43 | runtime.Environment.Structs.TryAdd( 44 | "query", new InteropStruct(testStruct)); 45 | 46 | runtime.Execute(expression); 47 | Assert.AreEqual(5d, testStruct.Lifetime); 48 | } 49 | 50 | public class TestClass 51 | { 52 | private double _value; 53 | 54 | public TestClass(double value) 55 | { 56 | _value = value; 57 | } 58 | 59 | [MoProperty("life_time")] 60 | public double Lifetime 61 | { 62 | get 63 | { 64 | return _value; 65 | } 66 | set 67 | { 68 | _value = value; 69 | } 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /src/MolangSharp.Tests/MathOptimizationVisitorTest.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Parser; 2 | using ConcreteMC.MolangSharp.Parser.Expressions; 3 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 4 | using ConcreteMC.MolangSharp.Parser.Visitors; 5 | using ConcreteMC.MolangSharp.Runtime; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | 8 | namespace ConcreteMC.MolangSharp.Tests; 9 | 10 | [TestClass] 11 | public class MathOptimizationVisitorTest 12 | { 13 | [TestMethod] 14 | public void Constants() 15 | { 16 | MoLangParser parser = new MoLangParser(new TokenIterator("7 + 2 * (6 + 3) / 3 - 7")); 17 | parser.ExpressionTraverser.Visitors.Add(new MathOptimizationVisitor()); 18 | 19 | var expr = parser.Parse(); 20 | Assert.IsInstanceOfType(expr, typeof(NumberExpression)); 21 | 22 | MoLangRuntime runtime = new MoLangRuntime(); 23 | var result = runtime.Execute(expr); 24 | 25 | Assert.AreEqual(6, result.AsDouble()); 26 | } 27 | 28 | [TestMethod] 29 | public void MathFunctions() 30 | { 31 | MoLangParser parser = new MoLangParser(new TokenIterator("math.floor(10.1 + 20.1)")); 32 | parser.ExpressionTraverser.Visitors.Add(new MathOptimizationVisitor()); 33 | 34 | var expr = parser.Parse(); 35 | Assert.IsInstanceOfType(expr, typeof(NumberExpression)); 36 | 37 | MoLangRuntime runtime = new MoLangRuntime(); 38 | var result = runtime.Execute(expr); 39 | 40 | Assert.AreEqual(30, result.AsDouble()); 41 | } 42 | } -------------------------------------------------------------------------------- /src/MolangSharp.Tests/MathTest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using ConcreteMC.MolangSharp.Parser; 3 | using ConcreteMC.MolangSharp.Runtime; 4 | using ConcreteMC.MolangSharp.Runtime.Struct; 5 | using ConcreteMC.MolangSharp.Runtime.Value; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | 8 | namespace ConcreteMC.MolangSharp.Tests; 9 | 10 | [TestClass] 11 | public class MathTest 12 | { 13 | [TestInitialize] 14 | public void Init() 15 | { 16 | //Init static 17 | var a = MoLangMath.Library; 18 | } 19 | 20 | private MoLangRuntime Setup(double a, double b) 21 | { 22 | MoLangRuntime runtime = new MoLangRuntime(); 23 | 24 | runtime.Environment.Structs["variable"] = new VariableStruct( 25 | new List>() 26 | { 27 | new KeyValuePair("a", new DoubleValue(a)), 28 | new KeyValuePair("b", new DoubleValue(b)) 29 | }); 30 | 31 | return runtime; 32 | } 33 | 34 | [TestMethod] 35 | public void ArithmeticsAdding() 36 | { 37 | const int a = 1200; 38 | const int b = 800; 39 | const double expectedResult = a + b; 40 | 41 | var expression = MoLangParser.Parse("v.a + v.b"); 42 | var runtime = Setup(a, b); 43 | 44 | var result = runtime.Execute(expression); 45 | Assert.AreEqual(expectedResult, result.AsDouble()); 46 | } 47 | 48 | [TestMethod] 49 | public void ArithmeticsSubtracting() 50 | { 51 | const double a = 1200; 52 | const double b = 800; 53 | const double expectedResult = a - b; 54 | 55 | var expression = MoLangParser.Parse("v.a - v.b"); 56 | var runtime = Setup(a, b); 57 | 58 | var result = runtime.Execute(expression); 59 | Assert.AreEqual(expectedResult, result.AsDouble()); 60 | } 61 | 62 | [TestMethod] 63 | public void ArithmeticsMultiplication() 64 | { 65 | const double a = 1200; 66 | const double b = 800; 67 | const double expectedResult = a * b; 68 | 69 | var expression = MoLangParser.Parse("v.a * v.b"); 70 | var runtime = Setup(a, b); 71 | 72 | var result = runtime.Execute(expression); 73 | Assert.AreEqual(expectedResult, result.AsDouble()); 74 | } 75 | 76 | [TestMethod] 77 | public void ArithmeticsDivision() 78 | { 79 | const double a = 1200; 80 | const double b = 800; 81 | const double expectedResult = a / b; 82 | 83 | var expression = MoLangParser.Parse("v.a / v.b"); 84 | var runtime = Setup(a, b); 85 | 86 | var result = runtime.Execute(expression); 87 | Assert.AreEqual( expectedResult, result.AsDouble()); 88 | } 89 | 90 | [TestMethod] 91 | public void OrderOfOperations() 92 | { 93 | var runtime = Setup(10, 20); 94 | 95 | var expr = MoLangParser.Parse("v.b + (7 + 2 * (6 + 3) / 3 - 7) + v.a"); 96 | var result = runtime.Execute(expr); 97 | Assert.AreEqual(36, result.AsDouble()); 98 | 99 | result = runtime.Execute(MoLangParser.Parse("10 * 2 - (7 + 9)")); 100 | Assert.AreEqual(4, result.AsDouble()); 101 | 102 | result = runtime.Execute(MoLangParser.Parse("12 + 2 * 44")); 103 | Assert.AreEqual(100, result.AsDouble()); 104 | } 105 | } -------------------------------------------------------------------------------- /src/MolangSharp.Tests/MolangSharp.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | 7 | false 8 | 9 | ConcreteMC.MolangSharp.Tests 10 | 11 | ConcreteMC.MolangSharp.Tests 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/MolangSharp.Tests/PerformanceTest.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using ConcreteMC.MolangSharp.Parser; 3 | using ConcreteMC.MolangSharp.Runtime; 4 | using ConcreteMC.MolangSharp.Runtime.Value; 5 | using ConcreteMC.MolangSharp.Utils; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | 8 | namespace ConcreteMC.MolangSharp.Tests; 9 | 10 | [TestClass] 11 | public class PerformanceTest 12 | { 13 | private Stopwatch _sw; 14 | private MoLangEnvironment _environment; 15 | private MoLangRuntime _runtime; 16 | 17 | [TestInitialize] 18 | public void Init() 19 | { 20 | _sw = new Stopwatch(); 21 | _environment = new MoLangEnvironment(); 22 | _runtime = new MoLangRuntime(_environment); 23 | 24 | _environment.SetValue(new MoPath("variable.test"), DoubleValue.One); 25 | _environment.SetValue(new MoPath("variable.life_time"), DoubleValue.Zero); 26 | 27 | MoLangRuntimeConfiguration.UseDummyValuesInsteadOfExceptions = false; 28 | MoLangRuntimeConfiguration.UseMoLangStackTrace = true; 29 | } 30 | 31 | [TestCleanup] 32 | public void Cleanup() 33 | { 34 | MoLangRuntimeConfiguration.UseMoLangStackTrace = true; 35 | MoLangRuntimeConfiguration.UseDummyValuesInsteadOfExceptions = false; 36 | } 37 | 38 | [TestMethod] 39 | public void TestVariableReadingMath() 40 | { 41 | const int iterations = 5000000; 42 | long total = 0; 43 | 44 | var expression = MoLangParser.Parse("variable.test * 20"); 45 | for (int i = 0; i < iterations; i++) 46 | { 47 | _sw.Restart(); 48 | var a = _runtime.Execute(expression); 49 | total += _sw.ElapsedMilliseconds; 50 | Assert.AreEqual(20, a.AsDouble()); 51 | } 52 | 53 | Debug.WriteLine($"Average: {((double)total / iterations):N10}ms"); 54 | } 55 | 56 | [TestMethod] 57 | public void TestVariableReading() 58 | { 59 | const int iterations = 5000000; 60 | long total = 0; 61 | 62 | var expression = MoLangParser.Parse("variable.test"); 63 | for (int i = 0; i < iterations; i++) 64 | { 65 | _sw.Restart(); 66 | var a = _runtime.Execute(expression); 67 | total += _sw.ElapsedMilliseconds; 68 | Assert.AreEqual(1, a.AsDouble()); 69 | } 70 | 71 | Debug.WriteLine($"Average: {((double)total / iterations):N10}ms"); 72 | } 73 | 74 | [TestMethod] 75 | public void TestVariableWriting() 76 | { 77 | const int iterations = 5000000; 78 | long total = 0; 79 | 80 | var expression = MoLangParser.Parse("variable.test = 20"); 81 | for (int i = 0; i < iterations; i++) 82 | { 83 | _sw.Restart(); 84 | _runtime.Execute(expression); 85 | total += _sw.ElapsedMilliseconds; 86 | } 87 | 88 | Debug.WriteLine($"Average: {((double)total / iterations):N10}ms"); 89 | } 90 | 91 | [TestMethod] 92 | public void TestInvalidPathPerformance() 93 | { 94 | const int iterations = 50000; 95 | long total = 0; 96 | 97 | var expression = MoLangParser.Parse("fr.test"); 98 | for (int i = 0; i < iterations; i++) 99 | { 100 | _sw.Restart(); 101 | 102 | try 103 | { 104 | _runtime.Execute(expression); 105 | } 106 | catch 107 | { 108 | 109 | } 110 | total += _sw.ElapsedMilliseconds; 111 | } 112 | 113 | Debug.WriteLine($"Average: {((double)total / iterations):N10}ms"); 114 | } 115 | 116 | [TestMethod] 117 | public void TestDummyValuesConfiguration() 118 | { 119 | var expression = MoLangParser.Parse("return fr.test"); 120 | MoLangRuntimeConfiguration.UseDummyValuesInsteadOfExceptions = true; 121 | var result = _runtime.Execute(expression); 122 | Assert.AreEqual(0d, result.AsDouble()); 123 | } 124 | 125 | [TestMethod] 126 | public void RawPerformanceTest() 127 | { 128 | var parsed = MoLangParser.Parse("math.cos(v.life_time * 60) * -45.0"); 129 | 130 | RunPerformanceTestAndOutput(parsed); 131 | } 132 | 133 | [TestMethod] 134 | public void InvalidPathPerformanceTest() 135 | { 136 | var parsed = MoLangParser.Parse("math.cos(a.life_time * 60) * -45.0"); 137 | 138 | RunPerformanceTestAndOutput(parsed); 139 | } 140 | 141 | [TestMethod] 142 | public void InvalidPathPerformanceWithoutExceptionThrowingTest() 143 | { 144 | MoLangRuntimeConfiguration.UseDummyValuesInsteadOfExceptions = true; 145 | var parsed = MoLangParser.Parse("math.cos(a.life_time * 60) * -45.0"); 146 | 147 | RunPerformanceTestAndOutput(parsed); 148 | } 149 | 150 | [TestMethod] 151 | public void InvalidPathPerformanceWithoutStackTraceThrowingTest() 152 | { 153 | MoLangRuntimeConfiguration.UseMoLangStackTrace = false; 154 | var parsed = MoLangParser.Parse("math.cos(a.life_time * 60) * -45.0"); 155 | 156 | RunPerformanceTestAndOutput(parsed); 157 | } 158 | 159 | 160 | private void LogOutput(long iterations, long timespent) 161 | { 162 | Debug.WriteLine($"Iterations: {iterations}. Elapsed: {timespent}ms Avg: {timespent / (double)iterations}ms"); 163 | } 164 | 165 | private void RunPerformanceTestAndOutput(IExpression expression) 166 | { 167 | var result = RunPerformanceTest(expression); 168 | LogOutput(result.iterations, result.timeSpent); 169 | } 170 | 171 | private (long iterations, long timeSpent) RunPerformanceTest(IExpression expression) 172 | { 173 | var iterations = 0L; 174 | var sw = Stopwatch.StartNew(); 175 | while (sw.ElapsedMilliseconds < 1000) 176 | { 177 | try 178 | { 179 | var e = _runtime.Execute(expression); 180 | } 181 | catch 182 | { 183 | 184 | } 185 | 186 | iterations++; 187 | } 188 | sw.Stop(); 189 | 190 | return (iterations, sw.ElapsedMilliseconds); 191 | } 192 | } -------------------------------------------------------------------------------- /src/MolangSharp.Tests/ScriptTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using ConcreteMC.MolangSharp.Attributes; 4 | using ConcreteMC.MolangSharp.Parser; 5 | using ConcreteMC.MolangSharp.Runtime; 6 | using ConcreteMC.MolangSharp.Runtime.Struct; 7 | using Microsoft.VisualStudio.TestTools.UnitTesting; 8 | 9 | namespace ConcreteMC.MolangSharp.Tests; 10 | 11 | [TestClass] 12 | public class ScriptTest 13 | { 14 | private Stopwatch _sw; 15 | private MoLangRuntime _runtime; 16 | [TestInitialize] 17 | public void Setup() 18 | { 19 | _sw = new Stopwatch(); 20 | _runtime = new MoLangRuntime(); 21 | _runtime.Environment.Structs.TryAdd("query", new InteropStruct(new TestClass(_sw)) { }); 22 | } 23 | 24 | [TestMethod("Script Execution")] 25 | public void ScriptRun() 26 | { 27 | _sw.Start(); 28 | var parsed = MoLangParser.Parse(@" 29 | t.a = 213 + 2 / 0.5 + 5 + 2 * 3; 30 | 31 | query.debug_output(1 + 2 * 3); 32 | 33 | array.test.0 = 100; 34 | array.test[1] = 200; 35 | array.test[2] = 10.5; 36 | 37 | query.debug_output(array.test[1]); 38 | 39 | t.b = 3; 40 | 41 | loop(10, { 42 | array.test[t.b] = array.test[t.b - 1] + 2; 43 | t.b = t.b + 1; 44 | }); 45 | 46 | for_each(v.r, array.test, { 47 | t.a = t.a + v.r; 48 | query.debug_output('hello1', t.a, v.r); 49 | }); 50 | 51 | t.b = 0; 52 | 53 | loop(100, { 54 | t.b = t.b + 1; 55 | t.a = t.a + math.cos((Math.PI / 180.0f) * 270) + t.b; 56 | query.debug_output(array.test[t.b]); 57 | query.debug_output('hello', 'test', t.a, array.test[2], t.b); 58 | }); 59 | 60 | query.debug_output(query.life_time()); 61 | return t.a; 62 | "); 63 | _sw.Stop(); 64 | var timeElapsedOnParsing = _sw.Elapsed; 65 | 66 | Assert.IsNotNull(parsed); 67 | 68 | Console.WriteLine($"Parser completed in {timeElapsedOnParsing.TotalMilliseconds}ms"); 69 | 70 | const int runs = 1000; 71 | const int warmupRuns = 10; 72 | 73 | TimeSpan totalElapsed = TimeSpan.Zero; 74 | TimeSpan max = TimeSpan.Zero; 75 | TimeSpan min = TimeSpan.MaxValue; 76 | 77 | for (int i = 0; i < runs + warmupRuns; i++) 78 | { 79 | _sw.Restart(); 80 | _runtime.Execute(parsed); 81 | _sw.Stop(); 82 | 83 | if (i >= warmupRuns) 84 | { 85 | totalElapsed += _sw.Elapsed; 86 | 87 | if (_sw.Elapsed < min) 88 | min = _sw.Elapsed; 89 | 90 | if (_sw.Elapsed > max) 91 | max = _sw.Elapsed; 92 | } 93 | } 94 | 95 | Console.WriteLine($"Executed {runs} runs. Total={totalElapsed.TotalMilliseconds}ms Avg={totalElapsed.TotalMilliseconds / runs}ms Max={max.TotalMilliseconds}ms Min={min.TotalMilliseconds}ms"); 96 | } 97 | 98 | public class TestClass 99 | { 100 | private Stopwatch _sw; 101 | public TestClass(Stopwatch sw) 102 | { 103 | _sw = sw; 104 | } 105 | 106 | [MoProperty("life_time")] 107 | public double Lifetime 108 | { 109 | get 110 | { 111 | return _sw.Elapsed.TotalSeconds; 112 | } 113 | } 114 | 115 | [MoFunction("debug_output")] 116 | public void DebugOutput(MoParams mo) 117 | { 118 | // var str = string.Join(" ", mo.GetParams().Select(x => x?.AsString())); 119 | // Console.WriteLine(str); 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /src/MolangSharp/Attributes/MoFunctionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ConcreteMC.MolangSharp.Attributes 4 | { 5 | /// 6 | /// Identifies a method as a MoLang Accessible function 7 | /// 8 | [AttributeUsage(AttributeTargets.Method)] 9 | public class MoFunctionAttribute : Attribute 10 | { 11 | /// 12 | /// The names this method is exposed via 13 | /// 14 | public string[] Name { get; } 15 | 16 | /// 17 | /// Identifies a method as a MoLang Accessible function 18 | /// 19 | /// The names this function is callable by in a MoLang expression 20 | public MoFunctionAttribute(params string[] functionNames) 21 | { 22 | Name = functionNames; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/MolangSharp/Attributes/MoObservableAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ConcreteMC.MolangSharp.Attributes 4 | { 5 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] 6 | public class MoObservableAttribute : Attribute { } 7 | } -------------------------------------------------------------------------------- /src/MolangSharp/Attributes/MoPropertyAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ConcreteMC.MolangSharp.Attributes 4 | { 5 | /// 6 | /// Identifies a class property/field as MoLang Accessible 7 | /// 8 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] 9 | public class MoPropertyAttribute : Attribute 10 | { 11 | /// 12 | /// The name of this property as used in a MoLang expression 13 | /// 14 | public string Name { get; } 15 | 16 | /// 17 | /// Identifies a class property/field as MoLang Accessible 18 | /// 19 | /// The name used for this property. 20 | public MoPropertyAttribute(string name) 21 | { 22 | Name = name; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/MolangSharp/MolangSharp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug;Release 5 | AnyCPU;x64 6 | true 7 | 1.0.3 8 | true 9 | netstandard2.1; netcoreapp6.0 10 | https://github.com/ConcreteMC/MolangSharp 11 | https://github.com/ConcreteMC/MolangSharp.git 12 | MPL-2.0 13 | A library implementing the MoLang expression language 14 | Kenny van Vulpen 15 | Kenny van Vulpen 16 | Copyright Kenny van Vulpen 2019-2022 17 | Minecraft Molang MCPE MoLang bedrock mojang alex 18 | 19 | - Fixed an issue with struct accessor 20 | 21 | 22 | ConcreteMC.MolangSharp 23 | ConcreteMC.MolangSharp 24 | 1.0.3 25 | ConcreteMC.MolangSharp 26 | git 27 | ConcreteMC.MolangSharp 28 | Alex.MoLang 29 | 30 | 31 | 32 | bin\x64\Debug\netstandard2.1\Alex.MoLang.xml 33 | 34 | 35 | 36 | bin\x64\Release\netstandard2.1\Alex.MoLang.xml 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Exceptions/MoLangParserException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ConcreteMC.MolangSharp.Parser.Exceptions 4 | { 5 | public class MoLangParserException : Exception 6 | { 7 | public MoLangParserException(string message) : base(message) { } 8 | 9 | public MoLangParserException(string message, Exception innerException) : base(message, innerException) { } 10 | } 11 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ConcreteMC.MolangSharp.Runtime; 3 | using ConcreteMC.MolangSharp.Runtime.Value; 4 | 5 | namespace ConcreteMC.MolangSharp.Parser 6 | { 7 | /// 8 | /// An abstract base class for all Expressions 9 | /// 10 | public abstract class Expression : IExpression 11 | { 12 | /// 13 | /// The parameters used by this expression 14 | /// 15 | public IExpression[] Parameters { get; set; } 16 | 17 | /// 18 | public ExpressionMeta Meta { get; } = new ExpressionMeta(); 19 | 20 | /// 21 | /// Create a new instance of the class 22 | /// 23 | /// 24 | /// The parameters used by this expression 25 | /// 26 | protected Expression(params IExpression[] parameters) 27 | { 28 | Parameters = parameters; 29 | } 30 | 31 | /// 32 | public abstract IMoValue Evaluate(MoScope scope, MoLangEnvironment environment); 33 | 34 | /// 35 | public virtual void Assign(MoScope scope, MoLangEnvironment environment, IMoValue value) 36 | { 37 | throw new Exception("Cannot assign a value to " + this.GetType()); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/ExpressionMeta.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser 5 | { 6 | /// 7 | /// Contains metadata about an expression 8 | /// 9 | public class ExpressionMeta 10 | { 11 | /// 12 | /// The token 13 | /// 14 | public Token Token { get; set; } 15 | 16 | /// 17 | /// The parent expression 18 | /// 19 | public IExpression Parent { get; set; } 20 | 21 | /// 22 | /// The previous expression 23 | /// 24 | public IExpression Previous { get; set; } 25 | 26 | /// 27 | /// The next expression 28 | /// 29 | public IExpression Next { get; set; } 30 | 31 | public override string ToString() 32 | { 33 | StringBuilder sb = new StringBuilder(255); 34 | bool includeFileInfoIfAvailable; 35 | 36 | if (Token != null) 37 | { 38 | sb.Append(Token.Text); 39 | includeFileInfoIfAvailable = true; 40 | } 41 | else 42 | { 43 | includeFileInfoIfAvailable = false; 44 | } 45 | 46 | if (includeFileInfoIfAvailable) 47 | { 48 | // sb.Append(" at offset "); 49 | 50 | // if (_nativeOffset == OFFSET_UNKNOWN) 51 | // sb.Append(""); 52 | // else 53 | // sb.Append(_nativeOffset); 54 | 55 | sb.Append(" in file:line:column "); 56 | sb.Append(""); 57 | sb.Append(':'); 58 | sb.Append(Token.Position.LineNumber); 59 | sb.Append(':'); 60 | sb.Append(Token.Position.Index); 61 | } 62 | else 63 | { 64 | sb.Append(""); 65 | } 66 | 67 | sb.AppendLine(); 68 | 69 | return sb.ToString(); 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/ExpressionTraverser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | using ConcreteMC.MolangSharp.Runtime.Exceptions; 7 | 8 | namespace ConcreteMC.MolangSharp.Parser 9 | { 10 | /// 11 | /// Traverses arrays of with 's 12 | /// 13 | public class ExpressionTraverser 14 | { 15 | /// 16 | /// The list of that will visit the traversed expressions 17 | /// 18 | public readonly List Visitors = new List(); 19 | 20 | private bool _stop = false; 21 | 22 | /// 23 | /// Traverse an array of expressions. 24 | /// 25 | /// The array of expressions to visit 26 | /// The traversed array of expressions 27 | public IExpression[] Traverse(IExpression[] expressions) 28 | { 29 | TraverseArray(expressions); 30 | 31 | return expressions.Where(x => x != null).ToArray(); 32 | } 33 | 34 | private void TraverseArray(IExpression[] expressions) 35 | { 36 | foreach (IExpressionVisitor visitor in Visitors) 37 | { 38 | visitor.BeforeTraverse(expressions); 39 | } 40 | 41 | for (var index = 0; index < expressions.Length; index++) 42 | { 43 | IExpression expression = expressions[index]; 44 | 45 | if (expression == null) 46 | throw new MoLangRuntimeException("Expression was null", null); 47 | 48 | expressions[index] = TraverseExpr(expression, null); 49 | 50 | if (_stop) 51 | { 52 | break; 53 | } 54 | } 55 | 56 | foreach (IExpressionVisitor visitor in Visitors) 57 | { 58 | visitor.AfterTraverse(expressions); 59 | } 60 | } 61 | 62 | private IExpression TraverseExpr(IExpression expression, IExpression parent) 63 | { 64 | expression = Visit(expression); 65 | expression.Meta.Parent = parent; 66 | 67 | var parameters = expression.Parameters; 68 | 69 | for (var index = 0; index < parameters.Length; index++) 70 | { 71 | parameters[index] = TraverseExpr(parameters[index], expression); 72 | 73 | if (_stop) 74 | { 75 | break; 76 | } 77 | } 78 | 79 | expression.Parameters = parameters; 80 | 81 | OnLeave(expression); 82 | 83 | return expression; 84 | } 85 | 86 | private IExpression Visit(IExpression expression) 87 | { 88 | foreach (var visitor in Visitors) 89 | { 90 | expression = visitor.OnVisit(this, expression); 91 | } 92 | 93 | return expression; 94 | } 95 | 96 | private void OnLeave(IExpression expression) 97 | { 98 | foreach (var visitor in Visitors) 99 | { 100 | visitor.OnLeave(expression); 101 | } 102 | } 103 | 104 | public void Stop() 105 | { 106 | _stop = true; 107 | } 108 | 109 | private static ConcurrentDictionary _cachedProperties = 110 | new ConcurrentDictionary(); 111 | 112 | private static PropertyInfo[] GetAllProperties(Type type) 113 | { 114 | return _cachedProperties.GetOrAdd( 115 | type, t => t.GetProperties(BindingFlags.Public | BindingFlags.Instance).ToArray()); 116 | } 117 | 118 | private object GetFieldValue(PropertyInfo field, object obj) 119 | { 120 | return field.GetValue(obj); 121 | } 122 | 123 | private void SetFieldValue(PropertyInfo field, object obj, object value) 124 | { 125 | field.SetValue(obj, value); 126 | } 127 | 128 | [Flags] 129 | public enum VisitationResult 130 | { 131 | None, 132 | 133 | RemoveCurrent = 0x01, 134 | StopTraversal = 0x02, 135 | DontTraverseChildren = 0x04, 136 | DontTraverseCurrent = 0x08, 137 | DontTraverseCurrentAndChildren = DontTraverseCurrent | DontTraverseChildren 138 | } 139 | } 140 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/ExpressionVisitor.cs: -------------------------------------------------------------------------------- 1 | namespace ConcreteMC.MolangSharp.Parser 2 | { 3 | /// 4 | /// An abstract class that can be used as a base for any ExpressionVisitor 5 | /// 6 | public abstract class ExpressionVisitor : IExpressionVisitor 7 | { 8 | /// 9 | public virtual void BeforeTraverse(IExpression[] expressions) { } 10 | 11 | /// 12 | public abstract IExpression OnVisit(ExpressionTraverser traverser, IExpression expression); 13 | 14 | /// 15 | public virtual void OnLeave(IExpression expression) { } 16 | 17 | /// 18 | public virtual void AfterTraverse(IExpression[] expressions) { } 19 | } 20 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/ArrayAccessExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Struct; 3 | using ConcreteMC.MolangSharp.Runtime.Value; 4 | using ConcreteMC.MolangSharp.Utils; 5 | 6 | namespace ConcreteMC.MolangSharp.Parser.Expressions 7 | { 8 | public class ArrayAccessExpression : Expression 9 | { 10 | public IExpression Array => Parameters[0]; 11 | public IExpression Index => Parameters[1]; 12 | 13 | public ArrayAccessExpression(IExpression array, IExpression index) : base(array, index) { } 14 | 15 | /// 16 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 17 | { 18 | var index = (int) Index.Evaluate(scope, environment).AsDouble(); 19 | MoPath path; 20 | 21 | if (Array is NameExpression nameExpression) 22 | { 23 | var p = nameExpression.Name; 24 | path = p; 25 | } 26 | else 27 | { 28 | var eval = Array.Evaluate(scope, environment); 29 | path = new MoPath($"{eval.AsString()}"); 30 | } 31 | 32 | var array = environment.GetValue(path); 33 | 34 | if (array is ArrayStruct asArray) 35 | return asArray[index]; 36 | 37 | return environment.GetValue(path); 38 | } 39 | 40 | /// 41 | public override void Assign(MoScope scope, MoLangEnvironment environment, IMoValue value) 42 | { 43 | var index = (int) Index.Evaluate(scope, environment).AsDouble(); 44 | 45 | MoPath path; 46 | 47 | if (Array is NameExpression nameExpression) 48 | { 49 | var p = nameExpression.Name; 50 | path = p; 51 | } 52 | else 53 | { 54 | var eval = Array.Evaluate(scope, environment); 55 | path = new MoPath($"{eval.AsString()}"); 56 | } 57 | 58 | var array = environment.GetValue(path); 59 | 60 | if (array is ArrayStruct asArray) 61 | { 62 | asArray[index] = value; 63 | } 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/AssignExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions 5 | { 6 | public class AssignExpression : Expression 7 | { 8 | public IExpression Variable => Parameters[0]; 9 | public IExpression Expression => Parameters[1]; 10 | 11 | public AssignExpression(IExpression variable, IExpression expr) : base(variable, expr) { } 12 | 13 | /// 14 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 15 | { 16 | Variable.Assign(scope, environment, Expression.Evaluate(scope, environment)); 17 | 18 | return DoubleValue.Zero; 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/BinaryOp/ArrowExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions.BinaryOp 5 | { 6 | public class ArrowExpression : BinaryOpExpression 7 | { 8 | public ArrowExpression(IExpression left, IExpression right) : base(left, right) { } 9 | 10 | /// 11 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 12 | { 13 | var leftEnv = Left.Evaluate(scope, environment); 14 | 15 | if (leftEnv is MoLangEnvironment leftMolangEnvironment) 16 | { 17 | return Right.Evaluate(scope, leftMolangEnvironment); 18 | } 19 | 20 | return null; 21 | } 22 | 23 | /// 24 | public override string GetSigil() 25 | { 26 | return "->"; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/BinaryOp/BooleanAndExpressio.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions.BinaryOp 5 | { 6 | public class BooleanAndExpression : BinaryOpExpression 7 | { 8 | /// 9 | public BooleanAndExpression(IExpression l, IExpression r) : base(l, r) { } 10 | 11 | /// 12 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 13 | { 14 | return new DoubleValue( 15 | Left.Evaluate(scope, environment).AsBool() && Right.Evaluate(scope, environment).AsBool()); 16 | } 17 | 18 | /// 19 | public override string GetSigil() 20 | { 21 | return "&&"; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/BinaryOp/BooleanOrExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions.BinaryOp 5 | { 6 | public class BooleanOrExpression : BinaryOpExpression 7 | { 8 | /// 9 | public BooleanOrExpression(IExpression l, IExpression r) : base(l, r) { } 10 | 11 | /// 12 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 13 | { 14 | return new DoubleValue( 15 | Left.Evaluate(scope, environment).AsBool() || Right.Evaluate(scope, environment).AsBool()); 16 | } 17 | 18 | /// 19 | public override string GetSigil() 20 | { 21 | return "||"; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/BinaryOp/CoalesceExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | using ConcreteMC.MolangSharp.Utils; 4 | 5 | namespace ConcreteMC.MolangSharp.Parser.Expressions.BinaryOp 6 | { 7 | public class CoalesceExpression : BinaryOpExpression 8 | { 9 | /// 10 | public CoalesceExpression(IExpression l, IExpression r) : base(l, r) { } 11 | 12 | /// 13 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 14 | { 15 | IMoValue evalLeft = Left.Evaluate(scope, environment); 16 | 17 | //IMoValue value = environment.GetValue(new MoPath(evalLeft.AsString())); 18 | 19 | if (evalLeft == null || !evalLeft.AsBool()) 20 | { 21 | return Right.Evaluate(scope, environment); 22 | } 23 | else 24 | { 25 | return evalLeft; 26 | } 27 | } 28 | 29 | /// 30 | public override string GetSigil() 31 | { 32 | return "??"; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/BinaryOp/DivideExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ConcreteMC.MolangSharp.Runtime; 3 | using ConcreteMC.MolangSharp.Runtime.Exceptions; 4 | using ConcreteMC.MolangSharp.Runtime.Value; 5 | 6 | namespace ConcreteMC.MolangSharp.Parser.Expressions.BinaryOp 7 | { 8 | public class DivideExpression : BinaryOpExpression 9 | { 10 | /// 11 | public DivideExpression(IExpression l, IExpression r) : base(l, r) { } 12 | 13 | /// 14 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 15 | { 16 | try 17 | { 18 | return new DoubleValue( 19 | Left.Evaluate(scope, environment).AsDouble() / Right.Evaluate(scope, environment).AsDouble()); 20 | } 21 | catch (Exception ex) 22 | { 23 | throw new MoLangRuntimeException("An unexpected error occured.", ex); 24 | } 25 | } 26 | 27 | /// 28 | public override string GetSigil() 29 | { 30 | return "/"; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/BinaryOp/EqualExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions.BinaryOp 5 | { 6 | public class EqualExpression : BinaryOpExpression 7 | { 8 | /// 9 | public EqualExpression(IExpression l, IExpression r) : base(l, r) { } 10 | 11 | /// 12 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 13 | { 14 | var left = Left.Evaluate(scope, environment); 15 | var right = Right.Evaluate(scope, environment); 16 | 17 | return new DoubleValue(left.Equals(right)); 18 | } 19 | 20 | /// 21 | public override string GetSigil() 22 | { 23 | return "=="; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/BinaryOp/GreaterExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions.BinaryOp 5 | { 6 | public class GreaterExpression : BinaryOpExpression 7 | { 8 | /// 9 | public GreaterExpression(IExpression l, IExpression r) : base(l, r) { } 10 | 11 | /// 12 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 13 | { 14 | return new DoubleValue( 15 | Left.Evaluate(scope, environment).AsDouble() > Right.Evaluate(scope, environment).AsDouble()); 16 | } 17 | 18 | /// 19 | public override string GetSigil() 20 | { 21 | return ">"; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/BinaryOp/GreaterOrEqualExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions.BinaryOp 5 | { 6 | public class GreaterOrEqualExpression : BinaryOpExpression 7 | { 8 | /// 9 | public GreaterOrEqualExpression(IExpression l, IExpression r) : base(l, r) { } 10 | 11 | /// 12 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 13 | { 14 | return new DoubleValue( 15 | Left.Evaluate(scope, environment).AsDouble() >= Right.Evaluate(scope, environment).AsDouble()); 16 | } 17 | 18 | /// 19 | public override string GetSigil() 20 | { 21 | return ">="; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/BinaryOp/MinusExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions.BinaryOp 5 | { 6 | public class MinusExpression : BinaryOpExpression 7 | { 8 | /// 9 | public MinusExpression(IExpression l, IExpression r) : base(l, r) { } 10 | 11 | /// 12 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 13 | { 14 | return new DoubleValue( 15 | Left.Evaluate(scope, environment).AsDouble() - Right.Evaluate(scope, environment).AsDouble()); 16 | } 17 | 18 | /// 19 | public override string GetSigil() 20 | { 21 | return "-"; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/BinaryOp/NotEqualExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions.BinaryOp 5 | { 6 | public class NotEqualExpression : BinaryOpExpression 7 | { 8 | /// 9 | public NotEqualExpression(IExpression l, IExpression r) : base(l, r) { } 10 | 11 | /// 12 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 13 | { 14 | var left = Left.Evaluate(scope, environment); 15 | var right = Right.Evaluate(scope, environment); 16 | 17 | return new DoubleValue(!left.Equals(right)); 18 | } 19 | 20 | /// 21 | public override string GetSigil() 22 | { 23 | return "!="; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/BinaryOp/PlusExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ConcreteMC.MolangSharp.Runtime; 3 | using ConcreteMC.MolangSharp.Runtime.Exceptions; 4 | using ConcreteMC.MolangSharp.Runtime.Value; 5 | 6 | namespace ConcreteMC.MolangSharp.Parser.Expressions.BinaryOp 7 | { 8 | public class PlusExpression : BinaryOpExpression 9 | { 10 | /// 11 | public PlusExpression(IExpression l, IExpression r) : base(l, r) { } 12 | 13 | /// 14 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 15 | { 16 | try 17 | { 18 | return new DoubleValue( 19 | Left.Evaluate(scope, environment).AsDouble() + Right.Evaluate(scope, environment).AsDouble()); 20 | } 21 | catch (Exception ex) 22 | { 23 | throw new MoLangRuntimeException(this, "An unexpected error occured.", ex); 24 | } 25 | } 26 | 27 | /// 28 | public override string GetSigil() 29 | { 30 | return "+"; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/BinaryOp/PowExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions.BinaryOp 5 | { 6 | public class PowExpression : BinaryOpExpression 7 | { 8 | /// 9 | public PowExpression(IExpression l, IExpression r) : base(l, r) { } 10 | 11 | /// 12 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 13 | { 14 | return new DoubleValue( 15 | Left.Evaluate(scope, environment).AsDouble() * Right.Evaluate(scope, environment).AsDouble()); 16 | } 17 | 18 | /// 19 | public override string GetSigil() 20 | { 21 | return "*"; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/BinaryOp/SmallerExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions.BinaryOp 5 | { 6 | public class SmallerExpression : BinaryOpExpression 7 | { 8 | /// 9 | public SmallerExpression(IExpression l, IExpression r) : base(l, r) { } 10 | 11 | /// 12 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 13 | { 14 | return new DoubleValue( 15 | Left.Evaluate(scope, environment).AsDouble() < Right.Evaluate(scope, environment).AsDouble()); 16 | } 17 | 18 | /// 19 | public override string GetSigil() 20 | { 21 | return "<"; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/BinaryOp/SmallerOrEqualExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions.BinaryOp 5 | { 6 | public class SmallerOrEqualExpression : BinaryOpExpression 7 | { 8 | /// 9 | public SmallerOrEqualExpression(IExpression l, IExpression r) : base(l, r) { } 10 | 11 | /// 12 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 13 | { 14 | return new DoubleValue( 15 | Left.Evaluate(scope, environment).AsDouble() <= Right.Evaluate(scope, environment).AsDouble()); 16 | } 17 | 18 | /// 19 | public override string GetSigil() 20 | { 21 | return "<="; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/BinaryOpExpression.cs: -------------------------------------------------------------------------------- 1 | namespace ConcreteMC.MolangSharp.Parser.Expressions 2 | { 3 | public abstract class BinaryOpExpression : Expression 4 | { 5 | public IExpression Left 6 | { 7 | get { return Parameters[0]; } 8 | set 9 | { 10 | Parameters[0] = value; 11 | } 12 | } 13 | 14 | public IExpression Right 15 | { 16 | get { return Parameters[1]; } 17 | set { Parameters[1] = value; } 18 | } 19 | 20 | protected BinaryOpExpression(IExpression l, IExpression r) : base(l, r) { } 21 | 22 | public abstract string GetSigil(); 23 | } 24 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/BooleanExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions 5 | { 6 | public class BooleanExpression : Expression 7 | { 8 | private readonly IMoValue _value; 9 | 10 | public BooleanExpression(bool value) : base() 11 | { 12 | _value = value ? DoubleValue.One : DoubleValue.Zero; 13 | } 14 | 15 | /// 16 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 17 | { 18 | return _value; 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/BooleanNotExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions 5 | { 6 | public class BooleanNotExpression : Expression 7 | { 8 | public BooleanNotExpression(IExpression value) : base(value) 9 | { 10 | //Value = value; 11 | } 12 | 13 | /// 14 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 15 | { 16 | return Parameters[0].Evaluate(scope, environment).AsBool() ? DoubleValue.Zero : 17 | DoubleValue.One; // .Equals(DoubleValue.One) ? DoubleValue.Zero : DoubleValue.One; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/BreakExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions 5 | { 6 | public class BreakExpression : Expression 7 | { 8 | /// 9 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 10 | { 11 | scope.IsBreak = true; 12 | 13 | return DoubleValue.Zero; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/ContinueExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions 5 | { 6 | public class ContinueExpression : Expression 7 | { 8 | /// 9 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 10 | { 11 | scope.IsContinue = true; 12 | 13 | return DoubleValue.Zero; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/ForEachExpression.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using ConcreteMC.MolangSharp.Runtime; 3 | using ConcreteMC.MolangSharp.Runtime.Struct; 4 | using ConcreteMC.MolangSharp.Runtime.Value; 5 | 6 | namespace ConcreteMC.MolangSharp.Parser.Expressions 7 | { 8 | public class ForEachExpression : Expression 9 | { 10 | public IExpression Variable => Parameters[0]; 11 | public IExpression Array => Parameters[1]; 12 | public IExpression Body => Parameters[2]; 13 | 14 | public ForEachExpression(IExpression variable, IExpression array, IExpression body) : base( 15 | variable, array, body) { } 16 | 17 | /// 18 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 19 | { 20 | IMoValue array = Array.Evaluate(scope, environment); 21 | 22 | if (array is VariableStruct vs) 23 | { 24 | MoScope subScope = new MoScope(scope.Runtime); 25 | 26 | foreach (IMoValue value in vs.Map.Values) 27 | { 28 | subScope.IsContinue = false; 29 | subScope.IsBreak = false; 30 | 31 | Variable.Assign( 32 | subScope, environment, value is VariableStruct vss ? vss.Map.FirstOrDefault().Value : value); 33 | 34 | Body.Evaluate(subScope, environment); 35 | 36 | if (subScope.ReturnValue != null) 37 | { 38 | return subScope.ReturnValue; 39 | } 40 | else if (subScope.IsBreak) 41 | { 42 | break; 43 | } 44 | } 45 | } 46 | 47 | return DoubleValue.Zero; 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/FuncCallExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | using ConcreteMC.MolangSharp.Utils; 4 | 5 | namespace ConcreteMC.MolangSharp.Parser.Expressions 6 | { 7 | public class FuncCallExpression : Expression 8 | { 9 | public MoPath Name { get; set; } 10 | 11 | public FuncCallExpression(MoPath name, IExpression[] args) : base(args) 12 | { 13 | Name = name; 14 | } 15 | 16 | /// 17 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 18 | { 19 | //List p = Args.ToList(); 20 | MoPath name = Name; /* Name is NameExpression expression ? expression.Name : 21 | new MoPath(Name.Evaluate(scope, environment).ToString());*/ 22 | 23 | IMoValue[] arguments = new IMoValue[Parameters.Length]; 24 | 25 | for (int i = 0; i < arguments.Length; i++) 26 | { 27 | arguments[i] = Parameters[i].Evaluate(scope, environment); 28 | } 29 | 30 | return environment.GetValue(name, new MoParams(arguments)); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/LoopExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions 5 | { 6 | public class LoopExpression : Expression 7 | { 8 | public IExpression Count => Parameters[0]; 9 | public IExpression Body => Parameters[1]; 10 | 11 | public LoopExpression(IExpression count, IExpression body) : base(count, body) { } 12 | 13 | /// 14 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 15 | { 16 | int loop = (int) Count.Evaluate(scope, environment).AsDouble(); 17 | MoScope subScope = new MoScope(scope.Runtime) {Runtime = scope.Runtime}; 18 | 19 | while (loop > 0) 20 | { 21 | subScope.IsContinue = false; 22 | subScope.IsBreak = false; 23 | 24 | Body.Evaluate(subScope, environment); 25 | loop--; 26 | 27 | if (subScope.ReturnValue != null) 28 | { 29 | return subScope.ReturnValue; 30 | } 31 | else if (subScope.IsBreak) 32 | { 33 | break; 34 | } 35 | } 36 | 37 | return DoubleValue.Zero; 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/NameExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | using ConcreteMC.MolangSharp.Utils; 4 | 5 | namespace ConcreteMC.MolangSharp.Parser.Expressions 6 | { 7 | public class NameExpression : Expression 8 | { 9 | public MoPath Name { get; set; } 10 | 11 | /// 12 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 13 | { 14 | return environment.GetValue(Name); 15 | } 16 | 17 | /// 18 | public override void Assign(MoScope scope, MoLangEnvironment environment, IMoValue value) 19 | { 20 | environment.SetValue(Name, value); 21 | } 22 | 23 | /// 24 | public NameExpression(string value) 25 | { 26 | Name = new MoPath(value); 27 | } 28 | 29 | public NameExpression(MoPath path) 30 | { 31 | Name = path; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/NumberExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions 5 | { 6 | public class NumberExpression : Expression 7 | { 8 | private readonly IMoValue _value; 9 | 10 | /// 11 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 12 | { 13 | return _value; 14 | } 15 | 16 | /// 17 | public NumberExpression(double value) : base() 18 | { 19 | _value = new DoubleValue(value); 20 | } 21 | 22 | public NumberExpression(IMoValue value) : base() 23 | { 24 | _value = value; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/ReturnExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions 5 | { 6 | public class ReturnExpression : Expression 7 | { 8 | /// 9 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 10 | { 11 | IMoValue eval = Parameters[0].Evaluate(scope, environment); 12 | scope.ReturnValue = eval; 13 | 14 | return eval; 15 | } 16 | 17 | /// 18 | public ReturnExpression(IExpression value) : base(value) { } 19 | } 20 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/ScriptExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ConcreteMC.MolangSharp.Runtime; 3 | using ConcreteMC.MolangSharp.Runtime.Exceptions; 4 | using ConcreteMC.MolangSharp.Runtime.Value; 5 | 6 | namespace ConcreteMC.MolangSharp.Parser.Expressions 7 | { 8 | public class ScriptExpression : Expression 9 | { 10 | public ScriptExpression(IExpression[] expressions) : base(expressions) { } 11 | 12 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 13 | { 14 | IMoValue result = DoubleValue.Zero; 15 | // MoScope scope = new MoScope(this); 16 | 17 | foreach (IExpression expression in Parameters) 18 | { 19 | if (expression == null) 20 | continue; 21 | 22 | try 23 | { 24 | result = expression.Evaluate(scope, environment); 25 | 26 | if (scope.ReturnValue != null) 27 | { 28 | result = scope.ReturnValue; 29 | 30 | break; 31 | } 32 | } 33 | catch (Exception ex) 34 | { 35 | throw new MoLangRuntimeException( 36 | expression, "An error occured while evaluating the expression", ex); 37 | } 38 | } 39 | 40 | return result; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/StatementExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions 5 | { 6 | public class StatementExpression : Expression 7 | { 8 | /// 9 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 10 | { 11 | foreach (IExpression expression in Parameters) 12 | { 13 | expression.Evaluate(scope, environment); 14 | 15 | if (scope.ReturnValue != null) 16 | { 17 | return scope.ReturnValue; 18 | } 19 | else if (scope.IsBreak || scope.IsContinue) 20 | { 21 | break; 22 | } 23 | } 24 | 25 | return DoubleValue.Zero; 26 | } 27 | 28 | /// 29 | public StatementExpression(IExpression[] value) : base(value) { } 30 | } 31 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/StringExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions 5 | { 6 | public class StringExpression : Expression 7 | { 8 | /// 9 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 10 | { 11 | return new StringValue(_value); 12 | } 13 | 14 | private string _value; 15 | 16 | /// 17 | public StringExpression(string value) : base() 18 | { 19 | _value = value; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/TernaryExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions 5 | { 6 | public class TernaryExpression : Expression 7 | { 8 | public IExpression Condition => Parameters[0]; 9 | public IExpression ThenExpr => Parameters[1]; 10 | public IExpression ElseExpr => Parameters[2]; 11 | 12 | public TernaryExpression(IExpression condition, IExpression thenExpr, IExpression elseExpr) : base( 13 | condition, thenExpr, elseExpr) { } 14 | 15 | /// 16 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 17 | { 18 | if (Condition.Evaluate(scope, environment).Equals(DoubleValue.One)) 19 | { 20 | return ThenExpr == null ? Condition.Evaluate(scope, environment) : 21 | ThenExpr.Evaluate(scope, environment); 22 | } 23 | else if (ElseExpr != null) 24 | { 25 | return ElseExpr.Evaluate(scope, environment); 26 | } 27 | 28 | return DoubleValue.Zero; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/ThisExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions 5 | { 6 | public class ThisExpression : Expression 7 | { 8 | /// 9 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 10 | { 11 | return environment.ThisVariable; // environment.GetValue(_this); 12 | } 13 | 14 | /// 15 | public ThisExpression() { } 16 | } 17 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/UnaryMinusExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions 5 | { 6 | public class UnaryMinusExpression : Expression 7 | { 8 | /// 9 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 10 | { 11 | return new DoubleValue(-(Parameters[0].Evaluate(scope, environment).AsDouble())); 12 | } 13 | 14 | /// 15 | public UnaryMinusExpression(IExpression value) : base(value) { } 16 | } 17 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Expressions/UnaryPlusExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Expressions 5 | { 6 | public class UnaryPlusExpression : Expression 7 | { 8 | /// 9 | public UnaryPlusExpression(IExpression value) : base(value) 10 | { 11 | //_value = value; 12 | } 13 | 14 | /// 15 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 16 | { 17 | return new DoubleValue(+(Parameters[0].Evaluate(scope, environment).AsDouble())); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/IExpression.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser 5 | { 6 | /// 7 | /// The interface for all expressions to implement 8 | /// 9 | public interface IExpression 10 | { 11 | /// 12 | /// Contains metadata about this expression 13 | /// 14 | ExpressionMeta Meta { get; } 15 | 16 | /// 17 | /// Evaluate the expression 18 | /// 19 | /// The current scope 20 | /// The environment provided by the 21 | /// The value returned by the expression 22 | IMoValue Evaluate(MoScope scope, MoLangEnvironment environment); 23 | 24 | /// 25 | /// Invoked when trying to assign a value to a property/field 26 | /// 27 | /// The current scope 28 | /// The environment provided by the 29 | /// The value to assign 30 | void Assign(MoScope scope, MoLangEnvironment environment, IMoValue value); 31 | 32 | /// 33 | /// The parameters used by this expression 34 | /// 35 | IExpression[] Parameters { get; set; } 36 | } 37 | 38 | public abstract class Expression : Expression 39 | { 40 | protected Expression(T value) : base() 41 | { 42 | Value = value; 43 | } 44 | 45 | public T Value { get; set; } 46 | } 47 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/IExpressionVisitor.cs: -------------------------------------------------------------------------------- 1 | namespace ConcreteMC.MolangSharp.Parser 2 | { 3 | /// 4 | /// The base interface for expression visitors. 5 | /// 6 | /// 7 | /// Expression visitors are mainly used to optimize arrays of 8 | /// One such example is the 9 | /// 10 | public interface IExpressionVisitor 11 | { 12 | /// 13 | /// Invoked before any ExpressionVisitor has traversed expression array 14 | /// 15 | /// The expressions to be traversed 16 | void BeforeTraverse(IExpression[] expressions); 17 | 18 | /// 19 | /// Invoked once for every 20 | /// 21 | /// The that invoked the visitor 22 | /// The expressions to visit 23 | /// The visited expression 24 | IExpression OnVisit(ExpressionTraverser traverser, IExpression expression); 25 | 26 | /// 27 | /// Invoked when all ExpressionVisitors have visited an expression 28 | /// 29 | /// The expressions that has been visited 30 | void OnLeave(IExpression expression); 31 | 32 | /// 33 | /// Invoked after all of the ExpressionVisitors have traversed an expression array 34 | /// 35 | /// The visited expression array 36 | void AfterTraverse(IExpression[] expressions); 37 | } 38 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/InfixParselet.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 2 | 3 | namespace ConcreteMC.MolangSharp.Parser 4 | { 5 | public abstract class InfixParselet 6 | { 7 | public Precedence Precedence { get; protected set; } 8 | 9 | public InfixParselet(Precedence precedence) 10 | { 11 | Precedence = precedence; 12 | } 13 | 14 | public abstract IExpression Parse(MoLangParser parser, Token token, IExpression leftExpr); 15 | } 16 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Parselet/ArrayAccessParselet.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Parser.Expressions; 2 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Parselet 5 | { 6 | /// 7 | /// Implements the indexer/array accessor parser 8 | /// 9 | /// 10 | /// Parses expressions such as "array[0]" 11 | /// 12 | public class ArrayAccessParselet : InfixParselet 13 | { 14 | /// 15 | public override IExpression Parse(MoLangParser parser, Token token, IExpression leftExpr) 16 | { 17 | IExpression index = parser.ParseExpression(Precedence); 18 | parser.ConsumeToken(TokenType.ArrayRight); 19 | 20 | return new ArrayAccessExpression(leftExpr, index); 21 | } 22 | 23 | /// 24 | public ArrayAccessParselet() : base(Precedence.ArrayAccess) { } 25 | } 26 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Parselet/AssignParselet.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Parser.Expressions; 2 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Parselet 5 | { 6 | /// 7 | /// Implements the "=" parselet 8 | /// 9 | public class AssignParselet : InfixParselet 10 | { 11 | /// 12 | public AssignParselet() : base(Precedence.Assignment) { } 13 | 14 | /// 15 | public override IExpression Parse(MoLangParser parser, Token token, IExpression leftExpr) 16 | { 17 | return new AssignExpression(leftExpr, parser.ParseExpression(Precedence)); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Parselet/BooleanNotParselet.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Parser.Expressions; 2 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Parselet 5 | { 6 | /// 7 | /// Implements the "!=" parselet 8 | /// 9 | public class BooleanNotParselet : PrefixParselet 10 | { 11 | /// 12 | public override IExpression Parse(MoLangParser parser, Token token) 13 | { 14 | return new BooleanNotExpression(parser.ParseExpression(Precedence.Prefix)); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Parselet/BooleanParselet.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Parser.Expressions; 2 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Parselet 5 | { 6 | /// 7 | /// Implements the boolean parselet 8 | /// 9 | public class BooleanParselet : PrefixParselet 10 | { 11 | /// 12 | public override IExpression Parse(MoLangParser parser, Token token) 13 | { 14 | return new BooleanExpression(bool.Parse(token.Text)); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Parselet/BracketScopeParselet.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using ConcreteMC.MolangSharp.Parser.Expressions; 3 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 4 | 5 | namespace ConcreteMC.MolangSharp.Parser.Parselet 6 | { 7 | /// 8 | /// Implements the scope parselet 9 | /// 10 | public class BracketScopeParselet : PrefixParselet 11 | { 12 | /// 13 | public override IExpression Parse(MoLangParser parser, Token token) 14 | { 15 | List exprs = new List(); 16 | 17 | if (!parser.MatchToken(TokenType.CurlyBracketRight)) 18 | { 19 | do 20 | { 21 | if (parser.MatchToken(TokenType.CurlyBracketRight, false)) 22 | { 23 | break; 24 | } 25 | 26 | exprs.Add(parser.ParseExpression(Precedence.Scope)); 27 | } while (parser.MatchToken(TokenType.Semicolon)); 28 | 29 | parser.ConsumeToken(TokenType.CurlyBracketRight); 30 | } 31 | 32 | return new StatementExpression(exprs.ToArray()); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Parselet/BreakParselet.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Parser.Expressions; 2 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Parselet 5 | { 6 | /// 7 | /// Implements the "break" instruction parser 8 | /// 9 | public class BreakParselet : PrefixParselet 10 | { 11 | /// 12 | public override IExpression Parse(MoLangParser parser, Token token) 13 | { 14 | return new BreakExpression(); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Parselet/ContinueParselet.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Parser.Expressions; 2 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Parselet 5 | { 6 | /// 7 | /// Implements the "continue" instruction parser 8 | /// 9 | public class ContinueParselet : PrefixParselet 10 | { 11 | /// 12 | public override IExpression Parse(MoLangParser parser, Token token) 13 | { 14 | return new ContinueExpression(); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Parselet/FloatParselet.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using ConcreteMC.MolangSharp.Parser.Exceptions; 3 | using ConcreteMC.MolangSharp.Parser.Expressions; 4 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 5 | 6 | namespace ConcreteMC.MolangSharp.Parser.Parselet 7 | { 8 | /// 9 | /// Implements Float parsing 10 | /// 11 | public class FloatParselet : PrefixParselet 12 | { 13 | private const NumberStyles NumberStyle = System.Globalization.NumberStyles.AllowDecimalPoint; 14 | private static readonly CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; 15 | 16 | /// 17 | public override IExpression Parse(MoLangParser parser, Token token) 18 | { 19 | if (float.TryParse(token.Text, NumberStyle, Culture, out var result)) 20 | { 21 | return new NumberExpression(result); 22 | } 23 | 24 | throw new MoLangParserException($"Could not parse \'{token.Text.ToString()}\' as float"); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Parselet/ForEachParselet.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Parser.Exceptions; 2 | using ConcreteMC.MolangSharp.Parser.Expressions; 3 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 4 | 5 | namespace ConcreteMC.MolangSharp.Parser.Parselet 6 | { 7 | /// 8 | /// Implements the "foreach" instruction parser 9 | /// 10 | public class ForEachParselet : PrefixParselet 11 | { 12 | /// 13 | public override IExpression Parse(MoLangParser parser, Token token) 14 | { 15 | if (!parser.TryParseArgs(out var expressions) || expressions.Length != 3) 16 | throw new MoLangParserException( 17 | $"ForEach: Expected 3 argument, {(expressions?.Length ?? 0)} argument given"); 18 | 19 | return new ForEachExpression(expressions[0], expressions[1], expressions[2]); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Parselet/GenericBinaryOpParselet.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Parser.Expressions.BinaryOp; 2 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Parselet 5 | { 6 | /// 7 | /// Generic binary operator parselet 8 | /// 9 | public class GenericBinaryOpParselet : InfixParselet 10 | { 11 | /// 12 | public GenericBinaryOpParselet(Precedence precedence) : base(precedence) { } 13 | 14 | /// 15 | public override IExpression Parse(MoLangParser parser, Token token, IExpression leftExpr) 16 | { 17 | IExpression rightExpr = parser.ParseExpression(Precedence); 18 | 19 | if (token.Type.Equals(TokenType.Arrow)) 20 | return new ArrowExpression(leftExpr, rightExpr); 21 | 22 | if (token.Type.Equals(TokenType.And)) 23 | return new BooleanAndExpression(leftExpr, rightExpr); 24 | 25 | if (token.Type.Equals(TokenType.Or)) 26 | return new BooleanOrExpression(leftExpr, rightExpr); 27 | 28 | if (token.Type.Equals(TokenType.Coalesce)) 29 | return new CoalesceExpression(leftExpr, rightExpr); 30 | 31 | if (token.Type.Equals(TokenType.Slash)) 32 | return new DivideExpression(leftExpr, rightExpr); 33 | 34 | if (token.Type.Equals(TokenType.EqualsEquals)) 35 | return new EqualExpression(leftExpr, rightExpr); 36 | 37 | if (token.Type.Equals(TokenType.Greater)) 38 | return new GreaterExpression(leftExpr, rightExpr); 39 | 40 | if (token.Type.Equals(TokenType.GreaterOrEquals)) 41 | return new GreaterOrEqualExpression(leftExpr, rightExpr); 42 | 43 | if (token.Type.Equals(TokenType.Minus)) 44 | return new MinusExpression(leftExpr, rightExpr); 45 | 46 | if (token.Type.Equals(TokenType.NotEquals)) 47 | return new NotEqualExpression(leftExpr, rightExpr); 48 | 49 | if (token.Type.Equals(TokenType.Plus)) 50 | return new PlusExpression(leftExpr, rightExpr); 51 | 52 | if (token.Type.Equals(TokenType.Asterisk)) 53 | return new PowExpression(leftExpr, rightExpr); 54 | 55 | if (token.Type.Equals(TokenType.Smaller)) 56 | return new SmallerExpression(leftExpr, rightExpr); 57 | 58 | if (token.Type.Equals(TokenType.SmallerOrEquals)) 59 | return new SmallerOrEqualExpression(leftExpr, rightExpr); 60 | 61 | return null; 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Parselet/GroupParselet.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 2 | 3 | namespace ConcreteMC.MolangSharp.Parser.Parselet 4 | { 5 | /// 6 | /// Implements the "group" parselet 7 | /// 8 | public class GroupParselet : PrefixParselet 9 | { 10 | /// 11 | public override IExpression Parse(MoLangParser parser, Token token) 12 | { 13 | IExpression expr = parser.ParseExpression(); 14 | var result = parser.ConsumeToken(TokenType.BracketRight); 15 | 16 | return expr; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Parselet/LoopParselet.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Parser.Exceptions; 2 | using ConcreteMC.MolangSharp.Parser.Expressions; 3 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 4 | 5 | namespace ConcreteMC.MolangSharp.Parser.Parselet 6 | { 7 | /// 8 | /// Implements the "loop" instruction parser 9 | /// 10 | public class LoopParselet : PrefixParselet 11 | { 12 | /// 13 | public override IExpression Parse(MoLangParser parser, Token token) 14 | { 15 | if (!parser.TryParseArgs(out var expressions) || expressions.Length != 2) 16 | throw new MoLangParserException( 17 | $"Loop: Expected 2 argument, {(expressions?.Length ?? 0)} argument given"); 18 | 19 | return new LoopExpression(expressions[0], expressions[1]); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Parselet/NameParselet.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Parser.Expressions; 2 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 3 | using ConcreteMC.MolangSharp.Utils; 4 | 5 | namespace ConcreteMC.MolangSharp.Parser.Parselet 6 | { 7 | /// 8 | /// Implements the "name" parser 9 | /// 10 | /// 11 | /// Used to parse function calls or property accessors. 12 | /// For example: "query.frame_time" or "math.min(10, 20)" 13 | /// 14 | public class NameParselet : PrefixParselet 15 | { 16 | /// 17 | public override IExpression Parse(MoLangParser parser, Token token) 18 | { 19 | var path = parser.FixNameShortcut(new MoPath(token.Text)); 20 | 21 | if (parser.TryParseArgs(out var expressions)) 22 | return new FuncCallExpression(path, expressions); 23 | 24 | return new NameExpression(path); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Parselet/NumberParselet.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using ConcreteMC.MolangSharp.Parser.Exceptions; 3 | using ConcreteMC.MolangSharp.Parser.Expressions; 4 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 5 | 6 | namespace ConcreteMC.MolangSharp.Parser.Parselet 7 | { 8 | /// 9 | /// Implements number parsing 10 | /// 11 | public class NumberParselet : PrefixParselet 12 | { 13 | private const NumberStyles NumberStyle = System.Globalization.NumberStyles.AllowDecimalPoint; 14 | private static readonly CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; 15 | 16 | /// 17 | public override IExpression Parse(MoLangParser parser, Token token) 18 | { 19 | if (double.TryParse(token.Text, NumberStyle, Culture, out var result)) 20 | { 21 | return new NumberExpression(result); 22 | } 23 | 24 | throw new MoLangParserException($"Could not parse \'{token.Text.ToString()}\' as a double"); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Parselet/ReturnParselet.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Parser.Expressions; 2 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Parselet 5 | { 6 | /// 7 | /// Implements the "return" instruction parser 8 | /// 9 | public class ReturnParselet : PrefixParselet 10 | { 11 | /// 12 | public override IExpression Parse(MoLangParser parser, Token token) 13 | { 14 | return new ReturnExpression(parser.ParseExpression()); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Parselet/StringParselet.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Parser.Expressions; 2 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Parselet 5 | { 6 | /// 7 | /// Implements the string parser 8 | /// 9 | public class StringParselet : PrefixParselet 10 | { 11 | /// 12 | public override IExpression Parse(MoLangParser parser, Token token) 13 | { 14 | return new StringExpression(token.Text); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Parselet/TernaryParselet.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Parser.Expressions; 2 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Parselet 5 | { 6 | /// 7 | /// Implements the Ternary expression parser 8 | /// 9 | public class TernaryParselet : InfixParselet 10 | { 11 | /// 12 | public TernaryParselet() : base(Precedence.Conditional) { } 13 | 14 | /// 15 | public override IExpression Parse(MoLangParser parser, Token token, IExpression leftExpr) 16 | { 17 | if (parser.MatchToken(TokenType.Colon)) 18 | { 19 | return new TernaryExpression(leftExpr, null, parser.ParseExpression(Precedence)); 20 | } 21 | else 22 | { 23 | IExpression thenExpr = parser.ParseExpression(Precedence); 24 | 25 | if (!parser.MatchToken(TokenType.Colon)) 26 | { 27 | return new TernaryExpression(leftExpr, thenExpr, null); 28 | } 29 | else 30 | { 31 | return new TernaryExpression(leftExpr, thenExpr, parser.ParseExpression(Precedence)); 32 | } 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Parselet/ThisParselet.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Parser.Expressions; 2 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Parselet 5 | { 6 | /// 7 | /// Implements the "this" instruction parser 8 | /// 9 | public class ThisParselet : PrefixParselet 10 | { 11 | /// 12 | public override IExpression Parse(MoLangParser parser, Token token) 13 | { 14 | return new ThisExpression(); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Parselet/UnaryMinusParselet.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Parser.Expressions; 2 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Parselet 5 | { 6 | /// 7 | /// Implements the unary minus parser 8 | /// 9 | public class UnaryMinusParselet : PrefixParselet 10 | { 11 | /// 12 | public override IExpression Parse(MoLangParser parser, Token token) 13 | { 14 | return new UnaryMinusExpression(parser.ParseExpression(Precedence.Prefix)); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Parselet/UnaryPlusParselet.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Parser.Expressions; 2 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Parselet 5 | { 6 | /// 7 | /// Implements the unary plus parser 8 | /// 9 | public class UnaryPlusParselet : PrefixParselet 10 | { 11 | /// 12 | public override IExpression Parse(MoLangParser parser, Token token) 13 | { 14 | return new UnaryPlusExpression(parser.ParseExpression(Precedence.Prefix)); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Precedence.cs: -------------------------------------------------------------------------------- 1 | namespace ConcreteMC.MolangSharp.Parser 2 | { 3 | public enum Precedence 4 | { 5 | Anything, 6 | Scope, 7 | 8 | Assignment, 9 | Conditional, 10 | ArrayAccess, 11 | 12 | Coalesce, 13 | 14 | And, 15 | Or, 16 | 17 | Compare, 18 | 19 | Sum, 20 | Product, 21 | Prefix, 22 | Arrow 23 | } 24 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/PrefixParselet.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Parser.Tokenizer; 2 | 3 | namespace ConcreteMC.MolangSharp.Parser 4 | { 5 | public abstract class PrefixParselet 6 | { 7 | public abstract IExpression Parse(MoLangParser parser, Token token); 8 | } 9 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Tokenizer/ITokenIterator.cs: -------------------------------------------------------------------------------- 1 | namespace ConcreteMC.MolangSharp.Parser.Tokenizer 2 | { 3 | public interface ITokenIterator 4 | { 5 | Token Next(); 6 | 7 | void Step(); 8 | } 9 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Tokenizer/Token.cs: -------------------------------------------------------------------------------- 1 | namespace ConcreteMC.MolangSharp.Parser.Tokenizer 2 | { 3 | /// 4 | /// A token is a piece of code in an expression 5 | /// 6 | public class Token 7 | { 8 | /// 9 | /// The type of this token 10 | /// 11 | public TokenType Type { get; } 12 | 13 | /// 14 | /// The piece of code defining this token 15 | /// 16 | public string Text { get; } 17 | 18 | /// 19 | /// The position in the source expression that this token was found at 20 | /// 21 | public TokenPosition Position { get; } 22 | 23 | /// 24 | /// Initializes a new token 25 | /// 26 | /// 27 | /// 28 | public Token(TokenType tokenType, TokenPosition position) 29 | { 30 | this.Type = tokenType; 31 | this.Text = tokenType.Symbol; 32 | this.Position = position; 33 | } 34 | 35 | /// 36 | /// Initializes a new token 37 | /// 38 | /// 39 | /// 40 | /// 41 | public Token(TokenType tokenType, string text, TokenPosition position) 42 | { 43 | this.Type = tokenType; 44 | this.Text = text; 45 | this.Position = position; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Tokenizer/TokenIterator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ConcreteMC.MolangSharp.Parser.Exceptions; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Tokenizer 5 | { 6 | public class TokenIterator : ITokenIterator 7 | { 8 | private readonly string _code; 9 | 10 | private int _index = 0; 11 | private int _currentLine = 0; 12 | private int _lastStep = 0; 13 | private int _lastStepLine = 0; 14 | 15 | public TokenIterator(string code) 16 | { 17 | this._code = code; 18 | } 19 | 20 | public Token Next() 21 | { 22 | while (_index < _code.Length) 23 | { 24 | if (_code.Length > _index + 1) 25 | { 26 | // check tokens with double chars 27 | TokenType token = TokenType.BySymbol(_code.Substring(_index, 2)); 28 | 29 | if (token != null) 30 | { 31 | _index += 2; 32 | 33 | return new Token(token, GetPosition()); 34 | } 35 | } 36 | 37 | var expr = GetCharacterAt(_index); 38 | TokenType tokenType = TokenType.BySymbol(expr); 39 | 40 | if (tokenType != null) 41 | { 42 | _index++; 43 | 44 | return new Token(tokenType, GetPosition()); 45 | } 46 | else 47 | { 48 | if (expr.Equals('\'')) 49 | { 50 | int stringStart = _index + 1; 51 | int stringLength = 0; 52 | 53 | while (stringStart + stringLength < _code.Length 54 | && !GetCharacterAt(stringStart + stringLength).Equals('\'')) 55 | { 56 | stringLength++; 57 | } 58 | 59 | stringLength++; 60 | _index = stringStart + stringLength; 61 | 62 | return new Token( 63 | TokenType.String, _code.Substring(stringStart, stringLength - 1), GetPosition()); 64 | } 65 | 66 | if (char.IsLetter(expr)) 67 | { 68 | var nameStart = _index; 69 | int nameLength = 1; 70 | 71 | while (nameStart + nameLength < _code.Length 72 | && (char.IsLetterOrDigit(GetCharacterAt(nameStart + nameLength)) 73 | || GetCharacterAt(nameStart + nameLength).Equals('_') 74 | || GetCharacterAt(nameStart + nameLength).Equals('.'))) 75 | { 76 | nameLength++; 77 | } 78 | 79 | string value = _code.Substring(_index, nameLength); 80 | TokenType token = TokenType.BySymbol(value); 81 | 82 | if (token == null) 83 | { 84 | token = TokenType.Name; 85 | } 86 | 87 | _index = nameStart + nameLength; 88 | 89 | return new Token(token, value, GetPosition()); 90 | } 91 | 92 | if (char.IsDigit(expr)) 93 | { 94 | int numStart = _index; 95 | int numLength = 1; 96 | bool hasDecimal = false; 97 | bool isFloat = false; 98 | 99 | 100 | while (numStart + numLength < _code.Length) 101 | { 102 | var character = GetCharacterAt(numStart + numLength); 103 | 104 | if (!char.IsDigit(character)) 105 | { 106 | if (character == 'f' && !isFloat) 107 | { 108 | isFloat = true; 109 | } 110 | else if (character == '.' && !hasDecimal) 111 | { 112 | hasDecimal = true; 113 | } 114 | else 115 | { 116 | break; 117 | } 118 | } 119 | 120 | numLength++; 121 | 122 | if (isFloat) 123 | break; 124 | } 125 | 126 | _index = numStart + numLength; 127 | 128 | if (isFloat) 129 | return new Token( 130 | TokenType.FloatingPointNumber, _code.Substring(numStart, numLength - 1), GetPosition()); 131 | 132 | return new Token(TokenType.Number, _code.Substring(numStart, numLength), GetPosition()); 133 | } 134 | 135 | if (expr.Equals('\n') || expr.Equals('\r')) 136 | { 137 | _currentLine++; 138 | } 139 | } 140 | 141 | _index++; 142 | } 143 | 144 | return new Token(TokenType.Eof, GetPosition()); 145 | } 146 | 147 | public void Step() 148 | { 149 | _lastStep = _index; 150 | _lastStepLine = _currentLine; 151 | } 152 | 153 | private TokenPosition GetPosition() 154 | { 155 | return new TokenPosition(_lastStepLine, _currentLine, _lastStep, _index); 156 | } 157 | 158 | private char GetCharacterAt(int index) 159 | { 160 | if (index > _code.Length - 1) 161 | throw new MoLangParserException( 162 | $"Value '{index + 1}' is outside of range Min: 0, Max:{_code.Length - 1}", 163 | new IndexOutOfRangeException(nameof(index))); 164 | 165 | return _code[index]; //.Substring(index, 1)[0]; 166 | } 167 | } 168 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Tokenizer/TokenPosition.cs: -------------------------------------------------------------------------------- 1 | namespace ConcreteMC.MolangSharp.Parser.Tokenizer 2 | { 3 | /// 4 | /// Describes the position of a 5 | /// 6 | public class TokenPosition 7 | { 8 | // public int StartLineNumber; 9 | //public int EndLineNumber; 10 | //public int StartColumn; 11 | // public int EndColumn; 12 | /// 13 | /// The linenumber this token was found at 14 | /// 15 | public int LineNumber { get; } 16 | 17 | /// 18 | /// The character index of this token 19 | /// 20 | public int Index { get; } 21 | 22 | public TokenPosition(int lastStepLine, int currentLine, int lastStep, int index) 23 | { 24 | LineNumber = currentLine; 25 | Index = index; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Tokenizer/TokenType.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | 3 | namespace ConcreteMC.MolangSharp.Parser.Tokenizer 4 | { 5 | public sealed class TokenType 6 | { 7 | public static readonly TokenType EqualsEquals = new TokenType("==", "EqualsEquals"); 8 | public static readonly TokenType NotEquals = new TokenType("!=", "Not Equals"); 9 | public static readonly TokenType Coalesce = new TokenType("??", "Coalesce"); 10 | public static readonly TokenType And = new TokenType("&&", "AndAnd"); 11 | public static readonly TokenType Or = new TokenType("||", "Or"); 12 | public static readonly TokenType GreaterOrEquals = new TokenType(">=", "Greater or equal to"); 13 | public static readonly TokenType SmallerOrEquals = new TokenType("<=", "Smaller or equal to"); 14 | public static readonly TokenType Arrow = new TokenType("->", "Arrow"); 15 | 16 | public static readonly TokenType Greater = new TokenType(">", "Bigger"); 17 | public static readonly TokenType Smaller = new TokenType("<", "Smaller"); 18 | public static readonly TokenType BracketLeft = new TokenType("(", "Bracket Left"); 19 | public static readonly TokenType BracketRight = new TokenType(")", "Bracket Right"); 20 | public static readonly TokenType ArrayLeft = new TokenType("[", "Array Left"); 21 | public static readonly TokenType ArrayRight = new TokenType("]", "Array Right"); 22 | public static readonly TokenType CurlyBracketLeft = new TokenType("{", "Curly Bracket Left"); 23 | public static readonly TokenType CurlyBracketRight = new TokenType("}", "Curly Bracket Right"); 24 | public static readonly TokenType Comma = new TokenType(",", "Comma"); 25 | public static readonly TokenType Assign = new TokenType("=", "Assign"); 26 | public static readonly TokenType Plus = new TokenType("+", "Plus"); 27 | public static readonly TokenType Minus = new TokenType("-", "Minus"); 28 | public static readonly TokenType Asterisk = new TokenType("*", "Asterisk"); 29 | public static readonly TokenType Slash = new TokenType("/", "Slash"); 30 | public static readonly TokenType Question = new TokenType("?", "Question"); 31 | public static readonly TokenType Colon = new TokenType(":", "Colon"); 32 | public static readonly TokenType Semicolon = new TokenType(";", "Semicolon"); 33 | public static readonly TokenType Bang = new TokenType("!", "Bang"); 34 | 35 | public static readonly TokenType Return = new TokenType("return", "Return"); 36 | public static readonly TokenType Continue = new TokenType("continue", "Continue"); 37 | public static readonly TokenType Break = new TokenType("break", "Break"); 38 | public static readonly TokenType ForEach = new TokenType("for_each", "ForEach"); 39 | public static readonly TokenType Loop = new TokenType("loop", "Loop"); 40 | public static readonly TokenType This = new TokenType("this", "Reference"); 41 | public static readonly TokenType True = new TokenType("true", "bool"); 42 | public static readonly TokenType False = new TokenType("false", "bool"); 43 | public static readonly TokenType String = new TokenType("", "string"); 44 | public static readonly TokenType Number = new TokenType("", "number"); 45 | public static readonly TokenType FloatingPointNumber = new TokenType("", "floating point number"); 46 | public static readonly TokenType Name = new TokenType("", "name"); 47 | public static readonly TokenType Eof = new TokenType("", "EndOfFile"); 48 | 49 | private static int _typeCounter = 0; 50 | 51 | /// 52 | /// A string "identifying" this tokentype 53 | /// 54 | public string Symbol { get; } 55 | 56 | /// 57 | /// A human readable identifier for this tokentype 58 | /// 59 | public string TypeName { get; } 60 | 61 | private readonly int _typeId; 62 | 63 | private TokenType(string symbol, string typename = "") 64 | { 65 | Symbol = symbol; 66 | TypeName = typename; 67 | 68 | _typeId = Interlocked.Increment(ref _typeCounter); 69 | } 70 | 71 | private static readonly TokenType[] Values = new TokenType[] 72 | { 73 | EqualsEquals, NotEquals, Coalesce, And, Or, GreaterOrEquals, SmallerOrEquals, Arrow, Greater, Smaller, 74 | BracketLeft, BracketRight, ArrayLeft, ArrayRight, CurlyBracketLeft, CurlyBracketRight, Comma, Assign, 75 | Plus, Minus, Asterisk, Slash, Question, Colon, Semicolon, Bang, Return, Continue, Break, ForEach, Loop, 76 | This, True, False, String, Number, Name, Eof 77 | }; 78 | 79 | /// 80 | /// Finds the TokenType by symbol 81 | /// 82 | /// The symbol to lookup 83 | /// The resulting TokenType or null if no match has been found 84 | public static TokenType BySymbol(string symbol) 85 | { 86 | foreach (TokenType tokenType in TokenType.Values) 87 | { 88 | if (tokenType.Symbol.Equals(symbol)) 89 | { 90 | return tokenType; 91 | } 92 | } 93 | 94 | return null; 95 | } 96 | 97 | /// 98 | /// Finds the TokenType by symbol 99 | /// 100 | /// The symbol to lookup 101 | /// The resulting TokenType or null if no match has been found 102 | public static TokenType BySymbol(char symbol) 103 | { 104 | return BySymbol(symbol.ToString()); 105 | } 106 | 107 | /// 108 | /// Checks if this tokentype is equal to 109 | /// 110 | /// 111 | /// 112 | public bool Equals(TokenType other) 113 | { 114 | return _typeId == other._typeId; 115 | } 116 | } 117 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Visitors/ExpressionConnectingVisitor.cs: -------------------------------------------------------------------------------- 1 | namespace ConcreteMC.MolangSharp.Parser.Visitors 2 | { 3 | public class ExpressionConnectingVisitor : ExpressionVisitor 4 | { 5 | //private LinkedList Stack { get; set; } = new LinkedList(); 6 | 7 | // private LinkedList _previousStack = null; 8 | 9 | private IExpression _last = null; 10 | 11 | /// 12 | public override void BeforeTraverse(IExpression[] expressions) 13 | { 14 | _last = null; 15 | // _previousStack = Stack; 16 | // Stack = new LinkedList(); 17 | } 18 | 19 | /// 20 | public override IExpression OnVisit(ExpressionTraverser traverser, IExpression expression) 21 | { 22 | var previous = _last; 23 | expression.Meta.Previous = previous; 24 | 25 | if (previous != null && previous != expression.Meta.Parent) 26 | { 27 | previous.Meta.Next = expression; 28 | } 29 | 30 | //Stack.AddLast(expression); 31 | 32 | return expression; 33 | } 34 | 35 | /// 36 | public override void OnLeave(IExpression expression) 37 | { 38 | _last = expression; 39 | //Stack.RemoveLast(); 40 | } 41 | 42 | /// 43 | public override void AfterTraverse(IExpression[] expressions) 44 | { 45 | base.AfterTraverse(expressions); 46 | //Stack = _previousStack; 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Visitors/FindingVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Visitors 5 | { 6 | public class FindingVisitor : ExpressionVisitor 7 | { 8 | private Predicate _predicate; 9 | public List FoundExpressions = new List(); 10 | 11 | public FindingVisitor(Predicate predicate) 12 | { 13 | _predicate = predicate; 14 | } 15 | 16 | /// 17 | public override IExpression OnVisit(ExpressionTraverser traverser, IExpression expression) 18 | { 19 | if (_predicate(expression)) 20 | { 21 | FoundExpressions.Add(expression); 22 | } 23 | 24 | return expression; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Visitors/FirstFindingVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ConcreteMC.MolangSharp.Parser.Visitors 4 | { 5 | public class FirstFindingVisitor : ExpressionVisitor 6 | { 7 | private Predicate _predicate; 8 | public IExpression Found = null; 9 | 10 | public FirstFindingVisitor(Predicate predicate) 11 | { 12 | _predicate = predicate; 13 | } 14 | 15 | /// 16 | public override IExpression OnVisit(ExpressionTraverser traverser, IExpression expression) 17 | { 18 | if (_predicate(expression)) 19 | { 20 | Found = expression; 21 | 22 | traverser.Stop(); 23 | } 24 | 25 | return expression; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Visitors/InteropOptimization/InteropOptimizationVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using ConcreteMC.MolangSharp.Parser.Expressions; 4 | using ConcreteMC.MolangSharp.Runtime; 5 | using ConcreteMC.MolangSharp.Runtime.Struct.Interop; 6 | using ConcreteMC.MolangSharp.Runtime.Value; 7 | using ConcreteMC.MolangSharp.Utils; 8 | 9 | namespace ConcreteMC.MolangSharp.Parser.Visitors.InteropOptimization 10 | { 11 | /// 12 | /// Optimizes interop calls to call the methods or properties directly instead of going through the MoLang query system. 13 | /// 14 | /// 15 | /// This is a very simple optimization that can be done to improve performance, it does come with some limitations however.
16 | /// These limitations are:
17 | /// - You need to pass in the object instance to the visitor options, this is because the visitor cannot know what object you are trying to access.
18 | /// - You cannot access properties or functions that are not directly on the object, for example: query.myThing.blabla will not work, but query.blabla will work.
19 | ///
20 | public class InteropOptimizationVisitor : ExpressionVisitor 21 | { 22 | private readonly InteropOptimizationVisitorOptions _options; 23 | 24 | public InteropOptimizationVisitor(InteropOptimizationVisitorOptions options) 25 | { 26 | options.Validate(); 27 | 28 | _options = options; 29 | } 30 | 31 | /// 32 | public override IExpression OnVisit(ExpressionTraverser traverser, IExpression expression) 33 | { 34 | switch (expression) 35 | { 36 | case FuncCallExpression functionCall: 37 | return _options.OptimizeFunctionCalls ? TryOptimizeFunctionCall(functionCall) : functionCall; 38 | 39 | case NameExpression nameExpression: 40 | return _options.OptimizeVariableAccess ? TryOptimizeNameExpression(nameExpression) : nameExpression; 41 | 42 | case AssignExpression assignExpression: 43 | return _options.OptimizeVariableAssignment ? TryOptimizeAssignExpression(assignExpression) : assignExpression; 44 | 45 | default: 46 | return expression; 47 | } 48 | } 49 | 50 | private IExpression TryOptimizeAssignExpression(AssignExpression expression) 51 | { 52 | //Optimize variable assignment to call the method directly. 53 | var variableToAccess = expression.Variable; 54 | 55 | if (variableToAccess is NameExpression nameExpression) 56 | { 57 | var name = nameExpression.Name.Value; 58 | var interopEntry = _options.InteropEntries.FirstOrDefault(x => x.Name == name); 59 | 60 | if (interopEntry == null) 61 | return expression; 62 | 63 | if (TryGetProperty(interopEntry.Cache, nameExpression.Name.Next, out var valueAccessor)) 64 | { 65 | return new OptimizedAssignExpression(interopEntry.Instance, valueAccessor, expression.Expression); 66 | } 67 | } 68 | 69 | return expression; 70 | } 71 | 72 | private IExpression TryOptimizeNameExpression(NameExpression expression) 73 | { 74 | //Optimize variable access to call the method directly. 75 | 76 | var name = expression.Name.Value; 77 | var interopEntry = _options.InteropEntries.FirstOrDefault(x => x.Name == name); 78 | 79 | if (interopEntry == null) 80 | return expression; 81 | 82 | if (TryGetProperty(interopEntry.Cache, expression.Name.Next, out var valueAccessor)) 83 | return new OptimizedNameExpression(interopEntry.Instance, valueAccessor); 84 | 85 | return expression; 86 | } 87 | 88 | private IExpression TryOptimizeFunctionCall(FuncCallExpression expression) 89 | { 90 | //Optimize function call to call the method directly. 91 | var name = expression.Name.Value; 92 | var interopEntry = _options.InteropEntries.FirstOrDefault(x => x.Name == name); 93 | 94 | if (interopEntry == null) 95 | return expression; 96 | 97 | if (TryGetFunction(interopEntry.Cache, expression.Name.Next, out var function)) 98 | return new OptimizedFunctionCallExpression(interopEntry.Instance, function, expression.Parameters); 99 | 100 | return expression; 101 | } 102 | 103 | private bool TryGetFunction(PropertyCache cache, MoPath path, out Func function) 104 | { 105 | if (path.HasChildren) 106 | { 107 | //Possibly trying to access another level in, this is not currently supported. 108 | function = null; 109 | return false; 110 | } 111 | 112 | if (cache.Functions.TryGetValue(path.Value, out var f)) 113 | { 114 | function = f; 115 | return true; 116 | } 117 | 118 | function = null; 119 | return false; 120 | } 121 | 122 | private bool TryGetProperty(PropertyCache cache, MoPath path, out ValueAccessor valueAccessor) 123 | { 124 | if (path.HasChildren) 125 | { 126 | //Possibly trying to access another level in, this is not currently supported. 127 | valueAccessor = null; 128 | return false; 129 | } 130 | 131 | if (cache.Properties.TryGetValue(path.Value, out var f)) 132 | { 133 | valueAccessor = f; 134 | return true; 135 | } 136 | 137 | valueAccessor = null; 138 | return false; 139 | } 140 | } 141 | 142 | public class OptimizedAssignExpression : Expression 143 | { 144 | private readonly object _instance; 145 | private readonly ValueAccessor _valueAccessor; 146 | private readonly IExpression _expressionExpression; 147 | 148 | public OptimizedAssignExpression(object instance, ValueAccessor valueAccessor, IExpression expressionExpression) 149 | { 150 | _instance = instance; 151 | _valueAccessor = valueAccessor; 152 | _expressionExpression = expressionExpression; 153 | } 154 | 155 | /// 156 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 157 | { 158 | _valueAccessor.Set(_instance, _expressionExpression.Evaluate(scope, environment)); 159 | 160 | return DoubleValue.Zero; 161 | } 162 | } 163 | 164 | public class OptimizedFunctionCallExpression : Expression 165 | { 166 | private readonly object _instance; 167 | private readonly Func _function; 168 | 169 | public OptimizedFunctionCallExpression(object instance, Func function, IExpression[] args) : base(args) 170 | { 171 | _instance = instance; 172 | _function = function; 173 | } 174 | 175 | /// 176 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 177 | { 178 | var arguments = new IMoValue[Parameters.Length]; 179 | 180 | for (int i = 0; i < arguments.Length; i++) 181 | { 182 | arguments[i] = Parameters[i].Evaluate(scope, environment); 183 | } 184 | 185 | return _function.Invoke(_instance, new MoParams(arguments)); 186 | } 187 | } 188 | 189 | public class OptimizedNameExpression : Expression 190 | { 191 | private readonly object _instance; 192 | private readonly ValueAccessor _valueAccessor; 193 | 194 | public OptimizedNameExpression(object instance, ValueAccessor valueAccessor) : base() 195 | { 196 | _instance = instance; 197 | _valueAccessor = valueAccessor; 198 | } 199 | 200 | /// 201 | public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment) 202 | { 203 | return _valueAccessor.Get(_instance); 204 | } 205 | } 206 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Visitors/InteropOptimization/InteropOptimizationVisitorOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ConcreteMC.MolangSharp.Runtime.Struct.Interop; 3 | 4 | namespace ConcreteMC.MolangSharp.Parser.Visitors.InteropOptimization 5 | { 6 | public class InteropEntry 7 | { 8 | public string Name { get; } 9 | public PropertyCache Cache { get; } 10 | public object Instance { get; } 11 | 12 | /// 13 | /// 14 | /// 15 | /// The name of the struct 16 | /// The struct instance 17 | public InteropEntry(string name, object instance) 18 | { 19 | Name = name; 20 | Cache = new PropertyCache(instance.GetType()); 21 | Instance = instance; 22 | } 23 | } 24 | 25 | public class InteropOptimizationVisitorOptions 26 | { 27 | public InteropEntry[] InteropEntries { get; set; } = Array.Empty(); 28 | 29 | /// 30 | /// Optimize variable access (MoProperty) 31 | /// 32 | public bool OptimizeVariableAccess { get; set; } = true; 33 | 34 | /// 35 | /// Optimize variable assignment (MoProperty) 36 | /// 37 | public bool OptimizeVariableAssignment { get; set; } = true; 38 | 39 | /// 40 | /// Optimize function calls (MoFunction) 41 | /// 42 | public bool OptimizeFunctionCalls { get; set; } = true; 43 | 44 | internal void Validate() 45 | { 46 | if (InteropEntries == null) 47 | throw new ArgumentNullException(nameof(InteropEntries)); 48 | 49 | if (InteropEntries.Length == 0) 50 | throw new ArgumentException("InteropEntries cannot be empty", nameof(InteropEntries)); 51 | 52 | if (!OptimizeVariableAccess && !OptimizeVariableAssignment && !OptimizeFunctionCalls) 53 | throw new ArgumentException("At least one optimization must be enabled", nameof(OptimizeVariableAccess)); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/MolangSharp/Parser/Visitors/MathOptimizationVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using ConcreteMC.MolangSharp.Parser.Expressions; 4 | using ConcreteMC.MolangSharp.Runtime; 5 | 6 | namespace ConcreteMC.MolangSharp.Parser.Visitors 7 | { 8 | /// 9 | /// Optimizes expressions by pre-calculating constant maths 10 | /// 11 | public class MathOptimizationVisitor : ExpressionVisitor 12 | { 13 | private static MoScope _scope = new MoScope(new MoLangRuntime()); 14 | private static MoLangEnvironment _environment = new MoLangEnvironment(); 15 | 16 | public override IExpression OnVisit(ExpressionTraverser traverser, IExpression expression) 17 | { 18 | return Visit(expression); 19 | } 20 | 21 | private static IExpression Visit(IExpression expression) 22 | { 23 | if (expression is FuncCallExpression nameExpression && nameExpression.Name.Value.Equals( 24 | "math", StringComparison.InvariantCultureIgnoreCase)) 25 | { 26 | return TryOptimizeMathFunction(nameExpression); 27 | } 28 | 29 | if (expression is BinaryOpExpression binaryOp) 30 | { 31 | return TryOptimize(binaryOp); 32 | } 33 | 34 | return expression; 35 | } 36 | 37 | private static IExpression TryOptimizeMathFunction(FuncCallExpression expression) 38 | { 39 | for (int i = 0; i < expression.Parameters.Length; i++) 40 | { 41 | expression.Parameters[i] = Visit(expression.Parameters[i]); 42 | } 43 | 44 | if (expression.Parameters.All(x => x is NumberExpression)) 45 | { 46 | var eval = expression.Evaluate(_scope, _environment); 47 | 48 | return new NumberExpression(eval); 49 | } 50 | 51 | return expression; 52 | } 53 | 54 | private static IExpression TryOptimize(BinaryOpExpression expression) 55 | { 56 | if (expression.Left is BinaryOpExpression l) 57 | expression.Left = TryOptimize(l); 58 | 59 | if (expression.Right is BinaryOpExpression r) 60 | expression.Right = TryOptimize(r); 61 | 62 | if (expression.Left is NumberExpression && expression.Right is NumberExpression) 63 | { 64 | //Can be pre-calculated! 65 | var eval = expression.Evaluate(_scope, _environment); 66 | 67 | return new NumberExpression(eval); 68 | } 69 | 70 | return expression; 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /src/MolangSharp/Runtime/Exceptions/MoLangRuntimeException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using ConcreteMC.MolangSharp.Parser; 4 | 5 | namespace ConcreteMC.MolangSharp.Runtime.Exceptions 6 | { 7 | /// 8 | /// Represents an error that occured during the execution of a MoLang expression 9 | /// 10 | public class MoLangRuntimeException : Exception 11 | { 12 | /// 13 | /// Contains a trace to where the exception occured in a MoLang expression. 14 | /// 15 | public string MolangTrace { get; } 16 | 17 | /// 18 | /// Initializes a new instance of the MoLangRuntimeException class with a specified error message. 19 | /// 20 | /// The error message 21 | public MoLangRuntimeException(string message) : this(message, null) { } 22 | 23 | /// 24 | /// Initializes a new instance of the MoLangRuntimeException class with a specified error message and a reference to the inner exception that is the cause of this exception. 25 | /// 26 | /// The error message 27 | /// A reference to the inner exception that is the cause of this exception 28 | public MoLangRuntimeException(string message, Exception baseException) : base(message, baseException) 29 | { 30 | MolangTrace = "Unknown"; 31 | } 32 | 33 | /// 34 | /// Initializes a new instance of the MoLangRuntimeException class with a reference to the expression that the error occured at, an error message and a reference to the inner exception that is the cause of this exception. 35 | /// 36 | /// The expression that this exception occured at 37 | /// The error message 38 | /// A reference to the inner exception that is the cause of this exception 39 | public MoLangRuntimeException(IExpression expression, string message, Exception baseException) : base( 40 | message, baseException) 41 | { 42 | if (MoLangRuntimeConfiguration.UseMoLangStackTrace) 43 | { 44 | StringBuilder sb = new StringBuilder(); 45 | 46 | do 47 | { 48 | if (expression.Meta?.Token?.Position != null) 49 | { 50 | var token = expression.Meta.Token; 51 | var tokenPosition = token.Position; 52 | 53 | sb.Append( 54 | $"at <{tokenPosition.LineNumber}:{tokenPosition.Index}> near {token.Type.TypeName} \"{token.Text}\""); 55 | //var frame = new StackFrame(null, tokenPosition.LineNumber, tokenPosition.Index); 56 | 57 | // frames.Add(frame); 58 | } 59 | 60 | expression = expression.Meta.Parent; 61 | } while (expression?.Meta?.Parent != null); 62 | 63 | MolangTrace = sb.ToString(); 64 | } 65 | //st.GetFrames() 66 | } 67 | 68 | //private IEnumerable 69 | } 70 | } -------------------------------------------------------------------------------- /src/MolangSharp/Runtime/MoLangEnvironment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ConcreteMC.MolangSharp.Runtime.Exceptions; 4 | using ConcreteMC.MolangSharp.Runtime.Struct; 5 | using ConcreteMC.MolangSharp.Runtime.Value; 6 | using ConcreteMC.MolangSharp.Utils; 7 | 8 | namespace ConcreteMC.MolangSharp.Runtime 9 | { 10 | /// 11 | /// Provides an execution environment for the 12 | /// 13 | /// 14 | /// An example of a MoLangEnvironment would be a minecraft entity. 15 | /// 16 | public class MoLangEnvironment : IMoValue 17 | { 18 | /// 19 | public object Value => Structs; 20 | 21 | /// 22 | /// The available root paths 23 | /// 24 | /// 25 | /// Contains the following root structs by default:
26 | /// math.
27 | /// temp.
28 | /// variable.
29 | /// array.
30 | /// context. 31 | ///
32 | public Dictionary Structs { get; } = 33 | new Dictionary(StringComparer.OrdinalIgnoreCase); 34 | 35 | /// 36 | /// The value that should be returned when an expression tries to access "this" 37 | /// 38 | public IMoValue ThisVariable { get; set; } = DoubleValue.Zero; 39 | 40 | /// 41 | /// Creates a new instance of the MoLangEnvironment class 42 | /// 43 | public MoLangEnvironment() 44 | { 45 | Structs.TryAdd("math", MoLangMath.Library); 46 | Structs.TryAdd("temp", new VariableStruct()); 47 | Structs.TryAdd("variable", new VariableStruct()); 48 | Structs.TryAdd("array", new VariableArrayStruct()); 49 | 50 | Structs.TryAdd("context", new ContextStruct()); 51 | } 52 | 53 | /// 54 | /// Get a MoLang variable by it's path 55 | /// 56 | /// The path to access 57 | /// The variable at specified path 58 | public IMoValue GetValue(MoPath path) 59 | { 60 | return GetValue(path, MoParams.Empty); 61 | } 62 | 63 | /// 64 | /// Get a MoLang variable by it's path and specified parameters 65 | /// 66 | /// The path to access 67 | /// The variable at specified path 68 | public IMoValue GetValue(MoPath path, MoParams param) 69 | { 70 | if (!Structs.TryGetValue(path.Value, out var v)) 71 | { 72 | if (MoLangRuntimeConfiguration.UseDummyValuesInsteadOfExceptions) 73 | return DoubleValue.Zero; 74 | 75 | throw new MoLangRuntimeException($"Invalid path: {path.Path}"); 76 | } 77 | 78 | return v.Get(path.Next, param); 79 | } 80 | 81 | /// 82 | /// Set a MoLang variable by its path 83 | /// 84 | /// 85 | /// 86 | /// 87 | public void SetValue(MoPath path, IMoValue value) 88 | { 89 | if (!Structs.TryGetValue(path.Value, out var v)) 90 | { 91 | throw new MoLangRuntimeException($"Invalid path: {path.Path}", null); 92 | } 93 | 94 | try 95 | { 96 | v.Set(path.Next, value); 97 | } 98 | catch (Exception ex) 99 | { 100 | throw new MoLangRuntimeException($"Cannot set value on struct: {path}", ex); 101 | } 102 | } 103 | 104 | /// 105 | public bool Equals(IMoValue b) 106 | { 107 | return Equals((object) b); 108 | } 109 | } 110 | } -------------------------------------------------------------------------------- /src/MolangSharp/Runtime/MoLangMath.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ConcreteMC.MolangSharp.Attributes; 4 | using ConcreteMC.MolangSharp.Runtime.Struct; 5 | using ConcreteMC.MolangSharp.Runtime.Value; 6 | 7 | namespace ConcreteMC.MolangSharp.Runtime 8 | { 9 | /// 10 | /// The default Math implementations for the MoLang runtime 11 | /// 12 | public static class MoLangMath 13 | { 14 | /// 15 | /// The Math library 16 | /// 17 | public static readonly QueryStruct Library = new QueryStruct( 18 | new Dictionary>(StringComparer.OrdinalIgnoreCase) 19 | { 20 | {"abs", param => Math.Abs(param.GetDouble(0))}, 21 | {"acos", param => Math.Acos(param.GetDouble(0))}, 22 | {"sin", param => Math.Sin(param.GetDouble(0) * (Math.PI / 180d))}, 23 | {"asin", param => Math.Asin(param.GetDouble(0))}, 24 | {"atan", param => Math.Atan(param.GetDouble(0))}, 25 | {"atan2", param => Math.Atan2(param.GetDouble(0), param.GetDouble(1))}, 26 | {"ceil", param => Math.Ceiling(param.GetDouble(0))}, 27 | {"clamp", param => Math.Min(param.GetDouble(1), Math.Max(param.GetDouble(0), param.GetDouble(2)))}, 28 | {"cos", param => Math.Cos(param.GetDouble(0) * (Math.PI / 180d))}, 29 | {"die_roll", param => DieRoll(param.GetDouble(0), param.GetDouble(1), param.GetDouble(2))}, 30 | {"die_roll_integer", param => DieRollInt(param.GetInt(0), param.GetInt(1), param.GetInt(2))}, 31 | {"exp", param => Math.Exp(param.GetDouble(0))}, 32 | {"mod", param => param.GetDouble(0) % param.GetDouble(1)}, 33 | {"floor", param => Math.Floor(param.GetDouble(0))}, 34 | {"hermite_blend", param => HermiteBlend(param.GetInt(0))}, 35 | {"lerp", param => Lerp(param.GetDouble(0), param.GetDouble(1), param.GetDouble(2))}, 36 | {"lerp_rotate", param => LerpRotate(param.GetDouble(0), param.GetDouble(1), param.GetDouble(2))}, 37 | {"ln", param => Math.Log(param.GetDouble(0))}, 38 | {"max", param => Math.Max(param.GetDouble(0), param.GetDouble(1))}, 39 | {"min", param => Math.Min(param.GetDouble(0), param.GetDouble(1))}, 40 | {"pi", param => Math.PI}, 41 | {"pow", param => Math.Pow(param.GetDouble(0), param.GetDouble(1))}, 42 | {"random", param => Random(param.GetDouble(0), param.GetDouble(1))}, 43 | {"random_integer", param => RandomInt(param.GetInt(0), param.GetInt(1))}, 44 | {"round", param => Math.Round(param.GetDouble(0))}, 45 | {"sqrt", param => Math.Sqrt(param.GetDouble(0))}, 46 | {"trunc", param => Math.Floor(param.GetDouble(0))}, 47 | }); 48 | 49 | public static double Random(double low, double high) 50 | { 51 | return low + _random.NextDouble() * (high - low); 52 | } 53 | 54 | private static Random _random = new Random(); 55 | 56 | public static int RandomInt(int low, int high) 57 | { 58 | return _random.Next(low, high); 59 | } 60 | 61 | public static double DieRoll(double num, double low, double high) 62 | { 63 | int i = 0; 64 | double total = 0; 65 | while (i++ < num) total += Random(low, high); 66 | 67 | return total; 68 | } 69 | 70 | public static int DieRollInt(int num, int low, int high) 71 | { 72 | int i = 0; 73 | int total = 0; 74 | while (i++ < num) total += RandomInt(low, high); 75 | 76 | return total; 77 | } 78 | 79 | public static int HermiteBlend(int value) 80 | { 81 | return (3 * value) ^ (2 - 2 * value) ^ 3; 82 | } 83 | 84 | public static double Lerp(double start, double end, double amount) 85 | { 86 | amount = Math.Max(0, Math.Min(1, amount)); 87 | 88 | return start + (end - start) * amount; 89 | } 90 | 91 | public static double LerpRotate(double start, double end, double amount) 92 | { 93 | start = Radify(start); 94 | end = Radify(end); 95 | 96 | if (start > end) 97 | { 98 | (start, end) = (end, start); 99 | } 100 | 101 | if (end - start > 180) 102 | { 103 | return Radify(end + amount * (360 - (end - start))); 104 | } 105 | 106 | return start + amount * (end - start); 107 | } 108 | 109 | public static double Radify(double num) 110 | { 111 | return (((num + 180) % 360) + 180) % 360; 112 | } 113 | } 114 | 115 | /// 116 | /// The default Math implementations for the MoLang runtime 117 | /// 118 | public sealed class MoLangMathImpl 119 | { 120 | private static IMoStruct _instance; 121 | public static IMoStruct Library => _instance ??= new InteropStruct(new MoLangMathImpl()); 122 | 123 | private MoLangMathImpl() { } 124 | 125 | [MoFunction("abs")] 126 | public double Abs(double value) => Math.Abs(value); 127 | 128 | [MoFunction("sin")] 129 | public double Sin(double value) => Math.Sin(value * (Math.PI / 180d)); 130 | 131 | [MoFunction("asin")] 132 | public double Asin(double value) => Math.Asin(value); 133 | 134 | [MoFunction("cos")] 135 | public double Cos(double value) => Math.Cos(value * (Math.PI / 180d)); 136 | 137 | [MoFunction("acos")] 138 | public double Acos(double value) => Math.Acos(value); 139 | 140 | [MoFunction("atan")] 141 | public double Atan(double value) => Math.Atan(value); 142 | 143 | [MoFunction("atan2")] 144 | public double Atan2(double y, double x) => Math.Atan2(y, x); 145 | 146 | [MoFunction("ceil")] 147 | public double Ceiling(double value) => Math.Ceiling(value); 148 | 149 | [MoFunction("clamp")] 150 | public double Clamp(double value, double min, double max) => Math.Clamp(value, min, max); 151 | 152 | [MoFunction("die_roll")] 153 | public double DieRoll(double num, double low, double high) 154 | { 155 | int i = 0; 156 | double total = 0; 157 | while (i++ < num) total += Random(low, high); 158 | 159 | return total; 160 | } 161 | 162 | [MoFunction("die_roll_integer")] 163 | public int DieRollInt(int num, int low, int high) 164 | { 165 | int i = 0; 166 | int total = 0; 167 | while (i++ < num) total += RandomInt(low, high); 168 | 169 | return total; 170 | } 171 | 172 | [MoFunction("exp")] 173 | public double Exp(double value) => Math.Exp(value); 174 | 175 | [MoFunction("mod")] 176 | public double Modulus(double x, double y) => x % y; 177 | 178 | [MoFunction("floor")] 179 | public double Floor(double value) => Math.Floor(value); 180 | 181 | [MoFunction("hermite_blend")] 182 | public int HermiteBlend(int value) => (3 * value) ^ (2 - 2 * value) ^ 3; 183 | 184 | [MoFunction("lerp")] 185 | public double Lerp(double start, double end, double amount) 186 | { 187 | amount = Math.Max(0, Math.Min(1, amount)); 188 | 189 | return start + (end - start) * amount; 190 | } 191 | 192 | [MoFunction("lerp_rotate")] 193 | public double LerpRotate(double start, double end, double amount) 194 | { 195 | start = Radify(start); 196 | end = Radify(end); 197 | 198 | if (start > end) 199 | { 200 | (start, end) = (end, start); 201 | } 202 | 203 | if (end - start > 180) 204 | { 205 | return Radify(end + amount * (360 - (end - start))); 206 | } 207 | 208 | return start + amount * (end - start); 209 | } 210 | 211 | [MoFunction("ln")] 212 | public double Log(double value) => Math.Log(value); 213 | 214 | [MoFunction("max")] 215 | public double Max(double value1, double value2) => Math.Max(value1, value2); 216 | 217 | [MoFunction("min")] 218 | public double Min(double value1, double value2) => Math.Min(value1, value2); 219 | 220 | [MoFunction("pi")] 221 | public double PiFunc() => Math.PI; 222 | 223 | [MoProperty("pi")] public double PI => Math.PI; 224 | 225 | [MoFunction("pow")] 226 | public double Pow(double x, double y) => Math.Pow(x, y); 227 | 228 | [MoFunction("random")] 229 | public double Random(double low, double high) 230 | { 231 | return low + _random.NextDouble() * (high - low); 232 | } 233 | 234 | [MoFunction("random_integer")] 235 | public int RandomInt(int low, int high) 236 | { 237 | return _random.Next(low, high); 238 | } 239 | 240 | [MoFunction("round")] 241 | public double Round(double value) => Math.Round(value); 242 | 243 | [MoFunction("sqrt")] 244 | public double Sqrt(double value) => Math.Sqrt(value); 245 | 246 | [MoFunction("trunc")] 247 | public double Truncate(double value) => Math.Floor(value); 248 | 249 | public double Radify(double num) 250 | { 251 | return (((num + 180) % 360) + 180) % 360; 252 | } 253 | 254 | private Random _random = new Random(); 255 | } 256 | } -------------------------------------------------------------------------------- /src/MolangSharp/Runtime/MoLangRuntime.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using ConcreteMC.MolangSharp.Parser; 3 | using ConcreteMC.MolangSharp.Runtime.Struct; 4 | using ConcreteMC.MolangSharp.Runtime.Value; 5 | 6 | namespace ConcreteMC.MolangSharp.Runtime 7 | { 8 | /// 9 | /// The runtime used to execute an array of 10 | /// 11 | public sealed class MoLangRuntime 12 | { 13 | /// 14 | /// The environment associated with this runtime instance 15 | /// 16 | public MoLangEnvironment Environment { get; } 17 | 18 | /// 19 | /// Create a new instance of MoLangRuntime with a new 20 | /// 21 | public MoLangRuntime() : this(new MoLangEnvironment()) { } 22 | 23 | /// 24 | /// Create a new instance of MoLangRuntime 25 | /// 26 | /// 27 | /// The environment used by this runtime instance 28 | /// 29 | public MoLangRuntime(MoLangEnvironment environment) 30 | { 31 | Environment = environment; 32 | } 33 | 34 | /// 35 | /// Evaluates the expressions provided and returns the resulting value (if any) or 36 | /// 37 | /// The expressions to evaluate 38 | /// 39 | /// The value returned by the expression (if any) or 40 | /// 41 | public IMoValue Execute(IExpression expressions) 42 | { 43 | return Execute(expressions, null); 44 | } 45 | 46 | /// 47 | /// Evaluates the expressions provided and returns the resulting value (if any) or 48 | /// 49 | /// The expression to evaluate 50 | /// The context to use 51 | /// 52 | /// The value returned by the expression (if any) or 53 | /// 54 | public IMoValue Execute(IExpression expression, IDictionary context) 55 | { 56 | if (expression == null) 57 | return DoubleValue.Zero; 58 | 59 | if (context != null && Environment.Structs.TryGetValue("context", out var cont) && cont is ContextStruct contextStruct) 60 | { 61 | contextStruct.Container = context; 62 | } 63 | 64 | IMoValue result = null; 65 | var scope = new MoScope(this); 66 | result = expression.Evaluate(scope, Environment); 67 | 68 | Environment.Structs["temp"].Clear(); 69 | 70 | return result ?? DoubleValue.Zero; 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /src/MolangSharp/Runtime/MoLangRuntimeConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace ConcreteMC.MolangSharp.Runtime 2 | { 3 | /// 4 | /// Global MoLang Runtime configuration options 5 | /// 6 | public static class MoLangRuntimeConfiguration 7 | { 8 | /// 9 | /// If true (default) allows you to locate an exception in the MoLang script when a runtime exception is thrown. 10 | /// 11 | public static bool UseMoLangStackTrace { get; set; } = true; 12 | 13 | /// 14 | /// If true, returns dummy values instead of throwing an exception when a variable is not found during runtime execution. (Experimental) 15 | /// 16 | public static bool UseDummyValuesInsteadOfExceptions { get; set; } = false; 17 | } 18 | } -------------------------------------------------------------------------------- /src/MolangSharp/Runtime/MoParams.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using ConcreteMC.MolangSharp.Runtime.Exceptions; 3 | using ConcreteMC.MolangSharp.Runtime.Struct; 4 | using ConcreteMC.MolangSharp.Runtime.Value; 5 | 6 | namespace ConcreteMC.MolangSharp.Runtime 7 | { 8 | /// 9 | /// Represents the parameters passed to a function by the MoLang expression 10 | /// 11 | public class MoParams 12 | { 13 | public static readonly MoParams Empty = new MoParams(new IMoValue[0]); 14 | 15 | private readonly IMoValue[] _parameters; 16 | 17 | public MoParams(params IMoValue[] param) 18 | { 19 | _parameters = param; 20 | } 21 | 22 | /// 23 | /// Gets the parameter at and returns its value as an 24 | /// 25 | /// The index of the parameter to retrieve 26 | /// 27 | /// The value at specified index 28 | /// 29 | public IMoValue Get(int index) 30 | { 31 | return _parameters[index]; 32 | } 33 | 34 | /// 35 | /// Get the typed value of a parameter at specified 36 | /// 37 | /// The index of the 38 | /// The expected type 39 | /// 40 | /// The parameter passed at 41 | /// 42 | /// 43 | public T Get(int index) 44 | { 45 | IMoValue obj = _parameters[index]; 46 | 47 | if (obj == null) 48 | throw new MoLangRuntimeException( 49 | $"MoParams: Expected parameter type of {typeof(T).Name} got null", null); 50 | 51 | if (obj?.GetType() == typeof(T)) 52 | { 53 | return (T) obj; 54 | } 55 | else 56 | { 57 | throw new MoLangRuntimeException( 58 | "MoParams: Expected parameter type of " + typeof(T).Name + ", " + obj.GetType().Name + " given.", 59 | null); 60 | } 61 | } 62 | 63 | /// 64 | /// Check if the index as specified by is available 65 | /// 66 | /// The index to check availability for 67 | /// 68 | /// True if the index is available 69 | /// 70 | public bool Contains(int index) 71 | { 72 | return _parameters.Length >= index + 1; 73 | } 74 | 75 | /// 76 | /// Gets the parameter at and returns its value as an 77 | /// 78 | /// The index of the parameter to retrieve 79 | /// 80 | /// The value at specified index 81 | /// 82 | public int GetInt(int index) 83 | { 84 | return (int) GetDouble(index); 85 | } 86 | 87 | /// 88 | /// Gets the parameter at and returns its value as a 89 | /// 90 | /// The index of the parameter to retrieve 91 | /// 92 | /// The value at specified index 93 | /// 94 | public double GetDouble(int index) 95 | { 96 | return Get(index).Value; 97 | } 98 | 99 | /// 100 | /// Gets the parameter at and returns its value as a 101 | /// 102 | /// The index of the parameter to retrieve 103 | /// 104 | /// The value at specified index 105 | /// 106 | public IMoStruct GetStruct(int index) 107 | { 108 | return Get(index); 109 | } 110 | 111 | /// 112 | /// Gets the parameter at and returns its value as a 113 | /// 114 | /// The index of the parameter to retrieve 115 | /// 116 | /// The value at specified index 117 | /// 118 | public string GetString(int index) 119 | { 120 | return Get(index).Value; 121 | } 122 | 123 | /// 124 | /// Gets the parameter at and returns its value as a 125 | /// 126 | /// The index of the parameter to retrieve 127 | /// 128 | /// The value at specified index 129 | /// 130 | public MoLangEnvironment GetEnv(int index) 131 | { 132 | return Get(index); 133 | } 134 | 135 | /// 136 | /// Get all parameters passed as an array 137 | /// 138 | /// 139 | /// An array of 140 | /// 141 | public IMoValue[] GetParams() 142 | { 143 | return _parameters; 144 | } 145 | 146 | /// 147 | public override string ToString() 148 | { 149 | var values = GetParams(); 150 | 151 | if (values != null && values.Length > 0) 152 | { 153 | return string.Join(',', values.Select(x => x.AsString())); 154 | } 155 | 156 | return base.ToString(); 157 | } 158 | } 159 | } -------------------------------------------------------------------------------- /src/MolangSharp/Runtime/MoScope.cs: -------------------------------------------------------------------------------- 1 | using ConcreteMC.MolangSharp.Runtime.Value; 2 | 3 | namespace ConcreteMC.MolangSharp.Runtime 4 | { 5 | public class MoScope 6 | { 7 | public bool IsBreak { get; set; } = false; 8 | public bool IsContinue { get; set; } = false; 9 | public IMoValue ReturnValue { get; set; } = null; 10 | 11 | public MoLangRuntime Runtime { get; set; } = null; 12 | 13 | public MoScope(MoLangRuntime runtime) 14 | { 15 | Runtime = runtime; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/MolangSharp/Runtime/Struct/ArrayStruct.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using ConcreteMC.MolangSharp.Runtime.Exceptions; 5 | using ConcreteMC.MolangSharp.Runtime.Value; 6 | using ConcreteMC.MolangSharp.Utils; 7 | 8 | namespace ConcreteMC.MolangSharp.Runtime.Struct 9 | { 10 | /// 11 | /// Represents an array of values 12 | /// 13 | public class ArrayStruct : IMoStruct 14 | { 15 | private IMoValue[] _array; 16 | 17 | /// 18 | /// Initializes a new instance of the ArrayStruct class with no values. 19 | /// 20 | public ArrayStruct() 21 | { 22 | _array = new IMoValue[0]; 23 | } 24 | 25 | /// 26 | /// Initializes a new instance of the ArrayStruct class. 27 | /// 28 | /// The values this ArrayStruct contains 29 | public ArrayStruct(IEnumerable values) 30 | { 31 | _array = values.ToArray(); 32 | } 33 | 34 | /// 35 | /// Indexes the ArrayStruct 36 | /// 37 | /// The index to get/set the value of 38 | public IMoValue this[int index] 39 | { 40 | get 41 | { 42 | if (index >= _array.Length) 43 | return DoubleValue.Zero; 44 | 45 | return _array[index % _array.Length]; 46 | } 47 | set 48 | { 49 | if (_array.Length == 0) 50 | return; 51 | 52 | _array[index % _array.Length] = value; 53 | } 54 | } 55 | 56 | /// 57 | public void Set(MoPath key, IMoValue value) 58 | { 59 | if (int.TryParse(key.Value, out int index)) 60 | { 61 | this[index] = value; 62 | } 63 | } 64 | 65 | /// 66 | public IMoValue Get(MoPath key, MoParams parameters) 67 | { 68 | if (int.TryParse(key.Value, out int index)) 69 | { 70 | return this[index]; 71 | } 72 | 73 | throw new MoLangRuntimeException($"Invalid path for array access: {key.Path.ToString()}"); 74 | } 75 | 76 | /// 77 | public void Clear() 78 | { 79 | throw new NotSupportedException("Cannot clear an ArrayStruct"); 80 | } 81 | 82 | /// 83 | public object Value => _array; 84 | } 85 | 86 | public class VariableArrayStruct : VariableStruct 87 | { 88 | /// 89 | protected override IMoStruct CreateNew() 90 | { 91 | return new ArrayStruct(); 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /src/MolangSharp/Runtime/Struct/ContextStruct.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ConcreteMC.MolangSharp.Runtime.Value; 4 | using ConcreteMC.MolangSharp.Utils; 5 | 6 | namespace ConcreteMC.MolangSharp.Runtime.Struct 7 | { 8 | public class ContextStruct : VariableStruct 9 | { 10 | internal IDictionary Container 11 | { 12 | get 13 | { 14 | return Map; 15 | } 16 | set 17 | { 18 | Map = value; 19 | } 20 | } 21 | 22 | public ContextStruct() : base() { } 23 | 24 | public ContextStruct(IEnumerable> values) : base(values) { } 25 | 26 | /// 27 | public override void Set(MoPath key, IMoValue value) 28 | { 29 | throw new NotSupportedException("Read-only context"); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/MolangSharp/Runtime/Struct/IMoStruct.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | using ConcreteMC.MolangSharp.Utils; 4 | 5 | namespace ConcreteMC.MolangSharp.Runtime.Struct 6 | { 7 | /// 8 | /// The interface used for all MoLang containers/structs 9 | /// 10 | public interface IMoStruct : IMoValue 11 | { 12 | /// 13 | /// Assign a value to a property 14 | /// 15 | /// The path of the property to modify 16 | /// The value to set 17 | void Set(MoPath key, IMoValue value); 18 | 19 | /// 20 | /// Get the value of a property 21 | /// 22 | /// The path of the property to get 23 | /// The parameters used to retrieve the value 24 | /// The value of the property retrieved 25 | IMoValue Get(MoPath key, MoParams parameters); 26 | 27 | /// 28 | /// Clears the struct 29 | /// 30 | void Clear(); 31 | 32 | /// 33 | bool IEquatable.Equals(IMoValue other) 34 | { 35 | return this.Equals((object) other); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/MolangSharp/Runtime/Struct/Interop/FieldAccessor.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Runtime.Struct.Interop 5 | { 6 | public class FieldAccessor : ValueAccessor 7 | { 8 | private readonly FieldInfo _fieldInfo; 9 | 10 | public FieldAccessor(FieldInfo fieldInfo) 11 | { 12 | _fieldInfo = fieldInfo; 13 | } 14 | 15 | public override bool CanRead => true; 16 | public override bool CanWrite => !_fieldInfo.IsInitOnly; 17 | 18 | /// 19 | public override IMoValue Get(object instance) 20 | { 21 | var value = _fieldInfo.GetValue(instance); 22 | 23 | return value is IMoValue moValue ? moValue : MoValue.FromObject(value); 24 | } 25 | 26 | /// 27 | public override void Set(object instance, IMoValue value) 28 | { 29 | _fieldInfo.SetValue(instance, value); 30 | InvokeChanged(); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/MolangSharp/Runtime/Struct/Interop/PropertyAccessor.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Runtime.Struct.Interop 5 | { 6 | public class PropertyAccessor : ValueAccessor 7 | { 8 | private readonly PropertyInfo _propertyInfo; 9 | 10 | public PropertyAccessor(PropertyInfo propertyInfo) 11 | { 12 | _propertyInfo = propertyInfo; 13 | } 14 | 15 | public override bool CanRead => _propertyInfo.CanRead; 16 | public override bool CanWrite => _propertyInfo.CanWrite; 17 | 18 | /// 19 | public override IMoValue Get(object instance) 20 | { 21 | var value = _propertyInfo.GetValue(instance); 22 | 23 | return value is IMoValue moValue ? moValue : MoValue.FromObject(value); 24 | } 25 | 26 | /// 27 | public override void Set(object instance, IMoValue value) 28 | { 29 | var propType = _propertyInfo.PropertyType; 30 | 31 | if (propType == typeof(double)) 32 | { 33 | _propertyInfo.SetValue(instance, value.AsDouble()); 34 | 35 | return; 36 | } 37 | else if (propType == typeof(float)) 38 | { 39 | _propertyInfo.SetValue(instance, value.AsFloat()); 40 | 41 | return; 42 | } 43 | else if (propType == typeof(bool)) 44 | { 45 | _propertyInfo.SetValue(instance, value.AsBool()); 46 | 47 | return; 48 | } 49 | else if (propType == typeof(string)) 50 | { 51 | _propertyInfo.SetValue(instance, value.AsString()); 52 | 53 | return; 54 | } 55 | 56 | _propertyInfo.SetValue(instance, value); 57 | InvokeChanged(); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /src/MolangSharp/Runtime/Struct/Interop/PropertyCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Reflection; 5 | using ConcreteMC.MolangSharp.Attributes; 6 | using ConcreteMC.MolangSharp.Runtime.Value; 7 | 8 | namespace ConcreteMC.MolangSharp.Runtime.Struct.Interop 9 | { 10 | public class PropertyCache 11 | { 12 | public readonly IReadOnlyDictionary Properties; 13 | 14 | public readonly IReadOnlyDictionary> Functions; 15 | 16 | public PropertyCache(Type arg) 17 | { 18 | var properties = new Dictionary(StringComparer.OrdinalIgnoreCase); 19 | var functions = new Dictionary>(StringComparer.OrdinalIgnoreCase); 20 | 21 | ProcessMethods(arg, functions); 22 | ProcessProperties(arg, properties); 23 | 24 | Functions = functions; 25 | Properties = properties; 26 | } 27 | 28 | private static void ProcessMethods(IReflect type, 29 | IDictionary> functions) 30 | { 31 | var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance); 32 | 33 | foreach (var method in methods) 34 | { 35 | var functionAttribute = method.GetCustomAttribute(); 36 | 37 | if (functionAttribute == null) 38 | continue; 39 | 40 | foreach (var name in functionAttribute.Name) 41 | { 42 | if (functions.ContainsKey(name)) 43 | { 44 | Debug.WriteLine($"Duplicate function \'{name}\' in {type.ToString()}"); 45 | 46 | continue; 47 | } 48 | 49 | IMoValue ExecuteMolangFunction(object instance, MoParams mo) 50 | { 51 | var methodParams = method.GetParameters(); 52 | IMoValue value = DoubleValue.Zero; 53 | 54 | object[] parameters = new object[methodParams.Length]; 55 | //List parameters = new List(); 56 | 57 | if (methodParams.Length == 1 && methodParams[0].ParameterType == typeof(MoParams)) 58 | { 59 | parameters[0] = mo; 60 | //parameters.Add(mo); 61 | } 62 | else 63 | { 64 | for (var index = 0; index < methodParams.Length; index++) 65 | { 66 | var parameter = methodParams[index]; 67 | 68 | if (!mo.Contains(index)) 69 | { 70 | if (!parameter.IsOptional) 71 | throw new MissingMethodException($"Missing parameter: {parameter.Name}"); 72 | 73 | break; 74 | } 75 | 76 | var t = parameter.ParameterType; 77 | 78 | if (t == typeof(MoParams)) 79 | { 80 | parameters[index] = mo; //.Add(mo); 81 | } 82 | else if (t == typeof(int)) 83 | { 84 | parameters[index] = mo.GetInt(index); 85 | } 86 | else if (t == typeof(double)) 87 | { 88 | parameters[index] = mo.GetDouble(index); 89 | } 90 | else if (t == typeof(float)) 91 | { 92 | parameters[index] = (float) mo.GetDouble(index); 93 | } 94 | else if (t == typeof(string)) 95 | { 96 | parameters[index] = mo.GetString(index); 97 | } 98 | else if (typeof(IMoStruct).IsAssignableFrom(t)) 99 | { 100 | parameters[index] = mo.GetStruct(index); 101 | } 102 | else if (typeof(MoLangEnvironment).IsAssignableFrom(t)) 103 | { 104 | parameters[index] = mo.GetEnv(index); 105 | } 106 | else 107 | { 108 | throw new Exception("Unknown parameter type."); 109 | } 110 | 111 | //TODO: Continue. 112 | } 113 | } 114 | 115 | var result = method.Invoke(instance, parameters); 116 | 117 | if (result != null) 118 | { 119 | if (result is IMoValue moValue) return moValue; 120 | 121 | return MoValue.FromObject(result); 122 | } 123 | 124 | return value; 125 | } 126 | 127 | functions.Add(name, ExecuteMolangFunction); 128 | } 129 | } 130 | } 131 | 132 | private static void ProcessProperties(IReflect type, IDictionary valueAccessors) 133 | { 134 | var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); 135 | 136 | foreach (var prop in properties) 137 | { 138 | foreach (var functionAttribute in prop.GetCustomAttributes()) 139 | { 140 | if (valueAccessors.ContainsKey(functionAttribute.Name)) 141 | continue; 142 | 143 | var accessor = new PropertyAccessor(prop); 144 | 145 | if (prop.GetCustomAttribute() != null) 146 | accessor.Observable = true; 147 | 148 | valueAccessors.Add(functionAttribute.Name, accessor); 149 | } 150 | } 151 | 152 | var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance); 153 | 154 | foreach (var prop in fields) 155 | { 156 | foreach (var functionAttribute in prop.GetCustomAttributes()) 157 | { 158 | if (valueAccessors.ContainsKey(functionAttribute.Name)) 159 | continue; 160 | 161 | var accessor = new FieldAccessor(prop); 162 | 163 | if (prop.GetCustomAttribute() != null) 164 | accessor.Observable = true; 165 | 166 | valueAccessors.Add(functionAttribute.Name, accessor); 167 | } 168 | } 169 | } 170 | } 171 | } -------------------------------------------------------------------------------- /src/MolangSharp/Runtime/Struct/Interop/ValueAccessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ConcreteMC.MolangSharp.Runtime.Value; 3 | 4 | namespace ConcreteMC.MolangSharp.Runtime.Struct.Interop 5 | { 6 | public abstract class ValueAccessor 7 | { 8 | public event EventHandler ValueChanged; 9 | public bool Observable { get; internal set; } 10 | 11 | public abstract bool CanRead { get; } 12 | public abstract bool CanWrite { get; } 13 | 14 | public abstract IMoValue Get(object instance); 15 | 16 | public abstract void Set(object instance, IMoValue value); 17 | 18 | protected void InvokeChanged() 19 | { 20 | ValueChanged?.Invoke(this, EventArgs.Empty); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/MolangSharp/Runtime/Struct/InteropStruct.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Diagnostics; 4 | using ConcreteMC.MolangSharp.Attributes; 5 | using ConcreteMC.MolangSharp.Runtime.Exceptions; 6 | using ConcreteMC.MolangSharp.Runtime.Struct.Interop; 7 | using ConcreteMC.MolangSharp.Runtime.Value; 8 | using ConcreteMC.MolangSharp.Utils; 9 | 10 | namespace ConcreteMC.MolangSharp.Runtime.Struct 11 | { 12 | /// 13 | /// Provides an interoperability layer around an object 14 | /// 15 | /// 16 | /// Allows MoLang to access fields, properties & functions in a .NET object 17 | /// 18 | /// Any fields or properties that you want to allow access to requires the to be applied. 19 | /// 20 | /// Any functions that should be accessible by MoLang require the to be applied 21 | /// 22 | public class InteropStruct : IMoStruct 23 | { 24 | private object _instance; 25 | private readonly PropertyCache _propertyCache; 26 | 27 | private static readonly ConcurrentDictionary PropertyCaches = 28 | new ConcurrentDictionary(); 29 | 30 | public InteropStruct(object instance) 31 | { 32 | _instance = instance; 33 | 34 | var type = instance.GetType(); 35 | 36 | _propertyCache = PropertyCaches.GetOrAdd(type, t => new PropertyCache(t)); 37 | } 38 | 39 | /// 40 | public object Value => _instance; 41 | 42 | /// 43 | public void Set(MoPath key, IMoValue value) 44 | { 45 | if (!key.HasChildren) 46 | { 47 | if (_propertyCache.Properties.TryGetValue(key.Value, out var accessor)) 48 | { 49 | if (!accessor.CanWrite) 50 | throw new MoLangRuntimeException("Cannot write to ReadOnly property!", null); 51 | 52 | accessor.Set(_instance, value); 53 | 54 | return; 55 | } 56 | 57 | throw new MoLangRuntimeException($"Variable was not a struct: {key}", null); 58 | } 59 | 60 | string main = key.Value; 61 | 62 | if (!string.IsNullOrWhiteSpace(main)) 63 | { 64 | if (!_propertyCache.Properties.TryGetValue(main, out var accessor)) 65 | { 66 | throw new MoLangRuntimeException($"Could not access property: {key}", null); 67 | } 68 | 69 | var container = accessor.Get(_instance); 70 | 71 | if (container is IMoStruct moStruct) 72 | { 73 | moStruct.Set(key.Next, value); 74 | } 75 | else 76 | { 77 | throw new MoLangRuntimeException($"Variable was not a struct: {key}", null); 78 | } 79 | } 80 | } 81 | 82 | private bool ExecuteGet(MoPath property, string main, MoParams parameters, out IMoValue returnValue) 83 | { 84 | if (_propertyCache.Properties.TryGetValue(main, out var accessor)) 85 | { 86 | if (!accessor.CanRead) 87 | throw new MoLangRuntimeException($"Cannot read from property '{property.ToString()}'", null); 88 | 89 | returnValue = accessor.Get(_instance); 90 | 91 | return true; 92 | } 93 | 94 | if (_propertyCache.Functions.TryGetValue(main, out var f)) 95 | { 96 | returnValue = f.Invoke(_instance, parameters); 97 | 98 | return true; 99 | } 100 | 101 | returnValue = DoubleValue.Zero; 102 | 103 | return false; 104 | } 105 | 106 | /// 107 | public IMoValue Get(MoPath key, MoParams parameters) 108 | { 109 | if (key.HasChildren) 110 | { 111 | string main = key.Value; 112 | 113 | if (!string.IsNullOrWhiteSpace(main)) 114 | { 115 | ExecuteGet(key, main, parameters, out var value); 116 | 117 | if (value is IMoStruct moStruct) 118 | { 119 | return moStruct.Get(key.Next, parameters); 120 | } 121 | 122 | return value; 123 | } 124 | } 125 | 126 | if (!ExecuteGet(key, key.Value, parameters, out var returnValue)) 127 | Debug.WriteLine($"({_instance.ToString()}) Unknown query: {key}"); 128 | 129 | return returnValue; 130 | } 131 | 132 | /// 133 | public void Clear() 134 | { 135 | throw new NotSupportedException("Cannot clear an InteropStruct."); 136 | } 137 | } 138 | } -------------------------------------------------------------------------------- /src/MolangSharp/Runtime/Struct/QueryStruct.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ConcreteMC.MolangSharp.Runtime.Value; 4 | using ConcreteMC.MolangSharp.Utils; 5 | 6 | namespace ConcreteMC.MolangSharp.Runtime.Struct 7 | { 8 | /// 9 | /// Represents a readonly query structure 10 | /// 11 | public class QueryStruct : IMoStruct 12 | { 13 | protected readonly IDictionary> Functions = 14 | new Dictionary>(StringComparer.OrdinalIgnoreCase); 15 | 16 | /// 17 | public object Value => this; 18 | 19 | public QueryStruct() { } 20 | 21 | public QueryStruct(IEnumerable>> parameters) 22 | { 23 | Functions = new Dictionary>(parameters); 24 | } 25 | 26 | /// 27 | public void Set(MoPath key, IMoValue value) 28 | { 29 | throw new NotSupportedException("Cannot set a value in a query struct."); 30 | } 31 | 32 | /// 33 | public IMoValue Get(MoPath key, MoParams parameters) 34 | { 35 | if (Functions.TryGetValue(key.Value, out var func)) 36 | { 37 | return MoValue.FromObject(func(parameters)); 38 | } 39 | 40 | return DoubleValue.Zero; 41 | } 42 | 43 | /// 44 | public void Clear() 45 | { 46 | Functions.Clear(); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/MolangSharp/Runtime/Struct/VariableStruct.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ConcreteMC.MolangSharp.Runtime.Exceptions; 4 | using ConcreteMC.MolangSharp.Runtime.Value; 5 | using ConcreteMC.MolangSharp.Utils; 6 | 7 | namespace ConcreteMC.MolangSharp.Runtime.Struct 8 | { 9 | /// 10 | /// Represents a dynamic variable 11 | /// 12 | public class VariableStruct : IMoStruct 13 | { 14 | public IDictionary Map { get; protected set; } 15 | 16 | /// 17 | public object Value => Map; 18 | 19 | public VariableStruct() 20 | { 21 | Map = new Dictionary(StringComparer.OrdinalIgnoreCase); 22 | } 23 | 24 | public VariableStruct(IEnumerable> values) 25 | { 26 | if (values != null) 27 | { 28 | Map = new Dictionary(values, StringComparer.OrdinalIgnoreCase); 29 | } 30 | else 31 | { 32 | Map = new Dictionary(StringComparer.OrdinalIgnoreCase); 33 | } 34 | } 35 | 36 | protected virtual IMoStruct CreateNew() 37 | { 38 | return new VariableStruct(); 39 | } 40 | 41 | /// 42 | public virtual void Set(MoPath key, IMoValue value) 43 | { 44 | if (!key.HasChildren) 45 | { 46 | Map[key.Value] = value; 47 | 48 | return; 49 | } 50 | 51 | string main = key.Value; 52 | 53 | if (!string.IsNullOrWhiteSpace(main)) 54 | { 55 | if (!Map.TryGetValue(main, out var container)) 56 | { 57 | if (!key.HasChildren) 58 | { 59 | throw new MoLangRuntimeException($"Variable was not a struct: {key}", null); 60 | } 61 | 62 | Map.TryAdd(main, container = CreateNew()); 63 | } 64 | 65 | if (container is IMoStruct moStruct) 66 | { 67 | moStruct.Set(key.Next, value); 68 | } 69 | else 70 | { 71 | throw new MoLangRuntimeException($"Variable was not a struct: {key}", null); 72 | } 73 | } 74 | } 75 | 76 | /// 77 | public virtual IMoValue Get(MoPath key, MoParams parameters) 78 | { 79 | if (key.HasChildren) 80 | { 81 | string main = key.Value; 82 | 83 | if (!string.IsNullOrWhiteSpace(main)) 84 | { 85 | IMoValue value = null; 86 | 87 | if (!Map.TryGetValue(main, out value)) 88 | { 89 | return DoubleValue.Zero; 90 | } 91 | 92 | if (value is IMoStruct moStruct) 93 | { 94 | return moStruct.Get(key.Next, parameters); 95 | } 96 | 97 | return value; 98 | } 99 | } 100 | 101 | if (Map.TryGetValue(key.Value, out var v)) 102 | return v; 103 | 104 | return DoubleValue.Zero; 105 | } 106 | 107 | /// 108 | public void Clear() 109 | { 110 | Map.Clear(); 111 | } 112 | } 113 | } -------------------------------------------------------------------------------- /src/MolangSharp/Runtime/Value/DoubleValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ConcreteMC.MolangSharp.Runtime.Value 4 | { 5 | /// 6 | /// Represents a double-precision floating-point number 7 | /// 8 | public class DoubleValue : IMoValue 9 | { 10 | private readonly double _value; 11 | 12 | /// 13 | object IMoValue.Value => _value; 14 | 15 | /// 16 | public double Value => _value; 17 | 18 | /// 19 | public bool Equals(IMoValue b) 20 | { 21 | if (_value == b.AsDouble()) 22 | return true; 23 | 24 | return false; 25 | } 26 | 27 | public DoubleValue(object value) 28 | { 29 | if (value is bool boolean) 30 | { 31 | _value = boolean ? 1.0 : 0.0; 32 | } 33 | else if (value is double dbl) 34 | { 35 | _value = dbl; 36 | } 37 | else if (value is float flt) 38 | { 39 | _value = flt; 40 | } 41 | else if (value is int integer) 42 | { 43 | _value = integer; 44 | } 45 | else 46 | { 47 | throw new NotSupportedException($"Cannot convert {value.GetType().FullName} to double"); 48 | } 49 | } 50 | 51 | public DoubleValue(bool value) 52 | { 53 | _value = value ? 1d : 0d; 54 | } 55 | 56 | public DoubleValue(double value) 57 | { 58 | _value = value; 59 | } 60 | 61 | public override bool Equals(object obj) 62 | { 63 | if (ReferenceEquals(null, obj)) return false; 64 | if (ReferenceEquals(this, obj)) return true; 65 | if (obj.GetType() != this.GetType()) return false; 66 | 67 | return (obj is DoubleValue dv && dv._value == _value); 68 | // ...the rest of the equality implementation 69 | } 70 | 71 | public bool Equals(DoubleValue other) 72 | { 73 | return _value.Equals(other._value); 74 | } 75 | 76 | /// 77 | public override int GetHashCode() 78 | { 79 | return _value.GetHashCode(); 80 | } 81 | 82 | /// 83 | public double AsDouble() 84 | { 85 | return _value; 86 | } 87 | 88 | /// 89 | public float AsFloat() 90 | { 91 | return (float) _value; 92 | } 93 | 94 | public static DoubleValue Zero { get; } = new DoubleValue(0d); 95 | public static DoubleValue One { get; } = new DoubleValue(1d); 96 | } 97 | } -------------------------------------------------------------------------------- /src/MolangSharp/Runtime/Value/MoValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using ConcreteMC.MolangSharp.Runtime.Struct; 5 | 6 | namespace ConcreteMC.MolangSharp.Runtime.Value 7 | { 8 | /// 9 | /// Resembles a MoLang compatible value. 10 | /// 11 | public interface IMoValue : IEquatable 12 | { 13 | object Value { get; } 14 | 15 | virtual string AsString() => Value.ToString(); 16 | 17 | virtual double AsDouble() => Value is double db ? db : 0d; 18 | 19 | virtual float AsFloat() => Value is float flt ? flt : (float) AsDouble(); 20 | 21 | virtual bool AsBool() => Value is bool b ? b : AsDouble() > 0; 22 | } 23 | 24 | public static class MoValue 25 | { 26 | public static IMoValue FromObject(object value) 27 | { 28 | if (value is IMoValue moValue) 29 | { 30 | return moValue; 31 | } 32 | 33 | if (value is string str) 34 | { 35 | return new StringValue(str); 36 | } 37 | 38 | if (value is IEnumerable enumerable) 39 | { 40 | List values = new List(); 41 | 42 | foreach (var enumObject in enumerable) 43 | { 44 | values.Add(FromObject(enumObject)); 45 | } 46 | 47 | return new ArrayStruct(values); 48 | } 49 | 50 | return new DoubleValue(value); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /src/MolangSharp/Runtime/Value/StringValue.cs: -------------------------------------------------------------------------------- 1 | namespace ConcreteMC.MolangSharp.Runtime.Value 2 | { 3 | /// 4 | /// Represents a string value 5 | /// 6 | public class StringValue : IMoValue 7 | { 8 | /// 9 | object IMoValue.Value => Value; 10 | 11 | /// 12 | public string Value { get; set; } 13 | 14 | public StringValue(string value) 15 | { 16 | Value = value; 17 | } 18 | 19 | /// 20 | public bool Equals(IMoValue b) 21 | { 22 | if (b is StringValue stringValue) 23 | return string.Equals(Value, stringValue.Value); // stringValue.Value. 24 | 25 | return false; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/MolangSharp/Utils/MoPath.cs: -------------------------------------------------------------------------------- 1 | namespace ConcreteMC.MolangSharp.Utils 2 | { 3 | /// 4 | /// Describes the path of a variable/function 5 | /// 6 | public class MoPath 7 | { 8 | /// 9 | /// The root of this path 10 | /// 11 | public MoPath Root { get; } 12 | 13 | /// 14 | /// The next element 15 | /// 16 | public MoPath Next { get; private set; } 17 | 18 | /// 19 | /// The full path 20 | /// 21 | public string Path { get; } 22 | 23 | /// 24 | /// The value of this path 25 | /// 26 | /// 27 | /// Think of this is the filename in a path 28 | /// 29 | public string Value { get; private set; } 30 | 31 | /// 32 | /// Whether this path has any child elements 33 | /// 34 | public bool HasChildren => Next != null; 35 | 36 | public MoPath(string path) 37 | { 38 | Next = null; 39 | Root = this; 40 | Path = path; 41 | 42 | var segments = path.Split('.'); 43 | Value = segments[0]; 44 | 45 | MoPath current = this; 46 | 47 | if (segments.Length > 1) 48 | { 49 | string currentPath = $"{Value}"; 50 | 51 | for (int i = 1; i < segments.Length; i++) 52 | { 53 | var value = segments[i]; 54 | 55 | if (string.IsNullOrWhiteSpace(value)) 56 | break; 57 | 58 | currentPath += $".{value}"; 59 | 60 | var moPath = new MoPath(Root, currentPath, value); 61 | current.Next = moPath; 62 | current = moPath; 63 | } 64 | } 65 | } 66 | 67 | internal MoPath(MoPath root, string path, string value) 68 | { 69 | Root = root; 70 | Path = path; 71 | Value = value; 72 | } 73 | 74 | internal void SetValue(string value) 75 | { 76 | Value = value; 77 | } 78 | 79 | /// 80 | public override string ToString() 81 | { 82 | return Path; 83 | } 84 | } 85 | } --------------------------------------------------------------------------------