Fix a couple of bugs in the REPL.

- Not sure what was going on, but fix #456. It makes more sense for the
  REPL to invoke the compiled code as a fiber than a function anyway.

- Flush stdout before reading from stdin since System.print() no longer
  does that automatically.
This commit is contained in:
Bob Nystrom
2017-10-13 07:58:57 -07:00
parent 97b2e1c818
commit 9661a5b999
12 changed files with 174 additions and 127 deletions
+43 -36
View File
@@ -1,5 +1,5 @@
import "meta" for Meta
import "io" for Stdin
import "io" for Stdin, Stdout
import "os" for Platform
/// Abstract base class for the REPL. Manages the input line and history, but
@@ -154,51 +154,44 @@ class Repl {
System.print()
// Guess if it looks like a statement or expression. Statements need to be
// evaluated at the top level in case they declare variables, but they
// don't return a value. Expressions need to have their result displayed.
var tokens = lex(input, false)
// Guess if it looks like a statement or expression. If it looks like an
// expression, we try to print the result.
var token = lexFirst(input)
// No code, so do nothing.
if (tokens.isEmpty) return
if (token == null) return
var first = tokens[0]
var isStatement =
first.type == Token.breakKeyword ||
first.type == Token.classKeyword ||
first.type == Token.forKeyword ||
first.type == Token.foreignKeyword ||
first.type == Token.ifKeyword ||
first.type == Token.importKeyword ||
first.type == Token.returnKeyword ||
first.type == Token.varKeyword ||
first.type == Token.whileKeyword
token.type == Token.breakKeyword ||
token.type == Token.classKeyword ||
token.type == Token.forKeyword ||
token.type == Token.foreignKeyword ||
token.type == Token.ifKeyword ||
token.type == Token.importKeyword ||
token.type == Token.returnKeyword ||
token.type == Token.varKeyword ||
token.type == Token.whileKeyword
var fiber
if (isStatement) {
fiber = Fiber.new {
// TODO: Should evaluate in main module, not repl's own.
Meta.eval(input)
}
var result = fiber.try()
if (fiber.error == null) return
fiber = Meta.compile(input)
} else {
// TODO: Should evaluate in main module, not repl's own.
var function = Meta.compileExpression(input)
if (function == null) return
fiber = Fiber.new(function)
var result = fiber.try()
if (fiber.error == null) {
// TODO: Handle error in result.toString.
showResult(result)
return
}
fiber = Meta.compileExpression(input)
}
// TODO: Include callstack.
showRuntimeError("Runtime error: %(fiber.error)")
// Stop if there was a compile error.
if (fiber == null) return
var result = fiber.try()
if (fiber.error != null) {
// TODO: Include callstack.
showRuntimeError("Runtime error: %(fiber.error)")
return
}
if (!isStatement) {
showResult(result)
}
}
lex(line, includeWhitespace) {
@@ -217,6 +210,18 @@ class Repl {
return tokens
}
lexFirst(line) {
var lexer = Lexer.new(line)
while (true) {
var token = lexer.readToken()
if (token.type == Token.eof) return null
if (token.type != Token.comment && token.type != Token.whitespace) {
return token
}
}
}
/// Gets the best possible auto-completion for the current line, or null if
/// there is none. The completion is the remaining string to append to the
/// line, not the entire completed line.
@@ -256,6 +261,7 @@ class SimpleRepl is Repl {
// Write the line.
System.write(line)
Stdout.flush()
}
showResult(value) {
@@ -351,6 +357,7 @@ class AnsiRepl is Repl {
// Position the cursor.
System.write("\r\x1b[%(2 + cursor)C")
Stdout.flush()
}
showResult(value) {
+43 -36
View File
@@ -1,7 +1,7 @@
// Generated automatically from src/module/repl.wren. Do not edit.
static const char* replModuleSource =
"import \"meta\" for Meta\n"
"import \"io\" for Stdin\n"
"import \"io\" for Stdin, Stdout\n"
"import \"os\" for Platform\n"
"\n"
"/// Abstract base class for the REPL. Manages the input line and history, but\n"
@@ -156,51 +156,44 @@ static const char* replModuleSource =
"\n"
" System.print()\n"
"\n"
" // Guess if it looks like a statement or expression. Statements need to be\n"
" // evaluated at the top level in case they declare variables, but they\n"
" // don't return a value. Expressions need to have their result displayed.\n"
" var tokens = lex(input, false)\n"
" // Guess if it looks like a statement or expression. If it looks like an\n"
" // expression, we try to print the result.\n"
" var token = lexFirst(input)\n"
"\n"
" // No code, so do nothing.\n"
" if (tokens.isEmpty) return\n"
" if (token == null) return\n"
"\n"
" var first = tokens[0]\n"
" var isStatement =\n"
" first.type == Token.breakKeyword ||\n"
" first.type == Token.classKeyword ||\n"
" first.type == Token.forKeyword ||\n"
" first.type == Token.foreignKeyword ||\n"
" first.type == Token.ifKeyword ||\n"
" first.type == Token.importKeyword ||\n"
" first.type == Token.returnKeyword ||\n"
" first.type == Token.varKeyword ||\n"
" first.type == Token.whileKeyword\n"
" token.type == Token.breakKeyword ||\n"
" token.type == Token.classKeyword ||\n"
" token.type == Token.forKeyword ||\n"
" token.type == Token.foreignKeyword ||\n"
" token.type == Token.ifKeyword ||\n"
" token.type == Token.importKeyword ||\n"
" token.type == Token.returnKeyword ||\n"
" token.type == Token.varKeyword ||\n"
" token.type == Token.whileKeyword\n"
"\n"
" var fiber\n"
" if (isStatement) {\n"
" fiber = Fiber.new {\n"
" // TODO: Should evaluate in main module, not repl's own.\n"
" Meta.eval(input)\n"
" }\n"
"\n"
" var result = fiber.try()\n"
" if (fiber.error == null) return\n"
" fiber = Meta.compile(input)\n"
" } else {\n"
" // TODO: Should evaluate in main module, not repl's own.\n"
" var function = Meta.compileExpression(input)\n"
" if (function == null) return\n"
"\n"
" fiber = Fiber.new(function)\n"
" var result = fiber.try()\n"
" if (fiber.error == null) {\n"
" // TODO: Handle error in result.toString.\n"
" showResult(result)\n"
" return\n"
" }\n"
" fiber = Meta.compileExpression(input)\n"
" }\n"
"\n"
" // TODO: Include callstack.\n"
" showRuntimeError(\"Runtime error: %(fiber.error)\")\n"
" // Stop if there was a compile error.\n"
" if (fiber == null) return\n"
"\n"
" var result = fiber.try()\n"
" if (fiber.error != null) {\n"
" // TODO: Include callstack.\n"
" showRuntimeError(\"Runtime error: %(fiber.error)\")\n"
" return\n"
" }\n"
"\n"
" if (!isStatement) {\n"
" showResult(result)\n"
" }\n"
" }\n"
"\n"
" lex(line, includeWhitespace) {\n"
@@ -219,6 +212,18 @@ static const char* replModuleSource =
" return tokens\n"
" }\n"
"\n"
" lexFirst(line) {\n"
" var lexer = Lexer.new(line)\n"
" while (true) {\n"
" var token = lexer.readToken()\n"
" if (token.type == Token.eof) return null\n"
"\n"
" if (token.type != Token.comment && token.type != Token.whitespace) {\n"
" return token\n"
" }\n"
" }\n"
" }\n"
"\n"
" /// Gets the best possible auto-completion for the current line, or null if\n"
" /// there is none. The completion is the remaining string to append to the\n"
" /// line, not the entire completed line.\n"
@@ -258,6 +263,7 @@ static const char* replModuleSource =
"\n"
" // Write the line.\n"
" System.write(line)\n"
" Stdout.flush()\n"
" }\n"
"\n"
" showResult(value) {\n"
@@ -353,6 +359,7 @@ static const char* replModuleSource =
"\n"
" // Position the cursor.\n"
" System.write(\"\r\x1b[%(2 + cursor)C\")\n"
" Stdout.flush()\n"
" }\n"
"\n"
" showResult(value) {\n"