Status: Draft
Version: 0.3
Date: June 12, 2026
Author: Egor Merkushev
License: MIT
This document defines the formal syntax of the Hypercode .hc file format using a Backus–Naur Form (BNF) grammar.
The goal is to provide an unambiguous reference for tool developers, parser authors, and implementers of Hypercode engines.
<hypercode> ::= { <command-line> }
<command-line> ::= <command> <newline> [<block>]
<command> ::= <identifier> [<class>] [<id>]
<class> ::= "." <identifier>
<id> ::= "#" <identifier>
<block> ::= <INDENT> { <command-line> } <DEDENT>
<identifier> ::= <letter> { <letter> | <digit> | "_" | "-" }
<letter> ::= "A" | ... | "Z" | "a" | ... | "z"
<digit> ::= "0" | ... | "9"
<newline> ::= "\n"
<INDENT> ::= (synthetic token emitted by the lexer when <indent> depth increases)
<DEDENT> ::= (synthetic token emitted by the lexer when <indent> depth decreases)
<indent> ::= <spaces> | <tabs>
<spaces> ::= <space> { <space> }
<tabs> ::= <tab> { <tab> }
<space> ::= " "
<tab> ::= "\t"
App
Logger.console
Database.pooled#primary-db
Connect
Migrate
WebServer#main
Listen
RegisterRoutes
HealthCheck.public
GetUsers.private
App
├── Logger (class: console)
├── Database (class: pooled, id: primary-db)
│ ├── Connect
│ └── Migrate
└── WebServer (id: main)
├── Listen
└── RegisterRoutes
├── HealthCheck (class: public)
└── GetUsers (class: private)
Service
SubService
Task
Worker.task#main
Root
Sub ← inconsistent indent (3 spaces?)
@bad#id
- Identifiers must not contain whitespace or special symbols.
- Indentation must be consistent (e.g., 2 or 4 spaces, or tabs—but not mixed).
- Indentation is significant (off-side rule): a nested
<block>must be indented deeper than its parent<command-line>. The lexer reads each line's leading<indent>, tracks it on an indentation stack, and emits the synthetic<INDENT>/<DEDENT>tokens when the depth increases or decreases. Because this context-sensitive relationship cannot be expressed in pure BNF, indentation handling is delegated to the lexer (seeHypercodeLexer.g4). - No support for inline attributes or arguments in
.hcfiles (these belong in.hcs).
A .hcs file contains optional import directives, cascade rules and optional
contract blocks.
<sheet> ::= { <import-stmt> } { <top-level-block> }
<import-stmt> ::= "@import" <ws> <quoted-string> <newline>
<top-level-block> ::= <dimension-block> | <contract-block> | <rule-block>
<dimension-block> ::= "@" <identifier> "[" <value> "]" ":" <newline>
<INDENT> { <rule-block> } <DEDENT>
<value> ::= <identifier>
<rule-block> ::= <selector> ":" <newline>
<INDENT> { <property-line> } <DEDENT>
<property-line> ::= <identifier> ":" <scalar> <newline>
<scalar> ::= <quoted-string> | <bare-scalar>
<quoted-string> ::= '"' { <char> } '"' | "'" { <char> } "'"
<bare-scalar> ::= any run of characters up to end of line (trimmed)
A bare scalar is type-inferred by the reader: true/false → bool, integral
form → int, decimal form without letters → double, anything else → string
(e.g. driver: sqlite is a valid bare string). Quoting forces string. The
source lexeme is preserved verbatim for v1 IR round-tripping.
@import "path.hcs" includes another sheet. All imports must precede rules,
context blocks and contracts (CSS discipline). An imported sheet expands
depth-first at the position of the directive, so the importing sheet's
own rules come later in source order and win specificity ties — the importer
overrides what it imports. Each sheet is expanded at most once per resolution
(diamond imports are deduplicated); a cyclic import is an error. Every rule
keeps the file it was defined in for provenance. Paths resolve relative to
the importing sheet's location.
<selector> ::= <simple-selector> { ">" <simple-selector> }
<simple-selector> ::= <type-sel> | <class-sel> | <id-sel>
<type-sel> ::= <identifier>
<class-sel> ::= "." <identifier>
<id-sel> ::= "#" <identifier>
Specificity (highest to lowest): id #x > class .x > type x.
A @contract: block declares property constraints for nodes matching a selector.
More-specific selectors may only narrow constraints — never widen them (monotonicity invariant).
<contract-block> ::= "@contract:" <newline>
<INDENT> { <contract-selector> } <DEDENT>
<contract-selector> ::= <selector> ":" <newline>
<INDENT> { <constraint-line> } <DEDENT>
<constraint-line> ::= <constraint-key> ":" <constraint-type>
[ ">=" <number> ] [ "<=" <number> ] <newline>
<constraint-key> ::= <identifier> [ "[?]" ] // "[?]" marks optional property
<constraint-type> ::= "string" | "int" | "float" | "bool"
@contract:
service:
timeout[?]: int >= 1 <= 300
name: string
.primary:
timeout: int >= 10 <= 200 # narrows — allowed
How contracts accumulate, intersect, and which monotonicity violations are diagnostics (HC2101–HC2103) is defined in the resolution semantics — this document covers syntax only.
- Define EBNF with optional comments, arguments, and macro support.
- Add parser conformance test suite.
- Define formal AST schema (YAML or JSON).
Version 0.3 (2026-06-12)
- Added
@importdirective grammar (HC-116):<import-stmt>precedes all blocks; expansion order, import-once and cycle semantics summarized here, defined normatively inHypercode_Resolution.md§5.1.
Version 0.2 (2026-06-11)
- Added Section 6:
.hcscascade sheet syntax (rules, selectors, dimension blocks). - Added
@contract:block grammar (HC-111): constraint-line syntax,[?]optional marker, bounds>=/<=. - Bare scalars documented (type inference, lexeme preservation); semantics
(contract accumulation, monotonicity diagnostics) live in
Hypercode_Resolution.md.
Version 0.1 (2025-07-12)
- Initial public draft of the Hypercode grammar in BNF.
- Describes core structural elements: command, class, ID, indentation-based hierarchy.