Giter Site home page Giter Site logo

cardillan / mindcode Goto Github PK

View Code? Open in Web Editor NEW
67.0 6.0 14.0 5.83 MB

A high level language for Mindustry Logic and Mindustry Schematics.

Home Page: http://mindcode.herokuapp.com/

License: MIT License

Shell 0.07% Java 99.07% ANTLR 0.69% Dockerfile 0.02% PowerShell 0.13% Procfile 0.01% Batchfile 0.02%
compiler mindustry mindustry-logic mindustry-schematics mlog

mindcode's Introduction

Project Map

Mindcode & Schemacode

Welcome to Mindcode, a high-level language for Mindustry. Mindustry is a game in the tower-defense genre. Mindustry added Logic in late 2020. Logic is a programming language, closer to assembly than a high-level language. Mindcode aims to make Mindustry programming easier for everyone.

Mindcode focuses of the following priorities:

  • Keeping terminology and naming convention close to Mindustry Logic.
  • Providing language constructs that are not prohibitively expensive given the astonishingly slow speeds and limited instruction space of Mindustry processors.
  • Employing various optimizations to produce efficient code.

Note

Please have a look at a poll about introducing compulsory semicolons to the Mindcode syntax here.

Schemacode is a specialized definition language designed for creating a complete Mindustry schematic as a text file. Schematics builder can be used to turn these definition files directly into Mindustry schematics, either as a binary .msch file, or as a text. Processors can be included in these schematics, complete with the code (specified in Mindcode or Mindustry Logic language) and linked blocks.

Latest development

Some of the latest enhancements to Mindcode are:

  • Loop Hoisting, a new optimization designed to pull invariant code out of loops to avoid repeated execution where possible.
  • All instructions added in Mindustry 7 build 146 are now supported.

A changelog is now maintained for releases.

Using Mindcode

Mindcode is available at http://mindcode.herokuapp.com/. Write some Mindcode in the Mindcode Source Code text area, then press the Compile button. The Mindustry Logic text area will contain the Logic version of your Mindcode. Copy the compiled version. Back in Mindustry, edit your processor, then use the Edit button in the Logic UI. Select Import from Clipboard. Mindustry is now ready to execute your code.

It is also possible to build Mindcode locally (see the Development section), and use the command line tool to compile your files, even copying the compiled code into the clipboard automatically if desired.

Mindcode Syntax

Please read the syntax document for a complete description of Mindcode syntax. The samples in the src/main/resources/samples directory are compiled on every test run and are thus always up-to-date with the most-recent version of Mindcode. If you programmed in any high-level language, you should feel right at home.

VS Code syntax highlighting

@schittli kindly contributed a VS Code syntax highlighter.

screenshot of Visual Studio Code, with Mindcode syntax highlighting

Download the extension from the Visual Studio marketplace. I'm not sure how well the extension supports latest additions to Mindcode.

IntelliJ IDEA syntax highlighting

IntelliJ IDEA (even the Community edition) can be easily configured for basic Mindcode syntax highlighting.

  • Go to File / Settings, or press Ctrl-Alt-S
  • Navigate to Editor / File types
  • Create new file type
    • Name: Mindcode
    • Description: Mindcode source file
    • Line comment: // (leave Only at line start unchecked)
    • Block comment start/end: leave empty
    • Hex prefix: 0x
    • Number postfixes: leave empty
    • Keywords: paste Mindcode keywords to the first list. Optionally, paste Mindustry Logic object names to the second list.
    • Ignore case: leave unchecked.
  • Assign a file extension *.mnd
Show full list of Mindcode keywords.
allocate
and
break
case
const
continue
def
do
else
elsif
end
false
for
heap
if
in
inline
loop
not
null
or
return
sensor
stack
then
true
when
while
Show full list of Mindustry Logic object names.
@additive-reconstructor
@aegires
@afflict
@air
@air-factory
@alpha
@ammo
@ammoCapacity
@anthicus
@antumbra
@arc
@arkycite
@arkycite-floor
@arkyic-boulder
@arkyic-stone
@arkyic-vent
@arkyic-wall
@arkyid
@armored-conveyor
@armored-duct
@atmospheric-concentrator
@atrax
@avert
@barrier-projector
@basalt
@basalt-boulder
@basic-assembler-module
@battery
@battery-large
@beam-link
@beam-node
@beam-tower
@beryllic-boulder
@beryllic-stone
@beryllic-stone-wall
@beryllium
@beryllium-wall
@beryllium-wall-large
@beta
@blast-compound
@blast-door
@blast-drill
@blast-mixer
@bluemat
@boosting
@boulder
@breach
@bridge-conduit
@bridge-conveyor
@bryde
@build-tower
@canvas
@carbide
@carbide-crucible
@carbide-wall
@carbide-wall-large
@carbon-boulder
@carbon-stone
@carbon-vent
@carbon-wall
@char
@chemical-combustion-chamber
@cleroi
@cliff
@cliff-crusher
@coal
@coal-centrifuge
@collaris
@color
@combustion-generator
@command-center
@commanded
@conduit
@config
@configure
@conquer
@constructor
@container
@controlled
@controller
@conveyor
@copper
@copper-wall
@copper-wall-large
@core-acropolis
@core-bastion
@core-citadel
@core-foundation
@core-nucleus
@core-shard
@core-zone
@corvus
@counter
@crater-stone
@crawler
@cryofluid
@cryofluid-mixer
@crystal-blocks
@crystal-cluster
@crystal-floor
@crystal-orbs
@crystalline-boulder
@crystalline-stone
@crystalline-stone-wall
@crystalline-vent
@cultivator
@cyanogen
@cyanogen-synthesizer
@cyclone
@cyerce
@dacite
@dacite-boulder
@dacite-wall
@dagger
@dark-metal
@darksand
@darksand-tainted-water
@darksand-water
@dead
@deconstructor
@deep-tainted-water
@deep-water
@dense-red-stone
@differential-generator
@diffuse
@diode
@dirt
@dirt-wall
@disassembler
@disperse
@disrupt
@distributor
@door
@door-large
@dormant
@duct
@duct-bridge
@duct-router
@duct-unloader
@dune-wall
@duo
@eclipse
@efficiency
@electric-heater
@electrolyzer
@elude
@emanate
@empty
@enabled
@eruption-drill
@evoke
@exponential-reconstructor
@ferric-boulder
@ferric-craters
@ferric-stone
@ferric-stone-wall
@firstItem
@fissile
@flag
@flare
@flux-reactor
@force-projector
@foreshadow
@fortress
@fuse
@gamma
@graphite
@graphite-press
@graphitic-wall
@grass
@ground-factory
@hail
@health
@heat
@heat-reactor
@heat-redirector
@heat-router
@heat-source
@horizon
@hotrock
@hydrogen
@hyper-processor
@ice
@ice-snow
@ice-wall
@illuminator
@impact-drill
@impact-reactor
@impulse-pump
@incinerator
@incite
@interplanetary-accelerator
@inverted-sorter
@item-source
@item-void
@itemCapacity
@junction
@kiln
@lancer
@large-constructor
@large-logic-display
@large-payload-mass-driver
@large-plasma-bore
@large-shield-projector
@laser-drill
@launch-pad
@lead
@legacy-mech-pad
@legacy-unit-factory
@legacy-unit-factory-air
@legacy-unit-factory-ground
@liquid-container
@liquid-junction
@liquid-router
@liquid-source
@liquid-tank
@liquid-void
@liquidCapacity
@locus
@logic-display
@logic-processor
@lustre
@mace
@magmarock
@malign
@mass-driver
@maxHealth
@mech-assembler
@mech-fabricator
@mech-refabricator
@mechanical-drill
@mechanical-pump
@mega
@meltdown
@melter
@memory-bank
@memory-cell
@mend-projector
@mender
@merui
@message
@metaglass
@metal-floor
@metal-floor-damaged
@micro-processor
@mineX
@mineY
@mining
@minke
@minute
@molten-slag
@mono
@moss
@mud
@multi-press
@multiplicative-reconstructor
@name
@naval-factory
@navanax
@neoplasia-reactor
@neoplasm
@nitrogen
@nova
@obviate
@oct
@oil
@oil-extractor
@omura
@ore-crystal-thorium
@ore-wall-beryllium
@ore-wall-thorium
@ore-wall-tungsten
@overdrive-dome
@overdrive-projector
@overflow-duct
@overflow-gate
@oxidation-chamber
@oxide
@oxynoe
@ozone
@parallax
@payload-conveyor
@payload-loader
@payload-mass-driver
@payload-router
@payload-source
@payload-unloader
@payload-void
@payloadCount
@payloadType
@pebbles
@phase-conduit
@phase-conveyor
@phase-fabric
@phase-heater
@phase-synthesizer
@phase-wall
@phase-wall-large
@phase-weaver
@pine
@plasma-bore
@plastanium
@plastanium-compressor
@plastanium-conveyor
@plastanium-wall
@plastanium-wall-large
@plated-conduit
@pneumatic-drill
@poly
@pooled-cryofluid
@power-node
@power-node-large
@power-source
@power-void
@powerCapacity
@powerNetCapacity
@powerNetIn
@powerNetOut
@powerNetStored
@precept
@prime-refabricator
@progress
@pulsar
@pulse-conduit
@pulverizer
@pur-bush
@pyratite
@pyratite-mixer
@pyrolysis-generator
@quad
@quasar
@quell
@radar
@range
@red-diamond-wall
@red-ice
@red-ice-boulder
@red-ice-wall
@red-stone
@red-stone-boulder
@red-stone-vent
@red-stone-wall
@redmat
@redweed
@regen-projector
@regolith
@regolith-wall
@reign
@reinforced-bridge-conduit
@reinforced-conduit
@reinforced-container
@reinforced-liquid-container
@reinforced-liquid-junction
@reinforced-liquid-router
@reinforced-liquid-tank
@reinforced-message
@reinforced-payload-conveyor
@reinforced-payload-router
@reinforced-pump
@reinforced-surge-wall
@reinforced-surge-wall-large
@reinforced-vault
@repair-point
@repair-turret
@retusa
@rhyolite
@rhyolite-boulder
@rhyolite-crater
@rhyolite-vent
@rhyolite-wall
@ripple
@risso
@rotary-pump
@rotation
@rough-rhyolite
@router
@rtg-generator
@salt
@salt-wall
@salvo
@sand
@sand-boulder
@sand-floor
@sand-wall
@sand-water
@scathe
@scatter
@scepter
@scorch
@scrap
@scrap-wall
@scrap-wall-gigantic
@scrap-wall-huge
@scrap-wall-large
@second
@segment
@sei
@separator
@shale
@shale-boulder
@shale-wall
@shallow-water
@shield-projector
@shielded-wall
@ship-assembler
@ship-fabricator
@ship-refabricator
@shock-mine
@shockwave-tower
@shootX
@shootY
@shooting
@shrubs
@silicon
@silicon-arc-furnace
@silicon-crucible
@silicon-smelter
@size
@slag
@slag-centrifuge
@slag-heater
@slag-incinerator
@small-deconstructor
@smite
@snow
@snow-boulder
@snow-pine
@snow-wall
@solar-panel
@solar-panel-large
@sorter
@space
@spawn
@spectre
@speed
@spiroct
@spore
@spore-cluster
@spore-moss
@spore-pine
@spore-pod
@spore-press
@spore-wall
@steam-generator
@stell
@stone
@stone-wall
@sublimate
@surge
@surge-alloy
@surge-conveyor
@surge-crucible
@surge-router
@surge-smelter
@surge-tower
@surge-wall
@surge-wall-large
@swarmer
@switch
@tainted-water
@tank-assembler
@tank-fabricator
@tank-refabricator
@tar
@team
@tecta
@tendrils
@tetrative-reconstructor
@thermal-generator
@thorium
@thorium-reactor
@thorium-wall
@thorium-wall-large
@thruster
@tick
@time
@timescale
@titan
@titanium
@titanium-conveyor
@titanium-wall
@titanium-wall-large
@totalItems
@totalLiquids
@totalPower
@toxopid
@tsunami
@tungsten
@tungsten-wall
@tungsten-wall-large
@turbine-condenser
@type
@underflow-duct
@underflow-gate
@unit
@unit-cargo-loader
@unit-cargo-unload-point
@unit-repair-tower
@unloader
@vanquish
@vault
@vela
@vent-condenser
@vibrant-crystal-cluster
@water
@water-extractor
@wave
@white-tree
@white-tree-dead
@world-cell
@world-message
@world-processor
@x
@y
@yellow-stone
@yellow-stone-boulder
@yellow-stone-plates
@yellow-stone-vent
@yellow-stone-wall
@yellowcoral
@zenith

Mindustry Logic References

If you don't know much about Mindustry Logic, you can read more information about them here:

There also exists a VSCode syntax highlighter for Mindustry Logic.

Development

There are two options for getting Mindcode up and running on your own machine. Using Docker, or running it natively:

With Docker & Docker Compose

docker-compose up --build

It can take a few minutes to download and compile all the required parts the first time you run this, but subsequent runs will be a lot faster.

The Mindcode UI will now be running on localhost, port 8080. Visit http://localhost:8080/ to interact with it.

Native installation

  1. Install Java 17+, Maven 3.6, and PostgreSQL
  2. Create a database in PostgreSQL named mindcode_development

Note: Docker configuration has been updated to allow running Mindcode in Docker alongside a local PostgreSQL installation.

Windows

Set environment variables with the PostgreSQL connection parameters. You can set them by running the following commands in the console:

SET SPRING_DATASOURCE_URL=jdbc:postgresql://localhost/mindcode_development
SET SPRING_DATASOURCE_USERNAME=postgres_username
SET SPRING_DATASOURCE_PASSWORD=postgres_password

You also need to set a JAVA_HOME variable pointing to the directory containing your Java 17 installation, for example (the exact path depends on the distribution and version of Java you've installed):

SET JAVA_HOME=C:\Program Files\Eclipse Adoptium\jdk-17.0.6.10-hotspot

(You can also set all these variables permanently in the System Properties dialog, in the Advanced tab, after pressing the Environment Variables... button.)

Then, using the same console window, run:

mvnw.cmd install

to build the app, and then

bin\webapp.bat

The Mindcode UI will now be running on localhost, port 8080. Visit http://localhost:8080/ to interact with it.

Linux

Set environment variables with the PostgreSQL connection parameters:

export SPRING_DATASOURCE_URL=jdbc:postgresql://localhost/mindcode_development
export SPRING_DATASOURCE_USERNAME=postgres_username
export SPRING_DATASOURCE_PASSWORD=postgres_password

Then run:

./mvnw install

from the project root to build the app, and then

bin/run-local

The Mindcode UI will now be running on localhost, port 8080. Visit http://localhost:8080/ to interact with it.

IDE

To run the application for your IDE, set the environment variables as described above (some IDEs allow to set them just in the IDE) and set the startup class to info.teksol.mindcode.webapp.WebappApplication. When you run or debug the project, the Mindcode UI will now be running on localhost, port 8080. Visit http://localhost:8080/ to interact with it.

Contributing

The compiler was written in a Test-Driven Development fashion. If you can, please look in src/main/test and attempt to emulate an existing test, so that we can prove that your proposal works as intended.

Tests in the info.teksol.mindcode.processor package are particularly useful. They compile a script and run it on an emulated processor, comparing values produced by print instructions with expected ones. The processor emulator cannot execute instructions interfacing with the Mindustry world - except Memory Bank and MemoryCell - but it can process all kinds of Mindcode control elements. Implementing some more complicated algorithms using loops, conditional statements and/or function helps tremendously. (The emulated processor runs much faster than Mindcode processors, so even more complicated algorithms are feasible to run.)

Roadmap

Or perhaps a wish-list, can be found here.

License

MIT. See LICENSE for the full text of the license.

mindcode's People

Contributors

alexkarezin avatar b-gran avatar cardillan avatar cyclotron3k avatar daviscodesbugs avatar dependabot[bot] avatar francois avatar iotabread avatar limonovthesecond2 avatar trebledj avatar

Stargazers

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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

mindcode's Issues

Ternary expression generated wrong

ore1 = @lead
ore2 = @coal
container = container1
    o1 = container.ore1
    o2 = container.ore2
    ore = o1>o2? ore2 : ore1

generated

sensor o1 container @ore1
sensor o2 container @ore2
jump 16 notEqual o2 true
set __tmp9 ore2
jump 17 always 0 0
set __tmp9 ore1
op greaterThan ore o1 __tmp9

It should be something like

sensor o1 container @ore1
sensor o2 container @ore2
jump 16 lessThanEq o1 o2
set ore ore2
jump 17 always 0 0
set ore ore1

Command-line compiler and optimizers restructuralization

I plan to implement the following restructuralizations as part of this issue:

  • Move the CompileMain and CompilerFacade from the webapp project to the compiler project. I believe the command-line compiler doesn't belong to the webapp project. @francois, I'm not sure you won't object to the CompilerFacade being moved over there as well - if you do, let me know, I'll refactor it. There aren't any unit tests affected by this change. (The main motivation is that the webapp project has unit tests that fail when PostgreSQL isn't running, while the compiler project builds without any problem.)
    • mindcode.bat will be created to run the command line compiler in Windows,
    • mindcode script for Linux will be updated to reflect the change of path to compiler jars -- the change is simple, but I won't be able to test it after the change.
  • Restructure the creation of logic instruction pipeline. I'll create an enum with constants representing individual optimizers and a method to return an instance of the optimizer. The pipeline will be built from all existing enums in reverse order. This will allow us to:
    • easily reorder the optimization pipeline if needed,
    • add support for parametrized activation/deactivation of individual optimizers (I'll implement command line support first, as I'm not a front-end guy).
    • easily build custom pipelines for unit tests,
    • allow error, warning and informational messages to be generated by the optimizers and then displayed. The idea is to allow dead code elimitator to display a list of eliminated variables, and a list of variables that are read, but never written to. As we lack formal checks for variable declaration, this will help catch typos in variable names. Again, I'll add support to the command-line compiler first.

Parameters not passed to inline functions

The following code

inline def d(n)
    n
end
print(1 < d(2))
printflush(message1)

produces

op lessThan __tmp1 1 __fn0_n
print __tmp1
printflush message1
end

The FunctionParameterOptimizer mistakenly removes assignment to function parameter __fn0_n.

REACTOR = reactor1 - If dynamically assigned, the variable remains != null when the block is destroyed / removed from the map

Good evening

I am afraid that this is a problem of the Mindustry programming language... I report it anyway. Either as information for other mindcode users or maybe as a bug report.

The summarized behavior:

  1. At runtime, the code assigns a Block (e.g. message1) to a variable
  2. Later, the assigned block is destroyed in the Game (or just removed :-))
  3. The Variable in step 1 does not reflect the destroyed block. The Variable should now be == null, but it is not

Example

I created this Test scenario:

The Map contains:

image

  1. The Processor
  2. message1 (just to display debug messages)
  3. switch1
  4. message1 and switch1 are linked to the processor

The test runs like this

  1. On startup, the code searches all blocks which are linked to the processor

  2. The found Switch-Block is dynamically assigned to the variable SwitchBlock

  3. message1 just displays the value of the variable SwitchBlock: is it == null or != null?
    image

  4. After some seconds, the SwitchBlock is removed from the Map

  5. Now, we have the issue: The variable SwitchBlock does not reflect the removed Switch-Block: it is still not null:
    image

I tested it using this Code:

// message1 is used to display Debug Messages
Msg1Block = message1

// Will be executed *just once*
if ProcessorLoop == null
	ProcessorLoop = 0
	
	// Search the Switch-Block, which is assigned to the Processor
 	idxN = 0
 	thisBlock = getlink(idxN)
 	while thisBlock != null
 		if thisBlock.type == @switch
 			// We've found the Switch-Block - assign it to a Variable
 			SwitchBlock = thisBlock
 		end
 		idxN += 1
 		thisBlock = getlink(idxN)
 	end
end

// Just display a heartbeat :-)
print("\n", ProcessorLoop)

// During this test, the referenced SwitchBlock will be removed from the Game-Map
// therefore, SwitchBlock *should* get null - but it does not:
if SwitchBlock == null
	print ("\nTest result: SwitchBlock == null")
else
	print ("\nTest result: SwitchBlock != null")
end


printflush(Msg1Block)

// Count Processor Loops
ProcessorLoop += 1

Thanks a lot, kind regards,
Thomas

Direct Access to the Sensor() function would be useful

Good evening

This is a nice to have suggestion.

It's great that mindcode abstracts the mindustry logic, but with the sensor function, it would be great if one could call it directly. The background is that sometimes things are computed at runtime. For example, we dynamically calculate which resource is required. Next, we must know if a building contains this resource:

// At runtime, we have calculated that we need lead
RequiredRessource = @lead

// How can we test if a building contains this resource?
// Something like this would be great:
RessourceStock = sensor(factory1, RequiredRessource)

// The actual solutions is verbose:
RscStock = case RequiredRessource
when @silicon
  factory1.silicon
when @lead
  factory1.lead
โ€ฆ
end

I find the possibility that abstraction can also make things more cumbersome interesting. I was never aware of that before.

Optimization removes an assignment to variable

The following code

alive = @unit.dead === 0
if alive
  print(alive)
end

compiled with optimization produces:

sensor __tmp0 @unit @dead
jump 4 notEqual __tmp0 0
print alive
jump 4 always 0 0
end

A correct result would be more akin to this:

sensor __tmp0 @unit @dead
op strictEqual alive __tmp0 0
jump 4 equal alive 0
print alive
jump 4 always 0 0
end

I don't think it's caused by dead code elimination, as alive is clearly used. Other optimization probably should only remove variables that are created by the compiler itself (the __tmp ones) -- that could be an easy check to add.

I'll try to fix the issue, I'm putting it here for reference.

(Edited to fix the examples.)

PrintMerger merges across pritflush

The PrintMerger optimizer merges print instructions across printflush() calls:

print("foo")
printflush(message1)
print("bar")
printflush(message2)

produces

printflush message1
print "foobar"
printflush message2
end

Thanks a lot! โ€ข Question: Heap/Stack: Can we define the Memory-Cell/-Bank Block name in a Variable?

Hello Franรงois

thank you very much for your great work!,

The syntax of the Mindustry Logic and the behavior of the commands is often terrible and so it is ingenious to have mindcode to fix the behavior of the Mindustry Logic commands :-)

It's great to have SW developers who knows how to program a compiler :-)

I have a question:

Heap/Stack: Can we define the Memory-Cell/-Bank Block name in a Variable?

Here, we define the name of the assigned Memory-Cell/-Bank Block to cell1:

allocate heap in cell1[0 ... 16], stack in cell1[16 ... 64]

Because the name of this block can change in the game, it would be useful if we could something like this:

memBlockName = "cell2"
allocate heap in memBlockName[0 ... 16], stack in memBlockName[16 ... 64]

Because this code does not work, I guess it is not possible,

therefore I share one idea I had:

You already use $ as a prefix for global variables. Maybe you could define a prefix that defines a Text-Macro (which just replaces a keyword by its replacement string before the Code will be compiled), e.g.

// The Compiler will replace `#memBlockName` with `cell2` before it startes compiling the code
#memBlockName = "cell2"
allocate heap in memBlockName[0 ... 16], stack in #memBlockName[16 ... 64]

Maybe a text macro function would open more useful possibility... But maybe it is also a stupid idea.

Thanks a lot, kind regards,
Thomas

Elsif does not compile without Else branch

This code doesn't compile:

if a 1 elsif b 2 end

whereas this does:

if a 1 elsif b 2 else 3 end

The behavior is identical with and without optimization. Is it designed like this, or should the else branch be optional after the elsif branch?

uradar output variable is replaced with __tmp

uradar's result variable gets replaced by a temporary variable - which is promptly never used again, meaning I need to edit the output code to use it. I can't use the optimizer, since uradar disappears completely with it.

ubind(@mono)
uradar(enemy, any, any, distance, 0, 1, result)
print(result.x)
printflush(message1)

...will compile into uradar enemy any any distance __tmp0 __tmp1 __tmp2. __tmp2 is never seen again - result is correctly used in later instructions though.

By the way, I think the reason it gets removed in optimization is because __tmp2 flags the whole line as redundant since it's never referenced again, even through it shouldn't be there in the first place. It's not an issue with the optimizer, rather the compiler itself.

Optimization problem: eliminate jumps to the very next line

I compile this code:

CENTER = foundation1
if uradar(enemy,any,any,distance,0,1,rslt)
    if len(rslt.x - @unit.x,rslt.y - @unit.y) < 20
        end()
    end
end

if ulocate(damaged, dmgx, dmgy, dmgbuilding)
    approach(dmgx, dmgy, 5)
    target(dmgx, dmgy, true)
else
    approach(CENTER.x + 10 * sin(@tick), CENTER.y + 10 * cos(@tick), 2)
end

and the result contains the following lines:
jump 14 always 0 0
jump 15 always 0 0
They are both jumps to the very next line, and would be very unneccesary(and will slow down the processor).
They should be deleted by the optimizer.

Optimization should not replace strictEqual with notEqual

There's a problem in the ImproveConditionalJumps optimizer: it assumes that notEqual is the opposite operation to strictEqual, but that isn't true: notEqual coerces types, and won't therefore distinguish between null and 0.

It can lead to compiled code that doesn't work as intended. This code snippet:

if @unit.dead === 0
  print("alive")
end

is meant to print "alive" it the @unit variable is not null and its @dead property is equal to 0 (in other words, only if the unit is bound and alive). Optimized code, however, looks like this:

sensor __tmp0 @unit @dead
jump 4 notEqual __tmp0 0
print "alive"
jump 0 always 0 0
end

This will print "alive" even if there's no bound unit (and the @unit variable is therefore null).

I think ImproveConditionalJumps shouldn't modify strictEqual comparisons.

Mindustry Logic: Probably missing op commands

Good evening

I have tried to use the max() function and I've got this error:

a = max(1,2)
Don't know how to handle function named [max]

Therefore I have tested all Mindustry Logic op commands and I guess that those are missing: (I have created a task list if someone would like to fix them :-))

  • log(10)
    op log10 result param1 param2
  • 2d simplex noise
    op noise result param1 param2
  • length of a vector
    op len result param1 param2
  • Angle of a vector in degree
    op angle result param1 param2
  • max of two numbers
    op max result param1 param2
  • min of two numbers
    op min result param1 param2
  • Bitwise and
    op and result param1 param2
  • Bitwise xor
    op xor result param1 param2
  • Bit shift left
    op shl result param1 param2
  • Bit shift right
    op shr result param1 param2
  • Integer division
    op idiv result param1 param2

Kind regards,
Thomas

Feature request: break, next

I think it would be useful to have break and next implemented for loops. (Also, having loop implemented would be a bit cleaner than having to do while true all the time).

Thanks for your hard work!

If and While conditions incorrectly evaluated

The if expression generates jump to the else branch by comparing the value of the condition expression to true:

https://github.com/francois/mindcode/blob/d283fa5fd8bc880c66d629bd1c74ecc21574244b/compiler/src/main/java/info/teksol/mindcode/mindustry/LogicInstructionGenerator.java#L127

However, true in Mindustry 7 is a constant equal to 1. The else branch is therefore executed only if the condition evaluates to something other than 1. The expected behavior is that the else branch is executed when the condition evaluates to 0.

While expression has an identical issue.

The fix is easy:

pipeline.emit(new LogicInstruction("jump", elseBranch, "equal", cond, "false"));

This might require some changes to keep the optimizers responsive to the generated code, though.

Is missing: the `Bitwise not operator`

Good evening

Unfortunately, I had overlooked that mindode does not understand the bitwise not operator. The Mindustry Game calls it Flip in its GUI, but the Mindustry Logic command is called as usual op not:

set TestVar 0xf
op not Result TestVar b
print TestVar
print Result

// Printed output (as expected): 
15
-16

Thanks a lot, kind regards,
Thomas

Double quotes not allowed inside strings

Mindcode allows to use escaped double quotes in string literals, like this:

print("\"Hi!\"")

However, Mindustry Logic doesn't support double quotes in strings, escaped or not, in any form - it refuses pasted code completely if it contains them. Mindustry 7 replaces double quotes with single quotes when entered via user interface, so Mindcode was updated to replace double quotes inside strings with single quotes as well.

Do not use double quotes in strings, though, the support for them might be completely removed in the future.

If Custom Functions can return a number, then they are broken

Good evening @francois

if I understand Custom Functions the right way and they are able to return a result:

allocate stack in cell1
// Just return the number 123
def test(n)
  123
end

then Custom Functions are broken :-(

The following test code needs:

  • message1
  • cell1

The code just does:

  • Display "Initializing App"
  • Display a counter
  • If the call // res = test(1) is activated then the script stops working
allocate stack in cell1

// Just return the number 123
def test(n)
  123
end

print("\nInitializing App")
printflush(message1)

// If the following line is activated, then the script stops working
// res = test(1)

print("\ntest: ", res)

ProcessLoop += 1
print("\nProcessLoop: ", ProcessLoop)

printflush(message1)

Thanks a lot, kind regards,
Thomas

Missing boolean NOT operator

Currently, there isn't a boolean negation operator. Both negation operators that are currently defined (! and not) compile to an op flip/not instruction (the operator is called "flip" in GUI and "not" in Mindustry "assembly" language), which produces bitwise negation. This means that the following code

while not switch1.enabled
    print("Press the button to start...")
    printflush(message1)
end

won't work (switch1.enabled produces 1 when the button is pressed, and bitwise negation makes it into -2).

I propose creating a new unary operator ~ for bitwise negation, and repurposing both ! and not for boolean negation. However, this change could break existing code. @francois, shall I do this?

BREAK statement does not compile

Following code does not compile when break statement included. When commented out, compiles.

allocate stack in cell1

n = 0
reactor = getlink(n)
lowest = 1
while reactor != null
    print("type: ", reactor.type)
    if (reactor.type != @thorium-reactor)
        print("not reactor!")
        break
    end
    pct_avail = reactor.cryofluid / reactor.liquidCapacity
    lowest  = (pct_avail < lowest) ? pct_avail : lowest
    // print(n, ": pct_avail is: ",pct_avail,"\n")
    reactor.enabled = pct_avail >= 0.25   
    n += 1
    reactor = getlink(n)    
end
print("lowest is: ",lowest)
printflush(message1)

Nonstandard operator priorities

The priority of operators, as currently defined, is somewhat unusual. In Mindcode, we have:

  1. ternary operator ? :
  2. assignment =, /=, **=, -=, +=, *=
  3. exponentiation **
  4. unary negation !, not (see also #72 for this)
  5. multiplication *, /, \, %
  6. addition +, -
  7. shift <<, >>
  8. inequality comparison <, <=, >, >=
  9. equality comparison ==, !=, ===
  10. bitwise operation &, |, ^
  11. boolean and and, &&
  12. boolean or or, ||
  13. unary minus - (this isn't working at the moment, but let's ignore it)

Some of this can be a bit surprising. Compared to other languages, both the ternary operator and assignments are way too high. The most obvious example I stumbled upon is a = b > c ? 0 : 1, which gets compiled to a wee bit unusual a = b > (c ? 0 : 1). I don't use assignments as expressions much, but it would in all probability behave in similarly unexpected ways.

I propose changing the order to a more common one (I tried to do it according to Ruby):

  1. unary negation !, not, ~
  2. exponentiation **
  3. unary minus - (I'll try to make it work while at it)
  4. multiplication *, /, \, %
  5. addition +, -
  6. shift <<, >>
  7. bitwise and &
  8. bitwise or/xor |, ^ (this would mean splitting the binop_bitwise_op category in two, all others are just reordering of existing terms)
  9. inequality comparison <, <=, >, >=
  10. equality comparison ==, !=, ===
  11. boolean and and, &&
  12. boolean or or, ||
  13. ternary operator ? :
  14. assignment =, /=, **=, -=, +=, *=

@francois, as this is again a code-breaking change (hopefully last from me for some while), would you be ok with me implementing this?

Syntax is not clear: what does === mean?

Good afternoon

If users come directly to Mindcode and do not study the Mindustry Logic commands first, then the difference between == and === is unclear.

Could you please describe it in the Syntax?

Thanks a lot, kind regards,
Thomas

Info: VSCode Syntax Highligher for Mindcode published :-)

Hello @francois

VS Code Extension: mindcode (Syntax Highlighter for the Mindcode Syntax / Grammar)

Because I constantly had problems recognising the associated if / end in Mindcode and because of the copy/paste I constantly overlooked wrong == and =. Therefore I wrote a small extension for Mindcode for Visual Studio Code to highlight the Mindcode Syntax / Grammar.

image

I cannot promise that I will be able to maintain it reliably, but at least it is already a help today :-)

Here is a Screenshot of the Extension in VS Code:
image

Kind regards,
Thomas

Add information about the requirement of a database in the development section of README

As the title says, there isn't any information about the need of a PostgreSQL database in the README. The first time I tried to run it locally I spent some time trying to figure out why it didn't work.
Also, please add a note about the JDBC_DATABASE_URL in the readme, and how to pass credentials with it. (for example, adding ?user=<user>&password=<password> to the end of the URL)

Schematics builder

This is about a feature I plan to add to Mindcode, although it probably will take some time.

My typical workflow with Mindcode is currently centered around code development. I write and compile code, then paste it into some processor in one of my game saves to test it, and when it looks good I update the schematics containing the entire setup. This is sometimes bothersome - I might forget whether I've updated a particular schematics, I might need to build the schematics afresh to get the processor and all contained blocks to an initial state, and I need to type the name again when saving the updated schematics back.

Therefore I'm going to create a schematics builder. It would provide two main functionalities:

  • decompile existing schematics into a definition file.
  • build schematics from a definition file and Mindcode codes for any processors in the schematics.

Obviously there would be a command-line version for both of these functions, but since inputs and outputs are both texts, it is quite straightforward to add support to the webapp as well.

The usage I envision is this:

  1. Create the schematics in Mindustry.
  2. Export the schematics via clipboard or to a file.
  3. Decompile the schematics to obtain definition file.
  4. Insert/replace code for processors (decompiled schematics would contain mlog, schema builder would support both mlog and Mindcode to be bundled with processors)
  5. Compile the schema (Mindcode gets compiled automatically).
  6. Export the schematics to clipboard/file.

It should be possible - and quite simple - co create simpler schematics definition by hand from scratch, for example if the schematics should contain just the processor and a few basic objects (switch, message, memory cell).

Schematics structure isn't that complicated: basically it is a list of tiles with their positions, contained types, rotation and block type-specific configuration. Processor code and linked blocks are encoded in this configuration.

At this point I'm not sure what format or syntax to use for the schematics definition. Initial version should allow to:

  • specify schematics metadata (name, description, labels and tags)
  • place blocks at positions within the schema
  • assign symbolic names to certain blocks, used to specify links (not just processors, bridges and nodes need to get linked too)
  • specify configuration for each block that supports it (eg. sorter/unloader item selection, bridge targets)
  • specify blocks linked to processors and their names
  • specify Mindcode/mlog code to use with each processor (embedded to schema definition or, in case of the command line compiler, from a file)

Some future version might provide additional functionality to:

  • fill an area with blocks of the same type - eg. plastanium wall from (0,0) to (10,0)
  • provide additional means to define block layout, eg. left-to-right, top-to-bottom list or relative block placement
  • automatically link all named blocks to a processor
  • use identical code for several processors, with possible alterations (eg. by setting variables or constants that could be referenced from the shared code).

Custom Functions: Can they just return numbers?

Good evening

I tried to write a function that takes an object type like @ground-factory and then, it searches the linked blocks for this type. If found, it returns the object:

// Gets the block which is linked to the Processor
// Example:  linkedBlock = GetLinkedBlock(@ground-factory)
def GetLinkedBlock(type)
    res = null
 	idxN = 0
 	thisBlock = getlink(idxN)
 	while thisBlock != null && res === null
 		if thisBlock.type == type
            res = thisBlock
 		end
 		idxN += 1
 		thisBlock = getlink(idxN)
 	end
    res
end

The function returns 1 if a block is found.

Therefore, I guess that Custom Functions can just return numbers. Is that right?

Thanks a lot, kind regards,
Thomas

Function with no parameters causes compilation error.

Same function, when it has a parameter, even if unused, compiles. When it does not, error:
Syntax error: [@42,182:184='def',<5>,17:0] on line 17:0: mismatched input 'def' expecting <EOF>

Example code:

allocate stack in cell1

def delay(n, fps)
  _x = 0
  deadline = @tick + n * fps
  while @tick < deadline
    _x += 1
  end
end

def delay2(n)
  print("test")
end

def delay3()
  print("test")
end

delay(1, 60)
delay2(1)
delay3()

draw triangle doesn't compile properly

when triangle() is compiled, it cuts off the last variable. This breaks anything you try to draw using it. triangle(x1,y1,x2,y2,x3,y3) becomes draw triangle x1 y1 x2 y2 x3

Parameters in functions are not preserved

for example:

allocate stack in bank1

def T(n)
  if n>0
    print(n)
    T(n-1)
    print(n)
  end
end

T(2)
printflush(message1)

in this case, n becomes a local. I would expect the output of this program to be: 2112
but it is not, and it seems like n is being globally altered in the recursive function, which makes sense in Logic. The project seems to go though much effort to push return addresses and parameters on the stack, but locals are not maintained (it seems).

Am I understanding this wrong? Did I write my program above incorrectly to expect 2112 as the output? Although I haven't figured out why... the program actually prints "20" and that's it. If I do T(3) it will print "30" and that's it.

I am interested in understanding this better!

Originally posted by @TheAtomAnt in #34

uradar is outside an if statement it isn't getting registered.

input :
ubind(@mega)
uradar(enemy,any,any,true,distance,e)
if e
if e === null
pass
else
if distance < 25
target(e.x, e.y, true)
print("state: attacking,")
end
end
end

result :

ubind @mega
jump 11 notEqual e true
jump 4 notEqual e null
jump 10 always 0 0
jump 10 greaterThanEq distance 25
sensor __tmp7 e @x
sensor __tmp8 e @y
ucontrol target __tmp7 __tmp8 true 0 0
print "state: attacking,"

plus if you put uradar in a if statement it outputs this
uradar enemy any any true distance e __tmp0
jump 12 notEqual __tmp0 true

in uradar adding __tmp0 is invalid.

Full support for Mindustry v7

At this moment, the code produced by Mindcode compiler is mostly Mindustry v6 compatible, although a support for the wait instruction (which is v7 specific) was already added. For a full v7 support, the following is necessary:

  • Go through instruction set and find new instructions/operations (missing lookup was already reported - #57, but there's also stop, packcolor, some new math operations (asin, acos, atan) and perhaps more.
  • Implement support for new instructions in the compiler, update syntax documentation
  • Implement selection of target platform (V6, V7, V7 world processor)
  • Review example code to make sure it works with v7 too
  • Decide how to implement the status World processor instruction

Feature request: inline functions

Just an idea I had: inline functions. A bit like macros, they would replace the invocation of said function with a copy of the function.

This would serve two purposes: you wouldn't need a stack (or memory) and they could also return values other than numbers; they could also return objects.

Obviously, they couldn't be used in recursion.

allocate heap indirection: first access does not use the indirection if it is a write-access...

Good evening @francois

thanks a lot for the compiler optimizer!, it is very powerful, I am testing it...

I have noted a strange behaviour with indirect heap allocation:

If the 1st access to the heap reads data, then it uses the memory cell name cell3 and not HEAPPTR

// Source
HEAPPTR = cell3
allocate heap in HEAPPTR
$a = 1
$b = 2
$c = 3

// The compiler uses 'cell3' and not 'HEAPPTR' for the 1st command:
write 1 cell3 0
write 2 HEAPPTR 1
write 3 HEAPPTR 2
end

โ€ฆ but if the 1st command reads from the heap, then it works :-)

// Source
HEAPPTR = cell3
allocate heap in HEAPPTR
print($a)
$a = 1
$b = 2
$c = 3

// here, the compiler always use 'HEAPPTR' :-)
set HEAPPTR cell3
read __tmp1 HEAPPTR 0
print __tmp1
write 1 HEAPPTR 0
write 2 HEAPPTR 1
write 3 HEAPPTR 2
end

Thanks a lot, kind regards,
Thomas

Feature request: elsif

I tried to use elsif the other day. The compiler didn't throw any warnings or errors, and seemed to produce valid output, but the code didn't work.

Unless I made a mistake: I think it would be useful if either the compiler throws an error when it encounters elsif, or just implement it.

uradar() parameters wrong

this: uradar(enemy,any,any,distance,0,1,rslt)
generates this:

set __tmp0 0
uradar enemy any any distance __tmp0 __tmp1 __tmp2

where __tmp0 is supposed to be the order, __tmp1 should be the found output, and __tmp2 should be the dest.
However, the processor UI shows:
"target enemy and any and any order __tmp1 sort distance outpot __tmp2
That is, __tmp0 is ignored, and __tmp1 is used as the order input, which is NULL.
Furthermore, this, used as an if condition, jumps over the statement if (tmp2!=true),
it should actually be (tmp2===null).(Don't rely on automatic type conversion unless you must!)

Maybe "by design"?: `x = 1` creates two Mindustry Logic instructions

Good evening

my mindcode source created more than 1000 Lines of Mindustry Logic instructions (I have tried to create a generic Ressource manager for factories using drones).

Btw.: Good to know: If one tries to import more than 1000 Logic instructions into Mindustry, then the game will silently remove all instructions after the 1000th line :-( Therefore, one starts searching non-existent bugs...

This issue is maybe the result of the AST design, maybe it's a bug. I know programming compiler optimization is hard work and I was not able to find the source of this issue.

I report the problem anyway.
You can close it again as "it's too much work" - after all, we're talking about a game. ๐Ÿ™‚

This code:

x = 1

Creates two Mindustry Logic instructions:

set tmp0 1
set x tmp0
end

This optimization would make the compiled code twice as fast ๐Ÿ˜Š๐Ÿ˜‰

Thanks a lot, kind regards,
Thomas

getlink doesn't prevent parameter from being eliminated by optimization

Variables passed to the getlink function aren't considered used and are eliminated by optimization. For example, this code:

index1 = 1
index2 = 2
print(index2)
block0 = getlink(0)
block1 = getlink(index1)
block2 = getlink(index2)
print(block0)
print(block1)
print(block2)

compiled with optimization produces this output:

set index2 2
print index2
getlink block0 __tmp2
getlink block1 index1
getlink block2 index2
print block0
print block1
print block2
end

The output is missing set instructions to set index1 and __tmp2 variables. Setting index2 is included, as it figures in the print instruction.

A workaround is to use other operation to mark the variable as used, as demonstrated with the index2 variable.

Optimizer: Variables are not always initialized

Good morning

In the last two days, I suspected that my mind was slipping because I could no longer do the math. What a relief that the optimizer is probably a little too active in the variable initialization:

With this code:

x = 1
print("\n x: ", x)
print("\n x+x: ", x+x)

the compiler does not initialize the variable x, but it calculates afterwards with the uninitialized variables to get x+x:

set __tmp2 "\n x+x: "
print "\n x: "
print 1
op add __tmp3 x x
print __tmp2
print __tmp3
end

For the other readers: This small workaround (let's the compiler calculate the variable x by adding 0) solves the problem:

x = 1 + 0
print("\n x: ", x)
print("\n x+x: ", x+x)

Thanks a lot, kind regards,
Thomas

Optimization problem: eliminate temporary variables

This code(again):

CENTER = foundation1
if uradar(enemy,any,any,distance,0,1,rslt)
    if len(rslt.x - @unit.x,rslt.y - @unit.y) < 20
        end()
    end
end

if ulocate(damaged, dmgx, dmgy, dmgbuilding)
    approach(dmgx, dmgy, 5)
    target(dmgx, dmgy, true)
else
    approach(CENTER.x + 10 * sin(@tick), CENTER.y + 10 * cos(@tick), 2)
end

compiled, the last 3 lines are:

set __tmp27 2
ucontrol approach __tmp21 __tmp26 __tmp27 0 0
end

That is redundant: we can eliminate __tmp27 and run ucontrol approach __tmp21 __tmp26 2 0 0 instead.

Summing up: ANY not-changed temporary variables should be eliminated

PS: I know Java, and I can help make a PR(and make commits) if you want(altho I have very little time)

Compiling the mindcode project does not work since the release `"Prepared separate web application using Spring Web"`

Good morning @francois

Thanks a lot for all your work!,
I have tried to compile mindcode and used your Native installation guide. Until to the commit from Apr 12, 2021 Permit "indirect" sensor references compiling works perfectly:

git checkout 03ecee928ce36ae33303e2b37f428c2f63634adb
mvn clean compile exec:java
โ€ฆ everything Ok

But if I move to the next release, Github Commit: Prepared separate web application using Spring Web I get a compiler error:

git checkout ab1ad052502e8f24a404cc064ca9840316b38534
mvn clean compile exec:java

[INFO] Reactor Summary:                                                                                                                                    
[INFO]                                                                                                                                                     
[INFO] mindcode-compiler 1.0.0 ............................ FAILURE [  3.806 s]                                                                            
[INFO] webapp 0.0.1-SNAPSHOT .............................. SKIPPED                                                                                        
[INFO] Mindcode 1.0.0-SNAPSHOT ............................ SKIPPED                                                                                        
[INFO] ------------------------------------------------------------------------                                                                            
[INFO] BUILD FAILURE                                                                                                                                       
[INFO] ------------------------------------------------------------------------                                                                            
[INFO] Total time:  4.383 s                                                                                                                                
[INFO] Finished at: 2021-04-20T10:43:38+02:00                                                                                                              
[INFO] ------------------------------------------------------------------------                                                                            
[ERROR] Failed to execute goal org.codehaus.mojo:exec-maven-plugin:3.0.0:java (default-cli) on project mindcode-compiler: The parameters 'mainClass' for go
al org.codehaus.mojo:exec-maven-plugin:3.0.0:java are missing or invalid -> [Help 1]
โ€ฆ
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/PluginParameterException

Unfortunately, I don't know Maven at all, so it's a bit tricky to find the cause. About Java, I know the version is always important - it's fine, I guess:

java.exe -version
openjdk version "11.0.10" 2021-01-19
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.10+9)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.10+9, mixed mode)
C:\GitWork\Clones\GitHub\Game - Mindustry - mindcode\src [(ab1ad05...)]

mvn -version
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: C:\Portable\Java-Maven\apache-maven-3.6.3\bin\..
Java version: 11.0.10, vendor: AdoptOpenJDK, runtime: C:\Portable\Java-JDK\jdk-11
Default locale: de_CH, platform encoding: Cp1252
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

I can not attach a html-file with the output of mvn clean compile exec:java -e and mvn clean compile exec:java -e -X, therefore I have added a pdf, so that the colors are preserved - this is much easier to read.

mvn clean compile exec_java -e -X.html.pdf
mvn clean compile exec_java -e.html.pdf

I really hope this is just a minor config issue. Please let me know if I can send you more information.

Thanks a lot, kind regards,
Thomas

case โ€ฆ when โ€ฆ end: Commenting out 'when' makes the parser stumble

Good afternoon

I just have a side note: I have found a somewhat troublesome oddity in the parser, it stumbles if we comment out when:

// This Code generates:
// Syntax error: [@3,6:9='case',<3>,2:0] on line 2:0: mismatched input 'case' expecting
n = 1
case n
  // when 1
  when 2
    print(2)
end

Thanks a lot, kind regards,
Thomas

ulocate parameters generated wrongly

the input
ulocate(building,core, false, outx, outy,found,myCore)
generates
ulocate building core false @copper outx outy __tmp10 found
I think it should be
ulocate building core false @copper outx outy found, myCore

It looks like it has decided to hard code the found parameter of ulocate to a new temporary variable to use it as the result.

If thios is a deliberate descision then perhaps it could be documented in the syntax?

Unexpected parse of recursive ternary operators

The expression a = (b > c) ? 1 : (d > e) ? 2 : 3, when compiled, produces somewhat unexpected parse tree:

rest=Seq{
	rest=NoOp{},
	last=Assignment{
		var=VarRef{name='a'}, 
		value=IfExpression{
			condition=IfExpression{
				condition=BinaryOp{left=VarRef{name='b'}, op='>', right=VarRef{name='c'}},
				trueBranch=NumericLiteral{literal='1'},
				falseBranch=BinaryOp{left=VarRef{name='d'}, op='>', right=VarRef{name='e'}}
			}, 
			trueBranch=NumericLiteral{literal='2'},
			falseBranch=NumericLiteral{literal='3'}
		}
	}
}

Turns out the code was parsed this way (added parentheses for clarity)

a = ((b > c) ? 1 : (d > e)) ? 2 : 3,

while I'd expect it to be

a = (b > c) ? 1 : ((d > e) ? 2 : 3).

With huge help from Google, I found out that this change

 expression : indirectpropaccess                                                                 # indirect_prop_access
            | propaccess                                                                         # property_access
-           | cond=expression QUESTION_MARK true_branch=expression COLON false_branch=expression # ternary_op
+           | cond=expression QUESTION_MARK (true_branch=expression COLON false_branch=expression) # ternary_op
            | case_expr                                                                          # case_expression
            | if_expr                                                                            # if_expression

would do the trick (I took the idea from Stackoverflow - it is a different problem, but the parentheses in ternary op definition are there). I don't think I fully comprehend the change, but it seems to work pretty well. Specifically, a parse tree of this abomination

a = (x > y) 
	? (b > c) ? (d > e) ? 1 : 2 : (f > g) ? 3 : 4
	: (h > i) ? (j > k) ? 5 : 6 : (l > m) ? 7 : 8

works as I'd expect - it evaluates the "innermost" ternary operators first. For reference, the resulting parse tree is

rest=Seq{
	rest=NoOp{},
	last=Assignment{
		var=VarRef{name='a'},
		value=IfExpression{
			condition=BinaryOp{left=VarRef{name='x'}, op='>', right=VarRef{name='y'}},
			trueBranch=IfExpression{
				condition=BinaryOp{left=VarRef{name='b'}, op='>', right=VarRef{name='c'}},
				trueBranch=IfExpression{
					condition=BinaryOp{left=VarRef{name='d'}, op='>', right=VarRef{name='e'}},
					trueBranch=NumericLiteral{literal='1'},
					falseBranch=NumericLiteral{literal='2'}
				},
				falseBranch=IfExpression{
					condition=BinaryOp{left=VarRef{name='f'}, op='>', right=VarRef{name='g'}},
					trueBranch=NumericLiteral{literal='3'},
					falseBranch=NumericLiteral{literal='4'}
				}
			},
			falseBranch=IfExpression{
				condition=BinaryOp{left=VarRef{name='h'}, op='>', right=VarRef{name='i'}},
				trueBranch=IfExpression{
					condition=BinaryOp{left=VarRef{name='j'}, op='>', right=VarRef{name='k'}},
					trueBranch=NumericLiteral{literal='5'},
					falseBranch=NumericLiteral{literal='6'}
				},
				falseBranch=IfExpression{
					condition=BinaryOp{left=VarRef{name='l'}, op='>', right=VarRef{name='m'}},
					trueBranch=NumericLiteral{literal='7'},
					falseBranch=NumericLiteral{literal='8'}
				}
			}
		}
	}
}

@francois, shall I create a PR with this grammar modification? I'm asking specifically because it has the potential to break existing Mindcode code.

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.