Added missing bitwise operators to parser. Added missing string representation for singletons AST nodes. Improved and bugfixed optimizer
This commit is contained in:
parent
3fcdb3ec0a
commit
e0701d8d9a
|
@ -278,7 +278,7 @@ proc newBinExpr*(literal: Token): LiteralExpr =
|
||||||
|
|
||||||
|
|
||||||
proc newFloatExpr*(literal: Token): LiteralExpr =
|
proc newFloatExpr*(literal: Token): LiteralExpr =
|
||||||
result = BinExpr(kind: floatExpr)
|
result = FloatExpr(kind: floatExpr)
|
||||||
result.literal = literal
|
result.literal = literal
|
||||||
|
|
||||||
|
|
||||||
|
@ -451,8 +451,11 @@ proc newClassDecl*(name: ASTNode, body: ASTNode,
|
||||||
|
|
||||||
proc `$`*(self: ASTNode): string =
|
proc `$`*(self: ASTNode): string =
|
||||||
case self.kind:
|
case self.kind:
|
||||||
of intExpr, floatExpr, hexExpr, binExpr, octExpr, strExpr:
|
of intExpr, floatExpr, hexExpr, binExpr, octExpr, strExpr, trueExpr, falseExpr, nanExpr, nilExpr, infExpr:
|
||||||
result &= &"Literal({LiteralExpr(self).literal.lexeme})"
|
if self.kind in {trueExpr, falseExpr, nanExpr, nilExpr, infExpr}:
|
||||||
|
result &= &"Literal({($self.kind)[0..^5]})"
|
||||||
|
else:
|
||||||
|
result &= &"Literal({LiteralExpr(self).literal.lexeme})"
|
||||||
of identExpr:
|
of identExpr:
|
||||||
result &= &"Identifier({IdentExpr(self).name})"
|
result &= &"Identifier({IdentExpr(self).name})"
|
||||||
of groupingExpr:
|
of groupingExpr:
|
||||||
|
|
|
@ -23,7 +23,7 @@ import math
|
||||||
type
|
type
|
||||||
WarningKind* = enum
|
WarningKind* = enum
|
||||||
unreachableCode,
|
unreachableCode,
|
||||||
localShadowsGlobal,
|
nameShadowedInOuterScope,
|
||||||
isWithALiteral,
|
isWithALiteral,
|
||||||
equalityWithSingleton,
|
equalityWithSingleton,
|
||||||
valueOverflow,
|
valueOverflow,
|
||||||
|
@ -34,21 +34,16 @@ type
|
||||||
node*: ASTNode
|
node*: ASTNode
|
||||||
|
|
||||||
Optimizer* = ref object
|
Optimizer* = ref object
|
||||||
constantFolding: bool
|
|
||||||
emitWarnings: bool
|
|
||||||
warnings: seq[Warning]
|
warnings: seq[Warning]
|
||||||
dryRun: bool
|
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
|
## Initializes a new optimizer object
|
||||||
## or resets the state of an existing one
|
## or resets the state of an existing one
|
||||||
if self != nil:
|
if self != nil:
|
||||||
result = self
|
result = self
|
||||||
new(result)
|
new(result)
|
||||||
result.constantFolding = constantFolding
|
|
||||||
result.emitWarnings = emitWarnings
|
|
||||||
result.dryRun = dryRun
|
|
||||||
|
|
||||||
|
|
||||||
proc newWarning(self: Optimizer, kind: WarningKind, node: ASTNode) =
|
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 `$`*(self: Warning): string = &"Warning(kind={self.kind}, node={self.node})"
|
||||||
|
|
||||||
|
|
||||||
proc optimizeNode(self: Optimizer, node: ASTNode): ASTNode
|
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)
|
assert parseHex(y.literal.lexeme, x) == len(y.literal.lexeme)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.newWarning(valueOverflow, node)
|
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:
|
of binExpr:
|
||||||
var x: int
|
var x: int
|
||||||
var y = BinExpr(node)
|
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)
|
assert parseBin(y.literal.lexeme, x) == len(y.literal.lexeme)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.newWarning(valueOverflow, node)
|
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:
|
of octExpr:
|
||||||
var x: int
|
var x: int
|
||||||
var y = OctExpr(node)
|
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)
|
assert parseOct(y.literal.lexeme, x) == len(y.literal.lexeme)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.newWarning(valueOverflow, node)
|
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:
|
of floatExpr:
|
||||||
var x: float
|
var x: float
|
||||||
var y = FloatExpr(node)
|
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)
|
assert parseFloat(y.literal.lexeme, x) == len(y.literal.lexeme)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.newWarning(valueOverflow, node)
|
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:
|
else:
|
||||||
result = node
|
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 =
|
proc optimizeUnary(self: Optimizer, node: UnaryExpr): ASTNode =
|
||||||
## Attempts to optimize unary expressions
|
## 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 =
|
proc optimizeBinary(self: Optimizer, node: BinaryExpr): ASTNode =
|
||||||
|
@ -130,11 +144,22 @@ proc optimizeBinary(self: Optimizer, node: BinaryExpr): ASTNode =
|
||||||
var a, b: ASTNode
|
var a, b: ASTNode
|
||||||
a = self.optimizeNode(node.a)
|
a = self.optimizeNode(node.a)
|
||||||
b = self.optimizeNode(node.b)
|
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:
|
if a.kind == intExpr and b.kind == intExpr:
|
||||||
# Optimizes integer operations
|
# 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
|
var x, y, z: int
|
||||||
discard parseInt(IntExpr(a).literal.lexeme, x)
|
discard parseInt(IntExpr(a).literal.lexeme, x)
|
||||||
discard parseInt(IntExpr(b).literal.lexeme, y)
|
discard parseInt(IntExpr(b).literal.lexeme, y)
|
||||||
|
@ -160,17 +185,54 @@ proc optimizeBinary(self: Optimizer, node: BinaryExpr): ASTNode =
|
||||||
z = x or y
|
z = x or y
|
||||||
of Slash:
|
of Slash:
|
||||||
# Special case, yields a float
|
# 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:
|
else:
|
||||||
discard # Unreachable
|
discard # Unreachable
|
||||||
except OverflowDefect:
|
except OverflowDefect:
|
||||||
self.newWarning(valueOverflow, node)
|
self.newWarning(valueOverflow, node)
|
||||||
return ASTNode(BinaryExpr(kind: binaryExpr, a: a, b: b, operator: node.operator))
|
return 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))))
|
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:
|
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
|
# 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:
|
else:
|
||||||
|
# There's no constant folding we can do!
|
||||||
result = node
|
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
|
## Runs the optimizer on the given source
|
||||||
## tree and returns a new optimized tree
|
## tree and returns a new optimized tree
|
||||||
## as well as a list of warnings that may
|
## as well as a list of warnings that may
|
||||||
## be of interest. Depending on whether any
|
## be of interest. The input tree may be
|
||||||
## optimization could be performed, the output
|
## identical to the output tree if no optimization
|
||||||
## may be identical to the input. If self.dryRun
|
## could be performed
|
||||||
## is set to true, no optimization is performed,
|
|
||||||
## but warnings and log messages are still
|
|
||||||
## generated
|
|
||||||
var newTree: seq[ASTNode] = @[]
|
var newTree: seq[ASTNode] = @[]
|
||||||
for node in tree:
|
for node in tree:
|
||||||
newTree.add(self.optimizeNode(node))
|
newTree.add(self.optimizeNode(node))
|
||||||
|
|
|
@ -305,7 +305,7 @@ proc comparison(self: Parser): ASTNode =
|
||||||
result = self.add()
|
result = self.add()
|
||||||
var operator: Token
|
var operator: Token
|
||||||
var right: ASTNode
|
var right: ASTNode
|
||||||
while self.match([LessThan, GreaterThan, LessOrEqual, GreaterOrEqual]):
|
while self.match([LessThan, GreaterThan, LessOrEqual, GreaterOrEqual, Is]):
|
||||||
operator = self.peek(-1)
|
operator = self.peek(-1)
|
||||||
right = self.add()
|
right = self.add()
|
||||||
result = newBinaryExpr(result, operator, right)
|
result = newBinaryExpr(result, operator, right)
|
||||||
|
@ -340,7 +340,29 @@ proc logicalOr(self: Parser): ASTNode =
|
||||||
var right: ASTNode
|
var right: ASTNode
|
||||||
while self.match(LogicalOr):
|
while self.match(LogicalOr):
|
||||||
operator = self.peek(-1)
|
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)
|
result = newBinaryExpr(result, operator, right)
|
||||||
|
|
||||||
|
|
||||||
|
@ -353,7 +375,7 @@ proc yieldExpr(self: Parser): ASTNode =
|
||||||
else:
|
else:
|
||||||
result = newYieldExpr(self.expression)
|
result = newYieldExpr(self.expression)
|
||||||
else:
|
else:
|
||||||
result = self.logicalOr()
|
result = self.bitwiseOr()
|
||||||
|
|
||||||
|
|
||||||
proc assignment(self: Parser): ASTNode =
|
proc assignment(self: Parser): ASTNode =
|
||||||
|
|
|
@ -39,7 +39,7 @@ while true:
|
||||||
tree = initParser().parse(tokens, filename)
|
tree = initParser().parse(tokens, filename)
|
||||||
optimized = initOptimizer().optimize(tree)
|
optimized = initOptimizer().optimize(tree)
|
||||||
except:
|
except:
|
||||||
echo getCurrentExceptionMsg()
|
echo &"A Nim runtime exception occurred: {getCurrentExceptionMsg()}"
|
||||||
continue
|
continue
|
||||||
|
|
||||||
echo "Tokenization step: "
|
echo "Tokenization step: "
|
||||||
|
|
Loading…
Reference in New Issue