Initial warning support
This commit is contained in:
parent
61aee1c0c4
commit
a3b4fd1048
|
@ -29,6 +29,7 @@ import std/strutils
|
||||||
import std/sequtils
|
import std/sequtils
|
||||||
import std/sets
|
import std/sets
|
||||||
import std/os
|
import std/os
|
||||||
|
import std/terminal
|
||||||
|
|
||||||
|
|
||||||
export ast
|
export ast
|
||||||
|
@ -89,6 +90,9 @@ export bytecode
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
|
WarningKind* {.pure.} = enum
|
||||||
|
## A warning enumeration type
|
||||||
|
UnreachableCode, UnusedName,
|
||||||
NameKind {.pure.} = enum
|
NameKind {.pure.} = enum
|
||||||
## A name enumeration type
|
## A name enumeration type
|
||||||
None, Module, Argument, Var, Function, CustomType, Enum
|
None, Module, Argument, Var, Function, CustomType, Enum
|
||||||
|
@ -216,6 +220,8 @@ type
|
||||||
# declarations so that we can patch them
|
# declarations so that we can patch them
|
||||||
# later
|
# later
|
||||||
forwarded: seq[tuple[name: Name, pos: int]]
|
forwarded: seq[tuple[name: Name, pos: int]]
|
||||||
|
# List of disabled warnings
|
||||||
|
disabledWarnings: seq[WarningKind]
|
||||||
CompileError* = ref object of PeonException
|
CompileError* = ref object of PeonException
|
||||||
compiler*: Compiler
|
compiler*: Compiler
|
||||||
node*: ASTNode
|
node*: ASTNode
|
||||||
|
@ -225,7 +231,7 @@ type
|
||||||
|
|
||||||
# Forward declarations
|
# Forward declarations
|
||||||
proc compile*(self: Compiler, ast: seq[Declaration], file: string, lines: seq[tuple[start, stop: int]], source: string, chunk: Chunk = nil,
|
proc compile*(self: Compiler, ast: seq[Declaration], file: string, lines: seq[tuple[start, stop: int]], source: string, chunk: Chunk = nil,
|
||||||
incremental: bool = false, isMainModule: bool = true): Chunk
|
incremental: bool = false, isMainModule: bool = true, disabledWarnings: seq[WarningKind] = @[]): Chunk
|
||||||
proc expression(self: Compiler, node: Expression)
|
proc expression(self: Compiler, node: Expression)
|
||||||
proc statement(self: Compiler, node: Statement)
|
proc statement(self: Compiler, node: Statement)
|
||||||
proc declaration(self: Compiler, node: Declaration)
|
proc declaration(self: Compiler, node: Declaration)
|
||||||
|
@ -237,7 +243,7 @@ proc matchImpl(self: Compiler, name: string, kind: Type, node: ASTNode = nil, al
|
||||||
proc infer(self: Compiler, node: LiteralExpr, allowGeneric: bool = false): Type
|
proc infer(self: Compiler, node: LiteralExpr, allowGeneric: bool = false): Type
|
||||||
proc infer(self: Compiler, node: Expression, allowGeneric: bool = false): Type
|
proc infer(self: Compiler, node: Expression, allowGeneric: bool = false): Type
|
||||||
proc inferOrError[T: LiteralExpr | Expression](self: Compiler, node: T, allowGeneric: bool = false): Type
|
proc inferOrError[T: LiteralExpr | Expression](self: Compiler, node: T, allowGeneric: bool = false): Type
|
||||||
proc findByName(self: Compiler, name: string): seq[Name]
|
proc findByName(self: Compiler, name: string, resolve: bool = true): seq[Name]
|
||||||
proc findByModule(self: Compiler, name: string): seq[Name]
|
proc findByModule(self: Compiler, name: string): seq[Name]
|
||||||
proc findByType(self: Compiler, name: string, kind: Type, depth: int = -1): seq[Name]
|
proc findByType(self: Compiler, name: string, kind: Type, depth: int = -1): seq[Name]
|
||||||
proc compare(self: Compiler, a, b: Type): bool
|
proc compare(self: Compiler, a, b: Type): bool
|
||||||
|
@ -276,6 +282,7 @@ proc newCompiler*(replMode: bool = false): Compiler =
|
||||||
result.isMainModule = false
|
result.isMainModule = false
|
||||||
result.closures = @[]
|
result.closures = @[]
|
||||||
result.forwarded = @[]
|
result.forwarded = @[]
|
||||||
|
result.disabledWarnings = @[]
|
||||||
|
|
||||||
|
|
||||||
## Public getters for nicer error formatting
|
## Public getters for nicer error formatting
|
||||||
|
@ -317,6 +324,21 @@ proc error(self: Compiler, message: string, node: ASTNode = nil) {.raises: [Comp
|
||||||
raise CompileError(msg: message, node: if node.isNil(): self.getCurrentNode() else: node, file: self.file, module: self.currentModule, compiler: self)
|
raise CompileError(msg: message, node: if node.isNil(): self.getCurrentNode() else: node, file: self.file, module: self.currentModule, compiler: self)
|
||||||
|
|
||||||
|
|
||||||
|
proc warning(self: Compiler, kind: WarningKind, message: string, node: ASTNode = nil) =
|
||||||
|
## Raises a warning
|
||||||
|
let node = if node.isNil(): self.getCurrentNode() else: node
|
||||||
|
let fn = self.getCurrentFunction()
|
||||||
|
var msg = &" (raised at line {node.token.line} in module '{self.currentModule}'"
|
||||||
|
if not fn.isNil() and fn.kind != lambdaExpr:
|
||||||
|
msg &= &", in function '{FunDecl(fn).name.token.lexeme}'"
|
||||||
|
msg &= ")"
|
||||||
|
if kind notin self.disabledWarnings:
|
||||||
|
stderr.styledWrite(fgYellow, &"Warning: ", fgCyan, "line ", fgRed, $node.token.line, fgCyan, ", module ", fgRed, &"'{self.currentModule}'")
|
||||||
|
if not fn.isNil() and fn.kind != lambdaExpr:
|
||||||
|
stderr.styledWrite(fgCyan, ", function ", fgRed, &"'{FunDecl(fn).name.token.lexeme}'")
|
||||||
|
stderr.styledWriteLine(fgCyan, " -> ", fgYellow, message)
|
||||||
|
|
||||||
|
|
||||||
proc step(self: Compiler): ASTNode {.inline.} =
|
proc step(self: Compiler): ASTNode {.inline.} =
|
||||||
## Steps to the next node and returns
|
## Steps to the next node and returns
|
||||||
## the consumed one
|
## the consumed one
|
||||||
|
@ -942,7 +964,7 @@ proc typeToStr(self: Compiler, typ: Type): string =
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
|
||||||
proc findByName(self: Compiler, name: string): seq[Name] =
|
proc findByName(self: Compiler, name: string, resolve: bool = true): seq[Name] =
|
||||||
## Looks for objects that have been already declared
|
## Looks for objects that have been already declared
|
||||||
## with the given name. Returns all objects that apply.
|
## with the given name. Returns all objects that apply.
|
||||||
## As with resolve(), this will cause type and function
|
## As with resolve(), this will cause type and function
|
||||||
|
@ -953,23 +975,25 @@ proc findByName(self: Compiler, name: string): seq[Name] =
|
||||||
if obj.isPrivate or not obj.exported:
|
if obj.isPrivate or not obj.exported:
|
||||||
continue
|
continue
|
||||||
result.add(obj)
|
result.add(obj)
|
||||||
for n in result:
|
if resolve:
|
||||||
if n.resolved:
|
for n in result:
|
||||||
continue
|
if n.resolved:
|
||||||
n.resolved = true
|
continue
|
||||||
case n.kind:
|
n.resolved = true
|
||||||
of NameKind.CustomType:
|
case n.kind:
|
||||||
self.typeDecl(TypeDecl(n.node), n)
|
of NameKind.CustomType:
|
||||||
of NameKind.Function:
|
self.typeDecl(TypeDecl(n.node), n)
|
||||||
if not n.valueType.isGeneric:
|
of NameKind.Function:
|
||||||
self.funDecl(FunDecl(n.node), n)
|
if not n.valueType.isGeneric:
|
||||||
else:
|
self.funDecl(FunDecl(n.node), n)
|
||||||
discard
|
else:
|
||||||
|
discard
|
||||||
|
|
||||||
|
|
||||||
proc findByModule(self: Compiler, name: string): seq[Name] =
|
proc findByModule(self: Compiler, name: string): seq[Name] =
|
||||||
## Looks for objects that have been already declared AS
|
## Looks for objects that have been already declared as
|
||||||
## public within the given module. Returns all objects that apply
|
## public within the given module. Returns all objects
|
||||||
|
## that apply
|
||||||
for obj in reversed(self.names):
|
for obj in reversed(self.names):
|
||||||
if not obj.isPrivate and obj.owner == name:
|
if not obj.isPrivate and obj.owner == name:
|
||||||
result.add(obj)
|
result.add(obj)
|
||||||
|
@ -1188,9 +1212,9 @@ proc endScope(self: Compiler) =
|
||||||
for name in self.names:
|
for name in self.names:
|
||||||
if name.depth > self.depth:
|
if name.depth > self.depth:
|
||||||
names.add(name)
|
names.add(name)
|
||||||
#[if not name.resolved:
|
if not name.resolved and not self.replMode:
|
||||||
# TODO: Emit a warning?
|
self.warning(UnusedName, &"'{name.ident.token.lexeme}' is declared but not used")
|
||||||
continue]#
|
#continue
|
||||||
if name.owner != self.currentModule and self.depth > -1:
|
if name.owner != self.currentModule and self.depth > -1:
|
||||||
# Names coming from other modules only go out of scope
|
# Names coming from other modules only go out of scope
|
||||||
# when the global scope is closed (i.e. at the end of
|
# when the global scope is closed (i.e. at the end of
|
||||||
|
@ -1384,7 +1408,7 @@ proc declareName(self: Compiler, node: ASTNode, mutable: bool = false) =
|
||||||
else:
|
else:
|
||||||
discard # TODO: Types, enums
|
discard # TODO: Types, enums
|
||||||
self.dispatchPragmas(n)
|
self.dispatchPragmas(n)
|
||||||
for name in self.findByName(declaredName):
|
for name in self.findByName(declaredName, resolve=false):
|
||||||
if name == n:
|
if name == n:
|
||||||
continue
|
continue
|
||||||
elif (name.kind == NameKind.Var and name.depth == self.depth) or name.kind in [NameKind.Module, NameKind.CustomType, NameKind.Enum]:
|
elif (name.kind == NameKind.Var and name.depth == self.depth) or name.kind in [NameKind.Module, NameKind.CustomType, NameKind.Enum]:
|
||||||
|
@ -1509,6 +1533,7 @@ proc beginProgram(self: Compiler): int =
|
||||||
codePos: self.chunk.code.len() + 12,
|
codePos: self.chunk.code.len() + 12,
|
||||||
ident: newIdentExpr(Token(lexeme: "", kind: Identifier)),
|
ident: newIdentExpr(Token(lexeme: "", kind: Identifier)),
|
||||||
kind: NameKind.Function,
|
kind: NameKind.Function,
|
||||||
|
resolved: true,
|
||||||
line: -1)
|
line: -1)
|
||||||
self.names.add(main)
|
self.names.add(main)
|
||||||
self.scopeOwners.add((main, 0))
|
self.scopeOwners.add((main, 0))
|
||||||
|
@ -2321,7 +2346,7 @@ proc declaration(self: Compiler, node: Declaration) =
|
||||||
|
|
||||||
|
|
||||||
proc compile*(self: Compiler, ast: seq[Declaration], file: string, lines: seq[tuple[start, stop: int]], source: string, chunk: Chunk = nil,
|
proc compile*(self: Compiler, ast: seq[Declaration], file: string, lines: seq[tuple[start, stop: int]], source: string, chunk: Chunk = nil,
|
||||||
incremental: bool = false, isMainModule: bool = true): Chunk =
|
incremental: bool = false, isMainModule: bool = true, disabledWarnings: seq[WarningKind] = @[]): Chunk =
|
||||||
## Compiles a sequence of AST nodes into a chunk
|
## Compiles a sequence of AST nodes into a chunk
|
||||||
## object
|
## object
|
||||||
if chunk.isNil():
|
if chunk.isNil():
|
||||||
|
@ -2337,6 +2362,7 @@ proc compile*(self: Compiler, ast: seq[Declaration], file: string, lines: seq[tu
|
||||||
self.lines = lines
|
self.lines = lines
|
||||||
self.source = source
|
self.source = source
|
||||||
self.isMainModule = isMainModule
|
self.isMainModule = isMainModule
|
||||||
|
self.disabledWarnings = disabledWarnings
|
||||||
if not incremental:
|
if not incremental:
|
||||||
self.jumps = @[]
|
self.jumps = @[]
|
||||||
let pos = self.beginProgram()
|
let pos = self.beginProgram()
|
||||||
|
@ -2375,7 +2401,7 @@ proc compileModule(self: Compiler, moduleName: string) =
|
||||||
path, self.lexer.getLines(),
|
path, self.lexer.getLines(),
|
||||||
source, persist=true),
|
source, persist=true),
|
||||||
path, self.lexer.getLines(), source, chunk=self.chunk, incremental=true,
|
path, self.lexer.getLines(), source, chunk=self.chunk, incremental=true,
|
||||||
isMainModule=false)
|
isMainModule=false, self.disabledWarnings)
|
||||||
self.depth = 0
|
self.depth = 0
|
||||||
self.current = current
|
self.current = current
|
||||||
self.ast = ast
|
self.ast = ast
|
||||||
|
|
|
@ -1114,7 +1114,7 @@ proc expressionStatement(self: Parser): Statement =
|
||||||
## Parses expression statements, which
|
## Parses expression statements, which
|
||||||
## are expressions followed by a semicolon
|
## are expressions followed by a semicolon
|
||||||
var expression = self.expression()
|
var expression = self.expression()
|
||||||
endOfLine("missing expression terminator", expression.token)
|
endOfLine("missing semicolon at end of expression", expression.token)
|
||||||
result = Statement(newExprStmt(expression, expression.token))
|
result = Statement(newExprStmt(expression, expression.token))
|
||||||
|
|
||||||
|
|
||||||
|
|
33
src/main.nim
33
src/main.nim
|
@ -186,7 +186,8 @@ proc repl =
|
||||||
quit(0)
|
quit(0)
|
||||||
|
|
||||||
|
|
||||||
proc runFile(f: string, fromString: bool = false, dump: bool = true, breakpoints: seq[uint64] = @[], dis: bool = false) =
|
proc runFile(f: string, fromString: bool = false, dump: bool = true, breakpoints: seq[uint64] = @[], dis: bool = false,
|
||||||
|
warnings: seq[WarningKind] = @[]) =
|
||||||
var
|
var
|
||||||
tokens: seq[Token] = @[]
|
tokens: seq[Token] = @[]
|
||||||
tree: seq[Declaration] = @[]
|
tree: seq[Declaration] = @[]
|
||||||
|
@ -231,7 +232,7 @@ proc runFile(f: string, fromString: bool = false, dump: bool = true, breakpoints
|
||||||
for node in tree:
|
for node in tree:
|
||||||
styledEcho fgGreen, "\t", $node
|
styledEcho fgGreen, "\t", $node
|
||||||
echo ""
|
echo ""
|
||||||
compiled = compiler.compile(tree, f, tokenizer.getLines(), input)
|
compiled = compiler.compile(tree, f, tokenizer.getLines(), input, disabledWarnings=warnings)
|
||||||
when debugCompiler:
|
when debugCompiler:
|
||||||
styledEcho fgCyan, "Compilation step:\n"
|
styledEcho fgCyan, "Compilation step:\n"
|
||||||
debugger.disassembleChunk(compiled, f)
|
debugger.disassembleChunk(compiled, f)
|
||||||
|
@ -297,7 +298,6 @@ proc runFile(f: string, fromString: bool = false, dump: bool = true, breakpoints
|
||||||
styledEcho fgBlue, "Source line: " , fgDefault, line
|
styledEcho fgBlue, "Source line: " , fgDefault, line
|
||||||
styledEcho fgCyan, " ".repeat(len("Source line: ") + line.find(lexeme)) & "^".repeat(abs(relPos.stop - relPos.start - line.find(lexeme)))
|
styledEcho fgCyan, " ".repeat(len("Source line: ") + line.find(lexeme)) & "^".repeat(abs(relPos.stop - relPos.start - line.find(lexeme)))
|
||||||
except CompileError:
|
except CompileError:
|
||||||
raise
|
|
||||||
let exc = CompileError(getCurrentException())
|
let exc = CompileError(getCurrentException())
|
||||||
let lexeme = exc.node.token.lexeme
|
let lexeme = exc.node.token.lexeme
|
||||||
var lineNo = exc.node.token.line
|
var lineNo = exc.node.token.line
|
||||||
|
@ -328,6 +328,7 @@ when isMainModule:
|
||||||
var file: string = ""
|
var file: string = ""
|
||||||
var fromString: bool = false
|
var fromString: bool = false
|
||||||
var dump: bool = true
|
var dump: bool = true
|
||||||
|
var warnings: seq[WarningKind] = @[]
|
||||||
var breaks: seq[uint64] = @[]
|
var breaks: seq[uint64] = @[]
|
||||||
var dis: bool = false
|
var dis: bool = false
|
||||||
for kind, key, value in optParser.getopt():
|
for kind, key, value in optParser.getopt():
|
||||||
|
@ -347,6 +348,24 @@ when isMainModule:
|
||||||
fromString = true
|
fromString = true
|
||||||
of "no-dump":
|
of "no-dump":
|
||||||
dump = false
|
dump = false
|
||||||
|
of "warnings":
|
||||||
|
if value.toLowerAscii() in ["yes", "on"]:
|
||||||
|
warnings = @[]
|
||||||
|
elif value.toLowerAscii() in ["no", "off"]:
|
||||||
|
for warning in WarningKind:
|
||||||
|
warnings.add(warning)
|
||||||
|
else:
|
||||||
|
stderr.writeLine("error: invalid value for option 'warnings' (valid options: yes, on, no, off)")
|
||||||
|
quit()
|
||||||
|
of "no-warn":
|
||||||
|
case value:
|
||||||
|
of "unusedVariable":
|
||||||
|
warnings.add(WarningKind.UnusedName)
|
||||||
|
of "unreachableCode":
|
||||||
|
warnings.add(WarningKind.UnreachableCode)
|
||||||
|
else:
|
||||||
|
stderr.writeLine("error: invalid warning name for option 'warnings'")
|
||||||
|
quit()
|
||||||
of "breakpoints":
|
of "breakpoints":
|
||||||
when not debugVM:
|
when not debugVM:
|
||||||
echo "error: cannot set breakpoints in release mode"
|
echo "error: cannot set breakpoints in release mode"
|
||||||
|
@ -377,18 +396,18 @@ when isMainModule:
|
||||||
dump = false
|
dump = false
|
||||||
of "b":
|
of "b":
|
||||||
when not debugVM:
|
when not debugVM:
|
||||||
echo "error: cannot set breakpoints in release mode"
|
stderr.writeLine("error: cannot set breakpoints in release mode")
|
||||||
quit()
|
quit()
|
||||||
for point in value.strip(chars={' '}).split(","):
|
for point in value.strip(chars={' '}).split(","):
|
||||||
try:
|
try:
|
||||||
breaks.add(parseBiggestUInt(point))
|
breaks.add(parseBiggestUInt(point))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
echo &"error: invalid breakpoint value '{point}'"
|
stderr.writeLine(&"error: invalid breakpoint value '{point}'")
|
||||||
quit()
|
quit()
|
||||||
of "d":
|
of "d":
|
||||||
dis = true
|
dis = true
|
||||||
else:
|
else:
|
||||||
echo &"error: unkown option '{key}'"
|
stderr.writeLine(&"error: unkown option '{key}'")
|
||||||
quit()
|
quit()
|
||||||
else:
|
else:
|
||||||
echo "usage: peon [options] [filename.pn]"
|
echo "usage: peon [options] [filename.pn]"
|
||||||
|
@ -396,7 +415,7 @@ when isMainModule:
|
||||||
if file == "":
|
if file == "":
|
||||||
repl()
|
repl()
|
||||||
else:
|
else:
|
||||||
runFile(file, fromString, dump, breaks, dis)
|
runFile(file, fromString, dump, breaks, dis, warnings)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ proc checkFrameStart(self: Debugger, n: int) =
|
||||||
for i, e in self.cfiData:
|
for i, e in self.cfiData:
|
||||||
if n == e.start and not (e.started or e.stopped):
|
if n == e.start and not (e.started or e.stopped):
|
||||||
e.started = true
|
e.started = true
|
||||||
styledEcho fgBlue, "\n==== Peon Bytecode Debugger - Begin Frame ", fgYellow, &"'{e.name}' ", fgBlue, "(", fgYellow, $i, fgBlue, ") ===="
|
styledEcho fgBlue, "\n==== Peon Bytecode Debugger - Function Start ", fgYellow, &"'{e.name}' ", fgBlue, "(", fgYellow, $i, fgBlue, ") ===="
|
||||||
styledEcho fgGreen, "\t- Start offset: ", fgYellow, $e.start
|
styledEcho fgGreen, "\t- Start offset: ", fgYellow, $e.start
|
||||||
styledEcho fgGreen, "\t- End offset: ", fgYellow, $e.stop
|
styledEcho fgGreen, "\t- End offset: ", fgYellow, $e.stop
|
||||||
styledEcho fgGreen, "\t- Argument count: ", fgYellow, $e.argc
|
styledEcho fgGreen, "\t- Argument count: ", fgYellow, $e.argc
|
||||||
|
@ -80,7 +80,7 @@ proc checkFrameEnd(self: Debugger, n: int) =
|
||||||
for i, e in self.cfiData:
|
for i, e in self.cfiData:
|
||||||
if n == e.stop and e.started and not e.stopped:
|
if n == e.stop and e.started and not e.stopped:
|
||||||
e.stopped = true
|
e.stopped = true
|
||||||
styledEcho fgBlue, "\n==== Peon Bytecode Debugger - End Frame ", fgYellow, &"'{e.name}' ", fgBlue, "(", fgYellow, $i, fgBlue, ") ===="
|
styledEcho fgBlue, "\n==== Peon Bytecode Debugger - Function End ", fgYellow, &"'{e.name}' ", fgBlue, "(", fgYellow, $i, fgBlue, ") ===="
|
||||||
|
|
||||||
|
|
||||||
proc simpleInstruction(self: Debugger, instruction: OpCode) =
|
proc simpleInstruction(self: Debugger, instruction: OpCode) =
|
||||||
|
|
Loading…
Reference in New Issue