2022-08-17 22:35:21 +02:00
|
|
|
# Copyright 2022 Mattia Giambirtone & All Contributors
|
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
|
|
|
|
## Peon's main executable
|
|
|
|
|
2022-08-18 11:15:55 +02:00
|
|
|
# Our stuff
|
|
|
|
import frontend/lexer as l
|
|
|
|
import frontend/parser as p
|
|
|
|
import frontend/compiler as c
|
|
|
|
import backend/vm as v
|
|
|
|
import util/serializer as s
|
|
|
|
import util/debugger
|
|
|
|
import util/symbols
|
|
|
|
import config
|
|
|
|
|
2022-04-07 15:06:15 +02:00
|
|
|
# Builtins & external libs
|
2022-08-17 19:31:27 +02:00
|
|
|
import std/strformat
|
|
|
|
import std/strutils
|
|
|
|
import std/terminal
|
|
|
|
import std/parseopt
|
2022-08-18 11:15:55 +02:00
|
|
|
when debugSerializer:
|
|
|
|
import std/times
|
2022-08-17 19:31:27 +02:00
|
|
|
import std/os
|
2022-05-25 11:49:21 +02:00
|
|
|
|
2022-05-20 18:34:14 +02:00
|
|
|
# Thanks art <3
|
2022-04-07 11:51:36 +02:00
|
|
|
import jale/editor as ed
|
2022-04-05 11:23:59 +02:00
|
|
|
import jale/templates
|
|
|
|
import jale/plugin/defaults
|
|
|
|
import jale/plugin/editor_history
|
|
|
|
import jale/keycodes
|
|
|
|
import jale/multiline
|
|
|
|
|
2022-04-07 15:06:15 +02:00
|
|
|
|
2022-04-07 11:51:36 +02:00
|
|
|
# Forward declarations
|
|
|
|
proc getLineEditor: LineEditor
|
2022-04-05 11:23:59 +02:00
|
|
|
|
2022-07-10 16:31:30 +02:00
|
|
|
|
2022-08-17 17:31:15 +02:00
|
|
|
proc repl =
|
2022-05-22 13:02:48 +02:00
|
|
|
styledEcho fgMagenta, "Welcome into the peon REPL!"
|
2022-04-11 14:41:20 +02:00
|
|
|
var
|
|
|
|
keep = true
|
|
|
|
tokens: seq[Token] = @[]
|
2022-05-18 13:32:32 +02:00
|
|
|
tree: seq[Declaration] = @[]
|
2022-08-16 12:20:07 +02:00
|
|
|
compiled: Chunk = newChunk()
|
2022-04-11 14:41:20 +02:00
|
|
|
serialized: Serialized
|
|
|
|
tokenizer = newLexer()
|
|
|
|
parser = newParser()
|
2022-05-30 22:06:15 +02:00
|
|
|
compiler = newCompiler(replMode=true)
|
2022-08-17 17:31:15 +02:00
|
|
|
vm = newPeonVM()
|
2022-05-24 22:26:45 +02:00
|
|
|
debugger = newDebugger()
|
2022-04-11 14:41:20 +02:00
|
|
|
serializer = newSerializer()
|
|
|
|
editor = getLineEditor()
|
|
|
|
input: string
|
2022-05-30 12:31:15 +02:00
|
|
|
current: string
|
2022-08-16 12:20:07 +02:00
|
|
|
incremental: bool = false
|
2022-04-11 14:41:20 +02:00
|
|
|
tokenizer.fillSymbolTable()
|
2022-04-07 11:51:36 +02:00
|
|
|
editor.bindEvent(jeQuit):
|
2022-05-20 18:34:14 +02:00
|
|
|
stdout.styledWriteLine(fgGreen, "Goodbye!")
|
|
|
|
editor.prompt = ""
|
2022-04-05 11:23:59 +02:00
|
|
|
keep = false
|
2022-05-30 12:31:15 +02:00
|
|
|
input = ""
|
2022-04-07 11:51:36 +02:00
|
|
|
editor.bindKey("ctrl+a"):
|
|
|
|
editor.content.home()
|
|
|
|
editor.bindKey("ctrl+e"):
|
|
|
|
editor.content.`end`()
|
2022-04-05 11:23:59 +02:00
|
|
|
while keep:
|
|
|
|
try:
|
2022-05-30 12:31:15 +02:00
|
|
|
# We incrementally add content to the input
|
|
|
|
# so that you can, for example, define a function
|
|
|
|
# then press enter and use it at the next iteration
|
|
|
|
# of the read loop
|
2022-08-16 12:20:07 +02:00
|
|
|
input = editor.read()
|
|
|
|
if input.len() == 0:
|
2022-04-28 18:06:53 +02:00
|
|
|
continue
|
2022-08-16 12:20:07 +02:00
|
|
|
elif input == "#reset":
|
|
|
|
compiled = newChunk()
|
2022-08-17 22:35:21 +02:00
|
|
|
compiler = newCompiler()
|
|
|
|
parser = newParser()
|
2022-08-16 12:20:07 +02:00
|
|
|
incremental = false
|
2022-05-30 12:31:15 +02:00
|
|
|
continue
|
2022-08-16 12:20:07 +02:00
|
|
|
elif input == "#clear":
|
2022-05-30 12:31:15 +02:00
|
|
|
stdout.write("\x1Bc")
|
|
|
|
continue
|
2022-05-20 18:34:14 +02:00
|
|
|
tokens = tokenizer.lex(input, "stdin")
|
2022-04-28 18:06:53 +02:00
|
|
|
if tokens.len() == 0:
|
|
|
|
continue
|
|
|
|
when debugLexer:
|
2022-05-20 18:34:14 +02:00
|
|
|
styledEcho fgCyan, "Tokenization step:"
|
2022-04-28 18:06:53 +02:00
|
|
|
for i, token in tokens:
|
|
|
|
if i == tokens.high():
|
|
|
|
# Who cares about EOF?
|
|
|
|
break
|
2022-05-20 18:34:14 +02:00
|
|
|
styledEcho fgGreen, "\t", $token
|
2022-04-28 18:06:53 +02:00
|
|
|
echo ""
|
2022-08-17 17:31:15 +02:00
|
|
|
tree = parser.parse(tokens, "stdin", tokenizer.getLines(), input, persist=true)
|
2022-05-07 10:48:01 +02:00
|
|
|
if tree.len() == 0:
|
|
|
|
continue
|
2022-04-28 18:06:53 +02:00
|
|
|
when debugParser:
|
2022-05-20 18:34:14 +02:00
|
|
|
styledEcho fgCyan, "Parsing step:"
|
2022-04-28 18:06:53 +02:00
|
|
|
for node in tree:
|
2022-05-20 18:34:14 +02:00
|
|
|
styledEcho fgGreen, "\t", $node
|
2022-04-28 18:06:53 +02:00
|
|
|
echo ""
|
2022-08-16 12:20:07 +02:00
|
|
|
discard compiler.compile(tree, "stdin", tokenizer.getLines(), input, chunk=compiled, incremental=incremental, terminateScope=false)
|
|
|
|
incremental = true
|
2022-04-28 18:06:53 +02:00
|
|
|
when debugCompiler:
|
2022-05-26 18:31:40 +02:00
|
|
|
styledEcho fgCyan, "Compilation step:\n"
|
2022-05-24 22:26:45 +02:00
|
|
|
debugger.disassembleChunk(compiled, "stdin")
|
2022-04-28 18:06:53 +02:00
|
|
|
echo ""
|
2022-05-02 17:26:38 +02:00
|
|
|
|
2022-08-16 12:20:07 +02:00
|
|
|
serialized = serializer.loadBytes(serializer.dumpBytes(compiled, "stdin"))
|
2022-04-28 18:06:53 +02:00
|
|
|
when debugSerializer:
|
2022-05-20 18:34:14 +02:00
|
|
|
styledEcho fgCyan, "Serialization step: "
|
2022-05-25 12:15:45 +02:00
|
|
|
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
|
2022-05-20 18:34:14 +02:00
|
|
|
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"
|
2022-05-25 14:38:34 +02:00
|
|
|
stdout.styledWrite(fgBlue, "\t- CFI segment: ")
|
|
|
|
if serialized.chunk.cfi == compiled.cfi:
|
|
|
|
styledEcho fgGreen, "OK"
|
|
|
|
else:
|
|
|
|
styledEcho fgRed, "Corrupted"
|
2022-04-28 18:06:53 +02:00
|
|
|
vm.run(serialized.chunk)
|
2022-04-05 11:23:59 +02:00
|
|
|
except LexingError:
|
2022-05-30 12:31:15 +02:00
|
|
|
input = ""
|
2022-08-17 17:31:15 +02:00
|
|
|
var exc = LexingError(getCurrentException())
|
|
|
|
if exc.lexeme == "":
|
|
|
|
exc.line -= 1
|
2022-08-15 22:15:06 +02:00
|
|
|
let relPos = exc.lexer.getRelPos(exc.line)
|
|
|
|
let line = exc.lexer.getSource().splitLines()[exc.line - 1].strip(chars={'\n'})
|
2022-05-22 13:02:48 +02:00
|
|
|
stderr.styledWriteLine(fgRed, "A fatal error occurred while parsing ", fgYellow, &"'{exc.file}'", fgRed, ", module ",
|
2022-08-15 22:15:06 +02:00
|
|
|
fgYellow, &"'{exc.file.extractFilename()}'", fgRed, ", line ", fgYellow, $exc.line, fgRed, " at ", fgYellow, &"'{exc.lexeme.escape()}'",
|
|
|
|
fgRed, ": ", fgGreen , getCurrentExceptionMsg())
|
2022-05-22 11:49:38 +02:00
|
|
|
styledEcho fgBlue, "Source line: " , fgDefault, line
|
|
|
|
styledEcho fgCyan, " ".repeat(len("Source line: ")) & "^".repeat(relPos.stop - relPos.start)
|
2022-04-05 11:23:59 +02:00
|
|
|
except ParseError:
|
2022-05-30 12:31:15 +02:00
|
|
|
input = ""
|
2022-05-22 13:02:48 +02:00
|
|
|
let exc = ParseError(getCurrentException())
|
|
|
|
let lexeme = exc.token.lexeme
|
2022-08-17 17:31:15 +02:00
|
|
|
var lineNo = exc.token.line
|
|
|
|
if exc.token.kind == EndOfFile:
|
|
|
|
lineNo -= 1
|
2022-08-15 22:15:06 +02:00
|
|
|
let relPos = exc.parser.getRelPos(lineNo)
|
2022-05-22 11:49:38 +02:00
|
|
|
let fn = parser.getCurrentFunction()
|
2022-08-15 22:15:06 +02:00
|
|
|
let line = exc.parser.getSource().splitLines()[lineNo - 1].strip(chars={'\n'})
|
2022-05-22 11:49:38 +02:00
|
|
|
var fnMsg = ""
|
|
|
|
if fn != nil and fn.kind == funDecl:
|
|
|
|
fnMsg &= &"in function '{FunDecl(fn).name.token.lexeme}'"
|
2022-05-22 13:02:48 +02:00
|
|
|
stderr.styledWriteLine(fgRed, "A fatal error occurred while parsing ", fgYellow, &"'{exc.file}'", fgRed, ", module ",
|
2022-08-15 22:15:06 +02:00
|
|
|
fgYellow, &"'{exc.module}'", fgRed, ", line ", fgYellow, $lineNo, fgRed, " at ", fgYellow, &"'{lexeme}'",
|
|
|
|
fgRed, ": ", fgGreen , getCurrentExceptionMsg())
|
2022-05-22 11:49:38 +02:00
|
|
|
styledEcho fgBlue, "Source line: " , fgDefault, line
|
|
|
|
styledEcho fgCyan, " ".repeat(len("Source line: ")) & "^".repeat(relPos.stop - relPos.start)
|
2022-04-12 12:18:25 +02:00
|
|
|
except CompileError:
|
2022-05-22 13:02:48 +02:00
|
|
|
let exc = CompileError(getCurrentException())
|
|
|
|
let lexeme = exc.node.token.lexeme
|
2022-08-17 17:31:15 +02:00
|
|
|
var lineNo = exc.node.token.line
|
|
|
|
if exc.node.token.kind == EndOfFile:
|
|
|
|
lineNo -= 1
|
2022-08-15 22:15:06 +02:00
|
|
|
let relPos = exc.compiler.getRelPos(lineNo)
|
|
|
|
let line = exc.compiler.getSource().splitLines()[lineNo - 1].strip(chars={'\n'})
|
|
|
|
var fn = exc.compiler.getCurrentFunction()
|
2022-05-22 11:49:38 +02:00
|
|
|
var fnMsg = ""
|
|
|
|
if fn != nil and fn.kind == funDecl:
|
|
|
|
fnMsg &= &"in function '{FunDecl(fn).name.token.lexeme}'"
|
2022-05-22 13:02:48 +02:00
|
|
|
stderr.styledWriteLine(fgRed, "A fatal error occurred while compiling ", fgYellow, &"'{exc.file}'", fgRed, ", module ",
|
2022-08-15 22:15:06 +02:00
|
|
|
fgYellow, &"'{exc.module}'", fgRed, ", line ", fgYellow, $lineNo, fgRed, " at ", fgYellow, &"'{lexeme}'",
|
|
|
|
fgRed, ": ", fgGreen , getCurrentExceptionMsg())
|
2022-05-22 11:49:38 +02:00
|
|
|
styledEcho fgBlue, "Source line: " , fgDefault, line
|
|
|
|
styledEcho fgCyan, " ".repeat(len("Source line: ")) & "^".repeat(relPos.stop - relPos.start)
|
2022-04-26 16:22:23 +02:00
|
|
|
except SerializationError:
|
2022-05-22 13:02:48 +02:00
|
|
|
let exc = SerializationError(getCurrentException())
|
|
|
|
stderr.styledWriteLine(fgRed, "A fatal error occurred while (de-)serializing", fgYellow, &"'{exc.file}'", fgGreen, ": ", getCurrentExceptionMsg())
|
2022-04-05 11:23:59 +02:00
|
|
|
quit(0)
|
|
|
|
|
2022-04-05 00:26:01 +02:00
|
|
|
|
2022-06-02 01:33:56 +02:00
|
|
|
proc runFile(f: string, interactive: bool = false, fromString: bool = false) =
|
2022-05-22 13:02:48 +02:00
|
|
|
var
|
2022-05-22 17:23:52 +02:00
|
|
|
tokens: seq[Token] = @[]
|
|
|
|
tree: seq[Declaration] = @[]
|
|
|
|
compiled: Chunk
|
|
|
|
serialized: Serialized
|
2022-05-22 13:02:48 +02:00
|
|
|
tokenizer = newLexer()
|
|
|
|
parser = newParser()
|
2022-08-01 10:44:38 +02:00
|
|
|
compiler = newCompiler()
|
2022-07-16 13:21:40 +02:00
|
|
|
debugger {.used.} = newDebugger()
|
2022-05-22 17:23:52 +02:00
|
|
|
serializer = newSerializer()
|
2022-05-22 13:02:48 +02:00
|
|
|
vm = newPeonVM()
|
2022-05-22 17:23:52 +02:00
|
|
|
input: string
|
2022-05-22 13:02:48 +02:00
|
|
|
tokenizer.fillSymbolTable()
|
|
|
|
try:
|
2022-06-02 01:33:56 +02:00
|
|
|
var f = f
|
|
|
|
if not fromString:
|
|
|
|
if not f.endsWith(".pn"):
|
|
|
|
f &= ".pn"
|
|
|
|
input = readFile(f)
|
|
|
|
else:
|
|
|
|
input = f
|
2022-08-16 12:20:07 +02:00
|
|
|
f = "<string>"
|
2022-05-22 17:23:52 +02:00
|
|
|
tokens = tokenizer.lex(input, f)
|
|
|
|
if tokens.len() == 0:
|
|
|
|
return
|
|
|
|
when debugLexer:
|
|
|
|
styledEcho fgCyan, "Tokenization step:"
|
|
|
|
for i, token in tokens:
|
|
|
|
if i == tokens.high():
|
|
|
|
# Who cares about EOF?
|
|
|
|
break
|
|
|
|
styledEcho fgGreen, "\t", $token
|
|
|
|
echo ""
|
2022-08-15 11:46:24 +02:00
|
|
|
tree = parser.parse(tokens, f, tokenizer.getLines(), input)
|
2022-05-22 17:23:52 +02:00
|
|
|
if tree.len() == 0:
|
|
|
|
return
|
|
|
|
when debugParser:
|
|
|
|
styledEcho fgCyan, "Parsing step:"
|
|
|
|
for node in tree:
|
|
|
|
styledEcho fgGreen, "\t", $node
|
|
|
|
echo ""
|
2022-08-15 11:46:24 +02:00
|
|
|
compiled = compiler.compile(tree, f, tokenizer.getLines(), input)
|
2022-05-22 17:23:52 +02:00
|
|
|
when debugCompiler:
|
2022-05-26 18:31:40 +02:00
|
|
|
styledEcho fgCyan, "Compilation step:\n"
|
2022-05-24 22:26:45 +02:00
|
|
|
debugger.disassembleChunk(compiled, f)
|
2022-05-22 17:23:52 +02:00
|
|
|
echo ""
|
2022-07-09 12:47:53 +02:00
|
|
|
var path = splitFile(f).dir
|
|
|
|
if path.len() > 0:
|
|
|
|
path &= "/"
|
|
|
|
path &= splitFile(f).name & ".pbc"
|
|
|
|
serializer.dumpFile(compiled, f, path)
|
|
|
|
serialized = serializer.loadFile(path)
|
2022-08-16 12:20:07 +02:00
|
|
|
if fromString:
|
|
|
|
discard tryRemoveFile("<string>.pbc")
|
2022-05-22 17:23:52 +02:00
|
|
|
when debugSerializer:
|
|
|
|
styledEcho fgCyan, "Serialization step: "
|
2022-05-24 22:26:45 +02:00
|
|
|
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
|
2022-05-22 17:23:52 +02:00
|
|
|
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"
|
2022-05-25 14:38:34 +02:00
|
|
|
stdout.styledWrite(fgBlue, "\t- CFI segment: ")
|
|
|
|
if serialized.chunk.cfi == compiled.cfi:
|
|
|
|
styledEcho fgGreen, "OK"
|
|
|
|
else:
|
|
|
|
styledEcho fgRed, "Corrupted"
|
2022-05-22 17:23:52 +02:00
|
|
|
vm.run(serialized.chunk)
|
2022-05-22 13:02:48 +02:00
|
|
|
except LexingError:
|
2022-08-17 17:31:15 +02:00
|
|
|
var exc = LexingError(getCurrentException())
|
|
|
|
if exc.lexeme == "":
|
|
|
|
exc.line -= 1
|
2022-08-15 11:46:24 +02:00
|
|
|
let relPos = exc.lexer.getRelPos(exc.line)
|
|
|
|
let line = exc.lexer.getSource().splitLines()[exc.line - 1].strip(chars={'\n'})
|
2022-05-22 13:02:48 +02:00
|
|
|
stderr.styledWriteLine(fgRed, "A fatal error occurred while parsing ", fgYellow, &"'{exc.file}'", fgRed, ", module ",
|
2022-07-31 16:09:22 +02:00
|
|
|
fgYellow, &"'{exc.file.extractFilename()}'", fgRed, ", line ", fgYellow, $exc.line, fgRed, " at ", fgYellow, &"'{exc.lexeme.escape()}'",
|
2022-05-22 13:02:48 +02:00
|
|
|
fgRed, ": ", fgGreen , getCurrentExceptionMsg())
|
|
|
|
styledEcho fgBlue, "Source line: " , fgDefault, line
|
|
|
|
styledEcho fgCyan, " ".repeat(len("Source line: ")) & "^".repeat(relPos.stop - relPos.start)
|
|
|
|
except ParseError:
|
|
|
|
let exc = ParseError(getCurrentException())
|
2022-07-31 16:09:22 +02:00
|
|
|
let lexeme = exc.token.lexeme
|
2022-08-17 17:31:15 +02:00
|
|
|
var lineNo = exc.token.line
|
|
|
|
if exc.token.kind == EndOfFile:
|
|
|
|
lineNo -= 1
|
2022-08-15 11:46:24 +02:00
|
|
|
let relPos = exc.parser.getRelPos(lineNo)
|
2022-05-22 13:02:48 +02:00
|
|
|
let fn = parser.getCurrentFunction()
|
2022-08-15 11:46:24 +02:00
|
|
|
let line = exc.parser.getSource().splitLines()[lineNo - 1].strip(chars={'\n'})
|
2022-05-22 13:02:48 +02:00
|
|
|
var fnMsg = ""
|
|
|
|
if fn != nil and fn.kind == funDecl:
|
|
|
|
fnMsg &= &"in function '{FunDecl(fn).name.token.lexeme}'"
|
|
|
|
stderr.styledWriteLine(fgRed, "A fatal error occurred while parsing ", fgYellow, &"'{exc.file}'", fgRed, ", module ",
|
2022-08-15 11:46:24 +02:00
|
|
|
fgYellow, &"'{exc.module}'", fgRed, ", line ", fgYellow, $lineNo, fgRed, " at ", fgYellow, &"'{lexeme}'",
|
2022-05-22 13:02:48 +02:00
|
|
|
fgRed, ": ", fgGreen , getCurrentExceptionMsg())
|
|
|
|
styledEcho fgBlue, "Source line: " , fgDefault, line
|
|
|
|
styledEcho fgCyan, " ".repeat(len("Source line: ")) & "^".repeat(relPos.stop - relPos.start)
|
|
|
|
except CompileError:
|
|
|
|
let exc = CompileError(getCurrentException())
|
2022-07-31 16:09:22 +02:00
|
|
|
let lexeme = exc.node.token.lexeme
|
2022-08-17 17:31:15 +02:00
|
|
|
var lineNo = exc.node.token.line
|
|
|
|
if exc.node.token.kind == EndOfFile:
|
|
|
|
lineNo -= 1
|
2022-08-15 11:46:24 +02:00
|
|
|
let relPos = exc.compiler.getRelPos(lineNo)
|
|
|
|
let line = exc.compiler.getSource().splitLines()[lineNo - 1].strip(chars={'\n'})
|
|
|
|
var fn = exc.compiler.getCurrentFunction()
|
2022-05-22 13:02:48 +02:00
|
|
|
var fnMsg = ""
|
|
|
|
if fn != nil and fn.kind == funDecl:
|
|
|
|
fnMsg &= &"in function '{FunDecl(fn).name.token.lexeme}'"
|
|
|
|
stderr.styledWriteLine(fgRed, "A fatal error occurred while compiling ", fgYellow, &"'{exc.file}'", fgRed, ", module ",
|
|
|
|
fgYellow, &"'{exc.module}'", fgRed, ", line ", fgYellow, $lineNo, fgRed, " at ", fgYellow, &"'{lexeme}'",
|
|
|
|
fgRed, ": ", fgGreen , getCurrentExceptionMsg())
|
|
|
|
styledEcho fgBlue, "Source line: " , fgDefault, line
|
|
|
|
styledEcho fgCyan, " ".repeat(len("Source line: ")) & "^".repeat(relPos.stop - relPos.start)
|
|
|
|
except SerializationError:
|
|
|
|
let exc = SerializationError(getCurrentException())
|
|
|
|
stderr.styledWriteLine(fgRed, "A fatal error occurred while (de-)serializing", fgYellow, &"'{exc.file}'", fgGreen, ": ", getCurrentExceptionMsg())
|
|
|
|
except IOError:
|
2022-05-22 17:23:52 +02:00
|
|
|
stderr.styledWriteLine(fgRed, "An error occurred while trying to read ", fgYellow, &"'{f}'", fgGreen, &": {getCurrentExceptionMsg()}")
|
2022-05-22 13:02:48 +02:00
|
|
|
except OSError:
|
2022-05-22 17:23:52 +02:00
|
|
|
stderr.styledWriteLine(fgRed, "An error occurred while trying to read ", fgYellow, &"'{f}'", fgGreen, &": {osErrorMsg(osLastError())} [errno {osLastError()}]")
|
2022-08-17 17:31:15 +02:00
|
|
|
|
2022-05-22 13:02:48 +02:00
|
|
|
|
|
|
|
|
|
|
|
when isMainModule:
|
|
|
|
setControlCHook(proc () {.noconv.} = quit(0))
|
2022-05-25 11:49:21 +02:00
|
|
|
var optParser = initOptParser(commandLineParams())
|
|
|
|
var file: string = ""
|
|
|
|
var fromString: bool = false
|
|
|
|
var interactive: bool = false
|
|
|
|
for kind, key, value in optParser.getopt():
|
|
|
|
case kind:
|
|
|
|
of cmdArgument:
|
|
|
|
file = key
|
|
|
|
of cmdLongOption:
|
|
|
|
case key:
|
|
|
|
of "help":
|
|
|
|
echo HELP_MESSAGE
|
|
|
|
quit()
|
|
|
|
of "version":
|
|
|
|
echo PEON_VERSION_STRING
|
|
|
|
quit()
|
|
|
|
of "string":
|
|
|
|
file = key
|
|
|
|
fromString = true
|
|
|
|
of "interactive":
|
|
|
|
interactive = true
|
|
|
|
else:
|
|
|
|
echo &"error: unkown option '{key}'"
|
|
|
|
quit()
|
|
|
|
of cmdShortOption:
|
|
|
|
case key:
|
|
|
|
of "h":
|
|
|
|
echo HELP_MESSAGE
|
|
|
|
quit()
|
|
|
|
of "v":
|
|
|
|
echo PEON_VERSION_STRING
|
|
|
|
quit()
|
|
|
|
of "s":
|
|
|
|
file = key
|
|
|
|
fromString = true
|
|
|
|
of "i":
|
|
|
|
interactive = true
|
|
|
|
else:
|
|
|
|
echo &"error: unkown option '{key}'"
|
|
|
|
quit()
|
|
|
|
else:
|
|
|
|
echo "usage: peon [options] [filename.pn]"
|
|
|
|
quit()
|
2022-08-16 12:20:07 +02:00
|
|
|
# TODO: Use interactive
|
2022-05-25 11:49:21 +02:00
|
|
|
if file == "":
|
2022-05-22 13:02:48 +02:00
|
|
|
repl()
|
|
|
|
else:
|
2022-06-02 01:33:56 +02:00
|
|
|
runFile(file, interactive, fromString)
|
2022-05-22 13:02:48 +02:00
|
|
|
|
2022-04-05 00:26:01 +02:00
|
|
|
|
|
|
|
|
2022-04-07 11:51:36 +02:00
|
|
|
proc getLineEditor: LineEditor =
|
|
|
|
result = newLineEditor()
|
|
|
|
result.prompt = "=> "
|
2022-04-11 14:41:20 +02:00
|
|
|
result.populateDefaults()
|
|
|
|
let history = result.plugHistory()
|
2022-05-18 13:32:32 +02:00
|
|
|
result.bindHistory(history)
|