Skip to content

Commit

Permalink
basic class support + property access
Browse files Browse the repository at this point in the history
  • Loading branch information
marcj committed Jul 29, 2022
1 parent 007e2cb commit 13b6661
Show file tree
Hide file tree
Showing 10 changed files with 430 additions and 141 deletions.
1 change: 0 additions & 1 deletion src/checker/MemoryPool.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
template<typename T, size_t Items = 4096, size_t BlockSize = sizeof(T) * (1 + Items)>
class MemoryPool {
public:
/* Member types */
typedef T value_type;
typedef T *pointer;
typedef T &reference;
Expand Down
228 changes: 153 additions & 75 deletions src/checker/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,85 @@ namespace ts::checker {
return std::move(program);
}

void pushName(sharedOpt<Node> name, Program &program) {
if (!name) {
program.pushOp(OP::Never);
return;
}

if (name->kind == SyntaxKind::Identifier) {
program.pushStringLiteral(to<Identifier>(name)->escapedText, name);
} else {
//computed type name like `[a]: string`
handle(name, program);
}
}

void pushFunction(OP op, sharedOpt<FunctionLikeDeclarationBase> node, Program &program, sharedOpt<Node> withName) {
if (node->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) {
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) {
size++;
handle(param, program);
}

if (withName) {
pushName(withName, program);
}
program.pushOp(op, reinterpret_pointer_cast<Node>(node));
program.pushUint16(size);
program.popSubroutine();

program.pushOp(OP::FunctionRef, reinterpret_pointer_cast<Node>(node));
program.pushAddress(subroutineIndex);
} else {
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) {
size++;
handle(param, program);
}

if (withName) {
pushName(withName, program);
}
program.pushOp(op, reinterpret_pointer_cast<Node>(node));
program.pushUint16(size);
}
}

void handle(const shared<Node> &node, Program &program) {
switch (node->kind) {
case SyntaxKind::SourceFile: {
Expand Down Expand Up @@ -980,7 +1059,10 @@ namespace ts::checker {
} else {
program.pushOp(instructions::TypeArgument, n->name);
}
//todo constraints
if (n->constraint) {
handle(n->constraint, program);
program.pushOp(instructions::TypeArgumentConstraint);
}
break;
}
case SyntaxKind::FunctionDeclaration: {
Expand All @@ -990,67 +1072,10 @@ namespace ts::checker {
if (symbol.declarations>1) {
//todo: embed error since function is declared twice
} else {
if (n->typeParameters) {
program.pushSubroutine(symbol);
//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: n->typeParameters->list) {
handle(param, program);
}
program.pushSlots();

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

for (auto &&param: n->parameters->list) {
size++;
handle(param, program);
}
program.pushOp(OP::Function, node);
program.pushUint16(size);
program.popSubroutine();

program.pushOp(OP::FunctionRef, node);
program.pushAddress(subroutineIndex);
program.popSubroutine();
} else {
program.pushSubroutine(symbol);
program.pushSlots();

unsigned int size = 0;
//first comes the return type
size++;
if (n->type) {
handle(n->type, program);
} else {
//todo: Infer from body
program.pushOp(OP::Unknown);
if (n->body) {
} else {
}
}

for (auto &&param: n->parameters->list) {
size++;
handle(param, program);
}

program.pushOp(OP::Function, node);
program.pushUint16(size);
program.popSubroutine();
}
program.pushSubroutine(symbol);
program.pushSlots();
pushFunction(OP::Function, n, program, n->name);
program.popSubroutine();
}
} else {
debug("No identifier in name");
Expand Down Expand Up @@ -1095,15 +1120,11 @@ namespace ts::checker {
} else {
program.pushOp(OP::Any, n);
}
if (n->name->kind == SyntaxKind::Identifier) {
program.pushStringLiteral(to<Identifier>(n->name)->escapedText, n->name);
} else {
//computed type name like `[a]: string`
handle(n->name, program);
}
pushName(n, program);
program.pushOp(OP::PropertySignature, n->name);
if (n->questionToken) program.pushOp(OP::Optional);
if (hasModifier(n, SyntaxKind::ReadonlyKeyword)) program.pushOp(OP::Readonly);
if (hasModifier(n, SyntaxKind::StaticKeyword)) program.pushOp(OP::Static);
break;
}
case SyntaxKind::PropertySignature: {
Expand All @@ -1113,12 +1134,7 @@ namespace ts::checker {
} else {
program.pushOp(OP::Any);
}
if (n->name->kind == SyntaxKind::Identifier) {
program.pushStringLiteral(to<Identifier>(n->name)->escapedText, n->name);
} else {
//computed type name like `[a]: string`
handle(n->name, program);
}
pushName(n, program);
program.pushOp(OP::PropertySignature, node);
if (n->questionToken) program.pushOp(OP::Optional);
if (hasModifier(n, SyntaxKind::ReadonlyKeyword)) program.pushOp(OP::Readonly);
Expand Down Expand Up @@ -1256,7 +1272,7 @@ namespace ts::checker {

program.pushOp(OP::Distribute);
distributeJumpIp = program.ip();
program.pushUint16(symbol.index);
program.pushSymbolAddress(symbol);
program.pushAddress(0);
}

Expand Down Expand Up @@ -1337,6 +1353,64 @@ namespace ts::checker {
//todo: handle `as const`, widen if not const
break;
}
case SyntaxKind::PropertyAccessExpression: {
//e.g. object.method
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);
break;
}
case SyntaxKind::ClassExpression: {
//todo: class expression, e.g. `const a = class {}`
throw std::runtime_error("class expression not supported");
break;
}
case SyntaxKind::ClassDeclaration: {
auto n = to<ClassDeclaration>(node);

if (!n->name) {
throw std::runtime_error("class without name not supported");
}

auto &symbol = program.pushSymbolForRoutine(n->name->escapedText, SymbolType::Class, n); //move this to earlier symbol-scan round
if (symbol.declarations>1) {
//todo: for functions/variable embed an error that symbol was declared twice in the same scope
throw std::runtime_error("Nope");
} else {
//populate routine
program.pushSubroutine(symbol);
program.blockTailCall();

if (n->typeParameters) {
for (auto &&p: n->typeParameters->list) {
handle(p, program);
}
}
program.pushSlots();

unsigned int size = 0;
for (auto &&member: n->members->list) {
size++;
handle(member, program);
}
program.pushOp(OP::Class, node);
program.pushUint16(size);
program.popSubroutine();
}
break;
}
case SyntaxKind::MethodDeclaration: {
auto n = to<MethodDeclaration>(node);

pushFunction(OP::Method, n, program, n->name);

if (n->questionToken) program.pushOp(OP::Optional);
if (hasModifier(n, SyntaxKind::ReadonlyKeyword)) program.pushOp(OP::Readonly);
if (hasModifier(n, SyntaxKind::StaticKeyword)) program.pushOp(OP::Static);
break;
}
case SyntaxKind::ArrayType: {
auto n = to<ArrayTypeNode>(node);
handle(n->elementType, program);
Expand Down Expand Up @@ -1453,6 +1527,10 @@ namespace ts::checker {
program.popSubroutine();
}
}
if (symbol.routine) {
program.pushOp(OP::SelfCheck);
program.pushAddress(symbol.routine->index);
}
}
} else {
debug("No identifier in name");
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,6 +141,7 @@ namespace ts::checker {
newLine = true;
break;
}
case OP::SelfCheck:
case OP::Set:
case OP::TypeArgumentDefault: {
params += fmt::format(" &{}", vm::readUint32(bin, i + 1));
Expand All @@ -167,10 +168,12 @@ namespace ts::checker {
vm::eatParams(op, &i);
break;
}
case OP::Method:
case OP::Function:
case OP::Union:
case OP::Tuple:
case OP::TemplateLiteral:
case OP::Class:
case OP::ObjectLiteral:
case OP::Slots:
case OP::Loads: {
Expand Down
11 changes: 9 additions & 2 deletions src/checker/instructions.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ namespace ts::instructions {
PropertySignature,

Class,

ObjectLiteral,
IndexSignature,
PropertyAccess,

Array,
Tuple,
Expand All @@ -55,6 +55,7 @@ namespace ts::instructions {

Optional,
Readonly,
Static,
Rest,
RestReuse, //in expressions like [...T, x] indicates that T can be stolen instead of copied

Expand All @@ -75,6 +76,12 @@ namespace ts::instructions {
*/
Slots,

/**
* Self checks an expression, like variable, class, interface, type, etc.
* TypeScript checks types even if they are never used. This SelfCheck makes sure all expression are visited at least once in main.
*/
SelfCheck,

///**
// * Stack parameter. For each JS variable, JS function, as well as type variables (mapped-type variable for example).
// *
Expand All @@ -98,7 +105,7 @@ namespace ts::instructions {
TypeArgument,
TypeArgumentDefault, //one parameter with the address of the subroutine of the default value

TypeArgumentConstraint, //expects an entry on the stack
TypeArgumentConstraint, //expects an entry (the constraint) on the stack

TemplateLiteral,

Expand Down
8 changes: 8 additions & 0 deletions src/checker/types2.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,15 @@ namespace ts::vm2 {
Number,
BigInt,
Boolean,
Symbol,
Literal,
IndexSignature,
Method,
MethodSignature,
Property,
PropertySignature,
Class,
ClassInstance,
ObjectLiteral,
Union,
Array,
Expand All @@ -48,6 +55,7 @@ namespace ts::vm2 {
Stored = 1 << 8, //Used somewhere as cache or as value (subroutine->result for example), and thus can not be stolen/modified
RestReuse = 1 << 9, //allow to reuse/steal T in ...T
Deleted = 1 << 10, //for debugging purposes
Static = 1 << 11,
};

struct Type;
Expand Down
Loading

0 comments on commit 13b6661

Please sign in to comment.