parser

package
v1.19.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 29, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

Package parser implements R7RS Scheme syntax parsing.

The parser converts a token stream into syntax values with source location:

Features

  • All Scheme datums: literals, symbols, lists, vectors, bytevectors
  • Full numeric tower: integers, floats, rationals, complex, big numbers
  • Quote forms: ', `, ,, ,@ and syntax variants #', #`, #,, #,@
  • Datum labels: #n= and #n# for shared/circular structures (R7RS 2.4)
  • Case folding: #!fold-case and #!no-fold-case directives
  • Symbol creation at parse time (with fold-case applied when active)

Usage

p := parser.NewParserWithFile(env, true, reader, "example.scm")
for {
    stx, err := p.ReadSyntax(ctx)
    if errors.Is(err, io.EOF) {
        break
    }
    // process stx
}

Error Handling

Parse errors are wrapped in ParserError with source location from the offending token. State that persists across expressions is deliberate and limited to fold-case mode (R7RS §2.1) and the trailing-EOF lookahead; datum labels are scoped to a single datum and cleared on every read (R7RS §2.4).

Index

Constants

View Source
const (
	ConstQuote            = "quote"
	ConstQuasiquote       = "quasiquote"
	ConstUnquote          = "unquote"
	ConstUnquoteSplicing  = "unquote-splicing"
	ConstSyntax           = "syntax"
	ConstQuasisyntax      = "quasisyntax"
	ConstUnsyntax         = "unsyntax"
	ConstUnsyntaxSplicing = "unsyntax-splicing"
)

Quote form identifiers.

View Source
const DefaultMaxParseDepth int = 10000

DefaultMaxParseDepth bounds structural nesting depth during parsing. Without a bound, adversarial input such as deeply nested parentheses triggers a fatal, unrecoverable Go stack overflow that kills the host process. 0 means unlimited. Mirrors the VM's DefaultMaxCallDepth.

Variables

View Source
var (
	// ErrUnknownTokenType is returned when the parser encounters an unrecognized token.
	ErrUnknownTokenType = werr.NewStaticError("unknown token type")
	ErrAlreadyClosed    = werr.NewStaticError("parser already closed")
)

Functions

func ParseComplexStringNumber

func ParseComplexStringNumber(s string) (values.Number, error)

ParseComplexStringNumber parses a rectangular complex number string ending in 'i'. Handles "3+4i", "1.5-2.5i", "1+inf.0i", "0+3/4i", etc.

On reject it returns a non-nil error carrying a werr sentinel (ErrInvalidFormat for a shape that is not this grammar; the part parser's own sentinel for a malformed real/imaginary part). See ParseImaginaryStringNumber for how the two callers treat the error differently.

func ParseImaginaryStringNumber

func ParseImaginaryStringNumber(s string) (values.Number, error)

ParseImaginaryStringNumber parses a pure imaginary string (ending in 'i') and returns the resulting complex number. Handles "+3i", "-2.5i", "+i", "-i", "+inf.0i", "-nan.0i", etc.

On reject it returns a non-nil error carrying a werr sentinel (ErrInvalidFormat for a shape that is not this grammar; the coefficient parser's own sentinel — ErrInvalidFormat / ErrDivisionByZero — for a malformed coefficient). Callers choose how to treat it: string->number discards it and tries the next grammar (R7RS §6.2.7 returns #f), while the reader wraps it with a source location so the cause stays reachable via errors.Is / errors.Unwrap.

func ParseRealFloatString

func ParseRealFloatString(s string) (values.Number, error)

ParseRealFloatString parses a real decimal or scientific-notation string as an inexact number. In-range magnitudes yield a float64-backed Float; magnitudes beyond float64 range (strconv reports ErrRange as ±Inf) promote to a BigFloat rather than failing. This mirrors the int64 -> BigInteger promotion in parseIntegerWithBase and, crucially, keeps the reader and string->number symmetric with the writer, which renders an out-of-range bigfloat in scientific notation (e.g. (write (* 1.0 (expt 10 1000))) -> "1e+1000"). Underflow to zero (float64 rounds a tiny magnitude to 0 without ErrRange) is left as the float64 0.0 it already produces, matching float64-based Schemes. The exponent marker is normalized to 'e' first. Returns a wrapped ErrInvalidFormat sentinel on a malformed string.

func ParseSpecialFloat

func ParseSpecialFloat(s string) (*values.Float, bool)

ParseSpecialFloat checks if s is +inf.0, -inf.0, +nan.0, or -nan.0 and returns the corresponding Float value. Returns (nil, false) if s is not a special-value string.

Types

type Parser

type Parser struct {
	// contains filtered or unexported fields
}

Parser represents a R7RS compliant Scheme syntax parser.

func NewParser

func NewParser(env *environment.EnvironmentFrame, skipComments bool, rdr io.RuneReader) *Parser

NewParser creates a new parser for the given reader and environment.

func NewParserWithFile

func NewParserWithFile(env *environment.EnvironmentFrame, skipComments bool, rdr io.RuneReader, file string) *Parser

NewParserWithFile creates a new parser with a specified source filename.

func (*Parser) Close

func (p *Parser) Close() error

Close closes the parser and releases resources. It returns ErrAlreadyClosed if the parser is already closed or was never read from (the tokenizer is created lazily on the first ReadSyntax).

func (*Parser) ReadSyntax

func (p *Parser) ReadSyntax(ctx context.Context) (syntax.SyntaxValue, error)

ReadSyntax reads and returns the next syntax value from the input. It honors ctx: a cancelled or deadline-exceeded context returns its error before reading the next form, so a long multi-form parse can be interrupted.

func (*Parser) SetFoldCase

func (p *Parser) SetFoldCase(on bool)

SetFoldCase enables or disables R7RS §2.1 fold-case mode at construction time, before any forms are read. This is the programmatic equivalent of a leading #!fold-case directive; it lets a caller (e.g. include-ci) read an entire file case-insensitively without the file itself carrying the directive. An in-file #!fold-case / #!no-fold-case directive still toggles the mode mid-stream.

func (*Parser) SetMaxDepth

func (p *Parser) SetMaxDepth(n int)

SetMaxDepth sets the maximum structural nesting depth allowed during parsing. A value of 0 (or negative, clamped to 0) disables the limit. Mirrors MachineContext.SetMaxCallDepth.

func (*Parser) Text

func (p *Parser) Text() string

Text returns the current text being parsed.

type ParserError

type ParserError struct {
	// contains filtered or unexported fields
}

ParserError represents an error that occurred during parsing.

The offending token (tok) carries the source position; file is the source name (empty for unnamed input such as a REPL stream). Location formats the two so the error's provenance survives to the final error chain — see REVIEW.md "Error Chain Losslessness".

func NewParserError

func NewParserError(tok tokenizer.Token, mess string) *ParserError

NewParserError creates a new parser error for the given token.

func NewParserErrorWithWrap

func NewParserErrorWithWrap(err error, tok tokenizer.Token, mess string) *ParserError

NewParserErrorWithWrap creates a new parser error wrapping another error.

func NewParserErrorWithWrapf

func NewParserErrorWithWrapf(err error, tok tokenizer.Token, mess string, vs ...any) *ParserError

NewParserErrorWithWrapf creates a new parser error wrapping another error.

func NewParserErrorf

func NewParserErrorf(tok tokenizer.Token, mess string, vs ...any) *ParserError

NewParserErrorf creates a new parser error for the given token.

func (*ParserError) EqualTo

func (p *ParserError) EqualTo(v values.Value) bool

func (*ParserError) Error

func (p *ParserError) Error() string

func (*ParserError) Is

func (p *ParserError) Is(err error) bool

Is implements errors.Is for ParserError. Two parser errors match when they carry the same message (the token is positional and is not part of error identity — see EqualTo for value-level equality that includes it), so errors.Is can distinguish parser failures; a wrapped cause is still reached through Unwrap.

func (*ParserError) IsVoid

func (p *ParserError) IsVoid() bool

func (*ParserError) Location

func (p *ParserError) Location() string

Location returns the offending token's position as "file:line:col", or "line:col" when the input is unnamed (e.g. a REPL stream, where the parser has no file name). It returns "" when the error has no located token.

Unlike SourceContext.Location, the unnamed-input form omits the leading colon ("1:0" rather than ":1:0"); line is 1-based and column 0-based, as in SourceContext.Location.

func (*ParserError) SchemeString

func (p *ParserError) SchemeString() string

func (*ParserError) Unwrap

func (p *ParserError) Unwrap() error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL