Documentation
¶
Overview ¶
Package irb is a pure-Go (CGO=0) reimplementation of the deterministic, interpreter-independent core of Ruby's IRB — the REPL-support machinery that does not need a live Ruby interpreter to run.
IRB's job splits cleanly in two. One half is pure computation over source text: deciding whether the lines typed so far form a complete statement or need more input, computing the nesting / indent level that drives the `irb(main):001:1*` continuation prompt, expanding the prompt %-specs, formatting the `=> value` result line, and colourising a token stream. None of that needs to *evaluate* anything. The other half — actually evaluating the code, reading from the terminal, and persisting the history file — is inherently tied to the running interpreter and the host. This package implements the first half faithfully and leaves the second to the host (the go-embedded-ruby runtime, `rbgo`).
What lives here ¶
- Lex turns Ruby source into a Ripper-faithful token stream (events + lexer states), the substrate IRB's RubyLex, NestingParser and Color all build on.
- CheckCode is the RubyLex input analysis: it returns Complete / More / SyntaxError for accumulated input, the heart of the multi-line prompt.
- OpenTokens / ScanOpens / ParseByLine port IRB::NestingParser's tracking of open syntactic constructs.
- CalcIndentLevel, LtypeFromOpenTokens and AutoIndent port the nesting-depth, literal-type and next-line auto-indent calculations.
- The PromptMode table, FormatPrompt and GeneratePrompt port IRB's prompt selection and %-spec expansion (:DEFAULT / :SIMPLE / :CLASSIC / :INF_RUBY / :NULL).
- ColorizeCode and Colorize port IRB::Color's token→ANSI-SGR mapping.
- FormatResult and TruncateResult port the deterministic result-formatting of IRB::Inspector / Irb#output_value (the inspected-string rendering of a live object stays a host seam).
- History plus DecodeHistoryLines / EncodeHistoryLines / TrimHistory port the in-memory history list and its on-disk multi-line encoding (the file I/O itself is a host seam).
- Commands, LookupCommand and ParseInput port IRB's command registry and the command-vs-expression dispatch decision (executing a command is a host seam).
Host seams (left to rbgo / the runtime) ¶
- Evaluation. This package never runs Ruby; it only decides *when* a statement is ready and *how* to render its already-computed inspection.
- Terminal I/O. Reading a line, readline/Reline, tty detection and the $stdout.tty? colour gating are the host's job; colour functions take a `colorable` flag instead of probing a terminal.
- History file I/O. Loading and saving the history file (paths, mtime checks, permissions) is the host's job; this package only models the list and its encoding.
- Object inspection. Producing the inspected string for a value (#inspect, pp, Marshal.dump, YAML.dump) needs the interpreter; ResolveInspector only resolves the requested inspector name.
Faithfulness is validated differentially against the real `irb` gem (MRI): the oracle tests run RubyLex / Color / process_indent_level over shared corpora and assert byte-for-byte agreement, and committed golden files record those decisions so the deterministic, ruby-free test suite holds 100% coverage on every platform and architecture.
Index ¶
- Constants
- Variables
- func AutoIndent(lines []string, lineIndex int, isNewline bool) int
- func CalcIndentLevel(opens []Token) int
- func CheckCode(code string) (Completeness, []Token)
- func Colorize(text string, seq []int, colorable bool) string
- func ColorizeCode(code string, complete, colorable bool) string
- func CommandNames() []string
- func DecodeHistoryLines(lines []string, multiline bool) []string
- func EncodeHistoryLines(entries []string) []string
- func FormatPrompt(format string, ctx PromptContext, ltype string, indent, lineNo int) string
- func FormatResult(returnFormat, content string) string
- func GeneratePrompt(mode PromptMode, ctx PromptContext, opens []Token, cont bool, lineNo int, ...) string
- func LookupCommand(name string) (string, bool)
- func LtypeFromOpenTokens(opens []Token) string
- func ResolveInspector(name string) (string, bool)
- func SaveLimit(setting any) int
- func TrimHistory(entries []string, limit int) []string
- func TruncateResult(inspected string, winWidth int, newlineBeforeMultiline, colorable bool) (string, bool)
- type CommandSpec
- type Completeness
- type Event
- type History
- type InspectorMode
- type LineResult
- type ParsedInput
- type PromptContext
- type PromptMode
- type Token
Constants ¶
const ( ExprBeg = 0x1 ExprEnd = 0x2 ExprEndArg = 0x4 ExprEndFn = 0x8 ExprArg = 0x10 ExprCmdArg = 0x20 ExprMid = 0x40 ExprFname = 0x80 ExprDot = 0x100 ExprClass = 0x200 ExprLabel = 0x400 ExprLabeled = 0x800 ExprFitem = 0x1000 )
Ripper lexer State bits (Ripper::EXPR_*). The continuation logic keys on these.
const DefaultEntryLimit = 1000
DefaultEntryLimit is IRB::History::DEFAULT_ENTRY_LIMIT.
Variables ¶
var ( PromptDefault = PromptMode{Name: "DEFAULT", PromptI: "%N(%m):%03n> ", PromptS: "%N(%m):%03n%l ", PromptC: "%N(%m):%03n* ", Return: "=> %s\n"} PromptSimple = PromptMode{Name: "SIMPLE", PromptI: ">> ", PromptS: "%l> ", PromptC: "?> ", Return: "=> %s\n"} PromptClassic = PromptMode{Name: "CLASSIC", PromptI: "%N(%m):%03n:%i> ", PromptS: "%N(%m):%03n:%i%l ", PromptC: "%N(%m):%03n:%i* ", Return: "%s\n"} PromptInfRuby = PromptMode{Name: "INF_RUBY", PromptI: "%N(%m):%03n> ", PromptS: "", PromptC: "", Return: "%s\n", AutoIndent: true} PromptNull = PromptMode{Name: "NULL", PromptI: "", PromptS: "", PromptC: "", Return: "%s\n"} )
Built-in prompt modes, copied from IRB.conf[:PROMPT]. The empty PromptI/S/C of :NULL / :INF_RUBY are represented as "".
var Commands = []CommandSpec{ {Name: "cd", Category: "Workspace", Aliases: []string{}}, {Name: "copy", Category: "Misc", Aliases: []string{}}, {Name: "irb", Category: "Multi-irb (DEPRECATED)", Aliases: []string{}}, {Name: "irb_backtrace", Category: "Debugging", Aliases: []string{"backtrace", "bt"}}, {Name: "irb_break", Category: "Debugging", Aliases: []string{"break"}}, {Name: "irb_catch", Category: "Debugging", Aliases: []string{"catch"}}, {Name: "irb_change_workspace", Category: "Workspace", Aliases: []string{"chws", "cws", "irb_chws", "irb_cws", "irb_change_binding", "irb_cb", "cb"}}, {Name: "irb_context", Category: "IRB", Aliases: []string{"context"}}, {Name: "irb_continue", Category: "Debugging", Aliases: []string{"continue"}}, {Name: "irb_current_working_workspace", Category: "Workspace", Aliases: []string{"cwws", "pwws", "irb_print_working_workspace", "irb_cwws", "irb_pwws", "irb_current_working_binding", "irb_print_working_binding", "irb_cwb", "irb_pwb"}}, {Name: "irb_debug", Category: "Debugging", Aliases: []string{"debug"}}, {Name: "irb_debug_info", Category: "Debugging", Aliases: []string{"info"}}, {Name: "irb_delete", Category: "Debugging", Aliases: []string{"delete"}}, {Name: "irb_disable_irb", Category: "IRB", Aliases: []string{"disable_irb"}}, {Name: "irb_edit", Category: "Misc", Aliases: []string{"edit"}}, {Name: "irb_exit", Category: "IRB", Aliases: []string{"exit", "quit", "irb_quit"}}, {Name: "irb_exit!", Category: "IRB", Aliases: []string{"exit!"}}, {Name: "irb_fg", Category: "Multi-irb (DEPRECATED)", Aliases: []string{"fg"}}, {Name: "irb_finish", Category: "Debugging", Aliases: []string{"finish"}}, {Name: "irb_help", Category: "Help", Aliases: []string{"help", "show_cmds"}}, {Name: "irb_history", Category: "IRB", Aliases: []string{"history", "hist"}}, {Name: "irb_info", Category: "IRB", Aliases: []string{}}, {Name: "irb_jobs", Category: "Multi-irb (DEPRECATED)", Aliases: []string{"jobs"}}, {Name: "irb_kill", Category: "Multi-irb (DEPRECATED)", Aliases: []string{"kill"}}, {Name: "irb_load", Category: "IRB", Aliases: []string{}}, {Name: "irb_ls", Category: "Context", Aliases: []string{"ls"}}, {Name: "irb_measure", Category: "Misc", Aliases: []string{"measure"}}, {Name: "irb_next", Category: "Debugging", Aliases: []string{"next"}}, {Name: "irb_pop_workspace", Category: "Workspace", Aliases: []string{"popws", "irb_popws", "irb_pop_binding", "irb_popb", "popb"}}, {Name: "irb_push_workspace", Category: "Workspace", Aliases: []string{"pushws", "irb_pushws", "irb_push_binding", "irb_pushb", "pushb"}}, {Name: "irb_require", Category: "IRB", Aliases: []string{}}, {Name: "irb_show_doc", Category: "Context", Aliases: []string{"show_doc", "ri"}}, {Name: "irb_show_source", Category: "Context", Aliases: []string{"show_source"}}, {Name: "irb_source", Category: "IRB", Aliases: []string{"source"}}, {Name: "irb_step", Category: "Debugging", Aliases: []string{"step"}}, {Name: "irb_whereami", Category: "Context", Aliases: []string{"whereami"}}, {Name: "irb_workspaces", Category: "Workspace", Aliases: []string{"workspaces", "irb_bindings", "bindings"}}, }
Commands is the built-in command table, mirroring IRB::Command.commands plus the default aliases registered in IRB::Command::DefaultCommands.
var InspectorModes = []InspectorMode{ {Canonical: "to_s", Aliases: []string{"false", "raw"}}, {Canonical: "p", Aliases: []string{"inspect"}}, {Canonical: "pp", Aliases: []string{"true", "pretty_inspect"}}, {Canonical: "yaml", Aliases: []string{"YAML"}}, {Canonical: "marshal", Aliases: []string{"Marshal", "MARSHAL"}}, }
InspectorModes is the deterministic alias table mirroring INSPECTORS' keys.
var PromptModes = map[string]PromptMode{ "DEFAULT": PromptDefault, "SIMPLE": PromptSimple, "CLASSIC": PromptClassic, "INF_RUBY": PromptInfRuby, "NULL": PromptNull, }
PromptModes maps the symbol name to the built-in mode.
Functions ¶
func AutoIndent ¶
AutoIndent computes the leading-space count IRB would apply to line lineIndex (0-based) of lines. isNewline indicates the cursor just moved to a fresh line (versus re-indenting the current one). It tokenizes the joined source and delegates to processIndentLevel.
func CalcIndentLevel ¶
CalcIndentLevel ports RubyLex#calc_indent_level: the nesting depth implied by a list of open tokens, used for the %i prompt spec and auto-indent.
func CheckCode ¶
func CheckCode(code string) (Completeness, []Token)
CheckCode analyses code and returns its completeness verdict together with the list of still-open construct tokens (useful for the prompt). It is the public entry point a REPL host calls after each line.
func Colorize ¶
Colorize wraps text in the given SGR codes (IRB::Color.colorize). When colorable is false it returns text unchanged.
func ColorizeCode ¶
ColorizeCode is the port of IRB::Color.colorize_code: it re-lexes code and emits it with each token wrapped in its colour. When colorable is false it returns code unchanged. complete=false suppresses colouring an only-incomplete trailing error (matching IRB's `allow_last_error`).
func CommandNames ¶
func CommandNames() []string
CommandNames returns all canonical command names (sorted by insertion order of the table).
func DecodeHistoryLines ¶
DecodeHistoryLines reconstructs entries from on-disk lines using IRB's continuation rule: a stored line ending in a backslash continues into the next line (the backslash is dropped and replaced by a newline). It ports the load_history merge loop. multiline selects the Reline behaviour (the only one that merges); when false each line is its own entry.
func EncodeHistoryLines ¶
EncodeHistoryLines renders entries for storage using IRB's save_history rule: each entry's embedded newlines become "\\\n" so a multi-line entry round-trips through DecodeHistoryLines. It ports `l.scrub.split("\n").join("\\\n")`.
func FormatPrompt ¶
func FormatPrompt(format string, ctx PromptContext, ltype string, indent, lineNo int) string
FormatPrompt expands one prompt format string the way IRB#format_prompt does. ltype is the literal-type character (from LtypeFromOpenTokens), indent is the nesting level, lineNo is the 1-based line number.
func FormatResult ¶
FormatResult applies a return-format template (e.g. "=> %s\n") to an inspected value, exactly like Ruby's format(@context.return_format, content). A format with no "%" is returned verbatim (the :NULL / :CLASSIC style "%s\n" still has one; ":RETURN without %" prints the template itself, matching output_value).
func GeneratePrompt ¶
func GeneratePrompt(mode PromptMode, ctx PromptContext, opens []Token, cont bool, lineNo int, prompting, autoIndent bool) string
GeneratePrompt ports IRB::Irb#generate_prompt: it picks the I/S/C format based on the open tokens and whether the statement continues, computes ltype/indent from the open tokens, and applies auto-indent padding when requested. continue is the caller's notion of statement continuation (e.g. from CheckCode==More); opens are the open construct tokens; lineNo is the current line number.
func LookupCommand ¶
LookupCommand resolves a command name or alias to its canonical name.
func LtypeFromOpenTokens ¶
LtypeFromOpenTokens ports RubyLex#ltype_from_open_tokens: the literal-type character ("/", `"`, etc.) used by the %l prompt spec while inside an open string / regexp / word-list / heredoc.
func ResolveInspector ¶
ResolveInspector maps a requested inspector name or alias to its canonical name, reporting whether it is known.
func SaveLimit ¶
SaveLimit resolves IRB's SAVE_HISTORY setting (false→0, true→default, otherwise the integer) the way IRB::History.save_history does. A negative value means "infinite".
func TrimHistory ¶
TrimHistory caps the entry list to the save limit, dropping the oldest entries, mirroring `hist.last(save_history)`. A negative (infinite) or non-positive-overflow limit returns the entries unchanged.
func TruncateResult ¶
func TruncateResult(inspected string, winWidth int, newlineBeforeMultiline, colorable bool) (string, bool)
TruncateResult ports the omit branch of IRB#output_value: keep only the first terminal "page" (one row of winWidth columns) of the inspected value and, when it overflows, append "..." (and a reset SGR when colourable). It returns the possibly-truncated content and whether truncation happened.
Types ¶
type CommandSpec ¶
CommandSpec describes one built-in IRB command.
type Completeness ¶
type Completeness int
Completeness is the verdict for accumulated input.
const ( // Complete means the statement is finished and ready to evaluate. Complete Completeness = iota // More means IRB should keep reading (open block/literal/heredoc, trailing // operator or backslash continuation). More // SyntaxError means the input is unrecoverably wrong (e.g. a stray `end`). SyntaxError )
func (Completeness) String ¶
func (c Completeness) String() string
type Event ¶
type Event string
Event is a Ripper-style lexer event name (the `on_*` symbol Ripper emits for each token). IRB's RubyLex, NestingParser and Color all dispatch on these event names plus the lexer State, so this package reproduces them faithfully rather than inventing its own token taxonomy. Only the events IRB actually inspects are produced; everything else collapses to a small set of generic events (e.g. on_op) exactly as Ripper does.
const ( EvInt Event = "on_int" EvFloat Event = "on_float" EvRational Event = "on_rational" EvImaginary Event = "on_imaginary" EvIdent Event = "on_ident" EvConst Event = "on_const" EvKw Event = "on_kw" EvIvar Event = "on_ivar" EvGvar Event = "on_gvar" EvCvar Event = "on_cvar" EvBackref Event = "on_backref" EvOp Event = "on_op" EvSp Event = "on_sp" EvNl Event = "on_nl" EvIgnoredNl Event = "on_ignored_nl" EvComment Event = "on_comment" EvSemicolon Event = "on_semicolon" EvComma Event = "on_comma" EvPeriod Event = "on_period" EvLabel Event = "on_label" EvLabelEnd Event = "on_label_end" EvLparen Event = "on_lparen" EvRparen Event = "on_rparen" EvLbracket Event = "on_lbracket" EvRbracket Event = "on_rbracket" EvLbrace Event = "on_lbrace" EvRbrace Event = "on_rbrace" EvTstringBeg Event = "on_tstring_beg" EvTstringContent Event = "on_tstring_content" EvTstringEnd Event = "on_tstring_end" EvRegexpBeg Event = "on_regexp_beg" EvRegexpEnd Event = "on_regexp_end" EvSymbeg Event = "on_symbeg" EvSymbolsBeg Event = "on_symbols_beg" EvQsymbolsBeg Event = "on_qsymbols_beg" EvWordsBeg Event = "on_words_beg" EvQwordsBeg Event = "on_qwords_beg" EvWordsSep Event = "on_words_sep" EvBacktick Event = "on_backtick" EvHeredocBeg Event = "on_heredoc_beg" EvHeredocEnd Event = "on_heredoc_end" EvEmbexprBeg Event = "on_embexpr_beg" EvEmbexprEnd Event = "on_embexpr_end" EvEmbvar Event = "on_embvar" EvTlambda Event = "on_tlambda" EvTlambeg Event = "on_tlambeg" EvCHAR Event = "on_CHAR" EvEmbdocBeg Event = "on_embdoc_beg" EvEmbdoc Event = "on_embdoc" EvEmbdocEnd Event = "on_embdoc_end" EvEnd Event = "on___end__" EvParseError Event = "on_parse_error" )
The Ripper events this port emits. Names match Ripper's `on_*` symbols so the continuation, nesting and colorize logic stays a line-by-line port of MRI.
type History ¶
type History struct {
// contains filtered or unexported fields
}
History is the pure in-memory list of input entries. A multi-line entry is stored as a single string containing embedded "\n".
func (*History) At ¶
At returns the entry at index i (0-based). Negative indices count from the end like Ruby's Array#[]. The ok result reports whether the index was in range.
type InspectorMode ¶
type InspectorMode struct {
Canonical string // canonical name, e.g. "p"
Aliases []string // accepted aliases, e.g. "inspect"
}
InspectorMode names the built-in IRB inspectors (the keys of IRB::Inspector::INSPECTORS). Selecting and running them needs the interpreter (they call #inspect / pp / Marshal.dump / YAML.dump on a live object), so this type is just the deterministic name/alias registry a host uses to resolve a requested mode; the actual value rendering is a host seam.
type LineResult ¶
LineResult holds, for one source line, the [line tokens] and the open-token lists before and after the line plus the minimum nesting depth reached within it — exactly the tuple NestingParser.parse_by_line yields.
func ParseByLine ¶
func ParseByLine(tokens []Token) []LineResult
ParseByLine is the port of NestingParser.parse_by_line: it splits the token stream into per-line nesting snapshots used by the auto-indent calculation.
type ParsedInput ¶
type ParsedInput struct {
IsCommand bool
Command string // canonical command name when IsCommand
Arg string // the argument text after the command name
Code string // the original code (always set)
}
ParsedInput is the result of analysing one input line: either a recognised command invocation or a plain Ruby expression.
func ParseInput ¶
func ParseInput(code string, localVariables map[string]bool, isAssignment bool) ParsedInput
ParseInput ports IRB::Context#parse_input's command-vs-expression decision. localVariables is the set of in-scope local variable names (a name that is a local variable shadows a command), and isAssignment reports whether the parser already classified the code as an assignment expression (assignments are never commands). A line is a command only when it is a single line, names a known command, is not shadowed by a local, and is not an assignment.
type PromptContext ¶
type PromptContext struct {
IRBName string // %N — the irb program name (default "irb")
Main string // %m — main object's to_s
MainIns string // %M — main object's inspect
}
PromptContext carries the per-session values the %-specs interpolate.
type PromptMode ¶
type PromptMode struct {
Name string
PromptI string // primary prompt
PromptS string // string-continuation prompt
PromptC string // statement-continuation prompt
Return string // return-value format
AutoIndent bool
}
PromptMode is one of IRB's built-in :PROMPT_MODE values.
type Token ¶
Token is one lexical token: a Ripper event, the exact source text, the lexer State after recognising it, and its 1-based [line, byte-column] position.
func OpenTokens ¶
OpenTokens returns the list of still-open tokens at the end of the stream.
func ScanOpens ¶
ScanOpens walks tokens and, after each token, reports the current list of open tokens (the .tok of each frame) to the optional callback. It returns the final list of open tokens (plus still-pending heredocs).
