Documentation
Complete reference for Mad Sam Notation — from first install to advanced usage. Everything you need to master MSN, its CLI, parser API, and ecosystem.
What is MSN?
Mad Sam Notation (MSN) is a token-efficient hierarchical data language that compiles directly to JSON. It was designed to minimize token usage in AI/LLM workflows while remaining human-readable and writable.
Key characteristics:
- Uses dash prefixes to represent hierarchy depth — no brackets, braces, or commas
- Up to 60% fewer tokens than equivalent JSON
- Automatic type inference for numbers, booleans, null, and strings
- Full JSON compatibility — every MSN file compiles to valid JSON
- Unlimited nesting depth — just keep adding dashes
- Comments are first-class citizens
- MIT licensed and fully open source
MSN files use the .msn extension and the MIME type application/x-msn. All files MUST be encoded in UTF-8.
Installation
CLI Tool — for compiling, validating, and formatting MSN files from the terminal:
npm install -g @madsn/cliParser Library — for integrating MSN into your Node.js / TypeScript projects:
npm install @madsn/parserValidator — standalone validation of MSN syntax:
npm install @madsn/validatorFormatter — auto-format MSN source files:
npm install @madsn/formatterVS Code Extension — search for "MSN" in the VS Code extensions marketplace, or install msn-vscode for syntax highlighting and language support.
Quick Start
1. Create a file called config.msn:
- name: My Application
- version: 1.0.0
- server
-- host: localhost
-- port: 30002. Compile with the CLI:
msn compile config.msnOutput:
{
"name": "My Application",
"version": "1.0.0",
"server": {
"host": "localhost",
"port": 3000
}
}3. Or use the library in code:
import { compile } from "@madsn/parser";
import { readFileSync } from "fs";
const source = readFileSync("config.msn", "utf-8");
const result = compile(source);
console.log(result);
// { name: "My Application", version: "1.0.0", server: { host: "localhost", port: 3000 } }4. Save to a file:
msn compile config.msn --output config.jsonLine Structure
Every MSN line follows one of these patterns:
- key: valueKey-value pair- keyContainer (creates nested object)-- * valueArray item (simple value)-- *Array object marker (children become object properties)# commentComment (ignored during compilation)Rules:
- Every data line MUST start with one or more dashes -
- There MUST be a space after the dash prefix
- Lines not starting with - or # (excluding blank lines) are invalid
Depth & Nesting
The number of dashes determines the depth level. Each additional dash increases the nesting depth by one.
- root # depth 1 (1 dash)
-- child # depth 2 (2 dashes)
--- grandchild: value # depth 3 (3 dashes)
---- deep: value # depth 4 (4 dashes)Compiles to:
{
"root": {
"child": {
"grandchild": "value",
"deep": "value"
}
}
}Depth Rules:
1. Depth can increase by at most 1 per line (you can't jump from 1 dash to 3 dashes)
2. Depth can decrease by any amount (closing multiple levels at once)
3. A line with N dashes is a child of the nearest preceding line with N-1 dashes
4. There is no limit to nesting depth — just keep adding dashes
Example — decreasing depth:
- server
-- ssl
--- enabled: true
--- cert: /path/to/cert.pem
-- host: localhost
- database
-- host: db.example.comNotice how -- host: localhost drops back to depth 2, closing the ssl object. Then - database drops to depth 1, closing server.
Key-Value Pairs
A key-value pair is written as dashes, a space, the key, a colon, a space, and the value:
- name: John
- age: 30
- active: true
- score: 95.5
- data: nullCompiles to:
{
"name": "John",
"age": 30,
"active": true,
"score": 95.5,
"data": null
}Rules:
1. The colon : separates the key from the value
2. There MUST be a space after the colon
3. Keys MUST NOT contain colons (unless the value part is used correctly)
4. Keys are case-sensitive
5. Leading/trailing whitespace in values is trimmed
Type Inference
MSN automatically infers types from values — no explicit typing needed:
42number42- count: 423.14number3.14- pi: 3.14-7number-7- offset: -7truebooleantrue- active: truefalsebooleanfalse- debug: falsenullnullnull- data: nullhellostring"hello"- name: hello"42"string"42"- code: "42"Rules:
1. Unquoted integers and floats become number
2. true and false become boolean
3. null becomes null
4. Everything else becomes string
5. Values wrapped in double quotes are always treated as string (use this to force a number to be a string)
6. Leading/trailing whitespace is trimmed before inference
Containers (Objects)
A key without a value creates a container — a nested object whose properties are defined by its children:
- database
-- host: localhost
-- port: 5432
-- credentials
--- username: admin
--- password: secretCompiles to:
{
"database": {
"host": "localhost",
"port": 5432,
"credentials": {
"username": "admin",
"password": "secret"
}
}
}Containers can be nested to any depth. Any line with only a key (no colon, no value) signals that the following deeper lines are its children.
Arrays
Use an asterisk * after the dash prefix to create array items:
Simple arrays — values on the same line as *:
- colors
-- * red
-- * green
-- * blueCompiles to:
{
"colors": ["red", "green", "blue"]
}Rules:
1. Array items use * after the dash prefix: -- * value
2. There MUST be a space after the *
3. All array items under a parent MUST be at the same depth
4. Array items inherit type inference (-- * 42 becomes the number 42)
5. Array items and key-value pairs SHOULD NOT be mixed under the same parent
Array Objects
A bare * with no value creates an array object entry. Children at a deeper level become the properties of that object:
- users
-- *
--- name: Alice
--- age: 30
--- role: admin
-- *
--- name: Bob
--- age: 25
--- role: userCompiles to:
{
"users": [
{ "name": "Alice", "age": 30, "role": "admin" },
{ "name": "Bob", "age": 25, "role": "user" }
]
}Nested array objects — arrays of objects can contain their own nested objects:
- services
-- *
--- name: web
--- config
---- port: 80
---- ssl: true
-- *
--- name: api
--- config
---- port: 3000
---- ssl: falseCompiles to:
{
"services": [
{ "name": "web", "config": { "port": 80, "ssl": true } },
{ "name": "api", "config": { "port": 3000, "ssl": false } }
]
}Multiline Values
MSN supports two multiline modes using the pipe | and angle bracket > characters:
Literal Block (|) — preserves line breaks:
- description: |
-- This is line one.
-- This is line two.
-- This is line three.Produces: "This is line one.\nThis is line two.\nThis is line three."
Folded Block (>) — joins lines with spaces:
- summary: >
-- This is a long paragraph
-- that will be joined into
-- a single line with spaces.Produces: "This is a long paragraph that will be joined into a single line with spaces."
Rules:
1. | preserves line breaks (literal block)
2. > folds lines into a single line joined by spaces (folded block)
3. Continuation lines MUST be at depth N+1 relative to the key
4. Continuation lines are treated as text, not as key-value pairs
Parser API (@madsn/parser)
The core parser package provides tokenization, parsing, AST construction, and compilation to JSON.
compile(source: string): JsonValue
Compiles MSN source text to a JSON-compatible JavaScript value (object, array, string, number, boolean, or null).
import { compile } from "@madsn/parser";
const result = compile(`
- name: My App
- version: 1.0.0
- server
-- host: localhost
-- port: 3000
`);
console.log(result);
// { name: "My App", version: "1.0.0", server: { host: "localhost", port: 3000 } }compileToString(source: string, indent?: number): string
Compiles MSN source and returns a formatted JSON string.
import { compileToString } from "@madsn/parser";
const json = compileToString("- name: hello\n- count: 42", 2);
// '{ "name": "hello", "count": 42 }'class Lexer
Tokenizes MSN source code into a stream of typed tokens.
import { Lexer } from "@madsn/parser";
const lexer = new Lexer("- name: hello");
const tokens = lexer.tokenize();
// [{ type: "KEY_VALUE", depth: 1, key: "name", value: "hello", ... }]class Parser
Parses MSN source into an Abstract Syntax Tree (AST).
import { Parser } from "@madsn/parser";
const parser = new Parser();
const ast = parser.parse("- name: hello");
// { type: "root", children: [{ type: "value", key: "name", value: "hello", ... }] }class Compiler
Converts MSN source to JSON (combines Lexer + Parser + AST evaluation).
import { Compiler } from "@madsn/parser";
const compiler = new Compiler();
const obj = compiler.compile("- name: hello");
const str = compiler.compileToString("- name: hello", 2);inferType(value: string): string | number | boolean | null
Infers the JSON type from a raw string value.
import { inferType } from "@madsn/parser";
inferType("42"); // 42 (number)
inferType("true"); // true (boolean)
inferType("null"); // null
inferType("hello"); // "hello" (string)
inferType('"42"'); // "42" (forced string)Validator API (@madsn/validator)
The validator checks MSN syntax and returns an array of errors and warnings.
validate(source: string): ValidationError[]
Validates MSN syntax and returns errors.
import { validate } from "@madsn/validator";
const errors = validate("- name: hello");
// [] (no errors)
const bad = validate("name: hello");
// [{ message: "Line must start with dashes", line: 1, severity: "error" }]validateIndentation(source: string): ValidationError[]
Checks for indentation-related issues (warnings about inconsistent spacing, depth jumps, etc.).
validateAll(source: string): ValidationError[]
Runs all validation checks — syntax errors, indentation warnings, and structural issues.
ValidationError type:
interface ValidationError {
message: string;
line: number;
column?: number;
severity: "error" | "warning";
}Formatter API (@madsn/formatter)
The formatter normalizes MSN source code for consistent style.
format(source: string, options?: FormatOptions): string
Formats MSN source text — trims whitespace, normalizes dash spacing, ensures consistent style.
import { format } from "@madsn/formatter";
const formatted = format(" - name: hello ");
// "- name: hello\n"FormatOptions:
finalNewlinebooleantrueAdd a newline at the end of the fileremoveBlankLinesbooleanfalseRemove blank lines between entriesimport { format } from "@madsn/formatter";
const formatted = format(source, {
finalNewline: true,
removeBlankLines: false,
});TypeScript Types
All packages export TypeScript types for full type safety.
Token — represents a single lexer token:
interface Token {
type: TokenType;
depth: number;
key?: string;
value?: string;
raw: string;
line: number;
multilineMode?: "|" | ">";
}ASTNode — a node in the abstract syntax tree:
interface ASTNode {
type: "object" | "array" | "value" | "array-item" | "root";
key?: string;
value?: JsonValue;
children: ASTNode[];
line?: number;
depth?: number;
}JsonValue — any valid JSON value:
type JsonValue =
| string
| number
| boolean
| null
| JsonValue[]
| { [key: string]: JsonValue };ValidationError — returned by the validator:
interface ValidationError {
message: string;
line: number;
column?: number;
severity: "error" | "warning";
}CLI Installation
Install the MSN CLI globally:
npm install -g @madsn/cliVerify the installation:
msn --version
msn --helpCLI Commands
msn compile — Compile an MSN file to JSON
msn compile config.msn
msn compile config.msn --indent 4
msn compile config.msn --output config.jsonmsn parse — Parse an MSN file and output the AST (useful for debugging)
msn parse config.msnmsn validate — Validate an MSN file for syntax errors
msn validate config.msnmsn format — Auto-format an MSN file
msn format config.msn
msn format config.msn --output formatted.msnCLI Options & Stdin
Options:
-o, --output Write output to a file instead of stdout-i, --indent JSON indentation level (default: 2)--stdinRead MSN input from stdin instead of a file-h, --helpShow help information-v, --versionShow the installed versionReading from stdin:
cat config.msn | msn compile --stdin
echo "- name: test" | msn compile --stdinPiping output:
msn compile config.msn | jq '.server.port'
msn compile config.msn > output.jsonJSON Mapping
MSN maps to JSON with these correspondences:
- key: value{ "key": value }- key (with children){ "key": { ... } }-- * value[ value, ... ]-- * (with children)[ { ... }, ... ]{ "key": "line1\nline2" }- key: > (folded){ "key": "line1 line2" }# comment*(omitted)*Formal Grammar (EBNF)
The formal grammar of MSN in Extended Backus-Naur Form:
document = { line } ;
line = comment | entry | blank ;
comment = { whitespace } , "#" , { any_char } ;
entry = dashes , " " , ( array_item | key_value | container ) , [ inline_comment ] ;
dashes = "-" , { "-" } ;
array_item = "* " , ( value | "" ) ;
key_value = key , ": " , value ;
container = key ;
key = identifier ;
value = string | number | boolean | null_val | multiline_marker ;
string = quoted_string | unquoted_string ;
number = integer | float ;
boolean = "true" | "false" ;
null_val = "null" ;
multiline_marker = "|" | ">" ;
inline_comment = " #" , { any_char } ;Error Handling
MSN parsers MUST report errors for:
1. Invalid line start — Lines not starting with - or # (excluding blank lines)
2. Depth jump — Depth increase greater than 1 between consecutive non-comment lines
3. Missing space after dashes — e.g. -name: value instead of - name: value
4. Missing space after colon — e.g. - name:value instead of - name: value
5. Mixed children — Array items and key-value pairs at the same depth under the same parent
6. Invalid multiline continuation — Continuation lines at wrong depth (must be exactly N+1)
Example error messages:
Error on line 3: Depth increased by 2 (from 1 to 3). Maximum increase is 1.
Error on line 5: Line must start with dashes (-).
Error on line 8: Missing space after colon in key-value pair.
Warning on line 12: Mixed array items and key-value pairs under the same parent.Project Structure
The MSN monorepo is organized into focused packages:
msn/
├── packages/
│ ├── parser/ Core parser (lexer, parser, compiler)
│ ├── cli/ Command-line interface
│ ├── validator/ Syntax validation
│ ├── formatter/ Auto-formatting
│ └── vscode-extension/ VS Code language support
├── tests/ Full test suite (Vitest)
├── docs/ Documentation source
├── examples/ Example MSN files
├── spec/ Language specification
├── playground/ Interactive web playground (standalone)
└── website/ Official website (Next.js)Packages overview:
@madsn/parsernpm i @madsn/parserCore tokenizer, parser, and compiler@madsn/clinpm i -g @madsn/cliTerminal commands: compile, parse, validate, format@madsn/validatornpm i @madsn/validatorSyntax and structure validation@madsn/formatternpm i @madsn/formatterAuto-format MSN source filesmsn-vscodeVS Code MarketplaceSyntax highlighting, language configVS Code Extension
The MSN VS Code extension provides:
- Syntax highlighting — full TextMate grammar for .msn files
- Language configuration — bracket matching, auto-closing, comment toggling
- File icon — recognizable icon for .msn files in the explorer
Installation:
Search for "MSN" in the VS Code extensions marketplace, or install via the command palette.
Features:
- Comments toggle with Ctrl+/
- Bracket matching for multiline blocks
- Auto-indentation for nested structures
- Recognized .msn file association
Contributing to the extension:
The extension source is in packages/vscode-extension/. The TextMate grammar is defined in syntaxes/msn.tmLanguage.json.
How to Contribute
MSN is fully open source under the MIT License. We welcome contributions of all kinds — code, documentation, bug reports, and ideas.
Step-by-step:
1. Fork the repository at github.com/sammadijaz/msn
2. Clone your fork locally:
git clone https://github.com/YOUR_USERNAME/msn.git
cd msn3. Install dependencies:
npm install4. Build all packages:
npm run build5. Create a branch:
git checkout -b feature/your-feature-name6. Make changes, write tests, update docs
7. Run tests:
npm test8. Submit a pull request with a clear description
Coding standards:
- TypeScript strict mode
- No any types without justification
- Write tests for all new features
- Follow the existing code style
Contribution Areas
Core Parser — Improve the lexer, parser, and compiler. Add error recovery, source maps, streaming support. *(Intermediate)*
CLI Tool — Add new commands, improve error messages, add watch mode, support config files. *(Beginner)*
VS Code Extension — Improve syntax highlighting, add IntelliSense, auto-completion, go-to-definition. *(Intermediate)*
Documentation — Write tutorials, improve API docs, add more examples, translate docs. *(Beginner)*
Playground — Improve the web playground with better error display, shareable URLs, themes. *(Beginner)*
Language Ports — Port the parser to other languages: Python, Rust, Go, Java, C#. *(Advanced)*
Reporting issues:
Please include the MSN input, expected output, actual output, Node.js version, and OS information.
Comments
Lines starting with
#are comments and are ignored during compilation:Rules:
1. Lines starting with
#(after optional whitespace) are full-line comments2.
#after a value (preceded by a space) starts an inline comment3. Comments are stripped during parsing and do not appear in output
4.
#inside quoted strings is NOT treated as a comment