Replies: 8 comments 5 replies
-
Two more brief ideas:
|
Beta Was this translation helpful? Give feedback.
-
Wow! You have a lot of ideas for improvement.
I dislike the current system. I'd prefer that all variables declared outside a method (in the top level) are considered global. There should be a main function (def main) (case insensitive) which is the program's entry point. Rather than putting all code at the top (global) level. If you don't have main, then you might make every line of code at global scope automatially "in main"- this is a copy of what C# does recently. So if you have a variable in main, it's local. If it is outside of main, it's global. Done.
Never used external variables (don't know what these are) but sounds like you want a declare statement for these (based on later ideas you mention)
Yeah, I think
Dislike the word allocate here because two CPUs might be sharing memory and it sounds like this CPU is authorative when it is not. Declare makes sense. Though we might have a lot of declares... You'll need
I think it is good form to copy these linked components to named variables in the code anyway. You can achieve both with the right declare statements. There is a gotcha here though. My prefered code structure is
If you don't put the copy of linked components in the loop, those links will break if the relevant structures are built after the CPU starts looping (or if they are destroyed and replaced). |
Beta Was this translation helpful? Give feedback.
-
Enums Might be useful to have a function say int() which outputs 0,1,2,3 for different enum labels. And one say str() which outputs the relevant tag as a string for debugging.
You could achieve this (probably) with a "pointer" being a pair of - a link to the relevant array function (jump table) and the length of the array. From there you can dynamically compute the jumps required. Length can also be used for range checks. Structs would indeed be nice to have. |
Beta Was this translation helpful? Give feedback.
-
@tom-leys Thanks for your feedback! Having these conversations helps really a lot. I think it is time I flesh out my objectives explicitly here:
This makes sense, and having a main function would automatically enforce a nice structure of declarations, but I don't want to have to support two different ways of doing this. Given the choice I'll opt for global scope main code. A small plus is that variable names in functions are mangled, while variable names outside functions are not, which is consistent and feels intuitive. Code blocks will allow you to enclose the main code in
As there would be no code outside of main, global variables would have to be declared anyway - there would be no other way to put them there. So having to declare globals explicitly seems natural now.
External variables represent memory cell/bank slots. More about them is here: Variables and constants. In Mindcode as designed by @francois, no variables were declared, even the external ones. So they needed the prefix to be identifiable. I still kinda like it - they're such a different beasts that having them stand out in the code seems useful to me. I'm now tending towards declaring them explicitly, in which case the prefix can be dropped. Again, users can choose whatever convention they want for them.
I originally wanted to do this, but as there isn't any string manipulation in mlog whatsoever, everything needs to be resolved at the compile time. I couldn't handle the However, maybe I could keep just A global replace of
I'll keep
There's a particular problem, though: say I use I might allow to declare linked blocks explicitly, say
Yes, I've run into this as well. I don't think this can be resolved at the language level, but I'll add a chapter to tips and tricks. I usually load blocks dynamically to solve both delayed building and dynamic block linking (where the name isn't fixed anyway) issues:
|
Beta Was this translation helpful? Give feedback.
-
Mindcode modules: moved to #149. |
Beta Was this translation helpful? Give feedback.
-
I'll create issues for individual topic where some concrete idea has already emerged. Relevant passages in the original post will be replaced with a reference to the specific issue. |
Beta Was this translation helpful? Give feedback.
-
Updated the initial post to reflect current developments. |
Beta Was this translation helpful? Give feedback.
-
I've updated the priorities a bit. I want to introduce processor-variables backed arrays as soon as possible, since I believe that currently it is a feature which will bring the greatest benefit. While pondering the implementation I realized that arrays of records/structures would be very useful performance-wise. Storing structure members in several separate variables would mean that retrieving each member at given index would require separate function call, while accessing a structure in an array would require just one function call. There will still be difficulties and optimization challenges, but structures will have additional benefits, e.g. for function return values. |
Beta Was this translation helpful? Give feedback.
-
Preamble
This issue quite comprehensively describes planned upcoming changes to Mindcode. I'll be very grateful for any comments on them (both on the planned syntax and the mechanisms behind that). I don't have any experience in designing languages, and it is quite possible that a lot of things I envision could be designed better.
General changes to the existing syntax are described here: #148. This issue also contains list of features that were completed as of Release 2.4.0.
Change in default language target
Default language target will be bumped to ML8A - corresponding to Mindustry 8 Logic - as soon as Mindustry 8 is released.
Itinerary of planned changes
The following chapters contain individual planned changes. There isn't any fixed implementation schedule, but the imagined order of implementation is about this:
out
variables in for-each loops and output function parametersfallthrough
andyield
in case expressionsExplicit variable declarations
See #150.
External variables
In addition to allocating a heap in a linked block or a variable, it will be possible to allocate it in a parameter variable as well.
In addition to the current way of allocating global variables on a heap, it will be possible to allocate global variables at specific index outside the heap and possibly in a different memory block. The index must be a compile time integer constant.
It will also be possible to allocate an array of a fixed size, either on a heap, or in a different memory block. Arrays will always be indexed starting at 0. If the array's position in a memory block doesn't start at 0, the indexes will be adjusted as necessary when accessing the array.
It will be allowed to allocate two external variables pointing to the same memory slot, at it is impossible to prevent this happening generally (memory blocks can be obtained through getlink and other means, and there's no general algorithm to determine whether two blocks obtained programmatically point to the same memory block or not). So, for example, the following will be legal:
Support for dynamic indexes in external variables might be added one day. It shouldn't require changes to the syntax.
Processor-variables backed arrays
It will be possible to create a fixed-size array, where individual elements are kept in a processor variable. When accessing an element at a constant index, the variable will be accessed directly. When accessing an element using a dynamic index, the proper variable for the element will be read/written using a compiler-generated function.
Arrays will be global and static: the array cannot be assigned to a variable and cannot be passed as an argument to a function, except as a vararg argument (and maybe additional support to inline functions).
Example:
Assignments between arrays, especially when they have the same size, might be supported.
Array iteration loops will be supported:
Code blocks
It will be possible to declare a code block using the following syntax:
The main use case for this is to create a main code block, and to apply a compiler/optimizer option to the entire code block.
Include files/modules/namespaces
See #149.
Refactoring of Mindustry Logic functions
Implementing namespaces would allow better organization of Mindcode functions into related groups (e.g. separate namespace for world-processor functions).
Current system would be accessible in one namespace for backwards compatibility, new systems would come with their own namespaces.
Type system
Types will provide more information to the compiler/optimizer, type checking and enforcing will make programs safer.
Possible types:
void
: no value (A void function). Type of expressions/statements not having a value, e.g. while loop.var
: corresponds exactly to the Mindustry Processor variableint
: an integer (Mindustry Processor long)float
: a float (Mindustry Processor double)string
block
: a block. Linked variable would be of typeblock
.unit
: a unitblock_t
: block type -@switch
unit_t
: unit type -@mono
item
: item type -@coal
liquid
: liquid type -@water
property
: sensable property -@totalItems
Records/structures
Record will be a user-defined type consisting of members. Each member of a record/structure will be stored in a processor variable.
Nested records will probably be supported.
Main benefits:
Possibly a Vector record consisting of x, y members, with arithmetic operations defined on them
Local compiler options
It will be possible to apply different compiler/optimizer options to a block of code using the
#set local
directive:List of options that can be applied this way will be limited. An example is the
goal
option.The
#set local
directive will be applied to the next expression, for example to a loop. Thereforewill prevent the loop from being unrolled, but will allow other loops in the program to be unrolled. Code blocks can be used to apply the directive to several expressions at once. Using
#set local
before function declaration will change the settings for the entire function.Short-circuit boolean evaluation
Short-circuit boolean evaluation will be introduced into the language in some form. The current plan is to use
and
andor
operators for short-circuiting, and&&
and||
operators for full boolean evaluation. If you know you want to avoid short-circuiting in some expression, it is advisable to use the latter form.fallthrough
andyield
in case expressionsThe
fallthrough
keyword can be used in awhen
branch incase
expression to transfer the control to the next branch:When
number
is 1, the code will print "onetwo". Whennumber
is 2, the output will be just "two".fallthrough
can be used anywhere in thewhen
branch, for example inside anif
statement.The
yield
keyword can be used in a when branch in case expression to terminate the evaluation of the case expression and provide the resulting value (the code will output "even"):The value after
yield
will be optional; when not given, the resulting value of the case expression will benull
.Memory model
Optimizations of external variables are planned for the future. These should prevent unnecessary reads and writes from/to memory blocks. To assist the compiler with these optimizations, it will be possible to assign a memory block or a variable one of three possible memory models:
volatile
: this means that the external memory may be modified by another processor, and therefore reads and writes from/to this memory must not be eliminated from the code.aliased
: this means that the external memory will not be modified by another processor, but the memory block might be accessed using two different variables.restricted
: this means the memory block will never be accessed using two different variables.A memory model can be assigned to a memory block, or to a concrete slot inside a memory block. Model assigned to a slot has precedence over model assigned to the whole block. Conflicting assignment on a block or slot level cause compilation error. Declaring a variable as restricted when it can be inferred from a code it is actually aliased causes compilation error.
Example:
Enums
It will be possible to declare enums using the
enum
keyword:Mindcode will assign values to the enums as it sees fit. There are no guarantees on the numbers whatsoever, except preserving the declaration order. They could be instruction addresses inside a case expression, for example, if there's just one case expression.
Mindcode will provide functions to access enum properties (e.g.
element.name
,element.next
,element.previous
). They might be a bit costly to use.Support for enums in list iteration Loops:
for i in enum_name
.Function pointers
Function pointers would allow storing a function address in a variable and calling the function through that variable. The internal mechanism is briefly described in the road map.
Assigning a function address to a variable could happen in several ways:
Range checking operator in boolean expressions
The case expression supports specifying ranges in
when
branches. Similarly, thein
operator will allow testing ranges in boolean expressions:Beta Was this translation helpful? Give feedback.
All reactions