parser

package
v1.18.0 Latest Latest
Warning

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

Go to latest
Published: Jul 10, 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 interning at parse time

Usage

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

Error Handling

Parse errors are wrapped in ParserError with source location from the offending token. The parser accumulates no state across expressions.

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.

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 it does not require a file name, so a parse error from unnamed REPL input still reports its line and column rather than dropping the location. Line is 1-based and column 0-based, matching SourceContext.Location's formatting for parity with compiler-side locations.

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