Sekura JS Language Reference

Prepared by A. T. Kuangaliyev, Jupiter Soft LLP, with assistance from ChatGPT.

1. Scope

Sekura JS is a source language compiled by sekura-js for the Memora8 (MR8) target. This reference describes syntax and behavior represented in the compiler source. Where implementation details do not define a stable language rule, the text labels the behavior as target-specific or unspecified.

This page covers .sjs source syntax. The compiler command line, generated artifacts, SJV, the supported SystemVerilog subset, SJP, and sekura-sjv are documented separately.

2. Source files and comments

Sekura JS source files conventionally use the .sjs extension. The parser reads a source file as a module containing top-level declarations.

A line comment begins with // and continues to the end of the line:

// A line comment
let answer: u32 = 42;

The lexer does not implement block comments.

Identifiers begin with a letter or underscore and continue with letters, digits, or underscores. The implementation uses the C character-classification functions; portable source should use ASCII identifiers.

3. Tokens and reserved words

The lexer reserves these words:

let const struct if else for while switch case default
break continue return goto import export as module void system
i8 u8 i16 u16 i32 u32

The words is and library are read as identifiers and interpreted specially only in the module-header position. They are not globally reserved by the lexer. Likewise, function is not a reserved word.

4. Literals

4.1 Integer literals

Integer literals are nonnegative tokens; a negative value is formed with unary -.

Supported source forms include decimal, hexadecimal with a lowercase 0x prefix, and binary with 0b, 0B, or b followed by binary digits:

let decimal: u32 = 42;
let hex: u32 = 0xff;
let binary_a: u32 = 0b101010;
let binary_b: u32 = b101010;
let negative: i32 = -10;

Fractional numeric literals are rejected. The lexer currently accepts lowercase 0x; use that spelling.

4.2 Strings

String literals use double quotes. The lexer recognizes these escapes:

Escape Character
\\n newline
\\t tab
\\r carriage return
\\\\ backslash
\\" double quote
\\0 NUL
\\' apostrophe

An unrecognized escape drops the backslash and keeps the following character. The source does not specify a text encoding. In generated MR8 code, string literals are stored in compiler-managed data and commonly used as 32-bit addresses; the full representation is an implementation detail.

5. Types

Built-in integer types:

Type Storage width Signedness
i8 8 bits signed
u8 8 bits unsigned
i16 16 bits signed
u16 16 bits unsigned
i32 32 bits signed
u32 32 bits unsigned

Other supported type forms include void, structure names, qualified structure names, the function type (), fixed-size arrays, and the dynamic byte-array form u8[].

let count: u32;
let bytes: u8[256];
let text: u8[];
let callback: ();
struct Point {
    x: u32;
    y: u32;
}

The parser accepts a type annotation where shown, but exact type-checking and conversion rules are incomplete in some compiler paths. The MR8 backend uses 32-bit values for ordinary integer expression results; narrow types primarily affect memory storage and loads/stores.

6. Variables, constants, and initialization

Use let for a variable and const for a constant declaration:

let count: u32 = 0;
const limit: u32 = 100;

let next(value: u32): u32 {
    let result: u32 = value + 1;
    return result;
}

A declaration may omit its type annotation, initializer, or both if accepted by the surrounding compiler path. For interoperable, clear source, provide explicit types for public interfaces and initialized storage.

A pointer-like variable can carry a default access layout using declaration syntax such as let ptr{u8};. See Addresses and memory access.

The exact restrictions on mutation of local and global constants are not uniformly enforced by all compilation paths; do not infer a complete immutability guarantee from the keyword alone.

7. Functions

A function declaration has a name, a parameter list, an optional return type, and a block body. Parameter types and the return type may be omitted syntactically:

let add(a: u32, b: u32): u32 {
    return a + b;
}

let reset() {
    return;
}

The compiler’s SJS parser accepts unannotated parameters and an omitted return annotation. Specify types explicitly for public functions and imported interfaces. The language front end does not implement a uniform, complete static type checker for every source operation; exact argument and result validation depends on the compilation path.

A call uses the ordinary expression form:

let total: u32 = add(20, 22);
reset();

8. Modules, imports, and exports

8.1 Module declarations

A module declaration has the form:

module NAME ["RegID"] [is library | is system];

Examples:

module application;
module language "419cc7730fc2f4f2-bda8fd025d816cbc";
module System "419e92c481e75a42-0cb9576cfb6b96ec" is system;
module math is library;

The parser accepts an omitted RegID. It rejects a RegID on a library module and rejects a module marked as both library and system. In modular compilation, the toolchain may obtain or persist module identity outside the source; RegID lifecycle is a toolchain concern.

In a module header, is introduces library or system. These words are contextual there.

8.2 Imports

An import names a source path and a required alias:

import "math.sjs" as math;

let value: u32 = math.add(20, 22);

Imported module members are referenced through the alias and a dot. Resolution rules and runtime dispatch details belong to the compiler/toolchain documentation.

8.3 Libraries

A library module is declared with is library. It can export declarations for a consuming module:

module math is library;

export let increment(value: u32): u32 {
    return value + 1;
}

The library-loading path incorporates used exported declarations and their needed dependencies into a consuming runtime module. A library is not itself a normal runtime module.

8.4 Exports

The export keyword applies to top-level declarations. The implementation supports exported functions, variables/constants, and structures:

export let add(a: u32, b: u32): u32 {
    return a + b;
}

export let counter: u32 = 0;

export struct Point {
    x: u32;
    y: u32;
}

Non-exported names remain internal to the module interface.

9. Statements and blocks

A function body and nested block use braces. The parser supports these statement forms:

  • variable or constant declaration;
  • expression statement;
  • nested block;
  • if / else;
  • while;
  • for;
  • switch, case, and default;
  • break, continue, and return;
  • label and goto, including conditional goto label(condition);.

Inline assembly in an SJS block is explicitly rejected. The compiler directs users to standalone .sasm files for assembly input.

9.1 Conditions and loops

Conditions use zero as false and nonzero as true in the MR8 code generator:

if (ready) {
    run();
} else {
    wait();
}

while (count < limit) {
    count++;
}

for (let i: u32 = 0; i < 10; i++) {
    visit(i);
}

The for initializer, condition, and update may be omitted. An omitted condition produces an unconditional loop in the MR8 backend.

9.2 Switch

A switch evaluates its selector and branches to the matching case; if none matches, it branches to default, or completes if no default exists. Execution then proceeds through the emitted case bodies in source order. Use break to leave the switch.

switch (command) {
    case 0:
        reset();
        break;
    case 1:
        read();
        break;
    default:
        reject();
}

9.3 Break and continue

In the MR8 backend, break branches to the nearest loop or switch end. continue branches to the nearest loop continuation point: the condition in a while, and the update expression in a for. A continue in a switch nested in a loop targets that loop.

9.4 Labels and goto

A label is an identifier followed by a colon. Use goto label; for an unconditional jump and goto label(condition); for a conditional jump taken when its expression is nonzero:

let check(value: u32): void {
    goto done(value);
    process(value);
done:
    return;
}

The MR8 code generator checks that a goto target label is present in the same function. Avoid duplicate labels; behavior for duplicate label declarations is not a defined language guarantee.

10. Expressions and operators

Expressions include literals, names, parenthesized expressions, calls, field selection, indexing, assignments, unary and binary operations, and the special MR8-oriented forms below.

10.1 Operator precedence

From lowest to highest precedence in the parser (operators on the same row associate left-to-right):

Precedence Operators
1 `
2 ^^
3 &&
4 `
5 ^
6 &
7 ==, !=, infix ?
8 <, >, <=, >=
9 binary ~
10 +, -
11 *, /, binary %, binary !

Assignment operators are right-associative and bind less tightly than binary operators. Postfix calls, member access, indexing, layout selectors, and postfix increment/decrement bind tightly. Prefix operators bind before binary expressions.

Parentheses are recommended whenever precedence might be unclear.

10.2 Arithmetic and bitwise operators

The parser and MR8 backend implement:

Unary:   -  ~  !  %  ?  &  *  ++  --
Binary:  +  -  *  /  %  &  |  ^  ~  !  ?
Logical: && || ^^

Binary + and - also support pointer-like arithmetic, scaled by the pointer’s access layout. Binary ~ emits the MR8 shift operation. Its exact shift convention follows the MR8 ISA implementation; write target-specific shifts only after verifying the intended shift direction and count rules.

Unary ~ is bitwise complement. Unary ! is logical negation: it produces 1 for zero and 0 for nonzero. Binary ! emits the MR8 MSB operation; its detailed search/count semantics are target-specific and are not specified here.

Unary % and %{...} are system-bus operations in the MR8 backend. They are target-specific. Binary % computes remainder.

The logical operators produce 0 or 1 in expression context in the MR8 backend. Do not assume whether both operands are evaluated in every context; condition generation can short-circuit && and ||.

10.3 Comparisons

The relational operators == != < <= > >= produce 0 or 1 in MR8 expression context. The MR8 compare instruction exposes signed and unsigned comparison flags; the exact ordered comparison selected is a target/compiler rule and should be confirmed against the intended type semantics before relying on mixed signedness.

The infix a ? b produces the raw result of the MR8 compare instruction rather than a Boolean:

let flags: u32 = left ? right;

10.4 Increment, decrement, and assignment

Prefix and postfix ++ and -- are parsed. For ordinary values, prefix changes the operand before yielding it; postfix yields the old value and then updates the operand. Pointer-like increments use the active layout’s byte step.

Assignments use =. Compound forms accepted by the parser are:

+= -= *= /= %= &= |= ^= ?= ~=

The left operand must be a writable storage location in supported compilation paths.

11. Addresses and memory access

SJS does not declare a separate pointer type in the parser. In the MR8 compiler, an untyped address is represented as a 32-bit value. The unary & obtains an address, and unary * dereferences an address.

let value: u32 = 10;
let address = &value;
let copy: u32 = *address;
*address = 20;

The {TYPE} suffix supplies an access layout for an address expression:

let bytes = &buffer;
let first: u8 = bytes{u8}[0];
let item: u32 = bytes{u32}[1];

A pointer-like variable may declare a default layout:

let ptr{u8};
ptr++;

The layout controls element interpretation and pointer arithmetic scaling. ptr{Point}[i].field interprets the address as elements of structure type Point. The exact validity of arbitrary addresses, bounds, aliasing, and alignment is not specified as a source-language safety guarantee.

The postfix {N} form selects a packed word/byte-oriented field in the MR8 backend. It is a target-oriented facility; consult MR8 documentation for its exact bit and byte indexing rules.

12. Arrays and structures

12.1 Arrays

Fixed-size arrays use TYPE[NUMBER]:

let words: u32[16];
let bytes: u8[256];

The type parser also accepts u8[], used as a dynamic/string pointer form. Its bounds, ownership, and general-purpose dynamic-array semantics are not specified by the language implementation.

Array indexing uses square brackets:

words[0] = 100;
bytes[1] = 0x41;

For typed arrays and layout-qualified addresses, MR8 element addressing uses the element layout’s size as its stride. Bounds checking is not provided as a source-language guarantee.

12.2 Structures

A structure declares named fields:

struct Point {
    x: u32;
    y: u32;
}

let point: Point;
point.x = 10;
point.y = 20;

Fields are accessed with .. In the MR8 target layout implementation, fields follow declaration order, each field offset is aligned to the natural alignment of its size (1, 2, 4, or 8 bytes), and total structure size is rounded to the largest field alignment. This describes the current target layout, not a portable cross-target ABI promise.

13. The ? size operator

Prefix ? accepts a type or expression and emits a size value in the MR8 backend:

let word_size: u32 = ? u32;
let point_size: u32 = ? (Point);
let value_size: u32 = ? (value);

Use parentheses around a type or expression when needed to make parsing unambiguous. The operation depends on MR8 target layout; complete behavior for every expression/type form is not a portable language guarantee.

14. Entry functions and MR8 module behavior

For modular MR8 code generation, a module may define main or runtime, but not both. The generated module entry dispatch invokes runtime when present; otherwise it invokes main when present. A user-defined __entry takes control of the entry path. The mutual-exclusion check is specific to modular code generation.

let main(): void {
    start();
}
let runtime(): void {
    start_service();
}

Runtime modules may receive compiler-generated __module_runtime service data. Entry dispatch and this data structure are MR8 ABI/toolchain behavior, not general source-language semantics.

15. Areas not fully specified

The compiler source does not establish a complete, target-independent specification for all of these areas:

  1. text encoding and the complete string storage/termination model;
  2. full integer literal range and overflow diagnostics;
  3. all signed/unsigned comparison and conversion rules;
  4. division and remainder edge cases such as division by zero;
  5. complete type inference and validation for omitted annotations;
  6. universal constant immutability guarantees;
  7. array bounds, pointer validity, aliasing, and memory safety;
  8. alignment behavior as a source-language rule;
  9. precise binary ! search semantics;
  10. complete shift-count behavior for binary ~;
  11. evaluation order for all expression operands;
  12. exact semantics of all system-bus forms;
  13. duplicate label behavior and all invalid-control-flow diagnostics;
  14. portable structure layout and ABI guarantees across targets.

This reference should be revised when these rules are formally adopted. It deliberately avoids describing implementation gaps as undefined behavior unless the language or target specification defines them that way.