diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml
index fb3c9b3..7712442 100644
--- a/.github/workflows/documentation.yml
+++ b/.github/workflows/documentation.yml
@@ -2,8 +2,6 @@ name: Documentation
on:
push:
- branches:
- - main
branches-ignore:
- gh-pages
diff --git a/README.md b/README.md
index b2dff4a..cfd0419 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ A Molang Parser & Runtime for C#
Getting Started
------------
-Coming soon.
+See the [documentation here](https://concretemc.github.io/MolangSharp/articles/getting-started.html).
Awesome Repositories
------------------------
diff --git a/docs/articles/getting-started.md b/docs/articles/getting-started.md
new file mode 100644
index 0000000..3056541
--- /dev/null
+++ b/docs/articles/getting-started.md
@@ -0,0 +1,26 @@
+# Getting Started
+
+## Basic example
+```c#
+using ConcreteMC.MolangSharp.Parser;
+using ConcreteMC.MolangSharp.Runtime;
+
+//Initialize the runtime
+MoLangRuntime runtime = new MoLangRuntime();
+
+//Parse our MoLang expression
+var parsed = MoLangParser.Parse("10 * 45.0");
+
+//Execute the expression
+var result = runtime.Execute(parsed);
+Console.WriteLine($"Result={result.AsDouble()}");
+```
+
+### Output
+```c#
+Result=450
+```
+
+## More examples
+Check out the Examples folder in the Github repository:
+[https://github.com/ConcreteMC/MolangSharp/tree/main/src/examples](https://github.com/ConcreteMC/MolangSharp/tree/main/src/examples)
\ No newline at end of file
diff --git a/docs/articles/index.md b/docs/articles/index.md
new file mode 100644
index 0000000..5a9dd54
--- /dev/null
+++ b/docs/articles/index.md
@@ -0,0 +1 @@
+# Articles
diff --git a/docs/articles/interop.md b/docs/articles/interop.md
new file mode 100644
index 0000000..2a917d4
--- /dev/null
+++ b/docs/articles/interop.md
@@ -0,0 +1,62 @@
+# Interoperability
+MolangSharp has the ability to expose class properties, fields & methods to the runtime.
+
+## Defining a property
+In the following example we will show howto expose a property or field to MoLang.
+
+```c#
+[MoProperty("myProperty")]
+public double MyProperty { get; set; }
+
+//The following property only has a GET accessor and will be readonly
+[MoProperty("myReadOnlyProperty")]
+public double ReadonlyProperty { get; }
+
+//This will expose the field to MoLang
+[MoProperty("myField")]
+public readonly double MyField;
+```
+
+## Defining a function/method
+In the following example we will show howto expose a method to MoLang.
+```c#
+[MoFunction("add")]
+public double Add(double a, double b)
+{
+ return a + b;
+}
+```
+
+## Example class
+This example will show how to expose our custom MyMath class to MoLang
+
+### The class
+This is the class we'll be exposing to MoLang
+```c#
+public class MyMath
+{
+ [MoFunction("sin")]
+ public double Sin(double value) => Math.Sin(value * (Math.PI / 180d));
+
+ [MoFunction("asin")]
+ public double Asin(double value) => Math.Asin(value * (Math.PI / 180d));
+
+ [MoFunction("cos")]
+ public double Cos(double value) => Math.Cos(value * (Math.PI / 180d));
+
+ [MoFunction("acos")]
+ public double Acos(double value) => Math.Acos(value * (Math.PI / 180d));
+}
+```
+
+### Making the class available to the runtime
+```c#
+MoLangEnvironment environment = new MoLangEnvironment();
+environment.Structs.Add("custom_math", new InteropStruct(new MyMath()));
+MoLangRuntime runtime = new MoLangRuntime(environment);
+```
+
+This would expose the MyMath class to the runtime instance created in the example. The following example would now be valid.
+```
+custom_math.sin(12.0);
+```
\ No newline at end of file
diff --git a/docs/articles/intro.md b/docs/articles/intro.md
deleted file mode 100644
index f65681d..0000000
--- a/docs/articles/intro.md
+++ /dev/null
@@ -1 +0,0 @@
-# Coming soon...
\ No newline at end of file
diff --git a/docs/articles/toc.yml b/docs/articles/toc.yml
index ff89ef1..9c42cb2 100644
--- a/docs/articles/toc.yml
+++ b/docs/articles/toc.yml
@@ -1,2 +1,8 @@
-- name: Introduction
- href: intro.md
+- name: Home
+ href: index.md
+
+- name: Getting Started
+ href: getting-started.md
+
+- name: Interoperability
+ href: interop.md
\ No newline at end of file
diff --git a/docs/docfx.json b/docs/docfx.json
index cb1b46c..30df283 100644
--- a/docs/docfx.json
+++ b/docs/docfx.json
@@ -52,10 +52,7 @@
"dest": "_site",
"globalMetadataFiles": [],
"fileMetadataFiles": [],
- "template": [
- "default",
- "templates/material"
- ],
+ "template": ["default", "templates/discordfx"],
"globalMetadata": {
"_enableSearch": "true"
},
diff --git a/docs/templates/discordfx/layout/_master.tmpl b/docs/templates/discordfx/layout/_master.tmpl
new file mode 100644
index 0000000..f25a5ee
--- /dev/null
+++ b/docs/templates/discordfx/layout/_master.tmpl
@@ -0,0 +1,68 @@
+{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
+{{!include(/^styles/.*/)}}
+{{!include(/^fonts/.*/)}}
+{{!include(favicon.ico)}}
+{{!include(logo.svg)}}
+{{!include(search-stopwords.json)}}
+
+
+
+
+ {{>partials/head}}
+
+
+
+
+
+
+
+
+ {{>partials/logo}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{^_disableBreadcrumb}}
+ {{>partials/breadcrumb}}
+ {{/_disableBreadcrumb}}
+
+
+ {{!body}}
+
+
+
+
+
+
+ {{>partials/scripts}}
+
+
+
+
diff --git a/docs/templates/discordfx/partials/footer.tmpl.partial b/docs/templates/discordfx/partials/footer.tmpl.partial
new file mode 100644
index 0000000..b110641
--- /dev/null
+++ b/docs/templates/discordfx/partials/footer.tmpl.partial
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/docs/templates/discordfx/partials/head.tmpl.partial b/docs/templates/discordfx/partials/head.tmpl.partial
new file mode 100644
index 0000000..43a2cc7
--- /dev/null
+++ b/docs/templates/discordfx/partials/head.tmpl.partial
@@ -0,0 +1,22 @@
+{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
+
+
+
+
+ {{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}}
+
+
+
+ {{#_description}} {{/_description}}
+
+
+
+
+
+
+
+
+ {{#_noindex}} {{/_noindex}}
+ {{#_enableSearch}} {{/_enableSearch}}
+ {{#_enableNewTab}} {{/_enableNewTab}}
+
diff --git a/docs/templates/discordfx/partials/li.tmpl.partial b/docs/templates/discordfx/partials/li.tmpl.partial
new file mode 100644
index 0000000..2c8a3d0
--- /dev/null
+++ b/docs/templates/discordfx/partials/li.tmpl.partial
@@ -0,0 +1,31 @@
+{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
+
+
+ {{#items}}
+ {{^dropdown}}
+
+ {{^leaf}}
+
+ {{/leaf}}
+ {{#topicHref}}
+
+ {{/topicHref}}
+ {{^topicHref}}
+ {{{name}}}
+ {{/topicHref}}
+
+ {{^leaf}}
+ {{>partials/li}}
+ {{/leaf}}
+
+ {{/dropdown}}
+ {{#dropdown}}
+
+ {{name}}
+
+
+ {{/dropdown}}
+ {{/items}}
+
diff --git a/docs/templates/discordfx/partials/logo.tmpl.partial b/docs/templates/discordfx/partials/logo.tmpl.partial
new file mode 100644
index 0000000..738ab5b
--- /dev/null
+++ b/docs/templates/discordfx/partials/logo.tmpl.partial
@@ -0,0 +1,6 @@
+{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
+
+
+
+ {{_appName}}
+
\ No newline at end of file
diff --git a/docs/templates/discordfx/partials/navbar.tmpl.partial b/docs/templates/discordfx/partials/navbar.tmpl.partial
new file mode 100644
index 0000000..10b82d7
--- /dev/null
+++ b/docs/templates/discordfx/partials/navbar.tmpl.partial
@@ -0,0 +1,13 @@
+{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
+
+
+
+
+ {{>partials/logo}}
+
+
+
+
+
+
+
diff --git a/docs/templates/discordfx/partials/scripts.tmpl.partial b/docs/templates/discordfx/partials/scripts.tmpl.partial
new file mode 100644
index 0000000..749cbcd
--- /dev/null
+++ b/docs/templates/discordfx/partials/scripts.tmpl.partial
@@ -0,0 +1,11 @@
+{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/templates/discordfx/partials/toc.tmpl.partial b/docs/templates/discordfx/partials/toc.tmpl.partial
new file mode 100644
index 0000000..c660966
--- /dev/null
+++ b/docs/templates/discordfx/partials/toc.tmpl.partial
@@ -0,0 +1,5 @@
+{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
+
+
diff --git a/docs/templates/discordfx/styles/colors.css b/docs/templates/discordfx/styles/colors.css
new file mode 100644
index 0000000..63590c7
--- /dev/null
+++ b/docs/templates/discordfx/styles/colors.css
@@ -0,0 +1,73 @@
+/* Color schemes */
+:root
+{
+ /* General */
+ --main-bg-color: #36393f;
+ --footer-bg-color: rgba(0,0,0,.4);
+ --table-strip-bg-color: #121315;
+ --table-header-bg-color: #202225;;
+ --table-header-color: hsla(0,0%,100%,.8);
+ --table-header-border-color: #040405;
+
+ /* Text */
+ --text-color: #dcddde;
+ --link-color: #00b0f4;
+ --link-hover-color: #4bd5ff;
+ --link-active-color: #fff;
+ --link-active-bg-color: #7289da;
+ --h3-color: #ffffff85;
+ --h4-color: #ffffffeb;
+ --h5-color: #ffffffd1;
+
+ /* Topbar */
+ --topbar-bg-color: #18191c;
+
+ /* Button */
+ --button-color: #747f8d;
+
+ /* Sidebar */
+ --separator-color: #4f545c;
+ --sidebar-bg-color: #2f3136;
+ --sidebar-item-color: #b9bbbe;
+ --sidebar-item-2nd-color: hsla(0,0%,100%,.35);
+ --sidebar-item-3rd-color: hsla(0,0%,100%,.25);
+
+ /* Scrollbar */
+ --scrollbar-bg-color: transparent;
+ --scrollbar-thumb-bg-color: rgba(0,0,0,.4);
+ --scrollbar-thumb-border-color: transparent;
+
+ /* Alerts and Blocks */
+ --alert-info-border-color: rgba(114,137,218,.5);
+ --alert-info-bg-color: rgba(114,137,218,.1);
+
+ --alert-warning-border-color: rgba(250,166,26,.5);
+ --alert-warning-bg-color: rgba(250,166,26,.1);
+
+ --alert-danger-border-color: rgba(240,71,71,.5);
+ --alert-danger-bg-color: rgba(240,71,71,.1);
+
+ --alert-tip-border-color: rgba(255,255,255,.5);
+ --alert-tip-bg-color: rgba(255,255,255,.1);
+
+ --blockquote-border-color: rgba(255,255,255,.5);
+ --blockquote-bg-color: rgba(255,255,255,.1);
+
+ --breadcrumb-bg-color: #2f3136;
+
+ /* Code Higlighting */
+ --code-bg-color: #18191c;
+ --code-color: #8790A3;
+ --code-keyword-color: #569cd6;
+ --code-comment-color: #57a64a;
+ --code-macro-color: #beb7ff;
+ --code-string-color: #d69d85;
+ --code-string-escape-color: #ffd68f;
+ --code-field-color: #c8c8c8;
+ --code-function-color: #dcdcaa;
+ --code-control-color: #d8a0df;
+ --code-class-color: #4ec9b0;
+ --code-number-color: #b5cea8;
+ --code-params-color: #9a9a9a;
+ --code-breakpoint-color: #8c2f2f;
+}
diff --git a/docs/templates/discordfx/styles/discord.css b/docs/templates/discordfx/styles/discord.css
new file mode 100644
index 0000000..a5a97b4
--- /dev/null
+++ b/docs/templates/discordfx/styles/discord.css
@@ -0,0 +1,681 @@
+/* Discord Style */
+
+::-webkit-scrollbar {
+ width: 10px;
+}
+
+::-webkit-scrollbar-track {
+ background: var(--scrollbar-bg-color);
+}
+
+::-webkit-scrollbar-thumb {
+ background: var(--scrollbar-thumb-bg-color);
+ border-color: var(--scrollbar-thumb-border-color);
+ border-radius: 5px;
+}
+
+::marker {
+ unicode-bidi: isolate;
+ font-variant-numeric: tabular-nums;
+ text-transform: none;
+ text-indent: 0px !important;
+ text-align: start !important;
+ text-align-last: start !important;
+}
+
+*, :after, :before
+{
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+html, body
+{
+ padding: 0;
+ margin: 0;
+ font: 15px/150% 'Roboto', sans-serif;
+ overflow: hidden;
+ color: var(--text-color);
+ background-color: var(--main-bg-color);
+
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+}
+
+img {
+ max-width: 100%;
+}
+
+ul > li, ol > li {
+ display: list-item;
+}
+
+h1,h2,h3,h4,h5
+{
+ color: var(--link-active-color);
+ position: relative;
+}
+
+h1, h2
+{
+ margin-block-start: 2em;
+}
+
+h3
+{
+ margin-block-start: 1em;
+ font-weight: 300;
+ font-size: 1.5em;
+ color: var(--h3-color);
+ margin-block-start: 3em;
+}
+
+h4
+{
+ opacity: 1;
+ color: var(--h4-color);
+ font-size: large;
+ border-bottom: 2px solid var(--separator-color);
+ margin: 20px 0 0 0;
+}
+
+
+h5 {
+ margin-block-end: .8em;
+ margin-block-start: 1em;
+ font-size: .85em;
+ font-weight: 500;
+ color: var(--h5-color);
+}
+
+h6 {
+ font-size: .75em;
+ margin: 0;
+}
+
+p
+{
+ font-weight: 400;
+}
+
+ul
+{
+ position: relative;
+}
+
+ul, ol
+{
+ padding-inline-start: 3em;
+}
+
+ul.level1
+{
+ list-style-type: none;
+ padding-inline-start: 0;
+}
+
+ul.level2, ul.level3
+{
+ padding-inline-start: 1em;
+ list-style-type: none;
+ font-size: .9em;
+}
+
+a
+{
+ color: var(--link-color);
+ text-decoration: none;
+ transition: color .25s;
+}
+
+a:focus, a:hover
+{
+ color: var(--link-hover-color);
+ text-decoration: underline;
+}
+
+a.anchorjs-link:hover {
+ text-decoration: none;
+}
+
+a.active, a:active
+{
+ color: var(--link-active-color);
+}
+
+.body-content
+{
+ display: flex;
+ flex-direction: row;
+ height: 100%;
+ overflow-x: hidden;
+ overflow-y: hidden;
+}
+
+.page-title
+{
+ margin-block-start: 0;
+}
+
+nav
+{
+ width: 300px;
+ transition: left .5s ease-out;
+ position: fixed;
+ left: -350px;
+ top: 40px;
+ bottom: 0;
+ background-color: var(--sidebar-bg-color);
+ overflow-y: auto;
+
+ display: flex;
+ flex-direction: column;
+
+ z-index: 1000;
+}
+
+h1:first-child
+{
+ margin-block-start: 1.1em;
+ margin-top: 1.1em;
+}
+
+.sidebar
+{
+ padding: 32px 17px 32px 32px;
+ flex: 1;
+}
+
+.sidebar-item
+{
+ font-size: 1em;
+ font-weight: 400;
+ display: block;
+ padding: 4px 16px;
+ color: var(--sidebar-item-color);
+}
+
+.sidebar-item.large, #navbar .sidebar-item
+{
+ padding: 8px 16px;
+}
+
+a.sidebar-item:hover, a.sidebar-item:focus
+{
+ color: var(--link-active-color);
+ text-decoration: none;
+}
+
+a.sidebar-item.active
+{
+ color: var(--link-active-color);
+}
+
+ul.level1 > li > a.sidebar-item
+{
+ background-color: transparent;
+ border-radius: 4px;
+}
+
+#toc ul.level1 > li > a.sidebar-item.active
+{
+ background-color: var(--link-active-bg-color);
+}
+
+.sidebar-item-separator
+{
+ height: 2px;
+ width: 100%;
+ background-color: var(--separator-color);
+ margin: 2em 0;
+ opacity: .8;
+}
+
+span.sidebar-item
+{
+ font-weight: 700;
+ text-transform: uppercase;
+ font-size: .8em;
+ color: var(--text-color);
+ margin-block-start: 1.25em;
+}
+
+.main-panel
+{
+ background-color: var(--main-bg-color);
+ flex: 1;
+ overflow-y: auto;
+ padding: 20px 40px;
+}
+
+.top-navbar
+{
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 0 40px;
+ height: 40px;
+ background-color: var(--topbar-bg-color);
+}
+
+.burger-icon
+{
+ margin-right: 1em;
+ color: var(--button-color);
+}
+
+.burger-icon:hover, .burger-icon:focus
+{
+ color: var(--link-active-color);
+}
+
+.burger-icon.active, .burger-icon:active
+{
+ color: var(--link-active-color);
+}
+
+.brand
+{
+ display: flex;
+ align-items: center;
+ justify-content: start;
+}
+
+.logomark
+{
+ height: 28px;
+}
+
+.brand-title
+{
+ padding: 0 .5em;
+ font-size: .9em;
+ color: var(--link-active-color);
+}
+
+.footer
+{
+ background-color: var(--footer-bg-color);
+ padding: 20px;
+ margin: 0 20px 20px 20px;
+ border-radius: 8px;
+ color: var(--link-active-color);
+}
+
+.footer > h4
+{
+ margin-block-start: 0;
+}
+
+.blackout
+{
+ display: block;
+ visibility: hidden;
+ position: absolute;
+ z-index: 100;
+ top: 40px;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ background-color: var(--footer-bg-color);
+}
+
+@keyframes showThat {
+ 0% { opacity: 0; visibility: hidden; }
+ 1% { opacity: 0; visibility: visible; }
+ 100% { opacity: 1; visibility: visible;}
+}
+
+@keyframes hideThat {
+ 0% { opacity: 1; visibility: visible; }
+ 99% { opacity: 0; visibility: visible; }
+ 100% { opacity: 0; visibility: hidden;}
+}
+
+.showThat
+{
+ animation: showThat .5s forwards;
+}
+
+.hideThat
+{
+ animation: hideThat .5s forwards;
+}
+
+
+
+@media (min-width: 1024px)
+{
+ nav
+ {
+ position: relative;
+ left: 0!important;
+ top: 0;
+ bottom: 0;
+ }
+
+ .top-navbar
+ {
+ display: none;
+ }
+
+ .blackout
+ {
+ display: none;
+ }
+}
+
+/* Table */
+
+.table-responsive
+{
+ overflow-x: auto;
+ margin-bottom: 64px;
+}
+
+table
+{
+ background-color: var(--code-bg-color);
+ border-collapse: collapse;
+ width: 100%;
+ table-layout: auto;
+}
+
+table.table-striped tbody tr:nth-child(2n)
+{
+ background-color: var(--table-strip-bg-color);
+}
+
+table thead
+{
+ background: var(--table-header-bg-color);
+}
+
+table th
+{
+ color: var(--table-header-color);
+ text-transform: uppercase;
+ font-size: 12px;
+ line-height: 15px;
+ border-bottom: 1px solid var(--table-header-border-color);
+ padding: 8px;
+}
+
+.table-condensed th {
+ text-align: left;
+}
+
+table td
+{
+ padding: 8px;
+ font-weight: 300;
+}
+
+table td > p
+{
+ margin: 0;
+}
+
+/* Alerts */
+.alert {
+ border-radius: 4px;
+ padding: 8px;
+ margin: 25px 0;
+}
+
+.alert > h5
+{
+ display: none;
+ margin: 0;
+}
+
+.alert > p
+{
+ margin: 0;
+ font-weight: 300;
+ font-size: 13px;
+}
+
+.alert.alert-info
+{
+ border: 2px solid var(--alert-info-border-color);
+ background: var(--alert-info-bg-color);
+}
+
+.alert.alert-warning
+{
+ border: 2px solid var(--alert-warning-border-color);
+ background: var(--alert-warning-bg-color);
+}
+
+.alert.alert-danger
+{
+ border: 2px solid var(--alert-danger-border-color);
+ background: var(--alert-danger-bg-color);
+}
+
+.TIP.alert.alert-info
+{
+ border: 2px solid var(--alert-tip-border-color);
+ background: var(--alert-tip-bg-color);
+}
+
+blockquote {
+ margin: 8px 0;
+ border-left: 4px solid var(--blockquote-border-color);
+ padding: 8px;
+ background: var(--blockquote-bg-color);
+ border-radius: 4px;
+}
+
+blockquote > p {
+ margin: 0;
+ font-style: italic;
+ font-size: 13px;
+}
+
+
+/* Breadcrumb */
+
+#breadcrumb
+{
+ padding: 8px 16px;
+ background: var(--breadcrumb-bg-color);
+ border-radius: 4px;
+ margin-bottom: 30px;
+}
+
+#breadcrumb:empty
+{
+ display: none;
+}
+
+ul.breadcrumb
+{
+ display: flex;
+ flex-direction: row;
+ margin: 0;
+}
+
+ul.breadcrumb > li {
+ margin-right: 6px;
+}
+
+ul.breadcrumb > li::before
+{
+ content: "/";
+ margin-right: 5px;
+}
+
+ul.breadcrumb > li:first-child::before
+{
+ content: "";
+ margin: 0;
+}
+
+
+/* Code */
+
+legend, pre
+{
+ display: block;
+ background-color: var(--code-bg-color);
+ padding: 16px;
+ border-radius: 4px;
+}
+
+code
+{
+ background-color: var(--code-bg-color);
+ padding: 2px 4px;
+ border-radius: 4px;
+}
+
+.hljs
+{
+ background: transparent;
+}
+
+/* DocFX related */
+
+.small {
+ font-size: .9em;
+}
+
+.pull-right
+{
+ float: right;
+}
+
+.mobile-hide
+{
+ visibility: hidden;
+}
+
+@media (min-width: 1024px)
+{
+ .mobile-hide
+ {
+ visibility: visible;
+ }
+}
+
+li
+{
+ display: block;
+ position: relative;
+}
+
+.expand-stub
+{
+ cursor: pointer;
+ position: absolute;
+ width: 20px;
+ height: 20px;
+ left: -10px;
+}
+
+ul.level1 > li > .expand-stub
+{
+ display: none;
+}
+
+.toc .nav > li > .expand-stub::before, .toc .nav > li.active > .expand-stub::before
+{
+ content: " ";
+ position: absolute;
+ transform: rotate(-90deg);
+ width: 10px;
+ height: 10px;
+ top: 5px;
+ left: 5px;
+ background-repeat: no-repeat;
+ background: url(/styles/down-arrow.svg);
+}
+
+.toc .nav > li.active > .expand-stub::before, .toc .nav > li.in > .expand-stub::before, .toc .nav > li.in.active > .expand-stub::before, .toc .nav > li.filtered > .expand-stub::before
+{
+ transform: none;
+}
+
+li > ul
+{
+ display: none;
+}
+
+li.in > ul
+{
+ display: block;
+}
+
+ul.level2 > li > a.sidebar-item,
+ul.level3 > li > a.sidebar-item
+{
+ font-weight: 500;
+ font-size: .95em;
+ padding: 0;
+ margin: 2px 16px;
+}
+
+ul.level2 > li > a.sidebar-item
+{
+ color: var(--sidebar-item-2nd-color);
+}
+
+ul.level3 > li > a.sidebar-item
+{
+ color: var(--sidebar-item-3rd-color);
+}
+
+ul.level2 > li > a.sidebar-item:hover,
+ul.level2 > li > a.sidebar-item:focus,
+ul.level3 > li > a.sidebar-item:hover,
+ul.level3 > li > a.sidebar-item:focus
+{
+ color: var(--link-active-color);
+ text-decoration: underline;
+}
+
+ul.level2 > li > a.sidebar-item.active,
+ul.level3 > li > a.sidebar-item.active
+{
+ color: var(--link-active-color);
+}
+
+.inheritance .level0:before,
+.inheritance .level1:before,
+.inheritance .level2:before,
+.inheritance .level3:before,
+.inheritance .level4:before,
+.inheritance .level5:before {
+ content: '↳';
+ margin-right: 5px;
+}
+
+.inheritance .level0 {
+ margin-left: 0em;
+}
+
+.inheritance .level1 {
+ margin-left: 1em;
+}
+
+.inheritance .level2 {
+ margin-left: 2em;
+}
+
+.inheritance .level3 {
+ margin-left: 3em;
+}
+
+.inheritance .level4 {
+ margin-left: 4em;
+}
+
+.inheritance .level5 {
+ margin-left: 5em;
+}
\ No newline at end of file
diff --git a/docs/templates/discordfx/styles/down-arrow.svg b/docs/templates/discordfx/styles/down-arrow.svg
new file mode 100644
index 0000000..e086126
--- /dev/null
+++ b/docs/templates/discordfx/styles/down-arrow.svg
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/templates/discordfx/styles/jquery.twbsPagination.js b/docs/templates/discordfx/styles/jquery.twbsPagination.js
new file mode 100644
index 0000000..332c01c
--- /dev/null
+++ b/docs/templates/discordfx/styles/jquery.twbsPagination.js
@@ -0,0 +1,317 @@
+/*!
+ * jQuery pagination plugin v1.4.1
+ * http://esimakin.github.io/twbs-pagination/
+ *
+ * Copyright 2014-2016, Eugene Simakin
+ * Released under Apache 2.0 license
+ * http://apache.org/licenses/LICENSE-2.0.html
+ */
+(function ($, window, document, undefined) {
+
+ 'use strict';
+
+ var old = $.fn.twbsPagination;
+
+ // PROTOTYPE AND CONSTRUCTOR
+
+ var TwbsPagination = function (element, options) {
+ this.$element = $(element);
+ this.options = $.extend({}, $.fn.twbsPagination.defaults, options);
+
+ if (this.options.startPage < 1 || this.options.startPage > this.options.totalPages) {
+ throw new Error('Start page option is incorrect');
+ }
+
+ this.options.totalPages = parseInt(this.options.totalPages);
+ if (isNaN(this.options.totalPages)) {
+ throw new Error('Total pages option is not correct!');
+ }
+
+ this.options.visiblePages = parseInt(this.options.visiblePages);
+ if (isNaN(this.options.visiblePages)) {
+ throw new Error('Visible pages option is not correct!');
+ }
+
+ if (this.options.onPageClick instanceof Function) {
+ this.$element.first().on('page', this.options.onPageClick);
+ }
+
+ // hide if only one page exists
+ if (this.options.hideOnlyOnePage && this.options.totalPages == 1) {
+ this.$element.trigger('page', 1);
+ return this;
+ }
+
+ if (this.options.totalPages < this.options.visiblePages) {
+ this.options.visiblePages = this.options.totalPages;
+ }
+
+ if (this.options.href) {
+ this.options.startPage = this.getPageFromQueryString();
+ if (!this.options.startPage) {
+ this.options.startPage = 1;
+ }
+ }
+
+ var tagName = (typeof this.$element.prop === 'function') ?
+ this.$element.prop('tagName') : this.$element.attr('tagName');
+
+ if (tagName === 'UL') {
+ this.$listContainer = this.$element;
+ } else {
+ this.$listContainer = $('');
+ }
+
+ this.$listContainer.addClass(this.options.paginationClass);
+
+ if (tagName !== 'UL') {
+ this.$element.append(this.$listContainer);
+ }
+
+ if (this.options.initiateStartPageClick) {
+ this.show(this.options.startPage);
+ } else {
+ this.render(this.getPages(this.options.startPage));
+ this.setupEvents();
+ }
+
+ return this;
+ };
+
+ TwbsPagination.prototype = {
+
+ constructor: TwbsPagination,
+
+ destroy: function () {
+ this.$element.empty();
+ this.$element.removeData('twbs-pagination');
+ this.$element.off('page');
+
+ return this;
+ },
+
+ show: function (page) {
+ if (page < 1 || page > this.options.totalPages) {
+ throw new Error('Page is incorrect.');
+ }
+ this.currentPage = page;
+
+ this.render(this.getPages(page));
+ this.setupEvents();
+
+ this.$element.trigger('page', page);
+
+ return this;
+ },
+
+ buildListItems: function (pages) {
+ var listItems = [];
+
+ if (this.options.first) {
+ listItems.push(this.buildItem('first', 1));
+ }
+
+ if (this.options.prev) {
+ var prev = pages.currentPage > 1 ? pages.currentPage - 1 : this.options.loop ? this.options.totalPages : 1;
+ listItems.push(this.buildItem('prev', prev));
+ }
+
+ for (var i = 0; i < pages.numeric.length; i++) {
+ listItems.push(this.buildItem('page', pages.numeric[i]));
+ }
+
+ if (this.options.next) {
+ var next = pages.currentPage < this.options.totalPages ? pages.currentPage + 1 : this.options.loop ? 1 : this.options.totalPages;
+ listItems.push(this.buildItem('next', next));
+ }
+
+ if (this.options.last) {
+ listItems.push(this.buildItem('last', this.options.totalPages));
+ }
+
+ return listItems;
+ },
+
+ buildItem: function (type, page) {
+ var $itemContainer = $(' '),
+ $itemContent = $(' '),
+ itemText = this.options[type] ? this.makeText(this.options[type], page) : page;
+
+ $itemContainer.addClass(this.options[type + 'Class']);
+ $itemContainer.data('page', page);
+ $itemContainer.data('page-type', type);
+ $itemContainer.append($itemContent.attr('href', this.makeHref(page)).addClass(this.options.anchorClass).html(itemText));
+
+ return $itemContainer;
+ },
+
+ getPages: function (currentPage) {
+ var pages = [];
+
+ var half = Math.floor(this.options.visiblePages / 2);
+ var start = currentPage - half + 1 - this.options.visiblePages % 2;
+ var end = currentPage + half;
+
+ // handle boundary case
+ if (start <= 0) {
+ start = 1;
+ end = this.options.visiblePages;
+ }
+ if (end > this.options.totalPages) {
+ start = this.options.totalPages - this.options.visiblePages + 1;
+ end = this.options.totalPages;
+ }
+
+ var itPage = start;
+ while (itPage <= end) {
+ pages.push(itPage);
+ itPage++;
+ }
+
+ return {"currentPage": currentPage, "numeric": pages};
+ },
+
+ render: function (pages) {
+ var _this = this;
+ this.$listContainer.children().remove();
+ var items = this.buildListItems(pages);
+ jQuery.each(items, function(key, item){
+ _this.$listContainer.append(item);
+ });
+
+ this.$listContainer.children().each(function () {
+ var $this = $(this),
+ pageType = $this.data('page-type');
+
+ switch (pageType) {
+ case 'page':
+ if ($this.data('page') === pages.currentPage) {
+ $this.addClass(_this.options.activeClass);
+ }
+ break;
+ case 'first':
+ $this.toggleClass(_this.options.disabledClass, pages.currentPage === 1);
+ break;
+ case 'last':
+ $this.toggleClass(_this.options.disabledClass, pages.currentPage === _this.options.totalPages);
+ break;
+ case 'prev':
+ $this.toggleClass(_this.options.disabledClass, !_this.options.loop && pages.currentPage === 1);
+ break;
+ case 'next':
+ $this.toggleClass(_this.options.disabledClass,
+ !_this.options.loop && pages.currentPage === _this.options.totalPages);
+ break;
+ default:
+ break;
+ }
+
+ });
+ },
+
+ setupEvents: function () {
+ var _this = this;
+ this.$listContainer.off('click').on('click', 'li', function (evt) {
+ var $this = $(this);
+ if ($this.hasClass(_this.options.disabledClass) || $this.hasClass(_this.options.activeClass)) {
+ return false;
+ }
+ // Prevent click event if href is not set.
+ !_this.options.href && evt.preventDefault();
+ _this.show(parseInt($this.data('page')));
+ });
+ },
+
+ makeHref: function (page) {
+ return this.options.href ? this.generateQueryString(page) : "#";
+ },
+
+ makeText: function (text, page) {
+ return text.replace(this.options.pageVariable, page)
+ .replace(this.options.totalPagesVariable, this.options.totalPages)
+ },
+ getPageFromQueryString: function (searchStr) {
+ var search = this.getSearchString(searchStr),
+ regex = new RegExp(this.options.pageVariable + '(=([^]*)|&|#|$)'),
+ page = regex.exec(search);
+ if (!page || !page[2]) {
+ return null;
+ }
+ page = decodeURIComponent(page[2]);
+ page = parseInt(page);
+ if (isNaN(page)) {
+ return null;
+ }
+ return page;
+ },
+ generateQueryString: function (pageNumber, searchStr) {
+ var search = this.getSearchString(searchStr),
+ regex = new RegExp(this.options.pageVariable + '=*[^]*');
+ if (!search) return '';
+ return '?' + search.replace(regex, this.options.pageVariable + '=' + pageNumber);
+
+ },
+ getSearchString: function (searchStr) {
+ var search = searchStr || window.location.search;
+ if (search === '') {
+ return null;
+ }
+ if (search.indexOf('?') === 0) search = search.substr(1);
+ return search;
+ }
+
+ };
+
+ // PLUGIN DEFINITION
+
+ $.fn.twbsPagination = function (option) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ var methodReturn;
+
+ var $this = $(this);
+ var data = $this.data('twbs-pagination');
+ var options = typeof option === 'object' ? option : {};
+
+ if (!data) $this.data('twbs-pagination', (data = new TwbsPagination(this, options) ));
+ if (typeof option === 'string') methodReturn = data[ option ].apply(data, args);
+
+ return ( methodReturn === undefined ) ? $this : methodReturn;
+ };
+
+ $.fn.twbsPagination.defaults = {
+ totalPages: 1,
+ startPage: 1,
+ visiblePages: 5,
+ initiateStartPageClick: true,
+ hideOnlyOnePage: false,
+ href: false,
+ pageVariable: '{{page}}',
+ totalPagesVariable: '{{total_pages}}',
+ page: null,
+ first: 'First',
+ prev: 'Previous',
+ next: 'Next',
+ last: 'Last',
+ loop: false,
+ onPageClick: null,
+ paginationClass: 'pagination',
+ nextClass: 'page-item next',
+ prevClass: 'page-item prev',
+ lastClass: 'page-item last',
+ firstClass: 'page-item first',
+ pageClass: 'page-item',
+ activeClass: 'active',
+ disabledClass: 'disabled',
+ anchorClass: 'page-link'
+ };
+
+ $.fn.twbsPagination.Constructor = TwbsPagination;
+
+ $.fn.twbsPagination.noConflict = function () {
+ $.fn.twbsPagination = old;
+ return this;
+ };
+
+ $.fn.twbsPagination.version = "1.4.1";
+
+})(window.jQuery, window, document);
diff --git a/docs/templates/discordfx/styles/main.css b/docs/templates/discordfx/styles/main.css
new file mode 100644
index 0000000..e69de29
diff --git a/docs/templates/discordfx/styles/main.js b/docs/templates/discordfx/styles/main.js
new file mode 100644
index 0000000..b9150b4
--- /dev/null
+++ b/docs/templates/discordfx/styles/main.js
@@ -0,0 +1,20 @@
+// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+function toggleMenu() {
+
+ var x = document.getElementById("sidebar");
+ var b = document.getElementById("blackout");
+
+ if (x.style.left === "0px")
+ {
+ x.style.left = "-350px";
+ b.classList.remove("showThat");
+ b.classList.add("hideThat");
+ }
+ else
+ {
+ x.style.left = "0px";
+ b.classList.remove("hideThat");
+ b.classList.add("showThat");
+ }
+}
\ No newline at end of file
diff --git a/docs/templates/discordfx/styles/url.min.js b/docs/templates/discordfx/styles/url.min.js
new file mode 100644
index 0000000..8057e0a
--- /dev/null
+++ b/docs/templates/discordfx/styles/url.min.js
@@ -0,0 +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)}});
\ No newline at end of file
diff --git a/docs/toc.yml b/docs/toc.yml
index c829e93..ab7ff6a 100644
--- a/docs/toc.yml
+++ b/docs/toc.yml
@@ -1,6 +1,7 @@
- name: Articles
href: articles/
- homepage: articles/intro.md
+ homepage: articles/index.md
+
- name: Api Documentation
href: api/
homepage: api/index.md
diff --git a/src/Alex.MoLang.sln b/src/Alex.MoLang.sln
index a20ea21..4392500 100644
--- a/src/Alex.MoLang.sln
+++ b/src/Alex.MoLang.sln
@@ -4,6 +4,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MolangSharp", "MolangSharp\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MolangSharp.Tests", "MolangSharp.Tests\MolangSharp.Tests.csproj", "{3FF89B16-9D85-4E87-83C7-D937F2ED7F5B}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{D73569B7-CCA2-458B-98CE-CC7531D414FA}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MolangSharp.Examples.Basic", "Examples\MolangSharp.Examples.Basic\MolangSharp.Examples.Basic.csproj", "{30FD4BFD-0792-495D-A6F6-600713011C77}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensibility", "Extensibility", "{EC0FCA2E-4326-4F6A-9210-684F90267012}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MolangSharp.Json.Newtonsoft", "Extensibility\MolangSharp.Json.Newtonsoft\MolangSharp.Json.Newtonsoft.csproj", "{D5E15128-EB40-4263-B9C9-9544F3ACA4CB}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -18,5 +26,17 @@ Global
{3FF89B16-9D85-4E87-83C7-D937F2ED7F5B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3FF89B16-9D85-4E87-83C7-D937F2ED7F5B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3FF89B16-9D85-4E87-83C7-D937F2ED7F5B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {30FD4BFD-0792-495D-A6F6-600713011C77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {30FD4BFD-0792-495D-A6F6-600713011C77}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {30FD4BFD-0792-495D-A6F6-600713011C77}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {30FD4BFD-0792-495D-A6F6-600713011C77}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D5E15128-EB40-4263-B9C9-9544F3ACA4CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D5E15128-EB40-4263-B9C9-9544F3ACA4CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D5E15128-EB40-4263-B9C9-9544F3ACA4CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D5E15128-EB40-4263-B9C9-9544F3ACA4CB}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {30FD4BFD-0792-495D-A6F6-600713011C77} = {D73569B7-CCA2-458B-98CE-CC7531D414FA}
+ {D5E15128-EB40-4263-B9C9-9544F3ACA4CB} = {EC0FCA2E-4326-4F6A-9210-684F90267012}
EndGlobalSection
EndGlobal
diff --git a/src/Examples/MolangSharp.Examples.Basic/BaseEntity.cs b/src/Examples/MolangSharp.Examples.Basic/BaseEntity.cs
new file mode 100644
index 0000000..b1042fd
--- /dev/null
+++ b/src/Examples/MolangSharp.Examples.Basic/BaseEntity.cs
@@ -0,0 +1,18 @@
+using System.Diagnostics;
+using ConcreteMC.MolangSharp.Attributes;
+using ConcreteMC.MolangSharp.Runtime;
+using ConcreteMC.MolangSharp.Runtime.Struct;
+
+namespace MolangSharp.Examples.Basic;
+
+public class BaseEntity : MoLangEnvironment
+{
+ private Stopwatch _stopwatch = Stopwatch.StartNew();
+ public BaseEntity()
+ {
+ Structs["query"] = new InteropStruct(this);
+ }
+
+ [MoProperty("life_time")]
+ public double LifeTime => _stopwatch.Elapsed.TotalSeconds;
+}
\ No newline at end of file
diff --git a/src/Examples/MolangSharp.Examples.Basic/MolangSharp.Examples.Basic.csproj b/src/Examples/MolangSharp.Examples.Basic/MolangSharp.Examples.Basic.csproj
new file mode 100644
index 0000000..54bc415
--- /dev/null
+++ b/src/Examples/MolangSharp.Examples.Basic/MolangSharp.Examples.Basic.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/src/Examples/MolangSharp.Examples.Basic/Program.cs b/src/Examples/MolangSharp.Examples.Basic/Program.cs
new file mode 100644
index 0000000..4cca536
--- /dev/null
+++ b/src/Examples/MolangSharp.Examples.Basic/Program.cs
@@ -0,0 +1,25 @@
+using System.Diagnostics;
+using System.Globalization;
+using ConcreteMC.MolangSharp.Parser;
+using ConcreteMC.MolangSharp.Runtime;
+using MolangSharp.Examples.Basic;
+
+Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
+Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
+
+//Create a new Entity which will use Interop to allow for MoLang->C# communication
+var myEntity = new BaseEntity();
+
+//Initialize the runtime
+MoLangRuntime runtime = new MoLangRuntime(myEntity);
+
+//Parse our MoLang expression
+var parsed = MoLangParser.Parse("math.cos(query.life_time * 60) * -45.0");
+
+Stopwatch sw = Stopwatch.StartNew();
+while (true)
+{
+ var value = runtime.Execute(parsed);
+ Console.WriteLine($"[{sw.Elapsed.ToString("mm\\:ss\\.fff")}] Rotation: {value.AsDouble():F3}");
+ Thread.Sleep(100);
+}
\ No newline at end of file
diff --git a/src/Extensibility/MolangSharp.Json.Newtonsoft/MolangExpressionConverter.cs b/src/Extensibility/MolangSharp.Json.Newtonsoft/MolangExpressionConverter.cs
new file mode 100644
index 0000000..b70356b
--- /dev/null
+++ b/src/Extensibility/MolangSharp.Json.Newtonsoft/MolangExpressionConverter.cs
@@ -0,0 +1,56 @@
+using System;
+using ConcreteMC.MolangSharp.Parser;
+using ConcreteMC.MolangSharp.Parser.Expressions;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace MolangSharp.Json.Newtonsoft
+{
+ ///
+ /// Implements a JsonConverter for reading
+ ///
+ public class MolangExpressionConverter : JsonConverter
+ {
+ public MolangExpressionConverter()
+ {
+
+ }
+
+ public override bool CanWrite => false;
+
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
+ JsonSerializer serializer)
+ {
+ JToken token = JToken.Load(reader);
+ if (token.Type == JTokenType.String)
+ {
+ string molang = token.Value();
+ return MoLangParser.Parse(molang);
+ }
+ else if (token.Type == JTokenType.Integer)
+ {
+ return new NumberExpression(token.Value());
+ }
+ else if (token.Type == JTokenType.Float)
+ {
+ return new NumberExpression(token.Value());
+ }
+ else if (token.Type == JTokenType.Boolean)
+ {
+ return new BooleanExpression(token.Value());
+ }
+
+ return existingValue;
+ }
+
+ public override bool CanConvert(Type objectType)
+ {
+ return typeof(IExpression).IsAssignableFrom(objectType);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Extensibility/MolangSharp.Json.Newtonsoft/MolangSharp.Json.Newtonsoft.csproj b/src/Extensibility/MolangSharp.Json.Newtonsoft/MolangSharp.Json.Newtonsoft.csproj
new file mode 100644
index 0000000..124ccf8
--- /dev/null
+++ b/src/Extensibility/MolangSharp.Json.Newtonsoft/MolangSharp.Json.Newtonsoft.csproj
@@ -0,0 +1,35 @@
+
+
+
+ netstandard2.1; netcoreapp6.0
+ disable
+ disable
+ true
+ ConcreteMC.MolangSharp.Json.Newtonsoft
+ ConcreteMC.MolangSharp.Json.Newtonsoft
+ ConcreteMC.MolangSharp.Json.Newtonsoft
+ Implements a JsonConverter for Molang expressions
+ Kenny van Vulpen
+ Kenny van Vulpen
+ Copyright Kenny van Vulpen 2019-2022
+ Minecraft Molang MCPE MoLang bedrock mojang alex json newtonsoft newtonsoft.json
+
+ git
+ https://github.com/ConcreteMC/MolangSharp.git
+ https://github.com/ConcreteMC/MolangSharp
+
+ 1.0.0
+
+ - Initial release
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/MolangSharp.Tests/BinaryOpTest.cs b/src/MolangSharp.Tests/BinaryOpTest.cs
new file mode 100644
index 0000000..b1fbd83
--- /dev/null
+++ b/src/MolangSharp.Tests/BinaryOpTest.cs
@@ -0,0 +1,38 @@
+using ConcreteMC.MolangSharp.Parser;
+using ConcreteMC.MolangSharp.Runtime;
+using ConcreteMC.MolangSharp.Runtime.Value;
+using ConcreteMC.MolangSharp.Utils;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace ConcreteMC.MolangSharp.Tests;
+
+[TestClass]
+public class BinaryOpTest
+{
+ private MoLangRuntime _runtime;
+
+ [TestInitialize]
+ public void Setup()
+ {
+ _runtime = new MoLangRuntime();
+ }
+
+ [TestMethod]
+ public void ZeroReturnsFalse()
+ {
+ var parsed = MoLangParser.Parse("return 0 ?? 'test'");
+ var result = _runtime.Execute(parsed);
+
+ Assert.AreEqual(result.AsString(), "test");
+ }
+
+ [TestMethod]
+ public void ZeroVariableReturnsFalse()
+ {
+ _runtime.Environment.SetValue(new MoPath("variable.lastNumber"), new DoubleValue(0));
+ var parsed = MoLangParser.Parse("return variable.lastNumber ?? 'test'");
+ var result = _runtime.Execute(parsed);
+
+ Assert.AreEqual(result.AsString(), "test");
+ }
+}
\ No newline at end of file
diff --git a/src/MolangSharp.Tests/InteropOptimizationVisitorTest.cs b/src/MolangSharp.Tests/InteropOptimizationVisitorTest.cs
new file mode 100644
index 0000000..f1048cd
--- /dev/null
+++ b/src/MolangSharp.Tests/InteropOptimizationVisitorTest.cs
@@ -0,0 +1,95 @@
+using ConcreteMC.MolangSharp.Attributes;
+using ConcreteMC.MolangSharp.Parser;
+using ConcreteMC.MolangSharp.Parser.Tokenizer;
+using ConcreteMC.MolangSharp.Parser.Visitors.InteropOptimization;
+using ConcreteMC.MolangSharp.Runtime;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace ConcreteMC.MolangSharp.Tests;
+
+[TestClass]
+public class InteropOptimizationVisitorTest
+{
+ [TestMethod]
+ public void VerifyFunctionCallOptimization()
+ {
+ var entity = new TestEntity();
+ var parser = new MoLangParser(new TokenIterator("query.getHealth()"));
+ parser.ExpressionTraverser.Visitors.Add(new InteropOptimizationVisitor(new InteropOptimizationVisitorOptions()
+ {
+ InteropEntries = new []
+ {
+ new InteropEntry("query", entity)
+ }
+ }));
+
+ var expression = parser.Parse();
+ Assert.IsInstanceOfType(expression, typeof(OptimizedFunctionCallExpression));
+
+ var runtime = new MoLangRuntime();
+ var result = runtime.Execute(expression);
+ Assert.IsNotNull(result);
+ Assert.AreEqual(20, result.AsDouble());
+ }
+
+ [TestMethod]
+ public void VerifyPropertyAccessOptimization()
+ {
+ var entity = new TestEntity();
+ var parser = new MoLangParser(new TokenIterator("query.health"));
+ parser.ExpressionTraverser.Visitors.Add(new InteropOptimizationVisitor(new InteropOptimizationVisitorOptions()
+ {
+ InteropEntries = new []
+ {
+ new InteropEntry("query", entity)
+ }
+ }));
+
+ var expression = parser.Parse();
+ Assert.IsInstanceOfType(expression, typeof(OptimizedNameExpression));
+
+ var runtime = new MoLangRuntime();
+ var result = runtime.Execute(expression);
+ Assert.IsNotNull(result);
+ Assert.AreEqual(20, result.AsDouble());
+ }
+
+ [TestMethod]
+ public void VerifyPropertyAssignOptimization()
+ {
+ var entity = new TestEntity();
+ var parser = new MoLangParser(new TokenIterator("query.health = 21"));
+ parser.ExpressionTraverser.Visitors.Add(new InteropOptimizationVisitor(new InteropOptimizationVisitorOptions()
+ {
+ InteropEntries = new []
+ {
+ new InteropEntry("query", entity)
+ }
+ }));
+
+ var expression = parser.Parse();
+ Assert.IsInstanceOfType(expression, typeof(OptimizedAssignExpression));
+
+ var runtime = new MoLangRuntime();
+ var result = runtime.Execute(expression);
+ Assert.IsNotNull(result);
+ Assert.AreEqual(21, entity.Health);
+ }
+
+ private class TestEntity
+ {
+ [MoProperty("health")]
+ public double Health { get; set; }
+
+ public TestEntity()
+ {
+ Health = 20;
+ }
+
+ [MoFunction("getHealth")]
+ public double GetHealth()
+ {
+ return Health;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/MolangSharp.Tests/MathOptimizationVisitorTest.cs b/src/MolangSharp.Tests/MathOptimizationVisitorTest.cs
new file mode 100644
index 0000000..dfb80b4
--- /dev/null
+++ b/src/MolangSharp.Tests/MathOptimizationVisitorTest.cs
@@ -0,0 +1,42 @@
+using ConcreteMC.MolangSharp.Parser;
+using ConcreteMC.MolangSharp.Parser.Expressions;
+using ConcreteMC.MolangSharp.Parser.Tokenizer;
+using ConcreteMC.MolangSharp.Parser.Visitors;
+using ConcreteMC.MolangSharp.Runtime;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace ConcreteMC.MolangSharp.Tests;
+
+[TestClass]
+public class MathOptimizationVisitorTest
+{
+ [TestMethod]
+ public void Constants()
+ {
+ MoLangParser parser = new MoLangParser(new TokenIterator("7 + 2 * (6 + 3) / 3 - 7"));
+ parser.ExpressionTraverser.Visitors.Add(new MathOptimizationVisitor());
+
+ var expr = parser.Parse();
+ Assert.IsInstanceOfType(expr, typeof(NumberExpression));
+
+ MoLangRuntime runtime = new MoLangRuntime();
+ var result = runtime.Execute(expr);
+
+ Assert.AreEqual(6, result.AsDouble());
+ }
+
+ [TestMethod]
+ public void MathFunctions()
+ {
+ MoLangParser parser = new MoLangParser(new TokenIterator("math.floor(10.1 + 20.1)"));
+ parser.ExpressionTraverser.Visitors.Add(new MathOptimizationVisitor());
+
+ var expr = parser.Parse();
+ Assert.IsInstanceOfType(expr, typeof(NumberExpression));
+
+ MoLangRuntime runtime = new MoLangRuntime();
+ var result = runtime.Execute(expr);
+
+ Assert.AreEqual(30, result.AsDouble());
+ }
+}
\ No newline at end of file
diff --git a/src/MolangSharp.Tests/MathTest.cs b/src/MolangSharp.Tests/MathTest.cs
index 529fb39..e261aee 100644
--- a/src/MolangSharp.Tests/MathTest.cs
+++ b/src/MolangSharp.Tests/MathTest.cs
@@ -90,7 +90,7 @@ public void ArithmeticsDivision()
[TestMethod]
public void OrderOfOperations()
{
- var runtime = Setup(10, 20);
+ var runtime = Setup(10, 20);
var expr = MoLangParser.Parse("v.b + (7 + 2 * (6 + 3) / 3 - 7) + v.a");
var result = runtime.Execute(expr);
diff --git a/src/MolangSharp.Tests/PerformanceTest.cs b/src/MolangSharp.Tests/PerformanceTest.cs
new file mode 100644
index 0000000..c9ef392
--- /dev/null
+++ b/src/MolangSharp.Tests/PerformanceTest.cs
@@ -0,0 +1,192 @@
+using System.Diagnostics;
+using ConcreteMC.MolangSharp.Parser;
+using ConcreteMC.MolangSharp.Runtime;
+using ConcreteMC.MolangSharp.Runtime.Value;
+using ConcreteMC.MolangSharp.Utils;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace ConcreteMC.MolangSharp.Tests;
+
+[TestClass]
+public class PerformanceTest
+{
+ private Stopwatch _sw;
+ private MoLangEnvironment _environment;
+ private MoLangRuntime _runtime;
+
+ [TestInitialize]
+ public void Init()
+ {
+ _sw = new Stopwatch();
+ _environment = new MoLangEnvironment();
+ _runtime = new MoLangRuntime(_environment);
+
+ _environment.SetValue(new MoPath("variable.test"), DoubleValue.One);
+ _environment.SetValue(new MoPath("variable.life_time"), DoubleValue.Zero);
+
+ MoLangRuntimeConfiguration.UseDummyValuesInsteadOfExceptions = false;
+ MoLangRuntimeConfiguration.UseMoLangStackTrace = true;
+ }
+
+ [TestCleanup]
+ public void Cleanup()
+ {
+ MoLangRuntimeConfiguration.UseMoLangStackTrace = true;
+ MoLangRuntimeConfiguration.UseDummyValuesInsteadOfExceptions = false;
+ }
+
+ [TestMethod]
+ public void TestVariableReadingMath()
+ {
+ const int iterations = 5000000;
+ long total = 0;
+
+ var expression = MoLangParser.Parse("variable.test * 20");
+ for (int i = 0; i < iterations; i++)
+ {
+ _sw.Restart();
+ var a = _runtime.Execute(expression);
+ total += _sw.ElapsedMilliseconds;
+ Assert.AreEqual(20, a.AsDouble());
+ }
+
+ Debug.WriteLine($"Average: {((double)total / iterations):N10}ms");
+ }
+
+ [TestMethod]
+ public void TestVariableReading()
+ {
+ const int iterations = 5000000;
+ long total = 0;
+
+ var expression = MoLangParser.Parse("variable.test");
+ for (int i = 0; i < iterations; i++)
+ {
+ _sw.Restart();
+ var a = _runtime.Execute(expression);
+ total += _sw.ElapsedMilliseconds;
+ Assert.AreEqual(1, a.AsDouble());
+ }
+
+ Debug.WriteLine($"Average: {((double)total / iterations):N10}ms");
+ }
+
+ [TestMethod]
+ public void TestVariableWriting()
+ {
+ const int iterations = 5000000;
+ long total = 0;
+
+ var expression = MoLangParser.Parse("variable.test = 20");
+ for (int i = 0; i < iterations; i++)
+ {
+ _sw.Restart();
+ _runtime.Execute(expression);
+ total += _sw.ElapsedMilliseconds;
+ }
+
+ Debug.WriteLine($"Average: {((double)total / iterations):N10}ms");
+ }
+
+ [TestMethod]
+ public void TestInvalidPathPerformance()
+ {
+ const int iterations = 50000;
+ long total = 0;
+
+ var expression = MoLangParser.Parse("fr.test");
+ for (int i = 0; i < iterations; i++)
+ {
+ _sw.Restart();
+
+ try
+ {
+ _runtime.Execute(expression);
+ }
+ catch
+ {
+
+ }
+ total += _sw.ElapsedMilliseconds;
+ }
+
+ Debug.WriteLine($"Average: {((double)total / iterations):N10}ms");
+ }
+
+ [TestMethod]
+ public void TestDummyValuesConfiguration()
+ {
+ var expression = MoLangParser.Parse("return fr.test");
+ MoLangRuntimeConfiguration.UseDummyValuesInsteadOfExceptions = true;
+ var result = _runtime.Execute(expression);
+ Assert.AreEqual(0d, result.AsDouble());
+ }
+
+ [TestMethod]
+ public void RawPerformanceTest()
+ {
+ var parsed = MoLangParser.Parse("math.cos(v.life_time * 60) * -45.0");
+
+ RunPerformanceTestAndOutput(parsed);
+ }
+
+ [TestMethod]
+ public void InvalidPathPerformanceTest()
+ {
+ var parsed = MoLangParser.Parse("math.cos(a.life_time * 60) * -45.0");
+
+ RunPerformanceTestAndOutput(parsed);
+ }
+
+ [TestMethod]
+ public void InvalidPathPerformanceWithoutExceptionThrowingTest()
+ {
+ MoLangRuntimeConfiguration.UseDummyValuesInsteadOfExceptions = true;
+ var parsed = MoLangParser.Parse("math.cos(a.life_time * 60) * -45.0");
+
+ RunPerformanceTestAndOutput(parsed);
+ }
+
+ [TestMethod]
+ public void InvalidPathPerformanceWithoutStackTraceThrowingTest()
+ {
+ MoLangRuntimeConfiguration.UseMoLangStackTrace = false;
+ var parsed = MoLangParser.Parse("math.cos(a.life_time * 60) * -45.0");
+
+ RunPerformanceTestAndOutput(parsed);
+ }
+
+
+ private void LogOutput(long iterations, long timespent)
+ {
+ Debug.WriteLine($"Iterations: {iterations}. Elapsed: {timespent}ms Avg: {timespent / (double)iterations}ms");
+ }
+
+ private void RunPerformanceTestAndOutput(IExpression expression)
+ {
+ var result = RunPerformanceTest(expression);
+ LogOutput(result.iterations, result.timeSpent);
+ }
+
+ private (long iterations, long timeSpent) RunPerformanceTest(IExpression expression)
+ {
+ var iterations = 0L;
+ var sw = Stopwatch.StartNew();
+ while (sw.ElapsedMilliseconds < 1000)
+ {
+ try
+ {
+ var e = _runtime.Execute(expression);
+ }
+ catch
+ {
+
+ }
+
+ iterations++;
+ }
+ sw.Stop();
+
+ return (iterations, sw.ElapsedMilliseconds);
+ }
+}
\ No newline at end of file
diff --git a/src/MolangSharp/Attributes/MoFunctionAttribute.cs b/src/MolangSharp/Attributes/MoFunctionAttribute.cs
index 41c10b8..fdb795f 100644
--- a/src/MolangSharp/Attributes/MoFunctionAttribute.cs
+++ b/src/MolangSharp/Attributes/MoFunctionAttribute.cs
@@ -2,11 +2,21 @@
namespace ConcreteMC.MolangSharp.Attributes
{
+ ///
+ /// Identifies a method as a MoLang Accessible function
+ ///
[AttributeUsage(AttributeTargets.Method)]
public class MoFunctionAttribute : Attribute
{
+ ///
+ /// The names this method is exposed via
+ ///
public string[] Name { get; }
+ ///
+ /// Identifies a method as a MoLang Accessible function
+ ///
+ /// The names this function is callable by in a MoLang expression
public MoFunctionAttribute(params string[] functionNames)
{
Name = functionNames;
diff --git a/src/MolangSharp/Attributes/MoObservableAttribute.cs b/src/MolangSharp/Attributes/MoObservableAttribute.cs
index c24bf89..4b32317 100644
--- a/src/MolangSharp/Attributes/MoObservableAttribute.cs
+++ b/src/MolangSharp/Attributes/MoObservableAttribute.cs
@@ -2,9 +2,6 @@
namespace ConcreteMC.MolangSharp.Attributes
{
- [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
- public class MoObservableAttribute : Attribute
- {
-
- }
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
+ public class MoObservableAttribute : Attribute { }
}
\ No newline at end of file
diff --git a/src/MolangSharp/Attributes/MoPropertyAttribute.cs b/src/MolangSharp/Attributes/MoPropertyAttribute.cs
index 9ea000f..74b97a8 100644
--- a/src/MolangSharp/Attributes/MoPropertyAttribute.cs
+++ b/src/MolangSharp/Attributes/MoPropertyAttribute.cs
@@ -2,11 +2,21 @@
namespace ConcreteMC.MolangSharp.Attributes
{
+ ///
+ /// Identifies a class property/field as MoLang Accessible
+ ///
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
public class MoPropertyAttribute : Attribute
{
+ ///
+ /// The name of this property as used in a MoLang expression
+ ///
public string Name { get; }
+ ///
+ /// Identifies a class property/field as MoLang Accessible
+ ///
+ /// The name used for this property.
public MoPropertyAttribute(string name)
{
Name = name;
diff --git a/src/MolangSharp/MolangSharp.csproj b/src/MolangSharp/MolangSharp.csproj
index a7bc644..546a62d 100644
--- a/src/MolangSharp/MolangSharp.csproj
+++ b/src/MolangSharp/MolangSharp.csproj
@@ -4,9 +4,9 @@
Debug;Release
AnyCPU;x64
true
- 1.0.0
+ 1.0.3
true
- netstandard2.1
+ netstandard2.1; netcoreapp6.0
https://github.com/ConcreteMC/MolangSharp
https://github.com/ConcreteMC/MolangSharp.git
MPL-2.0
@@ -15,25 +15,25 @@
Kenny van Vulpen
Copyright Kenny van Vulpen 2019-2022
Minecraft Molang MCPE MoLang bedrock mojang alex
- - Added a maths optimization step
-- Added documentation for a bunch of classes
+
+ - Fixed an issue with struct accessor
ConcreteMC.MolangSharp
ConcreteMC.MolangSharp
- 1.0.1
+ 1.0.3
ConcreteMC.MolangSharp
git
ConcreteMC.MolangSharp
- ConcreteMC.MolangSharp
+ Alex.MoLang
- bin\x64\Debug\netstandard2.1\Alex.MoLang.xml
+ bin\x64\Debug\netstandard2.1\Alex.MoLang.xml
- bin\x64\Release\netstandard2.1\Alex.MoLang.xml
+ bin\x64\Release\netstandard2.1\Alex.MoLang.xml
diff --git a/src/MolangSharp/Parser/Expression.cs b/src/MolangSharp/Parser/Expression.cs
new file mode 100644
index 0000000..00b0bcf
--- /dev/null
+++ b/src/MolangSharp/Parser/Expression.cs
@@ -0,0 +1,40 @@
+using System;
+using ConcreteMC.MolangSharp.Runtime;
+using ConcreteMC.MolangSharp.Runtime.Value;
+
+namespace ConcreteMC.MolangSharp.Parser
+{
+ ///
+ /// An abstract base class for all Expressions
+ ///
+ public abstract class Expression : IExpression
+ {
+ ///
+ /// The parameters used by this expression
+ ///
+ public IExpression[] Parameters { get; set; }
+
+ ///
+ public ExpressionMeta Meta { get; } = new ExpressionMeta();
+
+ ///
+ /// Create a new instance of the class
+ ///
+ ///
+ /// The parameters used by this expression
+ ///
+ protected Expression(params IExpression[] parameters)
+ {
+ Parameters = parameters;
+ }
+
+ ///
+ public abstract IMoValue Evaluate(MoScope scope, MoLangEnvironment environment);
+
+ ///
+ public virtual void Assign(MoScope scope, MoLangEnvironment environment, IMoValue value)
+ {
+ throw new Exception("Cannot assign a value to " + this.GetType());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/MolangSharp/Parser/ExpressionMeta.cs b/src/MolangSharp/Parser/ExpressionMeta.cs
new file mode 100644
index 0000000..0eaa6c9
--- /dev/null
+++ b/src/MolangSharp/Parser/ExpressionMeta.cs
@@ -0,0 +1,72 @@
+using System.Text;
+using ConcreteMC.MolangSharp.Parser.Tokenizer;
+
+namespace ConcreteMC.MolangSharp.Parser
+{
+ ///
+ /// Contains metadata about an expression
+ ///
+ public class ExpressionMeta
+ {
+ ///
+ /// The token
+ ///
+ public Token Token { get; set; }
+
+ ///
+ /// The parent expression
+ ///
+ public IExpression Parent { get; set; }
+
+ ///
+ /// The previous expression
+ ///
+ public IExpression Previous { get; set; }
+
+ ///
+ /// The next expression
+ ///
+ public IExpression Next { get; set; }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder(255);
+ bool includeFileInfoIfAvailable;
+
+ if (Token != null)
+ {
+ sb.Append(Token.Text);
+ includeFileInfoIfAvailable = true;
+ }
+ else
+ {
+ includeFileInfoIfAvailable = false;
+ }
+
+ if (includeFileInfoIfAvailable)
+ {
+ // sb.Append(" at offset ");
+
+ // if (_nativeOffset == OFFSET_UNKNOWN)
+ // sb.Append("");
+ // else
+ // sb.Append(_nativeOffset);
+
+ sb.Append(" in file:line:column ");
+ sb.Append("");
+ sb.Append(':');
+ sb.Append(Token.Position.LineNumber);
+ sb.Append(':');
+ sb.Append(Token.Position.Index);
+ }
+ else
+ {
+ sb.Append("");
+ }
+
+ sb.AppendLine();
+
+ return sb.ToString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/MolangSharp/Parser/ExprTraverser.cs b/src/MolangSharp/Parser/ExpressionTraverser.cs
similarity index 77%
rename from src/MolangSharp/Parser/ExprTraverser.cs
rename to src/MolangSharp/Parser/ExpressionTraverser.cs
index 60ac83f..6a86d54 100644
--- a/src/MolangSharp/Parser/ExprTraverser.cs
+++ b/src/MolangSharp/Parser/ExpressionTraverser.cs
@@ -7,12 +7,23 @@
namespace ConcreteMC.MolangSharp.Parser
{
- public class ExprTraverser
+ ///
+ /// Traverses arrays of with 's
+ ///
+ public class ExpressionTraverser
{
- public readonly List Visitors = new List();
+ ///
+ /// The list of that will visit the traversed expressions
+ ///
+ public readonly List Visitors = new List();
private bool _stop = false;
+ ///
+ /// Traverse an array of expressions.
+ ///
+ /// The array of expressions to visit
+ /// The traversed array of expressions
public IExpression[] Traverse(IExpression[] expressions)
{
TraverseArray(expressions);
@@ -22,7 +33,7 @@ public IExpression[] Traverse(IExpression[] expressions)
private void TraverseArray(IExpression[] expressions)
{
- foreach (IExprVisitor visitor in Visitors)
+ foreach (IExpressionVisitor visitor in Visitors)
{
visitor.BeforeTraverse(expressions);
}
@@ -42,7 +53,7 @@ private void TraverseArray(IExpression[] expressions)
}
}
- foreach (IExprVisitor visitor in Visitors)
+ foreach (IExpressionVisitor visitor in Visitors)
{
visitor.AfterTraverse(expressions);
}
@@ -54,10 +65,11 @@ private IExpression TraverseExpr(IExpression expression, IExpression parent)
expression.Meta.Parent = parent;
var parameters = expression.Parameters;
+
for (var index = 0; index < parameters.Length; index++)
{
parameters[index] = TraverseExpr(parameters[index], expression);
-
+
if (_stop)
{
break;
@@ -71,7 +83,7 @@ private IExpression TraverseExpr(IExpression expression, IExpression parent)
return expression;
}
- internal IExpression Visit(IExpression expression)
+ private IExpression Visit(IExpression expression)
{
foreach (var visitor in Visitors)
{
diff --git a/src/MolangSharp/Parser/ExpressionVisitor.cs b/src/MolangSharp/Parser/ExpressionVisitor.cs
new file mode 100644
index 0000000..5199b53
--- /dev/null
+++ b/src/MolangSharp/Parser/ExpressionVisitor.cs
@@ -0,0 +1,20 @@
+namespace ConcreteMC.MolangSharp.Parser
+{
+ ///
+ /// An abstract class that can be used as a base for any ExpressionVisitor
+ ///
+ public abstract class ExpressionVisitor : IExpressionVisitor
+ {
+ ///
+ public virtual void BeforeTraverse(IExpression[] expressions) { }
+
+ ///
+ public abstract IExpression OnVisit(ExpressionTraverser traverser, IExpression expression);
+
+ ///
+ public virtual void OnLeave(IExpression expression) { }
+
+ ///
+ public virtual void AfterTraverse(IExpression[] expressions) { }
+ }
+}
\ No newline at end of file
diff --git a/src/MolangSharp/Parser/Expressions/ArrayAccessExpression.cs b/src/MolangSharp/Parser/Expressions/ArrayAccessExpression.cs
index 2fa1d1c..b980fc6 100644
--- a/src/MolangSharp/Parser/Expressions/ArrayAccessExpression.cs
+++ b/src/MolangSharp/Parser/Expressions/ArrayAccessExpression.cs
@@ -10,15 +10,14 @@ public class ArrayAccessExpression : Expression
public IExpression Array => Parameters[0];
public IExpression Index => Parameters[1];
- public ArrayAccessExpression(IExpression array, IExpression index) : base(array, index)
- {
- }
+ public ArrayAccessExpression(IExpression array, IExpression index) : base(array, index) { }
///
public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment)
{
var index = (int) Index.Evaluate(scope, environment).AsDouble();
MoPath path;
+
if (Array is NameExpression nameExpression)
{
var p = nameExpression.Name;
@@ -31,9 +30,10 @@ public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment)
}
var array = environment.GetValue(path);
+
if (array is ArrayStruct asArray)
return asArray[index];
-
+
return environment.GetValue(path);
}
@@ -41,8 +41,9 @@ public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment)
public override void Assign(MoScope scope, MoLangEnvironment environment, IMoValue value)
{
var index = (int) Index.Evaluate(scope, environment).AsDouble();
-
+
MoPath path;
+
if (Array is NameExpression nameExpression)
{
var p = nameExpression.Name;
@@ -53,8 +54,9 @@ public override void Assign(MoScope scope, MoLangEnvironment environment, IMoVal
var eval = Array.Evaluate(scope, environment);
path = new MoPath($"{eval.AsString()}");
}
-
+
var array = environment.GetValue(path);
+
if (array is ArrayStruct asArray)
{
asArray[index] = value;
diff --git a/src/MolangSharp/Parser/Expressions/AssignExpression.cs b/src/MolangSharp/Parser/Expressions/AssignExpression.cs
index 2a702b0..a795a85 100644
--- a/src/MolangSharp/Parser/Expressions/AssignExpression.cs
+++ b/src/MolangSharp/Parser/Expressions/AssignExpression.cs
@@ -8,10 +8,7 @@ public class AssignExpression : Expression
public IExpression Variable => Parameters[0];
public IExpression Expression => Parameters[1];
- public AssignExpression(IExpression variable, IExpression expr) : base(variable, expr)
- {
-
- }
+ public AssignExpression(IExpression variable, IExpression expr) : base(variable, expr) { }
///
public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment)
diff --git a/src/MolangSharp/Parser/Expressions/BinaryOp/CoalesceExpression.cs b/src/MolangSharp/Parser/Expressions/BinaryOp/CoalesceExpression.cs
index b08ad22..23c9c2d 100644
--- a/src/MolangSharp/Parser/Expressions/BinaryOp/CoalesceExpression.cs
+++ b/src/MolangSharp/Parser/Expressions/BinaryOp/CoalesceExpression.cs
@@ -13,9 +13,10 @@ public CoalesceExpression(IExpression l, IExpression r) : base(l, r) { }
public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment)
{
IMoValue evalLeft = Left.Evaluate(scope, environment);
- IMoValue value = environment.GetValue(new MoPath(evalLeft.AsString()));
- if (value == null || !value.AsBool())
+ //IMoValue value = environment.GetValue(new MoPath(evalLeft.AsString()));
+
+ if (evalLeft == null || !evalLeft.AsBool())
{
return Right.Evaluate(scope, environment);
}
diff --git a/src/MolangSharp/Parser/Expressions/BinaryOp/PlusExpression.cs b/src/MolangSharp/Parser/Expressions/BinaryOp/PlusExpression.cs
index fc125bb..6ce086e 100644
--- a/src/MolangSharp/Parser/Expressions/BinaryOp/PlusExpression.cs
+++ b/src/MolangSharp/Parser/Expressions/BinaryOp/PlusExpression.cs
@@ -20,7 +20,7 @@ public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment)
}
catch (Exception ex)
{
- throw new MoLangRuntimeException("An unexpected error occured.", ex);
+ throw new MoLangRuntimeException(this, "An unexpected error occured.", ex);
}
}
diff --git a/src/MolangSharp/Parser/Expressions/BinaryOpExpression.cs b/src/MolangSharp/Parser/Expressions/BinaryOpExpression.cs
index fbc2c30..fe40521 100644
--- a/src/MolangSharp/Parser/Expressions/BinaryOpExpression.cs
+++ b/src/MolangSharp/Parser/Expressions/BinaryOpExpression.cs
@@ -17,9 +17,7 @@ public IExpression Right
set { Parameters[1] = value; }
}
- protected BinaryOpExpression(IExpression l, IExpression r) : base(l,r)
- {
- }
+ protected BinaryOpExpression(IExpression l, IExpression r) : base(l, r) { }
public abstract string GetSigil();
}
diff --git a/src/MolangSharp/Parser/Expressions/ForEachExpression.cs b/src/MolangSharp/Parser/Expressions/ForEachExpression.cs
index f3cc2fe..011a93a 100644
--- a/src/MolangSharp/Parser/Expressions/ForEachExpression.cs
+++ b/src/MolangSharp/Parser/Expressions/ForEachExpression.cs
@@ -11,10 +11,8 @@ public class ForEachExpression : Expression
public IExpression Array => Parameters[1];
public IExpression Body => Parameters[2];
- public ForEachExpression(IExpression variable, IExpression array, IExpression body) : base(variable, array, body)
- {
-
- }
+ public ForEachExpression(IExpression variable, IExpression array, IExpression body) : base(
+ variable, array, body) { }
///
public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment)
diff --git a/src/MolangSharp/Parser/Expressions/FuncCallExpression.cs b/src/MolangSharp/Parser/Expressions/FuncCallExpression.cs
index 78c0fad..4a419a5 100644
--- a/src/MolangSharp/Parser/Expressions/FuncCallExpression.cs
+++ b/src/MolangSharp/Parser/Expressions/FuncCallExpression.cs
@@ -7,6 +7,7 @@ namespace ConcreteMC.MolangSharp.Parser.Expressions
public class FuncCallExpression : Expression
{
public MoPath Name { get; set; }
+
public FuncCallExpression(MoPath name, IExpression[] args) : base(args)
{
Name = name;
@@ -16,7 +17,7 @@ public FuncCallExpression(MoPath name, IExpression[] args) : base(args)
public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment)
{
//List p = Args.ToList();
- MoPath name = Name;/* Name is NameExpression expression ? expression.Name :
+ MoPath name = Name; /* Name is NameExpression expression ? expression.Name :
new MoPath(Name.Evaluate(scope, environment).ToString());*/
IMoValue[] arguments = new IMoValue[Parameters.Length];
diff --git a/src/MolangSharp/Parser/Expressions/LoopExpression.cs b/src/MolangSharp/Parser/Expressions/LoopExpression.cs
index c31e082..537e4e7 100644
--- a/src/MolangSharp/Parser/Expressions/LoopExpression.cs
+++ b/src/MolangSharp/Parser/Expressions/LoopExpression.cs
@@ -8,18 +8,13 @@ public class LoopExpression : Expression
public IExpression Count => Parameters[0];
public IExpression Body => Parameters[1];
- public LoopExpression(IExpression count, IExpression body) : base(count, body)
- {
- }
+ public LoopExpression(IExpression count, IExpression body) : base(count, body) { }
///
public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment)
{
- int loop = (int)(double)Count.Evaluate(scope, environment).Value;
- MoScope subScope = new MoScope(scope.Runtime)
- {
- Runtime = scope.Runtime
- };
+ int loop = (int) Count.Evaluate(scope, environment).AsDouble();
+ MoScope subScope = new MoScope(scope.Runtime) {Runtime = scope.Runtime};
while (loop > 0)
{
diff --git a/src/MolangSharp/Parser/Expressions/NumberExpression.cs b/src/MolangSharp/Parser/Expressions/NumberExpression.cs
index ce9b270..9dc6990 100644
--- a/src/MolangSharp/Parser/Expressions/NumberExpression.cs
+++ b/src/MolangSharp/Parser/Expressions/NumberExpression.cs
@@ -18,7 +18,7 @@ public NumberExpression(double value) : base()
{
_value = new DoubleValue(value);
}
-
+
public NumberExpression(IMoValue value) : base()
{
_value = value;
diff --git a/src/MolangSharp/Parser/Expressions/ReturnExpression.cs b/src/MolangSharp/Parser/Expressions/ReturnExpression.cs
index 758e410..4c33305 100644
--- a/src/MolangSharp/Parser/Expressions/ReturnExpression.cs
+++ b/src/MolangSharp/Parser/Expressions/ReturnExpression.cs
@@ -15,8 +15,6 @@ public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment)
}
///
- public ReturnExpression(IExpression value) : base(value)
- {
- }
+ public ReturnExpression(IExpression value) : base(value) { }
}
}
\ No newline at end of file
diff --git a/src/MolangSharp/Parser/Expressions/ScriptExpression.cs b/src/MolangSharp/Parser/Expressions/ScriptExpression.cs
index 9c6fa96..ab68147 100644
--- a/src/MolangSharp/Parser/Expressions/ScriptExpression.cs
+++ b/src/MolangSharp/Parser/Expressions/ScriptExpression.cs
@@ -5,42 +5,39 @@
namespace ConcreteMC.MolangSharp.Parser.Expressions
{
- public class ScriptExpression : Expression
- {
- public ScriptExpression(IExpression[] expressions) : base(expressions)
- {
-
- }
-
- public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment)
- {
- IMoValue result = DoubleValue.Zero;
- // MoScope scope = new MoScope(this);
-
- foreach (IExpression expression in Parameters)
- {
- if (expression == null)
- continue;
+ public class ScriptExpression : Expression
+ {
+ public ScriptExpression(IExpression[] expressions) : base(expressions) { }
- try
- {
- result = expression.Evaluate(scope, environment);
+ public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment)
+ {
+ IMoValue result = DoubleValue.Zero;
+ // MoScope scope = new MoScope(this);
- if (scope.ReturnValue != null)
- {
- result = scope.ReturnValue;
+ foreach (IExpression expression in Parameters)
+ {
+ if (expression == null)
+ continue;
- break;
- }
- }
- catch (Exception ex)
- {
- throw new MoLangRuntimeException(
- expression, "An error occured while evaluating the expression", ex);
- }
- }
+ try
+ {
+ result = expression.Evaluate(scope, environment);
- return result;
- }
- }
+ if (scope.ReturnValue != null)
+ {
+ result = scope.ReturnValue;
+
+ break;
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new MoLangRuntimeException(
+ expression, "An error occured while evaluating the expression", ex);
+ }
+ }
+
+ return result;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/MolangSharp/Parser/Expressions/StatementExpression.cs b/src/MolangSharp/Parser/Expressions/StatementExpression.cs
index 8846830..ea9d452 100644
--- a/src/MolangSharp/Parser/Expressions/StatementExpression.cs
+++ b/src/MolangSharp/Parser/Expressions/StatementExpression.cs
@@ -26,8 +26,6 @@ public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment)
}
///
- public StatementExpression(IExpression[] value) : base(value)
- {
- }
+ public StatementExpression(IExpression[] value) : base(value) { }
}
}
\ No newline at end of file
diff --git a/src/MolangSharp/Parser/Expressions/TernaryExpression.cs b/src/MolangSharp/Parser/Expressions/TernaryExpression.cs
index f774290..78ed88f 100644
--- a/src/MolangSharp/Parser/Expressions/TernaryExpression.cs
+++ b/src/MolangSharp/Parser/Expressions/TernaryExpression.cs
@@ -9,10 +9,8 @@ public class TernaryExpression : Expression
public IExpression ThenExpr => Parameters[1];
public IExpression ElseExpr => Parameters[2];
- public TernaryExpression(IExpression condition, IExpression thenExpr, IExpression elseExpr) : base(condition, thenExpr, elseExpr)
- {
-
- }
+ public TernaryExpression(IExpression condition, IExpression thenExpr, IExpression elseExpr) : base(
+ condition, thenExpr, elseExpr) { }
///
public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment)
diff --git a/src/MolangSharp/Parser/Expressions/UnaryMinusExpression.cs b/src/MolangSharp/Parser/Expressions/UnaryMinusExpression.cs
index a9affdf..8e84b1c 100644
--- a/src/MolangSharp/Parser/Expressions/UnaryMinusExpression.cs
+++ b/src/MolangSharp/Parser/Expressions/UnaryMinusExpression.cs
@@ -12,9 +12,6 @@ public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment)
}
///
- public UnaryMinusExpression(IExpression value) : base(value)
- {
-
- }
+ public UnaryMinusExpression(IExpression value) : base(value) { }
}
}
\ No newline at end of file
diff --git a/src/MolangSharp/Parser/Expressions/UnaryPlusExpression.cs b/src/MolangSharp/Parser/Expressions/UnaryPlusExpression.cs
index e2e0852..47e0863 100644
--- a/src/MolangSharp/Parser/Expressions/UnaryPlusExpression.cs
+++ b/src/MolangSharp/Parser/Expressions/UnaryPlusExpression.cs
@@ -10,7 +10,7 @@ public UnaryPlusExpression(IExpression value) : base(value)
{
//_value = value;
}
-
+
///
public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment)
{
diff --git a/src/MolangSharp/Parser/IExprVisitor.cs b/src/MolangSharp/Parser/IExprVisitor.cs
deleted file mode 100644
index a83bb03..0000000
--- a/src/MolangSharp/Parser/IExprVisitor.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-namespace ConcreteMC.MolangSharp.Parser
-{
- public interface IExprVisitor
- {
- void BeforeTraverse(IExpression[] expressions);
-
- IExpression OnVisit(ExprTraverser traverser, IExpression expression);
-
- void OnLeave(IExpression expression);
-
- void AfterTraverse(IExpression[] expressions);
- }
-
- public abstract class ExprVisitor : IExprVisitor
- {
- ///
- public virtual void BeforeTraverse(IExpression[] expressions) { }
-
- ///
- public abstract IExpression OnVisit(ExprTraverser traverser, IExpression expression);
-
- ///
- public virtual void OnLeave(IExpression expression) { }
-
- ///
- public virtual void AfterTraverse(IExpression[] expressions) { }
- }
-}
\ No newline at end of file
diff --git a/src/MolangSharp/Parser/IExpression.cs b/src/MolangSharp/Parser/IExpression.cs
index e5895c6..a846f59 100644
--- a/src/MolangSharp/Parser/IExpression.cs
+++ b/src/MolangSharp/Parser/IExpression.cs
@@ -1,93 +1,40 @@
-using System;
-using System.Text;
-using ConcreteMC.MolangSharp.Parser.Tokenizer;
using ConcreteMC.MolangSharp.Runtime;
using ConcreteMC.MolangSharp.Runtime.Value;
namespace ConcreteMC.MolangSharp.Parser
{
+ ///
+ /// The interface for all expressions to implement
+ ///
public interface IExpression
{
+ ///
+ /// Contains metadata about this expression
+ ///
ExpressionMeta Meta { get; }
+ ///
+ /// Evaluate the expression
+ ///
+ /// The current scope
+ /// The environment provided by the
+ /// The value returned by the expression
IMoValue Evaluate(MoScope scope, MoLangEnvironment environment);
+ ///
+ /// Invoked when trying to assign a value to a property/field
+ ///
+ /// The current scope
+ /// The environment provided by the
+ /// The value to assign
void Assign(MoScope scope, MoLangEnvironment environment, IMoValue value);
+ ///
+ /// The parameters used by this expression
+ ///
IExpression[] Parameters { get; set; }
}
- public class ExpressionMeta
- {
- public Token Token { get; set; }
- public IExpression Parent { get; set; }
- public IExpression Previous { get; set; }
- public IExpression Next { get; set; }
-
- public override string ToString()
- {
- StringBuilder sb = new StringBuilder(255);
- bool includeFileInfoIfAvailable;
-
- if (Token != null)
- {
- sb.Append(Token.Text);
- includeFileInfoIfAvailable = true;
- }
- else
- {
- includeFileInfoIfAvailable = false;
- }
-
- if (includeFileInfoIfAvailable)
- {
- // sb.Append(" at offset ");
-
- // if (_nativeOffset == OFFSET_UNKNOWN)
- // sb.Append("");
- // else
- // sb.Append(_nativeOffset);
-
- sb.Append(" in file:line:column ");
- sb.Append("");
- sb.Append(':');
- sb.Append(Token.Position.LineNumber);
- sb.Append(':');
- sb.Append(Token.Position.Index);
- }
- else
- {
- sb.Append("");
- }
-
- sb.AppendLine();
-
- return sb.ToString();
- }
- }
-
- public abstract class Expression : IExpression
- {
- public IExpression[] Parameters { get; set; }
-
- ///
- public ExpressionMeta Meta { get; } = new ExpressionMeta();
-
- public Expression(params IExpression[] parameters)
- {
- Parameters = parameters;
- }
-
- ///
- public abstract IMoValue Evaluate(MoScope scope, MoLangEnvironment environment);
-
- ///
- public virtual void Assign(MoScope scope, MoLangEnvironment environment, IMoValue value)
- {
- throw new Exception("Cannot assign a value to " + this.GetType());
- }
- }
-
public abstract class Expression : Expression
{
protected Expression(T value) : base()
diff --git a/src/MolangSharp/Parser/IExpressionVisitor.cs b/src/MolangSharp/Parser/IExpressionVisitor.cs
new file mode 100644
index 0000000..a295ef0
--- /dev/null
+++ b/src/MolangSharp/Parser/IExpressionVisitor.cs
@@ -0,0 +1,38 @@
+namespace ConcreteMC.MolangSharp.Parser
+{
+ ///
+ /// The base interface for expression visitors.
+ ///
+ ///
+ /// Expression visitors are mainly used to optimize arrays of
+ /// One such example is the
+ ///
+ public interface IExpressionVisitor
+ {
+ ///
+ /// Invoked before any ExpressionVisitor has traversed expression array
+ ///
+ /// The expressions to be traversed
+ void BeforeTraverse(IExpression[] expressions);
+
+ ///
+ /// Invoked once for every
+ ///
+ /// The that invoked the visitor
+ /// The expressions to visit
+ /// The visited expression
+ IExpression OnVisit(ExpressionTraverser traverser, IExpression expression);
+
+ ///
+ /// Invoked when all ExpressionVisitors have visited an expression
+ ///
+ /// The expressions that has been visited
+ void OnLeave(IExpression expression);
+
+ ///
+ /// Invoked after all of the ExpressionVisitors have traversed an expression array
+ ///
+ /// The visited expression array
+ void AfterTraverse(IExpression[] expressions);
+ }
+}
\ No newline at end of file
diff --git a/src/MolangSharp/Parser/InfixParselet.cs b/src/MolangSharp/Parser/InfixParselet.cs
index fa00019..d4176ce 100644
--- a/src/MolangSharp/Parser/InfixParselet.cs
+++ b/src/MolangSharp/Parser/InfixParselet.cs
@@ -13,9 +13,4 @@ public InfixParselet(Precedence precedence)
public abstract IExpression Parse(MoLangParser parser, Token token, IExpression leftExpr);
}
-
- public abstract class PrefixParselet
- {
- public abstract IExpression Parse(MoLangParser parser, Token token);
- }
}
\ No newline at end of file
diff --git a/src/MolangSharp/Parser/MoLangParser.cs b/src/MolangSharp/Parser/MoLangParser.cs
index 52a6fa5..364bb04 100644
--- a/src/MolangSharp/Parser/MoLangParser.cs
+++ b/src/MolangSharp/Parser/MoLangParser.cs
@@ -17,7 +17,7 @@ namespace ConcreteMC.MolangSharp.Parser
public class MoLangParser
{
public delegate MoLangParser ParserFactory(ITokenIterator iterator);
-
+
///
/// The factory method used to provide instances of the
///
@@ -69,18 +69,19 @@ static MoLangParser()
InfixParselets.Add(TokenType.Arrow, new GenericBinaryOpParselet(Precedence.Arrow));
InfixParselets.Add(TokenType.Assign, new AssignParselet());
}
-
+
///
/// The expression traverser executed after the parsing of the expressions
///
- public ExprTraverser ExprTraverser { get; }
-
+ public ExpressionTraverser ExpressionTraverser { get; }
+
private readonly ITokenIterator _tokenIterator;
private readonly List _readTokens = new List();
+
public MoLangParser(ITokenIterator iterator)
{
_tokenIterator = iterator;
- ExprTraverser = new ExprTraverser();
+ ExpressionTraverser = new ExpressionTraverser();
}
///
@@ -110,7 +111,8 @@ public IExpression Parse()
}
} while (MatchToken(TokenType.Semicolon));
- var result = ExprTraverser.Traverse(exprs.ToArray());
+ var result = ExpressionTraverser.Traverse(exprs.ToArray());
+
if (result.Length > 1)
{
return new ScriptExpression(result);
@@ -156,7 +158,7 @@ internal IExpression ParseExpression(Precedence precedence)
IExpression expr = parselet.Parse(this, token);
InitExpr(expr, token);
-
+
return ParseInfixExpression(expr, precedence);
}
@@ -215,16 +217,19 @@ private Precedence GetPrecedence()
internal bool TryParseArgs(out IExpression[] expressions)
{
expressions = null;
-
+
if (!MatchToken(TokenType.BracketLeft)) return false;
+
if (MatchToken(TokenType.BracketRight))
{
expressions = Array.Empty();
+
return true;
}
-
+
List args = new List();
+
do
{
args.Add(ParseExpression());
@@ -233,6 +238,7 @@ internal bool TryParseArgs(out IExpression[] expressions)
ConsumeToken(TokenType.BracketRight);
expressions = args.ToArray();
+
return true;
}
@@ -264,6 +270,7 @@ internal MoPath FixNameShortcut(MoPath name)
}
name.SetValue(first);
+
return name; // String.Join(".", splits);
}
@@ -275,6 +282,7 @@ private Token ConsumeToken()
internal Token ConsumeToken(TokenType expectedType)
{
_tokenIterator.Step();
+
if (!TryReadToken(expectedType, out var token))
{
throw new MoLangParserException(
@@ -300,7 +308,7 @@ internal bool MatchToken(TokenType expectedType, bool consume)
{
if (!TryReadToken(expectedType, out _))
return false;
-
+
if (consume)
ConsumeToken();
@@ -310,6 +318,7 @@ internal bool MatchToken(TokenType expectedType, bool consume)
private bool TryReadToken(TokenType expectedType, out Token result)
{
result = ReadToken();
+
if (result == null || (expectedType != null && !result.Type.Equals(expectedType)))
{
return false;
@@ -317,7 +326,7 @@ private bool TryReadToken(TokenType expectedType, out Token result)
return true;
}
-
+
private Token ReadToken()
{
return ReadToken(0);
@@ -342,12 +351,12 @@ public static IExpression Parse(string input)
{
return Factory(new TokenIterator(input)).Parse();
}
-
+
private static MoLangParser DefaultFactory(ITokenIterator iterator)
{
var parser = new MoLangParser(iterator);
- parser.ExprTraverser.Visitors.Add(new MathOptimizationVisitor());
-
+ parser.ExpressionTraverser.Visitors.Add(new MathOptimizationVisitor());
+
return parser;
}
}
diff --git a/src/MolangSharp/Parser/Parselet/ArrayAccessParselet.cs b/src/MolangSharp/Parser/Parselet/ArrayAccessParselet.cs
index bdb474e..444b852 100644
--- a/src/MolangSharp/Parser/Parselet/ArrayAccessParselet.cs
+++ b/src/MolangSharp/Parser/Parselet/ArrayAccessParselet.cs
@@ -3,6 +3,12 @@
namespace ConcreteMC.MolangSharp.Parser.Parselet
{
+ ///
+ /// Implements the indexer/array accessor parser
+ ///
+ ///
+ /// Parses expressions such as "array[0]"
+ ///
public class ArrayAccessParselet : InfixParselet
{
///
diff --git a/src/MolangSharp/Parser/Parselet/AssignParselet.cs b/src/MolangSharp/Parser/Parselet/AssignParselet.cs
index fce99bf..a820902 100644
--- a/src/MolangSharp/Parser/Parselet/AssignParselet.cs
+++ b/src/MolangSharp/Parser/Parselet/AssignParselet.cs
@@ -3,6 +3,9 @@
namespace ConcreteMC.MolangSharp.Parser.Parselet
{
+ ///
+ /// Implements the "=" parselet
+ ///
public class AssignParselet : InfixParselet
{
///
diff --git a/src/MolangSharp/Parser/Parselet/BooleanNotParselet.cs b/src/MolangSharp/Parser/Parselet/BooleanNotParselet.cs
index bda4151..cd5e2e2 100644
--- a/src/MolangSharp/Parser/Parselet/BooleanNotParselet.cs
+++ b/src/MolangSharp/Parser/Parselet/BooleanNotParselet.cs
@@ -3,6 +3,9 @@
namespace ConcreteMC.MolangSharp.Parser.Parselet
{
+ ///
+ /// Implements the "!=" parselet
+ ///
public class BooleanNotParselet : PrefixParselet
{
///
diff --git a/src/MolangSharp/Parser/Parselet/BooleanParselet.cs b/src/MolangSharp/Parser/Parselet/BooleanParselet.cs
index a4a5b92..7bf2ea4 100644
--- a/src/MolangSharp/Parser/Parselet/BooleanParselet.cs
+++ b/src/MolangSharp/Parser/Parselet/BooleanParselet.cs
@@ -3,6 +3,9 @@
namespace ConcreteMC.MolangSharp.Parser.Parselet
{
+ ///
+ /// Implements the boolean parselet
+ ///
public class BooleanParselet : PrefixParselet
{
///
diff --git a/src/MolangSharp/Parser/Parselet/BracketScopeParselet.cs b/src/MolangSharp/Parser/Parselet/BracketScopeParselet.cs
index b318ba7..de2f45f 100644
--- a/src/MolangSharp/Parser/Parselet/BracketScopeParselet.cs
+++ b/src/MolangSharp/Parser/Parselet/BracketScopeParselet.cs
@@ -4,6 +4,9 @@
namespace ConcreteMC.MolangSharp.Parser.Parselet
{
+ ///
+ /// Implements the scope parselet
+ ///
public class BracketScopeParselet : PrefixParselet
{
///
diff --git a/src/MolangSharp/Parser/Parselet/BreakParselet.cs b/src/MolangSharp/Parser/Parselet/BreakParselet.cs
index e8cb6df..cd50097 100644
--- a/src/MolangSharp/Parser/Parselet/BreakParselet.cs
+++ b/src/MolangSharp/Parser/Parselet/BreakParselet.cs
@@ -3,6 +3,9 @@
namespace ConcreteMC.MolangSharp.Parser.Parselet
{
+ ///
+ /// Implements the "break" instruction parser
+ ///
public class BreakParselet : PrefixParselet
{
///
diff --git a/src/MolangSharp/Parser/Parselet/ContinueParselet.cs b/src/MolangSharp/Parser/Parselet/ContinueParselet.cs
index 1f8b073..7725f18 100644
--- a/src/MolangSharp/Parser/Parselet/ContinueParselet.cs
+++ b/src/MolangSharp/Parser/Parselet/ContinueParselet.cs
@@ -3,6 +3,9 @@
namespace ConcreteMC.MolangSharp.Parser.Parselet
{
+ ///
+ /// Implements the "continue" instruction parser
+ ///
public class ContinueParselet : PrefixParselet
{
///
diff --git a/src/MolangSharp/Parser/Parselet/FloatParselet.cs b/src/MolangSharp/Parser/Parselet/FloatParselet.cs
index 3b8223b..dbd79dd 100644
--- a/src/MolangSharp/Parser/Parselet/FloatParselet.cs
+++ b/src/MolangSharp/Parser/Parselet/FloatParselet.cs
@@ -1,15 +1,22 @@
+using System.Globalization;
using ConcreteMC.MolangSharp.Parser.Exceptions;
using ConcreteMC.MolangSharp.Parser.Expressions;
using ConcreteMC.MolangSharp.Parser.Tokenizer;
namespace ConcreteMC.MolangSharp.Parser.Parselet
{
+ ///
+ /// Implements Float parsing
+ ///
public class FloatParselet : PrefixParselet
{
+ private const NumberStyles NumberStyle = System.Globalization.NumberStyles.AllowDecimalPoint;
+ private static readonly CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture;
+
///
public override IExpression Parse(MoLangParser parser, Token token)
{
- if (float.TryParse(token.Text, out var result))
+ if (float.TryParse(token.Text, NumberStyle, Culture, out var result))
{
return new NumberExpression(result);
}
diff --git a/src/MolangSharp/Parser/Parselet/ForEachParselet.cs b/src/MolangSharp/Parser/Parselet/ForEachParselet.cs
index 75d0a40..4332474 100644
--- a/src/MolangSharp/Parser/Parselet/ForEachParselet.cs
+++ b/src/MolangSharp/Parser/Parselet/ForEachParselet.cs
@@ -4,14 +4,18 @@
namespace ConcreteMC.MolangSharp.Parser.Parselet
{
+ ///
+ /// Implements the "foreach" instruction parser
+ ///
public class ForEachParselet : PrefixParselet
{
///
public override IExpression Parse(MoLangParser parser, Token token)
{
if (!parser.TryParseArgs(out var expressions) || expressions.Length != 3)
- throw new MoLangParserException($"ForEach: Expected 3 argument, {(expressions?.Length ?? 0)} argument given");
-
+ throw new MoLangParserException(
+ $"ForEach: Expected 3 argument, {(expressions?.Length ?? 0)} argument given");
+
return new ForEachExpression(expressions[0], expressions[1], expressions[2]);
}
}
diff --git a/src/MolangSharp/Parser/Parselet/GenericBinaryOpParselet.cs b/src/MolangSharp/Parser/Parselet/GenericBinaryOpParselet.cs
index b346384..869ed07 100644
--- a/src/MolangSharp/Parser/Parselet/GenericBinaryOpParselet.cs
+++ b/src/MolangSharp/Parser/Parselet/GenericBinaryOpParselet.cs
@@ -3,6 +3,9 @@
namespace ConcreteMC.MolangSharp.Parser.Parselet
{
+ ///
+ /// Generic binary operator parselet
+ ///
public class GenericBinaryOpParselet : InfixParselet
{
///
@@ -24,7 +27,7 @@ public override IExpression Parse(MoLangParser parser, Token token, IExpression
if (token.Type.Equals(TokenType.Coalesce))
return new CoalesceExpression(leftExpr, rightExpr);
-
+
if (token.Type.Equals(TokenType.Slash))
return new DivideExpression(leftExpr, rightExpr);
diff --git a/src/MolangSharp/Parser/Parselet/GroupParselet.cs b/src/MolangSharp/Parser/Parselet/GroupParselet.cs
index 40eac26..7de6aa2 100644
--- a/src/MolangSharp/Parser/Parselet/GroupParselet.cs
+++ b/src/MolangSharp/Parser/Parselet/GroupParselet.cs
@@ -2,6 +2,9 @@
namespace ConcreteMC.MolangSharp.Parser.Parselet
{
+ ///
+ /// Implements the "group" parselet
+ ///
public class GroupParselet : PrefixParselet
{
///
diff --git a/src/MolangSharp/Parser/Parselet/LoopParselet.cs b/src/MolangSharp/Parser/Parselet/LoopParselet.cs
index f30966b..3a03198 100644
--- a/src/MolangSharp/Parser/Parselet/LoopParselet.cs
+++ b/src/MolangSharp/Parser/Parselet/LoopParselet.cs
@@ -4,14 +4,18 @@
namespace ConcreteMC.MolangSharp.Parser.Parselet
{
+ ///
+ /// Implements the "loop" instruction parser
+ ///
public class LoopParselet : PrefixParselet
{
///
public override IExpression Parse(MoLangParser parser, Token token)
{
if (!parser.TryParseArgs(out var expressions) || expressions.Length != 2)
- throw new MoLangParserException($"Loop: Expected 2 argument, {(expressions?.Length ?? 0)} argument given");
-
+ throw new MoLangParserException(
+ $"Loop: Expected 2 argument, {(expressions?.Length ?? 0)} argument given");
+
return new LoopExpression(expressions[0], expressions[1]);
}
}
diff --git a/src/MolangSharp/Parser/Parselet/NameParselet.cs b/src/MolangSharp/Parser/Parselet/NameParselet.cs
index 48f075f..0cd2e86 100644
--- a/src/MolangSharp/Parser/Parselet/NameParselet.cs
+++ b/src/MolangSharp/Parser/Parselet/NameParselet.cs
@@ -4,6 +4,13 @@
namespace ConcreteMC.MolangSharp.Parser.Parselet
{
+ ///
+ /// Implements the "name" parser
+ ///
+ ///
+ /// Used to parse function calls or property accessors.
+ /// For example: "query.frame_time" or "math.min(10, 20)"
+ ///
public class NameParselet : PrefixParselet
{
///
diff --git a/src/MolangSharp/Parser/Parselet/NumberParselet.cs b/src/MolangSharp/Parser/Parselet/NumberParselet.cs
index 4edba8f..f33b297 100644
--- a/src/MolangSharp/Parser/Parselet/NumberParselet.cs
+++ b/src/MolangSharp/Parser/Parselet/NumberParselet.cs
@@ -5,6 +5,9 @@
namespace ConcreteMC.MolangSharp.Parser.Parselet
{
+ ///
+ /// Implements number parsing
+ ///
public class NumberParselet : PrefixParselet
{
private const NumberStyles NumberStyle = System.Globalization.NumberStyles.AllowDecimalPoint;
@@ -13,7 +16,7 @@ public class NumberParselet : PrefixParselet
///
public override IExpression Parse(MoLangParser parser, Token token)
{
- if (double.TryParse(token.Text, out var result))
+ if (double.TryParse(token.Text, NumberStyle, Culture, out var result))
{
return new NumberExpression(result);
}
diff --git a/src/MolangSharp/Parser/Parselet/ReturnParselet.cs b/src/MolangSharp/Parser/Parselet/ReturnParselet.cs
index 60f1dc7..2b15bc2 100644
--- a/src/MolangSharp/Parser/Parselet/ReturnParselet.cs
+++ b/src/MolangSharp/Parser/Parselet/ReturnParselet.cs
@@ -3,6 +3,9 @@
namespace ConcreteMC.MolangSharp.Parser.Parselet
{
+ ///
+ /// Implements the "return" instruction parser
+ ///
public class ReturnParselet : PrefixParselet
{
///
diff --git a/src/MolangSharp/Parser/Parselet/StringParselet.cs b/src/MolangSharp/Parser/Parselet/StringParselet.cs
index 16d196e..65c2e38 100644
--- a/src/MolangSharp/Parser/Parselet/StringParselet.cs
+++ b/src/MolangSharp/Parser/Parselet/StringParselet.cs
@@ -3,6 +3,9 @@
namespace ConcreteMC.MolangSharp.Parser.Parselet
{
+ ///
+ /// Implements the string parser
+ ///
public class StringParselet : PrefixParselet
{
///
diff --git a/src/MolangSharp/Parser/Parselet/TernaryParselet.cs b/src/MolangSharp/Parser/Parselet/TernaryParselet.cs
index 22d8187..9c94b72 100644
--- a/src/MolangSharp/Parser/Parselet/TernaryParselet.cs
+++ b/src/MolangSharp/Parser/Parselet/TernaryParselet.cs
@@ -3,6 +3,9 @@
namespace ConcreteMC.MolangSharp.Parser.Parselet
{
+ ///
+ /// Implements the Ternary expression parser
+ ///
public class TernaryParselet : InfixParselet
{
///
diff --git a/src/MolangSharp/Parser/Parselet/ThisParselet.cs b/src/MolangSharp/Parser/Parselet/ThisParselet.cs
index 7aef8a9..1f3b606 100644
--- a/src/MolangSharp/Parser/Parselet/ThisParselet.cs
+++ b/src/MolangSharp/Parser/Parselet/ThisParselet.cs
@@ -3,6 +3,9 @@
namespace ConcreteMC.MolangSharp.Parser.Parselet
{
+ ///
+ /// Implements the "this" instruction parser
+ ///
public class ThisParselet : PrefixParselet
{
///
diff --git a/src/MolangSharp/Parser/Parselet/UnaryMinusParselet.cs b/src/MolangSharp/Parser/Parselet/UnaryMinusParselet.cs
index ccfef17..7a43345 100644
--- a/src/MolangSharp/Parser/Parselet/UnaryMinusParselet.cs
+++ b/src/MolangSharp/Parser/Parselet/UnaryMinusParselet.cs
@@ -3,6 +3,9 @@
namespace ConcreteMC.MolangSharp.Parser.Parselet
{
+ ///
+ /// Implements the unary minus parser
+ ///
public class UnaryMinusParselet : PrefixParselet
{
///
diff --git a/src/MolangSharp/Parser/Parselet/UnaryPlusParselet.cs b/src/MolangSharp/Parser/Parselet/UnaryPlusParselet.cs
index 90c4715..a8f9160 100644
--- a/src/MolangSharp/Parser/Parselet/UnaryPlusParselet.cs
+++ b/src/MolangSharp/Parser/Parselet/UnaryPlusParselet.cs
@@ -3,6 +3,9 @@
namespace ConcreteMC.MolangSharp.Parser.Parselet
{
+ ///
+ /// Implements the unary plus parser
+ ///
public class UnaryPlusParselet : PrefixParselet
{
///
diff --git a/src/MolangSharp/Parser/PrefixParselet.cs b/src/MolangSharp/Parser/PrefixParselet.cs
new file mode 100644
index 0000000..2620c98
--- /dev/null
+++ b/src/MolangSharp/Parser/PrefixParselet.cs
@@ -0,0 +1,9 @@
+using ConcreteMC.MolangSharp.Parser.Tokenizer;
+
+namespace ConcreteMC.MolangSharp.Parser
+{
+ public abstract class PrefixParselet
+ {
+ public abstract IExpression Parse(MoLangParser parser, Token token);
+ }
+}
\ No newline at end of file
diff --git a/src/MolangSharp/Parser/Tokenizer/ITokenIterator.cs b/src/MolangSharp/Parser/Tokenizer/ITokenIterator.cs
index e1c5dfb..7c381e9 100644
--- a/src/MolangSharp/Parser/Tokenizer/ITokenIterator.cs
+++ b/src/MolangSharp/Parser/Tokenizer/ITokenIterator.cs
@@ -1,8 +1,9 @@
namespace ConcreteMC.MolangSharp.Parser.Tokenizer
{
- public interface ITokenIterator
- {
- Token Next();
- void Step();
- }
+ public interface ITokenIterator
+ {
+ Token Next();
+
+ void Step();
+ }
}
\ No newline at end of file
diff --git a/src/MolangSharp/Parser/Tokenizer/Token.cs b/src/MolangSharp/Parser/Tokenizer/Token.cs
index 306d09a..e25230c 100644
--- a/src/MolangSharp/Parser/Tokenizer/Token.cs
+++ b/src/MolangSharp/Parser/Tokenizer/Token.cs
@@ -1,11 +1,30 @@
namespace ConcreteMC.MolangSharp.Parser.Tokenizer
{
+ ///
+ /// A token is a piece of code in an expression
+ ///
public class Token
{
- public TokenType Type;
- public string Text;
- public TokenPosition Position;
+ ///
+ /// The type of this token
+ ///
+ public TokenType Type { get; }
+ ///
+ /// The piece of code defining this token
+ ///
+ public string Text { get; }
+
+ ///
+ /// The position in the source expression that this token was found at
+ ///
+ public TokenPosition Position { get; }
+
+ ///
+ /// Initializes a new token
+ ///
+ ///
+ ///
public Token(TokenType tokenType, TokenPosition position)
{
this.Type = tokenType;
@@ -13,6 +32,12 @@ public Token(TokenType tokenType, TokenPosition position)
this.Position = position;
}
+ ///
+ /// Initializes a new token
+ ///
+ ///
+ ///
+ ///
public Token(TokenType tokenType, string text, TokenPosition position)
{
this.Type = tokenType;
diff --git a/src/MolangSharp/Parser/Tokenizer/TokenPosition.cs b/src/MolangSharp/Parser/Tokenizer/TokenPosition.cs
index ae6a8f0..b3ba135 100644
--- a/src/MolangSharp/Parser/Tokenizer/TokenPosition.cs
+++ b/src/MolangSharp/Parser/Tokenizer/TokenPosition.cs
@@ -1,13 +1,23 @@
namespace ConcreteMC.MolangSharp.Parser.Tokenizer
{
+ ///
+ /// Describes the position of a
+ ///
public class TokenPosition
{
// public int StartLineNumber;
//public int EndLineNumber;
//public int StartColumn;
// public int EndColumn;
- public int LineNumber;
- public int Index;
+ ///
+ /// The linenumber this token was found at
+ ///
+ public int LineNumber { get; }
+
+ ///
+ /// The character index of this token
+ ///
+ public int Index { get; }
public TokenPosition(int lastStepLine, int currentLine, int lastStep, int index)
{
diff --git a/src/MolangSharp/Parser/Tokenizer/TokenType.cs b/src/MolangSharp/Parser/Tokenizer/TokenType.cs
index 2faf1d6..c65924d 100644
--- a/src/MolangSharp/Parser/Tokenizer/TokenType.cs
+++ b/src/MolangSharp/Parser/Tokenizer/TokenType.cs
@@ -48,7 +48,14 @@ public sealed class TokenType
private static int _typeCounter = 0;
+ ///
+ /// A string "identifying" this tokentype
+ ///
public string Symbol { get; }
+
+ ///
+ /// A human readable identifier for this tokentype
+ ///
public string TypeName { get; }
private readonly int _typeId;
@@ -69,6 +76,11 @@ private TokenType(string symbol, string typename = "")
This, True, False, String, Number, Name, Eof
};
+ ///
+ /// Finds the TokenType by symbol
+ ///
+ /// The symbol to lookup
+ /// The resulting TokenType or null if no match has been found
public static TokenType BySymbol(string symbol)
{
foreach (TokenType tokenType in TokenType.Values)
@@ -82,11 +94,21 @@ public static TokenType BySymbol(string symbol)
return null;
}
+ ///
+ /// Finds the TokenType by symbol
+ ///
+ /// The symbol to lookup
+ /// The resulting TokenType or null if no match has been found
public static TokenType BySymbol(char symbol)
{
return BySymbol(symbol.ToString());
}
+ ///
+ /// Checks if this tokentype is equal to
+ ///
+ ///
+ ///
public bool Equals(TokenType other)
{
return _typeId == other._typeId;
diff --git a/src/MolangSharp/Parser/Visitors/ExprConnectingVisitor.cs b/src/MolangSharp/Parser/Visitors/ExpressionConnectingVisitor.cs
similarity index 86%
rename from src/MolangSharp/Parser/Visitors/ExprConnectingVisitor.cs
rename to src/MolangSharp/Parser/Visitors/ExpressionConnectingVisitor.cs
index f34055f..6f20f87 100644
--- a/src/MolangSharp/Parser/Visitors/ExprConnectingVisitor.cs
+++ b/src/MolangSharp/Parser/Visitors/ExpressionConnectingVisitor.cs
@@ -1,6 +1,6 @@
namespace ConcreteMC.MolangSharp.Parser.Visitors
{
- public class ExprConnectingVisitor : ExprVisitor
+ public class ExpressionConnectingVisitor : ExpressionVisitor
{
//private LinkedList Stack { get; set; } = new LinkedList();
@@ -17,7 +17,7 @@ public override void BeforeTraverse(IExpression[] expressions)
}
///
- public override IExpression OnVisit(ExprTraverser traverser, IExpression expression)
+ public override IExpression OnVisit(ExpressionTraverser traverser, IExpression expression)
{
var previous = _last;
expression.Meta.Previous = previous;
diff --git a/src/MolangSharp/Parser/Visitors/FindingVisitor.cs b/src/MolangSharp/Parser/Visitors/FindingVisitor.cs
index b0c2e40..ddaaa58 100644
--- a/src/MolangSharp/Parser/Visitors/FindingVisitor.cs
+++ b/src/MolangSharp/Parser/Visitors/FindingVisitor.cs
@@ -3,7 +3,7 @@
namespace ConcreteMC.MolangSharp.Parser.Visitors
{
- public class FindingVisitor : ExprVisitor
+ public class FindingVisitor : ExpressionVisitor
{
private Predicate _predicate;
public List FoundExpressions = new List();
@@ -14,7 +14,7 @@ public FindingVisitor(Predicate predicate)
}
///
- public override IExpression OnVisit(ExprTraverser traverser, IExpression expression)
+ public override IExpression OnVisit(ExpressionTraverser traverser, IExpression expression)
{
if (_predicate(expression))
{
diff --git a/src/MolangSharp/Parser/Visitors/FirstFindingVisitor.cs b/src/MolangSharp/Parser/Visitors/FirstFindingVisitor.cs
index ffaebed..03fa026 100644
--- a/src/MolangSharp/Parser/Visitors/FirstFindingVisitor.cs
+++ b/src/MolangSharp/Parser/Visitors/FirstFindingVisitor.cs
@@ -2,7 +2,7 @@
namespace ConcreteMC.MolangSharp.Parser.Visitors
{
- public class FirstFindingVisitor : ExprVisitor
+ public class FirstFindingVisitor : ExpressionVisitor
{
private Predicate _predicate;
public IExpression Found = null;
@@ -13,7 +13,7 @@ public FirstFindingVisitor(Predicate predicate)
}
///
- public override IExpression OnVisit(ExprTraverser traverser, IExpression expression)
+ public override IExpression OnVisit(ExpressionTraverser traverser, IExpression expression)
{
if (_predicate(expression))
{
diff --git a/src/MolangSharp/Parser/Visitors/InteropOptimization/InteropOptimizationVisitor.cs b/src/MolangSharp/Parser/Visitors/InteropOptimization/InteropOptimizationVisitor.cs
new file mode 100644
index 0000000..dc4c315
--- /dev/null
+++ b/src/MolangSharp/Parser/Visitors/InteropOptimization/InteropOptimizationVisitor.cs
@@ -0,0 +1,206 @@
+using System;
+using System.Linq;
+using ConcreteMC.MolangSharp.Parser.Expressions;
+using ConcreteMC.MolangSharp.Runtime;
+using ConcreteMC.MolangSharp.Runtime.Struct.Interop;
+using ConcreteMC.MolangSharp.Runtime.Value;
+using ConcreteMC.MolangSharp.Utils;
+
+namespace ConcreteMC.MolangSharp.Parser.Visitors.InteropOptimization
+{
+ ///
+ /// Optimizes interop calls to call the methods or properties directly instead of going through the MoLang query system.
+ ///
+ ///
+ /// This is a very simple optimization that can be done to improve performance, it does come with some limitations however.
+ /// These limitations are:
+ /// - 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.
+ /// - 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.
+ ///
+ public class InteropOptimizationVisitor : ExpressionVisitor
+ {
+ private readonly InteropOptimizationVisitorOptions _options;
+
+ public InteropOptimizationVisitor(InteropOptimizationVisitorOptions options)
+ {
+ options.Validate();
+
+ _options = options;
+ }
+
+ ///
+ public override IExpression OnVisit(ExpressionTraverser traverser, IExpression expression)
+ {
+ switch (expression)
+ {
+ case FuncCallExpression functionCall:
+ return _options.OptimizeFunctionCalls ? TryOptimizeFunctionCall(functionCall) : functionCall;
+
+ case NameExpression nameExpression:
+ return _options.OptimizeVariableAccess ? TryOptimizeNameExpression(nameExpression) : nameExpression;
+
+ case AssignExpression assignExpression:
+ return _options.OptimizeVariableAssignment ? TryOptimizeAssignExpression(assignExpression) : assignExpression;
+
+ default:
+ return expression;
+ }
+ }
+
+ private IExpression TryOptimizeAssignExpression(AssignExpression expression)
+ {
+ //Optimize variable assignment to call the method directly.
+ var variableToAccess = expression.Variable;
+
+ if (variableToAccess is NameExpression nameExpression)
+ {
+ var name = nameExpression.Name.Value;
+ var interopEntry = _options.InteropEntries.FirstOrDefault(x => x.Name == name);
+
+ if (interopEntry == null)
+ return expression;
+
+ if (TryGetProperty(interopEntry.Cache, nameExpression.Name.Next, out var valueAccessor))
+ {
+ return new OptimizedAssignExpression(interopEntry.Instance, valueAccessor, expression.Expression);
+ }
+ }
+
+ return expression;
+ }
+
+ private IExpression TryOptimizeNameExpression(NameExpression expression)
+ {
+ //Optimize variable access to call the method directly.
+
+ var name = expression.Name.Value;
+ var interopEntry = _options.InteropEntries.FirstOrDefault(x => x.Name == name);
+
+ if (interopEntry == null)
+ return expression;
+
+ if (TryGetProperty(interopEntry.Cache, expression.Name.Next, out var valueAccessor))
+ return new OptimizedNameExpression(interopEntry.Instance, valueAccessor);
+
+ return expression;
+ }
+
+ private IExpression TryOptimizeFunctionCall(FuncCallExpression expression)
+ {
+ //Optimize function call to call the method directly.
+ var name = expression.Name.Value;
+ var interopEntry = _options.InteropEntries.FirstOrDefault(x => x.Name == name);
+
+ if (interopEntry == null)
+ return expression;
+
+ if (TryGetFunction(interopEntry.Cache, expression.Name.Next, out var function))
+ return new OptimizedFunctionCallExpression(interopEntry.Instance, function, expression.Parameters);
+
+ return expression;
+ }
+
+ private bool TryGetFunction(PropertyCache cache, MoPath path, out Func function)
+ {
+ if (path.HasChildren)
+ {
+ //Possibly trying to access another level in, this is not currently supported.
+ function = null;
+ return false;
+ }
+
+ if (cache.Functions.TryGetValue(path.Value, out var f))
+ {
+ function = f;
+ return true;
+ }
+
+ function = null;
+ return false;
+ }
+
+ private bool TryGetProperty(PropertyCache cache, MoPath path, out ValueAccessor valueAccessor)
+ {
+ if (path.HasChildren)
+ {
+ //Possibly trying to access another level in, this is not currently supported.
+ valueAccessor = null;
+ return false;
+ }
+
+ if (cache.Properties.TryGetValue(path.Value, out var f))
+ {
+ valueAccessor = f;
+ return true;
+ }
+
+ valueAccessor = null;
+ return false;
+ }
+ }
+
+ public class OptimizedAssignExpression : Expression
+ {
+ private readonly object _instance;
+ private readonly ValueAccessor _valueAccessor;
+ private readonly IExpression _expressionExpression;
+
+ public OptimizedAssignExpression(object instance, ValueAccessor valueAccessor, IExpression expressionExpression)
+ {
+ _instance = instance;
+ _valueAccessor = valueAccessor;
+ _expressionExpression = expressionExpression;
+ }
+
+ ///
+ public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment)
+ {
+ _valueAccessor.Set(_instance, _expressionExpression.Evaluate(scope, environment));
+
+ return DoubleValue.Zero;
+ }
+ }
+
+ public class OptimizedFunctionCallExpression : Expression
+ {
+ private readonly object _instance;
+ private readonly Func _function;
+
+ public OptimizedFunctionCallExpression(object instance, Func function, IExpression[] args) : base(args)
+ {
+ _instance = instance;
+ _function = function;
+ }
+
+ ///
+ public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment)
+ {
+ var arguments = new IMoValue[Parameters.Length];
+
+ for (int i = 0; i < arguments.Length; i++)
+ {
+ arguments[i] = Parameters[i].Evaluate(scope, environment);
+ }
+
+ return _function.Invoke(_instance, new MoParams(arguments));
+ }
+ }
+
+ public class OptimizedNameExpression : Expression
+ {
+ private readonly object _instance;
+ private readonly ValueAccessor _valueAccessor;
+
+ public OptimizedNameExpression(object instance, ValueAccessor valueAccessor) : base()
+ {
+ _instance = instance;
+ _valueAccessor = valueAccessor;
+ }
+
+ ///
+ public override IMoValue Evaluate(MoScope scope, MoLangEnvironment environment)
+ {
+ return _valueAccessor.Get(_instance);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/MolangSharp/Parser/Visitors/InteropOptimization/InteropOptimizationVisitorOptions.cs b/src/MolangSharp/Parser/Visitors/InteropOptimization/InteropOptimizationVisitorOptions.cs
new file mode 100644
index 0000000..327a690
--- /dev/null
+++ b/src/MolangSharp/Parser/Visitors/InteropOptimization/InteropOptimizationVisitorOptions.cs
@@ -0,0 +1,56 @@
+using System;
+using ConcreteMC.MolangSharp.Runtime.Struct.Interop;
+
+namespace ConcreteMC.MolangSharp.Parser.Visitors.InteropOptimization
+{
+ public class InteropEntry
+ {
+ public string Name { get; }
+ public PropertyCache Cache { get; }
+ public object Instance { get; }
+
+ ///
+ ///
+ ///
+ /// The name of the struct
+ /// The struct instance
+ public InteropEntry(string name, object instance)
+ {
+ Name = name;
+ Cache = new PropertyCache(instance.GetType());
+ Instance = instance;
+ }
+ }
+
+ public class InteropOptimizationVisitorOptions
+ {
+ public InteropEntry[] InteropEntries { get; set; } = Array.Empty();
+
+ ///
+ /// Optimize variable access (MoProperty)
+ ///
+ public bool OptimizeVariableAccess { get; set; } = true;
+
+ ///
+ /// Optimize variable assignment (MoProperty)
+ ///
+ public bool OptimizeVariableAssignment { get; set; } = true;
+
+ ///
+ /// Optimize function calls (MoFunction)
+ ///
+ public bool OptimizeFunctionCalls { get; set; } = true;
+
+ internal void Validate()
+ {
+ if (InteropEntries == null)
+ throw new ArgumentNullException(nameof(InteropEntries));
+
+ if (InteropEntries.Length == 0)
+ throw new ArgumentException("InteropEntries cannot be empty", nameof(InteropEntries));
+
+ if (!OptimizeVariableAccess && !OptimizeVariableAssignment && !OptimizeFunctionCalls)
+ throw new ArgumentException("At least one optimization must be enabled", nameof(OptimizeVariableAccess));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/MolangSharp/Parser/Visitors/MathOptimizationVisitor.cs b/src/MolangSharp/Parser/Visitors/MathOptimizationVisitor.cs
index 9bfe8c5..13965f3 100644
--- a/src/MolangSharp/Parser/Visitors/MathOptimizationVisitor.cs
+++ b/src/MolangSharp/Parser/Visitors/MathOptimizationVisitor.cs
@@ -1,42 +1,73 @@
-using ConcreteMC.MolangSharp.Parser.Expressions;
+using System;
+using System.Linq;
+using ConcreteMC.MolangSharp.Parser.Expressions;
using ConcreteMC.MolangSharp.Runtime;
namespace ConcreteMC.MolangSharp.Parser.Visitors
{
- ///
- /// Optimizes expressions by pre-calculating static maths
- ///
- public class MathOptimizationVisitor : ExprVisitor
- {
- private MoScope _scope = new MoScope(new MoLangRuntime());
- private MoLangEnvironment _environment = new MoLangEnvironment();
-
- public override IExpression OnVisit(ExprTraverser traverser, IExpression expression)
- {
- if (expression is BinaryOpExpression binaryOp)
- {
- return TryOptimize(binaryOp);
- }
-
- return expression;
- }
-
- private IExpression TryOptimize(BinaryOpExpression expression)
- {
- if (expression.Left is BinaryOpExpression l)
- expression.Left = TryOptimize(l);
-
- if (expression.Right is BinaryOpExpression r)
- expression.Right = TryOptimize(r);
-
- if (expression.Left is NumberExpression && expression.Right is NumberExpression)
- {
- //Can be pre-calculated!
- var eval = expression.Evaluate(_scope, _environment);
- return new NumberExpression(eval);
- }
-
- return expression;
- }
- }
+ ///
+ /// Optimizes expressions by pre-calculating constant maths
+ ///
+ public class MathOptimizationVisitor : ExpressionVisitor
+ {
+ private static MoScope _scope = new MoScope(new MoLangRuntime());
+ private static MoLangEnvironment _environment = new MoLangEnvironment();
+
+ public override IExpression OnVisit(ExpressionTraverser traverser, IExpression expression)
+ {
+ return Visit(expression);
+ }
+
+ private static IExpression Visit(IExpression expression)
+ {
+ if (expression is FuncCallExpression nameExpression && nameExpression.Name.Value.Equals(
+ "math", StringComparison.InvariantCultureIgnoreCase))
+ {
+ return TryOptimizeMathFunction(nameExpression);
+ }
+
+ if (expression is BinaryOpExpression binaryOp)
+ {
+ return TryOptimize(binaryOp);
+ }
+
+ return expression;
+ }
+
+ private static IExpression TryOptimizeMathFunction(FuncCallExpression expression)
+ {
+ for (int i = 0; i < expression.Parameters.Length; i++)
+ {
+ expression.Parameters[i] = Visit(expression.Parameters[i]);
+ }
+
+ if (expression.Parameters.All(x => x is NumberExpression))
+ {
+ var eval = expression.Evaluate(_scope, _environment);
+
+ return new NumberExpression(eval);
+ }
+
+ return expression;
+ }
+
+ private static IExpression TryOptimize(BinaryOpExpression expression)
+ {
+ if (expression.Left is BinaryOpExpression l)
+ expression.Left = TryOptimize(l);
+
+ if (expression.Right is BinaryOpExpression r)
+ expression.Right = TryOptimize(r);
+
+ if (expression.Left is NumberExpression && expression.Right is NumberExpression)
+ {
+ //Can be pre-calculated!
+ var eval = expression.Evaluate(_scope, _environment);
+
+ return new NumberExpression(eval);
+ }
+
+ return expression;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/MolangSharp/Runtime/Exceptions/MissingQueryMethodException.cs b/src/MolangSharp/Runtime/Exceptions/MissingQueryMethodException.cs
deleted file mode 100644
index 44fc44f..0000000
--- a/src/MolangSharp/Runtime/Exceptions/MissingQueryMethodException.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System;
-using ConcreteMC.MolangSharp.Parser;
-
-namespace ConcreteMC.MolangSharp.Runtime.Exceptions
-{
- public class MissingQueryMethodException : MoLangRuntimeException
- {
- ///
- public MissingQueryMethodException(string message, Exception baseException) : base(message, baseException) { }
-
- ///
- public MissingQueryMethodException(IExpression expression, string message, Exception baseException) : base(
- expression, message, baseException) { }
- }
-}
\ No newline at end of file
diff --git a/src/MolangSharp/Runtime/Exceptions/MoLangRuntimeException.cs b/src/MolangSharp/Runtime/Exceptions/MoLangRuntimeException.cs
index bf66caf..9637e28 100644
--- a/src/MolangSharp/Runtime/Exceptions/MoLangRuntimeException.cs
+++ b/src/MolangSharp/Runtime/Exceptions/MoLangRuntimeException.cs
@@ -4,38 +4,64 @@
namespace ConcreteMC.MolangSharp.Runtime.Exceptions
{
+ ///
+ /// Represents an error that occured during the execution of a MoLang expression
+ ///
public class MoLangRuntimeException : Exception
{
+ ///
+ /// Contains a trace to where the exception occured in a MoLang expression.
+ ///
public string MolangTrace { get; }
+ ///
+ /// Initializes a new instance of the MoLangRuntimeException class with a specified error message.
+ ///
+ /// The error message
+ public MoLangRuntimeException(string message) : this(message, null) { }
+
+ ///
+ /// 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.
+ ///
+ /// The error message
+ /// A reference to the inner exception that is the cause of this exception
public MoLangRuntimeException(string message, Exception baseException) : base(message, baseException)
{
MolangTrace = "Unknown";
}
+ ///
+ /// 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.
+ ///
+ /// The expression that this exception occured at
+ /// The error message
+ /// A reference to the inner exception that is the cause of this exception
public MoLangRuntimeException(IExpression expression, string message, Exception baseException) : base(
message, baseException)
{
- StringBuilder sb = new StringBuilder();
-
- do
+ if (MoLangRuntimeConfiguration.UseMoLangStackTrace)
{
- if (expression.Meta?.Token?.Position != null)
+ StringBuilder sb = new StringBuilder();
+
+ do
{
- var token = expression.Meta.Token;
- var tokenPosition = token.Position;
+ if (expression.Meta?.Token?.Position != null)
+ {
+ var token = expression.Meta.Token;
+ var tokenPosition = token.Position;
- sb.Append(
- $"at <{tokenPosition.LineNumber}:{tokenPosition.Index}> near {token.Type.TypeName} \"{token.Text}\"");
- //var frame = new StackFrame(null, tokenPosition.LineNumber, tokenPosition.Index);
+ sb.Append(
+ $"at <{tokenPosition.LineNumber}:{tokenPosition.Index}> near {token.Type.TypeName} \"{token.Text}\"");
+ //var frame = new StackFrame(null, tokenPosition.LineNumber, tokenPosition.Index);
- // frames.Add(frame);
- }
+ // frames.Add(frame);
+ }
- expression = expression.Meta.Parent;
- } while (expression?.Meta?.Parent != null);
+ expression = expression.Meta.Parent;
+ } while (expression?.Meta?.Parent != null);
- MolangTrace = sb.ToString();
+ MolangTrace = sb.ToString();
+ }
//st.GetFrames()
}
diff --git a/src/MolangSharp/Runtime/MoLangEnvironment.cs b/src/MolangSharp/Runtime/MoLangEnvironment.cs
index c31e53d..67c41c0 100644
--- a/src/MolangSharp/Runtime/MoLangEnvironment.cs
+++ b/src/MolangSharp/Runtime/MoLangEnvironment.cs
@@ -7,18 +7,39 @@
namespace ConcreteMC.MolangSharp.Runtime
{
+ ///
+ /// Provides an execution environment for the
+ ///
+ ///
+ /// An example of a MoLangEnvironment would be a minecraft entity.
+ ///
public class MoLangEnvironment : IMoValue
{
///
public object Value => Structs;
- public Dictionary Structs { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase);
-
+ ///
+ /// The available root paths
+ ///
+ ///
+ /// Contains the following root structs by default:
+ /// math.
+ /// temp.
+ /// variable.
+ /// array.
+ /// context.
+ ///
+ public Dictionary Structs { get; } =
+ new Dictionary(StringComparer.OrdinalIgnoreCase);
+
///
/// The value that should be returned when an expression tries to access "this"
///
public IMoValue ThisVariable { get; set; } = DoubleValue.Zero;
+ ///
+ /// Creates a new instance of the MoLangEnvironment class
+ ///
public MoLangEnvironment()
{
Structs.TryAdd("math", MoLangMath.Library);
@@ -29,44 +50,61 @@ public MoLangEnvironment()
Structs.TryAdd("context", new ContextStruct());
}
- public IMoValue GetValue(MoPath name)
+ ///
+ /// Get a MoLang variable by it's path
+ ///
+ /// The path to access
+ /// The variable at specified path
+ public IMoValue GetValue(MoPath path)
{
- return GetValue(name, MoParams.Empty);
+ return GetValue(path, MoParams.Empty);
}
- public IMoValue GetValue(MoPath name, MoParams param)
+ ///
+ /// Get a MoLang variable by it's path and specified parameters
+ ///
+ /// The path to access
+ /// The variable at specified path
+ public IMoValue GetValue(MoPath path, MoParams param)
{
- try
- {
- return Structs[name.Value].Get(name.Next, param);
- }
- catch (Exception ex)
+ if (!Structs.TryGetValue(path.Value, out var v))
{
- throw new MoLangRuntimeException($"Cannot retrieve struct: {name}", ex);
+ if (MoLangRuntimeConfiguration.UseDummyValuesInsteadOfExceptions)
+ return DoubleValue.Zero;
+
+ throw new MoLangRuntimeException($"Invalid path: {path.Path}");
}
+
+ return v.Get(path.Next, param);
}
- public void SetValue(MoPath name, IMoValue value)
+ ///
+ /// Set a MoLang variable by its path
+ ///
+ ///
+ ///
+ ///
+ public void SetValue(MoPath path, IMoValue value)
{
- if (!Structs.TryGetValue(name.Value, out var v))
+ if (!Structs.TryGetValue(path.Value, out var v))
{
- throw new MoLangRuntimeException($"Invalid path: {name.Path}", null);
+ throw new MoLangRuntimeException($"Invalid path: {path.Path}", null);
}
-
+
try
{
- v.Set(name.Next, value);
+ v.Set(path.Next, value);
}
catch (Exception ex)
{
- throw new MoLangRuntimeException($"Cannot set value on struct: {name}", ex);
+ throw new MoLangRuntimeException($"Cannot set value on struct: {path}", ex);
}
}
///
public bool Equals(IMoValue b)
{
- return Equals((object)b);
+ return Equals((object) b);
}
}
}
\ No newline at end of file
diff --git a/src/MolangSharp/Runtime/MoLangMath.cs b/src/MolangSharp/Runtime/MoLangMath.cs
index ae51e8f..6d05361 100644
--- a/src/MolangSharp/Runtime/MoLangMath.cs
+++ b/src/MolangSharp/Runtime/MoLangMath.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
+using ConcreteMC.MolangSharp.Attributes;
using ConcreteMC.MolangSharp.Runtime.Struct;
+using ConcreteMC.MolangSharp.Runtime.Value;
namespace ConcreteMC.MolangSharp.Runtime
{
@@ -15,35 +17,33 @@ public static class MoLangMath
public static readonly QueryStruct Library = new QueryStruct(
new Dictionary>(StringComparer.OrdinalIgnoreCase)
{
- { "abs", param => Math.Abs(param.GetDouble(0)) },
- { "acos", param => Math.Acos(param.GetDouble(0)) },
- { "sin", param => Math.Sin(param.GetDouble(0) * (Math.PI / 180d)) },
- { "asin", param => Math.Asin(param.GetDouble(0)) },
- { "atan", param => Math.Atan(param.GetDouble(0)) },
- { "atan2", param => Math.Atan2(param.GetDouble(0), param.GetDouble(1)) },
- { "ceil", param => Math.Ceiling(param.GetDouble(0)) },
- {
- "clamp", param => Math.Min(param.GetDouble(1), Math.Max(param.GetDouble(0), param.GetDouble(2)))
- },
- { "cos", param => Math.Cos(param.GetDouble(0) * (Math.PI / 180d)) },
- { "die_roll", param => DieRoll(param.GetDouble(0), param.GetDouble(1), param.GetDouble(2)) },
- { "die_roll_integer", param => DieRollInt(param.GetInt(0), param.GetInt(1), param.GetInt(2)) },
- { "exp", param => Math.Exp(param.GetDouble(0)) },
- { "mod", param => param.GetDouble(0) % param.GetDouble(1) },
- { "floor", param => Math.Floor(param.GetDouble(0)) },
- { "hermite_blend", param => HermiteBlend(param.GetInt(0)) },
- { "lerp", param => Lerp(param.GetDouble(0), param.GetDouble(1), param.GetDouble(2)) },
- { "lerp_rotate", param => LerpRotate(param.GetDouble(0), param.GetDouble(1), param.GetDouble(2)) },
- { "ln", param => Math.Log(param.GetDouble(0)) },
- { "max", param => Math.Max(param.GetDouble(0), param.GetDouble(1)) },
- { "min", param => Math.Min(param.GetDouble(0), param.GetDouble(1)) },
- { "pi", param => Math.PI },
- { "pow", param => Math.Pow(param.GetDouble(0), param.GetDouble(1)) },
- { "random", param => Random(param.GetDouble(0), param.GetDouble(1)) },
- { "random_integer", param => RandomInt(param.GetInt(0), param.GetInt(1)) },
- { "round", param => Math.Round(param.GetDouble(0)) },
- { "sqrt", param => Math.Sqrt(param.GetDouble(0)) },
- { "trunc", param => Math.Floor(param.GetDouble(0)) },
+ {"abs", param => Math.Abs(param.GetDouble(0))},
+ {"acos", param => Math.Acos(param.GetDouble(0))},
+ {"sin", param => Math.Sin(param.GetDouble(0) * (Math.PI / 180d))},
+ {"asin", param => Math.Asin(param.GetDouble(0))},
+ {"atan", param => Math.Atan(param.GetDouble(0))},
+ {"atan2", param => Math.Atan2(param.GetDouble(0), param.GetDouble(1))},
+ {"ceil", param => Math.Ceiling(param.GetDouble(0))},
+ {"clamp", param => Math.Min(param.GetDouble(1), Math.Max(param.GetDouble(0), param.GetDouble(2)))},
+ {"cos", param => Math.Cos(param.GetDouble(0) * (Math.PI / 180d))},
+ {"die_roll", param => DieRoll(param.GetDouble(0), param.GetDouble(1), param.GetDouble(2))},
+ {"die_roll_integer", param => DieRollInt(param.GetInt(0), param.GetInt(1), param.GetInt(2))},
+ {"exp", param => Math.Exp(param.GetDouble(0))},
+ {"mod", param => param.GetDouble(0) % param.GetDouble(1)},
+ {"floor", param => Math.Floor(param.GetDouble(0))},
+ {"hermite_blend", param => HermiteBlend(param.GetInt(0))},
+ {"lerp", param => Lerp(param.GetDouble(0), param.GetDouble(1), param.GetDouble(2))},
+ {"lerp_rotate", param => LerpRotate(param.GetDouble(0), param.GetDouble(1), param.GetDouble(2))},
+ {"ln", param => Math.Log(param.GetDouble(0))},
+ {"max", param => Math.Max(param.GetDouble(0), param.GetDouble(1))},
+ {"min", param => Math.Min(param.GetDouble(0), param.GetDouble(1))},
+ {"pi", param => Math.PI},
+ {"pow", param => Math.Pow(param.GetDouble(0), param.GetDouble(1))},
+ {"random", param => Random(param.GetDouble(0), param.GetDouble(1))},
+ {"random_integer", param => RandomInt(param.GetInt(0), param.GetInt(1))},
+ {"round", param => Math.Round(param.GetDouble(0))},
+ {"sqrt", param => Math.Sqrt(param.GetDouble(0))},
+ {"trunc", param => Math.Floor(param.GetDouble(0))},
});
public static double Random(double low, double high)
@@ -111,4 +111,146 @@ public static double Radify(double num)
return (((num + 180) % 360) + 180) % 360;
}
}
+
+ ///
+ /// The default Math implementations for the MoLang runtime
+ ///
+ public sealed class MoLangMathImpl
+ {
+ private static IMoStruct _instance;
+ public static IMoStruct Library => _instance ??= new InteropStruct(new MoLangMathImpl());
+
+ private MoLangMathImpl() { }
+
+ [MoFunction("abs")]
+ public double Abs(double value) => Math.Abs(value);
+
+ [MoFunction("sin")]
+ public double Sin(double value) => Math.Sin(value * (Math.PI / 180d));
+
+ [MoFunction("asin")]
+ public double Asin(double value) => Math.Asin(value);
+
+ [MoFunction("cos")]
+ public double Cos(double value) => Math.Cos(value * (Math.PI / 180d));
+
+ [MoFunction("acos")]
+ public double Acos(double value) => Math.Acos(value);
+
+ [MoFunction("atan")]
+ public double Atan(double value) => Math.Atan(value);
+
+ [MoFunction("atan2")]
+ public double Atan2(double y, double x) => Math.Atan2(y, x);
+
+ [MoFunction("ceil")]
+ public double Ceiling(double value) => Math.Ceiling(value);
+
+ [MoFunction("clamp")]
+ public double Clamp(double value, double min, double max) => Math.Clamp(value, min, max);
+
+ [MoFunction("die_roll")]
+ public double DieRoll(double num, double low, double high)
+ {
+ int i = 0;
+ double total = 0;
+ while (i++ < num) total += Random(low, high);
+
+ return total;
+ }
+
+ [MoFunction("die_roll_integer")]
+ public int DieRollInt(int num, int low, int high)
+ {
+ int i = 0;
+ int total = 0;
+ while (i++ < num) total += RandomInt(low, high);
+
+ return total;
+ }
+
+ [MoFunction("exp")]
+ public double Exp(double value) => Math.Exp(value);
+
+ [MoFunction("mod")]
+ public double Modulus(double x, double y) => x % y;
+
+ [MoFunction("floor")]
+ public double Floor(double value) => Math.Floor(value);
+
+ [MoFunction("hermite_blend")]
+ public int HermiteBlend(int value) => (3 * value) ^ (2 - 2 * value) ^ 3;
+
+ [MoFunction("lerp")]
+ public double Lerp(double start, double end, double amount)
+ {
+ amount = Math.Max(0, Math.Min(1, amount));
+
+ return start + (end - start) * amount;
+ }
+
+ [MoFunction("lerp_rotate")]
+ public double LerpRotate(double start, double end, double amount)
+ {
+ start = Radify(start);
+ end = Radify(end);
+
+ if (start > end)
+ {
+ (start, end) = (end, start);
+ }
+
+ if (end - start > 180)
+ {
+ return Radify(end + amount * (360 - (end - start)));
+ }
+
+ return start + amount * (end - start);
+ }
+
+ [MoFunction("ln")]
+ public double Log(double value) => Math.Log(value);
+
+ [MoFunction("max")]
+ public double Max(double value1, double value2) => Math.Max(value1, value2);
+
+ [MoFunction("min")]
+ public double Min(double value1, double value2) => Math.Min(value1, value2);
+
+ [MoFunction("pi")]
+ public double PiFunc() => Math.PI;
+
+ [MoProperty("pi")] public double PI => Math.PI;
+
+ [MoFunction("pow")]
+ public double Pow(double x, double y) => Math.Pow(x, y);
+
+ [MoFunction("random")]
+ public double Random(double low, double high)
+ {
+ return low + _random.NextDouble() * (high - low);
+ }
+
+ [MoFunction("random_integer")]
+ public int RandomInt(int low, int high)
+ {
+ return _random.Next(low, high);
+ }
+
+ [MoFunction("round")]
+ public double Round(double value) => Math.Round(value);
+
+ [MoFunction("sqrt")]
+ public double Sqrt(double value) => Math.Sqrt(value);
+
+ [MoFunction("trunc")]
+ public double Truncate(double value) => Math.Floor(value);
+
+ public double Radify(double num)
+ {
+ return (((num + 180) % 360) + 180) % 360;
+ }
+
+ private Random _random = new Random();
+ }
}
\ No newline at end of file
diff --git a/src/MolangSharp/Runtime/MoLangRuntime.cs b/src/MolangSharp/Runtime/MoLangRuntime.cs
index e3bdb51..6a62736 100644
--- a/src/MolangSharp/Runtime/MoLangRuntime.cs
+++ b/src/MolangSharp/Runtime/MoLangRuntime.cs
@@ -14,7 +14,7 @@ public sealed class MoLangRuntime
/// The environment associated with this runtime instance
///
public MoLangEnvironment Environment { get; }
-
+
///
/// Create a new instance of MoLangRuntime with a new
///
@@ -30,7 +30,7 @@ public MoLangRuntime(MoLangEnvironment environment)
{
Environment = environment;
}
-
+
///
/// Evaluates the expressions provided and returns the resulting value (if any) or
///
@@ -56,14 +56,13 @@ public IMoValue Execute(IExpression expression, IDictionary co
if (expression == null)
return DoubleValue.Zero;
- if (Environment.Structs.TryGetValue("context", out IMoStruct cont) && cont is ContextStruct contextStruct)
+ if (context != null && Environment.Structs.TryGetValue("context", out var cont) && cont is ContextStruct contextStruct)
{
contextStruct.Container = context;
}
IMoValue result = null;
- MoScope scope = new MoScope(this);
-
+ var scope = new MoScope(this);
result = expression.Evaluate(scope, Environment);
Environment.Structs["temp"].Clear();
diff --git a/src/MolangSharp/Runtime/MoLangRuntimeConfiguration.cs b/src/MolangSharp/Runtime/MoLangRuntimeConfiguration.cs
new file mode 100644
index 0000000..c7b5ad1
--- /dev/null
+++ b/src/MolangSharp/Runtime/MoLangRuntimeConfiguration.cs
@@ -0,0 +1,18 @@
+namespace ConcreteMC.MolangSharp.Runtime
+{
+ ///
+ /// Global MoLang Runtime configuration options
+ ///
+ public static class MoLangRuntimeConfiguration
+ {
+ ///
+ /// If true (default) allows you to locate an exception in the MoLang script when a runtime exception is thrown.
+ ///
+ public static bool UseMoLangStackTrace { get; set; } = true;
+
+ ///
+ /// If true, returns dummy values instead of throwing an exception when a variable is not found during runtime execution. (Experimental)
+ ///
+ public static bool UseDummyValuesInsteadOfExceptions { get; set; } = false;
+ }
+}
\ No newline at end of file
diff --git a/src/MolangSharp/Runtime/MoParams.cs b/src/MolangSharp/Runtime/MoParams.cs
index 51079f2..3132e15 100644
--- a/src/MolangSharp/Runtime/MoParams.cs
+++ b/src/MolangSharp/Runtime/MoParams.cs
@@ -18,7 +18,7 @@ public MoParams(params IMoValue[] param)
{
_parameters = param;
}
-
+
///
/// Gets the parameter at and returns its value as an
///
@@ -50,7 +50,7 @@ public T Get(int index)
if (obj?.GetType() == typeof(T))
{
- return (T)obj;
+ return (T) obj;
}
else
{
@@ -81,9 +81,9 @@ public bool Contains(int index)
///
public int GetInt(int index)
{
- return (int)GetDouble(index);
+ return (int) GetDouble(index);
}
-
+
///
/// Gets the parameter at and returns its value as a
///
@@ -95,7 +95,7 @@ public double GetDouble(int index)
{
return Get(index).Value;
}
-
+
///
/// Gets the parameter at and returns its value as a
///
@@ -107,7 +107,7 @@ public IMoStruct GetStruct(int index)
{
return Get(index);
}
-
+
///
/// Gets the parameter at and returns its value as a
///
@@ -119,7 +119,7 @@ public string GetString(int index)
{
return Get(index).Value;
}
-
+
///
/// Gets the parameter at and returns its value as a
///
diff --git a/src/MolangSharp/Runtime/Struct/ArrayStruct.cs b/src/MolangSharp/Runtime/Struct/ArrayStruct.cs
index cccf2dd..8074e55 100644
--- a/src/MolangSharp/Runtime/Struct/ArrayStruct.cs
+++ b/src/MolangSharp/Runtime/Struct/ArrayStruct.cs
@@ -14,42 +14,46 @@ public class ArrayStruct : IMoStruct
{
private IMoValue[] _array;
+ ///
+ /// Initializes a new instance of the ArrayStruct class with no values.
+ ///
public ArrayStruct()
{
_array = new IMoValue[0];
}
+ ///
+ /// Initializes a new instance of the ArrayStruct class.
+ ///
+ /// The values this ArrayStruct contains
public ArrayStruct(IEnumerable values)
{
_array = values.ToArray();
- //_array.AddRange(values);
}
- private void Resize(int size)
- {
- if (size >= _array.Length)
- Array.Resize(ref _array, size + 1);
- }
-
+ ///
+ /// Indexes the ArrayStruct
+ ///
+ /// The index to get/set the value of
public IMoValue this[int index]
{
get
{
if (index >= _array.Length)
return DoubleValue.Zero;
- //Resize(index);
+
return _array[index % _array.Length];
}
set
{
if (_array.Length == 0)
return;
- //Resize(index);
-
+
_array[index % _array.Length] = value;
}
}
+ ///
public void Set(MoPath key, IMoValue value)
{
if (int.TryParse(key.Value, out int index))
@@ -58,6 +62,7 @@ public void Set(MoPath key, IMoValue value)
}
}
+ ///
public IMoValue Get(MoPath key, MoParams parameters)
{
if (int.TryParse(key.Value, out int index))
@@ -65,19 +70,22 @@ public IMoValue Get(MoPath key, MoParams parameters)
return this[index];
}
- throw new MoLangRuntimeException($"Invalid path for array access: {key.Path.ToString()}", null);
+ throw new MoLangRuntimeException($"Invalid path for array access: {key.Path.ToString()}");
}
+ ///
public void Clear()
{
- throw new NotImplementedException();
+ throw new NotSupportedException("Cannot clear an ArrayStruct");
}
+ ///
public object Value => _array;
}
public class VariableArrayStruct : VariableStruct
{
+ ///
protected override IMoStruct CreateNew()
{
return new ArrayStruct();
diff --git a/src/MolangSharp/Runtime/Struct/IMoStruct.cs b/src/MolangSharp/Runtime/Struct/IMoStruct.cs
index 1a7abdb..e4b0fd0 100644
--- a/src/MolangSharp/Runtime/Struct/IMoStruct.cs
+++ b/src/MolangSharp/Runtime/Struct/IMoStruct.cs
@@ -1,20 +1,38 @@
+using System;
using ConcreteMC.MolangSharp.Runtime.Value;
using ConcreteMC.MolangSharp.Utils;
namespace ConcreteMC.MolangSharp.Runtime.Struct
{
+ ///
+ /// The interface used for all MoLang containers/structs
+ ///
public interface IMoStruct : IMoValue
{
+ ///
+ /// Assign a value to a property
+ ///
+ /// The path of the property to modify
+ /// The value to set
void Set(MoPath key, IMoValue value);
+ ///
+ /// Get the value of a property
+ ///
+ /// The path of the property to get
+ /// The parameters used to retrieve the value
+ /// The value of the property retrieved
IMoValue Get(MoPath key, MoParams parameters);
+ ///
+ /// Clears the struct
+ ///
void Clear();
///
- bool IMoValue.Equals(IMoValue b)
+ bool IEquatable.Equals(IMoValue other)
{
- return this.Equals((object)b);
+ return this.Equals((object) other);
}
}
}
\ No newline at end of file
diff --git a/src/MolangSharp/Runtime/Struct/Interop/FieldAccessor.cs b/src/MolangSharp/Runtime/Struct/Interop/FieldAccessor.cs
index 33b65dd..189f6e4 100644
--- a/src/MolangSharp/Runtime/Struct/Interop/FieldAccessor.cs
+++ b/src/MolangSharp/Runtime/Struct/Interop/FieldAccessor.cs
@@ -14,7 +14,7 @@ public FieldAccessor(FieldInfo fieldInfo)
public override bool CanRead => true;
public override bool CanWrite => !_fieldInfo.IsInitOnly;
-
+
///
public override IMoValue Get(object instance)
{
@@ -22,7 +22,7 @@ public override IMoValue Get(object instance)
return value is IMoValue moValue ? moValue : MoValue.FromObject(value);
}
-
+
///
public override void Set(object instance, IMoValue value)
{
diff --git a/src/MolangSharp/Runtime/Struct/Interop/PropertyAccessor.cs b/src/MolangSharp/Runtime/Struct/Interop/PropertyAccessor.cs
index d4acdf5..d4edea6 100644
--- a/src/MolangSharp/Runtime/Struct/Interop/PropertyAccessor.cs
+++ b/src/MolangSharp/Runtime/Struct/Interop/PropertyAccessor.cs
@@ -14,7 +14,7 @@ public PropertyAccessor(PropertyInfo propertyInfo)
public override bool CanRead => _propertyInfo.CanRead;
public override bool CanWrite => _propertyInfo.CanWrite;
-
+
///
public override IMoValue Get(object instance)
{
@@ -31,24 +31,28 @@ public override void Set(object instance, IMoValue value)
if (propType == typeof(double))
{
_propertyInfo.SetValue(instance, value.AsDouble());
+
return;
}
else if (propType == typeof(float))
{
_propertyInfo.SetValue(instance, value.AsFloat());
+
return;
}
else if (propType == typeof(bool))
{
_propertyInfo.SetValue(instance, value.AsBool());
+
return;
}
else if (propType == typeof(string))
{
_propertyInfo.SetValue(instance, value.AsString());
+
return;
}
-
+
_propertyInfo.SetValue(instance, value);
InvokeChanged();
}
diff --git a/src/MolangSharp/Runtime/Struct/Interop/PropertyCache.cs b/src/MolangSharp/Runtime/Struct/Interop/PropertyCache.cs
index d2ca46b..dfa9bac 100644
--- a/src/MolangSharp/Runtime/Struct/Interop/PropertyCache.cs
+++ b/src/MolangSharp/Runtime/Struct/Interop/PropertyCache.cs
@@ -16,9 +16,8 @@ public class PropertyCache
public PropertyCache(Type arg)
{
var properties = new Dictionary(StringComparer.OrdinalIgnoreCase);
- var functions = new Dictionary>(
- StringComparer.OrdinalIgnoreCase);
-
+ var functions = new Dictionary>(StringComparer.OrdinalIgnoreCase);
+
ProcessMethods(arg, functions);
ProcessProperties(arg, properties);
@@ -90,7 +89,7 @@ IMoValue ExecuteMolangFunction(object instance, MoParams mo)
}
else if (t == typeof(float))
{
- parameters[index] = (float)mo.GetDouble(index);
+ parameters[index] = (float) mo.GetDouble(index);
}
else if (t == typeof(string))
{
@@ -142,6 +141,7 @@ private static void ProcessProperties(IReflect type, IDictionary() != null)
accessor.Observable = true;
@@ -157,8 +157,9 @@ private static void ProcessProperties(IReflect type, IDictionary() != null)
accessor.Observable = true;
diff --git a/src/MolangSharp/Runtime/Struct/Interop/ValueAccessor.cs b/src/MolangSharp/Runtime/Struct/Interop/ValueAccessor.cs
index b767940..0d2f6bc 100644
--- a/src/MolangSharp/Runtime/Struct/Interop/ValueAccessor.cs
+++ b/src/MolangSharp/Runtime/Struct/Interop/ValueAccessor.cs
@@ -5,13 +5,14 @@ namespace ConcreteMC.MolangSharp.Runtime.Struct.Interop
{
public abstract class ValueAccessor
{
- public event EventHandler ValueChanged;
+ public event EventHandler ValueChanged;
public bool Observable { get; internal set; }
public abstract bool CanRead { get; }
public abstract bool CanWrite { get; }
public abstract IMoValue Get(object instance);
+
public abstract void Set(object instance, IMoValue value);
protected void InvokeChanged()
diff --git a/src/MolangSharp/Runtime/Struct/InteropStruct.cs b/src/MolangSharp/Runtime/Struct/InteropStruct.cs
index 3ef616e..4358e17 100644
--- a/src/MolangSharp/Runtime/Struct/InteropStruct.cs
+++ b/src/MolangSharp/Runtime/Struct/InteropStruct.cs
@@ -44,7 +44,7 @@ public void Set(MoPath key, IMoValue value)
{
if (!key.HasChildren)
{
- if (_propertyCache.Properties.TryGetValue(key.ToString(), out var accessor))
+ if (_propertyCache.Properties.TryGetValue(key.Value, out var accessor))
{
if (!accessor.CanWrite)
throw new MoLangRuntimeException("Cannot write to ReadOnly property!", null);
@@ -87,16 +87,19 @@ private bool ExecuteGet(MoPath property, string main, MoParams parameters, out I
throw new MoLangRuntimeException($"Cannot read from property '{property.ToString()}'", null);
returnValue = accessor.Get(_instance);
+
return true;
}
if (_propertyCache.Functions.TryGetValue(main, out var f))
{
returnValue = f.Invoke(_instance, parameters);
+
return true;
}
returnValue = DoubleValue.Zero;
+
return false;
}
@@ -120,7 +123,7 @@ public IMoValue Get(MoPath key, MoParams parameters)
}
}
- if (!ExecuteGet(key, key.ToString(), parameters, out var returnValue))
+ if (!ExecuteGet(key, key.Value, parameters, out var returnValue))
Debug.WriteLine($"({_instance.ToString()}) Unknown query: {key}");
return returnValue;
@@ -129,7 +132,7 @@ public IMoValue Get(MoPath key, MoParams parameters)
///
public void Clear()
{
- throw new NotImplementedException();
+ throw new NotSupportedException("Cannot clear an InteropStruct.");
}
}
}
\ No newline at end of file
diff --git a/src/MolangSharp/Runtime/Struct/QueryStruct.cs b/src/MolangSharp/Runtime/Struct/QueryStruct.cs
index 705574f..9846a4b 100644
--- a/src/MolangSharp/Runtime/Struct/QueryStruct.cs
+++ b/src/MolangSharp/Runtime/Struct/QueryStruct.cs
@@ -32,11 +32,11 @@ public void Set(MoPath key, IMoValue value)
///
public IMoValue Get(MoPath key, MoParams parameters)
{
- if (Functions.TryGetValue(key.ToString(), out var func))
+ if (Functions.TryGetValue(key.Value, out var func))
{
return MoValue.FromObject(func(parameters));
}
-
+
return DoubleValue.Zero;
}
diff --git a/src/MolangSharp/Runtime/Struct/VariableStruct.cs b/src/MolangSharp/Runtime/Struct/VariableStruct.cs
index cc48595..4bddb2e 100644
--- a/src/MolangSharp/Runtime/Struct/VariableStruct.cs
+++ b/src/MolangSharp/Runtime/Struct/VariableStruct.cs
@@ -27,6 +27,10 @@ public VariableStruct(IEnumerable> values)
{
Map = new Dictionary(values, StringComparer.OrdinalIgnoreCase);
}
+ else
+ {
+ Map = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ }
}
protected virtual IMoStruct CreateNew()
@@ -37,8 +41,6 @@ protected virtual IMoStruct CreateNew()
///
public virtual void Set(MoPath key, IMoValue value)
{
- //var index = key.IndexOf('.');
-
if (!key.HasChildren)
{
Map[key.Value] = value;
@@ -50,13 +52,10 @@ public virtual void Set(MoPath key, IMoValue value)
if (!string.IsNullOrWhiteSpace(main))
{
- //object vstruct = Get(main, MoParams.Empty);
-
if (!Map.TryGetValue(main, out var container))
{
if (!key.HasChildren)
{
- //Map.TryAdd(main, container = new VariableStruct());
throw new MoLangRuntimeException($"Variable was not a struct: {key}", null);
}
@@ -71,29 +70,22 @@ public virtual void Set(MoPath key, IMoValue value)
{
throw new MoLangRuntimeException($"Variable was not a struct: {key}", null);
}
-
- //((IMoStruct) vstruct).Set(string.Join(".", segments), value);
-
- //Map[main] = (IMoStruct)vstruct;//.Add(main, (IMoStruct) vstruct);
}
}
///
public virtual IMoValue Get(MoPath key, MoParams parameters)
{
- //var index = key.IndexOf('.');
-
if (key.HasChildren)
{
string main = key.Value;
if (!string.IsNullOrWhiteSpace(main))
{
- IMoValue value = null; //Map[main];
+ IMoValue value = null;
if (!Map.TryGetValue(main, out value))
{
- // Log.Info($"Unknown variable map: {key}");
return DoubleValue.Zero;
}
@@ -109,9 +101,6 @@ public virtual IMoValue Get(MoPath key, MoParams parameters)
if (Map.TryGetValue(key.Value, out var v))
return v;
- //
- // Log.Info($"Unknown variable: {key}");
-
return DoubleValue.Zero;
}
diff --git a/src/MolangSharp/Runtime/Value/DoubleValue.cs b/src/MolangSharp/Runtime/Value/DoubleValue.cs
index 8102b98..bc5b614 100644
--- a/src/MolangSharp/Runtime/Value/DoubleValue.cs
+++ b/src/MolangSharp/Runtime/Value/DoubleValue.cs
@@ -2,10 +2,18 @@
namespace ConcreteMC.MolangSharp.Runtime.Value
{
- public class DoubleValue : IMoValue
+ ///
+ /// Represents a double-precision floating-point number
+ ///
+ public class DoubleValue : IMoValue
{
+ private readonly double _value;
+
+ ///
+ object IMoValue.Value => _value;
+
///
- object IMoValue.Value => Value;
+ public double Value => _value;
///
public bool Equals(IMoValue b)
@@ -16,11 +24,6 @@ public bool Equals(IMoValue b)
return false;
}
- private readonly double _value;
-
- ///
- public double Value => _value;
-
public DoubleValue(object value)
{
if (value is bool boolean)
@@ -65,7 +68,7 @@ public override bool Equals(object obj)
// ...the rest of the equality implementation
}
- protected bool Equals(DoubleValue other)
+ public bool Equals(DoubleValue other)
{
return _value.Equals(other._value);
}
@@ -79,43 +82,16 @@ public override int GetHashCode()
///
public double AsDouble()
{
- return Value;
+ return _value;
}
///
public float AsFloat()
{
- return (float)Value;
+ return (float) _value;
}
public static DoubleValue Zero { get; } = new DoubleValue(0d);
public static DoubleValue One { get; } = new DoubleValue(1d);
}
-
- /*public class FloatValue : IMoValue
- {
- ///
- object IMoValue.Value => Value;
-
- ///
- public float Value { get; }
-
- public FloatValue(object value) {
- if (value is bool) {
- Value = (bool) value ? 1.0f : 0.0f;
- } else if (value is float) {
- Value = (float) value;
- } else {
- Value = 1.0f;
- }
- }
-
- public FloatValue(float value)
- {
- Value = value;
- }
-
- public static FloatValue Zero => new FloatValue(0);
- public static FloatValue One => new FloatValue(1);
- }*/
}
\ No newline at end of file
diff --git a/src/MolangSharp/Runtime/Value/MoValue.cs b/src/MolangSharp/Runtime/Value/MoValue.cs
index d7e527d..514bed3 100644
--- a/src/MolangSharp/Runtime/Value/MoValue.cs
+++ b/src/MolangSharp/Runtime/Value/MoValue.cs
@@ -1,26 +1,22 @@
+using System;
using System.Collections;
using System.Collections.Generic;
using ConcreteMC.MolangSharp.Runtime.Struct;
namespace ConcreteMC.MolangSharp.Runtime.Value
-
{
- public interface IMoValue : IMoValue
- {
- new T Value { get; }
- }
-
- public interface IMoValue
+ ///
+ /// Resembles a MoLang compatible value.
+ ///
+ public interface IMoValue : IEquatable
{
object Value { get; }
- bool Equals(IMoValue b);
-
- string AsString() => Value.ToString();
+ virtual string AsString() => Value.ToString();
virtual double AsDouble() => Value is double db ? db : 0d;
- virtual float AsFloat() => Value is float flt ? flt : (float)AsDouble();
+ virtual float AsFloat() => Value is float flt ? flt : (float) AsDouble();
virtual bool AsBool() => Value is bool b ? b : AsDouble() > 0;
}
diff --git a/src/MolangSharp/Runtime/Value/StringValue.cs b/src/MolangSharp/Runtime/Value/StringValue.cs
index ab4e9ff..dab4636 100644
--- a/src/MolangSharp/Runtime/Value/StringValue.cs
+++ b/src/MolangSharp/Runtime/Value/StringValue.cs
@@ -1,6 +1,9 @@
namespace ConcreteMC.MolangSharp.Runtime.Value
{
- public class StringValue : IMoValue
+ ///
+ /// Represents a string value
+ ///
+ public class StringValue : IMoValue
{
///
object IMoValue.Value => Value;
diff --git a/src/MolangSharp/Utils/MoPath.cs b/src/MolangSharp/Utils/MoPath.cs
index 8268399..84ae1ba 100644
--- a/src/MolangSharp/Utils/MoPath.cs
+++ b/src/MolangSharp/Utils/MoPath.cs
@@ -1,13 +1,36 @@
namespace ConcreteMC.MolangSharp.Utils
{
+ ///
+ /// Describes the path of a variable/function
+ ///
public class MoPath
{
+ ///
+ /// The root of this path
+ ///
public MoPath Root { get; }
+
+ ///
+ /// The next element
+ ///
public MoPath Next { get; private set; }
+ ///
+ /// The full path
+ ///
public string Path { get; }
+
+ ///
+ /// The value of this path
+ ///
+ ///
+ /// Think of this is the filename in a path
+ ///
public string Value { get; private set; }
+ ///
+ /// Whether this path has any child elements
+ ///
public bool HasChildren => Next != null;
public MoPath(string path)
@@ -20,6 +43,7 @@ public MoPath(string path)
Value = segments[0];
MoPath current = this;
+
if (segments.Length > 1)
{
string currentPath = $"{Value}";
@@ -40,7 +64,7 @@ public MoPath(string path)
}
}
- internal MoPath(MoPath root,string path, string value)
+ internal MoPath(MoPath root, string path, string value)
{
Root = root;
Path = path;
@@ -51,11 +75,11 @@ internal void SetValue(string value)
{
Value = value;
}
-
+
///
public override string ToString()
{
- return Value;
+ return Path;
}
}
}
\ No newline at end of file