Skip to content

Commit

Permalink
code cleanup, added undo feature
Browse files Browse the repository at this point in the history
  • Loading branch information
emicklei committed Mar 14, 2013
1 parent f42e567 commit 676e145
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 84 deletions.
5 changes: 1 addition & 4 deletions .gitignore
@@ -1,5 +1,2 @@
rango
image.go
play.changes

chameleon.go
_rango_.go
16 changes: 7 additions & 9 deletions doc.go
Expand Up @@ -9,21 +9,21 @@ Install rango
go install ...rango
Rango shell commands
.q(uit) exit rango
.v(ars) show all variable names
.q(uit) exit rango
.v(ars) show all variable names
.s(ource) print the source entered since startup
<name> print a value when entered a known variable name
.u undo the last entry (e.g. to fix a compiler error)
<name> print a value when entered a known variable name
Features
import declaration
(almost) any go source that you can put inside the main() function
TODO
function declarations
multi variable declarations
multi import declarations
multi variable declarations per entry
multi import declarations per entry
multi statements per entry
.u undo the last entry (e.g. to fix a compiler error)
How it is made
Expand All @@ -32,8 +32,6 @@ Successively, for each new command line entry, a new program is generated in Go,
Any compiler error of the generated source is captured and printed by rango.
The output (stdout and stderr) of the generated program is captured and printed by rango.
Other known implementations:
https://gitorious.org/golang/go-repl/blobs/master/main.go
(c) 2013, Ernest Micklei. MIT License
*/
package main
18 changes: 9 additions & 9 deletions generate_compile_run.go
Expand Up @@ -17,20 +17,20 @@ var temporaryShellScriptName = "rangorun"

// Generate_compile_run takes a list of Go source lines, create a Go program from it, compiles that source and runs that program.
// Return the captured output from the compilation or the execution of the Go program.
func Generate_compile_run(goSourceFile string, entries []SourceHolder) string {
err := generate(goSourceFile, entries)
func Generate_compile_run(goSourceFile string, sourceLines []SourceHolder) string {
err := generate(goSourceFile, sourceLines)
if err != nil {
return fmt.Sprintf("[rango] generate Go source failed:%v", err)
}
output, _ := gorun(goSourceFile)
return output
}

// generate produces a Go source file from a list of Go code entries
func generate(goSourceFile string, entries []SourceHolder) error {
// generate produces a Go source file from a list of Go code sourceLines
func generate(goSourceFile string, sourceLines []SourceHolder) error {
t := template.Must(template.New("image").Parse(imageSourceTemplate()))
var sourceBuffer bytes.Buffer
t.Execute(&sourceBuffer, buildTemplateVars(entries))
t.Execute(&sourceBuffer, buildTemplateVars(sourceLines))
return ioutil.WriteFile(goSourceFile, sourceBuffer.Bytes(), 0644)
}

Expand Down Expand Up @@ -68,16 +68,16 @@ func gorun(goSourceFile string) (string, error) {
return string(logBytes), runError
}

// buildTemplateVars creates a templateVars struct from the list of code entries
func buildTemplateVars(entries []SourceHolder) templateVars {
// buildTemplateVars creates a templateVars struct from the list of code sourceLines
func buildTemplateVars(sourceLines []SourceHolder) templateVars {
imageVars := new(templateVars)
for i, each := range entries {
for i, each := range sourceLines {
switch each.Type {
case Import:
imageVars.Imports = append(imageVars.Imports, each.Source)
case Print:
// only preserve the last
if i == len(entries)-1 {
if i == len(sourceLines)-1 {
imageVars.Statements = append(imageVars.Statements, each.Source)
}
default:
Expand Down
75 changes: 30 additions & 45 deletions rango.go
Expand Up @@ -19,16 +19,15 @@ const (
)

var (
Stdin *bufio.Reader
imageName = "chameleon"
entries []SourceHolder
lastLoopCount int
loopCount int
Stdin *bufio.Reader
imageName = "generated_by_rango"
sourceLines []SourceHolder
entryCount int
)

func init() {
Stdin = bufio.NewReader(os.Stdin)
entries = []SourceHolder{}
sourceLines = []SourceHolder{}
}

func main() {
Expand Down Expand Up @@ -57,7 +56,6 @@ func processChanges() {
entered, err := in.ReadString('\n')
if len(entered) > 0 {
handleSource(strings.TrimRight(entered, "\n"), UpdateSourceOnly) // without newline
loopCount++
}
if err == io.EOF {
break
Expand All @@ -66,12 +64,12 @@ func processChanges() {
break
}
}
fmt.Printf("[rango] processed %d lines from %s\n", loopCount+1, changesName)
fmt.Printf("[rango] processed %d lines from %s\n", entryCount+1, changesName)
}

func loop() {
for {
//fmt.Printf("current loopcount:%d\n", loopCount)
//fmt.Printf("current entryCount:%d\n", entryCount)
fmt.Print("> ")
in := bufio.NewReader(os.Stdin)
entered, err := in.ReadString('\n')
Expand All @@ -83,7 +81,6 @@ func loop() {
if len(output) > 0 {
fmt.Println(output)
}
loopCount++
}
}

Expand All @@ -93,21 +90,18 @@ func dispatch(entry string) string {
}
switch {
case strings.HasPrefix(entry, ".v"):
fmt.Printf("%v\n", CollectVariables(entries))
loopCount--
fmt.Printf("%v\n", CollectVariables(sourceLines))
return ""
case strings.HasPrefix(entry, ".q"):
os.Exit(0)
case strings.HasPrefix(entry, ".s"):
loopCount--
return handlePrintSource()
case strings.HasPrefix(entry, ".u"):
return handleUndo()
case strings.HasPrefix(entry, "#"):
// TODO forget sources for current loopcount
// TODO forget sources for current entryCount
return handleSource(entry[1:], GenerateCompileRun)
case strings.HasPrefix(entry, "."):
loopCount--
return handleUnknownCommand(entry)
case isVariable(entry):
return handlePrintVariable(entry)
Expand All @@ -116,42 +110,38 @@ func dispatch(entry string) string {
}

func handleUndo() string {
undo(loopCount - 2) // loop already incremented
if loopCount <= 2 {
loopCount = 0
} else {
loopCount -= 2
}
undo(entryCount)
return handlePrintSource()
}

func handleSource(entry string, mode int) string {
if len(entry) == 0 {
return entry
}
entryCount++
if strings.HasPrefix(entry, "import ") {
return handleImport(entry)
}
if IsVariableDeclaration(entry) {
vardecl := NewVariableDecl(loopCount, entry)
vardecl := NewVariableDecl(entryCount, entry)
addEntry(vardecl)
// copied from PrintVariable
printEntry := fmt.Sprintf("fmt.Printf(\"%%v\",%s)", vardecl.VariableNames[0])
addEntry(NewPrint(loopCount, printEntry))
addEntry(NewPrint(entryCount, printEntry))
} else {
addEntry(NewStatement(loopCount, entry))
addEntry(NewStatement(entryCount, entry))
}
if UpdateSourceOnly == mode {
return ""
}
return Generate_compile_run(fmt.Sprintf("%s.go", imageName), entries)
return Generate_compile_run(fmt.Sprintf("%s.go", imageName), sourceLines)
}

func handlePrintSource() string {
var buf bytes.Buffer
line := 1
// First imports then functions then main statements
for _, each := range entries {
for _, each := range sourceLines {
if (Import == each.Type) && !each.Hidden {
if line > 1 {
buf.WriteString("\n")
Expand All @@ -160,7 +150,7 @@ func handlePrintSource() string {
line++
}
}
for _, each := range entries {
for _, each := range sourceLines {
if (Statement == each.Type || VariableDecl == each.Type) && !each.Hidden {
if line > 1 {
buf.WriteString("\n")
Expand All @@ -178,49 +168,44 @@ func handleUnknownCommand(entry string) string {

func handlePrintVariable(varname string) string {
printEntry := fmt.Sprintf("fmt.Printf(\"%%v\",%s)", varname)
addEntry(NewPrint(loopCount, printEntry))
return Generate_compile_run(fmt.Sprintf("%s.go", imageName), entries)
addEntry(NewPrint(entryCount, printEntry))
return Generate_compile_run(fmt.Sprintf("%s.go", imageName), sourceLines)
}

// handleImport adds a non-existing import package.
// Source will be updated on the next statement.
func handleImport(entry string) string {
entries = NewImport(loopCount, entry).AppendTo(entries)
sourceLines = NewImport(entryCount, entry).AppendTo(sourceLines)
return ""
}

func addEntry(holder SourceHolder) {
//fmt.Printf("%#v\n", holder)
entries = holder.AppendTo(entries)
sourceLines = holder.AppendTo(sourceLines)
}

func isVariable(entry string) bool {
for _, each := range entries {
for _, each := range sourceLines {
if each.IsVariable(entry) {
return true
}
}
return false
}

// undo removes entries appended
// undo removes sourceLines appended
func undo(until int) {
for i, each := range entries {
fmt.Printf("entry:%d loopcount:%d source:%s\n", i, each.LoopCount, each.Source)
}
fmt.Printf("remove all until:%d\n", until)
for {
if len(entries) == 0 {
if len(sourceLines) == 0 {
fmt.Println("(no go source)")
break
}
last := entries[len(entries)-1]
if until == last.LoopCount {
last := sourceLines[len(sourceLines)-1]
if last.EntryCount < until {
// set new entry count
entryCount = last.EntryCount
break
}
fmt.Printf("removed:%s\n", last.Source)
entries = entries[:len(entries)-1]
}
for i, each := range entries {
fmt.Printf("entry:%d loopcount:%d source:%s\n", i, each.LoopCount, each.Source)
sourceLines = sourceLines[:len(sourceLines)-1]
}
}
34 changes: 17 additions & 17 deletions sourceholder.go
Expand Up @@ -18,10 +18,10 @@ const (

// SourceHolder is basically one line of Go source code with meta data
type SourceHolder struct {
LoopCount int // In which REPL count was this created
Type int // one of the constants Import,...
Source string // Go code entered or hidden code produced by rango
Hidden bool // If false then hide this from source listing
EntryCount int // In which REPL count was this created
Type int // one of the constants Import,...
Source string // Go code entered or hidden code produced by rango
Hidden bool // If false then hide this from source listing
// type data
ImportNames []string // If the holder is of type Import then store the packages here
VariableNames []string // If the holder is of type VariableDecl then store the variable names here
Expand All @@ -33,36 +33,36 @@ func (s *SourceHolder) Hide() {
}

// NewImport creates a new SourceHolder of type Import
func NewImport(loopcount int, source string) SourceHolder {
return SourceHolder{LoopCount: loopcount, Type: Import, Source: source}
func NewImport(entryCount int, source string) SourceHolder {
return SourceHolder{EntryCount: entryCount, Type: Import, Source: source}
}

// NewStatement creates a new SourceHolder of type Statement
func NewStatement(loopcount int, source string) SourceHolder {
return SourceHolder{LoopCount: loopcount, Type: Statement, Source: source}
func NewStatement(entryCount int, source string) SourceHolder {
return SourceHolder{EntryCount: entryCount, Type: Statement, Source: source}
}

// NewVariableDecl creates a new SourceHolder of type VariableDecl
func NewVariableDecl(loopcount int, source string) SourceHolder {
func NewVariableDecl(entryCount int, source string) SourceHolder {
return SourceHolder{
LoopCount: loopcount,
EntryCount: entryCount,
Type: VariableDecl,
Source: source,
VariableNames: ParseVariableNames(source)}
}

// NewPrint creates a new SourceHolder of type Print
func NewPrint(loopcount int, source string) SourceHolder {
return SourceHolder{LoopCount: loopcount, Type: Print, Source: source, Hidden: true}
func NewPrint(entryCount int, source string) SourceHolder {
return SourceHolder{EntryCount: entryCount, Type: Print, Source: source, Hidden: true}
}

// AppendTo adds a new SourceHolder to the collection of entries.
// As a side effect, it may produce additional SourceHolders based on its type.
func (s SourceHolder) AppendTo(holders []SourceHolder) []SourceHolder {
extended := append(holders, s)
func (s SourceHolder) AppendTo(sourceLines []SourceHolder) []SourceHolder {
extended := append(sourceLines, s)
if VariableDecl == s.Type {
uselessSource := fmt.Sprintf("nop(%s)", s.VariableNames[0])
useless := NewStatement(s.LoopCount, uselessSource)
useless := NewStatement(s.EntryCount, uselessSource)
(&useless).Hide()
extended = append(extended, useless)
}
Expand All @@ -88,9 +88,9 @@ func ParseVariableNames(source string) []string {
}

// CollectVariables returns the list of declared variable names entered by the user.
func CollectVariables(holders []SourceHolder) []string {
func CollectVariables(sourceLines []SourceHolder) []string {
names := []string{}
for _, each := range holders {
for _, each := range sourceLines {
if VariableDecl == each.Type {
names = append(names, each.VariableNames[0])
}
Expand Down

0 comments on commit 676e145

Please sign in to comment.