Minor refactoring in preparation for additional modules
This commit is contained in:
parent
f5d091bb9b
commit
525a11adad
|
@ -1,3 +1,11 @@
|
|||
# peon-rewrite
|
||||
|
||||
Work in progress for Peon 0.2.x
|
||||
|
||||
|
||||
## What changed
|
||||
|
||||
- Peon will no longer use a runtime GC. Instead, the memory model will use lifetimes with regions. Ownership/uniqueness (a la Rust),
|
||||
might be added in the future, too
|
||||
- The compiler has been completely overhauled and no longer handles any code generation (in fact, currently there is no code generation
|
||||
at all, just a parser and a type checker). This is to allow for true multi-backend support as well as to improve separation of concerns
|
|
@ -48,8 +48,8 @@ http://www.apache.org/licenses/LICENSE-2.0 for more info.
|
|||
Basic Usage
|
||||
-----------
|
||||
|
||||
$ peon file.pn Run the given Peon source file
|
||||
$ peon file.pbc Run the given Peon bytecode file
|
||||
$ peon [options] file.pn Run the given Peon source file
|
||||
$ peon [options] file.pbc Run the given Peon bytecode file
|
||||
|
||||
|
||||
Options
|
||||
|
@ -58,8 +58,6 @@ Options
|
|||
-h, --help Show this help text and exit
|
||||
-v, --version Print the current peon version and exit
|
||||
-s, --string Execute the passed string as if it was a file
|
||||
-n, --noDump Don't dump the result of compilation to a file.
|
||||
Note that no dump is created when using -s/--string
|
||||
-m, --mode Set the compilation mode. Acceptable values are 'debug' and
|
||||
'release'. Defaults to 'debug'
|
||||
-c, --compile Compile the code, but do not execute it. Useful along with -d
|
||||
|
@ -72,5 +70,5 @@ Options
|
|||
-o, --output Rename the output file to this value
|
||||
--debug-lexer Show the lexer's output
|
||||
--debug-parser Show the parser's output
|
||||
--debug-compiler Show the compiler's output
|
||||
--debug-tc Show the typechecker's output
|
||||
"""
|
||||
|
|
|
@ -109,11 +109,13 @@ type
|
|||
UnreachableCode, UnusedName, ShadowOuterScope,
|
||||
MutateOuterScope
|
||||
|
||||
CompileError* = ref object of PeonException
|
||||
## A compilation error with location information
|
||||
TypeCheckError* = ref object of PeonException
|
||||
## A typechecking error with location information
|
||||
node*: ASTNode
|
||||
function*: Declaration
|
||||
compiler*: PeonCompiler
|
||||
# The instance of the typechecker that
|
||||
# raised the error
|
||||
instance*: TypeChecker
|
||||
|
||||
NameKind* {.pure.} = enum
|
||||
## A name enumeration type
|
||||
|
@ -166,14 +168,14 @@ type
|
|||
# Where is this node declared in its file?
|
||||
line*: int
|
||||
# The AST node associated with this node. This
|
||||
# is needed because we compile function and type
|
||||
# is needed because we tyoecheck function and type
|
||||
# declarations only if, and when, they're actually
|
||||
# used
|
||||
node*: Declaration
|
||||
# Who is this name exported to? (Only makes sense if isPrivate
|
||||
# equals false)
|
||||
exportedTo*: seq[Name]
|
||||
# Is this name generated by user code or internally by the compiler?
|
||||
# Is this name generated by user code or internally by the type checker?
|
||||
isReal: bool
|
||||
|
||||
TypeSignature = seq[tuple[name: string, kind: Type, default: Expression]]
|
||||
|
@ -247,19 +249,19 @@ type
|
|||
body*: TypedBlockStmt
|
||||
condition*: TypedExpr
|
||||
|
||||
CompilerFunc* = object
|
||||
## An internal compiler function called
|
||||
PragmaFunc* = object
|
||||
## An internal function called
|
||||
## by pragmas
|
||||
kind: PragmaKind
|
||||
handler: proc (self: PeonCompiler, pragma: Pragma, name: Name)
|
||||
handler: proc (self: TypeChecker, pragma: Pragma, name: Name)
|
||||
|
||||
PragmaKind* = enum
|
||||
## An enumeration of pragma types
|
||||
Immediate,
|
||||
Delayed
|
||||
|
||||
PeonCompiler* = ref object
|
||||
## The Peon compiler
|
||||
TypeChecker* = ref object
|
||||
## The Peon type checker
|
||||
current: int # The current node we're looking at
|
||||
tree: seq[Declaration] # The AST for the current module
|
||||
scopeDepth*: int # The current scope depth (0 == global, > 0 == local)
|
||||
|
@ -270,12 +272,12 @@ type
|
|||
source: string # The module's raw source code
|
||||
file: string # The module's filename
|
||||
isMainModule: bool # Are we the main module?
|
||||
currentFunction: Name # The current function we're compiling
|
||||
currentModule: Name # The current module we're compiling
|
||||
disabledWarnings: seq[WarningKind] # List of disabled compiler warnings
|
||||
currentFunction: Name # The current function we're checking
|
||||
currentModule: Name # The current module we're checking
|
||||
disabledWarnings: seq[WarningKind] # List of disabled warnings
|
||||
names: seq[TableRef[string, seq[Name]]] # Maps scope depths to namespaces
|
||||
# Compiler procedures called by pragmas
|
||||
compilerProcs: TableRef[string, CompilerFunc]
|
||||
# Internal procedures called by pragmas
|
||||
pragmas: TableRef[string, PragmaFunc]
|
||||
# Show full info about type mismatches when dispatching
|
||||
# function calls fails (we hide this under a boolean flag
|
||||
# because the output is quite verbose)
|
||||
|
@ -283,33 +285,33 @@ type
|
|||
|
||||
|
||||
## Forward declarations
|
||||
proc compile(self: PeonCompiler, node: ASTNode): TypedNode
|
||||
proc validate(self: TypeChecker, node: ASTNode): TypedNode
|
||||
proc toIntrinsic(name: string): Type
|
||||
proc done(self: PeonCompiler): bool {.inline.}
|
||||
proc done(self: TypeChecker): bool {.inline.}
|
||||
proc wrapType(self: Type): Type {.inline.}
|
||||
proc unwrapType(self: Type): Type {.inline.}
|
||||
proc toRef(self: Type): Type {.inline.}
|
||||
proc toConst(self: Type): Type {.inline.}
|
||||
proc toPtr(self: Type): Type {.inline.}
|
||||
proc toLent(self: Type): Type {.inline.}
|
||||
proc handleErrorPragma(self: PeonCompiler, pragma: Pragma, name: Name)
|
||||
proc handlePurePragma(self: PeonCompiler, pragma: Pragma, name: Name)
|
||||
proc handleMagicPragma(self: PeonCompiler, pragma: Pragma, name: Name)
|
||||
proc handleNullablePragma(self: PeonCompiler, pragma: Pragma, name: Name)
|
||||
proc warning(self: PeonCompiler, kind: WarningKind, message: string, name: Name = nil, node: ASTNode = nil)
|
||||
proc stringify*(self: PeonCompiler, typ: Type): string
|
||||
proc stringify*(self: PeonCompiler, typ: TypedNode): string
|
||||
proc inferOrError*(self: PeonCompiler, node: Expression): TypedExpr
|
||||
proc compare(self: PeonCompiler, a, b: Type): bool
|
||||
proc expression(self: PeonCompiler, node: Expression): TypedExpr
|
||||
proc specialize(self: PeonCompiler, name: Name, args: seq[Expression]): Type
|
||||
proc funDecl(self: PeonCompiler, node: FunDecl, name: Name = nil): TypedDecl
|
||||
proc getTypeDistance(self: PeonCompiler, a, b: Type): int
|
||||
proc handleErrorPragma(self: TypeChecker, pragma: Pragma, name: Name)
|
||||
proc handlePurePragma(self: TypeChecker, pragma: Pragma, name: Name)
|
||||
proc handleMagicPragma(self: TypeChecker, pragma: Pragma, name: Name)
|
||||
proc handleNullablePragma(self: TypeChecker, pragma: Pragma, name: Name)
|
||||
proc warning(self: TypeChecker, kind: WarningKind, message: string, name: Name = nil, node: ASTNode = nil)
|
||||
proc stringify*(self: TypeChecker, typ: Type): string
|
||||
proc stringify*(self: TypeChecker, typ: TypedNode): string
|
||||
proc inferOrError*(self: TypeChecker, node: Expression): TypedExpr
|
||||
proc compare(self: TypeChecker, a, b: Type): bool
|
||||
proc expression(self: TypeChecker, node: Expression): TypedExpr
|
||||
proc specialize(self: TypeChecker, name: Name, args: seq[Expression]): Type
|
||||
proc funDecl(self: TypeChecker, node: FunDecl, name: Name = nil): TypedDecl
|
||||
proc getTypeDistance(self: TypeChecker, a, b: Type): int
|
||||
|
||||
## Public getters for nicer error formatting
|
||||
proc getCurrentNode*(self: PeonCompiler): ASTNode = (if self.done(): self.tree[^1] else: self.tree[self.current - 1])
|
||||
proc getCurrentFunction*(self: PeonCompiler): Declaration {.inline.} = (if self.currentFunction.isNil(): nil else: self.currentFunction.node)
|
||||
proc getSource*(self: PeonCompiler): string {.inline.} = self.source
|
||||
proc getCurrentNode*(self: TypeChecker): ASTNode = (if self.done(): self.tree[^1] else: self.tree[self.current - 1])
|
||||
proc getCurrentFunction*(self: TypeChecker): Declaration {.inline.} = (if self.currentFunction.isNil(): nil else: self.currentFunction.node)
|
||||
proc getSource*(self: TypeChecker): string {.inline.} = self.source
|
||||
|
||||
|
||||
proc newTypedNode(node: ASTNode): TypedNode =
|
||||
|
@ -398,7 +400,7 @@ proc getName(self: TypedNode): Name =
|
|||
result = nil # TODO
|
||||
|
||||
|
||||
proc newPeonCompiler*: PeonCompiler =
|
||||
proc newTypeChecker*: TypeChecker =
|
||||
## Initializes a new compiler instance
|
||||
new(result)
|
||||
result.current = 0
|
||||
|
@ -412,20 +414,20 @@ proc newPeonCompiler*: PeonCompiler =
|
|||
result.currentFunction = nil
|
||||
result.disabledWarnings = @[]
|
||||
result.names = @[]
|
||||
result.compilerProcs = newTable[string, CompilerFunc]()
|
||||
result.compilerProcs["magic"] = CompilerFunc(kind: Immediate, handler: handleMagicPragma)
|
||||
result.compilerProcs["pure"] = CompilerFunc(kind: Immediate, handler: handlePurePragma)
|
||||
result.compilerProcs["error"] = CompilerFunc(kind: Delayed, handler: handleErrorPragma)
|
||||
result.compilerProcs["nullable"] = CompilerFunc(kind: Immediate, handler: handleNullablePragma)
|
||||
result.pragmas = newTable[string, PragmaFunc]()
|
||||
result.pragmas["magic"] = PragmaFunc(kind: Immediate, handler: handleMagicPragma)
|
||||
result.pragmas["pure"] = PragmaFunc(kind: Immediate, handler: handlePurePragma)
|
||||
result.pragmas["error"] = PragmaFunc(kind: Delayed, handler: handleErrorPragma)
|
||||
result.pragmas["nullable"] = PragmaFunc(kind: Immediate, handler: handleNullablePragma)
|
||||
result.showMismatches = false
|
||||
|
||||
|
||||
proc done(self: PeonCompiler): bool {.inline.} = self.current == self.tree.len()
|
||||
proc done(self: TypeChecker): bool {.inline.} = self.current == self.tree.len()
|
||||
proc `$`(self: Name): string = $(self[])
|
||||
proc `$`(self: Type): string = $(self[])
|
||||
|
||||
|
||||
proc peek(self: PeonCompiler): ASTNode {.inline.} =
|
||||
proc peek(self: TypeChecker): ASTNode {.inline.} =
|
||||
if self.tree.len() == 0:
|
||||
return nil
|
||||
if self.done():
|
||||
|
@ -433,7 +435,7 @@ proc peek(self: PeonCompiler): ASTNode {.inline.} =
|
|||
return self.tree[self.current]
|
||||
|
||||
|
||||
proc step(self: PeonCompiler): ASTNode {.inline.} =
|
||||
proc step(self: TypeChecker): ASTNode {.inline.} =
|
||||
if self.tree.len() == 0:
|
||||
return nil
|
||||
if self.done():
|
||||
|
@ -442,13 +444,13 @@ proc step(self: PeonCompiler): ASTNode {.inline.} =
|
|||
inc(self.current)
|
||||
|
||||
|
||||
proc error(self: PeonCompiler, message: string, node: ASTNode = nil) {.inline.} =
|
||||
## Raises a CompileError exception
|
||||
proc error(self: TypeChecker, message: string, node: ASTNode = nil) {.inline.} =
|
||||
## Raises a TypeCheckError exception
|
||||
let node = if node.isNil(): self.getCurrentNode() else: node
|
||||
raise CompileError(msg: message, node: node, line: node.token.line, file: node.file, compiler: self)
|
||||
raise TypeCheckError(msg: message, node: node, line: node.token.line, file: node.file, instance: self)
|
||||
|
||||
|
||||
proc handleMagicPragma(self: PeonCompiler, pragma: Pragma, name: Name) =
|
||||
proc handleMagicPragma(self: TypeChecker, pragma: Pragma, name: Name) =
|
||||
## Handles the "magic" pragma. Assumes the given name is already
|
||||
## declared
|
||||
if pragma.args.len() != 1:
|
||||
|
@ -470,7 +472,7 @@ proc handleMagicPragma(self: PeonCompiler, pragma: Pragma, name: Name) =
|
|||
self.error("'magic' pragma is not valid in this context")
|
||||
|
||||
|
||||
proc handleNullablePragma(self: PeonCompiler, pragma: Pragma, name: Name) =
|
||||
proc handleNullablePragma(self: TypeChecker, pragma: Pragma, name: Name) =
|
||||
## Handles the "nullable" pragma. Assumes the given name is already
|
||||
## declared
|
||||
if pragma.args.len() > 0:
|
||||
|
@ -483,7 +485,7 @@ proc handleNullablePragma(self: PeonCompiler, pragma: Pragma, name: Name) =
|
|||
self.error("'nullable' pragma is not valid in this context")
|
||||
|
||||
|
||||
proc handleErrorPragma(self: PeonCompiler, pragma: Pragma, name: Name) =
|
||||
proc handleErrorPragma(self: TypeChecker, pragma: Pragma, name: Name) =
|
||||
## Handles the "error" pragma
|
||||
if pragma.args.len() != 1:
|
||||
self.error("'error' pragma: wrong number of arguments")
|
||||
|
@ -494,7 +496,7 @@ proc handleErrorPragma(self: PeonCompiler, pragma: Pragma, name: Name) =
|
|||
self.error(pragma.args[0].token.lexeme[1..^2])
|
||||
|
||||
|
||||
proc handlePurePragma(self: PeonCompiler, pragma: Pragma, name: Name) =
|
||||
proc handlePurePragma(self: TypeChecker, pragma: Pragma, name: Name) =
|
||||
## Handles the "pure" pragma
|
||||
case name.node.kind:
|
||||
of NodeKind.funDecl:
|
||||
|
@ -505,7 +507,7 @@ proc handlePurePragma(self: PeonCompiler, pragma: Pragma, name: Name) =
|
|||
self.error("'pure' pragma is not valid in this context")
|
||||
|
||||
|
||||
proc warning(self: PeonCompiler, kind: WarningKind, message: string, name: Name = nil, node: ASTNode = nil) =
|
||||
proc warning(self: TypeChecker, kind: WarningKind, message: string, name: Name = nil, node: ASTNode = nil) =
|
||||
## Raises a warning
|
||||
if kind in self.disabledWarnings:
|
||||
return
|
||||
|
@ -640,7 +642,7 @@ proc toIntrinsic(name: string): Type =
|
|||
|
||||
|
||||
|
||||
proc infer(self: PeonCompiler, node: LiteralExpr): TypedExpr =
|
||||
proc infer(self: TypeChecker, node: LiteralExpr): TypedExpr =
|
||||
case node.kind:
|
||||
of trueExpr, falseExpr:
|
||||
return newTypedExpr(node, "bool".toIntrinsic())
|
||||
|
@ -664,13 +666,13 @@ proc infer(self: PeonCompiler, node: LiteralExpr): TypedExpr =
|
|||
discard
|
||||
|
||||
|
||||
proc infer(self: PeonCompiler, node: Expression): TypedExpr =
|
||||
proc infer(self: TypeChecker, node: Expression): TypedExpr =
|
||||
if node.isConst():
|
||||
return self.infer(LiteralExpr(node))
|
||||
result = self.expression(node)
|
||||
|
||||
|
||||
proc compareUnions*(self: PeonCompiler, a, b: seq[tuple[match: bool, kind: Type, value: Expression]]): bool =
|
||||
proc compareUnions*(self: TypeChecker, a, b: seq[tuple[match: bool, kind: Type, value: Expression]]): bool =
|
||||
## Compares type unions between each other
|
||||
var
|
||||
long = a
|
||||
|
@ -687,7 +689,7 @@ proc compareUnions*(self: PeonCompiler, a, b: seq[tuple[match: bool, kind: Type,
|
|||
return i >= short.len()
|
||||
|
||||
|
||||
proc matchUnion(self: PeonCompiler, a: Type, b: seq[tuple[match: bool, kind: Type, value: Expression]]): bool =
|
||||
proc matchUnion(self: TypeChecker, a: Type, b: seq[tuple[match: bool, kind: Type, value: Expression]]): bool =
|
||||
## Returns whether a non-union type a matches
|
||||
## the given untagged union b
|
||||
assert a.kind != Union
|
||||
|
@ -697,7 +699,7 @@ proc matchUnion(self: PeonCompiler, a: Type, b: seq[tuple[match: bool, kind: Typ
|
|||
return false
|
||||
|
||||
|
||||
proc matchGeneric(self: PeonCompiler, a: Type, b: seq[tuple[match: bool, kind: Type, value: Expression]]): bool =
|
||||
proc matchGeneric(self: TypeChecker, a: Type, b: seq[tuple[match: bool, kind: Type, value: Expression]]): bool =
|
||||
## Returns whether a concrete type matches the
|
||||
## given generic type b
|
||||
assert a.kind != Generic
|
||||
|
@ -727,7 +729,7 @@ proc isAny(typ: Type): bool =
|
|||
return false
|
||||
|
||||
|
||||
proc compare(self: PeonCompiler, a, b: Type): bool =
|
||||
proc compare(self: TypeChecker, a, b: Type): bool =
|
||||
if a.isAny() or b.isAny():
|
||||
return true
|
||||
if a.kind == b.kind:
|
||||
|
@ -832,7 +834,7 @@ proc compare(self: PeonCompiler, a, b: Type): bool =
|
|||
return false
|
||||
|
||||
|
||||
proc literal(self: PeonCompiler, node: LiteralExpr): TypedExpr =
|
||||
proc literal(self: TypeChecker, node: LiteralExpr): TypedExpr =
|
||||
case node.kind:
|
||||
of trueExpr, falseExpr:
|
||||
result = self.infer(node)
|
||||
|
@ -907,7 +909,7 @@ proc literal(self: PeonCompiler, node: LiteralExpr): TypedExpr =
|
|||
self.error(&"invalid AST node of kind {node.kind} at literal(): {node} (This is an internal error and most likely a bug!)")
|
||||
|
||||
|
||||
proc find(self: PeonCompiler, name: string, kind: Type = "any".toIntrinsic()): Name =
|
||||
proc find(self: TypeChecker, name: string, kind: Type = "any".toIntrinsic()): Name =
|
||||
## Looks up a name in all scopes starting from the current
|
||||
## one. Optionally matches it to the given type
|
||||
|
||||
|
@ -945,14 +947,14 @@ proc find(self: PeonCompiler, name: string, kind: Type = "any".toIntrinsic()): N
|
|||
dec(depth)
|
||||
|
||||
|
||||
proc findOrError(self: PeonCompiler, name: string, kind: Type = "any".toIntrinsic()): Name =
|
||||
proc findOrError(self: TypeChecker, name: string, kind: Type = "any".toIntrinsic()): Name =
|
||||
## Like find(), but raises an error if the name is not found
|
||||
result = self.find(name, kind)
|
||||
if result.isNil():
|
||||
self.error(&"reference to undefined name '{name}'")
|
||||
|
||||
|
||||
proc findAll(self: PeonCompiler, name: string, kind: Type = "any".toIntrinsic()): seq[Name] =
|
||||
proc findAll(self: TypeChecker, name: string, kind: Type = "any".toIntrinsic()): seq[Name] =
|
||||
## Like find(), but doesn't stop at the first match. Returns
|
||||
## a list of matches
|
||||
var depth = self.scopeDepth
|
||||
|
@ -980,7 +982,7 @@ proc findAll(self: PeonCompiler, name: string, kind: Type = "any".toIntrinsic())
|
|||
dec(depth)
|
||||
|
||||
|
||||
proc inferOrError(self: PeonCompiler, node: Expression): TypedExpr =
|
||||
proc inferOrError(self: TypeChecker, node: Expression): TypedExpr =
|
||||
## Attempts to infer the type of
|
||||
## the given expression and raises an
|
||||
## error if it fails
|
||||
|
@ -989,7 +991,7 @@ proc inferOrError(self: PeonCompiler, node: Expression): TypedExpr =
|
|||
self.error("expression has no type", node)
|
||||
|
||||
|
||||
proc stringify*(self: PeonCompiler, typ: Type): string =
|
||||
proc stringify*(self: TypeChecker, typ: Type): string =
|
||||
## Returns the string representation of a
|
||||
## type object
|
||||
if typ.isNil():
|
||||
|
@ -1089,7 +1091,7 @@ proc stringify*(self: PeonCompiler, typ: Type): string =
|
|||
discard # TODO(?)
|
||||
|
||||
|
||||
proc stringify*(self: PeonCompiler, typ: TypedNode): string =
|
||||
proc stringify*(self: TypeChecker, typ: TypedNode): string =
|
||||
if typ.node.isConst():
|
||||
return self.stringify(TypedExpr(typ).kind)
|
||||
case typ.node.kind:
|
||||
|
@ -1102,13 +1104,13 @@ proc stringify*(self: PeonCompiler, typ: TypedNode): string =
|
|||
discard # TODO
|
||||
|
||||
|
||||
proc beginScope(self: PeonCompiler) =
|
||||
proc beginScope(self: TypeChecker) =
|
||||
## Begins a new lexical scope
|
||||
inc(self.scopeDepth)
|
||||
self.names.add(newTable[string, seq[Name]]())
|
||||
|
||||
|
||||
proc endScope(self: PeonCompiler) =
|
||||
proc endScope(self: TypeChecker) =
|
||||
## Closes the current lexical
|
||||
## scope and reverts to the one
|
||||
discard self.names.pop()
|
||||
|
@ -1116,7 +1118,7 @@ proc endScope(self: PeonCompiler) =
|
|||
assert self.scopeDepth == self.names.high()
|
||||
|
||||
|
||||
proc check(self: PeonCompiler, term: Expression, expected: Type): TypedExpr {.inline, discardable.} =
|
||||
proc check(self: TypeChecker, term: Expression, expected: Type): TypedExpr {.inline, discardable.} =
|
||||
## Checks the type of the given expression against a known one.
|
||||
## Raises an error if appropriate and returns the typed expression
|
||||
## otherwise
|
||||
|
@ -1127,7 +1129,7 @@ proc check(self: PeonCompiler, term: Expression, expected: Type): TypedExpr {.in
|
|||
self.error("any is not a valid type in this context", term)
|
||||
|
||||
|
||||
proc check(self: PeonCompiler, term, expected: Type, node: ASTNode = nil): Type {.inline, discardable.} =
|
||||
proc check(self: TypeChecker, term, expected: Type, node: ASTNode = nil): Type {.inline, discardable.} =
|
||||
## Like the other check(), but works with two type objects.
|
||||
## The node is passed in to error() in case of a failure
|
||||
result = term
|
||||
|
@ -1137,7 +1139,7 @@ proc check(self: PeonCompiler, term, expected: Type, node: ASTNode = nil): Type
|
|||
self.error("any is not a valid type in this context", node=node)
|
||||
|
||||
|
||||
proc getTypeDistance(self: PeonCompiler, a, b: Type): int =
|
||||
proc getTypeDistance(self: TypeChecker, a, b: Type): int =
|
||||
## Gets the type distance of two Peon types. Assumes
|
||||
## a and b are already compatible (i.e. compare(a, b)
|
||||
## returns true). For more info, check out self.match()
|
||||
|
@ -1161,7 +1163,7 @@ proc getTypeDistance(self: PeonCompiler, a, b: Type): int =
|
|||
parent = parent.parent
|
||||
|
||||
|
||||
proc calcTypeDistance(self: PeonCompiler, typ: Type, sig: TypeSignature): int =
|
||||
proc calcTypeDistance(self: TypeChecker, typ: Type, sig: TypeSignature): int =
|
||||
## Computes the cumulative type distance between
|
||||
## the given type and the given type signature. It's
|
||||
## basically just adding up the type distances of each
|
||||
|
@ -1181,7 +1183,7 @@ proc calcTypeDistance(self: PeonCompiler, typ: Type, sig: TypeSignature): int =
|
|||
self.error(&"cannot compute type distance for object of type {self.stringify(typ)}")
|
||||
|
||||
|
||||
proc checkTypeSignature(self: PeonCompiler, typ: Type, sig: TypeSignature): bool =
|
||||
proc checkTypeSignature(self: TypeChecker, typ: Type, sig: TypeSignature): bool =
|
||||
## Helper for to check type signatures.
|
||||
## Returns true if the given type matches
|
||||
## the given type signature
|
||||
|
@ -1227,7 +1229,7 @@ proc checkTypeSignature(self: PeonCompiler, typ: Type, sig: TypeSignature): bool
|
|||
self.error(&"cannot check type signature for object of type {self.stringify(typ)}")
|
||||
|
||||
|
||||
proc match(self: PeonCompiler, name: string, sig: TypeSignature, node: ASTNode = nil): Name =
|
||||
proc match(self: TypeChecker, name: string, sig: TypeSignature, node: ASTNode = nil): Name =
|
||||
## Tries to find a matching type for a given (typeName, typeSignature) pair
|
||||
## and returns it. In this context, "type signature" means an ordered list of
|
||||
## tuples (paramName, paramType, paramDefault) that represents the arguments we
|
||||
|
@ -1357,7 +1359,7 @@ proc match(self: PeonCompiler, name: string, sig: TypeSignature, node: ASTNode =
|
|||
self.error(msg, node)
|
||||
|
||||
|
||||
proc specialize(self: PeonCompiler, name: Name, args: seq[Expression]): Type =
|
||||
proc specialize(self: TypeChecker, name: Name, args: seq[Expression]): Type =
|
||||
## Instantiates a generic type
|
||||
if not name.isGeneric:
|
||||
self.error(&"cannot instantiate concrete type from {self.stringify(name.valueType)}: a generic is required")
|
||||
|
@ -1392,7 +1394,7 @@ proc specialize(self: PeonCompiler, name: Name, args: seq[Expression]): Type =
|
|||
self.error(&"cannot instantiate concrete type for object of type {self.stringify(name.valueType)}")
|
||||
|
||||
|
||||
proc unpackTypes(self: PeonCompiler, condition: Expression, list: var seq[tuple[match: bool, kind: Type, value: Expression]], accept: bool = true, isGeneric: bool = false) =
|
||||
proc unpackTypes(self: TypeChecker, condition: Expression, list: var seq[tuple[match: bool, kind: Type, value: Expression]], accept: bool = true, isGeneric: bool = false) =
|
||||
## Recursively unpacks a type constraint
|
||||
case condition.kind:
|
||||
of identExpr:
|
||||
|
@ -1422,7 +1424,7 @@ proc unpackTypes(self: PeonCompiler, condition: Expression, list: var seq[tuple[
|
|||
self.error("invalid type constraint", condition)
|
||||
|
||||
|
||||
proc dispatchPragmas(self: PeonCompiler, name: Name) =
|
||||
proc dispatchPragmas(self: TypeChecker, name: Name) =
|
||||
## Dispatches pragmas bound to objects
|
||||
if name.node.isNil():
|
||||
return
|
||||
|
@ -1434,34 +1436,34 @@ proc dispatchPragmas(self: PeonCompiler, name: Name) =
|
|||
pragmas = LambdaExpr(name.node).pragmas
|
||||
else:
|
||||
discard # Unreachable
|
||||
var f: CompilerFunc
|
||||
var f: PragmaFunc
|
||||
for pragma in pragmas:
|
||||
if pragma.name.token.lexeme notin self.compilerProcs:
|
||||
if pragma.name.token.lexeme notin self.pragmas:
|
||||
self.error(&"unknown pragma '{pragma.name.token.lexeme}'")
|
||||
f = self.compilerProcs[pragma.name.token.lexeme]
|
||||
f = self.pragmas[pragma.name.token.lexeme]
|
||||
if f.kind != Immediate:
|
||||
continue
|
||||
f.handler(self, pragma, name)
|
||||
|
||||
|
||||
proc dispatchDelayedPragmas(self: PeonCompiler, name: Name) {.used.} =
|
||||
proc dispatchDelayedPragmas(self: TypeChecker, name: Name) {.used.} =
|
||||
## Dispatches pragmas bound to objects once they
|
||||
## are used
|
||||
if name.node.isNil():
|
||||
return
|
||||
var pragmas: seq[Pragma] = @[]
|
||||
pragmas = Declaration(name.node).pragmas
|
||||
var f: CompilerFunc
|
||||
var f: PragmaFunc
|
||||
for pragma in pragmas:
|
||||
if pragma.name.token.lexeme notin self.compilerProcs:
|
||||
if pragma.name.token.lexeme notin self.pragmas:
|
||||
self.error(&"unknown pragma '{pragma.name.token.lexeme}'")
|
||||
f = self.compilerProcs[pragma.name.token.lexeme]
|
||||
f = self.pragmas[pragma.name.token.lexeme]
|
||||
if f.kind == Immediate:
|
||||
continue
|
||||
f.handler(self, pragma, name)
|
||||
|
||||
|
||||
proc addName(self: PeonCompiler, name: Name) =
|
||||
proc addName(self: TypeChecker, name: Name) =
|
||||
## Adds a name to the current lexical scope
|
||||
var scope = self.names[self.scopeDepth]
|
||||
if scope.hasKey(name.ident.token.lexeme):
|
||||
|
@ -1484,7 +1486,7 @@ proc addName(self: PeonCompiler, name: Name) =
|
|||
scope[name.ident.token.lexeme].add(name)
|
||||
|
||||
|
||||
proc declare(self: PeonCompiler, node: ASTNode): Name {.discardable.} =
|
||||
proc declare(self: TypeChecker, node: ASTNode): Name {.discardable.} =
|
||||
## Declares a name into the current scope
|
||||
var scope = self.names[self.scopeDepth]
|
||||
var name: Name
|
||||
|
@ -1584,12 +1586,12 @@ proc declare(self: PeonCompiler, node: ASTNode): Name {.discardable.} =
|
|||
return name
|
||||
|
||||
|
||||
proc identifier(self: PeonCompiler, node: IdentExpr): TypedExpr =
|
||||
proc identifier(self: TypeChecker, node: IdentExpr): TypedExpr =
|
||||
## Compiles name resolution
|
||||
return newTypedIdentExpr(node, self.findOrError(node.name.lexeme))
|
||||
|
||||
|
||||
proc unary(self: PeonCompiler, node: UnaryExpr): TypedUnaryExpr =
|
||||
proc unary(self: TypeChecker, node: UnaryExpr): TypedUnaryExpr =
|
||||
## Compiles unary expressions
|
||||
var
|
||||
default: Expression
|
||||
|
@ -1602,7 +1604,7 @@ proc unary(self: PeonCompiler, node: UnaryExpr): TypedUnaryExpr =
|
|||
result = newTypedUnaryExpr(node, impl.returnType, typeOfA)
|
||||
|
||||
|
||||
proc binary(self: PeonCompiler, node: BinaryExpr): TypedBinaryExpr =
|
||||
proc binary(self: TypeChecker, node: BinaryExpr): TypedBinaryExpr =
|
||||
## Compiles unary expressions
|
||||
var
|
||||
default: Expression
|
||||
|
@ -1618,12 +1620,12 @@ proc binary(self: PeonCompiler, node: BinaryExpr): TypedBinaryExpr =
|
|||
result = newTypedBinaryExpr(node, impl.returnType, typeOfA, typeOfB)
|
||||
|
||||
|
||||
proc genericExpr(self: PeonCompiler, node: GenericExpr): TypedExpr =
|
||||
proc genericExpr(self: TypeChecker, node: GenericExpr): TypedExpr =
|
||||
## Compiles generic instantiation
|
||||
result = newTypedExpr(node, self.specialize(self.findOrError(node.ident.token.lexeme), node.args))
|
||||
|
||||
|
||||
proc call(self: PeonCompiler, node: CallExpr): TypedExpr =
|
||||
proc call(self: TypeChecker, node: CallExpr): TypedExpr =
|
||||
## Compiles call expressions. This includes
|
||||
## things like object and enum construction
|
||||
var args: TypeSignature = @[]
|
||||
|
@ -1700,14 +1702,14 @@ proc call(self: PeonCompiler, node: CallExpr): TypedExpr =
|
|||
self.error(&"object of type '{self.stringify(typ)}' is not callable", node)
|
||||
|
||||
|
||||
proc refExpr(self: PeonCompiler, node: Ref): TypedExpr =
|
||||
proc refExpr(self: TypeChecker, node: Ref): TypedExpr =
|
||||
## Compiles ref expressions
|
||||
result = self.check(node.value, "typevar".toIntrinsic())
|
||||
result.kind = result.kind.toRef()
|
||||
result.kind = result.kind.wrapType()
|
||||
|
||||
|
||||
proc constExpr(self: PeonCompiler, node: ast.Const): TypedExpr =
|
||||
proc constExpr(self: TypeChecker, node: ast.Const): TypedExpr =
|
||||
## Compiles const expressions
|
||||
var kind = "typevar".toIntrinsic()
|
||||
kind.wrapped = kind.wrapped.toConst()
|
||||
|
@ -1718,7 +1720,7 @@ proc constExpr(self: PeonCompiler, node: ast.Const): TypedExpr =
|
|||
result.kind = result.kind.wrapType()
|
||||
|
||||
|
||||
proc lentExpr(self: PeonCompiler, node: ast.Lent): TypedExpr =
|
||||
proc lentExpr(self: TypeChecker, node: ast.Lent): TypedExpr =
|
||||
## Compiles lent expressions
|
||||
var kind = "typevar".toIntrinsic()
|
||||
# Only match references
|
||||
|
@ -1765,7 +1767,7 @@ method assignment(self: BytecodeCompiler, node: ASTNode, compile: bool = true):
|
|||
]#
|
||||
|
||||
|
||||
proc expression(self: PeonCompiler, node: Expression): TypedExpr =
|
||||
proc expression(self: TypeChecker, node: Expression): TypedExpr =
|
||||
## Compiles expressions
|
||||
if node.isConst():
|
||||
return self.literal(LiteralExpr(node))
|
||||
|
@ -1796,43 +1798,43 @@ proc expression(self: PeonCompiler, node: Expression): TypedExpr =
|
|||
self.error(&"failed to compile expression of type {node.kind}")
|
||||
|
||||
|
||||
proc blockStmt(self: PeonCompiler, node: BlockStmt): TypedBlockStmt =
|
||||
proc blockStmt(self: TypeChecker, node: BlockStmt): TypedBlockStmt =
|
||||
## Compiles block statements
|
||||
self.beginScope()
|
||||
var body: seq[TypedNode] = @[]
|
||||
for decl in node.code:
|
||||
body.add(self.compile(decl))
|
||||
body.add(self.validate(decl))
|
||||
self.endScope()
|
||||
result = newTypedBlockStmt(node, body)
|
||||
|
||||
|
||||
proc ifStmt(self: PeonCompiler, node: IfStmt): TypedNode =
|
||||
## Compiles if/else statements for conditional
|
||||
proc ifStmt(self: TypeChecker, node: IfStmt): TypedNode =
|
||||
## Typechecks if/else statements for conditional
|
||||
## execution of code
|
||||
|
||||
# Check that the condition is a boolean and record it
|
||||
let condition = self.check(node.condition, "bool".toIntrinsic())
|
||||
# Compile the "then" part of "if-then-else"
|
||||
let then = TypedBlockStmt(self.compile(node.thenBranch))
|
||||
let then = TypedBlockStmt(self.validate(node.thenBranch))
|
||||
# Compile the "else" part
|
||||
let otherwise = TypedBlockStmt(self.compile(node.elseBranch))
|
||||
let otherwise = TypedBlockStmt(self.validate(node.elseBranch))
|
||||
# Note: Peon enforces the body of loops and conditionals to
|
||||
# always be a block statement (for a variety of very good reasons),
|
||||
# so the conversion here is safe
|
||||
return newTypedIfStmt(node, then, otherwise, condition)
|
||||
|
||||
|
||||
proc whileStmt(self: PeonCompiler, node: WhileStmt): TypedNode =
|
||||
## Compiles C-style while loops
|
||||
proc whileStmt(self: TypeChecker, node: WhileStmt): TypedNode =
|
||||
## Typechecks C-style while loops
|
||||
|
||||
# Compile and check the condition
|
||||
let condition = self.check(node.condition, "bool".toIntrinsic())
|
||||
# Compile the body
|
||||
return newTypedWhileStmt(node, TypedBlockStmt(self.compile(node.body)), condition)
|
||||
return newTypedWhileStmt(node, TypedBlockStmt(self.validate(node.body)), condition)
|
||||
|
||||
|
||||
proc varDecl(self: PeonCompiler, node: VarDecl): TypedVarDecl =
|
||||
## Compiles variable declarations
|
||||
proc varDecl(self: TypeChecker, node: VarDecl): TypedVarDecl =
|
||||
## Typechecks variable declarations
|
||||
var
|
||||
name = self.declare(node)
|
||||
init: TypedExpr
|
||||
|
@ -1845,7 +1847,7 @@ proc varDecl(self: PeonCompiler, node: VarDecl): TypedVarDecl =
|
|||
else:
|
||||
if node.isConst and not node.value.isConst():
|
||||
self.error("constant initializer is not a constant", node.value)
|
||||
init = TypedExpr(self.compile(node.value))
|
||||
init = TypedExpr(self.validate(node.value))
|
||||
typ = init.kind
|
||||
if not node.valueType.isNil():
|
||||
# Explicit type declaration always takes over
|
||||
|
@ -1864,8 +1866,8 @@ proc varDecl(self: PeonCompiler, node: VarDecl): TypedVarDecl =
|
|||
result = newTypedVarDecl(node, name, init)
|
||||
|
||||
|
||||
proc funDecl(self: PeonCompiler, node: FunDecl, name: Name = nil): TypedDecl =
|
||||
## Compiles function declarations
|
||||
proc funDecl(self: TypeChecker, node: FunDecl, name: Name = nil): TypedDecl =
|
||||
## Typechecks function declarations
|
||||
if node.token.kind == Operator and node.name.token.lexeme in [".", ]:
|
||||
self.error(&"Due to compiler limitations, the '{node.name.token.lexeme}' operator cannot be currently overridden", node.name)
|
||||
var name = name
|
||||
|
@ -1953,7 +1955,7 @@ proc funDecl(self: PeonCompiler, node: FunDecl, name: Name = nil): TypedDecl =
|
|||
if BlockStmt(node.body).code.len() == 0:
|
||||
self.error("cannot declare function with empty body")
|
||||
for decl in BlockStmt(node.body).code:
|
||||
discard self.compile(decl)
|
||||
discard self.validate(decl)
|
||||
self.endScope()
|
||||
# Restores the enclosing function (if any).
|
||||
# Makes nested calls work (including recursion)
|
||||
|
@ -1961,8 +1963,8 @@ proc funDecl(self: PeonCompiler, node: FunDecl, name: Name = nil): TypedDecl =
|
|||
|
||||
|
||||
|
||||
proc typeDecl(self: PeonCompiler, node: TypeDecl, name: Name = nil): TypedTypeDecl =
|
||||
## Compiles type declarations
|
||||
proc typeDecl(self: TypeChecker, node: TypeDecl, name: Name = nil): TypedTypeDecl =
|
||||
## Typechecks type declarations
|
||||
var name = name
|
||||
if name.isNil():
|
||||
name = self.declare(node)
|
||||
|
@ -2055,9 +2057,9 @@ proc typeDecl(self: PeonCompiler, node: TypeDecl, name: Name = nil): TypedTypeDe
|
|||
self.endScope()
|
||||
|
||||
|
||||
proc compile(self: PeonCompiler, node: ASTNode): TypedNode =
|
||||
## Dispatches typeless AST nodes to compile them into
|
||||
## typed ones
|
||||
proc validate(self: TypeChecker, node: ASTNode): TypedNode =
|
||||
## Dispatches typeless AST nodes to typecheck them and turn
|
||||
## them into typed ones
|
||||
case node.kind:
|
||||
of binaryExpr, unaryExpr, NodeKind.genericExpr, identExpr,
|
||||
groupingExpr, callExpr, intExpr, floatExpr, octExpr,
|
||||
|
@ -2078,12 +2080,12 @@ proc compile(self: PeonCompiler, node: ASTNode): TypedNode =
|
|||
of NodeKind.typeDecl:
|
||||
result = self.typeDecl(TypeDecl(node))
|
||||
else:
|
||||
self.error(&"failed to compile node {node.kind}")
|
||||
self.error(&"failed to dispatch node of type {node.kind}")
|
||||
|
||||
|
||||
proc compile*(self: PeonCompiler, tree: seq[Declaration], file, source: string, showMismatches: bool = false,
|
||||
proc validate*(self: TypeChecker, tree: seq[Declaration], file, source: string, showMismatches: bool = false,
|
||||
disabledWarnings: seq[WarningKind] = @[]): seq[TypedNode] =
|
||||
## Compiles a sequence of typeless AST nodes
|
||||
## Transforms a sequence of typeless AST nodes
|
||||
## into a sequence of typed AST nodes
|
||||
self.file = file
|
||||
self.source = source
|
||||
|
@ -2126,7 +2128,9 @@ proc compile*(self: PeonCompiler, tree: seq[Declaration], file, source: string,
|
|||
line: 1)
|
||||
self.addName(main)
|
||||
while not self.done():
|
||||
result.add(self.compile(self.step()))
|
||||
result.add(self.validate(self.step()))
|
||||
assert self.scopeDepth == 0
|
||||
self.endScope()
|
||||
assert self.scopeDepth == -1
|
||||
# Do not close the global scope if
|
||||
# we're being imported
|
||||
if self.isMainModule:
|
||||
self.endScope()
|
14
src/main.nim
14
src/main.nim
|
@ -17,7 +17,7 @@ import util/fmterr
|
|||
import util/symbols
|
||||
import frontend/parsing/lexer
|
||||
import frontend/parsing/parser
|
||||
import frontend/compiler/compiler
|
||||
import frontend/compiler/typechecker
|
||||
|
||||
import std/os
|
||||
import std/parseopt
|
||||
|
@ -63,7 +63,7 @@ proc test(warnings: seq[WarningKind] = @[], mismatches: bool = false) =
|
|||
keep = true
|
||||
tokens: seq[Token] = @[]
|
||||
tree: seq[Declaration] = @[]
|
||||
compiler = newPeonCompiler()
|
||||
typeChecker = newTypeChecker()
|
||||
lexer = newLexer()
|
||||
parser = newParser()
|
||||
editor = getLineEditor()
|
||||
|
@ -106,17 +106,17 @@ proc test(warnings: seq[WarningKind] = @[], mismatches: bool = false) =
|
|||
echo ""
|
||||
if debugCompiler:
|
||||
styledEcho fgCyan, "Compilation step:"
|
||||
for typedNode in compiler.compile(parser.parse(lexer.lex(input, "<stdin>"), lexer.getFile(), lexer.getLines(), lexer.getSource()),
|
||||
for typedNode in typeChecker.validate(parser.parse(lexer.lex(input, "<stdin>"), lexer.getFile(), lexer.getLines(), lexer.getSource()),
|
||||
lexer.getFile(), lexer.getSource(), showMismatches=mismatches, disabledWarnings=warnings):
|
||||
if debugCompiler:
|
||||
styledEcho fgGreen, &"\t{typedNode.node} -> {compiler.stringify(typedNode)}\n"
|
||||
styledEcho fgGreen, &"\t{typedNode.node} -> {typeChecker.stringify(typedNode)}\n"
|
||||
echo ""
|
||||
except LexingError:
|
||||
print(LexingError(getCurrentException()))
|
||||
except ParseError:
|
||||
print(ParseError(getCurrentException()))
|
||||
except CompileError:
|
||||
print(CompileError(getCurrentException()))
|
||||
except TypeCheckError:
|
||||
print(TypeCheckError(getCurrentException()))
|
||||
quit(0)
|
||||
#[
|
||||
var
|
||||
|
@ -218,7 +218,7 @@ when isMainModule:
|
|||
else:
|
||||
stderr.styledWriteLine(fgRed, styleBright, "Error: ", fgDefault, "invalid warning name for option 'noWarn'")
|
||||
quit()
|
||||
of "debug-compiler":
|
||||
of "debug-tc":
|
||||
debugCompiler = true
|
||||
of "compile":
|
||||
run = false
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
# limitations under the License.
|
||||
|
||||
## Utilities to print formatted error messages to stderr
|
||||
import frontend/compiler/compiler
|
||||
import frontend/compiler/typechecker
|
||||
import frontend/parsing/parser
|
||||
import frontend/parsing/lexer
|
||||
import errors
|
||||
|
@ -42,15 +42,15 @@ proc printError(file, line: string, lineNo: int, pos: tuple[start, stop: int], f
|
|||
stderr.styledWriteLine(fgDefault, line[pos.stop + 1..^1])
|
||||
|
||||
|
||||
proc print*(exc: CompileError) =
|
||||
proc print*(exc: TypeCheckError) =
|
||||
## Prints a formatted error message
|
||||
## for compilation errors to stderr
|
||||
## for type checking errors to stderr
|
||||
var file = exc.file
|
||||
var contents = ""
|
||||
case exc.line:
|
||||
of -1: discard
|
||||
of 0: contents = exc.compiler.getSource().strip(chars={'\n'}).splitLines()[exc.line]
|
||||
else: contents = exc.compiler.getSource().strip(chars={'\n'}).splitLines()[exc.line - 1]
|
||||
of 0: contents = exc.instance.getSource().strip(chars={'\n'}).splitLines()[exc.line]
|
||||
else: contents = exc.instance.getSource().strip(chars={'\n'}).splitLines()[exc.line - 1]
|
||||
printError(file, contents, exc.line, exc.node.getRelativeBoundaries(), exc.function, exc.msg)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue