Expand description
§repl_lang
The parts of a read-eval-print loop that every language needs and none should have to write twice: deciding when typed input forms a complete entry, keeping the session’s entries so diagnostics can point back into them, and editing the line being typed.
The crate owns no syntax and does no I/O. A language plugs in its own pipeline — lex, parse, evaluate — as a closure, and its own terminal or line source. repl-lang supplies the loop state between them:
Sessionaccumulates lines into an entry, runs the pipeline on it after each line, and commits the entry once the pipeline reportsStatus::Complete. Every committed entry becomes a source named<repl:N>in oneSourceMap, so adiag_langrenderer can show any diagnostic from any entry.Inputis what the pipeline sees: the entry so far, its global position, alexer_lang::Cursoralready placed there, andis_incomplete— the “errors only at the end of input mean keep typing” test that turns any error-reporting parser into a continuation-line detector.Editoris a terminal-agnostic line editor: cursor motion by visible character and by word, kill and yank, and a bounded history, driven byEditcommands the host maps from its own key events.
§Example
A session over a fixed script, with a pipeline that asks for more input until brackets balance:
use repl_lang::{Feed, Session, Status};
let mut session = Session::new();
let mut prompts = Vec::new();
for line in ["sum([1, 2,", " 3, 4])", "len([])"] {
prompts.push(if session.is_pending() { "..." } else { ">>>" });
let feed = session.feed(line, |input| {
let open = input.text().matches('[').count() + input.text().matches('(').count();
let close = input.text().matches(']').count() + input.text().matches(')').count();
if open > close { Status::Incomplete } else { Status::Complete(input.text().len()) }
})?;
if let Feed::Complete { entry, value } = feed {
println!("entry {} complete: {value} bytes", entry.number());
}
}
assert_eq!(prompts, [">>>", "...", ">>>"]);
assert_eq!(session.len(), 2);
assert_eq!(session.entry(1).map(|e| e.name()), Some("<repl:1>"));§Wiring a parser
With a parser that reports a missing token at the end of input — as
parser_lang does — the whole completeness
check is one call:
session.feed(line, |input| {
let tokens = lex(input.cursor()); // spans are already global
let mut parser = Parser::new(&tokens);
let program = parse_program(&mut parser);
let errors = parser.into_errors();
if input.is_incomplete(&errors) {
Status::Incomplete // "1 + (2 *" — keep reading
} else {
Status::Complete((program, errors)) // done, right or wrong
}
})The calc example in the repository is a complete REPL built this way.
§Features
std(default) — the standard library. Without it the crate is#![no_std]and needs onlyalloc; neither the session nor the editor uses operating-system facilities.
§Stability
The public surface is frozen and stable as of 1.0.0: it follows Semantic
Versioning, with no breaking changes before 2.0. The surface, the
behaviour that is part of the contract — entry naming and layout, the
completeness rule, the history and display-unit rules — and the SemVer
promise are catalogued in
docs/API.md.
Structs§
- BytePos
- A zero-based byte offset into a single source buffer.
- Cursor
- A zero-copy cursor over a
&str, the primitive every lexer is built on. - Diagnostic
- One thing a compiler stage has to say about the source: a
Severity, a headline message, a primaryLabelpointing at the span it concerns, and optionally secondary labels for related locations and trailing note/help lines. - Editor
- The editing state of one input line: its text, the cursor, a kill buffer, and a bounded history of submitted lines.
- Entry
- A committed entry: its number and where its text lives in the session’s source map.
- Input
- The pending text of one REPL entry, as the pipeline sees it.
- Session
- The state of one REPL session: the entry being typed, and every entry accepted so far.
- Source
File - A single source held by a
SourceMap: a display name, the owned source text, and the half-openSpanthe text occupies in the map’s global position space. - Source
Id - A small, copyable handle to one source in a
SourceMap. - Source
Map - A collection of sources laid out end to end in a single global position space.
- Span
- A half-open byte range
start..endinto a single source.