Get rid of REPL + minor refactorings

This commit is contained in:
Mattia Giambirtone 2023-06-29 13:57:50 +02:00
parent 45c5be7470
commit 919cc25579
Signed by: nocturn9x
GPG Key ID: 8270F9F467971E59
5 changed files with 59 additions and 191 deletions

View File

@ -114,21 +114,13 @@ out for yourself. Fortunately, the process is quite straightforward:
- First, you're gonna have to install [Nim](https://nim-lang.org/), the language peon is written in. I highly recommend - First, you're gonna have to install [Nim](https://nim-lang.org/), the language peon is written in. I highly recommend
using [choosenim](https://github.com/dom96/choosenim) to manage your Nim installations as it makes switching between them and updating them a breeze using [choosenim](https://github.com/dom96/choosenim) to manage your Nim installations as it makes switching between them and updating them a breeze
- Once Nim is installed, you should install [jale](https://git.nocturn9x.space/japl/jale), peon's custom line editor - Then, clone this repository and compile peon in release mode with `nim c -d:release --passC:"-flto" -o:peon src/main`, which should produce`peon` binary
library written by our beloved [Art](https://git.nocturn9x.space/art). This is needed for the REPL to work: just clone the repository, `cd` into it and run `nimble install`; this will install the library on your ready for you to play with (if your C toolchain doesn't support LTO then you can just omit the `--passC` option, although that would be pretty weird for
system so that the Nim compiler can find it later a modern linker)
- After jale has been installed, clone this repository and run the REPL with `nim r src/main` (in the appropriate
directory of course). If you want to do more than play around in the REPL, I recommend compiling peon in release
mode with `nim -d:release --passC:"-flto" -o:peon`, which should produce a `peon` binary ready for you to play with
(if your C toolchain doesn't support LTO then you can just omit the `--passC` option, although that would be pretty weird
for a modern linker)
- If you want to move the executable to a different directory (say, into your `PATH`), you should copy peon's standard - If you want to move the executable to a different directory (say, into your `PATH`), you should copy peon's standard
library (found in `/src/peon/stdlib`) into a known folder and edit the `moduleLookupPaths` variable inside `src/config.nim` library (found in `/src/peon/stdlib`) into a known folder, edit the `moduleLookupPaths` variable inside `src/config.nim`
by adding said folder to it so that the peon compiler knows where to find modules when you `import std;`. Hopefully I will by adding said folder to it so that the peon compiler knows where to find modules when you `import std;` and then recompile
automate this soon, but as of right now the work is all manual (and it's part of the fun, IMHO ;)) peon. Hopefully I will automate this soon, but as of right now the work is all manual
__Note__: On Linux, peon will also look into `~/.local/peon/stdlib` __Note__: On Linux, peon will also look into `~/.local/peon/stdlib` by default, so you can just create the `~/.local/peon` folder and copy `src/peon/stdlib` there
If you've done everything right, you should be able to run `peon` in your terminal and have it drop you into the REPL. Good
luck and have fun!

View File

@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
## The Peon runtime environment ## The Peon runtime environment
import ../config import config
# Sorry, but there only is enough space # Sorry, but there only is enough space
# for one GC in this VM :( # for one GC in this VM :(
@ -30,16 +30,16 @@ import std/strutils
import std/sets import std/sets
import std/monotimes import std/monotimes
import ../frontend/compiler/targets/bytecode/opcodes
import ../frontend/compiler/targets/bytecode/util/multibyte
when debugVM or debugMem or debugGC or debugAlloc: when debugVM or debugMem or debugGC or debugAlloc:
import std/strformat import std/strformat
import std/sequtils import std/sequtils
import std/terminal import std/terminal
import frontend/compiler/targets/bytecode/opcodes
import frontend/compiler/targets/bytecode/util/multibyte
when debugVM: when debugVM:
proc clearerr(stream: File) {.header: "stdio.h", importc.} proc clearerr(stream: File) {.header: "stdio.h", importc.}

View File

@ -48,7 +48,6 @@ http://www.apache.org/licenses/LICENSE-2.0 for more info.
Basic Usage Basic Usage
----------- -----------
$ peon Open an interactive session (REPL)
$ peon file.pn Run the given Peon source file $ peon file.pn Run the given Peon source file
$ peon file.pbc Run the given Peon bytecode file $ peon file.pbc Run the given Peon bytecode file
@ -62,9 +61,9 @@ Options
-n, --noDump Don't dump the result of compilation to a file. -n, --noDump Don't dump the result of compilation to a file.
Note that no dump is created when using -s/--string Note that no dump is created when using -s/--string
-b, --breakpoints Run the debugger at specific bytecode offsets (comma-separated). -b, --breakpoints Run the debugger at specific bytecode offsets (comma-separated).
Only available with --backend:bytecode and when compiled with VM Only available with --target:bytecode and when compiled with VM
debugging on debugging on (-d:debugVM at build time)
-d, --disassemble Disassemble the output of compilation (only makes sense with --backend:bytecode) -d, --disassemble Disassemble the output of compilation (only makes sense with --target:bytecode)
-m, --mode Set the compilation mode. Acceptable values are 'debug' and -m, --mode Set the compilation mode. Acceptable values are 'debug' and
'release'. Defaults to 'debug' 'release'. Defaults to 'debug'
-c, --compile Compile the code, but do not execute it. Useful along with -d -c, --compile Compile the code, but do not execute it. Useful along with -d
@ -72,11 +71,11 @@ Options
yes/on and no/off yes/on and no/off
--noWarn Disable a specific warning (for example, --noWarn:unusedVariable) --noWarn Disable a specific warning (for example, --noWarn:unusedVariable)
--showMismatches Show all mismatches when function dispatching fails (output is really verbose) --showMismatches Show all mismatches when function dispatching fails (output is really verbose)
--backend Select the compilation backend (valid values are: 'c' and 'bytecode'). Note --target Select the compilation target (valid values are: 'c' and 'bytecode'). Defaults to
that the REPL always uses the bytecode target. Defaults to 'bytecode' 'bytecode'
-o, --output Rename the output file with this value (with --backend:bytecode, a '.pbc' extension -o, --output Rename the output file with this value (with --target:bytecode, a '.pbc' extension
is added if not already present) is added if not already present)
--debug-dump Debug the bytecode serializer. Only makes sense with --backend:bytecode --debug-dump Debug the bytecode serializer. Only makes sense with --target:bytecode
--debug-lexer Debug the peon lexer --debug-lexer Show the lexer's output
--debug-parser Debug the peon parser --debug-parser Show the parser's output
""" """

View File

@ -829,7 +829,6 @@ proc match*(self: Compiler, name: string, kind: Type, node: ASTNode = nil, allow
msg = &"call to undefined function '{name}'" msg = &"call to undefined function '{name}'"
self.error(msg, node) self.error(msg, node)
elif impl.len() > 1: elif impl.len() > 1:
echo "AAAAAA\n\n"
# If we happen to find more than one match, we try again # If we happen to find more than one match, we try again
# and ignore forward declarations and automatic functions # and ignore forward declarations and automatic functions
impl = filterIt(impl, not it.valueType.forwarded and not it.valueType.isAuto) impl = filterIt(impl, not it.valueType.forwarded and not it.valueType.isAuto)

View File

@ -15,149 +15,30 @@
## Peon's main executable ## Peon's main executable
# Our stuff # Our stuff
import frontend/parsing/lexer as l
import frontend/parsing/parser as p
import frontend/compiler/targets/bytecode/target as b
import frontend/compiler/compiler as c
import backend/vm as v
import frontend/compiler/targets/bytecode/util/serializer as s
import frontend/compiler/targets/bytecode/util/debugger
import util/symbols
import util/fmterr
import config import config
import util/fmterr
import util/symbols
import backend/bytecode/vm
import frontend/parsing/lexer
import frontend/parsing/parser
import frontend/compiler/compiler
import frontend/compiler/targets/bytecode/util/debugger
import frontend/compiler/targets/bytecode/util/serializer
import frontend/compiler/targets/bytecode/target as bytecode
# Builtins & external libs # Builtins & external libs
import std/strformat import std/os
import std/times
import std/strutils import std/strutils
import std/terminal import std/terminal
import std/parseopt import std/parseopt
import std/times import std/strformat
import std/os
# Thanks art <3
import jale/editor as ed
import jale/templates
import jale/plugin/defaults
import jale/plugin/editor_history
import jale/keycodes
import jale/multiline
proc getLineEditor: LineEditor =
result = newLineEditor()
result.prompt = "=> "
result.populateDefaults()
let history = result.plugHistory()
result.bindHistory(history)
#[
proc repl(warnings: seq[WarningKind] = @[], mismatches: bool = false, mode: CompileMode = Debug, breakpoints: seq[uint64] = @[]) =
styledEcho fgMagenta, "Welcome into the peon REPL!"
var
keep = true
tokens: seq[Token] = @[]
tree: seq[Declaration] = @[]
compiler = newBytecodeCompiler(replMode=true)
compiled: Chunk = newChunk()
serialized: Serialized
tokenizer = newLexer()
vm = newPeonVM()
parser = newParser()
debugger = newDebugger()
serializer = newSerializer()
editor = getLineEditor()
input: string
first: bool = false
tokenizer.fillSymbolTable()
editor.bindEvent(jeQuit):
stdout.styledWriteLine(fgGreen, "Goodbye!")
keep = false
input = ""
editor.bindKey("ctrl+a"):
editor.content.home()
editor.bindKey("ctrl+e"):
editor.content.`end`()
while keep:
try:
input = editor.read()
if input == "#clear":
stdout.write("\x1Bc")
continue
elif input == "":
continue
tokens = tokenizer.lex(input, "stdin")
if tokens.len() == 0:
continue
if debugLexer:
styledEcho fgCyan, "Tokenization step:"
for i, token in tokens:
if i == tokens.high():
# Who cares about EOF?
break
styledEcho fgGreen, "\t", $token
echo ""
tree = parser.parse(tokens, "stdin", tokenizer.getLines(), input, persist=true)
if tree.len() == 0:
continue
if debugParser:
styledEcho fgCyan, "Parsing step:"
for node in tree:
styledEcho fgGreen, "\t", $node
echo ""
discard compiler.compile(tree, "stdin", tokenizer.getLines(), input, chunk=compiled, showMismatches=mismatches, disabledWarnings=warnings, mode=mode, incremental=first)
if debugCompiler:
styledEcho fgCyan, "Compilation step:\n"
debugger.disassembleChunk(compiled, "stdin")
echo ""
serialized = serializer.loadBytes(serializer.dumpBytes(compiled, "stdin"))
if debugSerializer:
styledEcho fgCyan, "Serialization step: "
styledEcho fgBlue, "\t- Peon version: ", fgYellow, &"{serialized.version.major}.{serialized.version.minor}.{serialized.version.patch}", fgBlue, " (commit ", fgYellow, serialized.commit[0..8], fgBlue, ") on branch ", fgYellow, serialized.branch
stdout.styledWriteLine(fgBlue, "\t- Compilation date & time: ", fgYellow, fromUnix(serialized.compileDate).format("d/M/yyyy HH:mm:ss"))
stdout.styledWrite(fgBlue, &"\t- Constants segment: ")
if serialized.chunk.consts == compiled.consts:
styledEcho fgGreen, "OK"
else:
styledEcho fgRed, "Corrupted"
stdout.styledWrite(fgBlue, &"\t- Code segment: ")
if serialized.chunk.code == compiled.code:
styledEcho fgGreen, "OK"
else:
styledEcho fgRed, "Corrupted"
stdout.styledWrite(fgBlue, "\t- Line info segment: ")
if serialized.chunk.lines == compiled.lines:
styledEcho fgGreen, "OK"
else:
styledEcho fgRed, "Corrupted"
stdout.styledWrite(fgBlue, "\t- Functions segment: ")
if serialized.chunk.functions == compiled.functions:
styledEcho fgGreen, "OK"
else:
styledEcho fgRed, "Corrupted"
if not first:
vm.run(serialized.chunk, repl=true, breakpoints=breakpoints)
first = true
else:
vm.resume(serialized.chunk)
except LexingError:
print(LexingError(getCurrentException()))
except ParseError:
print(ParseError(getCurrentException()))
except CompileError:
print(CompileError(getCurrentException()))
except SerializationError:
var file = SerializationError(getCurrentException()).file
if file notin ["<string>", ""]:
file = relativePath(file, getCurrentDir())
stderr.styledWriteLine(fgRed, styleBright, "Error while (de-)serializing ", fgYellow, file, fgDefault, &": {getCurrentException().msg}")
quit(0)
]#
proc runFile(f: string, fromString: bool = false, dump: bool = true, breakpoints: seq[uint64] = @[], proc runFile(f: string, fromString: bool = false, dump: bool = true, breakpoints: seq[uint64] = @[],
warnings: seq[WarningKind] = @[], mismatches: bool = false, mode: CompileMode = Debug, run: bool = true, warnings: seq[WarningKind] = @[], mismatches: bool = false, mode: CompileMode = Debug,
backend: PeonBackend = PeonBackend.Bytecode, output: string) = run: bool = true, backend: PeonBackend = PeonBackend.Bytecode, output: string) =
var var
tokens: seq[Token] = @[] tokens: seq[Token] = @[]
tree: seq[Declaration] = @[] tree: seq[Declaration] = @[]
@ -170,9 +51,9 @@ proc runFile(f: string, fromString: bool = false, dump: bool = true, breakpoints
serializer = newSerializer() serializer = newSerializer()
vm = newPeonVM() vm = newPeonVM()
input: string input: string
f = f
tokenizer.fillSymbolTable() tokenizer.fillSymbolTable()
try: try:
var f = f
if not fromString: if not fromString:
if not f.endsWith(".pn") and not f.endsWith(".pbc"): if not f.endsWith(".pn") and not f.endsWith(".pbc"):
f &= ".pn" f &= ".pn"
@ -258,22 +139,20 @@ proc runFile(f: string, fromString: bool = false, dump: bool = true, breakpoints
vm.run(serialized.chunk, breakpoints) vm.run(serialized.chunk, breakpoints)
else: else:
discard discard
except LexingError: except LexingError as exc:
print(LexingError(getCurrentException())) print(exc)
except ParseError: except ParseError as exc:
print(ParseError(getCurrentException())) print(exc)
except CompileError: except CompileError as exc:
print(CompileError(getCurrentException())) print(exc)
except SerializationError: except SerializationError as exc:
var file = SerializationError(getCurrentException()).file var file = exc.file
if file notin ["<string>", ""]: if file notin ["<string>", ""]:
file = relativePath(file, getCurrentDir()) file = relativePath(file, getCurrentDir())
stderr.styledWriteLine(fgRed, styleBright, "Error while (de-)serializing ", fgYellow, file, fgDefault, &": {getCurrentException().msg}") stderr.styledWriteLine(fgRed, styleBright, "Error while (de-)serializing ", fgYellow, file, fgDefault, &": {exc.msg}")
except IOError: except IOError as exc:
let exc = getCurrentException()
stderr.styledWriteLine(fgRed, styleBright, "Error while trying to read ", fgYellow, f, fgDefault, &": {exc.msg}") stderr.styledWriteLine(fgRed, styleBright, "Error while trying to read ", fgYellow, f, fgDefault, &": {exc.msg}")
except OSError: except OSError as exc:
let exc = getCurrentException()
stderr.styledWriteLine(fgRed, styleBright, "Error while trying to read ", fgYellow, f, fgDefault, &": {exc.msg} ({osErrorMsg(osLastError())})", stderr.styledWriteLine(fgRed, styleBright, "Error while trying to read ", fgYellow, f, fgDefault, &": {exc.msg} ({osErrorMsg(osLastError())})",
fgRed, "[errno ", fgYellow, $osLastError(), fgRed, "]") fgRed, "[errno ", fgYellow, $osLastError(), fgRed, "]")
@ -290,7 +169,7 @@ when isMainModule:
var mismatches: bool = false var mismatches: bool = false
var mode: CompileMode = CompileMode.Debug var mode: CompileMode = CompileMode.Debug
var run: bool = true var run: bool = true
var backend: PeonBackend var target: PeonBackend
var output: string = "" var output: string = ""
for kind, key, value in optParser.getopt(): for kind, key, value in optParser.getopt():
case kind: case kind:
@ -307,10 +186,10 @@ when isMainModule:
stderr.styledWriteLine(fgRed, styleBright, "Error: ", fgDefault, "invalid value for option 'mode' (valid options are: debug, release)") stderr.styledWriteLine(fgRed, styleBright, "Error: ", fgDefault, "invalid value for option 'mode' (valid options are: debug, release)")
quit() quit()
of "help": of "help":
echo HELP_MESSAGE echo HelpMessage
quit() quit()
of "version": of "version":
echo PEON_VERSION_STRING echo PeonVersionString
quit() quit()
of "string": of "string":
file = key file = key
@ -357,12 +236,12 @@ when isMainModule:
run = false run = false
of "output": of "output":
output = value output = value
of "backend": of "target":
case value: case value:
of "bytecode": of "bytecode":
backend = PeonBackend.Bytecode target = PeonBackend.Bytecode
of "c": of "c":
backend = PeonBackend.NativeC target = PeonBackend.NativeC
of "debug-dump": of "debug-dump":
debugSerializer = true debugSerializer = true
of "debug-lexer": of "debug-lexer":
@ -377,10 +256,10 @@ when isMainModule:
of "o": of "o":
output = value output = value
of "h": of "h":
echo HELP_MESSAGE echo HelpMessage
quit() quit()
of "v": of "v":
echo PEON_VERSION_STRING echo PeonVersionString
quit() quit()
of "s": of "s":
file = key file = key
@ -419,7 +298,6 @@ when isMainModule:
if breaks.len() == 0 and debugVM: if breaks.len() == 0 and debugVM:
breaks.add(0) breaks.add(0)
if file == "": if file == "":
echo "Sorry, the REPL is currently broken :(" echo HelpMessage
#repl(warnings, mismatches, mode, breaks)
else: else:
runFile(file, fromString, dump, breaks, warnings, mismatches, mode, run, backend, output) runFile(file, fromString, dump, breaks, warnings, mismatches, mode, run, target, output)