Skip to content

Commit

Permalink
remove Frame, basic infer function return type
Browse files Browse the repository at this point in the history
  • Loading branch information
marcj committed Jul 29, 2022
1 parent 13b6661 commit df8d115
Show file tree
Hide file tree
Showing 12 changed files with 645 additions and 349 deletions.
33 changes: 28 additions & 5 deletions src/checker/check2.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,35 @@ namespace ts::vm2 {
return false;
}
case TypeKind::Union: {
auto current = (TypeRef *) right->type;
while (current) {
if (extends(left, current->type)) return true;
current = current->next;
if (left->kind == TypeKind::Union) {
//number | string extends number => false
//number | string extends number | string => true
//string extends number | string => true

//pretty inefficient I know
auto current = (TypeRef *) right->type;
while (current) {
auto currentLeft = (TypeRef *) left->type;
bool fit = false;
while (currentLeft) {
if (extends(currentLeft->type, current->type)) {
fit = true;
break;
};
currentLeft = currentLeft->next;
}
if (!fit) return false;
current = current->next;
}
return true;
} else {
auto current = (TypeRef *) right->type;
while (current) {
if (extends(left, current->type)) return true;
current = current->next;
}
return false;
}
return false;
}
case TypeKind::Literal: {
switch (left->kind) {
Expand Down
134 changes: 107 additions & 27 deletions src/checker/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,10 +271,10 @@ namespace ts::checker {
for (unsigned int i = 0; visit.active && i<ops.size(); i++) {
visit.op = (OP) ops[i];
switch (visit.op) {
case OP::Frame: {
visit.frameDepth++;
break;
}
//case OP::Frame: {
// visit.frameDepth++;
// break;
//}
case OP::Tuple:
case OP::Union:
case OP::Intersection:
Expand Down Expand Up @@ -566,6 +566,15 @@ namespace ts::checker {
activeSubroutines.back()->popSection();
}

//if > 0 expression statements keep the return value on the stack, otherwise they are removed.
// e.g. `doIt()` removes its return type of `doIt()` while `doIt().deep()` keeps it (so that deep can be resolved).
unsigned int expressionResult = 0;
void pushKeepExpressionResult() {
expressionResult++;
}
void popKeepExpressionResult() {
expressionResult--;
}
void pushSlots() {
currentSubroutine()->slotIP = ip();
pushOp(OP::Slots);
Expand Down Expand Up @@ -779,31 +788,80 @@ namespace ts::checker {
}
}

void pushFunction(OP op, sharedOpt<FunctionLikeDeclarationBase> node, Program &program, sharedOpt<Node> withName) {
if (node->typeParameters) {
void pushFunction(OP op, sharedOpt<Node> node, Program &program, sharedOpt<Node> withName) {
sharedOpt<Node> body;
sharedOpt<Node> type;
sharedOpt<NodeArray> typeParameters;
sharedOpt<NodeArray> parameters;
if (auto a = to<FunctionDeclaration>(node)) {
body = a->body;
type = a->type;
parameters = a->parameters;
typeParameters = a->typeParameters;
} else if (auto a = to<MethodDeclaration>(node)) {
body = a->body;
type = a->type;
parameters = a->parameters;
typeParameters = a->typeParameters;
} else {
throw std::runtime_error("function type not supported");
}

auto pushBodyType = [&] {
unsigned int bodyAddress;
if (body) {
bodyAddress = program.pushSubroutineNameLess();
program.pushOp(OP::TypeArgument);
handle(body, program);
program.pushOp(OP::Loads);
program.pushUint16(0);
program.pushOp(OP::UnwrapInferBody);
program.popSubroutine();
}

if (type) {
handle(type, program);

if (bodyAddress) {
//type and body given, so we check if all `return` are valid.
//it is moved in its own subroutine, so that the return type is cached and only run once.
auto checkBodyAddress = program.pushSubroutineNameLess();
program.pushOp(OP::CheckBody);
program.pushAddress(bodyAddress);
program.popSubroutine();

program.pushOp(OP::Call);
program.pushAddress(checkBodyAddress);
program.pushUint16(0);
}
} else {
if (bodyAddress) {
//no type given, so we infer from body
program.pushOp(OP::InferBody);
program.pushAddress(bodyAddress);
} else {
program.pushOp(OP::Unknown);
}
}
};

if (typeParameters) {
//when there are type parameters, FunctionDeclaration returns a FunctionRef
//which indicates the VM that the function needs to be instantiated first.

auto subroutineIndex = program.pushSubroutineNameLess();
unsigned int size = 0;
for (auto &&param: node->typeParameters->list) {
for (auto &&param: typeParameters->list) {
handle(param, program);
}
program.pushSlots();

//first comes the return type
size++;
if (node->type) {
handle(node->type, program);
} else {
//todo: Infer from body, put into own subroutine so it is cached
program.pushOp(OP::Unknown);
if (node->body) {
} else {
}
}

for (auto &&param: node->parameters->list) {
pushBodyType();

for (auto &&param: parameters->list) {
size++;
handle(param, program);
}
Expand All @@ -821,17 +879,10 @@ namespace ts::checker {
unsigned int size = 0;
//first comes the return type
size++;
if (node->type) {
handle(node->type, program);
} else {
//todo: Infer from body
program.pushOp(OP::Unknown);
if (node->body) {
} else {
}
}

for (auto &&param: node->parameters->list) {
pushBodyType();

for (auto &&param: parameters->list) {
size++;
handle(param, program);
}
Expand Down Expand Up @@ -1179,6 +1230,15 @@ namespace ts::checker {
program.pushUint16(size);
break;
}
case SyntaxKind::IfStatement: {
const auto n = to<IfStatement>(node);
//todo handle n->expression, necessary for type narrowing
handle(n->thenStatement, program);
if (n->elseStatement) {
handle(n->elseStatement, program);
}
break;
}
case SyntaxKind::ParenthesizedExpression: {
const auto n = to<ParenthesizedExpression>(node);
handle(n->expression, program);
Expand Down Expand Up @@ -1232,9 +1292,27 @@ namespace ts::checker {

break;
}
case SyntaxKind::ReturnStatement: {
const auto n = to<ReturnStatement>(node);
handle(n->expression, program);
program.pushOp(OP::ReturnStatement);
break;
}
case SyntaxKind::Block: {
const auto n = to<Block>(node);
for (auto &&statement: n->statements->list) {
handle(statement, program);
}
break;
}
case SyntaxKind::ExpressionStatement: {
const auto n = to<ExpressionStatement>(node);
handle(n->expression, program);
if (!program.expressionResult) {
//expression statements that are not used need to be removed from the stack.
//.e.g `true;` or `doIt();`
program.pushOp(OP::Pop);
}
break;
}
case SyntaxKind::ConditionalExpression: {
Expand Down Expand Up @@ -1355,11 +1433,13 @@ namespace ts::checker {
}
case SyntaxKind::PropertyAccessExpression: {
//e.g. object.method
program.pushKeepExpressionResult();
auto n = to<PropertyAccessExpression>(node);
handle(n->expression, program);
//to avoid resolving Identifier to a symbol, we handle it manually
pushName(n->name, program);
program.pushOp(OP::PropertyAccess, node);
program.popKeepExpressionResult();
break;
}
case SyntaxKind::ClassExpression: {
Expand Down
3 changes: 3 additions & 0 deletions src/checker/debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,10 @@ namespace ts::checker {
newLine = true;
break;
}
case OP::CheckBody:
case OP::InferBody:
case OP::SelfCheck:
case OP::Inline:
case OP::Set:
case OP::TypeArgumentDefault: {
params += fmt::format(" &{}", vm::readUint32(bin, i + 1));
Expand Down
12 changes: 9 additions & 3 deletions src/checker/instructions.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,21 @@ namespace ts::instructions {
Dup, //Duplicates the current stack end
Set, //narrows/Sets a new value for a subroutine (variables)
Error,
Pop,
Inline, //Execute a subroutine on the same active frame

Frame, //creates a new stack frame
FrameEnd,
Return,
//Frame, //creates a new stack frame
//FrameEnd,
Return, //end of a subroutine
ReturnStatement, //used in inferring return types of functions

Subroutine,
Distribute, //calls a subroutine for each union member. one parameter (address to subroutine)
Call, //call a subroutine and push the result on the stack
TailCall,
CheckBody,
InferBody,
UnwrapInferBody,
};

enum class ErrorCode {
Expand Down
8 changes: 4 additions & 4 deletions src/checker/module2.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,15 +142,15 @@ namespace ts::vm2 {
auto map = findNormalizedMap(e.ip);

if (map.found()) {
std::size_t lineStart = code.rfind('\n', map.pos);
auto lineStart = code.rfind('\n', map.pos);
lineStart = lineStart == std::string::npos ? 0 : lineStart + 1;

std::size_t lineEnd = code.find('\n', map.end);
auto lineEnd = code.find('\n', map.end);
if (lineEnd == std::string::npos) lineEnd = code.size();
std::cout << cyan << fileName << ":" << yellow << map.pos << ":" << map.end << reset << " - " << red << "error" << reset << " TS0000: " << e.message << "\n\n";
std::cout << code.substr(lineStart, lineEnd - lineStart - 1) << "\n";
std::cout << "»" << code.substr(lineStart, lineEnd - lineStart - 1) << "\n";
auto space = map.pos - lineStart;
std::cout << std::string(space, ' ') << red << "^" << reset << "\n\n";
std::cout << "»" << std::string(space, ' ') << red << std::string(map.end - map.pos, '~') << reset << "\n\n";
continue;
}
}
Expand Down
Loading

0 comments on commit df8d115

Please sign in to comment.