diff --git a/src/codegen/SolsScope.c b/src/codegen/SolsScope.c index 54e81d9..5865c97 100644 --- a/src/codegen/SolsScope.c +++ b/src/codegen/SolsScope.c @@ -9,7 +9,13 @@ void addVariableToScope(SolsScope* scope, const char* name, SolsType type) { strncpy(s->id, name, sizeof(s->id) - 1); s->id[sizeof(s->id) - 1] = '\0'; - s->typeinfo = type; + s->typeinfo = ({ + ResultType(SolsType, charptr) _result = copySolsType(&type); + if (_result.error) { + return; + } + _result.as.success; + }); HASH_ADD_STR(scope->variables, id, s); } diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c index 65bddfa..f074cc6 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -305,7 +305,14 @@ ResultType(SolsType, charptr) getNodeType(SolsNode* node, SolsScope* scope) { if (type.as.success.type != STT_OBJECT) { return Error(SolsType, charptr, "Cannot use dot operator on non-object"); } - return findStructMemberType(&type.as.success, node->children.at[1].as.idName); + ResultType(SolsType, charptr) child = findStructMemberType(&type.as.success, node->children.at[1].as.idName); + if (child.error) { + return child; + } + if (child.as.success.metadata.isPrivate) { + return Error(SolsType, charptr, "Member is private, and cannot be accessed outside of object methods"); + } + return child; } default: break; } @@ -377,6 +384,12 @@ static inline ResultType(GroundProgram, charptr) generateSetNode(SolsNode* node, if (type.error) { return Error(GroundProgram, charptr, type.as.error); } + if (type.as.success.metadata.isPrivate) { + return Error(GroundProgram, charptr, "Member is private, and cannot be accessed outside of object methods"); + } + if (type.as.success.metadata.isProtected) { + return Error(GroundProgram, charptr, "Member is protected, and cannot be modified outside of object methods"); + } if (compareTypes(&type.as.success, &rtype.as.success) == false) { return Error(GroundProgram, charptr, "Type of member cannot be changed"); } @@ -1162,13 +1175,37 @@ ResultType(GroundProgram, charptr) generateStructNode(SolsNode* node, SolsScope* groundAddReferenceToInstruction(&structDefInst, groundCreateReference(TYPEREF, node->as.idName)); groundAddInstructionToProgram(&structDef, structDefInst); for (size_t i = 0; i < node->children.count; i++) { + bool private = false; + bool protected = false; // Add to type for type system SolsNode* child = &node->children.at[i]; + + if (child->type == SNT_DEF_PRIVATE) { + private = true; + child->type = SNT_DEF; + } + if (child->type == SNT_SET_PRIVATE) { + private = true; + child->type = SNT_OP_SET; + } + if (child->type == SNT_DEF_PROTECTED) { + protected = true; + child->type = SNT_DEF; + } + if (child->type == SNT_SET_PROTECTED) { + protected = true; + child->type = SNT_OP_SET; + } + if (child->type == SNT_DEF) { ResultType(SolsType, charptr) childType = getNodeType(child, scope); if (childType.error) { return Error(GroundProgram, charptr, childType.as.error); } + + childType.as.success.metadata.isPrivate = private; + childType.as.success.metadata.isProtected = protected; + addChildToSolsType(&type, childType.as.success, child->children.at[0].as.idName); // Add struct members to new scope @@ -1230,6 +1267,8 @@ ResultType(GroundProgram, charptr) generateStructNode(SolsNode* node, SolsScope* if (childType.error) { return Error(GroundProgram, charptr, childType.as.error); } + childType.as.success.metadata.isPrivate = private; + childType.as.success.metadata.isProtected = protected; addChildToSolsType(&type, childType.as.success, child->children.at[0].as.idName); // Generate constant initial value @@ -1274,6 +1313,16 @@ ResultType(GroundProgram, charptr) generateStructNode(SolsNode* node, SolsScope* SolsScope childScope = copySolsScope(scope); addVariableToScope(&childScope, "self", type); + // Modify self so that all members are not private or protected + SolsVariable* variable = findSolsVariable(&childScope, "self"); + if (variable == NULL) { + return Error(GroundProgram, charptr, "Failed to find self variable (this should never happen)"); + } + for (size_t i = 0; i < variable->typeinfo.children.count; i++) { + variable->typeinfo.children.at[i].type.metadata.isPrivate = false; + variable->typeinfo.children.at[i].type.metadata.isProtected = false; + } + for (size_t i = 0; i < constructor->as.type.children.count; i++) { addVariableToScope(&childScope, constructor->as.type.children.at[i].name, constructor->as.type.children.at[i].type); } diff --git a/src/lexer/SolsToken.h b/src/lexer/SolsToken.h index 02d4821..c63389e 100644 --- a/src/lexer/SolsToken.h +++ b/src/lexer/SolsToken.h @@ -18,6 +18,7 @@ typedef enum SolsTokenType { STT_OP_GREATER, STT_OP_LESSER, STT_OP_EQUAL, STT_OP_INEQUAL, STT_OP_EQGREATER, STT_OP_EQLESSER, STT_KW_DEF, STT_KW_LAMBDA, STT_KW_RETURN, STT_KW_USE, STT_KW_STRUCT, STT_KW_CONSTRUCTOR, STT_KW_DESTRUCTOR, + STT_KW_PRIVATE, STT_KW_PROTECTED, STT_KW_PUTS, STT_KW_IF, STT_KW_WHILE, STT_KW_NEW, STT_KW_GROUND, STT_LINE_END, STT_COMMA } SolsTokenType; diff --git a/src/lexer/SolsType.c b/src/lexer/SolsType.c index c51b084..6fbac10 100644 --- a/src/lexer/SolsType.c +++ b/src/lexer/SolsType.c @@ -1,5 +1,4 @@ #include "SolsType.h" -#include "lexer.h" #include "../include/error.h" #include "../include/estr.h" #include @@ -18,7 +17,9 @@ ResultType(SolsType, charptr) createSolsType(SolsTypeType in) { .children.count = 0, .children.at = ptr, .typeIsKnown = true, - .needsGroundStruct = false + .needsGroundStruct = false, + .metadata.isPrivate = false, + .metadata.isProtected = false }; return Success(SolsType, charptr, type); } @@ -37,7 +38,9 @@ ResultType(SolsType, charptr) createIdentifiedSolsType(char* in) { .children.count = 0, .children.at = NULL, .typeIsKnown = false, - .needsGroundStruct = false + .needsGroundStruct = false, + .metadata.isPrivate = false, + .metadata.isProtected = false })); } @@ -50,7 +53,8 @@ ResultType(SolsType, charptr) copySolsType(SolsType* type) { .returnType = NULL, .children.count = type->children.count, .children.capacity = type->children.capacity, - .children.at = NULL + .children.at = NULL, + .metadata = type->metadata }; if (type->identifierType != NULL) { diff --git a/src/lexer/SolsType.h b/src/lexer/SolsType.h index fe144f1..1027275 100644 --- a/src/lexer/SolsType.h +++ b/src/lexer/SolsType.h @@ -68,6 +68,11 @@ typedef struct SolsType { size_t count; size_t capacity; } children; + + struct { + bool isPrivate; + bool isProtected; + } metadata; } SolsType; // Assists with holding child types in the SolsType struct. diff --git a/src/lexer/lexer.c b/src/lexer/lexer.c index 6360a22..f0cb4b6 100644 --- a/src/lexer/lexer.c +++ b/src/lexer/lexer.c @@ -17,6 +17,8 @@ struct _SolsTokenTypeMap SolsTokenTypeMap[] = { {"struct", STT_KW_STRUCT}, {"constructor", STT_KW_CONSTRUCTOR}, {"destructor", STT_KW_DESTRUCTOR}, + {"private", STT_KW_PRIVATE}, + {"protected", STT_KW_PROTECTED}, {"ground", STT_KW_GROUND}, {"new", STT_KW_NEW}, {"{", STT_OPEN_CURLY}, diff --git a/src/parser/SolsNode.h b/src/parser/SolsNode.h index 91cd63f..639ff58 100644 --- a/src/parser/SolsNode.h +++ b/src/parser/SolsNode.h @@ -19,6 +19,7 @@ typedef enum SolsNodeType { SNT_OP_SET, SNT_OP_GREATER, SNT_OP_LESSER, SNT_OP_EQUAL, SNT_OP_INEQUAL, SNT_OP_EQGREATER, SNT_OP_EQLESSER, SNT_DEF, SNT_LAMBDA, SNT_FUNCTION_CALL, SNT_RETURN, + SNT_SET_PRIVATE, SNT_SET_PROTECTED, SNT_DEF_PRIVATE, SNT_DEF_PROTECTED, SNT_USE, SNT_STRUCT, SNT_CONSTRUCTOR, SNT_DESTRUCTOR, SNT_PUTS, SNT_IF, SNT_WHILE, SNT_NEW, SNT_GROUND, SNT_ROOT, SNT_EXPR_IN_PAREN, SNT_DOT, diff --git a/src/parser/parser.c b/src/parser/parser.c index b17f533..2d530fd 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -1866,6 +1866,9 @@ static inline ResultType(Nothing, charptr) parseStruct(SolsParser* parser) { } if (done) break; + bool private = false; + bool protected = false; + // key = value\n SolsToken keyTok = ({ ResultType(SolsToken, Nothing) _result = parserConsume(parser); @@ -1874,18 +1877,46 @@ static inline ResultType(Nothing, charptr) parseStruct(SolsParser* parser) { } _result.as.success; }); + if (keyTok.type == STT_KW_PRIVATE) { + // private key = value + private = true; + + ResultType(SolsToken, Nothing) result = parserConsume(parser); + if (result.error) { + return Error(Nothing, charptr, "Expecting identifier after 'private'"); + } + keyTok = result.as.success; + } + if (keyTok.type == STT_KW_PROTECTED) { + // protected key = value + protected = true; + + ResultType(SolsToken, Nothing) result = parserConsume(parser); + if (result.error) { + return Error(Nothing, charptr, "Expecting identifier after 'protected'"); + } + keyTok = result.as.success; + } if (keyTok.type == STT_KW_DEF) { // def fnName(Arg arg1, Arg arg2, ...) ReturnType {} ResultType(Nothing, charptr) defNode = parseDef(parser); if (defNode.error) { return Error(Nothing, charptr, defNode.as.error); } + if (protected) parser->currentParent->children.at[parser->currentParent->children.count - 1].type = SNT_DEF_PROTECTED; + if (private) parser->currentParent->children.at[parser->currentParent->children.count - 1].type = SNT_DEF_PRIVATE; addChildToSolsNode(&structNode, parser->currentParent->children.at[parser->currentParent->children.count - 1]); parser->currentParent->children.count--; continue; } if (keyTok.type == STT_KW_CONSTRUCTOR) { // constructor(Arg arg1, Arg arg2, ...) {} + if (private) { + return Error(Nothing, charptr, "Constructor cannot be private"); + } + if (protected) { + return Error(Nothing, charptr, "Constructor cannot be protected"); + } ResultType(Nothing, charptr) constructorNode = parseConstructor(parser); if (constructorNode.error) { return Error(Nothing, charptr, constructorNode.as.error); @@ -1896,6 +1927,12 @@ static inline ResultType(Nothing, charptr) parseStruct(SolsParser* parser) { } if (keyTok.type == STT_KW_DESTRUCTOR) { // destructor {} + if (private) { + return Error(Nothing, charptr, "Destructor cannot be private"); + } + if (protected) { + return Error(Nothing, charptr, "Destructor cannot be protected"); + } ResultType(Nothing, charptr) destructorNode = parseDestructor(parser); if (destructorNode.error) { return Error(Nothing, charptr, destructorNode.as.error); @@ -1960,6 +1997,8 @@ static inline ResultType(Nothing, charptr) parseStruct(SolsParser* parser) { } _result.as.success; }); + if (protected) setNode.type = SNT_SET_PROTECTED; + if (private) setNode.type = SNT_SET_PRIVATE; addChildToSolsNode(&setNode, ({ ResultType(SolsNode, charptr) _result = createSolsNode(SNT_IDENTIFIER, keyTok.as.idName); if (_result.error) { @@ -2074,6 +2113,14 @@ ResultType(Nothing, charptr) parse(SolsParser* parser) { createParserError(parser, "'destructor' must be used inside a struct definition"); break; } + case STT_KW_PRIVATE: { + createParserError(parser, "'private' must be used inside a struct definition"); + break; + } + case STT_KW_PROTECTED: { + createParserError(parser, "'protected' must be used inside a struct definition"); + break; + } } } if (parser->errors.count > 0) { diff --git a/tests/struct.sols b/tests/struct.sols index d0aa44e..15bc061 100644 --- a/tests/struct.sols +++ b/tests/struct.sols @@ -1,10 +1,15 @@ struct Person { - name = "" - age = 0 + protected name = "" + private age = 0 + def greet() string { return "Hello, " + self.name + "!" } + def dance() string { + return "Dancing..." + } + constructor(string name, int age) { self.name = name self.age = age @@ -20,3 +25,8 @@ max = Person("Max", 16) puts max puts max.greet() +puts max.name +// puts max.age (causes compile time error, age is private) +// max.name = "Maximilian" (causes compile time error, name is protected) + +max.dance()