diff --git a/src/backend/meta/ast.nim b/src/backend/meta/ast.nim index 00edbcc..2663807 100644 --- a/src/backend/meta/ast.nim +++ b/src/backend/meta/ast.nim @@ -278,7 +278,7 @@ proc newBinExpr*(literal: Token): LiteralExpr = proc newFloatExpr*(literal: Token): LiteralExpr = - result = BinExpr(kind: floatExpr) + result = FloatExpr(kind: floatExpr) result.literal = literal @@ -451,8 +451,11 @@ proc newClassDecl*(name: ASTNode, body: ASTNode, proc `$`*(self: ASTNode): string = case self.kind: - of intExpr, floatExpr, hexExpr, binExpr, octExpr, strExpr: - result &= &"Literal({LiteralExpr(self).literal.lexeme})" + of intExpr, floatExpr, hexExpr, binExpr, octExpr, strExpr, trueExpr, falseExpr, nanExpr, nilExpr, infExpr: + if self.kind in {trueExpr, falseExpr, nanExpr, nilExpr, infExpr}: + result &= &"Literal({($self.kind)[0..^5]})" + else: + result &= &"Literal({LiteralExpr(self).literal.lexeme})" of identExpr: result &= &"Identifier({IdentExpr(self).name})" of groupingExpr: diff --git a/src/backend/optimizer.nim b/src/backend/optimizer.nim index 3e3f6db..9add51f 100644 --- a/src/backend/optimizer.nim +++ b/src/backend/optimizer.nim @@ -23,7 +23,7 @@ import math type WarningKind* = enum unreachableCode, - localShadowsGlobal, + nameShadowedInOuterScope, isWithALiteral, equalityWithSingleton, valueOverflow, @@ -34,21 +34,16 @@ type node*: ASTNode Optimizer* = ref object - constantFolding: bool - emitWarnings: bool warnings: seq[Warning] dryRun: bool -proc initOptimizer*(self: Optimizer = nil, constantFolding = true, emitWarnings = true, dryRun = false): Optimizer = +proc initOptimizer*(self: Optimizer = nil): Optimizer = ## Initializes a new optimizer object ## or resets the state of an existing one if self != nil: result = self new(result) - result.constantFolding = constantFolding - result.emitWarnings = emitWarnings - result.dryRun = dryRun proc newWarning(self: Optimizer, kind: WarningKind, node: ASTNode) = @@ -56,8 +51,6 @@ proc newWarning(self: Optimizer, kind: WarningKind, node: ASTNode) = proc `$`*(self: Warning): string = &"Warning(kind={self.kind}, node={self.node})" - - proc optimizeNode(self: Optimizer, node: ASTNode): ASTNode @@ -82,7 +75,8 @@ proc checkConstants(self: Optimizer, node: ASTNode): ASTNode = assert parseHex(y.literal.lexeme, x) == len(y.literal.lexeme) except ValueError: self.newWarning(valueOverflow, node) - result = ASTNode(IntExpr(kind: intExpr, literal: Token(kind: Integer, lexeme: $x, line: y.literal.line, pos: (start: -1, stop: -1)))) + return node + result = IntExpr(kind: intExpr, literal: Token(kind: Integer, lexeme: $x, line: y.literal.line, pos: (start: -1, stop: -1))) of binExpr: var x: int var y = BinExpr(node) @@ -90,7 +84,8 @@ proc checkConstants(self: Optimizer, node: ASTNode): ASTNode = assert parseBin(y.literal.lexeme, x) == len(y.literal.lexeme) except ValueError: self.newWarning(valueOverflow, node) - result = ASTNode(IntExpr(kind: intExpr, literal: Token(kind: Integer, lexeme: $x, line: y.literal.line, pos: (start: -1, stop: -1)))) + return node + result = IntExpr(kind: intExpr, literal: Token(kind: Integer, lexeme: $x, line: y.literal.line, pos: (start: -1, stop: -1))) of octExpr: var x: int var y = OctExpr(node) @@ -98,7 +93,8 @@ proc checkConstants(self: Optimizer, node: ASTNode): ASTNode = assert parseOct(y.literal.lexeme, x) == len(y.literal.lexeme) except ValueError: self.newWarning(valueOverflow, node) - result = ASTNode(IntExpr(kind: intExpr, literal: Token(kind: Integer, lexeme: $x, line: y.literal.line, pos: (start: -1, stop: -1)))) + return node + result = IntExpr(kind: intExpr, literal: Token(kind: Integer, lexeme: $x, line: y.literal.line, pos: (start: -1, stop: -1))) of floatExpr: var x: float var y = FloatExpr(node) @@ -106,23 +102,41 @@ proc checkConstants(self: Optimizer, node: ASTNode): ASTNode = assert parseFloat(y.literal.lexeme, x) == len(y.literal.lexeme) except ValueError: self.newWarning(valueOverflow, node) - result = ASTNode(IntExpr(kind: intExpr, literal: Token(kind: Integer, lexeme: $x, line: y.literal.line, pos: (start: -1, stop: -1)))) + return node + result = FloatExpr(kind: floatExpr, literal: Token(kind: Float, lexeme: $x, line: y.literal.line, pos: (start: -1, stop: -1))) else: result = node -proc foldConstants(self: Optimizer, node: ASTNode): ASTNode = - ## Attempts to perform constant folding if it is feasible - ## and if the self.constantFolding field is set to true. - - if not self.constantFolding: - return node - - proc optimizeUnary(self: Optimizer, node: UnaryExpr): ASTNode = ## Attempts to optimize unary expressions - result = node - + var a = self.optimizeNode(node.a) + if self.warnings.len() > 0 and self.warnings[^1].kind == valueOverflow and self.warnings[^1].node == a: + # We can't optimize further, the overflow will be caught in the compiler + return UnaryExpr(kind: unaryExpr, a: a, operator: node.operator) + case a.kind: + of intExpr: + var x: int + discard parseInt(IntExpr(a).literal.lexeme, x) + case node.operator.kind: + of Tilde: + x = not x + of Minus: + x = -x + else: + discard # Unreachable + result = IntExpr(kind: intExpr, literal: Token(kind: Integer, lexeme: $x, line: node.operator.line, pos: (start: -1, stop: -1))) + of floatExpr: + var x: float + discard parseFloat(FloatExpr(a).literal.lexeme, x) + case node.operator.kind: + of Minus: + x = -x + else: + discard + result = FloatExpr(kind: floatExpr, literal: Token(kind: Float, lexeme: $x, line: node.operator.line, pos: (start: -1, stop: -1))) + else: + discard # Unreachable proc optimizeBinary(self: Optimizer, node: BinaryExpr): ASTNode = @@ -130,11 +144,22 @@ proc optimizeBinary(self: Optimizer, node: BinaryExpr): ASTNode = var a, b: ASTNode a = self.optimizeNode(node.a) b = self.optimizeNode(node.b) + if self.warnings.len() > 0 and self.warnings[^1].kind == valueOverflow and (self.warnings[^1].node == a or self.warnings[^1].node == b): + # We can't optimize further, the overflow will be caught in the compiler. We don't return the same node + # because optimizeNode might've been able to optimize one of the two operands and we don't know which + return BinaryExpr(kind: binaryExpr, a: a, b: b, operator: node.operator) + if node.operator.kind == DoubleEqual: + if a.kind in {trueExpr, falseExpr, nilExpr, nanExpr, infExpr}: + self.newWarning(equalityWithSingleton, a) + elif b.kind in {trueExpr, falseExpr, nilExpr, nanExpr, infExpr}: + self.newWarning(equalityWithSingleton, b) + elif node.operator.kind == Is: + if a.kind in {strExpr, intExpr, tupleExpr, dictExpr, listExpr, setExpr}: + self.newWarning(isWithALiteral, a) + elif b.kind in {strExpr, intExpr, tupleExpr, dictExpr, listExpr, setExpr}: + self.newWarning(isWithALiteral, b) if a.kind == intExpr and b.kind == intExpr: # Optimizes integer operations - if self.warnings.len() > 0 and self.warnings[^1].kind == valueOverflow and (self.warnings[^1].node == a or self.warnings[^1].node == b): - # We can't optimize further, the overflow will be caught in the compiler - return ASTNode(BinaryExpr(kind: binaryExpr, a: a, b: b, operator: node.operator)) var x, y, z: int discard parseInt(IntExpr(a).literal.lexeme, x) discard parseInt(IntExpr(b).literal.lexeme, y) @@ -160,17 +185,54 @@ proc optimizeBinary(self: Optimizer, node: BinaryExpr): ASTNode = z = x or y of Slash: # Special case, yields a float - return ASTNode(FloatExpr(kind: intExpr, literal: Token(kind: Float, lexeme: $(x / y), line: IntExpr(a).literal.line, pos: (start: -1, stop: -1)))) + return FloatExpr(kind: intExpr, literal: Token(kind: Float, lexeme: $(x / y), line: IntExpr(a).literal.line, pos: (start: -1, stop: -1))) else: discard # Unreachable except OverflowDefect: self.newWarning(valueOverflow, node) - return ASTNode(BinaryExpr(kind: binaryExpr, a: a, b: b, operator: node.operator)) - result = ASTNode(IntExpr(kind: intExpr, literal: Token(kind: Integer, lexeme: $z, line: IntExpr(a).literal.line, pos: (start: -1, stop: -1)))) + return BinaryExpr(kind: binaryExpr, a: a, b: b, operator: node.operator) + result = IntExpr(kind: intExpr, literal: Token(kind: Integer, lexeme: $z, line: IntExpr(a).literal.line, pos: (start: -1, stop: -1))) elif a.kind == floatExpr or b.kind == floatExpr: + var x, y, z: float + if a.kind == intExpr: + var temp: int + discard parseInt(IntExpr(a).literal.lexeme, temp) + x = float(temp) + self.newWarning(implicitConversion, a) + else: + discard parseFloat(FloatExpr(a).literal.lexeme, x) + if b.kind == intExpr: + var temp: int + discard parseInt(IntExpr(b).literal.lexeme, temp) + y = float(temp) + self.newWarning(implicitConversion, b) + else: + discard parseFloat(FloatExpr(b).literal.lexeme, y) # Optimizes float operations - result = node + try: + case node.operator.kind: + of Plus: + z = x + y + of Minus: + z = x - y + of Asterisk: + z = x * y + of FloorDiv: + z = x / y + of DoubleAsterisk: + z = pow(x, y) + of Percentage: + z = x mod y + of Slash: + z = x / y + else: + discard # Unreachable + except OverflowDefect: + self.newWarning(valueOverflow, node) + return BinaryExpr(kind: binaryExpr, a: a, b: b, operator: node.operator) + result = FloatExpr(kind: floatExpr, literal: Token(kind: Float, lexeme: $z, line: LiteralExpr(a).literal.line, pos: (start: -1, stop: -1))) else: + # There's no constant folding we can do! result = node @@ -198,12 +260,9 @@ proc optimize*(self: Optimizer, tree: seq[ASTNode]): tuple[tree: seq[ASTNode], w ## Runs the optimizer on the given source ## tree and returns a new optimized tree ## as well as a list of warnings that may - ## be of interest. Depending on whether any - ## optimization could be performed, the output - ## may be identical to the input. If self.dryRun - ## is set to true, no optimization is performed, - ## but warnings and log messages are still - ## generated + ## be of interest. The input tree may be + ## identical to the output tree if no optimization + ## could be performed var newTree: seq[ASTNode] = @[] for node in tree: newTree.add(self.optimizeNode(node)) diff --git a/src/backend/parser.nim b/src/backend/parser.nim index 326f2d2..d824c0c 100644 --- a/src/backend/parser.nim +++ b/src/backend/parser.nim @@ -305,7 +305,7 @@ proc comparison(self: Parser): ASTNode = result = self.add() var operator: Token var right: ASTNode - while self.match([LessThan, GreaterThan, LessOrEqual, GreaterOrEqual]): + while self.match([LessThan, GreaterThan, LessOrEqual, GreaterOrEqual, Is]): operator = self.peek(-1) right = self.add() result = newBinaryExpr(result, operator, right) @@ -340,7 +340,29 @@ proc logicalOr(self: Parser): ASTNode = var right: ASTNode while self.match(LogicalOr): operator = self.peek(-1) - right = self.logical_and() + right = self.logicalAnd() + result = newBinaryExpr(result, operator, right) + + +proc bitwiseAnd(self: Parser): ASTNode = + ## Parser a & b expressions + result = self.logicalOr() + var operator: Token + var right: ASTNode + while self.match(Pipe): + operator = self.peek(-1) + right = self.logicalOr() + result = newBinaryExpr(result, operator, right) + + +proc bitwiseOr(self: Parser): ASTNode = + ## Parser a | b expressions + result = self.bitwiseAnd() + var operator: Token + var right: ASTNode + while self.match(Ampersand): + operator = self.peek(-1) + right = self.bitwiseAnd() result = newBinaryExpr(result, operator, right) @@ -353,7 +375,7 @@ proc yieldExpr(self: Parser): ASTNode = else: result = newYieldExpr(self.expression) else: - result = self.logicalOr() + result = self.bitwiseOr() proc assignment(self: Parser): ASTNode = diff --git a/src/main.nim b/src/main.nim index cf24bbe..f45fca0 100644 --- a/src/main.nim +++ b/src/main.nim @@ -39,7 +39,7 @@ while true: tree = initParser().parse(tokens, filename) optimized = initOptimizer().optimize(tree) except: - echo getCurrentExceptionMsg() + echo &"A Nim runtime exception occurred: {getCurrentExceptionMsg()}" continue echo "Tokenization step: "