Nonstraightforward

From Esolang
Jump to navigation Jump to search

Nonstraightforward is an esolang by User:RaiseAfloppaFan3925 based off of Yappacino that extends JavaScript with uselessly verbose syntax.

Nonstraightforward
Paradigm(s) imperative, procedural, declarative, object-oriented (prototype-based)
Designed by User:RaiseAfloppaFan3925
Appeared in 2025
Type system dynamic, fake
Memory system variable-based
Computational class Turing-complete
Reference implementation Nonstraightforward
Influenced by Yappacino, TypeScript
File extension(s) .cvsc (legacy), .nstrfwd

Types

Like Yappacino, Nonstraightforward has fake types. The types can be anything, since the parser does not care and parses only one token as the type.

Types are required in both variable and function declarations, unlike Yappacino which only requires types in function declarations.

//                               This is the type |
//                                                V
public nondefault stable immutable readonly label number: PI <- 3.14159;

Access specifiers

Nonstraightforward brings to the table some access specifiers, which have an actual effect.

For example, a public declaration looks like:

public default stable immutable readonly synchronous subroutine: number <- abs(x --> number) {
    return x;
}

public nondefault stable mutable readwrite label number: counter <- 0;

The default keyword marks the declaration as a default export, which when compiled to JavaScript results in this:

// CommonJS
function abs(x) {
    return x;
}

module.exports.default["abs"] = abs;

// ES module
export default function abs(x) {
    return x;
}

Likewise, a private declaration can be declared with the private keyword instead.

private stable immutable readonly synchronous subroutine: number <- add(x, y --> number, number) {
    return x + y;
}

private stable mutable readwrite label number: privateCounter <- 0;

Immutability

Nonstraightforward has "tiers" or "levels" of immutability, specifically 4 levels of immutability. These are controlled with the following access specifiers.

mutable/immutable readonly/readwrite/writeonly

mutable and immutable

A mutable variable is a normal JavaScript variable declared with let, while a immutable variable or function will be declared with const.

immutable for functions does nothing right now, however that might change in the future.

readonly and readwrite and writeonly

A readonly variable or function will have Object.freeze() called on it, making it impossible to modify the object. However, this does NOT stop the variable itself from being modified.

private stable mutable readonly label object: x <- { x: 1, y: 2 };

x.x = 2; // does nothing because of Object.freeze()

x = { x: 3, y: 8 }; // works because x is mutable

A readwrite variable is a normal variable, with no special rules applied to it.

The writeonly access specifier does absolutely nothing as of now, but it is intended to make the variable impossible to read.

Tiers of Immutability

The four tiers of immutability mentioned earlier are based on immutability of the reference and immutability of the variable.

Tier Reference Value Access Specifiers Comment
Tier 1 Mutable Mutable mutable readwrite Typical variable declaration in JavaScript
Tier 2 Mutable Immutable mutable readonly The variable can be modified, but the value cannot be modified
Tier 3 Immutable Mutable immutable readwrite Same as JavaScript's const, reference is constant but value is mutable
Tier 4 Immutable Immutable immutable readonly True immutability. The value of the variable cannot be modified, and the variable cannot be changed to reference another object.

Variables

A variable in Nonstraightforward can be declared like so:

// for public variables
public default/nondefault mutable/immutable readonly/readwrite/writeonly label type: name <- value;

// for private variables
private mutable/immutable readonly/readwrite/writeonly label type: name <- value;

Examples:

public nondefault immutable readonly label number: PI <- 3.14159265358979;
private mutable readwrite label string: message <- "Hello, World!"

Functions

Functions, or subroutines in Nonstraightforward, can be declared like so:

// remember that you can have any number of parameters
// but it is a compile error for the number of types and
// number of parameters to not be equal

// for public subroutines
public default/nondefault mutable/immutable readonly/readwrite/writeonly asynchronous/synchronous subroutine: type <- name(arg1, arg2, arg3 --> type1, type2, type3)

// for private subroutines
private mutable/immutable readonly/readwrite/writeonly asynchronous/synchronous subroutine: type <- name(arg1, arg2, arg3 --> type1, type2, type3)

Example:

public nondefault immutable readonly synchronous subroutine: vec2 <- vec2_add(a, b --> vec2, vec2) {
    return { x: a.x + b.x, y: a.y + b.y };
}

Control flow

Nonstraightforward has some new control flow syntax made extremely long to rival Yappacino.

If/else statements

An if statement looks like this:

stipulate that the provision [condition] qualifies [body]

And the else keyword is replaced by otherwise.

Example:

stipulate that the provision x == 7 qualifies {
    ~/dev/stdout/write/ln("2763");
} otherwise stipulate that the provision x == 2763 qualifies {
    ~/dev/stdout/write/ln("That number seems familiar");
} otherwise {
    ~/dev/stdout/write/ln("I don't know that number.");
}

Parentheses surrounding the condition are optional.

While loops

A while loop looks like this:

so long as the condition [condition] is still upholding, then do [body]
JavaScript Nonstraightforward
break terminate
continue continue (as of C8)

Example:

private stable mutable readwrite label number: x <- 1;

so long as the condition x <= 10 is still upholding, then do {
    ~/dev/stdout/write/ln(`x = ${x}`);
    
    x++;
}

~/dev/stdout/write/ln("Finished!");

For loop

Currently, Nonstraightforward has one kind of for loop, which loops over a range of numbers.

for all values of [variable] in the range of numbers from [start] to [end] do [body]

This is an exclusive loop, it compiles to

for (let [variable] = [start]; [variable] < [end]; [variable]++) [body]

Example:

for all values of i in the range of numbers from 0 to 10 do {
    ~/dev/stdout/write/ln(`i = ${i}`);
}

Currently, decrement loops are not supported.

The SYSROOT variable

Like Yappacino, member accesses in Nonstraightforward are done with file paths. However, Nonstraightforward uses POSIX file paths, to contrast Yappacino's usage of Windows file paths.

To print something in Nonstraightforward, this function is used

~/dev/stdout/write/ln()

This compiles to

SYSROOT.dev.stdout.write.ln()

Likewise, member accesses are done with

~/dev/scope/object/member1/member2/member3

And it compiles to

object.member1.member2.member3

Importing modules

To import a declaration from a module, the following syntax is used:

from module "module" request thing; // for default declarations
from module "module" request { otherThing }; // for non-default declarations

And remember, Nonstraightforward compiles down to JavaScript, so this works ONLY with JavaScript/TypeScript modules.

Also, this syntax only compiles to ES imports, so for CommonJS imports, use require() instead.

Interfaces

Like TypeScript, Nonstraightforward supports interfaces. Interfaces in Nonstraightforward do nothing like the rest of the types, however they can serve as a guide for type layouts.

Example:

public nondefault stable object structure member layout Vector2 {
    stable member x <= number;
    stable member y <= number;
}