Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions docs/strict-mode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Strict Mode

| Lead Author(s) | Implemented | GitHub Links |
|---|---|---|
| moonheart08 | :x: | TBD |


## Overview

Introduce a new pragma, under the runtime pragma block (4xxx), which constrains most 'implementation-defined-like' behavior within the language.

An example of implementation-defined-like behavior is the behavior of negate on non-numeric values, where it simply treats the value as a number anyway (and thus returns 0).


## Motivation

Many of these behaviors within the Dreammaker language are not *expected* behaviors.
The following is a catalog of many of these behaviors.

## Built-in procs
### get_dir(from, to)
When given non-atom from or to, get_dir silently returns zero (no direction to target).
This behavior is not documented.
### length(x)
When given a type without a length (i.e. a number or non-list object), this returns 0.
This behavior is not documented.
### get_step(start, dir)
When given a non-atom start point, this returns null.
This behavior is not documented.
### rgb()
As written in byond, `rgb(<args>)` is seemingly, roughly `rgb_internal(list(<args>))` parsing wise.
The following behaviors are undocumented:
- c and s are aliases in HSL and HSV spaces, `rgb(h=1,c=2,l=3)` and `rgb(h=1,s=2,l=3)` are identical despite `chroma` and `saturation` being wildly different concepts. (HCL and HSL are very, very different color spaces.)
- non-numeric arguments for any of `r g b a h s c v l y` are completely ignored and treated as zero.
- Due to the above, when alpha is set to null, it becomes 0, and when alpha is not set at all, it is 255. This also means it's impossible to "un set"
- if `space` is a runtime value, then many of the arguments (like `h s l` and `r g b`) share memory without further checks, directly reinterpreting the numbers. They're presumably just substituted in for the first three arguments.
- there's SO MUCH im going to move on
### issaved(varname)
This always returns false on numeric indicies, which aren't valid here to begin with, and it *does* throw on otherwise invalid string keys.
This behavior is not documented. (not even the cases where it fails, actually)
### CRASH(reason)
This crashes the game silently (without clarifying the lack of a reason) if `reason` is not a string.
This behavior is not documented.
### floor(v), ceil(v), round(v), fract(v), trunc(v), sign(v) and other math functions
These treat any non-numeric value as 0.
This behavior is not documented.
### lowertext(str) and uppertext(str)
These pass any non-numeric value through unmodified (for example a mob does not get uppercased, and does not throw.)
This behavior is not documented.
### trimtext(str)
This converts any non-string input to an empty string.
This behavior is not documented.
### text2path(str)
This converts any non-string input to an empty string.
This behavior is not documented.
### text2ascii(str)
This coerces any non-string input into an empty string.
Out of bounds indicies simply return 0 instead of erroring.
This behavior is not documented.
### splittext()
This coerces any non-string input into an empty string.
Any non-string and non-regex delimiter results in an empty output.
This behavior is not documented.
### ...etc
There's a LOT and I'd like to get this draft up.
## Built-in operators
### Unary negate (`-x`)
This returns 0 for any non-numeric x.
This behavior is not documented.
### Unary bit not (`~x`)
This returns `16777215` on non-numeric x (24-bit negation of 0).
This behavior is not documented.
### `x in y`
When the lefthand `x` is not a list, atom, or the world, false is always returned.
This behavior is not documented.
### `++` and `--`
These operators treat any non-numeric value as 0.
This behavior is not documented.
### `^`, `&`, `|`, `<<`, and `>>`
These operators treat nulls as 0.
This behavior is not documented.
## Design

In general, the sole change made by "strict mode" should be that all of the unexpected behaviors listed in the motivation should be changed to appropriate thrown errors, terminating execution on that thread upon encountering the poorly defined operation.

In situations where DM would previously engage in implementation-exposing behavior, definite behavior (an exception) will be introduced. For example,
```dm
var/moblen = length(new /mob())
```
would put `0` in moblen normally, and throw when in strict mode.

## Considerations & Drawbacks

This does not impact parity in any way, as it is a pragma.

Performance wise, this enables future optimizations (i.e. specialization of code paths based on what the type could not possibly be without an error)

This is likely to uncover bugs, unexpected behavior, and "shortcut code" (Relying on this unexpected behavior) in most if not all SS13 codebases, and migration to enable the pragma if they wish is unlikely to be free.