Coder Social home page Coder Social logo

florianrappl / mages Goto Github PK

View Code? Open in Web Editor NEW
126.0 12.0 15.0 1.38 MB

:tophat: MAGES is a very simple, yet powerful, expression parser and interpreter.

License: MIT License

C# 99.59% PowerShell 0.21% Shell 0.20%
nuget expression-parser interpreter dotnet c-sharp

mages's Introduction

MAGES Logo

MAGES

GitHub CI NuGet Issues

Mages: Another Generalized Expression Simplifier

MAGES is the official successor to YAMP. It is a very simple, yet powerful, expression parser and interpreter. You can use MAGES to include a sophisticated, easy to customize, and lightweight scripting engine to your application.

Among other applications, MAGES has been used in Microsoft's PowerToys.

Current Status

MAGES was just updated (v3.0.0) with object metadata, direct list support, and JSX syntax.

Previous Status

2023:

MAGES was updated (v2.0.0) with support for complex numbers. Also, the build target and runtime has been updated to make use of modern possibilities.

2018:

The first stable version has been released. The current version 1.6.0 contains an improved REPL. The library contains everything to perform lightweight scripting operations in C#. A CodeProject article about the library (also containing some background and performance comparisons) is also available.

Installation

MAGES itself does not have any dependencies, however, the tests are dependent on NUnit and the benchmarks use BenchmarkDotNet. Usually, MAGES should be installed via the NuGet package source. If this does not work for you, then clone the source and build MAGES yourself. Make sure that all unit tests pass.

The whole library was designed to be consumed from .NET Core 3.0 (or higher) / .NET 5.0 (or higher) applications. This means it is (amongst others) compatible with Unity 2021.2 or Mono 6.4. The NuGet package is available via the official package feed.

Get Me Started!

In the most simple case you are creating a new engine to hold a global scope (for variables and functions) and launch the interpretation.

var engine = new Mages.Core.Engine();
var result = engine.Interpret("sin(2) * cos(pi / 4)"); // 0.642970376623918

You can also go-ahead and make reusable blocks from snippets.

var expOne = engine.Compile("exp(1)");
var result = expOne(); // 2.71828182845905

Or you can interact with elements created by MAGES.

var func = engine.Interpret("(x, y) => x * y + 3 * sqrt(x)") as Mages.Core.Function;
var result = func.Invoke(new Object[] { 4.0, 3.0 }); // 18.0

Or even simpler (details are explained in the getting started document):

var func = engine.Interpret("(x, y) => x * y + 3 * sqrt(x)") as Mages.Core.Function;
var result = func.Call(4, 3); // 18.0

These are just some of the more basic examples. More information can be found in the documentation.

Documentation

The documentation is given in form of Markdown documents being placed in the doc folder of this repository. The following links are worth checking out:

If anything is missing, unclear, or wrong then either submit a PR or file an issue. See the following section on contributions for more information.

Contributions

Contributions in form of feature implementations or bug fixes are highly welcome, but need to be performed in an organized and consistent way. The contribution guidelines should be read before starting any work.

Contributions may also be taken in form of bug reports and feature requests. Long live open-source development!

Versioning

The rules of semver are our bread and butter. In short this means:

  1. MAJOR versions at maintainers' discretion following significant changes to the codebase (e.g., breaking API changes)
  2. MINOR versions for backwards-compatible enhancements (e.g., performance improvements, additional extensions)
  3. PATCH versions for backwards-compatible bug fixes (e.g., specification compliance bugs, support issues)

Hence: Do not expect any breaking changes within the same major version.

Sponsors

The following companies sponsored part of the development of MAGES.

Thanks for all the support and trust in the project!

License

The MIT License (MIT)

Copyright (c) 2016-2024 Florian Rappl

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

mages's People

Contributors

florianrappl avatar ghostvaibhav avatar wootencl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mages's Issues

Filesystem plugin

I want to include a plugin (similar to the linear algebra one) that contains a set of file helpers. They all should be created in such a fashion to support piping quite well, e.g.,

"Some content" | file.create("foo.txt")
"Some content" | file.append("foo.txt")

The package should contain at least three objects (namespaces), file, directory and path. The first one works with files, the second one with directories, and the last contains some path helpers.

Provide `args` argument

Right now additional arguments are just omitted. This makes sense especially for external functions, however, internally the additional information may be wanted.

There are two possibilities. Either one introduces a special syntax, e.g.,

f = (x, y, ...) => length(args)
ans = f(1, 1, 3, 4, "hallo"); // 3

or one always collects everything

f = (x, y) => length(args)
ans = f(1, 1, 3, 4, "hallo"); // 5

Since the former would introduce parser changes I will most probably go for the latter (args being always available and collecting everything).

Now the only decision is the API design of such an object. We cannot expose object[], but we can expose an IDictionary wrapper that would also resemble arrays / lists. Essentially, the idea is the same as in JavaScript. The idea is described in greater detail in #17.

Update: Yes, the latter has been chosen.

Matrix operations

Currently, only numeric arithmetic operations are included. Its time to also include the most common matrix operations, e.g., MxM, MxV, MxS, M+M, M-M, ...

this for member functions

Functions that are created within objects should have an implicit this variable, e.g.,

var foo = new {
  a: 5,
  bar: () => this.a
};
foo.bar() // 5

However, this excludes (on purpose / by design) functions that are attached, e.g.,

var foo = new {
  a: 5
};
foo.bar = () => this.a;
foo.bar() // undefined

Hence functions that are given directly are treated as methods, while functions that are inserted later on are treated as ordinary functions.

It should be noted that the this is preserved, e.g.,

var foo = new {
  a: 5,
  bar: () => this.a
};
var bar = foo.bar;
var baz = new {
  a: 7,
  getA: foo.bar
};
foo.bar() // 5
bar() // 5
baz.getA() // 5

The reason is simple: When we get the function we also get its local scope, which is where the this is defined (and already associated with the object). Thus calling it without the object has no effect. Calling it from another object also has no effect, as the outer this is shadowed by the captured inner this.

User documentation

Document the primary use-cases and scenarios, e.g.,

  • Adding the engine
  • Interpreting scripts
  • Caching and "compiling"
  • Providing own objects and functions
  • Auto-complete and errors

Also provide a little bit more on the syntax, but not in the syntax document, but rather example-based.

Performance

Overall, MAGES should have a (much?) lower parse / interpretation time and GC pressure as compared to YAMP.

This item will setup a number of (micro?) benchmarks and result in the required changes to make MAGES faster than YAMP. Especially the GC pressure is of interest.

Random plugin

YAMP contained a large set of random functionality - MAGES only knows a very simple rand function. The random plugin provides a larger set of distributions - all coming with the functionality known from YAMP.

Serialize instructions and AST

It would be nice to have functions in the REPL to show the instructions for a certain expression. Another thing would be to have the syntax tree printed for a given expression.

The functions accessible from MAGES should be included in the REPL, but the basic functionality has to be included in the core library already. This would allow the serialization to be accessible also for any kind of client.

In the REPL this should then work similar to:

$ il("2+3");
const 2
const 3
const [Function]
getc 2

and for the AST we have:

$ ast("2+3");
Root: BinaryExpression.Add
- LValue: ConstantExpression
  - Value: 2
- RValue: ConstantExpression
  - Value: 3

Object metadata

Right now objects are simple IDictionary<String, Object> bags. This won't be changed for sure. However, giving them "special keys" that are associated with meta information would yield several advantages. It would give us the ability to create generic objects that - very much like JavaScript - define their prototype without much trouble.

Potentially, one way is to start metadata properties with an $ symbol, e.g., $prototype. There are many other options. The prototype metadata, for instance, could be used to distinguish an array-like object from a standard object (and many more).

One of the questions that needs to be answered before implementing this is the impact. It is clear that MAGES won't leave the road of having .NET System-level objects, instead of custom definitions. In that manner the metadata properties will just appear like regular keys. However,

  • should they be removed, e.g., for JSON?
  • should they be included in, e.g., keys?
  • how should one interact with them within MAGES - if at all
  • are they only "touchable" from, e.g., C# / .NET?

They could also be used to transport "attributes" and "documentation" of certain objects.

Logical functions

Besides comparison functions MAGES offers a simple set of elementary logical functions. This set consists of:

  • isnan
  • isint
  • isprime
  • isinfty

The same style as in #2, #5 and related issues should be used.

Extend documentation

Right now the documentation is already in a quite good state. However, there should be more examples, use-cases, and general best-practices.

Maybe also some sections are missing?

I would appreciate if anyone could give me some input here. I will try to improve / extend the existing version and adjust it to satisfy all needs.

Import and export

The REPL should contain two functions: import and export, which can be used to import code from a module (= file) or declare the code to export from a module. Every module will be processed with a fresh engine incl. user and api scope.

Such that we can use one file, e.g., sample.ms to use some other file:

var http = import("http.ms");
http.request("..."); 

where http.ms looks similar to

// ...
export(new {
  request: url => { /* ... */ }
});

export can be used anywhere, however, only the last usage is significant. export only takes a single argument. If its called with no argument the current list of exports will is "reset".

Generalized arrays

MAGES has the matrix literal []. This only supports numbers. Even logical operations do not leave a Boolean[,] behind, but a Double[,]. Nevertheless, for interacting with .NET functions and for users to have a convenience data structure a generalized array should be introduced.

Currently, I have two different syntaxes in mind (which one should it be?):

array_variant1 = new { a, b, c };
array_variant2 = new [ a, b, c ];

The first variant looks like a standard object, however, it does not contain any keys. The second one looks like a matrix, however, its started with new like objects.

Now one might point out that the following is ambiguous:

array_or_obj = new { };

But in fact it wouldn't be. The generalized array would in fact be an object that contains special numeric keys (stored as strings). Therefore the whole idea behind the generalized array is 100% the same as with JavaScript. In fact its not 100%, as an Array in JS also has features that cannot be found in standard objects, while in MAGES they are identical in every aspect.

So the following behavior is expected

obj = new { 1, 2, 3};
first = obj(0); // 1
obj.a = "hallo";
l = length(obj); // 4
last = obj(3); // null

It is up to the programmer to use such an object (generalized array) "correctly", i.e., to consistently use it with numeric keys. However, potentially a set of helpers to use it correctly / more reliably will be introduced in the long run.

Trigonometric functions

MAGES contains the most essential trigonometric function, e.g.:

  • sin
  • cos
  • tan
  • asin
  • acos
  • atan

The overloads follow pretty much the ones given in #2.

Auto currying

Functions that require at least 1 argument and are invoked with

  • 0-arguments will yield the function itself
  • less arguments than required will yield a curried version of itself

Examples. Let's say standard f(x) -> y functions are invoked as f(), then f() -> f, e.g., f()(3) = f(3). This is of course idempotent, such that f()()()()()(3) = f(3). Similarly, if f(x,y) -> z we obtain f() -> f and f(x) -> g where g(y) = f(x, y), e.g., f(3)(2) = f(3, 2) and g = f(5) with g(2) = f(5, 2) and g(0) = f(5, 0).

This will be applied to all automatically inserted (external) functions as well as available standard functions (also e.g., to operators like add or subtract). This also applies to functions that are defined within MAGES.

f = (x, y) => x + y;
g = f(2);
ans = g(3); // same as f(2, 3)

If more arguments are supplied the unnecessary arguments are just ignored. This behavior won't change.

Reduce and where

Two new functions should be handy in many scenarios: where and reduce. The latter is a nice addition to map, which transforms elements (providing a mapping from the source to the target). It will reduce the N elements to a single item.

The where function is essentially filter in JS (or Where in LINQ).

reduce will work with matrices and objects. It requires three arguments: a function, a start value, and an object to operate on.

new { a: 5, b: 9, c: 10 } | reduce(add, 0) // 0 + 5 + 9 + 10
[1, 3, 10] | reduce(subtract, 10) // 10 - 1 - 3 - 10

where will work with matrices and objects. It requires two arguments: a function and an object to operate on.

new { a: 5, b: 9, c: 10 } | where(x => x > 8) // new { b: 9, c: 10 }
[1, 3, 10] | where(greater(2)) // [3, 10]

Remark In v0.9 I will change greater, less, etc. to be actually like greater(x, y) == y > x. Right now (v0.8) it is greater(x, y) == x > y. There are some other functions that need to be reversed to be better for chaining, e.g., subtract, pow, or divide.

Continue statement

Now that we can enter loops (#20) and break from them (#24) it is also time to go back to the top of the loop.

Enable validation

Right now the evaluation is done without considering the validity of the code. This, however, has to be done implicitly. The IValidatable interface was implemented by all structures participating in the AST, such that any tree walker can use Validate on the elements.

This feature will now be implemented in two stages:

  1. Every AST element will only validate itself. Children will only be validated if they may cause invalid behavior on the parent (in the combination; not by themselves).
  2. There will be a dedicated validation tree walker, which could be used for standalone (excessive) validation. The operation tree walker, however, will perform the most simple validation (and will stop once any error is encountered).

Hence the performance should be minimally impacted with the possibility to get a much more detailed report in case of error.

Break statement

Since loops are entering the area it is also time to get out of a loop: see #20.

Document existing plugins

The REPL comes with some plugins. These plugins (and plugin development in general, i.e., what conventions and best practices should be applied) have to be documented somewhat.

For the v1 version this should be included.

Zip and concat

As an addition to #37 I'd also like to introduce two more useful functions: zip and concat. Both take two enumerables (Object or Matrix), however, while the first one gets a list r with length(r) = min(length(a), length(b)), the second one comes up with a result length(r) = length(a) + length(b).

var a = new { a: 2, b: 3 };
var b = new { a: 8 };
var r = zip(a, b); //new { "0": { "0": 2, "1": 8 } }

Compared to:

var a = new { a: 2, b: 3 };
var b = new { a: 8 };
var r = concat(a, b); //new { "0": 2, "1": 3, "2": 8 }

The general behavior regarding different types is similar to #37.

Extensibility for new functions

Functions and objects / constants should be easily integrable in MAGES. Ideally, one can just add them to scopes such that they can be resolved and used.

Add more standard functions

The set of trigonometric functions is quite complete, however, we could add some more functions to this set. In particular:

  • Hyperbolic functions (e.g., sech, csch)
  • Inverse hyperbolic functions (e.g., arsin)

Also some other special functions which are quite common could be included.

Auto-expose API

To benefit from scripting environments users need to expose their API in the scripting environment. This is tedious and not a lot of fun. Also errors are quite common.

One of the goals of MAGES is to make this as simple as possible. It should therefore be possible to just hand in

  • single functions (already possible, but names have to be given)
  • classes / types (see #9 for objects already)
  • "namespaces" / whole collections of classes (subsets of an assembly)

The scope of this feature is to provide attributes to auto-detect / include types. Furthermore, the process should also work w/o attributes in case an cannot access the assemblies (here a little bit more work is required on the user's side, but it should be still simple and straightforward).

Attachable properties

Properties should be attachable, similar to type functions. That way one could write, e.g.,

a = list(1, true, "foo");
a.length

Furthermore, it could be possible to give, e.g., matrices, some kind of properties - similar to dictionaries. This way we can have

v = [1, 2, 3];
v.x + v.y - v.z // 1 + 2 - 3 = 0

Attachable properties are most likely defined by a Dictionary<String, Func<T, Object>> where T is the type to attach a property to. So for every T (of the 6 types in MAGES) there would be zero or one such dictionary.

ObservableDictionary implementation

It would be convenient to supply an observable dictionary implementation. This class would implement the IDictionary<String, Object> interface and emit events on change.

Essentially, it would also improve the situation for providing custom elements. For performance reasons it is recommended to do the “wrapping” of exposed objects like this. If one gives Mages the following

var pt = new Point();

where

class Point 
{ 
  public Double X { get; set; }
  public Double Y { get; set; }
}

then everything will be wrapped with reflection being used as intermediate layer. However, if one does

var pt = new Dictionary<String, Object>();
pt[“x”] = 0.0;
pt[“y”] = 0.0;

then nothing will be wrapped. The disadvantage of the latter is that the owner won’t be notified if something changes.

This issue would now come up with

var pt = new ObservableDictionary();
pt["x"] = 0.0;
pt.Observe("y", (sender, newValue, oldValue) => { /* ... */ }, 0.0);

The last argument is the optional current value. The Observe method is an extension method. Only the Changed event will be available on the ObservableDictionary class.

Arithmetic functions

Standard arithmetic functions should be included, e.g.:

  • sqrt
  • pow
  • factorial
  • abs
  • sign
  • ceil
  • floor
  • exp
  • log

The basic operators (e.g., +) should be also available as a function (e.g., add). This would be interesting in a lot of scenarios.

is / as functions

I will introduce two handy functions: is() and as(). The first one checks if a given value is from a given type.

Examples:

is("String", "Test string") // true
is("Number", true) // false
is_bool = is("Boolean"); is_bool(14 ~= 7) // true

As the helpers (e.g., is_bool) are trivial to create on the fly (using currying) these won't be provided.

Similarly, there will be the as function, which is a defined way to do type casts.

Examples:

as("String", 3.5) // "3.5"
as("Matrix", true) // [1]
as("String", null) // ""

If a cast fails the result is of type "Undefined" (.NET null).

Notify on invalid tail

Right now the parser just ignores an invalid tail, however, this is not only inconsistent but also dangerous (users may think that everything is fine, when instead the source code is not fully transformed to operations).

I think the parser needs to be more strict on source code validity. Therefore I will make sure that there are no unused passages in a code that is considered valid.

Plugin architecture

MAGES should come with plugins. Of course, the general idea to allow extensibility is already deep in the core of MAGES, but plugins go one step further.

Instead of requiring users to take actions (e.g., deciding what to add and where) plugins should be pretty much auto add- and removable. Since MAGES does not require special objects plugin libraries do not need to reference Mages.Core. Therefore, any plugin is by definition decoupled from MAGES and is driven by convention.

The task here is to define the conventions and how these may be controlled from the engine's perspective. There will be a List<Plugin> instance in the engine's meta-data. Helpers like AddPlugin and RemovePlugin can be used. Any .NET library can contain one to many plugins.

This is supposed to work together with #13 nicely. In fact, the whole plugin mechanism is using #13 together with some other stuff (e.g., to find out what has been overwritten when introducing the plugin - to restore it at removal).

A library that is used as a plugin (but does not follow any conventions) will be most likely treated as if #13 was used for the library. The difference is just that the library can then be removed and is named in the formerly mentioned list of plugins.

Return statement

The return statement is a useful companion for functions with block statements as body. It essentially allows to interrupt the evaluation at a specific point.

Implementation-wise it is just a SimpleStatement with the return keyword in front. It just executes its expression and sets the execution position to the last available index (execution length) thus stopping the evaluation immediately.

Object transformation

Regarding #3: Arbitrary objects (from .NET) should be (if not transformable to primitives, e.g., doubles or boolean) be wrapped in IDictionary<String, Object> instances featuring custom setters that work with reflection. These setters use some of the magic from the function transformation.

Autocomplete / "intellisense"

There should be an extension method to give a listing of autocomplete strings for the current position.

For instance, for an empty source the global functions, scope variables, and available keywords (e.g., var) should be returned.

The function to obtain the autocomplete should take the source, and the index where to obtain the autocomplete for.

Pipe operator

I took some time to think about a proper usage for the pipe operator |. There is no bitwise-or implemented and it does not make much sense. However, I came to the conclusion that a pipe operator makes sense.

x = 2 | f | g | h

would be the same as

x = h(g(f(2)))

So the pipe operator puts the argument on the left side into the function on the right side. The functions will be called with a single argument, but this is no problem as intermediate anonymous functions may be created at will.

Type functions

Type functions allow using instances of types to invoke functions. This then allows

obj = new { a = "foo" }
a = obj.a

to be resolved dynamically as well

a = obj("a")

Object functions should come in two forms, the regular signature (getter)

static Object TypeFunctionForObjectType(this ObjectType context, Object[] arguments);

and the optional setter signature

static void TypeFunctionForObjectType(this ObjectType context, Object[] arguments, Object value);

It is important to recognize that they are not member functions of the type. The type could therefore be an interface, an enum, or an existing type. They will be associated by a mapping - a mapping that is dynamically resolved and therefore configurable by users.

Besides the dynamic object getter / setters it will be used to implement matrix indices.

Random numbers

MAGES comes with a simple set of functions to produce random numbers (essentially just exposing the Random class of .NET). Producing a random matrix, vector, or scalar should be possible by using a simple function.

Comparison functions

MAGES should contain a broad set of comparison functions, which includes - but is not limited to - the following functions:

  • min
  • max
  • lt
  • eq
  • gt

It basically follows the same tone given already in #2.

Refine existing functions

Some of the existing functions could be refined a bit more, such that they also work, e.g., with array-like objects.

Also some more tests for the included (standard) functions should be created.

Functional blocks

The function expressions do not support block statements as body right now. This is, however, specified in the syntax.

As long as there is no return statement (following up in v0.6 along with all other constructs, e.g., if and while) the last evaluated expression (most recently added value on the stack) will be returned.

Mages package repository

One thing that is crucial for any kind of language (however, especially for a scripting language) is to be connected to some kind of package repository. I don't want to create a whole new infrastructure, hence this hasn't been discussed so far. But I think there are some ways around it ...

  • Using the NuGet package infrastructure; essentially giving special tags and only content to a package. The content would be copied 1-1 with the convention that the "main" file should be named "index.ms".
  • Using GitHub Gists. Packages would be limited to one file and versioning (optional) would be done via Git as it is backed into GitHub Gists.
  • Using Git repositories independent of the origin. More complex, yet more files / possibilities.
  • Alternatively, other package managers such as bower or npm may be an option.

Currently, I tend to use the NuGet infrastructure. This would provide all required metadata, dependencies, and integrated support from the .NET side.

Intersection, union, and except

Another set of functions to complete the picture started with #37 and #38. This time intersection, union, and except. They should behave exactly as expected from set-theory. One of the differences to prior functions is that the consider the object keys as well, i.e.,

intersection(new { a: 2, c: 4 }, new { a: 9 }); // new { }
intersection(new { a: 2, c: 4 }, new { a: 2 }); // new { a: 2 }

For union we therefore have:

union(new { a: 2, c: 4 }, new { a: 9 }); // new { a: { "0": 2, "1": 9 }, c: 4 }
union(new { a: 2, c: 4 }, new { a: 2 }); // new { a: 2, c: 4 }

And for except the same thing, just as an exclusion (look at the order of arguments):

except(new { a: 9 }, new { a: 2, c: 4 }); // new { a: 2, c: 4 }
except(new { a: 2 }, new { a: 2, c: 4 }); // new { c: 4 }

Interpretation

Right now only the AST is generated from the tokenized content. The logical next step is to linearize it for a VM and start the interpretation.

REPL installer and distribution

I think the REPL (which was intentionally held short and sweet) is becoming more useful in the future. Therefore, I will supply a Squirrel-based installer that will be uploaded with each release on GitHub. Finally, there will be a Chocolatey package to allow automatic installation during system setup.

X-assignment operator

Besides the usual assignment operator = there should be a set of assignment operators that are obtained by using a binary operator X in combination, e.g., to get

r = r X (v); // also possible as
r X= v;

This X-Assignment operator would be automatically created by the parser as a special kind of expression consisting of a left side (with similar consistency check as the usual assignment operator) and a right side, plus an operator that has been used. The operator would then be resolvable just like the usual binary operator.

Provide map function

A map function would be useful to map (key, value) to (key, value' = f(value)) by a mapping function f. In conjunction with currying and the pipe-operator this would yield a powerful construct.

new { a: "Hallo", b: "Name", c: [1,2,3] } | map(length) // new { a: 5, b: 4, c: 4 }

Another example:

new { a: "Hallo", b: "Name", c: [1,2,3] } | map(is("String")) // new { a: true, b: true, c: false }

Of course we can also supply custom functions

[1, 2, 3] | map(x => x^3 + x^2 - x) // new { "0": 1, "1": 11, "2": 31 }

Please note that a matrix has to be mapped to an Array-like object, as the mapping also works with non-number types.

Include .NET Libraries

It would be nice if the REPL could load "arbitrary" .NET libraries - as if imported as a plugin. Essentially, this means wrapping the whole library. I don't believe another function would be useful, therefore I think the import should recognize this by the .dll extension.

var system = import("System.dll");

The search path should be (in order)

  • current working directory
  • mages (REPL) directory
  • GAC

Interpolated strings

It would be cool if the language would support string interpolation, e.g.,

a = 5;
b = 9;
s = `Here {a} is not like {b}. In fact the difference is {b - a}`;

There are multiple versions. The one above uses a new kind of string literal with backticks. One could also imagine something like

s = $"Here {a} is not like {b}. In fact the difference is {b - a}";

or any other starting character. Furthermore, the way to distinguish between expressions and the string is yet to be determined. Besides using curly braces to include arbitrary expressions one could think of

s = `Here $a is not like $b. In fact the difference is $(b - a)`;

So the syntax could be $ (or any other prefix, possibly defined as prefix before the usual string literal) followed by a single expression.

Overall, I tend to think that the initially shown variant is the best one for MAGES.

While statement

Now that if and return enter the stage we should also include while. This will follow the implementation of if (i.e., it will either force round brackets on the condition or curly brackets on the body).

The break keyword will also be made available, however, in a separate issue (#24). Similarly, for the continue keyword (#25).

If statement

There has to be a way to have conditions besides the condition operator ?. The if statement with an optional else block offers this capability. The implementation won't require round brackets. Of course, they can be added by the user, but again: not required.

Round brackets will be optional thus allowing to write if true { 2+3; }. The curly brackets are, however, required.

Alternatively, round brackets are required and curly brackets optional. This would be more standard-like, however, has the disadvantage to force a syntax on the wrong end.

Include Nuget packages

Now this is similar to #42, however, we distinguish with the .nupkg extension:

var anglesharp = import("AngleSharp.nupkg");

Versions can be specified by using a more specific file name, e.g.,

var anglesharp = import("AngleSharp.0.9.5.nupkg");

The extensibility comes from the module plugin, which allows different importers to be defined.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.