Giter Club home page Giter Club logo

rohansi / mond Goto Github PK

View Code? Open in Web Editor NEW
356.0 356.0 24.0 2.76 MB

A scripting language for .NET Core

Home Page: https://mond.rohan.dev/

License: MIT License

C# 68.26% JavaScript 27.71% TypeScript 2.52% HTML 0.40% CSS 1.11%
aot-compatible bytecode c-sharp compiler debugger dotnet dotnet-core embedded-scripting-language interpreter language mond nuget programming-language repl script-engine scripting scripting-language scripting-languages webassembly

mond's People

Contributors

dajoh avatar datzach avatar dependabot[bot] avatar grandstack avatar jharrilim avatar phrohdoh avatar rohansi avatar sirtony 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  avatar  avatar  avatar  avatar  avatar

mond's Issues

Async/await syntax and library improvements

The current async library works but really isn't nice to use. Errors inside tasks are impossible to handle and don't have meaningful stack traces.

Async functions should be able to fix both of these because the await expression would be able to do more than just save state and return.

More information will be added here later.

'Reverse' pipeline operator proposal.

I thought it'd be prudent to get feedback on this idea before anything gets implemented and pull requests opened. Because this idea is admittedly quite niche, simple though it is. Although if the array prototype had methods like map/filter & co I could see it having a much stronger use case.

The idea is to add a new operator similar to pipeline, except it points in the other direction and works in reverse. The function call is on the left, the value to be passed is on the right, and rather than inserting the value as the first parameter, it inserts it as the last parameter.

The main inspiration behind this is Ruby's blocks, as it would allow Mond to emulate this to a degree.

def qsort( collection, &comparer )
    ...
end

strings = [ "Watermelon", "Apple", "Grape", "Pear", "Honeydew" ]
sorted  = qsort( strings ) do | a, b |
    # Do some super-complex comparison magic
    a.length - b.length
end
fun qsort( collection, comparer ) {
    ...
}

var strings = [ "Watermelon", "Apple", "Grape", "Pear", "Honeydew" ];
var sorted  = qsort( string ) <| fun( a, b ) {
    // Even more magic
    return a.length() - b.length();
}

Redesign the VM bytecode format

Fixed 32-bit could maybe work, but 16-bit with an optional 16-bit extension could be good too. Primary reason is decode performance. The 32-bit reads into byte[] seem to upset the JIT so some normal array access should help out a lot. Plus things will be aligned.

Add __hash metamethod

Currently using object values as keys is flawed because there's no way to provide consistent hashes for equivalent values.

require() enhancements

  • Search directories
  • Search relative to current script?
  • Load IMondLibraryCollections by name?
    • From class libraries?

Defer clearing the stack until returning from a function

The Pop method can just decrement the stack pointer and we can clean up the old values from the stack later. This would need to track the max number of stack slots used at compile time so the VM knows how many slots to clear.

Incorrect array destructuring

var array = [ 1, 2 ];
var [ a, b, c, ...d, e ] = array;
return [ a, b, c, d, e ]; // => [ 1, 2, undefined, [], 2 ]
var [ ...x, y, z ] = [ 1, 2 ];
return [ x, y, z ]; // => [ [], undefined, 2 ]

.NET 3.5 Target

Is there any way to support .NET 3.5 (mono)?

Yes I know this would prob bamboozle the Async/Task support but thats not something that important for me.

I plan to integrate this into the RocketMod plugin framework (at https://github.com/RocketMod/Rocket.Scripting) but it targets only .NET 3.5 as some games it supports still use Unity 5.

`string.Format` support request.

Possible usecase:

const valuePrototype = {}.getPrototype().getPrototype();

const structMetas = {
    __get: fun (this, index) {
        error("unknown field '" + index + "' (get)");
    },
    __set: fun (this, index, value) {
        error("unknown field '" + index + "' (set)");
    }
}.setPrototype(valuePrototype);

fun struct(...properties) {
    foreach (var property in properties) {
//        if (property.getType() != "number")
//            error("struct field names must be numbers");
    }

    return fun (...values)
    {
        var pl = properties.length();
        if (values.length() != pl)
        {
            error(string.Format("struct requires {0} value{1}.", pl, pl > 1 ? "s" : ""));
        }

        var value = {};

        for (var i = 0; i < properties.length(); i++) {
            value[properties[i]] = values[i];
        }

        return value.setPrototype(structMetas);
    };
}

var Point = struct("x", "y");

var p1 = Point(1, 2);
var p2 = Point(10, 20);

printLn("p1.x = " + p1.x);
printLn("p1.y = " + p1.y);

printLn();

printLn("p2.x = " + p2.x);
printLn("p2.y = " + p2.y);

Currently this (rightfully) errors with:

mondbox(line 24): Undefined identifier 'string'

This is also the case with String, System.String and System.string.

How can I get all global variables?

I am creating calculator with Mond and Avalonia. I want to see all global variables on right side. How can I do that? how can I enumerate variables?

Currency I am using hack like this

var globalVariables = state.Run("return global;").Object.Where(x=>x.Value.Type != MondValueType.Function && x.Value.Type != MondValueType.Object);

but it lokks and feels wrong.

Proposal: User defined operators

Using metamethods it's currently possible to create a hack to simulate infix operators:

fun Infix( callback ) {
    return {
        __or: fun( left ) {
            return {
                __or: fun( _, right ) {
                    return callback( left, right );
                }
            };
        }
    };
}

const times = Infix( ( left, right ) -> left * right );

printLn( 5 |times| 10 ); //=> 50

I propose to borrow an idea from F# by allowing the programmer to truly define their own operator using the tools the language provides, rather than relying on clever hacks to simulate it.

These user-defined operators (UDOs) would have a few simple rules to follow:

  1. UDOs cannot override existing operators (i.e. trying to re-define + to give it new behaviour will not fly)
  2. UDOs will be limited to the tokens that make up any of the pre-existing operators, provided it does not conflict with rule 1
  3. Unary UDOs are prefix-only
  4. The arity of the operator declaration defines whether it is unary or binary, to that end, the declaration must either have an arity of 1 or 2, otherwise it's an error
  5. UDOs cannot have a splat/ellipsis argument

UDOs will have the following syntax (PEG notation. not nailed down, suggestions more than welcome):

userDefinedOperator = "operator" "(" udoToken+ ")" "(" udoArguments ")" functionBody
udoToken            = '<' / '>' / '.' / '/' / '?' / ':' / '~' / '='
                    / '!' / '%' / '^' / '&' / '*' / '-' / '+' / '|'

udoArguments = identifier / ( identifier ',' identifier )
functionBody = lambdaBody / block
lambdaBody   = "->" expression
block        = '{' statement+ '}'

Some trivial examples:

// Simulate range literals

operator( .. )( begin, end ) {
    // return a sequence that counts from begin to end
    // (upper bound inclusive range)

    return ( seq() {
        for( var i = begin; i <= end; ++i )
            yield i;
    } )();
}

// Existing operators are exempt from rule 1 only if the UDO form
// has a different arity than the built-in form. See below.
operator( ... )( begin, end ) {
    // upper bound exclusive range

    return ( seq() {
        for( var i = begin; i < end; ++i )
            yield i;
    } )();
}

// uppercase a string
// This is exempt from rule 1 because the XOR operator is a binary operator,
// while this UDO is a unary operator.
operator( ^ )( str ) -> str.toUpper();

var oneToFive = 1 ..  5;
var oneToFour = 1 ... 5;
var shouting  = ^"hello";

printLn( oneToFive.serialize() ); //=> [ 1, 2, 3, 4, 5 ]
printLn( oneToFour.serialize() ); //=> [ 1, 2, 3, 4 ]
printLn( shouting );              //=> 'HELLO'

I've got a semi-working proof of concept on a local build (that completely defies rules 1-3, but I digress), with only a little modification to the parser (most of it is in ParseExpression()) I was able to get things up and running.

In keeping with the F# spirit, some less trivial examples of UDOs would be their usage in FAKE, which uses UDOs to great effect for build scripts.

Range literal/slice syntax proposal.

Essentially, range literals would be more or less syntax sugar on generators or list comprehensions. Where var x = 1 .. 5; is functionally equivalent to var x = irange( 1, 5 );, and var x = 1 ... 5; is equivalent to var x = xrange( 1, 5 );.

The double-dot generates the values 1, 2, 3, 4, and 5, and the ellipsis generates 1, 2, 3, and 4.

//Upper-bound inclusive range.
seq irange( x, y ) {
    var min = Math.min( x, y );
    var max = Math.max( x, y );

    for( var i = min; i <= min; ++i )
        yield i;
}

//Upper-bound exclusive range.
seq xrange( x, y ) {
    var min = Math.min( x, y );
    var max = Math.max( x, y );

    for( var i = min; i < min; ++i )
        yield i;
}

This could also be extended to arrays (and possibly strings) by way of a slice syntax in the form of:

var slice = array[1 .. 5]; // [ 1, 2, 3, 4, 5 ]

and

var slice2 = array[ 1 ... 5 ]; // [ 1, 2, 3, 4 ]

where

var array = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];

Ruby allows chars to be used in range literals (i.e. alphabet = 'A' .. 'Z'), although for the sake of simplicity I think they should be restricted to numbers only, and instead they can be combined with comprehensions for extended functionality (var alphabet = [ chr( x + 65 ) : x in 0 ... 26).

How to add list / array to mondState?

Hi,

is there any possibility to add a list or an array to MondState?

var mondState = new MondState();
mondState["global.strValue"] = "my string value";
mondState["global.arrayValue"] = new List<string>();

Line 2 works fine, but in line 3 I get a compiler error Cannot implicitly convert type 'System.Collections.Generic.List<string>' to 'Mond.MondValue'.
I could not find any information about how to convert List<string> to Mond.MondValue in the documentation.

Any help is appreciated. :-)

Language Inconsistencies

Currently I see two pretty major inconsistencies in Mond, one having to do with functions inside objects, and the other having to do with list comprehensions.

Generally in Mond, when you create a function or a sequence using the fun/seq keywords a declaration is made. This means that code like this is valid and works:

var x = banana(fun bap() { return true; }, y, z);
bap();

This however, is not the case with object literals. This code is invalid, as no declaration is made for bap:

var x = { fun bap() { return true; } };
bap();

List comprehensions are inconsistent both in syntax and semantics. They're inconsistent syntactically because generators inside list comprehensions syntactically look like x in list compared to var x in list in foreach loops. They're inconsistent semantically because the 'core' expression of a list comprehension can refer to declarations before they physically appear in the source file, and inside list comprehensions is the only time in Mond where you can do that. Example:

[x : x in list]

I suggest the follow changes be made to Mond:

  • Object literals should only allow key-value syntax.
    • Intead do: add: (a, b) -> { ... } or add: fun (a, b) { ... }.
  • List comprehensions should be removed all together from the language. While I do think it's a cool feature (I was even the one to suggest it in the first place), I don't think it has much use.

Support expressions in remote debugger

The remote debugger only allows local variables to be used as watch expressions. Supporting any expression would be best but it should at least support value navigation (x.y, x[y]).

OnBreak should also immediately return while refreshing watch values because accessing it can trigger code to run.

Compiling watch expressions as Mond scripts would be best but local variables need to be mapped correctly. Maybe this can be done in MondDebugContext so we can rewrite the expression tree?

Http

Need this to make MondNet

Add sort to array prototype

Since MondValue implements IComparable<MondValue> we can do this easily. Probably no parameters, sort and sortDescending?

[Binding] Option to disable type checks

The type checks done on function bindings don't play nice with implicit casts:

const two = {
    __number: this -> 2,
};

return Math.sin(two);

Math.sin: argument 1 must be of type number

There should be an option to disable type checks for the class/module/function/parameter, or they should be skipped for objects.

It's probably best to skip checks for objects and just assume they will have a cast, but I'm not sure yet.

Adding __gte, __lt, and __lte metamethods.

Having __gt (and __eq) also do the work of >=, <, <= potentially makes using metamethods more confusing and restrictive for little gain. The main problem is that it makes it impossible to target specifically <, <=, and >= for different functionality from >, or disallowing the use of > entirely, meaning that overloading the comparison operators for completely different functionality (similarly to how C++ overloads the shift operators for streams) is of little usefulness when < and > end up calling the same function.

Admittedly this only really become a problem when working on mapping .NET operator overloads to metamethods for SirTony/Mond.BindingEx#3 because the CLR allows overloading all four separately.

Remove enableThis

enableThis is stupid magic that is confusing and doesn't even work well (breaks metamethods).

Standard Library

Mond currently doesn't really have any kind of a standard library aside from the prototype functions. The functions and modules defined in the REPL should be moved into the Mond assembly.

Th standard library should ideally:

  • be modular. You may not want certain functions to be available in some states.
  • be customizable. Functions like require should support custom loaders instead of just using File.ReadAllText.
  • work well with multiple states. The above points shouldn't be application wide.

Closures broken when created in loops in sequences

When using a function like this:

seq ints() {
    var i = 0;
    while (true) {
        var ii = i++;
        yield () -> ii;
    }
}

You will currently get this error:

mondbox(line 5): 'yield' cannot be used in functions

This is due to the new LoopContext class but that error hides a trickier issue. The fix used in 749a117 added a new leave instruction intended to be paired with enter. These create a new frame for locals when functions are created in loops, but this wont work in sequences because they can re-enter in the middle of the function.

The generated assembly of the loop body would look something like this:

enter 1
 ...
ret               // yield
resumeLocation:
 ...
leave

This looks fine but ret discards all of the frames created by enter in the current function, meaning when we resume it will be missing one.

My current idea to fix this involves adding two new instructions to save/restore the current locals value in another frame. This could improve sequence performance slightly and greatly simplify the compiler code.

[Binding] Support function overloading

The binding API currently errors when trying to bind more than one function with the same name. The binding API should be updated to handle this (and default parameters) automatically.

Simplify the public interface for running scripts

When MondLibraryManager was added the code required to run scripts became a lot more complex. Something should be done so you don't need to use MondLibraryManager unless you don't want the default libraries.

Remove right side dispatch from relational metamethods

Relational operators can currently dispatch on the right hand side when it is an object. This behavior is inconsistent with #47 but I'm not really sure if it's the best decision.

@SirTony What are your thoughts on this? #63 copies the RHS dispatch from __gt but doesn't implement it properly, but I feel like it just shouldn't be supported.

Inconsistency with declarations inside loop headers

while ((fun banana() -> true)()) { }
banana();

This is valid, but doing foreach(var x in xs) {} won't give you access to x after the loop. Should be fixed by making the declaration of banana part of the loop block.

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.