-
-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/add enum support #19
base: main
Are you sure you want to change the base?
Changes from all commits
436fdac
f2845d3
ea1586d
04269a4
cd6bc73
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,6 +55,7 @@ export enum ASTType | |
templateDef, | ||
functionDef, | ||
operatorDef, | ||
enumDef, | ||
visibility, | ||
block | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -68,6 +68,8 @@ import | |
ASTVisibility, | ||
ASTParams, | ||
ASTReturnType, | ||
ASTEnum, | ||
ASTEnumMember, | ||
ASTTemplate, | ||
ASTFunction, | ||
ASTOperator, | ||
|
@@ -94,7 +96,7 @@ function isNodeRelation(node: ASTNode | ASTRel): node is ASTRel | |
|
||
type IdentAndComments = {token: Token, comments: ASTNode[]} | ||
type IdentDef = {type?: ASTIdent, ident: ASTIdent} | ||
type BlockConfig = {allowExtStmt: boolean} | ||
type BlockConfig = {allowExtStmt: boolean, isEnum: boolean} | ||
|
||
export class Parser | ||
{ | ||
|
@@ -1288,7 +1290,7 @@ export class Parser | |
const cond = this.parseLogicExpr() | ||
if (!cond.isValid()) | ||
return cond as Result<undefined, ParsingErrors> | ||
const block = this.parseBlock({allowExtStmt: false}) | ||
const block = this.parseBlock({allowExtStmt: false, isEnum: false}) | ||
if (!block.isDefined()) | ||
return Err('MissingBlock') | ||
else if (block.isErr()) | ||
|
@@ -1309,7 +1311,7 @@ export class Parser | |
const cond = this.parseLogicExpr() | ||
if (!cond.isValid()) | ||
return cond as Result<undefined, ParsingErrors> | ||
const block = this.parseBlock({allowExtStmt: false}) | ||
const block = this.parseBlock({allowExtStmt: false, isEnum: false}) | ||
if (!block.isDefined()) | ||
return Err('MissingBlock') | ||
else if (block.isErr()) | ||
|
@@ -1327,7 +1329,7 @@ export class Parser | |
const match = this.match(TokenType.elseStmt) | ||
if (!match) | ||
return Err('UnreachableState') | ||
const block = this.parseBlock({allowExtStmt: false}) | ||
const block = this.parseBlock({allowExtStmt: false, isEnum: false}) | ||
if (!block.isDefined()) | ||
return Err('MissingBlock') | ||
else if (block.isErr()) | ||
|
@@ -1411,7 +1413,7 @@ export class Parser | |
return Err('MissingRightBracket') | ||
|
||
// Now match the loop body | ||
const block = this.parseBlock({allowExtStmt: false}) | ||
const block = this.parseBlock({allowExtStmt: false, isEnum: false}) | ||
if (!block.isDefined()) | ||
return Err('MissingBlock') | ||
else if (block.isErr()) | ||
|
@@ -1438,7 +1440,7 @@ export class Parser | |
if (cond.isErr()) | ||
return cond | ||
// Now parse the body of the loop | ||
const block = this.parseBlock({allowExtStmt: false}) | ||
const block = this.parseBlock({allowExtStmt: false, isEnum: false}) | ||
if (!block.isDefined()) | ||
return Err('MissingBlock') | ||
else if (block.isErr()) | ||
|
@@ -1695,7 +1697,7 @@ export class Parser | |
if (templateParams.isErr()) | ||
return templateParams | ||
|
||
const block = this.parseBlock({allowExtStmt: true}) | ||
const block = this.parseBlock({allowExtStmt: true, isEnum: false}) | ||
if (!block.isDefined()) | ||
return Err('MissingBlock') | ||
if (block.isErr()) | ||
|
@@ -1714,6 +1716,73 @@ export class Parser | |
return result | ||
} | ||
|
||
parseEnumDef(): Result<ASTNode, ParsingErrors> | ||
{ | ||
const token = this.lexer.token.clone() | ||
const match = this.match(TokenType.enumDef) | ||
if (!match) | ||
return Err('UnreachableState') | ||
|
||
const ident = this.parseIdent() | ||
if (!ident.isDefined()) | ||
return Err('InvalidTokenSequence') | ||
if (ident.isErr() || ident.isInvalid()) | ||
return ident | ||
|
||
const enumName = ident.val | ||
// If the symbol's already in the table but is not a function symbol, that's an error | ||
if (enumName.symbol && !enumName.symbol.type.mask(SymbolTypes.enum)) | ||
return Err('SymbolAlreadyDefined') | ||
enumName.symbol = new MangroveSymbol(enumName.value, new SymbolType(SymbolTypes.enum | SymbolTypes.type)) | ||
this.symbolTable.insert(enumName.symbol) | ||
|
||
const block = this.parseBlock({allowExtStmt: false, isEnum: true}) | ||
if (!block.isDefined()) | ||
return Err('InvalidTokenSequence') | ||
if (block.isErr() || block.isInvalid()) | ||
return block | ||
const astBlock = block.val as ASTBlock | ||
const enumMembers = astBlock.statements | ||
const valuesValid = enumMembers.every(value => value.valid) | ||
if (enumMembers && !valuesValid) | ||
this.symbolTable.pop(this) | ||
return Ok(new ASTEnum(token, enumName, enumMembers as ASTEnumMember[])) | ||
} | ||
|
||
parseEnumMember(): Result<ASTEnumMember, ParsingErrors> | ||
{ | ||
let value | ||
const token = this.lexer.token | ||
if (token.typeIsOneOf(TokenType.comma)) | ||
this.lexer.next() | ||
this.skipWhite() | ||
const ident = this.parseIdent() | ||
if (!ident.isDefined()) | ||
return Err('InvalidTokenSequence') | ||
if (ident.isErr() || ident.isInvalid()) | ||
return ident | ||
// Parse the actual enum value | ||
this.skipWhite() | ||
if (token.typeIsOneOf(TokenType.assignOp)) | ||
{ | ||
this.lexer.next() | ||
this.skipWhite() | ||
value = this.parseInt() | ||
} | ||
|
||
if (value) | ||
{ | ||
if (!value.isDefined) | ||
return Err('InvalidTokenSequence') | ||
if (value.isErr() || value.isInvalid()) | ||
return Err('InvalidAssignment') | ||
|
||
return Ok(new ASTEnumMember(token, ident.val, value.val)) | ||
} | ||
|
||
return Ok(new ASTEnumMember(token, ident.val)) | ||
} | ||
|
||
parseFunctionDef(): Result<ASTNode, ParsingErrors> | ||
{ | ||
const functionToken = this.lexer.token.clone() | ||
|
@@ -1752,7 +1821,7 @@ export class Parser | |
if (returnType.isErr()) | ||
return returnType | ||
|
||
const block = this.parseBlock({allowExtStmt: false}) | ||
const block = this.parseBlock({allowExtStmt: false, isEnum: false}) | ||
if (!block.isDefined()) | ||
return Err('MissingBlock') | ||
if (block.isErr()) | ||
|
@@ -1826,7 +1895,7 @@ export class Parser | |
if (returnType.isErr()) | ||
return returnType | ||
|
||
const block = this.parseBlock({allowExtStmt: false}) | ||
const block = this.parseBlock({allowExtStmt: false, isEnum: false}) | ||
if (!block.isDefined()) | ||
return Err('MissingBlock') | ||
if (block.isErr()) | ||
|
@@ -1854,6 +1923,8 @@ export class Parser | |
const token = this.lexer.token | ||
if (config.allowExtStmt && token.typeIsOneOf(TokenType.visibility)) | ||
return this.parseVisibility() | ||
if (config.isEnum) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should probably check There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So the only token that's considered the enum type is the initial enum keyword, we also can't really force every token inside the enum to be of the enum type as when we parse the value assignments to the enum members they need to have their own typing, seperate to the enum. Since the isEnum config key is set only once we've read the enum keyword and continue to parse the braces we thought this would be fine as no other routes could lead to this being reached and parsing anything else incorrectly, let us know if you have any thoughts on this one There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It comes down to: Do we want to only allow enums to contain enumeration values, or do we want to allow them to contain conditional logic, visibility statements, functions, operator overloads, etc? One of the biggest problems C++ has always had with its enumerations (after fixing the scope problem) is there's no way to directly attach functionality to the type such as helpers to string-ify the enumeration values, and operators to help build complex values from enumerated values. If you agree that can make sense here, and given this current setup does allow visibility statements already, then we think checking for TokenType.enumDef is correct, otherwise we agree with the restriction |
||
return this.parseEnumMember() | ||
if (token.typeIsOneOf(TokenType.fromStmt)) | ||
return this.parseImportStmt() | ||
if (token.typeIsOneOf(TokenType.ifStmt)) | ||
|
@@ -1862,6 +1933,8 @@ export class Parser | |
return this.parseForStmt() | ||
if (token.typeIsOneOf(TokenType.whileStmt)) | ||
return this.parseWhileStmt() | ||
if (token.typeIsOneOf(TokenType.enumDef)) | ||
return this.parseEnumDef() | ||
|
||
const stmt = this.parseDefine() | ||
if (stmt.isInvalid()) | ||
|
@@ -1940,7 +2013,7 @@ export class Parser | |
const nodes = this.skipWhite() | ||
while (!token.typeIsOneOf(TokenType.eof)) | ||
{ | ||
const stmt = this.parseStatement({allowExtStmt: true}) | ||
const stmt = this.parseStatement({allowExtStmt: true, isEnum: false}) | ||
if (!stmt.isValid() && this.haveIdent) | ||
{ | ||
const ident = this.ident as ASTIdent | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
class
isstruct
above as while the keyword isclass
, the language defines them as structures-with-members so internally we've been usingstruct