Source text and lexical structure
Before the compiler can reason about types, it turns UTF-8 source text into tokens. Most formatting is flexible, but literal spelling and statement termination are deliberately small and predictable.
Source files
Kinmokusei source files conventionally use .km and contain UTF-8 text. A file is a module scope, not textual inclusion. Importing a file makes selected declarations available; it does not paste its source into the caller.
Raw NUL characters are rejected, including inside strings and comments. To store a NUL byte in a string, write an escape such as "\x00" instead.
application/
├── main.km
└── formatting.kmDiagnostics refer to the original path, one-based line and column, and zero-based UTF-8 byte offsets. Generated Go locations are not substituted for source locations.
Whitespace
Spaces and tabs separate tokens. Line breaks can terminate complete statements, but incomplete expressions continue across them. These declarations parse the same way:
const answer = 42;const answer =
42;The parser recognizes an omitted semicolon at a completed statement boundary; the lexer does not insert tokens into multiline expressions or types.
Comments
Line comments begin with // and continue to the end of the line:
const timeout = 30; // secondsBlock comments begin with /* and end at the next */:
/* The retry count is intentionally conservative.
Keep this synchronized with the service policy. */
const retries = 3;Block comments are not nested. An unclosed block comment is a lexical diagnostic.
Identifiers
An identifier begins with _ or a Unicode letter. Later characters may also be Unicode digits.
const service2 = "api";
const 温度 = 42;
const _internal = true;Names are case-sensitive: User, user, and USER are different bindings. The compiler separately checks whether public names collide after conversion to generated Go naming conventions.
The single identifier _ is the blank binding in supported binding lists, ranges, catches, and cases. It means “this position is intentionally unused”; it does not introduce a readable local variable.
Keywords
Keywords cannot be used as ordinary identifiers. The major families are:
| Family | Keywords |
|---|---|
| Declarations | const, let, function, class, struct, interface, constructor |
| Relationships | extends, implements, virtual, override, final, abstract |
| Visibility/member kind | public, private, protected, static |
| Control flow | if, else, while, for, of, switch, case, default |
| Branching | return, break, continue, goto, fallthrough |
| Errors | try, catch, finally, throw |
| Concurrency | go, await, detach, select |
| Values and operators | true, false, nil, null, new, this, super, as |
| Boundaries | import, from, export, defer |
type, alias, distinct, enum, constraint, accessor get / set, and struct-method pointer are recognized contextually. Treat them as reserved in those grammar positions even though their lexer representation differs from the fixed keyword set.
Numeric literals
Integer literals accept decimal digits and Go-compatible binary, octal, and hexadecimal forms:
const count = 42;
const zero = 0;
const mask = 0xff;
const bits = 0b1010;
const permissions = 0o755;
const grouped = 1_000;A floating-point literal may contain a decimal point or exponent; hexadecimal floats use a binary exponent:
const ratio = 0.25;
const whole = 42.0;
const fraction = .5;
const exponent = 1e3;
const hexadecimal = 0x1.8p2;
const imaginary = 2.5i;A leading sign is a unary operator, not part of the literal:
const minimum: int8 = -128;Separator placement and radix digits follow Go's literal rules. Untyped integer, floating, and complex constants default to int, float64, and complex128 without another expected type. Constants retain precision until a typed boundary checks representability; runtime numeric values never widen implicitly. Integer-valued untyped numeric constants may also be used in indices, slice bounds, and collection/channel sizes.
String literals
Strings use double quotes and remain on one source line:
const plain = "hello";
const escaped = "first\nsecond";
const unicode = "温泉たまご";
const raw: bstring = b"\xff\x00";Escapes follow Go-compatible quoted string escapes. Supported control escapes are \a, \b, \f, \n, \r, \t and \v; \\ and \" escape a backslash and a double quote. \xNN and three-digit octal escapes encode one byte, while \uNNNN and \UNNNNNNNN encode a valid Unicode code point as UTF-8. Digit counts are exact; octal values must fit one byte. For example, "\x00" or "\000" stores NUL, but "\0" is not a valid Go-style escape.
A raw newline, missing closing quote, invalid escape, surrogate code point or code point above U+10FFFF is a lexical diagnostic. Single-quoted character literals, raw-string/backtick and template/interpolation syntax are not implemented. Use an int32 code-point value and string(codePoint) when needed.
Source text must be UTF-8. An ordinary literal also checks its decoded value: "\xff" is rejected, while "\xc3\xa9" is valid UTF-8. Prefix a quoted literal with an immediately adjacent b to create a bstring containing arbitrary bytes: b"\xff" is valid. Both forms use the same escape grammar; the prefix does not enable raw/backtick literals. Indexing returns a byte; range iteration yields code points and, in its two-binding form, byte offsets. See strings and Unicode for conversions, validation and slicing rules.
Boolean and absence literals
true and false have type boolean. Conditions accept only boolean; no value is converted by truthiness.
null belongs to checked nullable types such as User | null. nil is the lower-level Go nil value used at nil-capable Go boundaries. They are intentionally distinct and are not interchangeable by an implicit conversion.
Punctuation and semicolons
Parentheses group expressions and delimit conditions, parameters, and arguments. Braces delimit blocks and literal bodies. Brackets form arrays, indexing, slicing, and Go-shaped explicit type arguments.
The following forms use a semicolon terminator, which may be omitted at a completed line, before }, or at end of file:
- imports and binding declarations;
return,throw, branch, assignment, update, and expression statements;- channel sends, raw
gocalls,defer, anddetach; - grouped C export declarations where the grammar shows one.
Do not write a semicolon after a function, class, struct, interface, loop, switch, select, or try block.
Two statements on the same line still require a separator, and a three-clause for always requires both ; separators. Expressions can continue across lines: call followed by (value) is one call, and value followed by [index] is one indexing expression. Use an explicit ; to separate statements that would otherwise join. A newline inside a block comment also counts as a line break.
An immediate newline after return or throw ends a bare return or rethrow. A value-returning function rejects a bare return; a bare rethrow requires a catch. Keep the value, or its opening (, on the keyword's line when writing a multiline operand. Optional labels following break and continue must also remain on the keyword's line. These restricted-newline rules apply even if other statements use explicit semicolons.
function main(): void {
let count = 0;
count++;
if (count === 1) {
console("ready");
}
}What fails before parsing
The lexer reports invalid UTF-8, an unexpected character or character sequence, an unterminated string/comment, and an invalid string escape. Parsing continues where possible so one check can report more than the first mistake.
With the token boundaries established, Bindings and scope explains how declarations introduce names and how long those names remain valid.