Added missing bitwise operators to parser. Added missing string representation for singletons AST nodes. Improved and bugfixed optimizer

This commit is contained in:
nocturn9x 2021-09-28 18:07:08 +02:00
parent 3fcdb3ec0a
commit e0701d8d9a
4 changed files with 127 additions and 43 deletions

View File

@ -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:

View File

@ -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))

View File

@ -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 =

View File

@ -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: "