dev icon indicating copy to clipboard operation
dev copied to clipboard

Implement Straight-Forward Operators of Two Variables

Open Aldrin-John-Olaer-Manalansan opened this issue 3 years ago • 14 comments

Please Implement a Syntax regarding Arithmetic, Comparison, and Logical Operators. I would be super convenient for us Programmers if we can do something like this instead of relying on opcodes all the time for this simple variable to variable operations.

// ~~~~~~~~~~~~~~~~~~~~ARITHMETIC Operations~~~~~~~~~~~~~~~~~~~~
int 3@(15@,8i) += 1@ // 005A: 3@(15@,8i) += 1@  // (int)
int 2@(0@,10i) -= 20@(1@,7i) // 2@(0@,10i) -= 20@(1@,7i)  // (int)
int 25@ *= 29@ // 006A: 25@ *= 29@  // (int)
int 1@ /= 17@(0@,4i) // 0072: 1@ /= 17@(0@,4i) // (int)

int $anyname += $addme(23@,7i) // 0058: $anyname += $addme(23@,7i) // (int)
int $anyname -= $subtractme // 0060: $anyname -= $subtractme // (int)
int $anyname(0@,5i) *= $multiplyme(0@,5i) // 0068: $anyname(0@,5i) *= $multiplyme(0@,5i) // (int)
int $anyname(14@,3i) /= $divideme // 0070: $anyname(14@,3i) /= $divideme  // (int)

int 17@($index1,5i) += $addme($index2,8i) // 005C: 17@($index1,5i) += $addme($index2,8i) // (int)
int 3@ -= $subtractme($GIRLFRIEND,6i) // 0064: 3@ -= $subtractme($GIRLFRIEND,6i)  // (int)
int 17@ *= $PARAMEDIC_MISSION_LEVEL // 006E: 17@ *= $PARAMEDIC_MISSION_LEVEL // (int)
int 19@(0@,7i) /= $var // 0076: 19@(0@,7i) /= $var // (int)

int $1923 += 25@ // 005E: $1923 += 25@  // (int)
int $6681 -= 171@ // 0066: $6681 -= 171@  // (int)
int $var *= 22@ // 006C: $var *= 22@ // (int)
int $8200 /= 51@ // 0074: $8200 /= 51@ // (int)

float 3@ += 9@ // 005B: 3@ += 9@  // (float)
float 13@ -= 4@(31@,7f) // 0063: 13@ -= 4@(31@,7f)  // (float)
float 17@(25@,5f) *= 0@ // 006B: 17@(25@,5f) *= 0@  // (float)
float 2@(0@,3f) /= 21@(1@,7f) // 0073: 2@(0@,3f) /= 21@(1@,7f)  // (float)

float $myvariable(0@,3f) += $addme(1@,5f) // 0059: $myvariable(0@,3f) += $addme(1@,5f) // (float)
float $something -= $subtractme(15@,4f) // 0061: $something -= $subtractme(15@,4i) // (float)
float $useful(13@,7f) *= $multiplyme // 0069: $useful(13@,7f) *= $multiplyme // (float)
float $factor /= $divideme // 0071: $factor /= $divideme // (float)

float 1@ += $addme(31@,5f) // 005D: 1@ += $addme(31@,5f) // (float)
float 2@(31@,5f) -= $subtractme // 0065: 2@(31@,5f) -= $subtractme // (float)
float 3@ *= $multiplyme // 006F: 3@ *= $multiplyme // (float)
float 4@(0@,8f) /= $divideme(1@,8f) // 0077: 4@(0@,8f) /= $divideme(1@,8f) // (float)

float $var += 7@(31@,3f) // 005F: $var += 7@(31@,3f)  // (float)
float $var -= 20@ // 0067: $var -= 20@ // (float)
float $var(0@,3f) *= 17@(1@,5f) // 006D: $var(0@,3f) *= 17@(1@,5f) // (float)
float $var(5@,7f) /= 13@ // 0075: $var(5@,7f) /= 13@ // (float)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

// ~~~~~~~~~~~~~~~~~~~~Comparison Operations~~~~~~~~~~~~~~~~~~~~
int 18@ == 21@ // 003B:  18@ == 21@  // (int)
int 18@ <> 21@ // 803B:  18@ <> 21@  // (int)
int 43@ >= 271@ // 002D:  43@ >= 271@  // (int)
int 27@ <= 33@ // 801D:  27@ <=33@  // (int) // 801D: not  27@ > 33@  // (int)
int 27@ > 33@ // 001D:  27@ > 33@  // (int)
int 43@ < 271@ // 802D:  43@ < 271@  // (int) // 802D: not 43@ >= 271@  // (int)

int $var1 == $var2 // 003A: $var1 == $var2 // (int)
int $var1(3@,7i) <> $var2 // 803A: $var1(3@,7i) <> $var2 // (int)
int $var1(0@,3i) >= $var2($index,7i) // 002C: $var1(0@,3i) >= $var2($index,7i)  // (int)
int $var1 <= $var2 // 801C: $var1 <= $var2 // (int) // 801C: not $var1 > $var2 // (int)
int $var1 > $var2(31@,6i) // 001C: $var1 > $var2(31@,6i) // (int)
int $var1 < $var2 // 802C: $var1 < $var2  // (int) // 802C: not $var1 >= $var2  // (int)

int 8@($index,7i) == $var(13@,3i) // 07D6: 8@($index,7i) == $var(13@,3i) // (int)
int 8@($index,7i) <> $var(13@,3i) // 87D6: 8@($index,7i) <> $var(13@,3i) // (int)
int 1@ >= $var(16@,13i) // 002F: 1@ >= $var(16@,13i) // (int)
int 9@ <= $var // 801F: 9@ <= $var // (int) // 801F: not 9@ > $var // (int)
int 9@ > $var // 001F: 9@ > $var // (int)
int 1@ < $var // 802F: 1@ < $var // (int) // 802F: not 1@ >= $var // (int)

int $var1(4@,10i) == 6@ // 003C: $var1(4@,10i) == 6@ // (int)
int $var1(4@,10i) <> 6@ // 803C: $var1(4@,10i) <> 6@ // (int)
int $var1 >= 0@(31@,10i) // 002E: $var1 >= 0@(31@,10i) // (int)
int $var1 <= 3@ // 801E: $var1 <= 3@ // (int) // 801E: not $var1 > 3@ // (int)
int $var1 > 3@ // 001E: $var1 > 3@ // (int)
int $var1(31@,10i) < 0@ // 802E: $var1(31@,10i) < 0@ // (int) // 802E: not $var1(31@,10i) >= 0@  // (int)

float 0@ == 6@ // 0045: 0@ == 6@  // (float)
float 1@ <> 7@ // 8045: 1@ <> 7@  // (float)
float 2@ >= 8@ // 0035: 2@ >= 8@  // (float)
float 3@ <= 9@ // 8025: 3@ <= 9@  // (float) // 8025: not 3@ > 9@  // (float)
float 4@ > 10@ // 0025: 4@ > 10@  // (float)
float 5@ < 11@ // 8035: 5@ < 11@ // (float) // 8035: not 5@ >= 11@  // (float)

float $3499 == $3507($8549,151f) // 0044: $3499 == $3507($8549,151f)  // (float)
float $3499 <> $3507($8549,151f) // 8044: $3499 <> $3507($8549,151f)  // (float)
float $8276 >= $8278 // 0034: $8276 >= $8278  // (float)
float $8276 <= $8278 // 8024: $8276 <= $8278 // (float) // 8024: not $8276 > $8278 // (float)
float $HJ_CAR_Z > $HJ_CAR_Z_MAX // 0024: $HJ_CAR_Z > $HJ_CAR_Z_MAX // (float)
float $HJ_CAR_Z < $HJ_CAR_Z_MAX // 8034: $HJ_CAR_Z < $HJ_CAR_Z_MAX  // (float) // 8034: not $HJ_CAR_Z >= $HJ_CAR_Z_MAX  // (float)

float 52@ == $var // 07D7: 52@ == $var // (float)
float 52@ <> $var // 87D7: 52@ <> $var // (float)
float 189@(217@,8f) >= $var // 0037: 189@(217@,8f) >= $var  // (float)
float 189@(217@,8f) <= $var // 8027: 189@(217@,8f) <= $var // (float) // 8027: not 189@(217@,8f) > $var // (float)
float 513@(227@,10f) > $var // 0027: 513@(227@,10f) > $var // (float)
float 513@(227@,10f) < $var // 8037: 513@(227@,10f) < $var  // (float) // 8037: not 513@(227@,10f) >= $var  // (float)

float $var == 0@ // 0046: $var == 0@ // (float)
float 8046: $var <> 0@ // 8046: $var <> 0@ // (float)
float $var >= 181@(217@,8f) // 0036: $var >= 181@(217@,8f) // (float)
float $var <= 513@(227@,10f) // 8026: $var <= 513@(227@,10f) // (float) // 8026: not $var > 513@(227@,10f) // (float)
float $var > 513@(227@,10f) // 0026: $TEMPVAR_FLOAT_1 > 513@(227@,10f) // (float)
float $var < 181@(217@,8f) // 8036: $var < 181@(217@,8f) // (float) // 8036: not $var >= 181@(217@,8f) // (float)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

// ~~~~~~~~~~~~~~~~~~~~Logical Operations~~~~~~~~~~~~~~~~~~~~
0@ &= 1@ // 0B17: 0@ &= 1@
0@ = 1@ AND 2@ // 0B10: 0@ = 1@ AND 2@

0@ |= 1@ // 0B18: 0@ |= 1@
0@ = 1@ OR 2@ // 0B11: 0@ = 1@ OR 2@

0@ ^= 1@ // 0B19: 0@ ^= 1@
0@ = 1@ XOR 2@ // 0B12: 0@ = 1@ XOR 2@

NOT 0@ // 0B1A: NOT 0@
0@ EQUAL_NOT 1@ // 0B13: 0@ = NOT 1@

0@ %= 1@ // 0B1B: 0@ %= 1@
0@ = 1@ MOD 2@ // 0B14: 0@ = 1@ MOD 2@

0@ >>= 1@ // 0B1C: 0@ >>= 1@
0@ = 1@ SHR 2@ // 0B15: 0@ = 1@ SHR 2@

0@ <<= 1@ // 0B1D: 0@ <<= 1@
0@ = 1@ SHL 2@ // 0B16: 0@ = 1@ SHL 2@

Inspiration for the Implementation:

  1. I do not see any Syntax Collision towards the currently implemented syntax Versus the list of what I have proposed above

  2. I know that there is a feature which you can declare a default data type of a variable using

var
<var name>: <type>
end

// this can also be done outside the VAR END block:
<type> <var name>

The problem here is that some programmers like me uses dynamic data type variables. An example case is that I sometimes store a float value on it, then an integer value on it. So defining default data types is useless towards it... Another workaround is to redefine the datatype of the variable

{$CLEO}
0000:
var
 0@: Int
 1@: Int
end
1@ = 7
0@ = 2
1@ -= 0@
var
 0@: Float
 2@: Float
end
0@ = 3.4
2@ = 4.1
2@ *= 0@
0A93: terminate_this_custom_script

but this method is so incovenient.

Thats why I wanted a convenient syntax implementation where we can do it like this:

{$CLEO}
0000:
1@ = 7
2@ = 2.0

0@ = 31
int 1@ -= 0@ // the compiler automatically treat this as opcode 0062: 1@ -=0@  // (int)
0@ = 1.3
float 2@ /= 0@ // the compiler automatically treat this as opcode 0073: 2@ /= 0@ // (float)
0A93: terminate_this_custom_script

If there are any concerns or flaw towards my suggestion then I would be happy to hear it.

In previous versions, variable declarations have been implemented https://docs.sannybuilder.com/coding/variables Please read the rest of the document carefully

XMDS avatar Aug 20 '21 02:08 XMDS

In previous versions, variable declarations have been implemented https://docs.sannybuilder.com/coding/variables Please read the rest of the document carefully

Like I said Above, redeclaring variable data types is inconvenient:

... // A very Inconvenient Syntax due to the need of redeclaring variable data types
Var
 0@: Float
 1@: Float
 2@: Float
 3@: Float
 4@: Float
 5@: Float
 6@: Float
 7@: Float
 8@: Float
end
00a0=4,store_actor $PLAYER_ACTOR position_to 0@ 1@ 2@
00a0=4,store_actor 31@ position_to 3@ 4@ 5@
6@ = 3@
7@ = 4@
8@ = 5@
6@ -= 0@
7@ -= 1@
8@ -= 2@
...
... // Some long code
...
Var
 0@: Int
 1@: Int
 2@: Int
 3@: Int
 4@: Int
 5@: Int
 9@: Int
 10@: Int
 11@: Int
end
092B: 0@ = group $PLAYER_GROUP member 0
092B: 1@ = group $PLAYER_GROUP member 1
092B: 2@ = group $PLAYER_GROUP member 2
092B: 3@ = group $PLAYER_GROUP member 3
092B: 4@ = group $PLAYER_GROUP member 4
092B: 5@ = group $PLAYER_GROUP member 5
9@ = 0@
10@ = 1@
11@ = 2@
9@ -= 3@
10@ -= 4@
11@ -= 5@
...
... // Some long code
...
Var
 0@: Float
 1@: Float
 2@: Float
 3@: Float
 4@: Float
 5@: Float
 12@: Float
 13@: Float
 14@: Float
end
083D: get_actor $PLAYER_ACTOR velocity_in_direction_XYZ 0@ 1@ 2@
083D: get_actor 31@ velocity_in_direction_XYZ 3@ 4@ 5@
12@ = 3@
13@ = 4@
14@ = 5@
12@ += 0@
13@ += 1@
14@ += 2@

But instead Implement this Idea which does make sense and is easy and convenient to use:

... // A convenient way where the arithmetic operators decide what kind of data types it treats its variables
00a0=4,store_actor $PLAYER_ACTOR position_to 0@ 1@ 2@
00a0=4,store_actor 31@ position_to 3@ 4@ 5@
float 6@ = 3@
float 7@ = 4@
float 8@ = 5@
float 6@ -= 0@
float 7@ -= 1@
float 8@ -= 2@
...
... // Some long code
...
092B: 0@ = group $PLAYER_GROUP member 0
092B: 1@ = group $PLAYER_GROUP member 1
092B: 2@ = group $PLAYER_GROUP member 2
092B: 3@ = group $PLAYER_GROUP member 3
092B: 4@ = group $PLAYER_GROUP member 4
092B: 5@ = group $PLAYER_GROUP member 5
int 9@ = 0@
int 10@ = 1@
int 11@ = 2@
int 9@ -= 3@
int 10@ -= 4@
int 11@ -= 5@
...
... // Some long code
...
083D: get_actor $PLAYER_ACTOR velocity_in_direction_XYZ 0@ 1@ 2@
083D: get_actor 31@ velocity_in_direction_XYZ 3@ 4@ 5@
float 12@ = 3@
float 13@ = 4@
float 14@ = 5@
float 12@ += 0@
float 13@ += 1@
float 14@ += 2@

it's possible to declare a variable of a built-in type (Int, Float, String, LongString) using only the type name. Syntax:

<type> <variable name>

Any command such as assignment, mathematics, judging size, bit operation, etc. can use int, float in front to declare variable types, and it also supports custom variable names. I have a CLEO source code, maybe it can be used as your example: https://github.com/XMDS/OP_0DD2FixAsm_call/blob/master/source/Test%20script/draw_shadow.txt

XMDS avatar Aug 20 '21 07:08 XMDS

This is not implemented yet, and I tried to compile it and is gives an error(obvious).

float 4@ -= 1@

Only inconvenient variable declarations before arithmetic operations is what I see on the document. I am pretty sure I haven't missed anything.

For commands that use 2 variables, both variables need to be declared:

float 0@
float 1@
0@ -= 1@

Or

Float 1@
Float 0@ -= 1@

XMDS avatar Aug 20 '21 07:08 XMDS

Just to clarify, I did not said that Variable declarations wasn't implemented yet. I use variable declaration all the time in my cleo scripts, but it is just so inconvenient. I hope you get my point.

For commands that use 2 variables, both variables need to be declared:

float 0@
float 1@
0@ -= 1@

Or

Float 1@
Float 0@ -= 1@

That's what I am talking about, it is inconvenient. Wouldn't you agree?

But if the compiler can understand something like this:

int 0@ *= 1@
float 2@ /= 3@

Then cleo coding would be easier.

For commands that use 2 variables, both variables need to be declared:

float 0@
float 1@
0@ -= 1@

Or

Float 1@
Float 0@ -= 1@

That's what I am talking about, it is inconvenient. Wouldn't you agree?

But if the compiler can understand something like this:

int 0@ *= 1@
float 2@ /= 3@

Then cleo coding would be easier.

I think it is very necessary to tell the compiler the data type of a variable behind. CLEO math commands only support calculation of the same type of data, CLEO4 math commands and bit operation commands only support int type. Nevertheless, It will be very blurry

XMDS avatar Aug 20 '21 07:08 XMDS

For: https://github.com/sannybuilder/dev/issues/32

XMDS avatar Aug 20 '21 07:08 XMDS

The compiler just needs to parse the parameters that are currently present on the currently line. I don't see anything wrong with that.

  1. The compiler will scan either (Int, Float) to tell what data type its variables are.
  2. check the first variable if it is global($) or local(@)
  3. check the operator (=, +=, -=, *=, /=, ==, <>, >=, <=,>, <)
  4. check the second parameter if global($) or local(@)
  5. find the proper opcode that suits the four parameters above.

Here is an example:

int $var += 0@
  1. has int(case insensitive) string match
  2. first variable is a global $
  3. operator is +=
  4. second variable is a local @
  5. the compiler treat it as opcode 005E: $var += 0@

There are many ways to parse this parameters. My best suggestion is through RegEx . But it is up to the SB Dev team how they will parse it.

I agree with the fact that declaring both variables in the expression is annoying.

However what I don't really like in this proposal is the fact we may miss possible errors when the variable was explicitly defined with another type.

Consider this example:

float a = 1.0
int b = 1
int c = 0

int c += a

the developer wanted to write c += b but made a mistake. so the calculation result will be wrong.

What would be really useful if the compiler could deduce the variable type from the assigned value, so you don't really need to declare its type in trivial cases like

 x = 0 // here x has int type 
 y = 10 // here y has int type
 x += y // both variables are int  => 005a

y = 1.0 // now y is float

x += y // error, different types

this would require a proper control-flow analysis which we don't have yet.

I can probably make another option for the compiler to let the shorthand syntax proposed here, but it will be disabled by default.

x87 avatar Aug 20 '21 19:08 x87

Consider this example:

float a = 1.0
int b = 1
int c = 0

int c += a

the developer wanted to write c += b but made a mistake. so the calculation result will be wrong.

You are right on this one, that is an advantage of declaring variables before a certain operation for syntax error checking. So those who will use this feature should already have it in mind. But I think that it will not be a big deal because we are just adding an extra syntax feature which is a good thing because it allows us to do straightforward operations like. int c += a while still preserving the current conventions existed.

That does not mean that we will make this as a replacement of the current convention when coding a script. Coders can still do variable declarations, and compile the same as before.

If this feature gets implemented, then coders can optionally use straightforward variable to variable operations.

What would be really useful if the compiler could deduce the variable type from the assigned value, so you don't really need to declare its type in trivial cases like

 x = 0 // here x has int type 
 y = 10 // here y has int type
 x += y // both variables are int  => 005a

y = 1.0 // now y is float

x += y // error, different types

this would require a proper control-flow analysis which we don't have yet.

I can probably make another option for the compiler to let the shorthand syntax proposed here, but it will be disabled by default.

Maybe Variable Data Type Tracking which automatically detects the data types of variables. The idea would be:

  1. Compiler Scans from top to bottom
  2. Everytime it encounters an Opcode, it will change its Data Type, an example would be:
 x = 0
        // starting from this line, x is now declared as "Int"
 y = 10
        // starting from this line, y is now declared as "Int"
 x += y // both variables are int  => 005a
y = 1.0
        // starting from this line, x is now declared as "Float"
x += y // Int += Float Warns due to Syntax Error

092B: 0@ = group $PLAYER_GROUP member 5
        // starting from this line, 0@ is now declared as "Int"
092B: 1@ = group $PLAYER_GROUP member 4
        // starting from this line, 1@ is now declared as "Int"
092B: 2@ = group $PLAYER_GROUP member 3
        // starting from this line, 2@ is now declared as "Int"
092B: 3@ = group $PLAYER_GROUP member 2
        // starting from this line, 3@ is now declared as "Int"
092B: 4@ = group $PLAYER_GROUP member 1
        // starting from this line, 4@ is now declared as "Int"
092B: 5@ = group $PLAYER_GROUP member 0
        // starting from this line, 5@ is now declared as "Int"
0@ += 1@ // Opcode 005A:
1@ -= 2@ // Opcode 0062:
2@ *= 3@ // Opcode 006A:
3@ /= 4@ // Opcode 0072:
00a0: store_actor $PLAYER_ACTOR position_to 0@ 1@ 2@
        // starting from this line, 0@, 1@, and 2@ are now declared as "Float"
00a0: store_actor 31@ position_to 3@ 4@ 5@
        // starting from this line, 3@, 4@, and 5@ are now declared as "Float"
3@ -= 0@ // Opcode 0063:
4@ -= 1@ // Opcode 0063:
5@ -= 2@ // Opcode 0063:
3@ += 0@ // Opcode 005B:
4@ += 1@ // Opcode 005B:
5@ += 2@ // Opcode 005B:

That means that the Compiler must know all the data type of the returning variables of all possible Opcodes that exists then redeclare them automatically.

Proposed bitwise operations syntax using examples from OP:

0@ &= 1@ // 0B17: 0@ &= 1@
0@ = 1@ & 2@ // 0B10: 0@ = 1@ AND 2@

0@ |= 1@ // 0B18: 0@ |= 1@
0@ = 1@ | 2@ // 0B11: 0@ = 1@ OR 2@

0@ ^= 1@ // 0B19: 0@ ^= 1@
0@ = 1@ ^ 2@ // 0B12: 0@ = 1@ XOR 2@

~0@ // 0B1A: NOT 0@
0@ = ~1@ // 0B13: 0@ = NOT 1@

0@ %= 1@ // 0B1B: 0@ %= 1@
0@ = 1@ % 2@ // 0B14: 0@ = 1@ MOD 2@

0@ >>= 1@ // 0B1C: 0@ >>= 1@
0@ = 1@ >> 2@ // 0B15: 0@ = 1@ SHR 2@

0@ <<= 1@ // 0B1D: 0@ <<= 1@
0@ = 1@ << 2@ // 0B16: 0@ = 1@ SHL 2@

x87 avatar Oct 09 '22 19:10 x87

Another suggestion by @MiranDMC

0@ = 1.0 // opcode deduced from type
var 0@ = 1.0 // type deduced and declared
var 0@ : Float // we already have it
var 0@ : Float = 1.0 // declare and assign
var 0@ : Float = 1 // auto conversion of constant type (1 -> 1.0) at compilation stage
var 0@ : Float = 1@ // valid as long 1@ type matches 0@ or is not declared

// give name ('as' or 'alias' keyword?)
var 0@ as PosX // same as const 0@ = PosX
var 0@ : Float as PosX // declare type and give name (var and const combined)
var 0@ : Float as PosX = 1.0 // declare type, give name and assign value

var 0@ : Float as PosX = 1@ // allow if 1@ type is not declared or is Float
var 0@ = 1@ // same behaviour as raw 0@ = 1@, but also declare 0@ type

var 0@ : array 3 of Float as Pos = 1.0 // hmmm, now what? Assign first arg? Initialize all array's elements?

x87 avatar Aug 10 '23 01:08 x87

Implemented in SB 4.0