Skip to content

Commit

Permalink
ivy: remove all parallelism
Browse files Browse the repository at this point in the history
Running the lexer as a goroutine required synchronization for
no good purpose. Just run the lexer on demand, from the parser's
goroutine, and avoid all need for synchronization.
  • Loading branch information
robpike committed Sep 16, 2015
1 parent cea8b2c commit eb87de8
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 61 deletions.
34 changes: 0 additions & 34 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"os"
"strconv"
"strings"
"sync"
"time"
)

Expand All @@ -26,7 +25,6 @@ var DebugFlags = [...]string{
// A Config holds information about the configuration of the system.
// The zero value of a Config represents the default values for all settings.
type Config struct {
mu sync.Mutex
prompt string
output io.Writer
errOutput io.Writer
Expand Down Expand Up @@ -61,56 +59,44 @@ func (c *Config) init() {
}
}

func (c *Config) sync() func() {
c.mu.Lock()
return c.mu.Unlock
}

// Output returns the writer to be used for program output.
func (c *Config) Output() io.Writer {
defer c.sync()()
c.init()
return c.output
}

// SetOutput sets the writer to which program output is printed; default is os.Stdout.
func (c *Config) SetOutput(output io.Writer) {
defer c.sync()()
c.init()
c.output = output
}

// ErrOutput returns the writer to be used for error output.
func (c *Config) ErrOutput() io.Writer {
defer c.sync()()
c.init()
return c.errOutput
}

// SetErrOutput sets the writer to which error output is printed; default is os.Stderr.
func (c *Config) SetErrOutput(output io.Writer) {
defer c.sync()()
c.init()
c.errOutput = output
}

// Format returns the formatting string. If empty, the default
// formatting is used, as defined by the bases.
func (c *Config) Format() string {
defer c.sync()()
return c.format
}

// Format returns the formatting string for rationals.
func (c *Config) RatFormat() string {
defer c.sync()()
return c.ratFormat
}

// SetFormat sets the formatting string. Rational formatting
// is just this format applied twice with a / in between.
func (c *Config) SetFormat(s string) {
defer c.sync()()
c.init()
c.formatVerb = 0
c.formatPrec = 0
Expand Down Expand Up @@ -143,13 +129,11 @@ func (c *Config) SetFormat(s string) {
// FloatFormat returns the parsed information about the format,
// if it's a floating-point format.
func (c *Config) FloatFormat() (verb byte, prec int, ok bool) {
defer c.sync()()
return c.formatVerb, c.formatPrec, c.formatFloat
}

// Debug returns the value of the specified boolean debugging flag.
func (c *Config) Debug(flag string) bool {
defer c.sync()()
for i, f := range DebugFlags {
if f == flag {
return c.debug[i]
Expand All @@ -161,7 +145,6 @@ func (c *Config) Debug(flag string) bool {
// SetDebug sets the value of the specified boolean debugging flag.
// It returns false if the flag is unknown.
func (c *Config) SetDebug(flag string, state bool) bool {
defer c.sync()()
c.init()
for i, f := range DebugFlags {
if f == flag {
Expand All @@ -174,90 +157,77 @@ func (c *Config) SetDebug(flag string, state bool) bool {

// Origin returns the index origin, default 1.
func (c *Config) Origin() int {
defer c.sync()()
return c.origin
}

// BigOrigin returns the index origin as a *big.Int.
func (c *Config) BigOrigin() *big.Int {
defer c.sync()()
return c.bigOrigin
}

// SetOrigin sets the index origin.
func (c *Config) SetOrigin(origin int) {
defer c.sync()()
c.init()
c.origin = origin
c.bigOrigin = big.NewInt(int64(origin))
}

// Prompt returns the interactive prompt.
func (c *Config) Prompt() string {
defer c.sync()()
return c.prompt
}

// SetPrompt sets the interactive prompt.
func (c *Config) SetPrompt(prompt string) {
defer c.sync()()
c.init()
c.prompt = prompt
}

// Random returns the generator for random numbers.
func (c *Config) Random() *rand.Rand {
defer c.sync()()
c.init()
return c.random
}

// SetRandomSeed sets the seed for the random number generator.
func (c *Config) SetRandomSeed(seed int64) {
defer c.sync()()
c.init()
c.source.Seed(seed)
}

// MaxBits returns the maximum integer size to store, in bits.
func (c *Config) MaxBits() uint {
defer c.sync()()
c.init()
return c.maxBits
}

// MaxBits sets the maximum integer size to store, in bits.
func (c *Config) SetMaxBits(digits uint) {
defer c.sync()()
c.init()
c.maxBits = digits
}

// MaxDigits returns the maximum integer size to print as integer, in digits.
func (c *Config) MaxDigits() uint {
defer c.sync()()
c.init()
return c.maxDigits
}

// SetMaxDigits sets the maximum integer size to print as integer, in digits.
func (c *Config) SetMaxDigits(digits uint) {
defer c.sync()()
c.init()
c.maxDigits = digits
}

// FloatPrec returns the floating-point precision in bits.
// The exponent size is fixed by math/big.
func (c *Config) FloatPrec() uint {
defer c.sync()()
c.init()
return c.floatPrec
}

// SetFloatPrec sets the floating-point precision in bits.
func (c *Config) SetFloatPrec(prec uint) {
defer c.sync()()
c.init()
if prec == 0 {
panic("zero float precision")
Expand All @@ -267,25 +237,21 @@ func (c *Config) SetFloatPrec(prec uint) {

// Base returns the input and output bases.
func (c *Config) Base() (inputBase, outputBase int) {
defer c.sync()()
return c.inputBase, c.outputBase
}

// InputBase returns the input base.
func (c *Config) InputBase() int {
defer c.sync()()
return c.inputBase
}

// OutputBase returns the output base.
func (c *Config) OutputBase() int {
defer c.sync()()
return c.outputBase
}

// SetBase sets the input and output bases.
func (c *Config) SetBase(inputBase, outputBase int) {
defer c.sync()()
c.init()
c.inputBase = inputBase
c.outputBase = outputBase
Expand Down
16 changes: 1 addition & 15 deletions exec/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@

package exec

import (
"sync"

"robpike.io/ivy/value"
)
import "robpike.io/ivy/value"

// Symtab is a symbol table, a map of names to values.
type Symtab map[string]value.Value
Expand All @@ -17,9 +13,6 @@ type Symtab map[string]value.Value
// It is the only implementation of ../value/Context, but since it references the value
// package, there would be a cycle if that package depended on this type definition.
type Context struct {
// Locks UnaryFn and BinaryFn, which are accessed through UserDefined
// from the scanner.
mu sync.Mutex
// Stack is a stack of symbol tables, one entry per function (op) invocation,
// plus the 0th one at the base.
Stack []Symtab
Expand All @@ -45,11 +38,6 @@ func NewContext() value.Context {
return c
}

func (c *Context) sync() func() {
c.mu.Lock()
return c.mu.Unlock
}

// SetConstants re-assigns the fundamental constant values using the current
// setting of floating-point precision.
func (c *Context) SetConstants() {
Expand Down Expand Up @@ -142,7 +130,6 @@ func (c *Context) EvalUnary(op string, right value.Value) value.Value {
}

func (c *Context) UserDefined(op string, isBinary bool) bool {
defer c.sync()()
if isBinary {
return c.BinaryFn[op] != nil
}
Expand Down Expand Up @@ -178,7 +165,6 @@ func (c *Context) EvalBinary(left value.Value, op string, right value.Value) val
// some error checking and adds the function to the sequencing
// information used by the save method.
func (c *Context) Define(fn *Function) {
defer c.sync()()
c.noVar(fn.Name)
if fn.IsBinary {
c.BinaryFn[fn.Name] = fn
Expand Down
4 changes: 2 additions & 2 deletions parse/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ func (p *Parser) nextErrorOut(errorOut bool) scan.Token {
if tok.Type != scan.EOF {
p.peekTok = scan.Token{Type: scan.EOF}
} else {
tok = <-p.scanner.Tokens
tok = p.scanner.Next()
}
if tok.Type == scan.Error && errorOut {
p.errorf("%q", tok)
Expand All @@ -282,7 +282,7 @@ func (p *Parser) peek() scan.Token {
if tok.Type != scan.EOF {
return tok
}
p.peekTok = <-p.scanner.Tokens
p.peekTok = p.scanner.Next()
return p.peekTok
}

Expand Down
33 changes: 23 additions & 10 deletions scan/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ type stateFn func(*Scanner) stateFn

// Scanner holds the state of the scanner.
type Scanner struct {
Tokens chan Token // channel of scanned items
tokens chan Token // channel of scanned items
config *config.Config
context value.Context
r io.ByteReader
Expand Down Expand Up @@ -145,7 +145,7 @@ func (l *Scanner) emit(t Type) {
if l.config.Debug("tokens") {
fmt.Fprintf(l.config.Output(), "%s:%d: emit %s\n", l.name, l.line, Token{t, l.line, s})
}
l.Tokens <- Token{t, l.line, s}
l.tokens <- Token{t, l.line, s}
l.start = l.pos
l.width = 0
}
Expand Down Expand Up @@ -173,7 +173,7 @@ func (l *Scanner) acceptRun(valid string) {

// errorf returns an error token and continues to scan.
func (l *Scanner) errorf(format string, args ...interface{}) stateFn {
l.Tokens <- Token{Error, l.start, fmt.Sprintf(format, args...)}
l.tokens <- Token{Error, l.start, fmt.Sprintf(format, args...)}
return lexAny
}

Expand All @@ -183,20 +183,33 @@ func New(conf *config.Config, context value.Context, name string, r io.ByteReade
r: r,
name: name,
line: 1,
Tokens: make(chan Token), // Must be unbuffered to synchronize with parser.
tokens: make(chan Token, 2), // We need a little room to save tokens.
config: conf,
context: context,
state: lexAny,
}
go l.run()
return l
}

// run runs the state machine for the Scanner.
func (l *Scanner) run() {
for l.state = lexAny; l.state != nil; {
l.state = l.state(l)
// Next returns the next token.
func (l *Scanner) Next() Token {
// The lexer is concurrent but we don't want it to run in parallel
// with the rest of the interpreter, so we only run the state machine
// when we need a token.
for l.state != nil {
select {
case tok := <-l.tokens:
return tok
default:
// Run the machine
l.state = l.state(l)
}
}
if l.tokens != nil {
close(l.tokens)
l.tokens = nil
}
close(l.Tokens)
return Token{EOF, 0, "EOF"}
}

// state functions
Expand Down

0 comments on commit eb87de8

Please sign in to comment.