Skip to content
This repository was archived by the owner on May 23, 2021. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions reader/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package reader

import "github.com/spy16/sabre/runtime"

// Option for Reader
type Option func(*Reader)

// MacroTable is a lookup table that maps the first rune of a macro expression to its
// implementation.
type MacroTable map[rune]Macro

// WithMacros sets the macro table for the reader
func WithMacros(t MacroTable) Option {
if t == nil {
t = MacroTable{
'"': readString,
';': readComment,
':': readKeyword,
'\\': readCharacter,
'(': readList,
')': UnmatchedDelimiter(),
'\'': quoteFormReader("quote"),
'~': quoteFormReader("unquote"),
'`': quoteFormReader("syntax-quote"),
}
}

return func(r *Reader) {
r.macros = t
}
}

// WithDispatch sets the dispatch table for the reader
func WithDispatch(t MacroTable) Option {
if t == nil {
t = MacroTable{}
}

return func(r *Reader) {
r.dispatch = t
}
}

// WithPredefinedSymbols maps a set of symbols to a set of values globally.
func WithPredefinedSymbols(ss map[string]runtime.Value) Option {
if ss == nil {
ss = map[string]runtime.Value{
"nil": runtime.Nil{},
"true": runtime.Bool(true),
"false": runtime.Bool(false),
}
}

return func(r *Reader) {
r.predef = ss
}
}

func withDefaults(opt []Option) []Option {
return append([]Option{
WithMacros(nil),
WithDispatch(nil),
WithPredefinedSymbols(nil),
}, opt...)
}
36 changes: 13 additions & 23 deletions reader/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,6 @@ var (
'v': '\v',
}

predefSymbols = map[string]runtime.Value{
"nil": runtime.Nil{},
"true": runtime.Bool(true),
"false": runtime.Bool(false),
}

charLiterals = map[string]rune{
"tab": '\t',
"space": ' ',
Expand All @@ -65,23 +59,17 @@ var (
// supports only primitive data types from runtime package by default. Support for
// custom forms can be added using SetMacro(). File name is inferred from the value &
// type information of 'r' OR can be set manually on the Reader instance returned.
func New(r io.Reader) *Reader {
return &Reader{
func New(r io.Reader, opts ...Option) *Reader {
rd := &Reader{
File: inferFileName(r),
rs: bufio.NewReader(r),
macros: map[rune]Macro{
'"': readString,
';': readComment,
':': readKeyword,
'\\': readCharacter,
'(': readList,
')': UnmatchedDelimiter(),
'\'': quoteFormReader("quote"),
'~': quoteFormReader("unquote"),
'`': quoteFormReader("syntax-quote"),
},
dispatch: map[rune]Macro{},
}

for _, option := range withDefaults(opts) {
option(rd)
}

return rd
}

// Reader consumes characters from a stream and parses them into symbolic expressions
Expand All @@ -94,9 +82,10 @@ type Reader struct {
buf []rune
line, col int
lastCol int
macros map[rune]Macro
dispatch map[rune]Macro
macros MacroTable
dispatch MacroTable
dispatching bool
predef map[string]runtime.Value
}

// All consumes characters from stream until EOF and returns a list of all the forms
Expand Down Expand Up @@ -364,7 +353,8 @@ func (rd *Reader) readOne() (runtime.Value, error) {
return nil, err
}

if predefVal, found := predefSymbols[v.(runtime.Symbol).String()]; found {
// TODO(performance): type assertion necessary given the above call to `readSymbol`?
if predefVal, found := rd.predef[v.(runtime.Symbol).String()]; found {
return predefVal, nil
}

Expand Down
4 changes: 3 additions & 1 deletion repl/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ func WithPrompts(oneLine, multiLine string) Option {
// Reader. This is useful when you want REPL to use custom reader instance.
func WithReaderFactory(factory ReaderFactory) Option {
if factory == nil {
factory = ReaderFactoryFunc(reader.New)
factory = ReaderFactoryFunc(func(r io.Reader) *reader.Reader {
return reader.New(r)
})
}

return func(repl *REPL) {
Expand Down