MiniC N.Edition (JS-like Scripting Language)

Monkey Programming Forums/User Modules/MiniC N.Edition (JS-like Scripting Language)

Goodlookinguy(Posted 2014) [#1]
Original MiniC Topic: http://www.monkeycoder.co.nz/Community/posts.php?topic=3616&page=1

2016 Documentation: http://www.nrgs.org/dev/miniccbdocs/2016/
Download: https://bitbucket.org/Goodlookinguy/minic-n.edition/downloads
(Download the default branch, any other branch is prone to more crashes and bugs)

If you don't know what MiniC is, read the above topic. This is MiniC N.Edition. I decided that instead of more-or-less spamming on the original MiniC topic, I'd make my own. Since I've turned the language into something different than was probably envisioned by the original creator.

MiniC N.Edition is my overhauled (internally), fixed, and more robust version MiniC (if Mini makes sense anymore).

Note: Most original MiniC code will run in this version. Although because of several bugs in the original MiniC and the changes I've made so that MiniC variable altering is done like every other programming language, that is, it doesn't use references by default anymore, your original code may not work as intended. Although maybe now it will work as intended *wink wink*.

Fixes (from the original MiniC only)

* Fixed variable resolution issues
* Fixed negative numbers
* Fixed order of operations ( look down a bit on docs -> http://www.nrgs.org/dev/miniccbdocs/lang.html )
* Fixed early returns ( no longer continues to run code if function returns )
* Fixed empty functions crash
* Fixed statement list issues
* Fixed parameter name collision issues
Changes

* Added bitwise operators AND, OR, XOR, and NOT ( &, |, ^, ~ )
* Added string multiplication which repeats text (similar to python)
* Added object operator overloading ( documentation on how to do this will come later, although 1 example does exist )
* Added scope operator overloading ( there's an example on page 2 )
* Added better number reading (scientific notation) ( e.g. val = 3.45e-2 )
* Added boolean AND, OR, and NOT ( &&, ||, ! )
* Added modulo and integer modulo ( %, ~% )
* Added integer division ( ~/ )
* Added ternary if block ( ?: )
* Added bitshifting ( >>, <<, >>> )
* Added pre/post increment/decrement ( ++, -- )
* Added reference variables ( e.g. i = &b )
* Added identical operators ( ===, !== )
* Added arrays and arrays of arrays [of arrays [of arrays [...]]] ( [] )
* Added special variable "arguments" inside every function call that contains all arguments against that function
* Added all augmented assignment operators
* Added for loop (experimental as of 1/29/2014)
* Added foreach loop (experimental as of 1/29/2014)
* Added delete keyword ( delete var [, ...] )
* Added break/continue keywords ( for loops )
* Added typeof function for returning types ( e.g. typeof(3.5) == 'number' )
* Added 'as' keyword for type cast primary types (e.g. 3.5 as 'string' )
* Added 'has' keyword for checking if a scope contains a property (e.g. {var val=0} has 'val' )
* Added 'in' keyword for 'foreach' loops (e.g. foreach( item in {var a=0,b=2,c=4} ) print(item); )
* Added object notation for scopes ( . ) ( This is final (unless some other problems popup) )
* Added lazy in scope variable assignment ( e.g. someScope.newVar = 3 )
* Added var keyword to define in-scope variable ( does not overwrite parent scope )
* Added object closures ( same as func returning this after running that func )
* Added internal object interfacing between Monkey and MiniC
* Added call stack tracer when compiled in debug mode ( sort-of experimental as of 1/29/2014 )
* Added null type ( e.g. my_var = null )
* Added yield (partially working 10/7/2015, does not yield from foreach calls yet)
* Added contextless evaluation of statements into the VMConsole
* Removed need for braces in if/else blocks
* Removed need for braces in while blocks
* Assignments can now be done anywhere! (e.g. while( c = get_char() ) print(c); )
* The dollar sign is a valid identifier character like in JS ( $ )
* Increased Scanner, Parser, and VM speed (esp. noticeable for mobile)
Current Notes (1/30/2014)
* Working on changing how 'this' operator works
* Binary and hex reading is coming

You can get it here: https://bitbucket.org/Goodlookinguy/minic-n.edition

I'm extremely open to useful changes if anyone wants to add any. I'm still currently fixing, updating, and upgrading the code. By the end of these changes, there will be a number of extra changes, like hexadecimal, binary, and scientific notation for numbers. As well as explicit datatype conversion in the form of "(expr) as (type)".

FAQs
I need functions...
My MiniC Common Bindings library will turn MiniC N.Edition into an extremely robust tool. It even has eval and include functions now. The documentation is slowly being made as it stands, but here's what's available - http://www.nrgs.org/dev/miniccbdocs/

How to do integer modulo and division?
~/ for division. ~% for modulo. These operators are based on Google's Dart language.

Something is not executing as expected...
Post it. I'll see if it's MiniC or you.

What kind of language is MiniC N. Edition?
MiniC N. Edition is a multi-paradigm programming language (imperative, functional, etc.). It takes many good things from many languages and slaps it into one. It's sort of a jack-of-all trades language that allows you to program the way you want to.

Any future plans? (Dec 2015)
Yes, although probably not until 2016. Number one thing is to change how extended classes work to allow for overriding of primary types and such. Next, I want to add in immutable variables. (static ee = func() { return 3 };). After that I want to continue work on my AST-to-ASM conversion so that you can pre-compile scripts for games that will run and compile faster than the AST-VM. It also means I have to finish my Bytecode-VM.

What does the 'N' really stand for in 'N.Edition'
Nicholas. It's my name.

Is there a realistic example of how this could be used?
Oh, most definitely. Take a look at my console example in my MiniC Common Bindings library.

Why do you bump the thread so much?
Because I have ADD. If I don't constantly bump this thread and get reactions my motivation will die off faster than you can blink an eye. I'm not kidding. This is not a joke or meant to get a laugh. My ADD is a really serious problem in my life and one of the ways I've overcome it is to constantly engage in whatever I'm doing.

Example Code
This piece of code here shows a very JavaScript-esque language design. This is of course an optional style as the language is multi-paradigm.
format_code('var e = (func(){return 6;})();
print(e);

if (e == 6 && (func()
{
return true;
})())
{
print("Hello");
}')


Sammy(Posted 2014) [#2]
Looks like a great number of fixes and additions. I'm looking forward to having a closer look when I get home tomorrow. Well done!


Goodlookinguy(Posted 2014) [#3]
Edit: I finished refactoring the stringly typed code. It's less error prone now. As a bonus, it's now significantly faster on mobile devices and mildly faster on computers, although it probably isn't noticeable on modern computers.

Best Edit: So, scope access is coming! The original creator of MiniC wanted this and I have half solved it. I think I'll have it ready within the next 3 hours (an all nighter for me).

Be prepared for even more drastic changes. I've been working over the past day to speed MiniC up greatly. Right now it's stringly typed, which I'm not a huge fan of. So all of that is being switched to integer constants to speed up the Scanner, Parser, and VM. The speed-up should be more noticeable on mobile devices than anywhere else.

To put in perspective how big what I'm doing is right now, it's basically like I'm rebuilding MiniC from the ground up.


Goodlookinguy(Posted 2014) [#4]
Well, for those interested, I just made some awesome changes. Scope support was finally added. Along with that the keyword delete, which allows you to delete variables in that scope (don't try outside it...yet).

Exampleformat_codebox('test = func()
{
inner = func()
{
name = 'Hello';
return this;
};

value = inner();
delete inner; // removes inner as a function declaration wasting memory space
return this;
};

tester = test();
print(tester.value.name);
print((test().value).name); // test().value.name will crash it...because I haven't gotten around to resolving it
tester.value.name = "It Works";
print(tester.value.name);')Example 2format_codebox('vector2d = func()
{
x = 0.0;
y = 0.0;
return this;
};

set_to_zero = func( vector )
{
vector.x = 0.0;
vector.y = 0.0;
return vector;
};

test = vector2d();
test.x = 10.0;
test.y = 11.0;
print(test.x);
print(test.y);
set_to_zero(test);
print(test.x);
print(test.y);')


Sammy(Posted 2014) [#5]
Quite impressive. I was surprised by some of the language features(operator overloading!) that are available in this, well done(and Erik) Nicholas!


slenkar(Posted 2014) [#6]
EY this is really cool, checkin it out


Goodlookinguy(Posted 2014) [#7]
Thanks for the support.

I just added break/continue. Here's the test I had for it...
format_code('i = 0;
while ( c = func(){ return i++; } ) // <-- I just did this to make sure it would work
{
out = c();
if ( out == 3 ) continue;
if ( out > 10 ) break;
print(out + "Hello World");
}')
The break keyword works the same as "return 0" in while loops. I really should do something about disallowing that kind of code, but meh. I don't know if I want to go to that extent to block something most people won't do.

Hey, for anyone wanting to help, I am looking for help dealing with closure scope handling. I've been actively solving it, but it's extremely difficult to get 100% correct. I have it working to an extent, but not the way it fully should work. I figured out what was causing the bug, but am unable to solve it. It may actually be unsolvable. So anyone still wanting to help solve it, please read through this (well, only really the last few comments). Also, my brain hurts. This much cyclic thinking is dangerous. Basically solved with the new 'var' keyword.


Goodlookinguy(Posted 2014) [#8]
Big Update! Big because it's really quite neat. You can now interface internal monkey objects with MiniC. You can see it here in my overloading/interfacing example.

The only downside to the implementation is that you can't set variables with the equal operator. It has to be set like it's a function.
format_code('vector.x(20); // sets x to 20
vector.x; // returns value of x (which is 20)')The reason it has to be like this is because of the way MiniC is set up. It's nearly impossible for me to change this at the moment.


therevills(Posted 2014) [#9]
Very cool stuff :)


Goodlookinguy(Posted 2014) [#10]
I woke up this morning with a realization of how to allow the equal sign (and augmented assignment ops). It requires minutely more overhead, but overall is not noticeable. I updated my example with the new code. Oh, I also renamed the interface and it's method names. I was hoping no one had used this functionality yet, so...


Shinkiro1(Posted 2014) [#11]
Great additions!

How strict is MiniC about errors at runtime?
Let's say you access a scope which has not been defined, will it crash?
If so can you catch an exception.

Ps: I feel that it should be pretty liberal in such cases and probably log the error/stop execution (much in the way like javascript)


Goodlookinguy(Posted 2014) [#12]
MiniC is hit and miss about errors. I had planned to change this but I found that getting everything I needed into the system was more important. When I come around to adding in the easier debugging, it will give you the line number and character where the crash happened. I might also make some sort of call stack tracer like Monkey has.

Also, to answer your question directly:
Let's say you access a scope which has not been defined, will it crash?
Yes.

Unrelated Edit: Hey, I just discovered that SubilmeText + Set Syntax: C is working fairly well for MiniC. For the most part I don't see any big issues and as a bonus I get intellisense.


Goodlookinguy(Posted 2014) [#13]
I just added a new function 'append' to my MiniC Common Bindings library. It's useful for appending data to scopes. It's like the 'prototype' keyword in JavaScript.format_code('Test = func()
{
var onlyVar = 100;
return this;
};

example = Test();
print(example.onlyVar);
append(example, 'twoVars', 2000);
print(example.twoVars);')In other notes: I also fixed some small bugs with regard to pre/post increment/decrement in object interfacing as well as fixing how the 'Register' method works, that is, where it places the variables.


Goodlookinguy(Posted 2014) [#14]
Edit: I've started working on a stack tracer. I've been able to test it a bit and it shows promise for detecting errors and helping deal with them.

I just made major changes to the parser. Everyone using this is encouraged to make sure their code worked exactly the same as it did before and that it's not giving parser errors. The parser errors were somewhat rare but at the same time very possible.

Aside from that little problem, I just added immediate object scopes. It's like running a function that was purely made for data, except you get to skip a step...

I copied this sort of style from Lua/JS because I think it's extremely handy and adds more flexibility.format_code('data = { var example = 'Hello' };

print(data.example);

ITakeScopes = func( scope )
{
print(scope.one);
print(scope.two);
};

ITakeScopes({var one = 'Hello there', var two = 'Number 2'});')In my Common Bindings library, I recently added a 'contains' function which when used in conjunction with this will allow you to check if this variable exists in the scope.format_code('ITakeScopes = func( scope )
{
if ( contains(scope, 'one') )
print(scope.one);
if ( contains(scope, 'two') )
print(scope.two);
};

ITakeScopes({var one = 'Hello there', var two = 'Number 2'});')


Skn3(Posted 2014) [#15]
Hey GLG,

this is all looking great and tons of progress. What's your end goal? What are you making it for?

A suggestion for the append() type functions, what if you did it little like javascript and allowed the lazy approach?

e.g.

myObject.myNewVar = 123 ?


Goodlookinguy(Posted 2014) [#16]
What's your end goal?
That's a fantastic question that I have no answer for. I'm one of those people that sort-of just keeps going like the energizer bunny until I feel satisfied.
What are you making it for?
I'm making it for an RPG I've been making for a while now.
A suggestion for the append() type functions, what if you did it little like javascript and allowed the lazy approach?
e.g.
myObject.myNewVar = 123 ?
I was actually considering it, but then I thought that it might make debugging harder. However, maybe not now since I implemented the tracer. I'll rethink that decision.


Goodlookinguy(Posted 2014) [#17]
I just finished adding null support. If anyone runs into any issues with null, please tell me.


Shinkiro1(Posted 2014) [#18]
About error handling again:

The thing about crashing the program when an error occurs is that you will have to rerun/recompile the whole program.
If it just stopped executing the script and tell you someway that there is something going on you can just fix the error in the script and reload it.
All of this can be done without any recompiling/rerunning the entire app.

The only reason for using a scripting language in the first place is that you want to avoid rerunning/recompiling your app.
Well that's just my opinion anyway ^^


Goodlookinguy(Posted 2014) [#19]
Can be done...I just need some sleep. I've been up for too many hours and my brain is going fuzzy. I'll attempt to do something about soft error crashing tomorrow.


Halfdan(Posted 2014) [#20]
Thanks, this is great

I'm going to check out the monkey object interfacing, sounds cool


Shinkiro1(Posted 2014) [#21]
I hope I didn't sound too negative.
I really appreciate what you are doing here, just wanted to let you know what I think would be important.

You could handle soft errors with a preprocessor:
#MINIC_USE_SOFTERRORS = False

And then have something like this internally:
format_code('
Function MinicError(line:Int, error:String)
#If MINIC_USE_SOFTERRORS
ErrorStack.Push("Error on line " + line + ": " + error)
#Else
Error("MiniC Error on line " + line + ": " + error)
#End
End
')


Goodlookinguy(Posted 2014) [#22]
I broke major compatibility with all object interfacing and operator overloading. I don't think I'll have to do a change like this again. This was really a one time thing.

I also just added the lazy assignment of variables to scopes. e.g.format_code('myObject = {};
myObject.myNewVar = 123;
print(myObject.myNewVar);')

Edit: I accidentally forgot to push that commit 9 minutes ago when I posted this. It's there now.

I hope I didn't sound too negative.
I really appreciate what you are doing here, just wanted to let you know what I think would be important.
Nah, I didn't take it as negative. I was just really tired. Actually, I'm still really tired because I went to sleep at 8AM (I have DSPD!) and some idiot with a jackhammer woke me up and has been going at it for close to 6 hours now.

You could handle soft errors with a preprocessor:
#MINIC_USE_SOFTERRORS = False
I'm just going to use debug builds. That's what they're there for.

And as far as the soft errors go, it's actually much harder than you think. The VM is an abstract syntax tree and because of that I can't just stop on a dime easily like I could with a bytecode interpreter. So I have to really think carefully about what I do in order to make it work as expected.


Goodlookinguy(Posted 2014) [#23]
I always take Friday/Sat off and I came back to find that the latest commit is running the code incorrectly. Please use the one approved off on BitBucket until I resolve what broke the code. The sad part is that I didn't notice and I had implemented a soft error catcher which appeared to be working...although now I'm thinking that it might just be because something else was broken.

Edit: Okay, I found what's causing it and I'm attempting to fix it. It doesn't appear to be anything big, just big enough to kill tons of code.

Edit: Fixed! It was unbelievably easy to fix but until I took a shower I couldn't solve it. I was in the shower and suddenly it hit me.


Goodlookinguy(Posted 2014) [#24]
Okay, I've implemented the soft crashing thingy. It seems to be working...although I don't know 100% yet. If anyone has any issues like a lockup (which I experienced and fixed [...I think]), give me your code (if it's nothing too private) so I can attempt to fix the bug.

Edit: Fixed bug caused by stack. Seriously though, the code looked right (look for yourself). I think Monkey Stacks need to be changed...


therevills(Posted 2014) [#25]
Edit: Fixed bug caused by stack. Seriously though, the code looked right (look for yourself). I think Monkey Stacks need to be changed...

OT: I just quickly coded this and it works fine with an IntStack in MonkeyX76d:
format_code('Strict

Function Main:Int()
Local s := New IntStack()
s.Push(1)
s.Push(2)
s.Push(3)
Print "start = " + s.Length()

While Not s.IsEmpty()
Print s.Pop()
Wend
Print "end"
Return True
End')


Goodlookinguy(Posted 2014) [#26]
Very interesting. I'm not sure what caused it to break or why it continues to break if I change it back.


therevills(Posted 2014) [#27]
That is strange, when you have time it might be worth investigating further... but since you have a work around dont worry about it too much.


Goodlookinguy(Posted 2014) [#28]
Yeah, that's what I was thinking. I'll come back some other time.

In other news: There appears to be a new scope bug. I swear I never get a break from these bugs...I have no idea what causes it either. Definitely has something to do with the ternary if operator.

Edit: I think I may I have probably found it...and if I'm right, that's bad.

Bug...I'm working on it...
format_code('Test = func( data )
{
var group = {};
group.name = true ? data.name : null;
return this;
};

example = Test({var name='abc'});')

Edit 2: I did find it right after posting the broken example. This will take a bit of time to fix...

Edit 3: Fixed! Geez...whew...

Edit 4: Required more fixes...that I finished. Everything in the latest commit should be working 100% correctly.


Goodlookinguy(Posted 2014) [#29]
Okay, the latest commit is very stable. It's running my huge test code with 100% accuracy. So I assume this means I've resolved all of the current bugs.

This also means that null is now a type. Use null if you wish. It will create a place holder variable if you do something like...
format_code('myvalue = null;')The only way to remove variables is to use the delete keyword...e.g.format_code('delete myvalue;')
...speaking of which, I need to make delete work for variables inside scopes...

Edit: I solved the stack issue. It was my fault that I didn't anticipate some really specific code.


Goodlookinguy(Posted 2014) [#30]
I've sort-of added support for the 'this' operator. I'd be cautious using it though. It's not been thoroughly tested although it has been tested.

Here is an exampleformat_codebox('haha = func()
{
var data = {};
data.parent = 'hello';

data.setParent = func( parent )
{
this.parent = parent;
};

return this;
};

test = haha();
test.data.setParent('what');
print(test.data.parent);')Edit: I think I've caught just about every error that could happen or at the very least most of the ones that you'd run into. I've been writing code for my own game and have been figuring out the errors as I went along.
Edit 2: There's a bug in the insta object scopes and assignments. I'll attempt to fix it as soon as possible.


Goodlookinguy(Posted 2014) [#31]
I just fixed the boolean not operator. Not really sure why it was the way it was...so anyways...

I'm planning on making the language quicker at dealing with common actions. If anyone has any ideas, tell me. Cause right now I'm designing the internal array stuff. Then I'll be onto the for loop and some other stuff.

Question
I want to pose a little question here. Would the keywords be better off case insensitive or case sensitive? Right now they're case sensitive.


therevills(Posted 2014) [#32]
I prefer case sensitive and it would be match Monkey.


Shinkiro1(Posted 2014) [#33]
I also prefer case sensitive.


Goodlookinguy(Posted 2014) [#34]
Alright, I'll stick to case sensitive then.

Also, right now I have a LinkedHashMap implementation that I was planning on using for arrays because it seems like a lot of major scripting languages do this. However, I want to hear if this is a good or bad idea. Cause it would allow you to do stuff like...format_code('test = [];
test['Hello'] = 'World';
print(test['Hello']); // prints 'World'')But could also do this...format_code('test = ['Example', 'Data'];
print(test[0]); // prints 'Example'
print(test[1]); // prints 'Data'')And could lead to this...format_code('test = ['Example', 'Data'];
test['Hello'] = 'World';
print(test[2]); // prints 'World'')
So I'm not feeling 100% about it. Personally, I like to keep map, stacks, queues, lists, and more all separate.

Edit: Hash Map and Linked Hash Map Implementation --> https://bitbucket.org/Goodlookinguy/xaddon/src/3799957/basic/cHashMap.monkey
Your brain will explode. The hash map and linked hash map implementation use very new Monkey features that are still considered experimental.


Goodlookinguy(Posted 2014) [#35]
Well, I'm holding off on what I want to do with arrays/maps until after I'm done with the Jam I'm in the middle of. However, I wanted to have some features so I've semi-implemented some new features that allow you to use scopes like objects in that you can overload their types by simply assigning them variables with certain names.

Oh, I also added 'typeof' as a built-in function. More built-in functions are to come. Although I'll try and keep the number of them low.

Example of scope overloading...
format_codebox('Vector = {
var new = func( x, y )
{
return
{
var x = x,
var y = y,
var add$ = Vector.add,
var sub$ = Vector.sub,
var mul$ = Vector.mul,
var div$ = Vector.div,
var idiv$ = Vector.intdiv
};
},

var add = func( a, b )
{
return Vector.new(a.x + b.x, a.y + b.y);
},

var sub = func( a, b )
{
return Vector.new(a.x - b.x, a.y - b.y);
},

var mul = func( a, b )
{
if ( typeof(b) == 'number' )
return Vector.new(a.x * b, a.y * b);
return Vector.new(a.x * b.x, a.y * b.y);
},

var div = func( a, b )
{
if ( typeof(b) == 'number' )
return Vector.new(a.x / b, a.y / b);
return Vector.new(a.x / b.x, a.y / b.y);
},

var intdiv = func( a, b )
{
if ( typeof(b) == 'number' )
return Vector.new(a.x ~/ b, a.y ~/ b);
return Vector.new(a.x ~/ b.x, a.y ~/ b.y);
}
};

test = Vector.new(10, 12);
test2= Vector.new(33, 23);
test3 = test + test2;
print(test3.x + ',' + test3.y);
print("------------");
test3 = test / 3;
print(test3.x + ',' + test3.y);
print("------------");
test3 = test ~/ 3;
print(test3.x + ',' + test3.y);
print("------------");
test3 = test2 - test;
print(test3.x + ',' + test3.y);
print("------------");
test3 = test2 * 3;
print(test3.x + ',' + test3.y);
print("------------");')

Edit: Just fixed a bug that caused the full call stack to not be shown.


Goodlookinguy(Posted 2014) [#36]
I just added a bunch of useful stuff. After this jam I need to write a language reference because it's grown considerably.

1) for and foreach loops + in keyword
format_code('foreach( val in {var n=3,var m=5,var o=7} )
print(val);

for ( var i = 0; i < 10; i++ )
print(i);')

2) Type casting and checking if a scope contains a variable
format_code('if ( {var i=0} has 'i' )
print('Yes, it does contain i');

if ( {var i=0} has 'b' )
print('Yes, it does contain b');
else
print('No, it does not contain b');

print(!{var i=0} has 'b');
print(!{var i=0} has 'i');

var test = 3.5 as 'int' + 5;
print(test);')


Goodlookinguy(Posted 2014) [#37]
Added identical operators (e.g. === !==) and scientific notation (e.g. 3.5e+4).

The identical operators allow you to test for type as well as value. Look at the below example for an idea of what I mean.

format_codebox('test = 50;
test2= '50';

// a - true
if ( test == test2 )
print('True, test == test2');
else
print('False, test == test2');

// b - false
if ( test === test2 )
print('True, test === test2');
else
print('False, test === test2');

// c - false
if ( test != test2 )
print('True, test != test2');
else
print('False, test != test2');

// d - true
if ( test !== test2 )
print('True, test !== test2');
else
print('False, test !== test2');')

Oh yeah, I also added array access type code from scopes. It's quite buggy at the moment, in that there are things you can't do because of how the parser parses right now. It does work to an extent though. Read this for an idea of what's working and not working.

format_code('test = {var a = 'Hello', var b = 'World'};
print(test['a'] + ' ' + test['b']);

test['c'] = {var i=10};
print(test['c']['i']);')


Update: I did a fairly drastic overhaul on some of the parser. You may want to take a look at the latest commit, although it is considered unstable at the moment. It has increased the flexibility of the parser quite a bit.

All valid code
format_codebox('test = {};
test['b']=func() {
return {
var c=func() { print('hi'); }
};
};
test['b']().c();

test = {};
test['b']=func(){ return func(){print('hi');}; };
test['b']()();

test = {};
test['b'] = {var b='hi'};
test['b'].b;

b = func(){ return 10; }();
print(b);')


Goodlookinguy(Posted 2014) [#38]
Fixed how variables are assigned. They are no longer assigned by reference by default. This can now be done manually. This example should be more than enough of an explanation.

format_code('i = func(){ return 10; };
b = &i;
c = i;
i = func(){ return 15; };
print(b());
print(c());')

I also added 'print' as a built-in keyword. It works like the print in Python. You can separate text by commas and it automatically puts spaces between them. If something is not a string or number, it will show its type in parenthesis.

The next addition will be the arrays. I've decided that I'm just going to make them like regular arrays. If you want a map, use scopes since they're just maps in reality. I also still have further work to do with these. The way they work is not how I want them to work. I want them to work like closures and they currently work like functions that just happen to be in another function at the moment.

Edit: I just fixed how functions take variables. I forgot that before, but it's in now.
format_code('test = func( a )
{
a = func(){return 10;};
};

b = func(){return 15;};
test(b);
print(b());
test(&b);
print(b());')

Edit: Just discovered that I broke object interfacing after that parser overhaul the other day. I'm trying some things and it's going alright. I have no solution to setting, although I figured out some for function calls and property accessing.
Woo hoo! The parser and VM are stable! The latest commit is passing my tests. Although I did have to change some of my tests because the change in how variable assignments work now messed up some of the test.


Goodlookinguy(Posted 2014) [#39]
I've added arrays and the special variable 'arguments' (which is an array) inside every function call to allow for "unlimited" arguments against a function.

Arrays use stacks
format_code('test = [15, 20, 30, 40, 50, [100]];
print(test[5][0]);
print(test[0]);
print(test[1]);
print(test[2]);
print(test[3]);
print(test[4]);
print(test.pop().top);
test.length = 10;
print("Length: " + test.length);')

"Unlimited" Arguments
format_code('example = func()
{
for ( var i = 0; i < arguments.length; i++ )
print(arguments[i]);
};

example('a','b','c', 1, 2, 3, null, func(){}, {});')

Tomorrow I plan to do something about how the foreach operator works so that it'll work with scopes, arrays, and even internal objects. With that said, I bid you adieu for the night.

Edit: Hey, if anyone has been following along and is interested in writing up some documentation, I'd be very grateful. I'm not very good at explaining things in human words. I'm much better at solving problems than I am explaining about them. My brain does the work and I just sort-of follow along as it magically does things. So, well, that's all.


Sammy(Posted 2014) [#40]
Getting quite sophisticated now, keep up the good work! :)


Samah(Posted 2014) [#41]
Back when MiniC was in its infancy, I forked it and added support for variable lists and multiple return values, like in Lua. Are you interested in pulling that? It's an old change so the implementation may be slightly different now.
https://bitbucket.org/swoolcock/minic-samah


Goodlookinguy(Posted 2014) [#42]
As I mentioned to therevills, that kind of code looks odd to me, however, if you wanted to fork my version, add it, and request a pull from my branch, I'd add it. That said, I'm not going to directly pull from your repo because of the vast differences from the original MiniC internally.


Samah(Posted 2014) [#43]
The original syntax was something like:
format_code('// assign 3 values
a,b,c = 1,2,3;

// create a function that returns 3 values
foo = func() {
return 1,2,3;
};

// call that function and assign to 3 variables
a,b,c = foo();

// call the function again, but ignore the first two return values
_,_,d = foo();')

I'll see how well it matches up with current tokens, or if there might be a better way of expressing it (if the syntax conflicts).


Goodlookinguy(Posted 2014) [#44]
Just added Unicode support for variable names (in the laziest way possible). I don't think the example will show up right here since the forums don't have Unicode support. I spoke too soon, I guess Unicode support was added.

format_codebox('ひらがな = func()
{
return "hiragana != カタカナ";
};

カタカナ = func()
{
return "katakana != ひらがな";
};

ಠ_ಠ¡ = func()
{
return "~_~¡";
};

★☆❶❷㉚㍄㏒ = func()
{
return "what a very odd name";
};

print(ひらがな());
print(カタカナ());
print(ಠ_ಠ¡());
print(★☆❶❷㉚㍄㏒());')
Aside from that I added the rest of the operator overloading support for scopes.


My current goal is to write clear documentation on the language. I'm hoping to have something made by Wednesday.


Goodlookinguy(Posted 2014) [#45]
I made some documentation: http://www.nrgs.org/dev/miniccbdocs/lang.html It's not much, but at least it's something. I'll do more down the road.


Aman(Posted 2014) [#46]
Very interesting. I wrote a very basic programming language 2 years ago. This is a great project for me to learn from. Bookmarked!


Goodlookinguy(Posted 2014) [#47]
Be careful what you take away from it, as I've done some very odd things to make certain things work. Like very hacky/kludge type solutions which I'm absolutely sure had better solutions but I've yet to figure out. It may be because the original creator of MiniC had done some things in odd ways that it only furthered the oddities later on because I didn't correct them early on.


Goodlookinguy(Posted 2014) [#48]
Alright, I updated the documentation big time: http://www.nrgs.org/dev/miniccbdocs/lang.html (press Ctrl+F5 is the documentation page doesn't talk about arrays, scopes, and more)

I believe I've covered the most important things I added to MiniC. The documentation is subject to change later.


Skn3(Posted 2014) [#49]
This is really progressing nicely!

Whats the module like for string manipulation and garbage?


Goodlookinguy(Posted 2014) [#50]
I was actually looking into porting a rope module and using it for strings. However, I'm not sure how worthwhile it is considering that I'm going to make it so that arrays can join together since they're using stacks. It would basically be like a string builder.

Oh, and you may want to read this post I made on my site about string repeats on the various platforms: http://www.nrgs.org/1984/efficient-string-repeat/


Samah(Posted 2014) [#51]
I've been messing around with the execution path trying to make it stack/instruction based. Point being, you could then yield and resume. Coroutines ahoy!


Goodlookinguy(Posted 2014) [#52]
Oh really? I was doing that a few weeks ago but never went any further than some simple tests. If you manage any sort of rewrite that doesn't kill other features, I'd love to see it.


Goodlookinguy(Posted 2014) [#53]
I'm sort-of satisfied with the current way MiniC works. If there are any severe bugs I missed, please tell me. I'm not likely to work on this for another 4 months. I go in cycles, and my work cycle for this is pretty much done at the moment.


Goodlookinguy(Posted 2015) [#54]
After more than a year of not using Monkey, I've come back to finish what I started and am reviving this project.

First things first: Yield was added. It does not work for function calls or foreach yet, but only because I'm going to sleep right now. I'm adding it as soon as I wake up.
format_code('// Wait 30 frames
var m = 0;
yield (m++ > 30);

print("Start...");

if (false)
{
print("Hello World");
}
else
{
print("M");
yield key_hit(KEY_R);
print("R");
yield key_hit(KEY_T);
print("T");
}

for (var i = 0; i < 4; i++)
{
yield key_hit(KEY_F);
print("F --> " + i);
var w = 0;
yield ((w++ % 2) == 1);
}

var i = 0;
while (i++ < 4)
{
yield key_hit(KEY_W);
print("W --> " + i);
var w = 0;
yield ((w++ % 2) == 1);
}

print("End...");

// Freezes at end
yield false;')

Second thing is that there are multiline comments now because I don't even know why there weren't already multiline comments...

Arg...!


ImmutableOctet(SKNG)(Posted 2015) [#55]
Sweet, always liked this project. Probably the best choice for adding user-created scripting to a Monkey game.


Goodlookinguy(Posted 2015) [#56]
Just pushed an update for empty yields that wait a single frame before executing the next bit.

format_code('for (var i = 0; i < 4; i++)
{
yield key_hit(KEY_F);
print("F --> " + i);
yield;
}')

More yield code is coming soon...

After that internal class delegates are coming. They will allow features similar to JavaScript's "prototype" ability.


therevills(Posted 2015) [#57]
Looking good!

One of the things which slightly bugs me with scripting with MX is that you have to modify the version within the build folder to see the changes in real time and remember to copy the changes back to the source directory (or you will lose the changes).


Goodlookinguy(Posted 2015) [#58]
Finally, had time to add function/scoped/recursive code yielding.

format_code('var run = func()
{
yield key_hit(KEY_SPACE);
print("Hello Monkeys");
yield;
run();
};

run();')

One more thing to go and then some more neat stuff after that. I'm liking how this is all coming out. I actually thought adding yield would be tricky, but this is easier than anticipated. I need a key_hit replacement though, because it does not work in recursive situations without an extra yield to wait a frame.

One of the things which slightly bugs me with scripting with MX is that you have to modify the version within the build folder to see the changes in real time and remember to copy the changes back to the source directory (or you will lose the changes).


I use realtime data copy systems like DSyncronize to deal with this particular problem.


Goodlookinguy(Posted 2015) [#59]
Made critical fixes today...

format_code('* Fixed if-else conditional null scope bug
* FIxed single and multiline comment detection
* Fixed function call yield bug *** (updated)')

Now this works as intended when executed in on-render. A nice little text box writer.

format_code('var show_text = func(text)
{
var mm = 0;
var clickTime = millisecs();

var yielder = func(itext, upto, tf, mm)
{
set_color(100, 80, 80);
draw_rect(10, 440, 620, 35);
set_color(255, 255, 255);
draw_text(str_substr(itext, 0, upto), 14, 443);
if (tf)
return (mm++ % 3) == 2;
return true;
};

for (var i = 0; i < text.length; i++)
{
yield yielder(text, i, true, &mm);
if ((millisecs() - clickTime > 200) && key_down(KEY_SPACE))
{
i = text.length - 1;
clickTime = millisecs();
}
}

yield yielder(text, text.length, false, 0) && (millisecs() - clickTime > 200) && key_down(KEY_SPACE);
return true;
};

yield show_text("Press the spacebar after this is done...");
yield show_text("Hello World of Tomorrow!");
yield show_text("Look, a different message is appearing!");

yield false;')

Edit: Made text-box example even better. Woo~

------------------
Edit: I noticed scoping issues while writing the text-box code. I'm going to assume it is something squirly and hard to track down. I'll try, but I can't promise anything. I think it is related to this bug report I made: https://bitbucket.org/Goodlookinguy/minic-n.edition/issues/20/internal-objects-in-scopes

Another edit: Nevermind, it turns out it is another critical bug caused by the new yield systems. Oi, adding yield felt easy but now I see everything that I missed...mostly simple stuff, but still, absolutely necessary...


Shinkiro1(Posted 2015) [#60]
Hi, just started using this today and it went really well / easy setup.

> I use realtime data copy systems like DSyncronize to deal with this particular problem.
For people on mac/linux, you can use rsync to sync 2 files/folders. Unfortunately there is no --watch option that does it automatically.
I found this though: https://github.com/mattes/autorsync


Goodlookinguy(Posted 2015) [#61]
If you create any code that seems to be having issues for no obvious reason, please post it or report it in the Bitbucket page.

I should also warn that if you are using any internal classes that I am going to be redoing how those work within the next few months so that internal objects can be modified much like JavaScript. However, it will require completely redoing the code in this fashion: https://bitbucket.org/Goodlookinguy/minic-n.edition/issues/21/class-delegates-setters-getters-and-more


Shinkiro1(Posted 2015) [#62]
I want to have all functions available in all my scripts.

What's the most efficient way to do this?
I guess parent-scopes and RegisterGlobal() are in the right direction?

----

PS: I also read the docs and how you can use Singeltons to not instantiate multiple delegates. If possible I don't want to do that and just register everything once and then have it available in all scopes.

---

edit 2: when calling script.RuntimeErr() how can I have it output linenumbers. do i have to feed info into the stacktrace?

---

edit 3: when using your common bindings string split function, i can use array.get(index) on the returned value but not array[index].
Is that intentional? I get that it won't work with your own types but aren't arrays special?
(yes I also have MCArrayObject included)


Goodlookinguy(Posted 2015) [#63]
I want to have all functions available in all my scripts.

What's the most efficient way to do this?
I guess parent-scopes and RegisterGlobal() are in the right direction?

----

PS: I also read the docs and how you can use Singeltons to not instantiate multiple delegates. If possible I don't want to do that and just register everything once and then have it available in all scopes.


RegisterGlobal should do what you're asking. I used it in the common bindings to globalize all of the functions and values.


edit 2: when calling script.RuntimeErr() how can I have it output linenumbers. do i have to feed info into the stacktrace?


Compile in debug mode to get error output automatically. In release mode it fails silently.

Edit: If you mean that you are having issues knowing which script is failing, use the script name parameter in LoadScript. As an example...
format_code('Local scriptData:String = LoadString("scripts/script.mc")
Local myScript:VMScope = vm.LoadScript(script, Null, "script.mc")')


edit 3: when using your common bindings string split function, i can use array.get(index) on the returned value but not array[index].
Is that intentional? I get that it won't work with your own types but aren't arrays special?
(yes I also have MCArrayObject included)

The string functions are outdated (and supposed to have been fully replaced) and MCArrayObject is old code that was supposed to be removed from the Common Bindings.

I'll do an update right now to correct this issue and so you can use this...

format_code('var t = "Hello World".split(" ");
print(t[0]);')

Edit: The above code should be working now, but I haven't tested it because I'm quite tired.


Shinkiro1(Posted 2015) [#64]
hey, thanks a lot. i really enjoy working with this minic version.


Goodlookinguy(Posted 2016) [#65]
My head hurts designing a way for scopes and the like to be done with bytecodes instead of the AST. The AST made this easy.

Anyone who has any experience in designing these and has ideas, I'm all for it. Right now I'm allocating to local stacks, but it prevents accessing more than one level above. The addresses seen in the example are outright wrong in several cases, but that was because I forgot what I was doing for a bit and accidentally used the allocation addr, which pushes an address on the stack. It's a whole bunch of headache is basically what I'm saying...

(Note that this assumes that "func xyz" will be allowed in the language in the near future. It may not be, but that doesn't detract from the point of this.)
format_code('//////////////////////////////////////////////////
func fibonacci( n )
{
var a = 0;
var b = 0;
for (var i = 0; i < n; i++)
{
var t = a;
a = b;
b = t + b;
}
return a;
}

var fib = fibonacci(8);
print(fib);

//////////////////////////////////////////////////
Meta Code Func Name Table ID Min Params Max Parameters Code Size External Object
.extfunc "print" 0 1 -1 (n) 0xFFFFFFFF -1 (MCPrintExtern Instance)
.function "fibonacci" 1 1 1 0x0000002A ? Null

//////////////////////////////////////////////////

; starting at 0x00000000
alstack
push float 8 ; accepts in "human" or hex form
call fibonacci ; push current addr to call stack (call stack is size of 65535 max, min is 512)
popt fib
push fib
call print
dlstack

; starting at 0x0000002A (for example purposes)
alstack ; allocates local stack
ppopt n ; pop from parent stack and assign (n=0x0000); ppopt [0x0000002A]+0x0000
tload a, 0 ; (a=0x0001) ; tload [0x0000002A]+0x0001, 0
tload b, 0 ; (b=0x0002) ; tload [0x0000002A]+0x0002, 0
.for_0R2EW2B
alstack ; 0x0000002A+0x12 = 0x0000003C
tload i, 0 ; (i=0x0000) tload [0x0000003C]+0x0000, 0
jlt rel forf ; jumps if less than to addr with relative proximity ; jlt rel 0x0000004A+<.forf_0R2EW2B>
tload t, a
copy a, b ; push b : push a : asn - optimization
fadd2 b, t ; push t : push b : fadd - optimization
popt b ; push b : asn - optimization
inc i
jmp nrel for_0R2EW2B
.forf_0R2EW2B
dlstack
.endfor_0R2EW2B
ppush a ; push to parent stack
dlstack
ret ; pop current addr from call stack and jump to addr')

At the very least, at least know that my current goal and progress is towards a bytecode VM because I want to easily serialize the data at any time and load it back up just as quick.

I'll say this much, this is the first time I've ever created a bytecode VM and I have a new-found respect for Java, even though I still don't care for the Java language itself.


therevills(Posted 2016) [#66]
Great to see you still working on this!


Goodlookinguy(Posted 2016) [#67]
Okay, so after my brain died on me the other day, I stopped doing that for a week. I'll be going back today, but geez...I actually had a real migraine. Although it was probably caused by me not eating or drinking because I was engrossed with this.

Anyways, in related news, yesterday I took a stab at porting this to MX2 so that I would know where I could make the code more portable to MX2 in an attempt to sync them somewhat. I was able to convert a chunk of the code, but not the rest because you can't cast to interfaces in MX2 yet. So I'm waiting for that to continue on. Just know that for the most part, it was almost completely portable with a fair amount of ease. I'll just call this a lucky break.

I specified what I did here if you want to know the technicals: https://bitbucket.org/Goodlookinguy/minic-n.edition/issues/25/mx2-port

EDIT
Question: Would anyone be interested in an MX or MX2 based scripting language? Something that very closely resembles the language you work in? Maybe even something that ignores types, but allows you to specify anyways so that code can literally be copied and pasted? Just thinking out loud, but any interest?


CopperCircle(Posted 2016) [#68]
Hi, yes a scripting language would be great, I have my own very simple maths scripting language to do formulas in some of my business apps, but a fully featured scripting language would be nice and powerful.


Goodlookinguy(Posted 2016) [#69]
MiniC N.Edition is a fully featured scripting language that is basically on par with JS in terms functions. Only bad thing is that it's using an AST VM instead of bytecode VM, which has made things like adding yielding difficult, not impossible, it was just difficult; and that the language is based heavily on JavaScript. What I was asking about was specifically whether making another scripting language that uses MX or MX2 syntax instead of JavaScript styled syntax was wanted since using two separate syntaxes might be a no-go for people.


CopperCircle(Posted 2016) [#70]
I have only just had a look at MiniC and it looks great, but yes I personally do not like JS as a language and think a MX syntax would be great.


Goodlookinguy(Posted 2016) [#71]
To anyone using this: I just uncovered a treasure trove of bugs revolving around arrays + scopes. I'm going to be pushing a bunch of commits in rapid succession probably. Don't know when I'll find the end of the bugs. It's caused by extremely specific cases, but just causes the system to crumble. I honestly never noticed this.

Edit: A few of the bugs have been fixed. There still appear to be some more, but the major ones I noticed were resolved.

Also: The Blob Monster in MiniC N.Edition has been added. You will need my MiniC Common Bindings to test though.

Edit:
Simplest test case for reproducing the bug I mentioned. It's caused by improper evaluation. The parser node order may be wrong. So this might take a day or two to fix.
format_code('var b = func(xx, yy)
{
var c = { var d = [0] };
c.d.length = 10;

for (var i = 0; i < 10; i++)
c.d[i] = {var x = 0, var y = 0};

return c;
};

var e = b(3, 4);')

Edit 3/19/2016
Finally figured out what the cause was. Basically, I was trying to access the array from the scope of it's container instead of the scope it was in. So basically...

"a.b" access 'b' from scope 'a' is the idea.
"a.b[i]" was saying get 'i' from the scope of 'b', which itself is not a scope of a's scope.

Hopefully you can understand what I mean. If not, don't worry 'bout it. I'm fixing it since I finally figured out the cause.


CopperCircle(Posted 2016) [#72]
Hi, are there any more minic examples of working with monkey classes? I want to make a calculator that works with values in my monkey objects, how do I access monkey objects to get/set values?

eg: var result = number1.value + number2.value (number1 and 2 are monkey objects in a stack with a field called value)

Thanks.


Goodlookinguy(Posted 2016) [#73]
Read this at the "Internal Objects" part at the very bottom -> http://www.nrgs.org/dev/miniccbdocs/lang.html
Also, the BlobMonster replication I made is using some code that you could use for reference: https://bitbucket.org/Goodlookinguy/minic-n.edition/src/default/tests/blobmonster.monkey

Hopefully that should explain it. Although, as I've stated, for over year I've only just realized, I do plan to changing how that works at some point. Maybe, though, I can just use some adapters or something...I dunno. I want to keep some backwards compatibility if possible.

Edit: If you're still having trouble, you'll have to give me a day or two to update the documentation, which is outdated. It also doesn't list a number of features which is extra bad. Like for instance, alongside being able to interface with objects, you can also add operator overloads for those objects through the interfaces. There's also console output, and just a number of other features that are undocumented.


CopperCircle(Posted 2016) [#74]
Thanks, I will hold off until the docs are updated, operator overloading would be great.


Goodlookinguy(Posted 2016) [#75]
New documentation. It might be considered incomplete (even though it covers most things), but I'm tired of writing documentation for now. I've been writing it for the last 3 days. I think my writing became more sloppy. I tried to keep it non-personal, but I think I drifted a few times.

http://www.nrgs.org/dev/miniccbdocs/2016/

Edit: I want to rename MiniC N.Edition to be something more catchy...Banana maybe? Get it? Ha ha ha ha.......hmm...


ImmutableOctet(SKNG)(Posted 2016) [#76]
Just read the documentation; thank you for summarizing the changes so far. There were a few minor typos, but it was overall succinct and very informative.

I've got to say, I'm really liking the syntax. Great work, Nicholas, I'm glad to see the language is still being maintained.


CopperCircle(Posted 2016) [#77]
Good job, the docs are clear and helpful!


Goodlookinguy(Posted 2016) [#78]
I'm now officially going 100%, all out, into developing and finishing the Bytecode VM, along with BCC (Bytecode Converter or Bytecode Assembler if you prefer), and Assembly Mnemonic Scanner & Parser (for debugging).

Originally I was just sort-of developing it whenever I had some time. However, now that I've caused an "Access Violation" error, in the AST VM, that can't be debugged, that I only know is an access violation because I looked up the error code returned, I decided it's best to lay the AST VM to rest. AST VMs aren't really meant to scale and as I did with it anyways.

On that note, I need to mention that I decided to start over the bytecode system because my other one was...mmm...I can't describe it well. Basically, I made some key mistakes that caused issues, plus I was working on code I started a while ago, so I'm just giong to scrap it. Hopefully this will lower said brain hurting from before.

@ImmutableOctet I'll look into fixing those typos in a day or two. If you know any specific ones, point me to them.

Edit: I've released the code for the brand new bytecode stuff I'm working on. It's about 5 hours of work in, so basically that means it has a long way to go. However, from the start, the design of this system is much better than the other one I had made, which was making my head hurt because I was doing strange things and I was trying to ultra compress stuff. Lesson learned, slow down and stop drinking so much caffeine...


Goodlookinguy(Posted 2016) [#79]
For the bytecode compilation I've started adding static evaluation (incomplete). Progress (and code used to produce image contents) can be seen here: https://bitbucket.org/Goodlookinguy/minic-n.edition/commits/branch/bytecodes

This is the same code without static evaluation (left) and with static evaluation (right).



Edit: I just added a preliminary VM. I need to start making it right now so that I can ensure that the code generation works as expected.

--------------------------
Update! Here's a code generation test (also confirmed working with the VM).
format_code('if (3 != 3 && 4 == 4 || 5 == 5) {
"bleep" + 3;
}')

Code Gen (without static evaluation)
format_codebox(' PUSH 3
PUSH 3
NEQ
JMPF l9
POP
PUSH 4
PUSH 4
EQ
.l9 AND
JMPT l15
POP
PUSH 5
PUSH 5
EQ
.l15 OR
PJMPF l21
PUSH "bleep"
PUSH 3
ADD
POP
.l21 NOP')

Code Gen (with some static evaluation)
format_codebox(' PUSH 0
JMPF l5
POP
PUSH 1
.l5 AND
JMPT l9
POP
PUSH 1
.l9 OR
PJMPF l13
PUSH "bleep3"
POP
.l13 NOP')

The bytecode VM can run this code currently and confirms it's structurally sound.


Shinkiro1(Posted 2016) [#80]
Looks cool. How much faster will be ByteVM be than the normal VM?


Goodlookinguy(Posted 2016) [#81]
It depends. Loops (while, for, foreach) will likely be slower than the AST one (maybe). However, most other things will be faster and even if they aren't faster though, the system will be far more stable and less likely to cause problematic untraceable bugs.

Edit: In case I was a bit sloppy on my wording, what I'm really trying to say is that I won't know until more is ready and a decent test can be done. Also, when I start to put in optimizations it might improve it further.

Another Edit 5 hours later: Due to the new system, I've actually been able to design a "garbage collector" that should improve speed in general.
--------------------
Update!

I've designed a fake threading system that allows concurrent code to run at the same time (not really the same time, but...). This is still a WIP, but I have written the code and it works. I'm thinking that I can do something similar to JS's setTimeout function for running concurrent code. Right now it is not fake thread safe. Which sounds funny, but it isn't. I'll be looking into ways to make it better at a later point in time. My primary focus is still everything else.


Goodlookinguy(Posted 2016) [#82]
I've been redesigning the VM system entirely breaking it up into smaller pieces, just general refactoring. After I'm complete the VM should be generic enough to support other typeless languages, which was my ultimate goal anyways.

My main issue at the moment is how to go about injecting bytecodes into the middle of other bytecode without interrupting the jumps. Once I have that worked out, I'll be able to finish refactoring the rest of the stuff (none of which compiles right now so I'm desperate to solve this problem).

Example if let's say there is an include function
format_code('blah = hello
include("somefile.mc")
if (true)
{
// the jumps are misplaced...
}
// this is problematic')
Edit: 7 minutes after posting I came up with a solution even though I'd been working on it for days. Where the include file is, I'll just jump to the file which is pushed onto the end of the bytecode stack and returns to the earlier point in the stack. This way there's no updating of the pointer position from the variables and anything else. I'll probably have to build the include function into the system, but we'll see. Anyways, now I'm very happy!!
Edit Again: Now I just have to workout how to handle an include that is inside the non-global scope. So...there's still work to be done is what that means.

Edit 1 Day Later: The new collection types will support indexes, keys, and values. This is in contrast to the old style that did not support indexed values. However, there will still be an array type for fast array code when needed.

This is an example of exactly how the system would work when implemented.
format_code('var collection = { "Hello", "World" };
print(collection[0] + collection[1]);
collection.push("!");
print(collection[0] + collection[1] + collection[2]);
collection[99999] = "Banana";
collection["Banana"] = "MX"
foreach (var item in collection)
{
print(item.index);
print(item.key);
print(item.value);
}
print(collection.length); // prints "5"')
It will support the actions "insert(index, value)", "remove(index)", "push(value)", "pop()", and "collection has 'xyz'" as well as the property "length".

The new language standards will also be using camelCasing (not PascalCasing or underscores). It looks appropriate for the language.

Geez I've written a lot. Well, I'll shut up for now until I have something more substantial. Which hopefully won't be too long from now.


Goodlookinguy(Posted 2016) [#83]
I've gotten quite lazy with my commit messages lately. https://bitbucket.org/Goodlookinguy/minic-n.edition/src/bytecodes/bccvm/

> (Bytecode) Small fixes to MC Compiler (BCC); Starting to add code so that it can compile again.
> (Bytecode) More fixes (will compile again very soon!)


Anyways, I've designed how the code will look to run the new VM for anyone interested. It has a very different style from the last one. I really wanted to hardcore modularize the system so that I didn't end up in the same predicament as the AST VM where it was hard to maintain because everything was shoved into a single file.

format_codebox(''
Local vm:VM2 = New VM2()
vm.cpu = New MCCPU()
vm.mem = New MCMemory()
vm.gc = New MCGC()
Local process := vm.CreateProcess(VM2Process.NoPriorityLimit)
Local thread := process.CreateThread(bcc.bytecodes, VM2Thread.MainThread)
thread.Start(0) ' 0 is the pointer position
vm.ExecuteMachine(process.procID) ' if unspecified, it will run everything!
Local childProcess := process.CreateSubProcess(vm, VM2Process.NoPriorityLimit)
childProcess.StartThread(other.bytecodes, VM2Thread.RunUntilYieldOrEnd) ' start immediately without extra code
vm.ExecuteMachine(childProcess.procID) ' thread runs and is disposed of (if done, not if yielded)
Local thread2 := childProcess.CreateThread(VM2Thread.RunUntilYieldOrEnd)
'''' This next bit will keep the thread alive once it finishes
'''' doing its job. This way it only has to be created once to keep
'''' being used to execute functions.
thread2.keepAlive = True
thread2.ExecuteFunction(vm, "onUpdate") ' executes the function immediately')

The new "threading" system is now essential for the system to run code. I'm sure I can simplify it a bit more than shown above, but for now this is what I'm aiming at. It should be as easy as shown above to run code.

There will be a MiniCVM function to get a pre-configured VM for running MiniC bytecodes. So that part won't have to be done manually.

On another new-ish note, the new memory system is highly optimized using pools and such to prevent constantly creating new objects. I also avoided extra null objects on all values because that was silly in the last system. Hopefully this won't have too much, if any, effect on the speed. Even if it does, the other optimizations will probably equalize it.


Goodlookinguy(Posted 2016) [#84]
After 3 weeks, the bytecode VM compiles again. I'm on vacation, so my updates might be a little slow or even fast. I'm not at home, so it depends on whether or not I'm settled in wherever I'm settled in.

However, my main point was that I conquered the massive refactoring project and am really on top of this. Woo~ More to come as I'm only about 50% done. Lots of little bugs everywhere too.

------------
Edit: Some preliminary tests resulted in <1ms execution times setting a variable and executing an if-statement. This is fantastic! I expect to have a bunch of the basic features finished today.

Also, I'm still trying to figure this out, but if a statement is returned early from a function, how would one go about descoping?

format_code('var a = func()
{
if (1) {
return "early";
}
};
a();')

JMP i10
.i2 PUSHSB
PUSH 1
JMPF i7
PUSH "early"
RET
.i7 RET
REF i2
PUSH a
LASN
PUSH a
CALL

Edit: Maybe RET can pop the scope block too. Although that can make the code harder to read. At least it would resolve this issue and would reduce the number of opcodes.


Shinkiro1(Posted 2016) [#85]
Hey Goodlookinguy, just wanted to say thanks for constantly improving MiniC.
I am using MiniC in my RPG and without it I would have had to write my own language.
I suspect that eventually I might have more MiniC code than monkey.


Goodlookinguy(Posted 2016) [#86]
I am using MiniC in my RPG and without it I would have had to write my own language.
I suspect that eventually I might have more MiniC code than monkey.


This makes it all the more important that I finish the bytecode VM since the other one is unstable. Also, thank you for using it. That provides higher motivation for me to continue. Although I am doing it for myself as well (for an ARPG).

Also, when ready, I think you'll like the new bytecode VM a lot once you get accustomed to it. Which will take some time since it now uses "processes" to group bytecode and "threading" to execute code from the processes. It also supports making your own custom CPU, Memory, OS, and a few other things, although somewhat limited in customization.


CopperCircle(Posted 2016) [#87]
I'm also using, great work :) looking forward to the bytecode VM. Are you going to port it to MX2?


Goodlookinguy(Posted 2016) [#88]
I already started to port it to MX2 a few months back, but discovered it to be unable to port currently. When MX2 has a few more of the core language features ready, I'll be able to fully port it.

----------
Edit 6/12: I'm writing backpatching code right now for breaks/continues. It's taking longer than expected because of an unknown bug that is causing me some issues.


Goodlookinguy(Posted 2016) [#89]
I'm about 75% done with the MCBCC (MiniC Bytecode Compiler). I just finished while-loops, break, and continue (both of which required significant work having to backpatch them and all). Things from here-on should actually be easier than before. Also, the optimizer will be coming soon after v1.0 along with an extended CPU set. It will reduce overhead.

On a serious note: I found a long standing parser bug. I will hopefully resolve it in the next day, but you'll need to update to the bytecodes branch to get the fix because I don't feel like merging the two branches after the fix.

This is the simplest case for the bug I could conceive.
format_code('x = 0;
if (1) { x++; ++x; }')
~~Edit~~
Eek, the bug was ~really~ long standing. It came from the statement list. It's fixed now.

~~~~~~~~~~~~~ UPDATE 6/16/16 ~~~~~~~~~~~~~
Today's date is great. Anyways, I'm about 85% done now. For-loops, while-loops, yield, references, 'this', 'super', and immediate scopes are working. Scope accessors are on the way next. Scope mutators may take some time. All-in-all I give it 5 days before I'm completed at version 1.0 (that is, 5 days in vacation time. If things come up, it may delay me.)

~~~~~~~~~~~~~ UPDATE 6/17/16 ~~~~~~~~~~~~~
Added scope accessors and mutators. This is working!
format_code('var a = { var b = 1, var c = { var d = 3, var e = 5 } };
a.c.d = (a.b = 16) + a.c.e;')
Variable Results...
format_code('a:
b = 16
c:
d = 21
e = 5')

~~~~~~~~~~~~~ UPDATE 6/18/16 ~~~~~~~~~~~~~
Function Declarations and Function Calls are very hard to implement. It's Jenga code left-and-right with a hint of the hydra code spliced in-between. It will take further time to resolve the bizarre bugs.
It was long, it was hard, it was fought with a passion, but I have finally conquered functions in a single-pass style. Tested and I'm getting 60FPS with <1ms execution times. This is freaking awesome! All that's left to do now is to add external functions and I'm set! (I also have to add type casting, deleting, and blah, blah, blah, but those are no big deal)
Edit: Hmm...I lied. There's even more I forgot and it is back to the first statement again. This code is particularly difficult because it is tied into the core instructions and I'm not really sure how to alleviate it at the moment.
~~~~~~~~~~~~~ UPDATE 6/19/16 ~~~~~~~~~~~~~
In my biggest hack/cheat yet, I introduced the concept of contexted values. This fixes the function return value problem as well as other mysterious issues. I may in fact rewrite some of the code at some point to rely on context instead of the hacky code I have in there right now. It may be the end-all solution to scope related issues.
Edit: It turns out the parser has a few bugs. The most negative of which is watching it generate a completely wrong AST at one point. This is bad, because it means I may have to rewrite part of the parser since I'm getting completely wrong results.
format_code('c.d(4).s + 5;')
This generates a very wrong tree structure and ughhhh. This is starting to get very demanding. Damn it, someone fund me so that I can go buy some energy drinks.
Edit 2: I got this next bit working, but I can't get it to parse out correctly without paranthesis. If anyone wants to jump in and fix this part of the parser, go right ahead. The parser, unlike the VM component, is easy and straightforward to follow and follows standard parser setups.
format_code('(c.d(4)).s + 5;')
~~~~~~~~~~~~~ UPDATE 6/20/16 ~~~~~~~~~~~~~
Got external functions working...rejoice at how close this is to being finished.

~~~~~~~~~~~~~ UPDATE 6/23/16 ~~~~~~~~~~~~~
Creating simple MCCPU ASM code scanner & parser.
~~~~~~~~~~~~~ UPDATE 6/28/16 ~~~~~~~~~~~~~
I was working on another, smaller, project. Now that I'm back to this one again, I wanted to wait a bit to see what needed to be done about certain issues in my head. I've come to the conclusion that I'm going to have to do a large-scale refactor again. This is horrible as I just got done with the last one 2 weeks ago. The problem is that there are several structurally weak areas as well as a recent change that made the compiled assembly size bloat. This to me is a call for further refactoring which will hopefully not devolve in refuctoring. Thankfully a significant change I made was the idea of "contexts" for stack values. This is helping solve a great number of issues which will make the refactoring go a lot smoother.
~~~~~~~~~~~~~ UPDATE 7/14/16 ~~~~~~~~~~~~~
Mmmmmmmm.....still refactoring. This will probably take until the end of the month if I'm being honest. I also did a code line count and ignoring empty lines and comments, it's 6,564 lines of code. That's a lot of code and would explain why it's taking exponentially longer to fix issues and refactor. Luckily I've gotten a handle on the code structure and am working out the wrinkles in the structure weaknesses.
Aside from the boo-hoo, whoa is me, and I need hugs and energy drinks, I've now also started implementing a "prototype" system that works very similarly to JS. In fact, my ultimate goal is to make some javascript code portable with little to no changes. Of course, certain things will have to be changed. "val | 0" will not convert 'val' into a magical int when doing division or modulus operations, so that part can't be helped as it is my language, not JS. However, that doesn't mean one day that it won't work as the system is setup to handle int values in the future. Whatever the case, there's a lot more work to do, but things are starting to become really amazing. It's just too bad I had made the language so complex as to cause me to have to do this much work just for an initial release of the bytecode compiler.


Goodlookinguy(Posted 2016) [#90]
I haven't made any commits in a while because it's so damned unstable and outright broken that I am going to avoid pushing a commit until most of this is finished. I'm doing a couple of things right now while I'm also working out a lot of structure issues.

1. Soft Semicolon (compiler option)
format_code('var a = 5
var b = 6')

The system will automatically insert semicolons after those statements. The system will basically guess where you intended to have semicolons. This can and just might lead to harder to uncover bugs if you write code like that. However, I noticed that based on the code structure, semicolons are almost just there to make the user feel more comfortable. I'm all for simplicity where allowed.

2. Types are not special
format_code('var a = new String(); // id = 3
a[0] = 72;
var b = 'H';
var c = a === b;')

3. Because types are not special (and who needs semicolons), prototypes can be done
format_code('String.prototype.thing = function( something ) // new object identification is created because of this statement
{
return this.length + something
}

var a = "Hello"
print(a.thing(4));')

Expect this and more from the magical land of my head finally getting a hold of this crap after 2 months. It's too bad I didn't write the code like this from the start otherwise this would be a little bit easier to deal with. Live and learn I guess.


CopperCircle(Posted 2016) [#91]
Good work, keep it up :)


Goodlookinguy(Posted 2016) [#92]
Soft Semicolons parser option has been added to the original VM in a commit via the bytecodes branch. I should've pushed it through the default branch, but didn't. So, if you want that option, you'll need to get that.

To load the options you'll need to do something like this
format_code('
Local pOptions:ParserOptions = New ParserOptions()
pOptions.softSemicolons = True

Local vm:VM = new VM()
Local myScript:VMScope = vm.LoadScript(script, Null, "script.mc", pOptions)')

On a side note: I made it all work again and 7 commits were pushed through. I'm getting ever closer to the goal.

Edit 2 months later: I'm stuck. I haven't been able to get past a blockade for 2 months. So...crap.


Shinkiro1(Posted 2016) [#93]
Just a heads up again, I have around ~80 miniC functions in my RPG right now and I control events, battle animation, enemy AI, and NPC's with miniC. Although I am using an older version it's really stable and when it fails the error messages are actually useful (unlike many other languages). Yielding is also something I discovered recently and now I can write stuff like this.
format_code('
message("message 1");
yield message_confirmed(); // waits until player confirms message 1
message("message 2");
')

I can't recommend this module enough, it's awesome.


Goodlookinguy(Posted 2016) [#94]
I'm glad that the original version is working well for you considering it has a considerable number of bugs, mostly in scoping access since I didn't use context when doing things.

As for me, I'm still solving an incredibly complex issue. If I can get over this single issue, everything else will fall into place. This issue is so over the top hard to solve though, that I've been a standstill for a few months.

Edit: I also want to add new operators to evolve the language into an even more modern language. However, I really have to solve this other issue...but here are some of the thoughts that have been going around in my head...

I want some of my favorite C# features...
format_code('var a = b ?? "example"')
format_code('var a = b?.example')

Switches with fall-through
format_code('switch (value)
{
case example:
break;
}')

I also want to add in some of my own thoughts on language additions.
format_code('switchif (value > 5; value)
{

}')

Traditional...
format_code('for (var i = 0; i < 10; i++)
doAction();')
My thoughts on a quicker way
format_code('loop (10) doAction();')
The loop index would be accessible through the "li" variable...
format_code('loop (10) print(li);')

------------------
Holy s**t! I found a workaround that breaks my code structure, but who cares, it works! I'm going to push a beautiful update today. I'm besides myself right now because I've been trying to make a fix for months.

Edit again: It took 5 hours of actual debugging (with the debugger) to solve the issues. It was way, way, way more unexpected execution wise than I thought it was. I'm convinced that I over-complicated certain "features." Either way, I kid you not, the issue is fixed and the commit was pushed a few minutes ago. Now the project can come to a reliable solution conclusion.


Goodlookinguy(Posted 2016) [#95]
So I'm rewriting part of the VM instruction set and adding reflection. How does everyone feel about reflection requiring a symbol as shown below?

format_code('var example = { var value = 3 };
var a = "@example";
a.value = 5;
print(example.value); // prints 5')

It makes it relatively easy to 1) detect, and 2) help block malicious input if that's a problem. I borrowed the symbol idea from php which does it for the same reason (I hope).


Shinkiro1(Posted 2017) [#96]
@Goodlookinguy
What's the difference declaring a variable with the var keyword, as opposed to without?
In the docs it says, var makes a variable local. If i have a function without var in the outer most scope of the file, is it reachable outside the file?


Goodlookinguy(Posted 2017) [#97]
Okay, so basically var prevents this particular behavior that was inherited from the original MiniC where it would look up every scope above it to see if there was a variable by that name and assign it.

format_code('outerScopeVariable = "Hi";
(func(){ outerScopeVariable = "Hello"; })();
print(outerScopeVariable); // prints "Hello"')

format_code('outerScopeVariable = "Hi";
(func(){ var outerScopeVariable = "Hello"; })();
print(outerScopeVariable); // prints "Hi"')

If there is no scope above it that has that variable name, then it is defined locally by default.

format_code('(func(){ var outerScopeVariable = "Hello"; })();
print(outerScopeVariable); // error, outerScopeVariable is not defined')


Shinkiro1(Posted 2017) [#98]
Got it. It seems to be a good idea to use var wherever possible then.
It seems the same rules apply to function parameters.

format_code('
var start = func(actionId, userId, targetId) {
// code
};

print(actionId); // prints the passed value
')

This is a bit confusing compared to other languages, but in my case it's actually useful (don't have to create an outer scope variable).


Shinkiro1(Posted 2017) [#99]
Another question
format_code('
var HeroId = func(userId) {
var id = battle_userid(userId) + 1;
return "hero" + (id as 'int');
};
')

HeroId() should return a string like:
“hero1” but not “hero1.0”
I tried casting with as ‘int’ but that doesn’t seem to work.

Any ideas how I can solve this?


Goodlookinguy(Posted 2017) [#100]
Hmm, this seems like a slight oversight on the original VM design. It's not a problem on the new VM, but the new VM is incomplete. The problem is that in the old VM when casting to int it doesn't change its type, it makes a number like "1.4" become "1.0". I thought I had code in there to determine if it was an integer so that it would produce what you're expecting, but I guess it doesn't. I'll try fixing it in the next day. I've just been extremely busy.

Edit: I found the line number is 1374 in vm.monkey. I'll fix it when I'm free.


Shinkiro1(Posted 2017) [#101]
That's cool. Thank you.


Goodlookinguy(Posted 2017) [#102]
I'll push out an update soon. Basically, what I did was to set a variable whenever you do "xyz as 'int'" that tells the variable if it gets cast to a string, to act like an integer. I haven't tested it and won't be testing it right now because I'm redesigning my house and I don't have much time to push this fix in.

To iterate, keep your code the way you have it right now. That's the only way this will work because all numbers are a float as far as the VM is concerned.

Edit: I pushed out the update, but I can almost guarantee there is probably a bug somewhere. So if you have any odd or unexpected output, alert me and I'll attempt to fix whatever it is.


Shinkiro1(Posted 2017) [#103]
It still returns it as floating point on glfw.
For my code it also never takes the code path for Case "string","str" (line 1374).
Thank you for your help :)


Goodlookinguy(Posted 2017) [#104]
I am stupid. I had the intention to do something specific when I got to the code and then I totally ignored what I intended to do.

For now this should work.
format_code('(something as 'int') as 'str'')

I'll attempt to do what I actually intended soon.


Shinkiro1(Posted 2017) [#105]
Dude, thanks! That works.
I totally get now why, now I feel stupid xD

I am planning on shipping a demo soon, and the whole battle system wouldn't have worked without this :)


Goodlookinguy(Posted 2017) [#106]
I failed here. I should have just made an "intstr" or "intstring" conversion. The reason I added the other thing was so it would act like JavaScript. Basically, 'blah' + xyz as 'int' would be "blah0" or whatever, but then I didn't do it. Mmmm...

I mostly wrote this post as a reminder to myself when I have the time to update that. I think I'll add 'intstr' as an optional form of quickly doing that.