Reverted to old style REPL and added note in the README
This commit is contained in:
parent
3dead5a555
commit
26e16e7f8e
|
@ -42,6 +42,13 @@ Peon is a simple, functional, async-first programming language with a focus on c
|
||||||
**Disclaimer**: The project is still in its very early days: lots of stuff is not implemented, a work in progress or
|
**Disclaimer**: The project is still in its very early days: lots of stuff is not implemented, a work in progress or
|
||||||
otherwise outright broken. Feel free to report bugs!
|
otherwise outright broken. Feel free to report bugs!
|
||||||
|
|
||||||
|
|
||||||
|
**Disclaimer 2**: Currently the REPL is very basic (it incrementally joins your lines with a newline, as if it was compiling a new file every time),
|
||||||
|
because incremental compilation is designed for modules and it doesn't play well with the interactive nature of a REPL session. To show the current state
|
||||||
|
of the REPL, type `#show` (this will print all the code that has been typed so far), while to reset everything (will un-import modules and stuff), type
|
||||||
|
`#reset`. You can also type `#clear` if you want a clean slate to type in, but note that it won't reset the REPL state.
|
||||||
|
|
||||||
|
|
||||||
Also, yes: peon is yet another programming language inspired by Bob's book, but it is also **very**
|
Also, yes: peon is yet another programming language inspired by Bob's book, but it is also **very**
|
||||||
different from Lox, which is an object-oriented, dynamically typed and very high level programming language, whereas
|
different from Lox, which is an object-oriented, dynamically typed and very high level programming language, whereas
|
||||||
peon is a statically-typed, functional language which aims to allow low-level interfacing with C and Nim code while
|
peon is a statically-typed, functional language which aims to allow low-level interfacing with C and Nim code while
|
||||||
|
|
|
@ -55,10 +55,11 @@ $ peon file.pbc Runs the given Peon bytecode file
|
||||||
Command-line options
|
Command-line options
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
-h, --help Shows this help text and exits
|
-h, --help Show this help text and exits
|
||||||
-v, --version Prints the peon version number and exits
|
-v, --version Print the peon version number and exits
|
||||||
-s, --string Executes the passed string as if it was a file
|
-s, --string Execute the passed string as if it was a file
|
||||||
-i, --interactive Enables interactive mode: a REPL session is opened after execution
|
-n, --nodump Don't dump the result of compilation to a *.pbc file
|
||||||
-d, --nodump Disables dumping the result of bytecode compilation to files
|
-b, --breakpoints Run the debugger at specific bytecode offsets (comma-separated).
|
||||||
-b, --breakpoints Pauses execution of the peon virtual machine and runs the debugger at specific bytecode offsets
|
Only available when compiled with -d:debugVM
|
||||||
|
-d, --disassemble Disassemble the given bytecode file instead of executing it
|
||||||
"""
|
"""
|
||||||
|
|
59
src/main.nim
59
src/main.nim
|
@ -55,21 +55,19 @@ proc repl =
|
||||||
compiled: Chunk = newChunk()
|
compiled: Chunk = newChunk()
|
||||||
serialized: Serialized
|
serialized: Serialized
|
||||||
tokenizer = newLexer()
|
tokenizer = newLexer()
|
||||||
parser = newParser()
|
|
||||||
compiler = newCompiler(replMode=true)
|
|
||||||
vm = newPeonVM()
|
vm = newPeonVM()
|
||||||
debugger = newDebugger()
|
debugger = newDebugger()
|
||||||
serializer = newSerializer()
|
serializer = newSerializer()
|
||||||
editor = getLineEditor()
|
editor = getLineEditor()
|
||||||
input: string
|
input: string
|
||||||
current: string
|
current: string
|
||||||
incremental: bool = false
|
|
||||||
tokenizer.fillSymbolTable()
|
tokenizer.fillSymbolTable()
|
||||||
editor.bindEvent(jeQuit):
|
editor.bindEvent(jeQuit):
|
||||||
stdout.styledWriteLine(fgGreen, "Goodbye!")
|
stdout.styledWriteLine(fgGreen, "Goodbye!")
|
||||||
editor.prompt = "=> "
|
editor.prompt = "=> "
|
||||||
keep = false
|
keep = false
|
||||||
input = ""
|
input = ""
|
||||||
|
current = ""
|
||||||
editor.bindKey("ctrl+a"):
|
editor.bindKey("ctrl+a"):
|
||||||
editor.content.home()
|
editor.content.home()
|
||||||
editor.bindKey("ctrl+e"):
|
editor.bindKey("ctrl+e"):
|
||||||
|
@ -77,18 +75,20 @@ proc repl =
|
||||||
while keep:
|
while keep:
|
||||||
try:
|
try:
|
||||||
input = editor.read()
|
input = editor.read()
|
||||||
if input.len() == 0:
|
if input == "#reset":
|
||||||
continue
|
|
||||||
elif input == "#reset":
|
|
||||||
compiled = newChunk()
|
compiled = newChunk()
|
||||||
compiler = newCompiler()
|
current = ""
|
||||||
parser = newParser()
|
|
||||||
incremental = false
|
|
||||||
continue
|
continue
|
||||||
|
elif input == "#show":
|
||||||
|
echo current
|
||||||
elif input == "#clear":
|
elif input == "#clear":
|
||||||
stdout.write("\x1Bc")
|
stdout.write("\x1Bc")
|
||||||
continue
|
continue
|
||||||
tokens = tokenizer.lex(input, "stdin")
|
else:
|
||||||
|
current &= input & "\n"
|
||||||
|
if current.len() == 0:
|
||||||
|
continue
|
||||||
|
tokens = tokenizer.lex(current, "stdin")
|
||||||
if tokens.len() == 0:
|
if tokens.len() == 0:
|
||||||
continue
|
continue
|
||||||
when debugLexer:
|
when debugLexer:
|
||||||
|
@ -99,7 +99,7 @@ proc repl =
|
||||||
break
|
break
|
||||||
styledEcho fgGreen, "\t", $token
|
styledEcho fgGreen, "\t", $token
|
||||||
echo ""
|
echo ""
|
||||||
tree = parser.parse(tokens, "stdin", tokenizer.getLines(), input, persist=true)
|
tree = newParser().parse(tokens, "stdin", tokenizer.getLines(), current)
|
||||||
if tree.len() == 0:
|
if tree.len() == 0:
|
||||||
continue
|
continue
|
||||||
when debugParser:
|
when debugParser:
|
||||||
|
@ -107,8 +107,7 @@ proc repl =
|
||||||
for node in tree:
|
for node in tree:
|
||||||
styledEcho fgGreen, "\t", $node
|
styledEcho fgGreen, "\t", $node
|
||||||
echo ""
|
echo ""
|
||||||
discard compiler.compile(tree, "stdin", tokenizer.getLines(), input, chunk=compiled, incremental=incremental)
|
compiled = newCompiler(replMode=true).compile(tree, "stdin", tokenizer.getLines(), current)
|
||||||
incremental = true
|
|
||||||
when debugCompiler:
|
when debugCompiler:
|
||||||
styledEcho fgCyan, "Compilation step:\n"
|
styledEcho fgCyan, "Compilation step:\n"
|
||||||
debugger.disassembleChunk(compiled, "stdin")
|
debugger.disassembleChunk(compiled, "stdin")
|
||||||
|
@ -141,7 +140,6 @@ proc repl =
|
||||||
styledEcho fgRed, "Corrupted"
|
styledEcho fgRed, "Corrupted"
|
||||||
vm.run(serialized.chunk)
|
vm.run(serialized.chunk)
|
||||||
except LexingError:
|
except LexingError:
|
||||||
input = ""
|
|
||||||
var exc = LexingError(getCurrentException())
|
var exc = LexingError(getCurrentException())
|
||||||
let relPos = exc.lexer.getRelPos(exc.line)
|
let relPos = exc.lexer.getRelPos(exc.line)
|
||||||
let line = exc.lexer.getSource().splitLines()[exc.line - 1].strip(chars={'\n'})
|
let line = exc.lexer.getSource().splitLines()[exc.line - 1].strip(chars={'\n'})
|
||||||
|
@ -151,13 +149,14 @@ proc repl =
|
||||||
styledEcho fgBlue, "Source line: " , fgDefault, line
|
styledEcho fgBlue, "Source line: " , fgDefault, line
|
||||||
styledEcho fgCyan, " ".repeat(len("Source line: ") + line.find(exc.lexeme)) & "^".repeat(abs(relPos.stop - relPos.start - line.find(exc.lexeme)))
|
styledEcho fgCyan, " ".repeat(len("Source line: ") + line.find(exc.lexeme)) & "^".repeat(abs(relPos.stop - relPos.start - line.find(exc.lexeme)))
|
||||||
except ParseError:
|
except ParseError:
|
||||||
input = ""
|
|
||||||
let exc = ParseError(getCurrentException())
|
let exc = ParseError(getCurrentException())
|
||||||
let lexeme = exc.token.lexeme
|
let lexeme = exc.token.lexeme
|
||||||
var lineNo = exc.token.line
|
var lineNo = exc.token.line
|
||||||
|
if exc.token.kind == EndOfFile:
|
||||||
|
dec(lineNo)
|
||||||
let relPos = exc.parser.getRelPos(lineNo)
|
let relPos = exc.parser.getRelPos(lineNo)
|
||||||
let fn = parser.getCurrentFunction()
|
let fn = exc.parser.getCurrentFunction()
|
||||||
let line = exc.parser.getSource().splitLines()[lineNo - 1].strip(chars={'\n'})
|
let line = exc.parser.getSource().splitLines()[lineNo - 1].strip()
|
||||||
var fnMsg = ""
|
var fnMsg = ""
|
||||||
if fn != nil and fn.kind == funDecl:
|
if fn != nil and fn.kind == funDecl:
|
||||||
fnMsg &= &"in function '{FunDecl(fn).name.token.lexeme}'"
|
fnMsg &= &"in function '{FunDecl(fn).name.token.lexeme}'"
|
||||||
|
@ -187,7 +186,7 @@ proc repl =
|
||||||
quit(0)
|
quit(0)
|
||||||
|
|
||||||
|
|
||||||
proc runFile(f: string, interactive: bool = false, fromString: bool = false, dump: bool = true, breakpoints: seq[uint64] = @[]) =
|
proc runFile(f: string, fromString: bool = false, dump: bool = true, breakpoints: seq[uint64] = @[], dis: bool = false) =
|
||||||
var
|
var
|
||||||
tokens: seq[Token] = @[]
|
tokens: seq[Token] = @[]
|
||||||
tree: seq[Declaration] = @[]
|
tree: seq[Declaration] = @[]
|
||||||
|
@ -204,8 +203,11 @@ proc runFile(f: string, interactive: bool = false, fromString: bool = false, dum
|
||||||
try:
|
try:
|
||||||
var f = f
|
var f = f
|
||||||
if not fromString:
|
if not fromString:
|
||||||
if not f.endsWith(".pn"):
|
if not f.endsWith(".pn") and not f.endsWith(".pbc"):
|
||||||
f &= ".pn"
|
f &= ".pn"
|
||||||
|
if dis:
|
||||||
|
debugger.disassembleChunk(serializer.loadFile(f).chunk, f)
|
||||||
|
return
|
||||||
input = readFile(f)
|
input = readFile(f)
|
||||||
else:
|
else:
|
||||||
input = f
|
input = f
|
||||||
|
@ -238,7 +240,7 @@ proc runFile(f: string, interactive: bool = false, fromString: bool = false, dum
|
||||||
if path.len() > 0:
|
if path.len() > 0:
|
||||||
path &= "/"
|
path &= "/"
|
||||||
path &= splitFile(f).name & ".pbc"
|
path &= splitFile(f).name & ".pbc"
|
||||||
if dump or not fromString:
|
if dump and not fromString:
|
||||||
serializer.dumpFile(compiled, f, path)
|
serializer.dumpFile(compiled, f, path)
|
||||||
serialized = serializer.loadFile(path)
|
serialized = serializer.loadFile(path)
|
||||||
else:
|
else:
|
||||||
|
@ -281,6 +283,8 @@ proc runFile(f: string, interactive: bool = false, fromString: bool = false, dum
|
||||||
let exc = ParseError(getCurrentException())
|
let exc = ParseError(getCurrentException())
|
||||||
let lexeme = exc.token.lexeme
|
let lexeme = exc.token.lexeme
|
||||||
var lineNo = exc.token.line
|
var lineNo = exc.token.line
|
||||||
|
if exc.token.kind == EndOfFile:
|
||||||
|
dec(lineNo)
|
||||||
let relPos = exc.parser.getRelPos(lineNo)
|
let relPos = exc.parser.getRelPos(lineNo)
|
||||||
let fn = parser.getCurrentFunction()
|
let fn = parser.getCurrentFunction()
|
||||||
let line = exc.parser.getSource().splitLines()[lineNo - 1].strip(chars={'\n'})
|
let line = exc.parser.getSource().splitLines()[lineNo - 1].strip(chars={'\n'})
|
||||||
|
@ -322,9 +326,9 @@ when isMainModule:
|
||||||
var optParser = initOptParser(commandLineParams())
|
var optParser = initOptParser(commandLineParams())
|
||||||
var file: string = ""
|
var file: string = ""
|
||||||
var fromString: bool = false
|
var fromString: bool = false
|
||||||
var interactive: bool = false
|
|
||||||
var dump: bool = true
|
var dump: bool = true
|
||||||
var breaks: seq[uint64] = @[]
|
var breaks: seq[uint64] = @[]
|
||||||
|
var dis: bool = false
|
||||||
for kind, key, value in optParser.getopt():
|
for kind, key, value in optParser.getopt():
|
||||||
case kind:
|
case kind:
|
||||||
of cmdArgument:
|
of cmdArgument:
|
||||||
|
@ -340,8 +344,6 @@ when isMainModule:
|
||||||
of "string":
|
of "string":
|
||||||
file = key
|
file = key
|
||||||
fromString = true
|
fromString = true
|
||||||
of "interactive":
|
|
||||||
interactive = true
|
|
||||||
of "no-dump":
|
of "no-dump":
|
||||||
dump = false
|
dump = false
|
||||||
of "breakpoints":
|
of "breakpoints":
|
||||||
|
@ -354,6 +356,8 @@ when isMainModule:
|
||||||
except ValueError:
|
except ValueError:
|
||||||
echo &"error: invalid breakpoint value '{point}'"
|
echo &"error: invalid breakpoint value '{point}'"
|
||||||
quit()
|
quit()
|
||||||
|
of "disassemble":
|
||||||
|
dis = true
|
||||||
else:
|
else:
|
||||||
echo &"error: unkown option '{key}'"
|
echo &"error: unkown option '{key}'"
|
||||||
quit()
|
quit()
|
||||||
|
@ -368,9 +372,7 @@ when isMainModule:
|
||||||
of "s":
|
of "s":
|
||||||
file = key
|
file = key
|
||||||
fromString = true
|
fromString = true
|
||||||
of "i":
|
of "n":
|
||||||
interactive = true
|
|
||||||
of "d":
|
|
||||||
dump = false
|
dump = false
|
||||||
of "b":
|
of "b":
|
||||||
when not debugVM:
|
when not debugVM:
|
||||||
|
@ -382,17 +384,18 @@ when isMainModule:
|
||||||
except ValueError:
|
except ValueError:
|
||||||
echo &"error: invalid breakpoint value '{point}'"
|
echo &"error: invalid breakpoint value '{point}'"
|
||||||
quit()
|
quit()
|
||||||
|
of "d":
|
||||||
|
dis = true
|
||||||
else:
|
else:
|
||||||
echo &"error: unkown option '{key}'"
|
echo &"error: unkown option '{key}'"
|
||||||
quit()
|
quit()
|
||||||
else:
|
else:
|
||||||
echo "usage: peon [options] [filename.pn]"
|
echo "usage: peon [options] [filename.pn]"
|
||||||
quit()
|
quit()
|
||||||
# TODO: Use interactive
|
|
||||||
if file == "":
|
if file == "":
|
||||||
repl()
|
repl()
|
||||||
else:
|
else:
|
||||||
runFile(file, interactive, fromString, dump, breaks)
|
runFile(file, fromString, dump, breaks, dis)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue