implements PR-19.3 parser and ast globals lifecycle markers
This commit is contained in:
parent
b190227b53
commit
738eea71ee
@ -113,18 +113,26 @@ public final class PbsAst {
|
|||||||
ErrorDecl,
|
ErrorDecl,
|
||||||
EnumDecl,
|
EnumDecl,
|
||||||
CallbackDecl,
|
CallbackDecl,
|
||||||
|
GlobalDecl,
|
||||||
ConstDecl,
|
ConstDecl,
|
||||||
ImplementsDecl,
|
ImplementsDecl,
|
||||||
InvalidDecl {
|
InvalidDecl {
|
||||||
Span span();
|
Span span();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum LifecycleMarker {
|
||||||
|
NONE,
|
||||||
|
INIT,
|
||||||
|
FRAME
|
||||||
|
}
|
||||||
|
|
||||||
public record FunctionDecl(
|
public record FunctionDecl(
|
||||||
String name,
|
String name,
|
||||||
ReadOnlyList<Parameter> parameters,
|
ReadOnlyList<Parameter> parameters,
|
||||||
ReturnKind returnKind,
|
ReturnKind returnKind,
|
||||||
TypeRef returnType,
|
TypeRef returnType,
|
||||||
TypeRef resultErrorType,
|
TypeRef resultErrorType,
|
||||||
|
LifecycleMarker lifecycleMarker,
|
||||||
Block body,
|
Block body,
|
||||||
Span span) implements TopDecl {
|
Span span) implements TopDecl {
|
||||||
}
|
}
|
||||||
@ -185,6 +193,13 @@ public final class PbsAst {
|
|||||||
Span span) implements TopDecl {
|
Span span) implements TopDecl {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public record GlobalDecl(
|
||||||
|
String name,
|
||||||
|
TypeRef explicitType,
|
||||||
|
Expression initializer,
|
||||||
|
Span span) implements TopDecl {
|
||||||
|
}
|
||||||
|
|
||||||
public record ConstDecl(
|
public record ConstDecl(
|
||||||
String name,
|
String name,
|
||||||
TypeRef explicitType,
|
TypeRef explicitType,
|
||||||
@ -637,6 +652,7 @@ public final class PbsAst {
|
|||||||
BarrelErrorItem,
|
BarrelErrorItem,
|
||||||
BarrelEnumItem,
|
BarrelEnumItem,
|
||||||
BarrelServiceItem,
|
BarrelServiceItem,
|
||||||
|
BarrelGlobalItem,
|
||||||
BarrelConstItem,
|
BarrelConstItem,
|
||||||
BarrelCallbackItem {
|
BarrelCallbackItem {
|
||||||
Span span();
|
Span span();
|
||||||
@ -693,6 +709,12 @@ public final class PbsAst {
|
|||||||
Span span) implements BarrelItem {
|
Span span) implements BarrelItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public record BarrelGlobalItem(
|
||||||
|
Visibility visibility,
|
||||||
|
String name,
|
||||||
|
Span span) implements BarrelItem {
|
||||||
|
}
|
||||||
|
|
||||||
public record BarrelConstItem(
|
public record BarrelConstItem(
|
||||||
Visibility visibility,
|
Visibility visibility,
|
||||||
String name,
|
String name,
|
||||||
|
|||||||
@ -316,6 +316,7 @@ public final class PbsLexer {
|
|||||||
map.put("ctor", PbsTokenKind.CTOR);
|
map.put("ctor", PbsTokenKind.CTOR);
|
||||||
map.put("let", PbsTokenKind.LET);
|
map.put("let", PbsTokenKind.LET);
|
||||||
map.put("const", PbsTokenKind.CONST);
|
map.put("const", PbsTokenKind.CONST);
|
||||||
|
map.put("global", PbsTokenKind.GLOBAL);
|
||||||
map.put("declare", PbsTokenKind.DECLARE);
|
map.put("declare", PbsTokenKind.DECLARE);
|
||||||
map.put("struct", PbsTokenKind.STRUCT);
|
map.put("struct", PbsTokenKind.STRUCT);
|
||||||
map.put("contract", PbsTokenKind.CONTRACT);
|
map.put("contract", PbsTokenKind.CONTRACT);
|
||||||
|
|||||||
@ -39,6 +39,7 @@ public enum PbsTokenKind {
|
|||||||
DECLARE,
|
DECLARE,
|
||||||
LET,
|
LET,
|
||||||
CONST,
|
CONST,
|
||||||
|
GLOBAL,
|
||||||
STRUCT,
|
STRUCT,
|
||||||
CONTRACT,
|
CONTRACT,
|
||||||
ERROR,
|
ERROR,
|
||||||
|
|||||||
@ -274,6 +274,8 @@ public final class PbsModuleVisibilityValidator {
|
|||||||
registerNonFunctionDeclaration(declarations, NonFunctionKind.ENUM, enumDecl.name(), enumDecl.span(), nameTable);
|
registerNonFunctionDeclaration(declarations, NonFunctionKind.ENUM, enumDecl.name(), enumDecl.span(), nameTable);
|
||||||
} else if (topDecl instanceof PbsAst.ServiceDecl serviceDecl) {
|
} else if (topDecl instanceof PbsAst.ServiceDecl serviceDecl) {
|
||||||
registerNonFunctionDeclaration(declarations, NonFunctionKind.SERVICE, serviceDecl.name(), serviceDecl.span(), nameTable);
|
registerNonFunctionDeclaration(declarations, NonFunctionKind.SERVICE, serviceDecl.name(), serviceDecl.span(), nameTable);
|
||||||
|
} else if (topDecl instanceof PbsAst.GlobalDecl globalDecl) {
|
||||||
|
registerNonFunctionDeclaration(declarations, NonFunctionKind.GLOBAL, globalDecl.name(), globalDecl.span(), nameTable);
|
||||||
} else if (topDecl instanceof PbsAst.ConstDecl constDecl) {
|
} else if (topDecl instanceof PbsAst.ConstDecl constDecl) {
|
||||||
registerNonFunctionDeclaration(declarations, NonFunctionKind.CONST, constDecl.name(), constDecl.span(), nameTable);
|
registerNonFunctionDeclaration(declarations, NonFunctionKind.CONST, constDecl.name(), constDecl.span(), nameTable);
|
||||||
} else if (topDecl instanceof PbsAst.CallbackDecl callbackDecl) {
|
} else if (topDecl instanceof PbsAst.CallbackDecl callbackDecl) {
|
||||||
@ -356,6 +358,9 @@ public final class PbsModuleVisibilityValidator {
|
|||||||
if (item instanceof PbsAst.BarrelServiceItem) {
|
if (item instanceof PbsAst.BarrelServiceItem) {
|
||||||
return NonFunctionKind.SERVICE;
|
return NonFunctionKind.SERVICE;
|
||||||
}
|
}
|
||||||
|
if (item instanceof PbsAst.BarrelGlobalItem) {
|
||||||
|
return NonFunctionKind.GLOBAL;
|
||||||
|
}
|
||||||
if (item instanceof PbsAst.BarrelConstItem) {
|
if (item instanceof PbsAst.BarrelConstItem) {
|
||||||
return NonFunctionKind.CONST;
|
return NonFunctionKind.CONST;
|
||||||
}
|
}
|
||||||
@ -384,6 +389,9 @@ public final class PbsModuleVisibilityValidator {
|
|||||||
if (item instanceof PbsAst.BarrelServiceItem serviceItem) {
|
if (item instanceof PbsAst.BarrelServiceItem serviceItem) {
|
||||||
return serviceItem.name();
|
return serviceItem.name();
|
||||||
}
|
}
|
||||||
|
if (item instanceof PbsAst.BarrelGlobalItem globalItem) {
|
||||||
|
return globalItem.name();
|
||||||
|
}
|
||||||
if (item instanceof PbsAst.BarrelConstItem constItem) {
|
if (item instanceof PbsAst.BarrelConstItem constItem) {
|
||||||
return constItem.name();
|
return constItem.name();
|
||||||
}
|
}
|
||||||
@ -412,6 +420,9 @@ public final class PbsModuleVisibilityValidator {
|
|||||||
if (item instanceof PbsAst.BarrelServiceItem serviceItem) {
|
if (item instanceof PbsAst.BarrelServiceItem serviceItem) {
|
||||||
return serviceItem.visibility();
|
return serviceItem.visibility();
|
||||||
}
|
}
|
||||||
|
if (item instanceof PbsAst.BarrelGlobalItem globalItem) {
|
||||||
|
return globalItem.visibility();
|
||||||
|
}
|
||||||
if (item instanceof PbsAst.BarrelConstItem constItem) {
|
if (item instanceof PbsAst.BarrelConstItem constItem) {
|
||||||
return constItem.visibility();
|
return constItem.visibility();
|
||||||
}
|
}
|
||||||
@ -469,6 +480,7 @@ public final class PbsModuleVisibilityValidator {
|
|||||||
ERROR,
|
ERROR,
|
||||||
ENUM,
|
ENUM,
|
||||||
SERVICE,
|
SERVICE,
|
||||||
|
GLOBAL,
|
||||||
CONST,
|
CONST,
|
||||||
CALLBACK
|
CALLBACK
|
||||||
}
|
}
|
||||||
|
|||||||
@ -98,6 +98,9 @@ public final class PbsBarrelParser {
|
|||||||
if (cursor.match(PbsTokenKind.SERVICE)) {
|
if (cursor.match(PbsTokenKind.SERVICE)) {
|
||||||
return parseSimpleItem(visibility, visibilityToken, PbsTokenKind.SERVICE);
|
return parseSimpleItem(visibility, visibilityToken, PbsTokenKind.SERVICE);
|
||||||
}
|
}
|
||||||
|
if (cursor.match(PbsTokenKind.GLOBAL)) {
|
||||||
|
return parseSimpleItem(visibility, visibilityToken, PbsTokenKind.GLOBAL);
|
||||||
|
}
|
||||||
if (cursor.match(PbsTokenKind.CONST)) {
|
if (cursor.match(PbsTokenKind.CONST)) {
|
||||||
return parseSimpleItem(visibility, visibilityToken, PbsTokenKind.CONST);
|
return parseSimpleItem(visibility, visibilityToken, PbsTokenKind.CONST);
|
||||||
}
|
}
|
||||||
@ -126,6 +129,7 @@ public final class PbsBarrelParser {
|
|||||||
case ERROR -> new PbsAst.BarrelErrorItem(visibility, name.lexeme(), itemSpan);
|
case ERROR -> new PbsAst.BarrelErrorItem(visibility, name.lexeme(), itemSpan);
|
||||||
case ENUM -> new PbsAst.BarrelEnumItem(visibility, name.lexeme(), itemSpan);
|
case ENUM -> new PbsAst.BarrelEnumItem(visibility, name.lexeme(), itemSpan);
|
||||||
case SERVICE -> new PbsAst.BarrelServiceItem(visibility, name.lexeme(), itemSpan);
|
case SERVICE -> new PbsAst.BarrelServiceItem(visibility, name.lexeme(), itemSpan);
|
||||||
|
case GLOBAL -> new PbsAst.BarrelGlobalItem(visibility, name.lexeme(), itemSpan);
|
||||||
case CONST -> new PbsAst.BarrelConstItem(visibility, name.lexeme(), itemSpan);
|
case CONST -> new PbsAst.BarrelConstItem(visibility, name.lexeme(), itemSpan);
|
||||||
case CALLBACK -> new PbsAst.BarrelCallbackItem(visibility, name.lexeme(), itemSpan);
|
case CALLBACK -> new PbsAst.BarrelCallbackItem(visibility, name.lexeme(), itemSpan);
|
||||||
default -> null;
|
default -> null;
|
||||||
|
|||||||
@ -131,6 +131,10 @@ final class PbsDeclarationParser {
|
|||||||
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "callback declarations");
|
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "callback declarations");
|
||||||
return parseCallbackDeclaration(declareToken);
|
return parseCallbackDeclaration(declareToken);
|
||||||
}
|
}
|
||||||
|
if (cursor.match(PbsTokenKind.GLOBAL)) {
|
||||||
|
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "global declarations");
|
||||||
|
return parseGlobalDeclaration(declareToken);
|
||||||
|
}
|
||||||
if (cursor.match(PbsTokenKind.CONST)) {
|
if (cursor.match(PbsTokenKind.CONST)) {
|
||||||
return parseConstDeclaration(declareToken, pendingAttributes);
|
return parseConstDeclaration(declareToken, pendingAttributes);
|
||||||
}
|
}
|
||||||
@ -166,8 +170,10 @@ final class PbsDeclarationParser {
|
|||||||
return new PbsAst.InvalidDecl("invalid declare form", span(declareToken.start(), token.end()));
|
return new PbsAst.InvalidDecl("invalid declare form", span(declareToken.start(), token.end()));
|
||||||
}
|
}
|
||||||
|
|
||||||
PbsAst.FunctionDecl parseFunction(final PbsToken fnToken) {
|
PbsAst.FunctionDecl parseFunction(
|
||||||
return parseFunctionLike(fnToken);
|
final PbsToken fnToken,
|
||||||
|
final ReadOnlyList<PbsAst.Attribute> pendingAttributes) {
|
||||||
|
return parseFunctionLike(fnToken, pendingAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
PbsAst.ImplementsDecl parseImplementsDeclaration(final PbsToken implementsToken) {
|
PbsAst.ImplementsDecl parseImplementsDeclaration(final PbsToken implementsToken) {
|
||||||
@ -188,7 +194,7 @@ final class PbsDeclarationParser {
|
|||||||
cursor.advance();
|
cursor.advance();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
methods.add(parseFunctionLike(cursor.previous()));
|
methods.add(parseFunctionLike(cursor.previous(), ReadOnlyList.empty()));
|
||||||
}
|
}
|
||||||
end = consume(PbsTokenKind.RIGHT_BRACE, "Expected '}' to end implements body").end();
|
end = consume(PbsTokenKind.RIGHT_BRACE, "Expected '}' to end implements body").end();
|
||||||
} else {
|
} else {
|
||||||
@ -377,7 +383,7 @@ final class PbsDeclarationParser {
|
|||||||
final var ctors = new ArrayList<PbsAst.CtorDecl>();
|
final var ctors = new ArrayList<PbsAst.CtorDecl>();
|
||||||
while (!cursor.check(PbsTokenKind.RIGHT_BRACE) && !cursor.isAtEnd()) {
|
while (!cursor.check(PbsTokenKind.RIGHT_BRACE) && !cursor.isAtEnd()) {
|
||||||
if (cursor.match(PbsTokenKind.FN)) {
|
if (cursor.match(PbsTokenKind.FN)) {
|
||||||
methods.add(parseFunctionLike(cursor.previous()));
|
methods.add(parseFunctionLike(cursor.previous(), ReadOnlyList.empty()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (cursor.match(PbsTokenKind.CTOR)) {
|
if (cursor.match(PbsTokenKind.CTOR)) {
|
||||||
@ -441,7 +447,7 @@ final class PbsDeclarationParser {
|
|||||||
cursor.advance();
|
cursor.advance();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
methods.add(parseFunctionLike(cursor.previous()));
|
methods.add(parseFunctionLike(cursor.previous(), ReadOnlyList.empty()));
|
||||||
}
|
}
|
||||||
final var rightBrace = consume(PbsTokenKind.RIGHT_BRACE, "Expected '}' to end service body");
|
final var rightBrace = consume(PbsTokenKind.RIGHT_BRACE, "Expected '}' to end service body");
|
||||||
return new PbsAst.ServiceDecl(name.lexeme(), ReadOnlyList.wrap(methods), span(declareToken.start(), rightBrace.end()));
|
return new PbsAst.ServiceDecl(name.lexeme(), ReadOnlyList.wrap(methods), span(declareToken.start(), rightBrace.end()));
|
||||||
@ -519,7 +525,10 @@ final class PbsDeclarationParser {
|
|||||||
return new PbsAst.EnumDecl(name.lexeme(), ReadOnlyList.wrap(cases), span(declareToken.start(), Math.max(rightParen.end(), semicolon.end())));
|
return new PbsAst.EnumDecl(name.lexeme(), ReadOnlyList.wrap(cases), span(declareToken.start(), Math.max(rightParen.end(), semicolon.end())));
|
||||||
}
|
}
|
||||||
|
|
||||||
private PbsAst.FunctionDecl parseFunctionLike(final PbsToken fnToken) {
|
private PbsAst.FunctionDecl parseFunctionLike(
|
||||||
|
final PbsToken fnToken,
|
||||||
|
final ReadOnlyList<PbsAst.Attribute> pendingAttributes) {
|
||||||
|
final var lifecycleMarker = validateAndResolveLifecycleMarker(pendingAttributes);
|
||||||
final var name = consumeCallableName();
|
final var name = consumeCallableName();
|
||||||
consume(PbsTokenKind.LEFT_PAREN, "Expected '(' after function name");
|
consume(PbsTokenKind.LEFT_PAREN, "Expected '(' after function name");
|
||||||
final var parameters = typeParser.parseParametersUntilRightParen();
|
final var parameters = typeParser.parseParametersUntilRightParen();
|
||||||
@ -534,6 +543,7 @@ final class PbsDeclarationParser {
|
|||||||
returnSpec.kind(),
|
returnSpec.kind(),
|
||||||
returnSpec.returnType(),
|
returnSpec.returnType(),
|
||||||
returnSpec.resultErrorType(),
|
returnSpec.resultErrorType(),
|
||||||
|
lifecycleMarker,
|
||||||
body,
|
body,
|
||||||
span(fnToken.start(), body.span().getEnd()));
|
span(fnToken.start(), body.span().getEnd()));
|
||||||
}
|
}
|
||||||
@ -614,6 +624,20 @@ final class PbsDeclarationParser {
|
|||||||
span(declareToken.start(), semicolon.end()));
|
span(declareToken.start(), semicolon.end()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PbsAst.GlobalDecl parseGlobalDeclaration(final PbsToken declareToken) {
|
||||||
|
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected global name");
|
||||||
|
consume(PbsTokenKind.COLON, "Expected ':' after global name");
|
||||||
|
final var explicitType = typeParser.parseTypeRef();
|
||||||
|
consume(PbsTokenKind.EQUAL, "Expected '=' after global type annotation");
|
||||||
|
final var initializer = exprParser.parseExpression();
|
||||||
|
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after global declaration");
|
||||||
|
return new PbsAst.GlobalDecl(
|
||||||
|
name.lexeme(),
|
||||||
|
explicitType,
|
||||||
|
initializer,
|
||||||
|
span(declareToken.start(), semicolon.end()));
|
||||||
|
}
|
||||||
|
|
||||||
private PbsAst.Block parseBlock() {
|
private PbsAst.Block parseBlock() {
|
||||||
return blockParserDelegate.parse();
|
return blockParserDelegate.parse();
|
||||||
}
|
}
|
||||||
@ -646,6 +670,43 @@ final class PbsDeclarationParser {
|
|||||||
return context.parseMode() == PbsParser.ParseMode.ORDINARY;
|
return context.parseMode() == PbsParser.ParseMode.ORDINARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PbsAst.LifecycleMarker validateAndResolveLifecycleMarker(
|
||||||
|
final ReadOnlyList<PbsAst.Attribute> attributes) {
|
||||||
|
if (attributes.isEmpty()) {
|
||||||
|
return PbsAst.LifecycleMarker.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isOrdinaryMode()) {
|
||||||
|
reportAttributesNotAllowed(attributes, "Attributes are not allowed before top-level functions");
|
||||||
|
return PbsAst.LifecycleMarker.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
PbsAst.LifecycleMarker marker = PbsAst.LifecycleMarker.NONE;
|
||||||
|
for (final var attribute : attributes) {
|
||||||
|
final PbsAst.LifecycleMarker nextMarker;
|
||||||
|
if ("Init".equals(attribute.name())) {
|
||||||
|
nextMarker = PbsAst.LifecycleMarker.INIT;
|
||||||
|
} else if ("Frame".equals(attribute.name())) {
|
||||||
|
nextMarker = PbsAst.LifecycleMarker.FRAME;
|
||||||
|
} else {
|
||||||
|
reportAttributesNotAllowed(attributes, "Only marker attributes [Init] and [Frame] are allowed before top-level functions");
|
||||||
|
return PbsAst.LifecycleMarker.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!attribute.arguments().isEmpty()) {
|
||||||
|
reportAttributesNotAllowed(attributes, "Lifecycle markers do not accept arguments");
|
||||||
|
return PbsAst.LifecycleMarker.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (marker != PbsAst.LifecycleMarker.NONE && marker != nextMarker) {
|
||||||
|
reportAttributesNotAllowed(attributes, "Top-level functions cannot combine [Init] and [Frame]");
|
||||||
|
return PbsAst.LifecycleMarker.NONE;
|
||||||
|
}
|
||||||
|
marker = nextMarker;
|
||||||
|
}
|
||||||
|
return marker;
|
||||||
|
}
|
||||||
|
|
||||||
private PbsToken consume(final PbsTokenKind kind, final String message) {
|
private PbsToken consume(final PbsTokenKind kind, final String message) {
|
||||||
if (cursor.check(kind)) {
|
if (cursor.check(kind)) {
|
||||||
return cursor.advance();
|
return cursor.advance();
|
||||||
|
|||||||
@ -30,12 +30,6 @@ final class PbsTopLevelParser {
|
|||||||
var pendingAttributes = ReadOnlyList.<PbsAst.Attribute>empty();
|
var pendingAttributes = ReadOnlyList.<PbsAst.Attribute>empty();
|
||||||
if (cursor.check(PbsTokenKind.LEFT_BRACKET)) {
|
if (cursor.check(PbsTokenKind.LEFT_BRACKET)) {
|
||||||
pendingAttributes = attributeParser.parseAttributeList();
|
pendingAttributes = attributeParser.parseAttributeList();
|
||||||
if (isOrdinaryMode()) {
|
|
||||||
reportAttributesNotAllowed(
|
|
||||||
pendingAttributes,
|
|
||||||
"Attributes are not allowed in ordinary .pbs source modules");
|
|
||||||
pendingAttributes = ReadOnlyList.empty();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cursor.match(PbsTokenKind.IMPORT)) {
|
if (cursor.match(PbsTokenKind.IMPORT)) {
|
||||||
@ -45,8 +39,7 @@ final class PbsTopLevelParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cursor.match(PbsTokenKind.FN)) {
|
if (cursor.match(PbsTokenKind.FN)) {
|
||||||
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "top-level functions");
|
topDecls.add(declarationParser.parseFunction(cursor.previous(), pendingAttributes));
|
||||||
topDecls.add(declarationParser.parseFunction(cursor.previous()));
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,7 @@ class PbsBarrelParserTest {
|
|||||||
pub fn sum(a: int, b: int) -> int;
|
pub fn sum(a: int, b: int) -> int;
|
||||||
mod fn run(state: optional int) -> result<Err> (value: int, meta: float);
|
mod fn run(state: optional int) -> result<Err> (value: int, meta: float);
|
||||||
pub struct Vec2;
|
pub struct Vec2;
|
||||||
|
mod global Palette;
|
||||||
pub callback Tick;
|
pub callback Tick;
|
||||||
""";
|
""";
|
||||||
|
|
||||||
@ -25,7 +26,7 @@ class PbsBarrelParserTest {
|
|||||||
final var barrel = PbsBarrelParser.parse(tokens, fileId, diagnostics);
|
final var barrel = PbsBarrelParser.parse(tokens, fileId, diagnostics);
|
||||||
|
|
||||||
assertTrue(diagnostics.isEmpty(), "Valid barrel should parse without diagnostics");
|
assertTrue(diagnostics.isEmpty(), "Valid barrel should parse without diagnostics");
|
||||||
assertEquals(4, barrel.items().size());
|
assertEquals(5, barrel.items().size());
|
||||||
|
|
||||||
final var sum = assertInstanceOf(PbsAst.BarrelFunctionItem.class, barrel.items().get(0));
|
final var sum = assertInstanceOf(PbsAst.BarrelFunctionItem.class, barrel.items().get(0));
|
||||||
assertEquals("sum", sum.name());
|
assertEquals("sum", sum.name());
|
||||||
@ -39,7 +40,8 @@ class PbsBarrelParserTest {
|
|||||||
assertEquals(PbsAst.TypeRefKind.NAMED_TUPLE, run.returnType().kind());
|
assertEquals(PbsAst.TypeRefKind.NAMED_TUPLE, run.returnType().kind());
|
||||||
|
|
||||||
assertInstanceOf(PbsAst.BarrelStructItem.class, barrel.items().get(2));
|
assertInstanceOf(PbsAst.BarrelStructItem.class, barrel.items().get(2));
|
||||||
assertInstanceOf(PbsAst.BarrelCallbackItem.class, barrel.items().get(3));
|
assertInstanceOf(PbsAst.BarrelGlobalItem.class, barrel.items().get(3));
|
||||||
|
assertInstanceOf(PbsAst.BarrelCallbackItem.class, barrel.items().get(4));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@ -75,12 +75,17 @@ class PbsParserTest {
|
|||||||
void shouldParseDeclarationFamiliesWithShape() {
|
void shouldParseDeclarationFamiliesWithShape() {
|
||||||
final var source = """
|
final var source = """
|
||||||
fn f() -> int { return 1; }
|
fn f() -> int { return 1; }
|
||||||
|
[Init]
|
||||||
|
fn init() -> void { return; }
|
||||||
|
[Frame]
|
||||||
|
fn frame() -> void { return; }
|
||||||
declare struct S(pub mut x: int, y: optional int) { fn run() -> void { return; } ctor make(x: int) { return; } }
|
declare struct S(pub mut x: int, y: optional int) { fn run() -> void { return; } ctor make(x: int) { return; } }
|
||||||
declare contract C { fn run(x: int) -> int; }
|
declare contract C { fn run(x: int) -> int; }
|
||||||
declare service Game { fn tick(x: int) -> int { return x; } }
|
declare service Game { fn tick(x: int) -> int { return x; } }
|
||||||
declare error Err { Fail; Crash; }
|
declare error Err { Fail; Crash; }
|
||||||
declare enum Mode(Idle = 0, Run = 1);
|
declare enum Mode(Idle = 0, Run = 1);
|
||||||
declare callback TickCb(x: int) -> result<Err> int;
|
declare callback TickCb(x: int) -> result<Err> int;
|
||||||
|
declare global STATE: int = 10;
|
||||||
declare const LIMIT: int = 10;
|
declare const LIMIT: int = 10;
|
||||||
implements C for S using s { fn run(x: int) -> int { return x; } }
|
implements C for S using s { fn run(x: int) -> int { return x; } }
|
||||||
""";
|
""";
|
||||||
@ -91,32 +96,40 @@ class PbsParserTest {
|
|||||||
final PbsAst.File ast = PbsParser.parse(tokens, fileId, diagnostics);
|
final PbsAst.File ast = PbsParser.parse(tokens, fileId, diagnostics);
|
||||||
|
|
||||||
assertTrue(diagnostics.isEmpty(), "Parser should represent declaration families with expected shapes");
|
assertTrue(diagnostics.isEmpty(), "Parser should represent declaration families with expected shapes");
|
||||||
assertEquals(9, ast.topDecls().size());
|
assertEquals(12, ast.topDecls().size());
|
||||||
|
|
||||||
final var structDecl = assertInstanceOf(PbsAst.StructDecl.class, ast.topDecls().get(1));
|
final var initDecl = assertInstanceOf(PbsAst.FunctionDecl.class, ast.topDecls().get(1));
|
||||||
|
assertEquals(PbsAst.LifecycleMarker.INIT, initDecl.lifecycleMarker());
|
||||||
|
|
||||||
|
final var frameDecl = assertInstanceOf(PbsAst.FunctionDecl.class, ast.topDecls().get(2));
|
||||||
|
assertEquals(PbsAst.LifecycleMarker.FRAME, frameDecl.lifecycleMarker());
|
||||||
|
|
||||||
|
final var structDecl = assertInstanceOf(PbsAst.StructDecl.class, ast.topDecls().get(3));
|
||||||
assertEquals(2, structDecl.fields().size());
|
assertEquals(2, structDecl.fields().size());
|
||||||
assertTrue(structDecl.hasBody());
|
assertTrue(structDecl.hasBody());
|
||||||
assertEquals(1, structDecl.methods().size());
|
assertEquals(1, structDecl.methods().size());
|
||||||
assertEquals(1, structDecl.ctors().size());
|
assertEquals(1, structDecl.ctors().size());
|
||||||
|
|
||||||
final var contractDecl = assertInstanceOf(PbsAst.ContractDecl.class, ast.topDecls().get(2));
|
final var contractDecl = assertInstanceOf(PbsAst.ContractDecl.class, ast.topDecls().get(4));
|
||||||
assertEquals(1, contractDecl.signatures().size());
|
assertEquals(1, contractDecl.signatures().size());
|
||||||
|
|
||||||
final var serviceDecl = assertInstanceOf(PbsAst.ServiceDecl.class, ast.topDecls().get(3));
|
final var serviceDecl = assertInstanceOf(PbsAst.ServiceDecl.class, ast.topDecls().get(5));
|
||||||
assertEquals(1, serviceDecl.methods().size());
|
assertEquals(1, serviceDecl.methods().size());
|
||||||
|
|
||||||
final var errorDecl = assertInstanceOf(PbsAst.ErrorDecl.class, ast.topDecls().get(4));
|
final var errorDecl = assertInstanceOf(PbsAst.ErrorDecl.class, ast.topDecls().get(6));
|
||||||
assertEquals(2, errorDecl.cases().size());
|
assertEquals(2, errorDecl.cases().size());
|
||||||
|
|
||||||
final var enumDecl = assertInstanceOf(PbsAst.EnumDecl.class, ast.topDecls().get(5));
|
final var enumDecl = assertInstanceOf(PbsAst.EnumDecl.class, ast.topDecls().get(7));
|
||||||
assertEquals(2, enumDecl.cases().size());
|
assertEquals(2, enumDecl.cases().size());
|
||||||
|
|
||||||
final var callbackDecl = assertInstanceOf(PbsAst.CallbackDecl.class, ast.topDecls().get(6));
|
final var callbackDecl = assertInstanceOf(PbsAst.CallbackDecl.class, ast.topDecls().get(8));
|
||||||
assertEquals(PbsAst.ReturnKind.RESULT, callbackDecl.returnKind());
|
assertEquals(PbsAst.ReturnKind.RESULT, callbackDecl.returnKind());
|
||||||
assertEquals("Err", callbackDecl.resultErrorType().name());
|
assertEquals("Err", callbackDecl.resultErrorType().name());
|
||||||
|
|
||||||
assertInstanceOf(PbsAst.ConstDecl.class, ast.topDecls().get(7));
|
final var globalDecl = assertInstanceOf(PbsAst.GlobalDecl.class, ast.topDecls().get(9));
|
||||||
final var implementsDecl = assertInstanceOf(PbsAst.ImplementsDecl.class, ast.topDecls().get(8));
|
assertEquals("STATE", globalDecl.name());
|
||||||
|
assertInstanceOf(PbsAst.ConstDecl.class, ast.topDecls().get(10));
|
||||||
|
final var implementsDecl = assertInstanceOf(PbsAst.ImplementsDecl.class, ast.topDecls().get(11));
|
||||||
assertEquals(1, implementsDecl.methods().size());
|
assertEquals(1, implementsDecl.methods().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,6 +152,26 @@ class PbsParserTest {
|
|||||||
assertTrue(diagnostics.stream().anyMatch(d -> d.getCode().equals(ParseErrors.E_PARSE_ATTRIBUTES_NOT_ALLOWED.name())));
|
assertTrue(diagnostics.stream().anyMatch(d -> d.getCode().equals(ParseErrors.E_PARSE_ATTRIBUTES_NOT_ALLOWED.name())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldParseLifecycleMarkersOnlyOnTopLevelFunctions() {
|
||||||
|
final var source = """
|
||||||
|
[Init]
|
||||||
|
fn init() -> void { return; }
|
||||||
|
[Frame]
|
||||||
|
fn frame() -> void { return; }
|
||||||
|
""";
|
||||||
|
final var diagnostics = DiagnosticSink.empty();
|
||||||
|
final var fileId = new FileId(0);
|
||||||
|
|
||||||
|
final PbsAst.File ast = PbsParser.parse(PbsLexer.lex(source, fileId, diagnostics), fileId, diagnostics);
|
||||||
|
|
||||||
|
assertTrue(diagnostics.isEmpty(), "Lifecycle markers should be accepted on top-level functions");
|
||||||
|
final var initDecl = assertInstanceOf(PbsAst.FunctionDecl.class, ast.topDecls().get(0));
|
||||||
|
final var frameDecl = assertInstanceOf(PbsAst.FunctionDecl.class, ast.topDecls().get(1));
|
||||||
|
assertEquals(PbsAst.LifecycleMarker.INIT, initDecl.lifecycleMarker());
|
||||||
|
assertEquals(PbsAst.LifecycleMarker.FRAME, frameDecl.lifecycleMarker());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldParseServiceAndMemberNamesUsingErrorKeyword() {
|
void shouldParseServiceAndMemberNamesUsingErrorKeyword() {
|
||||||
final var source = """
|
final var source = """
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user