diff --git a/src/config.nim b/src/config.nim index 12c2291..218807b 100644 --- a/src/config.nim +++ b/src/config.nim @@ -27,7 +27,7 @@ when len(PEON_COMMIT_HASH) != 40: const PEON_BRANCH* = "master" when len(PEON_BRANCH) > 255: {.fatal: "The git branch name's length must be less than or equal to 255 characters".} -const DEBUG_TRACE_VM* = false # Traces VM execution +const DEBUG_TRACE_VM* = true # Traces VM execution const DEBUG_TRACE_GC* = false # Traces the garbage collector (TODO) const DEBUG_TRACE_ALLOCATION* = false # Traces memory allocation/deallocation const DEBUG_TRACE_COMPILER* = false # Traces the compiler diff --git a/src/frontend/compiler.nim b/src/frontend/compiler.nim index c71eff1..e40f8c3 100644 --- a/src/frontend/compiler.nim +++ b/src/frontend/compiler.nim @@ -796,10 +796,10 @@ proc matchImpl(self: Compiler, name: string, kind: Type): Name = return impl[0] -proc emitFunction(self: Compiler, node: Name) = +proc emitFunction(self: Compiler, name: Name) = ## Wrapper to emit LoadFunction instructions self.emitByte(LoadFunction) - self.emitBytes((node.codePos + 4).toTriple()) + self.emitBytes((name.codePos + 4).toTriple()) proc generateCall(self: Compiler, fn: Name, args: seq[Expression]) = @@ -994,6 +994,9 @@ proc identifier(self: Compiler, node: IdentExpr) = # no matter the scope depth self.emitConstant(node, self.inferType(node)) else: + if s.valueType.kind == Function: + self.emitByte(LoadFunction) + self.emitBytes(s.codePos.toTriple()) self.detectClosureVariable(s) if not s.isClosedOver: # Static name resolution, loads value at index in the stack. Very fast. Much wow. @@ -1006,6 +1009,8 @@ proc identifier(self: Compiler, node: IdentExpr) = # not much slower than indexing our stack (since they're both dynamic arrays at runtime anyway) self.emitByte(LoadClosure) self.emitBytes(self.closedOver.high().toTriple()) + if s.valueType.kind == Function: + self.emitByte(PopC) proc assignment(self: Compiler, node: ASTNode) = @@ -1164,7 +1169,41 @@ proc whileStmt(self: Compiler, node: WhileStmt) = self.emitLoop(start) -proc callExpr(self: Compiler, node: CallExpr) = +proc isPure(self: Compiler, node: ASTNode): bool = + ## Checks if a function has any side effects + var pragmas: seq[Pragma] + case node.kind: + of lambdaExpr: + pragmas = LambdaExpr(node).pragmas + else: + pragmas = Declaration(node).pragmas + if pragmas.len() == 0: + return false + for pragma in pragmas: + if pragma.name.name.lexeme == "pure": + return true + return false + + +proc checkCallIsPure(self: Compiler, node: ASTnode): bool = + ## Checks if a call has any side effects + if not self.isPure(node): + return true + var pragmas: seq[Pragma] + case node.kind: + of lambdaExpr: + pragmas = LambdaExpr(node).pragmas + else: + pragmas = Declaration(node).pragmas + if pragmas.len() == 0: + return false + for pragma in pragmas: + if pragma.name.name.lexeme == "pure": + return true + return false + + +proc callExpr(self: Compiler, node: CallExpr): Name = ## Compiles code to call a function var args: seq[tuple[name: string, kind: Type]] = @[] var argExpr: seq[Expression] = @[] @@ -1186,16 +1225,29 @@ proc callExpr(self: Compiler, node: CallExpr) = case node.callee.kind: of identExpr: funct = self.matchImpl(IdentExpr(node.callee).name.lexeme, Type(kind: Function, returnType: Type(kind: Any), args: args)) + of NodeKind.callExpr: + var node = node.callee + while node.kind == callExpr: + funct = self.callExpr(CallExpr(node)) + node = CallExpr(node).callee + + # funct = self.matchImpl(IdentExpr(node.callee).name.lexeme, Type(kind: Function, returnType: Type(kind: Any), args: args)) else: discard # TODO: Lambdas self.generateCall(funct, argExpr) + if self.scopeDepth > 0 and not self.checkCallIsPure(node.callee): + if not self.currentFunction.name.isNil(): + self.error(&"cannot make sure that calls to '{self.currentFunction.name.token.lexeme}' are side-effect free") + else: + self.error(&"cannot make sure that call is side-effect free") + result = funct proc expression(self: Compiler, node: Expression) = ## Compiles all expressions case node.kind: of NodeKind.callExpr: - self.callExpr(CallExpr(node)) # TODO + discard self.callExpr(CallExpr(node)) # TODO of getItemExpr: discard # TODO: Get rid of this of pragmaExpr: diff --git a/src/frontend/parser.nim b/src/frontend/parser.nim index 423821e..6c6ba3e 100644 --- a/src/frontend/parser.nim +++ b/src/frontend/parser.nim @@ -1047,7 +1047,7 @@ proc statement(self: Parser): Statement = # TODO # from module import a [, b, c as d] discard self.step() - result = self.importStmt(fromStmt = true) + result = self.importStmt(fromStmt=true) of While: discard self.step() result = self.whileStmt() @@ -1076,38 +1076,29 @@ proc statement(self: Parser): Statement = result = self.expressionStatement() -proc parsePragma(self: Parser): tuple[global: bool, pragmas: seq[Pragma]] = +proc parsePragmas(self: Parser): seq[Pragma] = ## Parses pragmas - result.global = true - var - decl: Declaration = nil - found = false - for node in self.tree: - if node.token.line == self.peek(-1).line and node.kind in {NodeKind.varDecl, typeDecl, funDecl, lambdaExpr}: - decl = node - found = true - break - if not found: - # Dummy declaration - result.global = false - decl = Declaration(pragmas: @[]) var name: IdentExpr args: seq[LiteralExpr] exp: Expression + names: seq[string] while not self.match("]") and not self.done(): args = @[] self.expect(Identifier, "expecting pragma name") + if self.peek(-1).lexeme in names: + self.error("duplicate pragmas are not allowed") + names.add(self.peek(-1).lexeme) name = newIdentExpr(self.peek(-1)) if not self.match(":"): if self.match("]"): - decl.pragmas.add(newPragma(name, @[])) + result.add(newPragma(name, @[])) break elif self.match("("): while not self.match(")") and not self.done(): exp = self.primary() if not exp.isLiteral(): - self.error("invalid syntax") + self.error("pragma arguments can only be literals") args.add(LiteralExpr(exp)) if not self.match(","): break @@ -1115,12 +1106,11 @@ proc parsePragma(self: Parser): tuple[global: bool, pragmas: seq[Pragma]] = else: exp = self.primary() if not exp.isLiteral(): - self.error("invalid syntax") + self.error("pragma arguments can only be literals") args.add(LiteralExpr(exp)) if self.match(","): continue - decl.pragmas.add(newPragma(name, args)) - result.pragmas = decl.pragmas + result.add(newPragma(name, args)) proc typeDecl(self: Parser): TypeDecl = @@ -1202,11 +1192,8 @@ proc declaration(self: Parser): Declaration = result = self.funDecl(isOperator=true) of TokenType.Pragma: discard self.step() - let temp = self.parsePragma() - if not temp.global: - for p in temp.pragmas: - self.tree.add(p) - result = nil + for p in self.parsePragmas(): + self.tree.add(p) of Type: discard self.step() result = self.typeDecl()