Giter Site home page Giter Site logo

mlogjs / mlogjs Goto Github PK

View Code? Open in Web Editor NEW
63.0 63.0 7.0 4.15 MB

Compile javascript into Mindustry logic code (mlog)

Home Page: https://mlogjs.github.io/mlogjs/

License: MIT License

JavaScript 11.66% TypeScript 80.95% CSS 1.16% Vue 6.22%
compiler javascript-compiler mindustry mindustry-logic mlog typescript-compiler

mlogjs's Introduction

mlogjs

JavaScript MLog Compiler

ci npm docs unreleased docs chat

Features

  • Optimizing compiler
  • Easy to use
  • Fully Typed API
  • Macros
  • All Instructions and Variables
  • An online editor integrated to the docs

Installation

In the terminal

npm i -g mlogjs

Usage

Creating hello.js

print("Hello world");
printFlush();

Compile

mlogjs hello.js

Examples

You can find other usage examples here.

Changelog

Changes for each release are documented in the CHANGELOG.

Contribute

Having trouble? Ask for help on discord or open an issue

Help and suggestions are welcomed!

License

MIT

mlogjs's People

Contributors

jeanjpnm avatar lideming avatar qzlin avatar weisrc 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

Watchers

 avatar  avatar  avatar  avatar  avatar

mlogjs's Issues

Use a single subrepo to manage the docs

Putting this here for future plans.

The goal is to have a better integration with tools available to us, and also have an opportunity to:

  • Use another web framework
  • Use a better design for the whole website
  • Add more features to the documentation (like type links, and a playground)
  • Add more features to the editor
  • Dynamic deploys
  • Pull request previews

Editor lacks nice features

The editor works fine, but it doesn't have some quality of life features like:

  • Dark mode
  • Sourcemapping (select output text to see the equivalent part on the source code and vice-versa)
  • Toggles to change the compiler settings
  • Decent support for mobile devices

Incorrect `switch` statement optimizations

The switch statement does not optimize it's cases correctly:

const a = 1 as number

switch (a) {
    case 0:
        print("a")
        break
    case 1:
        print("b")
    case 3:
        print("c")
        break
}

switch (a) {
    case 0:
        print("a")
    case 1:
        print("b")
        break
    case 3:
        print("c")
}

Generates:

print "b"
print "b"
jump null always
end

This show that the jump adress is never resolved and the break statements (or their absence) are not taken into account during optimization.

Add support for labels

Labels give users more options to model the control flow of the program, potentially being more lightweight than if statements and specially function calls.

[bug] Switch statements incorrectly optimize cases with constants

In the following example, we see that a is a constant declared with a random value, which may be 0, 1 or 2.

The switch statement is expected to preserve case a:, but it doesn't.

const a = Math.floor(Math.rand(3))

const b = 2

switch (b) {
    case a:
        print("is a")
        break
    case 2:
        print("is b")
}

Output:

op rand a:1:6 10
print "is b"
end

Declaring a with let generates the correct output:

Expected output:

op rand a:1:4 10
jump 3 strictEqual 2 a:1:4
jump 5 always
print "is a"
jump 6 always
print "is b"
end

Version: latest (cd75edd)

Partial recursion support

Although proper recursion is not possible in mlog, it is possible to implement a tail-call optimization, and give a warning or an error when users try to use regular recursion.

print function in string template bug

version: 0.4.5

function switch_test(value: number) {
    let whatEver = 'whatEver';
    switch (value) {
        case 0:
            whatEver =  'case0';
            break;
        case 1:
            whatEver =  'case1';
            break;
        case 2:
            whatEver =  'case2';
            break;
    }
    return whatEver;
}

print('case0: ',switch_test(0),' case1: ',switch_test(1),' case2: ',switch_test(2))
printFlush();

compile

set value:1:21 0
set &rswitch_test:1:0 3
jump 17 always
set value:1:21 1
set &rswitch_test:1:0 6
jump 17 always
set value:1:21 2
set &rswitch_test:1:0 9
jump 17 always
print "case0: "
print &fswitch_test:1:0
print " case1: "
print &fswitch_test:1:0
print " case2: "
print &fswitch_test:1:0
printflush message1
end
set whatEver:2:8 "whatEver"
jump 22 strictEqual value:1:21 0
jump 24 strictEqual value:1:21 1
jump 26 strictEqual value:1:21 2
jump 27 always
set whatEver:2:8 "case0"
jump 27 always
set whatEver:2:8 "case1"
jump 27 always
set whatEver:2:8 "case2"
set &fswitch_test:1:0 whatEver:2:8
set @counter &rswitch_test:1:0

result:
image

expected: case0:case0 cacse1:case1 case2:case2

function switch body jump bug

version: 0.4.4
When a function call another function have switch body have more then one case to return,and there is no final return statement.It will jump between two functions infinity when no matched value.

function switchTest(value) {
    switch (value) {
        case 'test':
            return ;

        case 'test1':
            return ;

    }
}
function anotherFunction() {
    let value = 'notTest';
    switchTest(value);
}

anotherFunction();
print('done');
printFlush();

compiled:

set &ranotherFunction:11:0 2
jump 10 always
print "done"
printflush message1
end
jump 8 strictEqual value:1:20 "test"
jump 9 strictEqual value:1:20 "test1"
jump 10 always
set @counter &rswitchTest:1:0
set @counter &rswitchTest:1:0
set value:12:8 "notTest"
set value:1:20 value:12:8
set &rswitchTest:1:0 14
jump 5 always
set @counter &ranotherFunction:11:0

Support object destructuring on function arguments.

Allow users to declare functions that can destructure objects/arrays to help increase readability.

Limitations:

  • As with other cases of destructuring, the rest operator (...) won't be supported.

These two examples should yield the same instructions:

function doSomething(x, y) {
    print`
current x: ${x}
current y: ${y}
    `
}


doSomething(Vars.unit.x, Vars.unit.y)
function doSomething({ x, y }) {
    print`
current x: ${x}
current y: ${y}
    `
}

doSomething(Vars.unit)

for loop break incorrectly when condition is constant

Problem

If condition of for loop is a constant, the jump command from break statement will point to null
Example:

for (let i = 0; true; i++) {
    if (i > 10)
        break
}
print("test")

Output:

set i:1:9 0
jump null greaterThan i:1:9 10
op add i:1:9 i:1:9 1
jump 1 always
print "test"

Expected Behavior

INFO

  • Version: current_latest (Apr 9th, 8862b79)

[bug] Empty if statements don't compile correctly

most people are not gonna use the if statement like this but...
basically:

let num1 = 100
let num2 = 150

if (num1 < 150) { }
else {
    printFlush()
}

// expected output is to be nothing on message1

output:

set num1:1:4 100
set num2:2:4 150
op greaterThan &t0 num1:1:4 num2:2:4
jump 5 always
print "num1 is less or equal than num2"
printflush message1
end

i think the jump instruction shouldn't be "always"
but instead a not equal

[bug] The operation cache does not propagate correctly on `if` `else` braches

In the following example we can see that the assignment to a in the if branch prevents the else branch from reusing the cached operation a + 1.

let a = Math.rand(10)

print(a + 1)

if (a > 5) {
  a = 2
} else {
  print(a + 1)
}

printFlush()

Output:

op rand &t0 10
op floor a:1:4 &t0
op add &t1 a:1:4 1
print &t1
jump 7 lessThanEq a:1:4 5
set a:1:4 5
jump 9 always
op add &t2 a:1:4 1
print &t2
printflush message1

Version: latest (cd75edd)

[Feature Request] A nodejs/web api

First off, Killer work on this project!

I'd like to request a nodejs/web api for this project. My use case for this would be a website I can visit locally on my phone (while playing mobile version) with an editor, that can compile code for me to copy right from the website and back into mindustry.

If your ok with it, I can give a whack at a pull request for this feature, and maybe even provide you with the editor (once finished) that you could bundle into this project if you'd like

Escaped backslashes are treated differently

Instead of print("\\"); becoming print "\", it becomes print "\\" causing two backslash characters to be printed onto the message.

For Example:

print("\\");
printFlush();

will compile as:

print "\\"
printflush message1

Which when put into a processor (with a message) outputs this:
2023-05-31 (2)

Add an instruction cache

As the 3dcube example shows, there are many scenarios where duplicated operations are performed, even if they can be cached.

So to solve that performance issue I propose adding an instruction cache for "side effect free" operations (which may include function calls).

Every time a variable is assign it would clear the cache for ALL scopes that can reference said variable.

Use an intermediate representation

This is more of a suggestion than an actual request, using an intermediate representation that doesn't have as many edge cases can make optimizations more precise.

Jump compression

Most of the generated jump instructions can be compressed to reduce the output size.

As an example the following condition could be compacted:

read &t0 cell1 0
op equal &t1 &t0 0
jump 5 equal &t1 0
op rand &t2 10000000000
write &t2 cell1 0

to this:

read &t0 cell1 0
jump 5 notEqual &t0 0
op rand &t2 10000000000
write &t2 cell1 0

An option to disable this behavior should be added too.

Function inlining does not work with guard clauses

I noticed that function inlining doesn't work properly with code like this:

function parseItem(n) {
  if(n === 1) return Items.copper
  if(n === 1) return Items.lead
  return null;
}

print(parseItem(1))

Produces:

set &t0:3:0 @copper
set &t0:3:0 @lead
set &t0:3:0 null
print &t0:3:0
end

While the expected output was:

set &t0:3:0 @copper
print &t0:3:0
end

The example source has repeated if comparisons to show that all of the return statements got inlined, which is
not expected and has extremely high potential to cause silent issues.

The return value of functions is not consistent with empty return statements.

The variable that stores the return value of a function is not set to null on empty or implicit returns.

Sample code:

let val1 = 6;
let val2 = 1;

let a = fun(val1);
let b = fun(val2);

print`
a: ${a}
b: ${b}`;
printFlush();

function fun(value) {
  value * 2; // is here to prevent inlining
  if (value > 5) return "text";
  // also happens if you uncomment the next line
  // return
}

Expected logs:

a: text
b: null

Actual logs:

a: text
b: text

This shows that the temporary variable should be set to null on the cases mentioned above.

Support for the `??` operator

The null coalescing operator (??) offers a consice way to lazily get the value of an expression if the left operand is null.

Right now the best way to do it is:

const result = value == null ? lazyDefault() : value

Which could be reduced to:

const result = value ?? lazyDefault()

Standardize changesets and release methods

Even though the compiler hasn't reached version 1.0 it would be pretty nice to have an automatically generated changeset which people could look into to see what exactly changes between each version, it could also make finding the source of an error easier in the future.

My suggestion is using the changesets package and a bot to deal with this.

The second point is that the compiler and the editor releases are out of sync, people using the compiler always get the most recent
features and bug fixes after people who use the web editor. But people using the web editor are also more vulnerable to bugs that come
from the rapid changes that occur inside the main branch.

To fix that both compiler and editor have to get their "unstable" releases at the same time, while still preserving the stable ones.

The compiler could have a x.y.z-next.a version and the unstable deploy of the editor could be under a different url.

Cached operations propagate incorrectly in switch statements

Cached operations propagate incorreclty across switch case bodies. In the following example, it is visible in the output that
the results from the operations in the first case are being reused in the second, even though they will never be executed.

const x = Math.rand(10);
const y = Math.rand(2) > 1 ? 1 : 2;
switch (y) {
  case 1:
    print`${x + y} > ${x - y}`;
    break;
  case 2:
    print`${x - y} < ${x + y}`;
    break;
}

printFlush();

Output:

op rand x:1:6 10
op rand &t0 2
jump 5 lessThanEq &t0 1
set y:2:6 1
jump 6 always
set y:2:6 2
jump 9 strictEqual y:2:6 1
jump 15 strictEqual y:2:6 2
jump 18 always
op add &t1 x:1:6 y:2:6
op sub &t2 x:1:6 y:2:6
print &t1
print " > "
print &t2
jump 18 always
print &t2
print " < "
print &t1
printflush message1

Variable names inside regular scopes are broken.

When not using the --compact-names flag the compiler lets variables be overriden by other variables with the same name under a child scope:

let foo = 10;
{
  let foo = 1;
  print(foo);
}
print(foo);

outputs

set foo 10
set foo 1
print foo
print foo
end

`Vars.unit` should not be treated as a constant

Treating Vars.unit as a constant is not ideal since its value can change via unitBind.

Here is an example of the issue:

unitBind(Units.poly);
const first = Vars.unit;
unitBind(Units.mega);
const second = Vars.unit;
print`first is a ${first}`;
print`second is a ${second}`;
printFlush();

Compiles to:

ubind @poly
ubind @mega
print "first is a "
print @unit
print "\n"
print "second is a "
print @unit
print "\n"
printflush message1

The expected output for the script is:

first is a poly
second is a mega

But the output at the current version is:

first is a mega
second is a mega

As you can see, treating @unit as a constant leads to unintended behavior.

Version: latest (d8c4266)

[bug] Cached operations propagate incorrectly in ternary expressions

The following example shows that the operation a + 1 is reused on the alternate branch, even though it
can only be executed in the consequent branch.

const a = Math.rand(10)
const b = a > 5 ? (a + 1) * 2 : (a + 1) * 3

Output:

op rand a:1:6 10
jump 5 lessThanEq a:1:6 5
op add &t0 a:1:6 1
op mul b:2:6 &t0 2
jump 6 always
op mul b:2:6 &t0 3
end

Version: latest (a9676c1)
Similar to #144.

Inconsistent variable names on instructions with multiple return values

Usually assigned variables have their positions in the source file as names. However, that is not the case for variables that are outputted by commands with multiple return values like unitLocate and unitControl (this one in a specific function overload).

Demonstration

Passing the following input to the compiler results in the following output:

const [found, x, y, building] = unitLocate("building", "core", true);

print(found, x, y, building);
ulocate building core 1 @copper t1 t2 t0 t3
print t0
print t1
print t2
print t3
end

While the expected output is:

ulocate building core 1 @copper 1:14 1:17 1:7 1:20
print 1:7
print 1:14
print 1:17
print 1:20
end

I'm sure this has to do with how the macros for those instructions return ObjectValues.
One possible solution would be to add a sort "hook" that gets called when an IValue gets put into a scope with a name. That way certain types of values can rename their internal name to fit that of the scope variable holding them.

for loop continue never increase index

for loop continue statement have bug,it will never increase index and turn into infinite loop.

for(let i=0;i<10;i++){
    continue;
}
print('done');
printFlush();

compiler output:

set i:1:8 0
op lessThan &t0 i:1:8 10
jump 6 equal &t0 0
jump 1 always
op add i:1:8 i:1:8 1
jump 1 always
print "done"
printflush message1
end

mlogjs version: 0.4.2

Testable examples

Add real life uses of mlogjs that can be used to test more complex interactions and also easily validated on the game.

Support function hoisting

It is a common pattern to write the main code and define the functions bellow to make the code easier to read. But since the compiler does not hoist function declarations with pattern cannot be applied.

Default parameters for "flush" commands

Commands like printflush and drawflush currently require users to pass a destination building, even though most of the
time it's gonna be message1 and display1, respectively.

While this makes writing code more convenient I am not sure about how it might impact future changes on the compiler API

Jump with no address in nested function calls

The following code has an incorrect output:

print(b(5))

function b(x) {
    return x * c(x)
}

function c(x) {
    return x * 2
}

Compiler output:

set &t1:c:7:0 10
jump null always
print &t0:b:3:0
end

The bug seems to have been introduced in the version 0.4.0

Docs?

Heya! I stumbled across this and thought it'd be nice to check out, but there's no docs so I can't use it... Are you able to make/show me the docs for it?

Add support for modules

Because fery few other compilers did that. Also, this requires changes on the sourcemapping format and the name handling and resolution.

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.