sannybuilder / dev

Sanny Builder Bug Tracker and Roadmap development
https://sannybuilder.com
49 stars 0 forks source link

Custom Classes proposal #37

Open x87 opened 4 years ago

x87 commented 4 years ago

Class Syntax

Here is the proposal to add a new syntax for defining custom classes in scripts. They may eventually become a replacement for static classes in the classes.db.

Let's take a look at an example:

class Actor
  putInCar(car)
     036A: put_actor self in_car car
  end
end

var
  0@: Actor
end

0@.putInCar($car)

Here we defined a class named Actor that has one method putInCar in. putInCar method has one parameter car. Then we declared a variable 0@ of the type Actor and finally called method putInCar on that class instance.

Important Notes

Design Choices

class Actor
  putInCar(car)
     036A: put_actor self in_car car
     0@ = 2
  end
end

0@ = 1
0@.putInCar($car)
// 0@ is now equal to 2

There are a few possible choices to this dilemma:

1) leave it as is, considering this a liberate language design decision (when you invoke subroutines with gosub they could alter local scope as well) 2) forbid using local variables syntax inside methods. only use arguments passed into the method. this could be inconvenient if you need to keep a local state. 3) add extra prologue and epilogue before and after each method call that will store and restore all local variables. this adds extra overhead in runtime 4) use CLEO's scm_function that basically does # 3. this adds unnecessary dependency on CLEO.

class Actor
  putInCar(car)
     0AB1: call_scm_func @local 0

     :local
     036A: put_actor self in_car car
     0@ = 2
     0AB2: ret 0
  end
end

0@ = 1
0@.putInCar($car)
// 0@ is still 1
x87 commented 4 years ago

Conditional Methods

method could be conditional:

class Actor
  inAnyCar()
     00DF:   actor self in_any_car 
  end
end
var
  0@: Actor
end
if
  0@.InAnyCar()
then

gets compiled as

if
   00DF:   actor 0@ in_any_car
then

Design Choices

If the method is a template that simply gets inserted in-place of a call statement (see Design choices in the first post) you can not put anything except conditional opcodes in a method to be able to use it in the conditional expression. i.e. this would not be allowed:

class Math
  isZero(x)
     if
       x == 0
     then
        0485:  return_true
     else
        059A:  return_false
     end
  end
end
if
  Math.isZero(1) // false
then
x87 commented 4 years ago

Returning Results

It's unclear how to work with returning a value from the methods, i.e consider:

class Actor
  pos()
     00A0: store_actor self position_to x y z
  end

how to represent x, y, z in the code?

Design Choices

1) pass variables into the method as parameters

  pos(x, y, z)
     00A0: store_actor self position_to x y z
  end
0@.pos(1@, 2@, 3@) // 00A0: store_actor 0@ position_to 1@ 2@ 3@

always having to pass a variable to store a result into can be tedious for simple cases like:

  sqr(x, result)
     0A90: result  = x * x
  end

one would want to simply have

class Math
  sqr(x)
     0A90: result  = x * x
  end
end

0@ = Math.sqr(2)

2) add an implicit variable result then will represent the left side of the call expression:

  pos()
     00A0: store_actor self position_to result[0] result[1] result[2] // variant 1
     00A0: store_actor self position_to result.0 result.1 result.2 // variant 2
  end

then

( 1@, 2@, 3@ ) = 0@.pos() // 00A0: store_actor 0@ position_to 1@ 2@ 3@

this adds a lot of unusual syntax into the language.

3) combine both approaches, i.e. have an implicit result for a single value and pass variables into the method for multiple values:

class Math
  sqr(x)
     0A90: result  = x * x
  end

   pi(int, frac)
      int = 3
      frac = .14
   end
end

0@ = Math.sqr(2) // 0@ = 4
Math.pi(1@, 2@) // 1@ = 3, 2@ = .14
wmysterio commented 4 years ago

Powerful! The question is: will the compiler pick up the declared classes if they are described in another file and were included via the {$i} directive. There are plans to write such classes (I already have everything ready for this) and I would like to save the developer from copying everything every time.

I also wanted to ask: will it be possible to add constants to a class? Like this

class Draw
    const MaxWidth = 640.0
end
//
0@ > Draw.MaxWidth
x87 commented 4 years ago

@wmysterio yes, the compiler definitely should be able to read class declarations from imported files.

static class variables could be a nice feature! Will keep that in mind.

x87 commented 3 years ago

Class instances should support inline declaration similar to primitive types:

int x = 0
Player p = Player.Create(x, 10.0, 20.0, -100.0)
x87 commented 1 year ago

todo: update this proposal after #263

x87 commented 1 month ago

2024 Draft


class Car
    /// Directly mapped to command CREATE_CAR
    /// Function and Command signatures must match
    Create<0x00A5>(modelId: int, x: float, y: float, z: float): Car

    /// Update is an instance method
    Update(self: Car, flag: int)
        // update
    end

    /// Can return logical values
    Check(self: Car): logical
        return true
    end
end

Car c = Car.Create(#INFERNUS, 0, 0, 0) // compiled as 00A5
if c.Check() // compiled as a scm function
then
    c.Update(1) // compiled as a scm function
end