# Rust Scopes List Notation Parser

This is a configurable parser for [Scopes List Notation (SLN)](https://scopes.readthedocs.io/en/latest/dataformat/), written in Rust.

SLN was invented for the [Scopes programming language](http://scopes.rocks), so also take a look at it.

It parses input using the [token parser](https://gitlab.com/porky11/token-parser), which is an intermediate representation for code structure. In short, every value is either a symbol (represented as a string) or a parser (parsing other tokens).

## Representation

SLN is a simple representation suitable for both code and data. It is indentation-based (similar to Python) and directly maps to a list representation (like Lisp). It also supports brackets for nesting, which is most useful for single-line expressions. You can use brackets to opt out of indentation entirely, treating SLN as a pure s-expression parser, or mix both styles.

## Benefits

This notation offers several advantages:
- **Simplicity and Flexibility**: It can replace common text formats like XML, JSON, TOML, YAML...
- **Multiline Strings**: Ideal for embedding long texts (like HTML) or code from other languages (like shader code).
- **Token Parser Integration**: Parsed output is compatible with the token parser crate. You can reuse parsers or write your own without rewriting core logic.
- **Prototyping for Languages**: For DSLs or new languages, start with SLN and switch representations later without major refactors.

## Usage

First, create a parser. Use the default `Parser::new()` for minimal features or `Parser::scopes()` for full SLN support (matching the Scopes language syntax). Then, parse any iterable of characters (from a string or file content) into a token parser.

Here's a basic example:

```rust
use std::fs;

use rslnp::{Error, Parser};

fn main() -> Result<(), Error> {
    // Create a parser configured for SLN
    let parser = Parser::scopes();

    // Parse a string
    let input = r#"
# A comment
hello-world # Single symbol
list item1 item2 # List of items

# Multiline string
""""
    This is a multiline string.
    It preserves newlines and removes the indentation.
"#;

    let mut token_parser = parser.parse(input.chars())?;

    // Handle tokens using the token parser

    Ok(())
}
```

Strings are parsed into lists. If prefixed (configurable), they become a list with a prefix symbol (like "string") followed by the content symbol. Otherwise, it's a single content symbol.

For full details, see the [API documentation](https://docs.rs/rslnp).

## Configuration

The parser is highly configurable via builder methods. Key options include:

- **Indentation**: Set the number of spaces for one nesting level with `indent(u8)`. Set to 0 to disable indentation-based nesting.
- **Unpack Single Elements**: Use `unpack_single(bool)` to interpret lines with a single element as lists (or not).
- **Multi-Indent**: Allow multiple indent levels per line with `allow_multi_indent(bool)`.
- **Brackets**: Add bracket pairs with `with_brackets(left: char, right: char, prefix: Option<&str>)` or disable with `without_brackets()`.
- **Strings**: Add delimiters with `with_strings(delimiter: char, prefix: Option<&str>)` or disable with `without_strings()`.
- **Comments**: Add comment starters with `with_comments(char)` or disable with `without_comments()`.
- **Separators**: Add inline list separators with `with_separator(char)` or disable with `without_separator()`.
- **Symbol Characters**: Add always-single-char symbols with `with_symbol_character(char)` or disable with `without_symbol_character()`.

Example configuration:

```rust
let custom_parser = Parser::new()
    .indent(2) // 2 spaces per level
    .unpack_single(false)
    .with_brackets('[', ']', Some("array"))
    .with_strings('\'', None) // Single-quoted strings without prefix
    .with_comments(';'); // Semicolon comments
```
