Stick And String (SAS 1.1)
A human-readable, deterministic data serialization format.
SAS is designed to be easy to read and write by hand, while remaining strict enough to parse unambiguously — no comma errors, no indentation-sensitivity, and full JSON interoperability.
# SAS 1.1 example
__sas_version__ -> "1.1"
server ::
host -> "localhost"
port -> 8080
tags -> ["api" | "production"]
tls -> true
:: server
Install
Node.js / Browser
npm install stick-and-string
Python
pip install stick-and-string
Quick Start
JavaScript
import { parseSAS, sasToJSON, jsonToSAS } from 'stick-and-string';
// Parse SAS → JS object
const obj = parseSAS(`
server ::
host -> "localhost"
port -> 8080
:: server
`);
console.log(obj.server.port); // 8080
// SAS → JSON string
const json = sasToJSON(sasSource);
// JS object / JSON string → SAS
const sas = jsonToSAS({ host: 'localhost', port: 8080 });
CommonJS
const { parseSAS, sasToJSON, jsonToSAS } = require('stick-and-string');
Browser (ESM)
<script type="module">
import { parseSAS } from './node_modules/stick-and-string/index.mjs';
</script>
Python
from sas_tools import parse_sas, sas_to_json, json_to_sas
# Parse SAS → dict
obj = parse_sas(open("config.sas").read())
print(obj["server"]["port"]) # 8080
# SAS → JSON string
json_str = sas_to_json(open("config.sas").read())
# dict → SAS string
sas = json_to_sas({"host": "localhost", "port": 8080})
CLI
Both packages install a sas command.
# Validate a SAS file
sas validate config.sas
# Convert SAS → JSON
sas to-json config.sas
sas to-json config.sas --output config.json
sas to-json config.sas --compact
# Convert JSON → SAS
sas to-sas data.json
sas to-sas data.json --output data.sas --no-version
# Round-trip check (great for CI)
sas roundtrip config.sas
sas roundtrip data.json
Format Overview
Key → Value pairs
name -> "Alice"
port -> 8080
enabled -> true
ratio -> 3.14
nothing -> null
Block objects (named open/close)
server ::
host -> "db.internal"
port -> 5432
:: server
Inline objects (small, flat)
origin -> { lat -> 37.77 | lon -> -122.41 }
Inline arrays (scalars)
tags -> ["api" | "production" | "v2"]
Block arrays
hosts ::
- "web1.internal"
- "web2.internal"
:: hosts
Block arrays of objects
servers ::
- ::
host -> "a.local"
port -> 8080
:: -
- ::
host -> "b.local"
port -> 9090
:: -
:: servers
Multiline strings
description -> """
Line one.
Line two.
"""
Comments
# This is a comment
# Inline comments are NOT permitted
API Reference
JavaScript
| Export | Signature | Description |
|---|---|---|
parseSAS |
(source: string) → object |
Parse SAS document |
sasToJSON |
(source, {indent, stripVersion}) → string |
SAS → JSON string |
sasToObject |
(source) → object |
SAS → plain JS object |
jsonToSAS |
(input, {versionHeader, indent}) → string |
object/JSON → SAS |
SASParseError |
class | Parse error with .lineNum |
JSONToSASError |
class | Serialization error with .path |
Python
| Export | Signature | Description |
|---|---|---|
parse_sas |
(source: str) → dict |
Parse SAS document |
sas_to_json |
(source, indent, strip_version) → str |
SAS → JSON string |
sas_to_object |
(source) → dict |
SAS → Python dict |
json_to_sas |
(input, version_header, indent) → str |
dict/JSON → SAS |
SASParseError |
class | Parse error with .line_num |
JSONToSASError |
class | Serialization error with .path |
Error Codes
All errors include a line number. Parser errors are never silent.
| Code | Description |
|---|---|
| E01 | Duplicate key in same scope |
| E02 | Block closer mismatch or bare :: |
| E03 | Unclosed block or multiline string at EOF |
| E04 | Invalid escape sequence |
| E05 | Invalid number (leading zero, +, NaN, etc.) |
| E06 | Wrong-case boolean or null (True, NULL, …) |
| E07 | Inline comment |
| E08 | Missing spaces around -> |
| E09 | Missing spaces around | in inline array/object |
| E10 | Trailing | |
| E11 | Non-scalar in inline array |
| E12 | Nested inline object |
| E13 | Key starting with - |
| E14 | Mixed block content (array items + pairs) |
| E15 | Anonymous closer outside array context |
Publishing
npm
cd npm/
npm publish --access public
PyPI
cd pip/
pip install build twine
python -m build
twine upload dist/*
License
Copyright (c) 2025 Sourasish Das
Licensed under the Vexon Open-Control License (VOCL) 1.0.
Metadata
Release files for stick-and-string 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| stick_and_string-1.1.0.tar.gz | 14.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| stick_and_string-1.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 28.3 kB
Release files / stick_and_string-1.1.0.tar.gz
| Download URL | stick_and_string-1.1.0.tar.gz |
|---|---|
| Size | 14.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b29b4a854a7c36364dda259b2dfd814edc01c27f3d949d640d9c2bae7ad7723b
|
|
BLAKE2b-256 checksum How to use checksums |
055702d18fcadf36f6d4e4887454a681412bf4c9c86885e110edce08686eaf16
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Release files / stick_and_string-1.1.0-py3-none-any.whl
| Download URL | stick_and_string-1.1.0-py3-none-any.whl |
|---|---|
| Size | 14.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
934e46d2ac507b89bd8e6fc9b29813ffe7d8f534ea7a69d1cfb99f63df90bacf
|
|
BLAKE2b-256 checksum How to use checksums |
8a457c5a3528a333f80e0271d80887f968afc010d797e8da744211436f611a34
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|