Removed broken optimizer module
This commit is contained in:
parent
8d31b79302
commit
0493181262
|
@ -629,7 +629,6 @@ proc detectClosureVariable(self: Compiler, name: IdentExpr, depth: int = self.sc
|
|||
proc identifier(self: Compiler, node: IdentExpr) =
|
||||
## Compiles access to identifiers
|
||||
let s = self.resolve(node)
|
||||
echo s[]
|
||||
if s == nil:
|
||||
self.error(&"reference to undeclared name '{node.token.lexeme}'")
|
||||
elif s.isConst:
|
||||
|
@ -920,7 +919,10 @@ proc inferDeclType(self: Compiler, node: Declaration): ASTNode =
|
|||
proc expression(self: Compiler, node: ASTNode) =
|
||||
## Compiles all expressions
|
||||
if self.inferExprType(node) == nil:
|
||||
self.error("expression has no type")
|
||||
if node.kind != identExpr:
|
||||
# So we can raise a more appropriate
|
||||
# error in self.identifier()
|
||||
self.error("expression has no type")
|
||||
case node.kind:
|
||||
of getItemExpr:
|
||||
discard # TODO
|
||||
|
|
|
@ -1,384 +0,0 @@
|
|||
# Copyright 2022 Mattia Giambirtone & All Contributors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import meta/ast
|
||||
import meta/token
|
||||
|
||||
import parseutils
|
||||
import strformat
|
||||
import strutils
|
||||
import math
|
||||
|
||||
|
||||
type
|
||||
WarningKind* = enum
|
||||
unreachableCode,
|
||||
nameShadowing,
|
||||
isWithALiteral,
|
||||
equalityWithSingleton,
|
||||
valueOverflow,
|
||||
implicitConversion,
|
||||
invalidOperation
|
||||
|
||||
Warning* = ref object
|
||||
kind*: WarningKind
|
||||
node*: ASTNode
|
||||
|
||||
Optimizer* = ref object
|
||||
warnings: seq[Warning]
|
||||
foldConstants*: bool
|
||||
|
||||
|
||||
proc newOptimizer*(foldConstants: bool = true): Optimizer =
|
||||
## Initializes a new optimizer object
|
||||
new(result)
|
||||
result.foldConstants = foldConstants
|
||||
result.warnings = @[]
|
||||
|
||||
|
||||
proc newWarning(self: Optimizer, kind: WarningKind, node: ASTNode) =
|
||||
self.warnings.add(Warning(kind: kind, node: node))
|
||||
|
||||
|
||||
proc `$`*(self: Warning): string = &"Warning(kind={self.kind}, node={self.node})"
|
||||
|
||||
|
||||
# Forward declaration
|
||||
proc optimizeNode(self: Optimizer, node: ASTNode): ASTNode
|
||||
|
||||
|
||||
proc optimizeConstant(self: Optimizer, node: ASTNode): ASTNode =
|
||||
## Performs some checks on constant AST nodes such as
|
||||
## integers. This method converts all of the different
|
||||
## integer forms (binary, octal and hexadecimal) to
|
||||
## decimal integers. Overflows are checked here too
|
||||
if not self.foldConstants:
|
||||
return node
|
||||
case node.kind:
|
||||
of intExpr:
|
||||
var x: int
|
||||
var y = IntExpr(node)
|
||||
try:
|
||||
discard parseInt(y.literal.lexeme, x)
|
||||
except ValueError:
|
||||
self.newWarning(valueOverflow, node)
|
||||
result = node
|
||||
of hexExpr:
|
||||
var x: int
|
||||
var y = HexExpr(node)
|
||||
try:
|
||||
discard parseHex(y.literal.lexeme, x)
|
||||
except ValueError:
|
||||
self.newWarning(valueOverflow, node)
|
||||
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)
|
||||
try:
|
||||
discard parseBin(y.literal.lexeme, x)
|
||||
except ValueError:
|
||||
self.newWarning(valueOverflow, node)
|
||||
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)
|
||||
try:
|
||||
discard parseOct(y.literal.lexeme, x)
|
||||
except ValueError:
|
||||
self.newWarning(valueOverflow, node)
|
||||
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)
|
||||
try:
|
||||
discard parseFloat(y.literal.lexeme, x)
|
||||
except ValueError:
|
||||
self.newWarning(valueOverflow, node)
|
||||
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 optimizeUnary(self: Optimizer, node: UnaryExpr): ASTNode =
|
||||
## Attempts to optimize unary expressions
|
||||
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
|
||||
of Tilde:
|
||||
self.newWarning(invalidOperation, node)
|
||||
return node
|
||||
else:
|
||||
discard
|
||||
result = FloatExpr(kind: floatExpr, literal: Token(kind: Float, lexeme: $x, line: node.operator.line, pos: (start: -1, stop: -1)))
|
||||
else:
|
||||
result = node
|
||||
|
||||
|
||||
proc optimizeBinary(self: Optimizer, node: BinaryExpr): ASTNode =
|
||||
## Attempts to optimize binary expressions
|
||||
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 newBinaryExpr(a, node.operator, b)
|
||||
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
|
||||
var x, y, z: int
|
||||
discard parseInt(IntExpr(a).literal.lexeme, x)
|
||||
discard parseInt(IntExpr(b).literal.lexeme, y)
|
||||
try:
|
||||
case node.operator.kind:
|
||||
of Plus:
|
||||
z = x + y
|
||||
of Minus:
|
||||
z = x - y
|
||||
of Star:
|
||||
z = x * y
|
||||
of FloorDiv:
|
||||
z = int(x / y)
|
||||
of DoubleStar:
|
||||
if y >= 0:
|
||||
z = x ^ y
|
||||
else:
|
||||
# Nim's builtin pow operator can't handle
|
||||
# negative exponents, so we use math's
|
||||
# pow and convert from/to floats instead
|
||||
z = pow(x.float, y.float).int
|
||||
of Percentage:
|
||||
z = x mod y
|
||||
of Caret:
|
||||
z = x xor y
|
||||
of Ampersand:
|
||||
z = x and y
|
||||
of Pipe:
|
||||
z = x or y
|
||||
of Slash:
|
||||
# Special case, yields a float
|
||||
return newFloatExpr(Token(kind: Float, lexeme: $(x / y), line: IntExpr(a).literal.line, pos: (start: -1, stop: -1)))
|
||||
else:
|
||||
result = newBinaryExpr(a, node.operator, b)
|
||||
except OverflowDefect:
|
||||
self.newWarning(valueOverflow, node)
|
||||
return newBinaryExpr(a, node.operator, b)
|
||||
except RangeDefect:
|
||||
# TODO: What warning do we raise here?
|
||||
return newBinaryExpr(a, node.operator, b)
|
||||
result = newIntExpr(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) == IntExpr(a).literal.lexeme.len()
|
||||
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) == IntExpr(b).literal.lexeme.len()
|
||||
y = float(temp)
|
||||
self.newWarning(implicitConversion, b)
|
||||
else:
|
||||
discard parseFloat(FloatExpr(b).literal.lexeme, y)
|
||||
# Optimizes float operations
|
||||
try:
|
||||
case node.operator.kind:
|
||||
of Plus:
|
||||
z = x + y
|
||||
of Minus:
|
||||
z = x - y
|
||||
of Star:
|
||||
z = x * y
|
||||
of FloorDiv, Slash:
|
||||
z = x / y
|
||||
of DoubleStar:
|
||||
z = pow(x, y)
|
||||
of Percentage:
|
||||
z = x mod y
|
||||
else:
|
||||
result = newBinaryExpr(a, node.operator, b)
|
||||
except OverflowDefect:
|
||||
self.newWarning(valueOverflow, node)
|
||||
return newBinaryExpr(a, node.operator, b)
|
||||
result = newFloatExpr(Token(kind: Float, lexeme: $z, line: LiteralExpr(a).literal.line, pos: (start: -1, stop: -1)))
|
||||
elif a.kind == strExpr and b.kind == strExpr:
|
||||
var a = StrExpr(a)
|
||||
var b = StrExpr(b)
|
||||
case node.operator.kind:
|
||||
of Plus:
|
||||
result = newStrExpr(Token(kind: String, lexeme: "'" & a.literal.lexeme[1..<(^1)] & b.literal.lexeme[1..<(^1)] & "'", pos: (start: -1, stop: -1)))
|
||||
else:
|
||||
result = node
|
||||
elif a.kind == strExpr and self.optimizeNode(b).kind == intExpr and not (self.warnings.len() > 0 and self.warnings[^1].kind == valueOverflow and self.warnings[^1].node == b):
|
||||
var a = StrExpr(a)
|
||||
var b = IntExpr(b)
|
||||
var bb: int
|
||||
discard parseInt(b.literal.lexeme, bb)
|
||||
case node.operator.kind:
|
||||
of Star:
|
||||
result = newStrExpr(Token(kind: String, lexeme: "'" & a.literal.lexeme[1..<(^1)].repeat(bb) & "'"))
|
||||
else:
|
||||
result = node
|
||||
elif b.kind == strExpr and self.optimizeNode(a).kind == intExpr and not (self.warnings.len() > 0 and self.warnings[^1].kind == valueOverflow and self.warnings[^1].node == a):
|
||||
var b = StrExpr(b)
|
||||
var a = IntExpr(a)
|
||||
var aa: int
|
||||
discard parseInt(a.literal.lexeme, aa)
|
||||
case node.operator.kind:
|
||||
of Star:
|
||||
result = newStrExpr(Token(kind: String, lexeme: "'" & b.literal.lexeme[1..<(^1)].repeat(aa) & "'"))
|
||||
else:
|
||||
result = node
|
||||
else:
|
||||
# There's no constant folding we can do!
|
||||
result = node
|
||||
|
||||
|
||||
proc optimizeNode(self: Optimizer, node: ASTNode): ASTNode =
|
||||
## Analyzes an AST node and attempts to perform
|
||||
## optimizations on it. If no optimizations can be
|
||||
## applied or self.foldConstants is set to false,
|
||||
## then the same node is returned
|
||||
if not self.foldConstants:
|
||||
return node
|
||||
case node.kind:
|
||||
of exprStmt:
|
||||
result = newExprStmt(self.optimizeNode(ExprStmt(node).expression), ExprStmt(node).token)
|
||||
of intExpr, hexExpr, octExpr, binExpr, floatExpr, strExpr:
|
||||
result = self.optimizeConstant(node)
|
||||
of unaryExpr:
|
||||
result = self.optimizeUnary(UnaryExpr(node))
|
||||
of binaryExpr:
|
||||
result = self.optimizeBinary(BinaryExpr(node))
|
||||
of groupingExpr:
|
||||
# Recursively unnests groups
|
||||
result = self.optimizeNode(GroupingExpr(node).expression)
|
||||
of callExpr:
|
||||
var node = CallExpr(node)
|
||||
for i, positional in node.arguments.positionals:
|
||||
node.arguments.positionals[i] = self.optimizeNode(positional)
|
||||
for i, (key, value) in node.arguments.keyword:
|
||||
node.arguments.keyword[i].value = self.optimizeNode(value)
|
||||
result = node
|
||||
of sliceExpr:
|
||||
var node = SliceExpr(node)
|
||||
for i, e in node.ends:
|
||||
node.ends[i] = self.optimizeNode(e)
|
||||
node.expression = self.optimizeNode(node.expression)
|
||||
result = node
|
||||
of tryStmt:
|
||||
var node = TryStmt(node)
|
||||
node.body = self.optimizeNode(node.body)
|
||||
if node.finallyClause != nil:
|
||||
node.finallyClause = self.optimizeNode(node.finallyClause)
|
||||
if node.elseClause != nil:
|
||||
node.elseClause = self.optimizeNode(node.elseClause)
|
||||
for i, handler in node.handlers:
|
||||
node.handlers[i].body = self.optimizeNode(node.handlers[i].body)
|
||||
result = node
|
||||
of funDecl:
|
||||
var decl = FunDecl(node)
|
||||
for i, node in decl.defaults:
|
||||
decl.defaults[i] = self.optimizeNode(node)
|
||||
decl.body = self.optimizeNode(decl.body)
|
||||
result = decl
|
||||
of blockStmt:
|
||||
var node = BlockStmt(node)
|
||||
for i, n in node.code:
|
||||
node.code[i] = self.optimizeNode(n)
|
||||
result = node
|
||||
of varDecl:
|
||||
var decl = VarDecl(node)
|
||||
decl.value = self.optimizeNode(decl.value)
|
||||
result = decl
|
||||
of assignExpr:
|
||||
var asgn = AssignExpr(node)
|
||||
asgn.value = self.optimizeNode(asgn.value)
|
||||
result = asgn
|
||||
of listExpr:
|
||||
var l = ListExpr(node)
|
||||
for i, e in l.members:
|
||||
l.members[i] = self.optimizeNode(e)
|
||||
result = node
|
||||
of setExpr:
|
||||
var s = SetExpr(node)
|
||||
for i, e in s.members:
|
||||
s.members[i] = self.optimizeNode(e)
|
||||
result = node
|
||||
of tupleExpr:
|
||||
var t = TupleExpr(node)
|
||||
for i, e in t.members:
|
||||
t.members[i] = self.optimizeNode(e)
|
||||
result = node
|
||||
of dictExpr:
|
||||
var d = DictExpr(node)
|
||||
for i, e in d.keys:
|
||||
d.keys[i] = self.optimizeNode(e)
|
||||
for i, e in d.values:
|
||||
d.values[i] = self.optimizeNode(e)
|
||||
result = node
|
||||
else:
|
||||
result = node
|
||||
|
||||
|
||||
proc optimize*(self: Optimizer, tree: seq[ASTNode]): tuple[tree: seq[ASTNode], warnings: seq[Warning]] =
|
||||
## 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. The input tree may be
|
||||
## identical to the output tree if no optimization
|
||||
## could be performed. Constant folding can be
|
||||
## turned off by setting foldConstants to false
|
||||
## when initializing the optimizer object. This
|
||||
## optimization step also takes care of detecting
|
||||
## closed-over variables so that the compiler can
|
||||
## emit appropriate instructions for them later on
|
||||
var newTree: seq[ASTNode] = @[]
|
||||
for node in tree:
|
||||
newTree.add(self.optimizeNode(node))
|
||||
result = (tree: newTree, warnings: self.warnings)
|
|
@ -898,6 +898,11 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isL
|
|||
returnType=nil)
|
||||
elif not isOperator:
|
||||
self.error("funDecl: invalid state")
|
||||
# Beware: lots of code duplication ahead. I agree,
|
||||
# it's disgusting, but each case of argument parsing
|
||||
# is specialized for a given context and is hard to
|
||||
# generalize elegantly into a single function that
|
||||
# makes sense
|
||||
if self.match(Colon):
|
||||
# Function has explicit return type
|
||||
if self.match([Function, Coroutine, Generator]):
|
||||
|
@ -1040,7 +1045,7 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isL
|
|||
|
||||
proc expression(self: Parser): Expression =
|
||||
## Parses expressions
|
||||
result = self.assignment()
|
||||
result = self.assignment() # Highest-level expression
|
||||
|
||||
|
||||
proc expressionStatement(self: Parser): Statement =
|
||||
|
|
27
src/test.nim
27
src/test.nim
|
@ -17,8 +17,6 @@ import frontend/lexer as l
|
|||
import frontend/parser as p
|
||||
import frontend/compiler as c
|
||||
import backend/vm as v
|
||||
# TODO: Broken
|
||||
# import frontend/optimizer as o
|
||||
import util/serializer as s
|
||||
import util/debugger
|
||||
|
||||
|
@ -29,10 +27,9 @@ proc getLineEditor: LineEditor
|
|||
|
||||
# Handy dandy compile-time constants
|
||||
const debugLexer = false
|
||||
const debugParser = true
|
||||
const debugCompiler = false
|
||||
const debugOptimizer = false
|
||||
const debugSerializer = false
|
||||
const debugParser = false
|
||||
const debugCompiler = true
|
||||
const debugSerializer = true
|
||||
|
||||
|
||||
when isMainModule:
|
||||
|
@ -42,12 +39,10 @@ when isMainModule:
|
|||
tokens: seq[Token] = @[]
|
||||
tree: seq[ASTNode] = @[]
|
||||
compiled: Chunk
|
||||
# optimized: tuple[tree: seq[ASTNode], warnings: seq[Warning]]
|
||||
serialized: Serialized
|
||||
serializedRaw: seq[byte]
|
||||
tokenizer = newLexer()
|
||||
parser = newParser()
|
||||
# optimizer = newOptimizer()
|
||||
compiler = newCompiler()
|
||||
serializer = newSerializer()
|
||||
vm = newPeonVM()
|
||||
|
@ -83,22 +78,6 @@ when isMainModule:
|
|||
for node in tree:
|
||||
echo "\t", node
|
||||
echo ""
|
||||
# The optimizer needs work to function properly
|
||||
# with the compiler
|
||||
# optimized = optimizer.optimize(tree)
|
||||
when debugOptimizer:
|
||||
echo &"Optimization step (constant folding enabled: {optimizer.foldConstants}):"
|
||||
for node in optimized.tree:
|
||||
echo "\t", node
|
||||
echo ""
|
||||
stdout.write(&"Produced warnings: ")
|
||||
if optimized.warnings.len() > 0:
|
||||
echo ""
|
||||
for warning in optimized.warnings:
|
||||
echo "\t", warning
|
||||
else:
|
||||
stdout.write("No warnings produced\n")
|
||||
echo ""
|
||||
compiled = compiler.compile(tree, "<stdin>")
|
||||
when debugCompiler:
|
||||
echo "Compilation step:"
|
||||
|
|
Loading…
Reference in New Issue