implements PR-10.3
This commit is contained in:
parent
6b85dc33e4
commit
416c4e7a11
@ -0,0 +1,682 @@
|
|||||||
|
package p.studio.compiler.pbs.parser;
|
||||||
|
|
||||||
|
import p.studio.compiler.pbs.ast.PbsAst;
|
||||||
|
import p.studio.compiler.pbs.lexer.PbsToken;
|
||||||
|
import p.studio.compiler.pbs.lexer.PbsTokenKind;
|
||||||
|
import p.studio.compiler.source.Span;
|
||||||
|
import p.studio.compiler.source.diagnostics.RelatedSpan;
|
||||||
|
import p.studio.utilities.structures.ReadOnlyList;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
final class PbsDeclarationParser {
|
||||||
|
private static final boolean REQUIRE_SEMICOLON = true;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
interface BlockParserDelegate {
|
||||||
|
PbsAst.Block parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
interface DeclarationTerminatorDelegate {
|
||||||
|
long consume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
interface TopLevelSynchronizer {
|
||||||
|
void synchronize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final PbsParserContext context;
|
||||||
|
private final PbsTokenCursor cursor;
|
||||||
|
private final PbsExprParser exprParser;
|
||||||
|
private final PbsAttributeParser attributeParser;
|
||||||
|
private final PbsTypeParser typeParser;
|
||||||
|
private final BlockParserDelegate blockParserDelegate;
|
||||||
|
private final DeclarationTerminatorDelegate declarationTerminatorDelegate;
|
||||||
|
private final TopLevelSynchronizer topLevelSynchronizer;
|
||||||
|
|
||||||
|
PbsDeclarationParser(
|
||||||
|
final PbsParserContext context,
|
||||||
|
final PbsExprParser exprParser,
|
||||||
|
final PbsAttributeParser attributeParser,
|
||||||
|
final PbsTypeParser typeParser,
|
||||||
|
final BlockParserDelegate blockParserDelegate,
|
||||||
|
final DeclarationTerminatorDelegate declarationTerminatorDelegate,
|
||||||
|
final TopLevelSynchronizer topLevelSynchronizer) {
|
||||||
|
this.context = context;
|
||||||
|
this.cursor = context.cursor();
|
||||||
|
this.exprParser = exprParser;
|
||||||
|
this.attributeParser = attributeParser;
|
||||||
|
this.typeParser = typeParser;
|
||||||
|
this.blockParserDelegate = blockParserDelegate;
|
||||||
|
this.declarationTerminatorDelegate = declarationTerminatorDelegate;
|
||||||
|
this.topLevelSynchronizer = topLevelSynchronizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
PbsAst.ImportDecl parseImport(final PbsToken importToken) {
|
||||||
|
final var items = new ArrayList<PbsAst.ImportItem>();
|
||||||
|
if (cursor.match(PbsTokenKind.LEFT_BRACE)) {
|
||||||
|
while (!cursor.check(PbsTokenKind.RIGHT_BRACE) && !cursor.isAtEnd()) {
|
||||||
|
if (cursor.match(PbsTokenKind.IDENTIFIER)) {
|
||||||
|
final var itemName = cursor.previous();
|
||||||
|
String alias = null;
|
||||||
|
if (cursor.match(PbsTokenKind.AS)) {
|
||||||
|
alias = consume(PbsTokenKind.IDENTIFIER, "Expected alias identifier after 'as'").lexeme();
|
||||||
|
}
|
||||||
|
items.add(new PbsAst.ImportItem(
|
||||||
|
itemName.lexeme(),
|
||||||
|
alias,
|
||||||
|
span(itemName.start(), cursor.previous().end())));
|
||||||
|
cursor.match(PbsTokenKind.COMMA);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
report(cursor.peek(), ParseErrors.E_PARSE_UNEXPECTED_TOKEN, "Invalid import item");
|
||||||
|
cursor.advance();
|
||||||
|
}
|
||||||
|
consume(PbsTokenKind.RIGHT_BRACE, "Expected '}' in import list");
|
||||||
|
consume(PbsTokenKind.FROM, "Expected 'from' in named import");
|
||||||
|
}
|
||||||
|
|
||||||
|
final var moduleRef = parseModuleRef();
|
||||||
|
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after import");
|
||||||
|
return new PbsAst.ImportDecl(
|
||||||
|
ReadOnlyList.wrap(items),
|
||||||
|
moduleRef,
|
||||||
|
span(importToken.start(), semicolon.end()));
|
||||||
|
}
|
||||||
|
|
||||||
|
PbsAst.ModuleRef parseModuleRef() {
|
||||||
|
final var at = consume(PbsTokenKind.AT, "Expected '@' in module reference");
|
||||||
|
final var project = consume(PbsTokenKind.IDENTIFIER, "Expected project identifier in module reference");
|
||||||
|
consume(PbsTokenKind.COLON, "Expected ':' in module reference");
|
||||||
|
final var segments = new ArrayList<String>();
|
||||||
|
final var firstSegment = consume(PbsTokenKind.IDENTIFIER, "Expected module identifier");
|
||||||
|
segments.add(firstSegment.lexeme());
|
||||||
|
var end = firstSegment.end();
|
||||||
|
while (cursor.match(PbsTokenKind.SLASH)) {
|
||||||
|
final var segment = consume(PbsTokenKind.IDENTIFIER, "Expected module path segment after '/'");
|
||||||
|
segments.add(segment.lexeme());
|
||||||
|
end = segment.end();
|
||||||
|
}
|
||||||
|
return new PbsAst.ModuleRef(project.lexeme(), ReadOnlyList.wrap(segments), span(at.start(), end));
|
||||||
|
}
|
||||||
|
|
||||||
|
PbsAst.TopDecl parseDeclareTopDecl(
|
||||||
|
final PbsToken declareToken,
|
||||||
|
final ReadOnlyList<PbsAst.Attribute> pendingAttributes) {
|
||||||
|
if (cursor.match(PbsTokenKind.STRUCT)) {
|
||||||
|
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "struct declarations");
|
||||||
|
return parseStructDeclaration(declareToken);
|
||||||
|
}
|
||||||
|
if (cursor.match(PbsTokenKind.CONTRACT)) {
|
||||||
|
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "contract declarations");
|
||||||
|
return parseContractDeclaration(declareToken);
|
||||||
|
}
|
||||||
|
if (cursor.match(PbsTokenKind.SERVICE)) {
|
||||||
|
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "service declarations");
|
||||||
|
return parseServiceDeclaration(declareToken);
|
||||||
|
}
|
||||||
|
if (cursor.match(PbsTokenKind.ERROR)) {
|
||||||
|
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "error declarations");
|
||||||
|
return parseErrorDeclaration(declareToken);
|
||||||
|
}
|
||||||
|
if (cursor.match(PbsTokenKind.ENUM)) {
|
||||||
|
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "enum declarations");
|
||||||
|
return parseEnumDeclaration(declareToken);
|
||||||
|
}
|
||||||
|
if (cursor.match(PbsTokenKind.CALLBACK)) {
|
||||||
|
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "callback declarations");
|
||||||
|
return parseCallbackDeclaration(declareToken);
|
||||||
|
}
|
||||||
|
if (cursor.match(PbsTokenKind.CONST)) {
|
||||||
|
return parseConstDeclaration(declareToken, pendingAttributes);
|
||||||
|
}
|
||||||
|
if (cursor.match(PbsTokenKind.HOST)) {
|
||||||
|
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "host declarations");
|
||||||
|
if (isNotInterfaceMode()) {
|
||||||
|
final var end = declarationTerminatorDelegate.consume();
|
||||||
|
report(cursor.previous(), ParseErrors.E_PARSE_RESERVED_DECLARATION,
|
||||||
|
"'declare host' is reserved and not supported in ordinary source modules");
|
||||||
|
return new PbsAst.InvalidDecl("reserved declare host", span(declareToken.start(), end));
|
||||||
|
}
|
||||||
|
return parseHostDeclaration(declareToken);
|
||||||
|
}
|
||||||
|
if (cursor.match(PbsTokenKind.BUILTIN)) {
|
||||||
|
consume(PbsTokenKind.TYPE, "Expected 'type' in 'declare builtin type' declaration");
|
||||||
|
if (isNotInterfaceMode()) {
|
||||||
|
final var end = declarationTerminatorDelegate.consume();
|
||||||
|
report(cursor.previous(), ParseErrors.E_PARSE_RESERVED_DECLARATION,
|
||||||
|
"'declare builtin type' is reserved and not supported in ordinary source modules");
|
||||||
|
return new PbsAst.InvalidDecl("reserved declare builtin type", span(declareToken.start(), end));
|
||||||
|
}
|
||||||
|
return parseBuiltinTypeDeclaration(declareToken, pendingAttributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pendingAttributes.isEmpty()) {
|
||||||
|
reportAttributesNotAllowed(pendingAttributes, "Attributes are not valid for this declaration form");
|
||||||
|
}
|
||||||
|
|
||||||
|
report(cursor.peek(), ParseErrors.E_PARSE_UNEXPECTED_TOKEN,
|
||||||
|
"Expected declaration kind after 'declare'");
|
||||||
|
topLevelSynchronizer.synchronize();
|
||||||
|
final var token = cursor.previous();
|
||||||
|
return new PbsAst.InvalidDecl("invalid declare form", span(declareToken.start(), token.end()));
|
||||||
|
}
|
||||||
|
|
||||||
|
PbsAst.FunctionDecl parseFunction(final PbsToken fnToken) {
|
||||||
|
return parseFunctionLike(fnToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
PbsAst.ImplementsDecl parseImplementsDeclaration(final PbsToken implementsToken) {
|
||||||
|
final var contractName = consume(PbsTokenKind.IDENTIFIER, "Expected contract name in 'implements'");
|
||||||
|
consume(PbsTokenKind.FOR, "Expected 'for' in 'implements' declaration");
|
||||||
|
final var ownerName = consume(PbsTokenKind.IDENTIFIER, "Expected owner name after 'for'");
|
||||||
|
consume(PbsTokenKind.USING, "Expected 'using' in 'implements' declaration");
|
||||||
|
final var binderName = consume(PbsTokenKind.IDENTIFIER, "Expected binder name after 'using'");
|
||||||
|
|
||||||
|
final var methods = new ArrayList<PbsAst.FunctionDecl>();
|
||||||
|
long end;
|
||||||
|
if (cursor.check(PbsTokenKind.LEFT_BRACE)) {
|
||||||
|
cursor.advance();
|
||||||
|
while (!cursor.check(PbsTokenKind.RIGHT_BRACE) && !cursor.isAtEnd()) {
|
||||||
|
if (!cursor.match(PbsTokenKind.FN)) {
|
||||||
|
report(cursor.peek(), ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
||||||
|
"Implements body accepts only function declarations");
|
||||||
|
cursor.advance();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
methods.add(parseFunctionLike(cursor.previous()));
|
||||||
|
}
|
||||||
|
end = consume(PbsTokenKind.RIGHT_BRACE, "Expected '}' to end implements body").end();
|
||||||
|
} else {
|
||||||
|
report(cursor.peek(), ParseErrors.E_PARSE_EXPECTED_TOKEN,
|
||||||
|
"Expected '{' to start 'implements' body");
|
||||||
|
end = declarationTerminatorDelegate.consume();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PbsAst.ImplementsDecl(
|
||||||
|
contractName.lexeme(),
|
||||||
|
ownerName.lexeme(),
|
||||||
|
binderName.lexeme(),
|
||||||
|
ReadOnlyList.wrap(methods),
|
||||||
|
span(implementsToken.start(), end));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PbsAst.HostDecl parseHostDeclaration(final PbsToken declareToken) {
|
||||||
|
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected host declaration name");
|
||||||
|
consume(PbsTokenKind.LEFT_BRACE, "Expected '{' to start host declaration body");
|
||||||
|
final var signatures = new ArrayList<PbsAst.FunctionSignature>();
|
||||||
|
while (!cursor.check(PbsTokenKind.RIGHT_BRACE) && !cursor.isAtEnd()) {
|
||||||
|
var attributes = ReadOnlyList.<PbsAst.Attribute>empty();
|
||||||
|
if (cursor.check(PbsTokenKind.LEFT_BRACKET)) {
|
||||||
|
attributes = attributeParser.parseAttributeList();
|
||||||
|
}
|
||||||
|
if (!cursor.match(PbsTokenKind.FN)) {
|
||||||
|
report(cursor.peek(), ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
||||||
|
"Host declaration body accepts only function signatures");
|
||||||
|
cursor.advance();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
signatures.add(parseFunctionSignature(cursor.previous(), attributes));
|
||||||
|
}
|
||||||
|
final var rightBrace = consume(PbsTokenKind.RIGHT_BRACE, "Expected '}' to end host declaration body");
|
||||||
|
return new PbsAst.HostDecl(
|
||||||
|
name.lexeme(),
|
||||||
|
ReadOnlyList.wrap(signatures),
|
||||||
|
span(declareToken.start(), rightBrace.end()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PbsAst.BuiltinTypeDecl parseBuiltinTypeDeclaration(
|
||||||
|
final PbsToken declareToken,
|
||||||
|
final ReadOnlyList<PbsAst.Attribute> declarationAttributes) {
|
||||||
|
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected builtin type name");
|
||||||
|
consume(PbsTokenKind.LEFT_PAREN, "Expected '(' in builtin type declaration");
|
||||||
|
final var fields = parseBuiltinTypeFields();
|
||||||
|
consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after builtin type fields");
|
||||||
|
|
||||||
|
consume(PbsTokenKind.LEFT_BRACE, "Expected '{' to start builtin type body");
|
||||||
|
final var signatures = new ArrayList<PbsAst.FunctionSignature>();
|
||||||
|
while (!cursor.check(PbsTokenKind.RIGHT_BRACE) && !cursor.isAtEnd()) {
|
||||||
|
var attributes = ReadOnlyList.<PbsAst.Attribute>empty();
|
||||||
|
if (cursor.check(PbsTokenKind.LEFT_BRACKET)) {
|
||||||
|
attributes = attributeParser.parseAttributeList();
|
||||||
|
}
|
||||||
|
if (!cursor.match(PbsTokenKind.FN)) {
|
||||||
|
report(cursor.peek(), ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
||||||
|
"Builtin type body accepts only function signatures");
|
||||||
|
cursor.advance();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
signatures.add(parseFunctionSignature(cursor.previous(), attributes));
|
||||||
|
}
|
||||||
|
final var rightBrace = consume(PbsTokenKind.RIGHT_BRACE, "Expected '}' to end builtin type body");
|
||||||
|
return new PbsAst.BuiltinTypeDecl(
|
||||||
|
name.lexeme(),
|
||||||
|
ReadOnlyList.wrap(fields),
|
||||||
|
ReadOnlyList.wrap(signatures),
|
||||||
|
declarationAttributes,
|
||||||
|
span(declareToken.start(), rightBrace.end()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<PbsAst.BuiltinFieldDecl> parseBuiltinTypeFields() {
|
||||||
|
final var fields = new ArrayList<PbsAst.BuiltinFieldDecl>();
|
||||||
|
if (cursor.check(PbsTokenKind.RIGHT_PAREN)) {
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
final var start = cursor.peek();
|
||||||
|
var attributes = ReadOnlyList.<PbsAst.Attribute>empty();
|
||||||
|
if (cursor.check(PbsTokenKind.LEFT_BRACKET)) {
|
||||||
|
attributes = attributeParser.parseAttributeList();
|
||||||
|
}
|
||||||
|
|
||||||
|
var isPublic = false;
|
||||||
|
if (cursor.match(PbsTokenKind.PUB)) {
|
||||||
|
isPublic = true;
|
||||||
|
if (cursor.match(PbsTokenKind.MUT)) {
|
||||||
|
final var mutToken = cursor.previous();
|
||||||
|
report(mutToken, ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
||||||
|
"'mut' is not allowed in builtin type fields");
|
||||||
|
}
|
||||||
|
} else if (cursor.match(PbsTokenKind.MUT)) {
|
||||||
|
final var mutToken = cursor.previous();
|
||||||
|
report(mutToken, ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
||||||
|
"'mut' is not allowed in builtin type fields");
|
||||||
|
}
|
||||||
|
|
||||||
|
final var fieldName = consume(PbsTokenKind.IDENTIFIER, "Expected builtin field name");
|
||||||
|
consume(PbsTokenKind.COLON, "Expected ':' after builtin field name");
|
||||||
|
final var typeRef = typeParser.parseTypeRef();
|
||||||
|
fields.add(new PbsAst.BuiltinFieldDecl(
|
||||||
|
fieldName.lexeme(),
|
||||||
|
typeRef,
|
||||||
|
isPublic,
|
||||||
|
attributes,
|
||||||
|
span(start.start(), typeRef.span().getEnd())));
|
||||||
|
} while (cursor.match(PbsTokenKind.COMMA) && !cursor.check(PbsTokenKind.RIGHT_PAREN));
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PbsAst.StructDecl parseStructDeclaration(final PbsToken declareToken) {
|
||||||
|
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected struct name");
|
||||||
|
consume(PbsTokenKind.LEFT_PAREN, "Expected '(' in struct declaration");
|
||||||
|
final var fields = parseStructFields();
|
||||||
|
consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after struct fields");
|
||||||
|
|
||||||
|
final var methods = new ArrayList<PbsAst.FunctionDecl>();
|
||||||
|
final var ctors = new ArrayList<PbsAst.CtorDecl>();
|
||||||
|
final boolean hasBody;
|
||||||
|
long end;
|
||||||
|
if (cursor.match(PbsTokenKind.SEMICOLON)) {
|
||||||
|
hasBody = false;
|
||||||
|
end = cursor.previous().end();
|
||||||
|
} else if (cursor.match(PbsTokenKind.LEFT_BRACE)) {
|
||||||
|
hasBody = true;
|
||||||
|
final var bodyParse = parseStructBodyAndConsumeRightBrace(cursor.previous());
|
||||||
|
methods.addAll(bodyParse.methods().asList());
|
||||||
|
ctors.addAll(bodyParse.ctors().asList());
|
||||||
|
end = bodyParse.end();
|
||||||
|
} else {
|
||||||
|
report(cursor.peek(), ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
||||||
|
"Struct declaration must end with ';' or contain a '{...}' body");
|
||||||
|
hasBody = false;
|
||||||
|
end = declarationTerminatorDelegate.consume();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PbsAst.StructDecl(
|
||||||
|
name.lexeme(),
|
||||||
|
ReadOnlyList.wrap(fields),
|
||||||
|
ReadOnlyList.wrap(methods),
|
||||||
|
ReadOnlyList.wrap(ctors),
|
||||||
|
hasBody,
|
||||||
|
span(declareToken.start(), end));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<PbsAst.StructField> parseStructFields() {
|
||||||
|
final var fields = new ArrayList<PbsAst.StructField>();
|
||||||
|
if (cursor.check(PbsTokenKind.RIGHT_PAREN)) {
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
final var start = cursor.peek();
|
||||||
|
var isPublic = false;
|
||||||
|
var isMutable = false;
|
||||||
|
if (cursor.match(PbsTokenKind.PUB)) {
|
||||||
|
isPublic = true;
|
||||||
|
if (cursor.match(PbsTokenKind.MUT)) {
|
||||||
|
isMutable = true;
|
||||||
|
}
|
||||||
|
} else if (cursor.match(PbsTokenKind.MUT)) {
|
||||||
|
final var mutToken = cursor.previous();
|
||||||
|
report(mutToken, ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
||||||
|
"'mut' is only valid immediately after 'pub' in struct fields");
|
||||||
|
}
|
||||||
|
|
||||||
|
final var fieldName = consume(PbsTokenKind.IDENTIFIER, "Expected struct field name");
|
||||||
|
consume(PbsTokenKind.COLON, "Expected ':' after struct field name");
|
||||||
|
final var typeRef = typeParser.parseTypeRef();
|
||||||
|
fields.add(new PbsAst.StructField(
|
||||||
|
fieldName.lexeme(),
|
||||||
|
typeRef,
|
||||||
|
isPublic,
|
||||||
|
isMutable,
|
||||||
|
span(start.start(), typeRef.span().getEnd())));
|
||||||
|
} while (cursor.match(PbsTokenKind.COMMA) && !cursor.check(PbsTokenKind.RIGHT_PAREN));
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
private StructBodyParse parseStructBodyAndConsumeRightBrace(final PbsToken leftBrace) {
|
||||||
|
final var methods = new ArrayList<PbsAst.FunctionDecl>();
|
||||||
|
final var ctors = new ArrayList<PbsAst.CtorDecl>();
|
||||||
|
while (!cursor.check(PbsTokenKind.RIGHT_BRACE) && !cursor.isAtEnd()) {
|
||||||
|
if (cursor.match(PbsTokenKind.FN)) {
|
||||||
|
methods.add(parseFunctionLike(cursor.previous()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cursor.match(PbsTokenKind.CTOR)) {
|
||||||
|
ctors.add(parseCtorDeclarationInBody(cursor.previous()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
report(cursor.peek(), ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
||||||
|
"Struct body accepts only 'fn' methods and 'ctor' declarations");
|
||||||
|
cursor.advance();
|
||||||
|
}
|
||||||
|
final var rightBrace = consume(PbsTokenKind.RIGHT_BRACE, "Expected '}' to end struct body");
|
||||||
|
return new StructBodyParse(ReadOnlyList.wrap(methods), ReadOnlyList.wrap(ctors), rightBrace.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
private PbsAst.CtorDecl parseCtorDeclarationInBody(final PbsToken ctorToken) {
|
||||||
|
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected constructor name");
|
||||||
|
consume(PbsTokenKind.LEFT_PAREN, "Expected '(' after constructor name");
|
||||||
|
final var parameters = typeParser.parseParametersUntilRightParen();
|
||||||
|
consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after constructor parameters");
|
||||||
|
final var body = parseBlock();
|
||||||
|
return new PbsAst.CtorDecl(
|
||||||
|
name.lexeme(),
|
||||||
|
ReadOnlyList.wrap(parameters),
|
||||||
|
body,
|
||||||
|
span(ctorToken.start(), body.span().getEnd()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PbsAst.ContractDecl parseContractDeclaration(final PbsToken declareToken) {
|
||||||
|
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected contract name");
|
||||||
|
consume(PbsTokenKind.LEFT_BRACE, "Expected '{' to start contract body");
|
||||||
|
final var signatures = new ArrayList<PbsAst.FunctionSignature>();
|
||||||
|
while (!cursor.check(PbsTokenKind.RIGHT_BRACE) && !cursor.isAtEnd()) {
|
||||||
|
var attributes = ReadOnlyList.<PbsAst.Attribute>empty();
|
||||||
|
if (cursor.check(PbsTokenKind.LEFT_BRACKET)) {
|
||||||
|
attributes = attributeParser.parseAttributeList();
|
||||||
|
if (isOrdinaryMode()) {
|
||||||
|
reportAttributesNotAllowed(attributes, "Attributes are not allowed in ordinary .pbs source modules");
|
||||||
|
attributes = ReadOnlyList.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!cursor.match(PbsTokenKind.FN)) {
|
||||||
|
report(cursor.peek(), ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
||||||
|
"Contract body accepts only function signatures");
|
||||||
|
cursor.advance();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
signatures.add(parseFunctionSignature(cursor.previous(), attributes));
|
||||||
|
}
|
||||||
|
final var rightBrace = consume(PbsTokenKind.RIGHT_BRACE, "Expected '}' to end contract body");
|
||||||
|
return new PbsAst.ContractDecl(name.lexeme(), ReadOnlyList.wrap(signatures), span(declareToken.start(), rightBrace.end()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PbsAst.ServiceDecl parseServiceDeclaration(final PbsToken declareToken) {
|
||||||
|
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected service name");
|
||||||
|
consume(PbsTokenKind.LEFT_BRACE, "Expected '{' to start service body");
|
||||||
|
final var methods = new ArrayList<PbsAst.FunctionDecl>();
|
||||||
|
while (!cursor.check(PbsTokenKind.RIGHT_BRACE) && !cursor.isAtEnd()) {
|
||||||
|
if (!cursor.match(PbsTokenKind.FN)) {
|
||||||
|
report(cursor.peek(), ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
||||||
|
"Service body accepts only method declarations");
|
||||||
|
cursor.advance();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
methods.add(parseFunctionLike(cursor.previous()));
|
||||||
|
}
|
||||||
|
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()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PbsAst.ErrorDecl parseErrorDeclaration(final PbsToken declareToken) {
|
||||||
|
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected error name");
|
||||||
|
consume(PbsTokenKind.LEFT_BRACE, "Expected '{' to start error body");
|
||||||
|
final var cases = new ArrayList<String>();
|
||||||
|
while (!cursor.check(PbsTokenKind.RIGHT_BRACE) && !cursor.isAtEnd()) {
|
||||||
|
final var caseToken = consume(PbsTokenKind.IDENTIFIER, "Expected error case label");
|
||||||
|
cases.add(caseToken.lexeme());
|
||||||
|
consume(PbsTokenKind.SEMICOLON, "Expected ';' after error case label");
|
||||||
|
}
|
||||||
|
final var rightBrace = consume(PbsTokenKind.RIGHT_BRACE, "Expected '}' to end error body");
|
||||||
|
return new PbsAst.ErrorDecl(name.lexeme(), ReadOnlyList.wrap(cases), span(declareToken.start(), rightBrace.end()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PbsAst.EnumDecl parseEnumDeclaration(final PbsToken declareToken) {
|
||||||
|
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected enum name");
|
||||||
|
consume(PbsTokenKind.LEFT_PAREN, "Expected '(' after enum name");
|
||||||
|
|
||||||
|
final var cases = new ArrayList<PbsAst.EnumCase>();
|
||||||
|
final var caseLabels = new HashMap<String, Span>();
|
||||||
|
final var explicitCaseIds = new HashMap<Long, Span>();
|
||||||
|
var hasExplicitCases = false;
|
||||||
|
var hasImplicitCases = false;
|
||||||
|
if (!cursor.check(PbsTokenKind.RIGHT_PAREN)) {
|
||||||
|
do {
|
||||||
|
final var caseName = consume(PbsTokenKind.IDENTIFIER, "Expected enum case label");
|
||||||
|
final var caseNameSpan = span(caseName.start(), caseName.end());
|
||||||
|
Long explicitValue = null;
|
||||||
|
if (cursor.match(PbsTokenKind.EQUAL)) {
|
||||||
|
final var intToken = consume(PbsTokenKind.INT_LITERAL, "Expected integer literal after '=' in enum case");
|
||||||
|
final var intTokenSpan = span(intToken.start(), intToken.end());
|
||||||
|
explicitValue = parseLongOrNull(intToken.lexeme());
|
||||||
|
hasExplicitCases = true;
|
||||||
|
if (explicitValue == null) {
|
||||||
|
report(intToken, ParseErrors.E_PARSE_INVALID_ENUM_FORM,
|
||||||
|
"Invalid explicit enum identifier");
|
||||||
|
} else {
|
||||||
|
final var firstIdSpan = explicitCaseIds.putIfAbsent(explicitValue, intTokenSpan);
|
||||||
|
if (firstIdSpan != null) {
|
||||||
|
p.studio.compiler.source.diagnostics.Diagnostics.error(context.diagnostics(),
|
||||||
|
ParseErrors.E_PARSE_DUPLICATE_ENUM_CASE_ID.name(),
|
||||||
|
"Duplicate explicit enum identifier '%s'".formatted(explicitValue),
|
||||||
|
intTokenSpan,
|
||||||
|
List.of(new RelatedSpan("First explicit enum identifier is here", firstIdSpan)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hasImplicitCases = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var firstLabelSpan = caseLabels.putIfAbsent(caseName.lexeme(), caseNameSpan);
|
||||||
|
if (firstLabelSpan != null) {
|
||||||
|
p.studio.compiler.source.diagnostics.Diagnostics.error(context.diagnostics(),
|
||||||
|
ParseErrors.E_PARSE_DUPLICATE_ENUM_CASE_LABEL.name(),
|
||||||
|
"Duplicate enum case label '%s'".formatted(caseName.lexeme()),
|
||||||
|
caseNameSpan,
|
||||||
|
List.of(new RelatedSpan("First enum case label is here", firstLabelSpan)));
|
||||||
|
}
|
||||||
|
|
||||||
|
cases.add(new PbsAst.EnumCase(caseName.lexeme(), explicitValue, span(caseName.start(), cursor.previous().end())));
|
||||||
|
} while (cursor.match(PbsTokenKind.COMMA) && !cursor.check(PbsTokenKind.RIGHT_PAREN));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasExplicitCases && hasImplicitCases) {
|
||||||
|
report(name, ParseErrors.E_PARSE_INVALID_ENUM_FORM,
|
||||||
|
"Enum declarations cannot mix implicit and explicit case identifiers");
|
||||||
|
}
|
||||||
|
|
||||||
|
final var rightParen = consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after enum cases");
|
||||||
|
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after enum declaration");
|
||||||
|
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) {
|
||||||
|
final var name = consumeCallableName();
|
||||||
|
consume(PbsTokenKind.LEFT_PAREN, "Expected '(' after function name");
|
||||||
|
final var parameters = typeParser.parseParametersUntilRightParen();
|
||||||
|
final var rightParen = consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after parameter list");
|
||||||
|
|
||||||
|
final var returnSpec = typeParser.parseCallableReturnSpec(rightParen);
|
||||||
|
|
||||||
|
final var body = parseBlock();
|
||||||
|
return new PbsAst.FunctionDecl(
|
||||||
|
name.lexeme(),
|
||||||
|
ReadOnlyList.wrap(parameters),
|
||||||
|
returnSpec.kind(),
|
||||||
|
returnSpec.returnType(),
|
||||||
|
returnSpec.resultErrorType(),
|
||||||
|
body,
|
||||||
|
span(fnToken.start(), body.span().getEnd()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PbsAst.FunctionSignature parseFunctionSignature(
|
||||||
|
final PbsToken fnToken,
|
||||||
|
final ReadOnlyList<PbsAst.Attribute> attributes) {
|
||||||
|
final var name = consumeCallableName();
|
||||||
|
consume(PbsTokenKind.LEFT_PAREN, "Expected '(' after function name");
|
||||||
|
final var parameters = typeParser.parseParametersUntilRightParen();
|
||||||
|
final var rightParen = consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after parameter list");
|
||||||
|
|
||||||
|
final var returnSpec = typeParser.parseCallableReturnSpec(rightParen);
|
||||||
|
|
||||||
|
long end = returnSpec.returnType().span().getEnd();
|
||||||
|
if (REQUIRE_SEMICOLON) {
|
||||||
|
end = consume(PbsTokenKind.SEMICOLON, "Expected ';' after function signature").end();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PbsAst.FunctionSignature(
|
||||||
|
name.lexeme(),
|
||||||
|
ReadOnlyList.wrap(parameters),
|
||||||
|
returnSpec.kind(),
|
||||||
|
returnSpec.returnType(),
|
||||||
|
returnSpec.resultErrorType(),
|
||||||
|
attributes,
|
||||||
|
span(fnToken.start(), end));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PbsToken consumeCallableName() {
|
||||||
|
if (cursor.check(PbsTokenKind.IDENTIFIER) || cursor.check(PbsTokenKind.ERROR)) {
|
||||||
|
return cursor.advance();
|
||||||
|
}
|
||||||
|
final var token = cursor.peek();
|
||||||
|
report(token, ParseErrors.E_PARSE_EXPECTED_TOKEN, "Expected a function name, but found " + token.kind());
|
||||||
|
if (!cursor.isAtEnd()) {
|
||||||
|
return cursor.advance();
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PbsAst.CallbackDecl parseCallbackDeclaration(final PbsToken declareToken) {
|
||||||
|
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected callback name");
|
||||||
|
consume(PbsTokenKind.LEFT_PAREN, "Expected '(' after callback name");
|
||||||
|
final var parameters = typeParser.parseParametersUntilRightParen();
|
||||||
|
final var rightParen = consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after callback parameter list");
|
||||||
|
|
||||||
|
final var returnSpec = typeParser.parseCallableReturnSpec(rightParen);
|
||||||
|
|
||||||
|
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after callback declaration");
|
||||||
|
return new PbsAst.CallbackDecl(
|
||||||
|
name.lexeme(),
|
||||||
|
ReadOnlyList.wrap(parameters),
|
||||||
|
returnSpec.kind(),
|
||||||
|
returnSpec.returnType(),
|
||||||
|
returnSpec.resultErrorType(),
|
||||||
|
span(declareToken.start(), semicolon.end()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PbsAst.ConstDecl parseConstDeclaration(
|
||||||
|
final PbsToken declareToken,
|
||||||
|
final ReadOnlyList<PbsAst.Attribute> attributes) {
|
||||||
|
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected constant name");
|
||||||
|
consume(PbsTokenKind.COLON, "Expected ':' after constant name");
|
||||||
|
final var typeRef = typeParser.parseTypeRef();
|
||||||
|
|
||||||
|
PbsAst.Expression initializer = null;
|
||||||
|
if (cursor.match(PbsTokenKind.EQUAL)) {
|
||||||
|
initializer = exprParser.parseExpression();
|
||||||
|
}
|
||||||
|
|
||||||
|
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after constant declaration");
|
||||||
|
return new PbsAst.ConstDecl(
|
||||||
|
name.lexeme(),
|
||||||
|
typeRef,
|
||||||
|
initializer,
|
||||||
|
attributes,
|
||||||
|
span(declareToken.start(), semicolon.end()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PbsAst.Block parseBlock() {
|
||||||
|
return blockParserDelegate.parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void rejectAttributesBeforeUnsupportedTopDecl(
|
||||||
|
final ReadOnlyList<PbsAst.Attribute> attributes,
|
||||||
|
final String targetSurface) {
|
||||||
|
if (attributes.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reportAttributesNotAllowed(attributes, "Attributes are not allowed before " + targetSurface);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reportAttributesNotAllowed(
|
||||||
|
final ReadOnlyList<PbsAst.Attribute> attributes,
|
||||||
|
final String message) {
|
||||||
|
for (final var attribute : attributes) {
|
||||||
|
p.studio.compiler.source.diagnostics.Diagnostics.error(context.diagnostics(),
|
||||||
|
ParseErrors.E_PARSE_ATTRIBUTES_NOT_ALLOWED.name(),
|
||||||
|
message,
|
||||||
|
attribute.span());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isNotInterfaceMode() {
|
||||||
|
return context.parseMode() != PbsParser.ParseMode.INTERFACE_MODULE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isOrdinaryMode() {
|
||||||
|
return context.parseMode() == PbsParser.ParseMode.ORDINARY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PbsToken consume(final PbsTokenKind kind, final String message) {
|
||||||
|
if (cursor.check(kind)) {
|
||||||
|
return cursor.advance();
|
||||||
|
}
|
||||||
|
final var token = cursor.peek();
|
||||||
|
report(token, ParseErrors.E_PARSE_EXPECTED_TOKEN, message + ", found " + token.kind());
|
||||||
|
if (!cursor.isAtEnd()) {
|
||||||
|
return cursor.advance();
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void report(final PbsToken token, final ParseErrors parseErrors, final String message) {
|
||||||
|
context.report(token, parseErrors, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Span span(final long start, final long end) {
|
||||||
|
return context.span(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long parseLongOrNull(final String text) {
|
||||||
|
try {
|
||||||
|
return Long.parseLong(text);
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record StructBodyParse(
|
||||||
|
ReadOnlyList<PbsAst.FunctionDecl> methods,
|
||||||
|
ReadOnlyList<PbsAst.CtorDecl> ctors,
|
||||||
|
long end) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,13 +4,10 @@ import p.studio.compiler.pbs.ast.PbsAst;
|
|||||||
import p.studio.compiler.pbs.lexer.PbsToken;
|
import p.studio.compiler.pbs.lexer.PbsToken;
|
||||||
import p.studio.compiler.pbs.lexer.PbsTokenKind;
|
import p.studio.compiler.pbs.lexer.PbsTokenKind;
|
||||||
import p.studio.compiler.source.Span;
|
import p.studio.compiler.source.Span;
|
||||||
import p.studio.compiler.source.diagnostics.RelatedSpan;
|
|
||||||
import p.studio.compiler.source.identifiers.FileId;
|
import p.studio.compiler.source.identifiers.FileId;
|
||||||
import p.studio.utilities.structures.ReadOnlyList;
|
import p.studio.utilities.structures.ReadOnlyList;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* High-level manual parser for PBS source files.
|
* High-level manual parser for PBS source files.
|
||||||
@ -33,6 +30,8 @@ public final class PbsParser {
|
|||||||
private final PbsExprParser exprParser;
|
private final PbsExprParser exprParser;
|
||||||
private final PbsAttributeParser attributeParser;
|
private final PbsAttributeParser attributeParser;
|
||||||
private final PbsTypeParser typeParser;
|
private final PbsTypeParser typeParser;
|
||||||
|
private final PbsDeclarationParser declarationParser;
|
||||||
|
private final PbsTopLevelParser topLevelParser;
|
||||||
|
|
||||||
private PbsParser(
|
private PbsParser(
|
||||||
final ReadOnlyList<PbsToken> tokens,
|
final ReadOnlyList<PbsToken> tokens,
|
||||||
@ -44,6 +43,15 @@ public final class PbsParser {
|
|||||||
this.exprParser = new PbsExprParser(context, this::parseExpressionSurfaceBlock);
|
this.exprParser = new PbsExprParser(context, this::parseExpressionSurfaceBlock);
|
||||||
this.attributeParser = new PbsAttributeParser(context, this::isTopLevelRestartToken);
|
this.attributeParser = new PbsAttributeParser(context, this::isTopLevelRestartToken);
|
||||||
this.typeParser = new PbsTypeParser(context);
|
this.typeParser = new PbsTypeParser(context);
|
||||||
|
this.declarationParser = new PbsDeclarationParser(
|
||||||
|
context,
|
||||||
|
exprParser,
|
||||||
|
attributeParser,
|
||||||
|
typeParser,
|
||||||
|
this::parseBlock,
|
||||||
|
this::consumeDeclarationTerminator,
|
||||||
|
this::synchronizeTopLevel);
|
||||||
|
this.topLevelParser = new PbsTopLevelParser(context, attributeParser, declarationParser);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,130 +76,21 @@ public final class PbsParser {
|
|||||||
* Parses a full file as a sequence of imports and top-level declarations.
|
* Parses a full file as a sequence of imports and top-level declarations.
|
||||||
*/
|
*/
|
||||||
private PbsAst.File parseFile() {
|
private PbsAst.File parseFile() {
|
||||||
final var imports = new ArrayList<PbsAst.ImportDecl>();
|
return topLevelParser.parseFile();
|
||||||
final var topDecls = new ArrayList<PbsAst.TopDecl>();
|
|
||||||
|
|
||||||
while (!cursor.isAtEnd()) {
|
|
||||||
var pendingAttributes = ReadOnlyList.<PbsAst.Attribute>empty();
|
|
||||||
if (cursor.check(PbsTokenKind.LEFT_BRACKET)) {
|
|
||||||
pendingAttributes = parseAttributeList();
|
|
||||||
if (isOrdinaryMode()) {
|
|
||||||
reportAttributesNotAllowed(
|
|
||||||
pendingAttributes,
|
|
||||||
"Attributes are not allowed in ordinary .pbs source modules");
|
|
||||||
pendingAttributes = ReadOnlyList.empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cursor.match(PbsTokenKind.IMPORT)) {
|
|
||||||
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "import declarations");
|
|
||||||
imports.add(parseImport(cursor.previous()));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cursor.match(PbsTokenKind.FN)) {
|
|
||||||
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "top-level functions");
|
|
||||||
topDecls.add(parseFunction(cursor.previous()));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cursor.match(PbsTokenKind.DECLARE)) {
|
|
||||||
topDecls.add(parseDeclareTopDecl(cursor.previous(), pendingAttributes));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cursor.match(PbsTokenKind.IMPLEMENTS)) {
|
|
||||||
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "implements declarations");
|
|
||||||
topDecls.add(parseImplementsDeclaration(cursor.previous()));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cursor.match(PbsTokenKind.MOD, PbsTokenKind.PUB)) {
|
|
||||||
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "barrel-only visibility modifiers");
|
|
||||||
final var token = cursor.previous();
|
|
||||||
report(token, ParseErrors.E_PARSE_VISIBILITY_IN_SOURCE,
|
|
||||||
"Visibility modifiers are barrel-only and cannot appear in .pbs declarations");
|
|
||||||
topDecls.add(new PbsAst.InvalidDecl("visibility modifier in source", span(token.start(), token.end())));
|
|
||||||
synchronizeTopLevel();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cursor.check(PbsTokenKind.EOF)) {
|
|
||||||
if (!pendingAttributes.isEmpty()) {
|
|
||||||
reportAttributesNotAllowed(pendingAttributes, "Attributes must precede a valid declaration");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pendingAttributes.isEmpty()) {
|
|
||||||
reportAttributesNotAllowed(pendingAttributes, "Attributes must precede a valid declaration");
|
|
||||||
}
|
|
||||||
final var token = cursor.peek();
|
|
||||||
report(token, ParseErrors.E_PARSE_UNEXPECTED_TOKEN,
|
|
||||||
"Expected top-level declaration ('fn', 'declare', 'implements') or import");
|
|
||||||
topDecls.add(new PbsAst.InvalidDecl("unexpected top-level token", span(token.start(), token.end())));
|
|
||||||
synchronizeTopLevel();
|
|
||||||
}
|
|
||||||
|
|
||||||
final var eof = cursor.peek();
|
|
||||||
return new PbsAst.File(
|
|
||||||
ReadOnlyList.wrap(imports),
|
|
||||||
ReadOnlyList.wrap(topDecls),
|
|
||||||
span(0, eof.end()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses import syntax and stores it in the AST.
|
* Parses import syntax and stores it in the AST.
|
||||||
*/
|
*/
|
||||||
private PbsAst.ImportDecl parseImport(final PbsToken importToken) {
|
private PbsAst.ImportDecl parseImport(final PbsToken importToken) {
|
||||||
final var items = new ArrayList<PbsAst.ImportItem>();
|
return declarationParser.parseImport(importToken);
|
||||||
if (cursor.match(PbsTokenKind.LEFT_BRACE)) {
|
|
||||||
while (!cursor.check(PbsTokenKind.RIGHT_BRACE) && !cursor.isAtEnd()) {
|
|
||||||
if (cursor.match(PbsTokenKind.IDENTIFIER)) {
|
|
||||||
final var itemName = cursor.previous();
|
|
||||||
String alias = null;
|
|
||||||
if (cursor.match(PbsTokenKind.AS)) {
|
|
||||||
alias = consume(PbsTokenKind.IDENTIFIER, "Expected alias identifier after 'as'").lexeme();
|
|
||||||
}
|
|
||||||
items.add(new PbsAst.ImportItem(
|
|
||||||
itemName.lexeme(),
|
|
||||||
alias,
|
|
||||||
span(itemName.start(), cursor.previous().end())));
|
|
||||||
cursor.match(PbsTokenKind.COMMA);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
report(cursor.peek(), ParseErrors.E_PARSE_UNEXPECTED_TOKEN, "Invalid import item");
|
|
||||||
cursor.advance();
|
|
||||||
}
|
|
||||||
consume(PbsTokenKind.RIGHT_BRACE, "Expected '}' in import list");
|
|
||||||
consume(PbsTokenKind.FROM, "Expected 'from' in named import");
|
|
||||||
}
|
|
||||||
|
|
||||||
final var moduleRef = parseModuleRef();
|
|
||||||
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after import");
|
|
||||||
return new PbsAst.ImportDecl(
|
|
||||||
ReadOnlyList.wrap(items),
|
|
||||||
moduleRef,
|
|
||||||
span(importToken.start(), semicolon.end()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a module reference such as {@code @core:math/tools}.
|
* Parses a module reference such as {@code @core:math/tools}.
|
||||||
*/
|
*/
|
||||||
private PbsAst.ModuleRef parseModuleRef() {
|
private PbsAst.ModuleRef parseModuleRef() {
|
||||||
final var at = consume(PbsTokenKind.AT, "Expected '@' in module reference");
|
return declarationParser.parseModuleRef();
|
||||||
final var project = consume(PbsTokenKind.IDENTIFIER, "Expected project identifier in module reference");
|
|
||||||
consume(PbsTokenKind.COLON, "Expected ':' in module reference");
|
|
||||||
final var segments = new ArrayList<String>();
|
|
||||||
final var firstSegment = consume(PbsTokenKind.IDENTIFIER, "Expected module identifier");
|
|
||||||
segments.add(firstSegment.lexeme());
|
|
||||||
var end = firstSegment.end();
|
|
||||||
while (cursor.match(PbsTokenKind.SLASH)) {
|
|
||||||
final var segment = consume(PbsTokenKind.IDENTIFIER, "Expected module path segment after '/'");
|
|
||||||
segments.add(segment.lexeme());
|
|
||||||
end = segment.end();
|
|
||||||
}
|
|
||||||
return new PbsAst.ModuleRef(project.lexeme(), ReadOnlyList.wrap(segments), span(at.start(), end));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -200,260 +99,49 @@ public final class PbsParser {
|
|||||||
private PbsAst.TopDecl parseDeclareTopDecl(
|
private PbsAst.TopDecl parseDeclareTopDecl(
|
||||||
final PbsToken declareToken,
|
final PbsToken declareToken,
|
||||||
final ReadOnlyList<PbsAst.Attribute> pendingAttributes) {
|
final ReadOnlyList<PbsAst.Attribute> pendingAttributes) {
|
||||||
if (cursor.match(PbsTokenKind.STRUCT)) {
|
return declarationParser.parseDeclareTopDecl(declareToken, pendingAttributes);
|
||||||
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "struct declarations");
|
|
||||||
return parseStructDeclaration(declareToken);
|
|
||||||
}
|
|
||||||
if (cursor.match(PbsTokenKind.CONTRACT)) {
|
|
||||||
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "contract declarations");
|
|
||||||
return parseContractDeclaration(declareToken);
|
|
||||||
}
|
|
||||||
if (cursor.match(PbsTokenKind.SERVICE)) {
|
|
||||||
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "service declarations");
|
|
||||||
return parseServiceDeclaration(declareToken);
|
|
||||||
}
|
|
||||||
if (cursor.match(PbsTokenKind.ERROR)) {
|
|
||||||
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "error declarations");
|
|
||||||
return parseErrorDeclaration(declareToken);
|
|
||||||
}
|
|
||||||
if (cursor.match(PbsTokenKind.ENUM)) {
|
|
||||||
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "enum declarations");
|
|
||||||
return parseEnumDeclaration(declareToken);
|
|
||||||
}
|
|
||||||
if (cursor.match(PbsTokenKind.CALLBACK)) {
|
|
||||||
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "callback declarations");
|
|
||||||
return parseCallbackDeclaration(declareToken);
|
|
||||||
}
|
|
||||||
if (cursor.match(PbsTokenKind.CONST)) {
|
|
||||||
return parseConstDeclaration(declareToken, pendingAttributes);
|
|
||||||
}
|
|
||||||
if (cursor.match(PbsTokenKind.HOST)) {
|
|
||||||
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "host declarations");
|
|
||||||
if (isNotInterfaceMode()) {
|
|
||||||
final var end = consumeDeclarationTerminator();
|
|
||||||
report(cursor.previous(), ParseErrors.E_PARSE_RESERVED_DECLARATION,
|
|
||||||
"'declare host' is reserved and not supported in ordinary source modules");
|
|
||||||
return new PbsAst.InvalidDecl("reserved declare host", span(declareToken.start(), end));
|
|
||||||
}
|
|
||||||
return parseHostDeclaration(declareToken);
|
|
||||||
}
|
|
||||||
if (cursor.match(PbsTokenKind.BUILTIN)) {
|
|
||||||
consume(PbsTokenKind.TYPE, "Expected 'type' in 'declare builtin type' declaration");
|
|
||||||
if (isNotInterfaceMode()) {
|
|
||||||
final var end = consumeDeclarationTerminator();
|
|
||||||
report(cursor.previous(), ParseErrors.E_PARSE_RESERVED_DECLARATION,
|
|
||||||
"'declare builtin type' is reserved and not supported in ordinary source modules");
|
|
||||||
return new PbsAst.InvalidDecl("reserved declare builtin type", span(declareToken.start(), end));
|
|
||||||
}
|
|
||||||
return parseBuiltinTypeDeclaration(declareToken, pendingAttributes);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pendingAttributes.isEmpty()) {
|
|
||||||
reportAttributesNotAllowed(pendingAttributes, "Attributes are not valid for this declaration form");
|
|
||||||
}
|
|
||||||
|
|
||||||
report(cursor.peek(), ParseErrors.E_PARSE_UNEXPECTED_TOKEN,
|
|
||||||
"Expected declaration kind after 'declare'");
|
|
||||||
synchronizeTopLevel();
|
|
||||||
final var token = cursor.previous();
|
|
||||||
return new PbsAst.InvalidDecl("invalid declare form", span(declareToken.start(), token.end()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PbsAst.HostDecl parseHostDeclaration(final PbsToken declareToken) {
|
private PbsAst.HostDecl parseHostDeclaration(final PbsToken declareToken) {
|
||||||
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected host declaration name");
|
throw new UnsupportedOperationException();
|
||||||
consume(PbsTokenKind.LEFT_BRACE, "Expected '{' to start host declaration body");
|
|
||||||
final var signatures = new ArrayList<PbsAst.FunctionSignature>();
|
|
||||||
while (!cursor.check(PbsTokenKind.RIGHT_BRACE) && !cursor.isAtEnd()) {
|
|
||||||
var attributes = ReadOnlyList.<PbsAst.Attribute>empty();
|
|
||||||
if (cursor.check(PbsTokenKind.LEFT_BRACKET)) {
|
|
||||||
attributes = parseAttributeList();
|
|
||||||
}
|
|
||||||
if (!cursor.match(PbsTokenKind.FN)) {
|
|
||||||
report(cursor.peek(), ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
|
||||||
"Host declaration body accepts only function signatures");
|
|
||||||
cursor.advance();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
signatures.add(parseFunctionSignature(cursor.previous(), attributes));
|
|
||||||
}
|
|
||||||
final var rightBrace = consume(PbsTokenKind.RIGHT_BRACE, "Expected '}' to end host declaration body");
|
|
||||||
return new PbsAst.HostDecl(
|
|
||||||
name.lexeme(),
|
|
||||||
ReadOnlyList.wrap(signatures),
|
|
||||||
span(declareToken.start(), rightBrace.end()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PbsAst.BuiltinTypeDecl parseBuiltinTypeDeclaration(
|
private PbsAst.BuiltinTypeDecl parseBuiltinTypeDeclaration(
|
||||||
final PbsToken declareToken,
|
final PbsToken declareToken,
|
||||||
final ReadOnlyList<PbsAst.Attribute> declarationAttributes) {
|
final ReadOnlyList<PbsAst.Attribute> declarationAttributes) {
|
||||||
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected builtin type name");
|
throw new UnsupportedOperationException();
|
||||||
consume(PbsTokenKind.LEFT_PAREN, "Expected '(' in builtin type declaration");
|
|
||||||
final var fields = parseBuiltinTypeFields();
|
|
||||||
consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after builtin type fields");
|
|
||||||
|
|
||||||
consume(PbsTokenKind.LEFT_BRACE, "Expected '{' to start builtin type body");
|
|
||||||
final var signatures = new ArrayList<PbsAst.FunctionSignature>();
|
|
||||||
while (!cursor.check(PbsTokenKind.RIGHT_BRACE) && !cursor.isAtEnd()) {
|
|
||||||
var attributes = ReadOnlyList.<PbsAst.Attribute>empty();
|
|
||||||
if (cursor.check(PbsTokenKind.LEFT_BRACKET)) {
|
|
||||||
attributes = parseAttributeList();
|
|
||||||
}
|
|
||||||
if (!cursor.match(PbsTokenKind.FN)) {
|
|
||||||
report(cursor.peek(), ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
|
||||||
"Builtin type body accepts only function signatures");
|
|
||||||
cursor.advance();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
signatures.add(parseFunctionSignature(cursor.previous(), attributes));
|
|
||||||
}
|
|
||||||
final var rightBrace = consume(PbsTokenKind.RIGHT_BRACE, "Expected '}' to end builtin type body");
|
|
||||||
return new PbsAst.BuiltinTypeDecl(
|
|
||||||
name.lexeme(),
|
|
||||||
ReadOnlyList.wrap(fields),
|
|
||||||
ReadOnlyList.wrap(signatures),
|
|
||||||
declarationAttributes,
|
|
||||||
span(declareToken.start(), rightBrace.end()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArrayList<PbsAst.BuiltinFieldDecl> parseBuiltinTypeFields() {
|
private ArrayList<PbsAst.BuiltinFieldDecl> parseBuiltinTypeFields() {
|
||||||
final var fields = new ArrayList<PbsAst.BuiltinFieldDecl>();
|
throw new UnsupportedOperationException();
|
||||||
if (cursor.check(PbsTokenKind.RIGHT_PAREN)) {
|
|
||||||
return fields;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
final var start = cursor.peek();
|
|
||||||
var attributes = ReadOnlyList.<PbsAst.Attribute>empty();
|
|
||||||
if (cursor.check(PbsTokenKind.LEFT_BRACKET)) {
|
|
||||||
attributes = parseAttributeList();
|
|
||||||
}
|
|
||||||
|
|
||||||
var isPublic = false;
|
|
||||||
if (cursor.match(PbsTokenKind.PUB)) {
|
|
||||||
isPublic = true;
|
|
||||||
if (cursor.match(PbsTokenKind.MUT)) {
|
|
||||||
final var mutToken = cursor.previous();
|
|
||||||
report(mutToken, ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
|
||||||
"'mut' is not allowed in builtin type fields");
|
|
||||||
}
|
|
||||||
} else if (cursor.match(PbsTokenKind.MUT)) {
|
|
||||||
final var mutToken = cursor.previous();
|
|
||||||
report(mutToken, ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
|
||||||
"'mut' is not allowed in builtin type fields");
|
|
||||||
}
|
|
||||||
|
|
||||||
final var fieldName = consume(PbsTokenKind.IDENTIFIER, "Expected builtin field name");
|
|
||||||
consume(PbsTokenKind.COLON, "Expected ':' after builtin field name");
|
|
||||||
final var typeRef = parseTypeRef();
|
|
||||||
fields.add(new PbsAst.BuiltinFieldDecl(
|
|
||||||
fieldName.lexeme(),
|
|
||||||
typeRef,
|
|
||||||
isPublic,
|
|
||||||
attributes,
|
|
||||||
span(start.start(), typeRef.span().getEnd())));
|
|
||||||
} while (cursor.match(PbsTokenKind.COMMA) && !cursor.check(PbsTokenKind.RIGHT_PAREN));
|
|
||||||
|
|
||||||
return fields;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void rejectAttributesBeforeUnsupportedTopDecl(
|
private void rejectAttributesBeforeUnsupportedTopDecl(
|
||||||
final ReadOnlyList<PbsAst.Attribute> attributes,
|
final ReadOnlyList<PbsAst.Attribute> attributes,
|
||||||
final String targetSurface) {
|
final String targetSurface) {
|
||||||
if (attributes.isEmpty()) {
|
throw new UnsupportedOperationException();
|
||||||
return;
|
|
||||||
}
|
|
||||||
reportAttributesNotAllowed(
|
|
||||||
attributes,
|
|
||||||
"Attributes are not allowed before " + targetSurface);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reportAttributesNotAllowed(
|
private void reportAttributesNotAllowed(
|
||||||
final ReadOnlyList<PbsAst.Attribute> attributes,
|
final ReadOnlyList<PbsAst.Attribute> attributes,
|
||||||
final String message) {
|
final String message) {
|
||||||
for (final var attribute : attributes) {
|
throw new UnsupportedOperationException();
|
||||||
p.studio.compiler.source.diagnostics.Diagnostics.error(context.diagnostics(),
|
|
||||||
ParseErrors.E_PARSE_ATTRIBUTES_NOT_ALLOWED.name(),
|
|
||||||
message,
|
|
||||||
attribute.span());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isNotInterfaceMode() {
|
private boolean isNotInterfaceMode() {
|
||||||
return context.parseMode() != ParseMode.INTERFACE_MODULE;
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isOrdinaryMode() {
|
private boolean isOrdinaryMode() {
|
||||||
return context.parseMode() == ParseMode.ORDINARY;
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private PbsAst.StructDecl parseStructDeclaration(final PbsToken declareToken) {
|
private PbsAst.StructDecl parseStructDeclaration(final PbsToken declareToken) {
|
||||||
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected struct name");
|
throw new UnsupportedOperationException();
|
||||||
consume(PbsTokenKind.LEFT_PAREN, "Expected '(' in struct declaration");
|
|
||||||
final var fields = parseStructFields();
|
|
||||||
consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after struct fields");
|
|
||||||
|
|
||||||
final var methods = new ArrayList<PbsAst.FunctionDecl>();
|
|
||||||
final var ctors = new ArrayList<PbsAst.CtorDecl>();
|
|
||||||
final boolean hasBody;
|
|
||||||
long end;
|
|
||||||
if (cursor.match(PbsTokenKind.SEMICOLON)) {
|
|
||||||
hasBody = false;
|
|
||||||
end = cursor.previous().end();
|
|
||||||
} else if (cursor.match(PbsTokenKind.LEFT_BRACE)) {
|
|
||||||
hasBody = true;
|
|
||||||
final var bodyParse = parseStructBodyAndConsumeRightBrace(cursor.previous());
|
|
||||||
methods.addAll(bodyParse.methods().asList());
|
|
||||||
ctors.addAll(bodyParse.ctors().asList());
|
|
||||||
end = bodyParse.end();
|
|
||||||
} else {
|
|
||||||
report(cursor.peek(), ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
|
||||||
"Struct declaration must end with ';' or contain a '{...}' body");
|
|
||||||
hasBody = false;
|
|
||||||
end = consumeDeclarationTerminator();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new PbsAst.StructDecl(
|
|
||||||
name.lexeme(),
|
|
||||||
ReadOnlyList.wrap(fields),
|
|
||||||
ReadOnlyList.wrap(methods),
|
|
||||||
ReadOnlyList.wrap(ctors),
|
|
||||||
hasBody,
|
|
||||||
span(declareToken.start(), end));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArrayList<PbsAst.StructField> parseStructFields() {
|
private ArrayList<PbsAst.StructField> parseStructFields() {
|
||||||
final var fields = new ArrayList<PbsAst.StructField>();
|
throw new UnsupportedOperationException();
|
||||||
if (cursor.check(PbsTokenKind.RIGHT_PAREN)) {
|
|
||||||
return fields;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
final var start = cursor.peek();
|
|
||||||
var isPublic = false;
|
|
||||||
var isMutable = false;
|
|
||||||
if (cursor.match(PbsTokenKind.PUB)) {
|
|
||||||
isPublic = true;
|
|
||||||
if (cursor.match(PbsTokenKind.MUT)) {
|
|
||||||
isMutable = true;
|
|
||||||
}
|
|
||||||
} else if (cursor.match(PbsTokenKind.MUT)) {
|
|
||||||
final var mutToken = cursor.previous();
|
|
||||||
report(mutToken, ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
|
||||||
"'mut' is only valid immediately after 'pub' in struct fields");
|
|
||||||
}
|
|
||||||
|
|
||||||
final var fieldName = consume(PbsTokenKind.IDENTIFIER, "Expected struct field name");
|
|
||||||
consume(PbsTokenKind.COLON, "Expected ':' after struct field name");
|
|
||||||
final var typeRef = parseTypeRef();
|
|
||||||
fields.add(new PbsAst.StructField(
|
|
||||||
fieldName.lexeme(),
|
|
||||||
typeRef,
|
|
||||||
isPublic,
|
|
||||||
isMutable,
|
|
||||||
span(start.start(), typeRef.span().getEnd())));
|
|
||||||
} while (cursor.match(PbsTokenKind.COMMA) && !cursor.check(PbsTokenKind.RIGHT_PAREN));
|
|
||||||
|
|
||||||
return fields;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ReadOnlyList<PbsAst.Attribute> parseAttributeList() {
|
private ReadOnlyList<PbsAst.Attribute> parseAttributeList() {
|
||||||
@ -461,234 +149,55 @@ public final class PbsParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private StructBodyParse parseStructBodyAndConsumeRightBrace(final PbsToken leftBrace) {
|
private StructBodyParse parseStructBodyAndConsumeRightBrace(final PbsToken leftBrace) {
|
||||||
final var methods = new ArrayList<PbsAst.FunctionDecl>();
|
throw new UnsupportedOperationException();
|
||||||
final var ctors = new ArrayList<PbsAst.CtorDecl>();
|
|
||||||
while (!cursor.check(PbsTokenKind.RIGHT_BRACE) && !cursor.isAtEnd()) {
|
|
||||||
if (cursor.match(PbsTokenKind.FN)) {
|
|
||||||
methods.add(parseFunctionLike(cursor.previous()));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (cursor.match(PbsTokenKind.CTOR)) {
|
|
||||||
ctors.add(parseCtorDeclarationInBody(cursor.previous()));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
report(cursor.peek(), ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
|
||||||
"Struct body accepts only 'fn' methods and 'ctor' declarations");
|
|
||||||
cursor.advance();
|
|
||||||
}
|
|
||||||
final var rightBrace = consume(PbsTokenKind.RIGHT_BRACE, "Expected '}' to end struct body");
|
|
||||||
return new StructBodyParse(ReadOnlyList.wrap(methods), ReadOnlyList.wrap(ctors), rightBrace.end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PbsAst.CtorDecl parseCtorDeclarationInBody(final PbsToken ctorToken) {
|
private PbsAst.CtorDecl parseCtorDeclarationInBody(final PbsToken ctorToken) {
|
||||||
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected constructor name");
|
throw new UnsupportedOperationException();
|
||||||
consume(PbsTokenKind.LEFT_PAREN, "Expected '(' after constructor name");
|
|
||||||
final var parameters = parseParametersUntilRightParen();
|
|
||||||
consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after constructor parameters");
|
|
||||||
final var body = parseBlock();
|
|
||||||
return new PbsAst.CtorDecl(
|
|
||||||
name.lexeme(),
|
|
||||||
ReadOnlyList.wrap(parameters),
|
|
||||||
body,
|
|
||||||
span(ctorToken.start(), body.span().getEnd()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PbsAst.ContractDecl parseContractDeclaration(final PbsToken declareToken) {
|
private PbsAst.ContractDecl parseContractDeclaration(final PbsToken declareToken) {
|
||||||
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected contract name");
|
throw new UnsupportedOperationException();
|
||||||
consume(PbsTokenKind.LEFT_BRACE, "Expected '{' to start contract body");
|
|
||||||
final var signatures = new ArrayList<PbsAst.FunctionSignature>();
|
|
||||||
while (!cursor.check(PbsTokenKind.RIGHT_BRACE) && !cursor.isAtEnd()) {
|
|
||||||
var attributes = ReadOnlyList.<PbsAst.Attribute>empty();
|
|
||||||
if (cursor.check(PbsTokenKind.LEFT_BRACKET)) {
|
|
||||||
attributes = parseAttributeList();
|
|
||||||
if (isOrdinaryMode()) {
|
|
||||||
reportAttributesNotAllowed(attributes, "Attributes are not allowed in ordinary .pbs source modules");
|
|
||||||
attributes = ReadOnlyList.empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!cursor.match(PbsTokenKind.FN)) {
|
|
||||||
report(cursor.peek(), ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
|
||||||
"Contract body accepts only function signatures");
|
|
||||||
cursor.advance();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
signatures.add(parseFunctionSignature(cursor.previous(), attributes));
|
|
||||||
}
|
|
||||||
final var rightBrace = consume(PbsTokenKind.RIGHT_BRACE, "Expected '}' to end contract body");
|
|
||||||
return new PbsAst.ContractDecl(name.lexeme(), ReadOnlyList.wrap(signatures), span(declareToken.start(), rightBrace.end()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PbsAst.ServiceDecl parseServiceDeclaration(final PbsToken declareToken) {
|
private PbsAst.ServiceDecl parseServiceDeclaration(final PbsToken declareToken) {
|
||||||
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected service name");
|
throw new UnsupportedOperationException();
|
||||||
consume(PbsTokenKind.LEFT_BRACE, "Expected '{' to start service body");
|
|
||||||
final var methods = new ArrayList<PbsAst.FunctionDecl>();
|
|
||||||
while (!cursor.check(PbsTokenKind.RIGHT_BRACE) && !cursor.isAtEnd()) {
|
|
||||||
if (!cursor.match(PbsTokenKind.FN)) {
|
|
||||||
report(cursor.peek(), ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
|
||||||
"Service body accepts only method declarations");
|
|
||||||
cursor.advance();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
methods.add(parseFunctionLike(cursor.previous()));
|
|
||||||
}
|
|
||||||
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()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PbsAst.ErrorDecl parseErrorDeclaration(final PbsToken declareToken) {
|
private PbsAst.ErrorDecl parseErrorDeclaration(final PbsToken declareToken) {
|
||||||
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected error name");
|
throw new UnsupportedOperationException();
|
||||||
consume(PbsTokenKind.LEFT_BRACE, "Expected '{' to start error body");
|
|
||||||
final var cases = new ArrayList<String>();
|
|
||||||
while (!cursor.check(PbsTokenKind.RIGHT_BRACE) && !cursor.isAtEnd()) {
|
|
||||||
final var caseToken = consume(PbsTokenKind.IDENTIFIER, "Expected error case label");
|
|
||||||
cases.add(caseToken.lexeme());
|
|
||||||
consume(PbsTokenKind.SEMICOLON, "Expected ';' after error case label");
|
|
||||||
}
|
|
||||||
final var rightBrace = consume(PbsTokenKind.RIGHT_BRACE, "Expected '}' to end error body");
|
|
||||||
return new PbsAst.ErrorDecl(name.lexeme(), ReadOnlyList.wrap(cases), span(declareToken.start(), rightBrace.end()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PbsAst.EnumDecl parseEnumDeclaration(final PbsToken declareToken) {
|
private PbsAst.EnumDecl parseEnumDeclaration(final PbsToken declareToken) {
|
||||||
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected enum name");
|
throw new UnsupportedOperationException();
|
||||||
consume(PbsTokenKind.LEFT_PAREN, "Expected '(' after enum name");
|
|
||||||
|
|
||||||
final var cases = new ArrayList<PbsAst.EnumCase>();
|
|
||||||
final var caseLabels = new HashMap<String, Span>();
|
|
||||||
final var explicitCaseIds = new HashMap<Long, Span>();
|
|
||||||
var hasExplicitCases = false;
|
|
||||||
var hasImplicitCases = false;
|
|
||||||
if (!cursor.check(PbsTokenKind.RIGHT_PAREN)) {
|
|
||||||
do {
|
|
||||||
final var caseName = consume(PbsTokenKind.IDENTIFIER, "Expected enum case label");
|
|
||||||
final var caseNameSpan = span(caseName.start(), caseName.end());
|
|
||||||
Long explicitValue = null;
|
|
||||||
if (cursor.match(PbsTokenKind.EQUAL)) {
|
|
||||||
final var intToken = consume(PbsTokenKind.INT_LITERAL, "Expected integer literal after '=' in enum case");
|
|
||||||
final var intTokenSpan = span(intToken.start(), intToken.end());
|
|
||||||
explicitValue = parseLongOrNull(intToken.lexeme());
|
|
||||||
hasExplicitCases = true;
|
|
||||||
if (explicitValue == null) {
|
|
||||||
report(intToken, ParseErrors.E_PARSE_INVALID_ENUM_FORM,
|
|
||||||
"Invalid explicit enum identifier");
|
|
||||||
} else {
|
|
||||||
final var firstIdSpan = explicitCaseIds.putIfAbsent(explicitValue, intTokenSpan);
|
|
||||||
if (firstIdSpan != null) {
|
|
||||||
p.studio.compiler.source.diagnostics.Diagnostics.error(context.diagnostics(),
|
|
||||||
ParseErrors.E_PARSE_DUPLICATE_ENUM_CASE_ID.name(),
|
|
||||||
"Duplicate explicit enum identifier '%s'".formatted(explicitValue),
|
|
||||||
intTokenSpan,
|
|
||||||
List.of(new RelatedSpan("First explicit enum identifier is here", firstIdSpan)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
hasImplicitCases = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
final var firstLabelSpan = caseLabels.putIfAbsent(caseName.lexeme(), caseNameSpan);
|
|
||||||
if (firstLabelSpan != null) {
|
|
||||||
p.studio.compiler.source.diagnostics.Diagnostics.error(context.diagnostics(),
|
|
||||||
ParseErrors.E_PARSE_DUPLICATE_ENUM_CASE_LABEL.name(),
|
|
||||||
"Duplicate enum case label '%s'".formatted(caseName.lexeme()),
|
|
||||||
caseNameSpan,
|
|
||||||
List.of(new RelatedSpan("First enum case label is here", firstLabelSpan)));
|
|
||||||
}
|
|
||||||
|
|
||||||
cases.add(new PbsAst.EnumCase(caseName.lexeme(), explicitValue, span(caseName.start(), cursor.previous().end())));
|
|
||||||
} while (cursor.match(PbsTokenKind.COMMA) && !cursor.check(PbsTokenKind.RIGHT_PAREN));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasExplicitCases && hasImplicitCases) {
|
|
||||||
report(name, ParseErrors.E_PARSE_INVALID_ENUM_FORM,
|
|
||||||
"Enum declarations cannot mix implicit and explicit case identifiers");
|
|
||||||
}
|
|
||||||
|
|
||||||
final var rightParen = consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after enum cases");
|
|
||||||
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after enum declaration");
|
|
||||||
return new PbsAst.EnumDecl(name.lexeme(), ReadOnlyList.wrap(cases), span(declareToken.start(), Math.max(rightParen.end(), semicolon.end())));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a top-level function declaration.
|
* Parses a top-level function declaration.
|
||||||
*/
|
*/
|
||||||
private PbsAst.FunctionDecl parseFunction(final PbsToken fnToken) {
|
private PbsAst.FunctionDecl parseFunction(final PbsToken fnToken) {
|
||||||
return parseFunctionLike(fnToken);
|
return declarationParser.parseFunction(fnToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PbsAst.FunctionDecl parseFunctionLike(final PbsToken fnToken) {
|
private PbsAst.FunctionDecl parseFunctionLike(final PbsToken fnToken) {
|
||||||
final var name = consumeCallableName();
|
throw new UnsupportedOperationException();
|
||||||
consume(PbsTokenKind.LEFT_PAREN, "Expected '(' after function name");
|
|
||||||
final var parameters = parseParametersUntilRightParen();
|
|
||||||
final var rightParen = consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after parameter list");
|
|
||||||
|
|
||||||
final var returnSpec = parseCallableReturnSpec(rightParen);
|
|
||||||
|
|
||||||
final var body = parseBlock();
|
|
||||||
return new PbsAst.FunctionDecl(
|
|
||||||
name.lexeme(),
|
|
||||||
ReadOnlyList.wrap(parameters),
|
|
||||||
returnSpec.kind(),
|
|
||||||
returnSpec.returnType(),
|
|
||||||
returnSpec.resultErrorType(),
|
|
||||||
body,
|
|
||||||
span(fnToken.start(), body.span().getEnd()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PbsAst.FunctionSignature parseFunctionSignature(
|
private PbsAst.FunctionSignature parseFunctionSignature(
|
||||||
final PbsToken fnToken,
|
final PbsToken fnToken,
|
||||||
final ReadOnlyList<PbsAst.Attribute> attributes) {
|
final ReadOnlyList<PbsAst.Attribute> attributes) {
|
||||||
final var name = consumeCallableName();
|
throw new UnsupportedOperationException();
|
||||||
consume(PbsTokenKind.LEFT_PAREN, "Expected '(' after function name");
|
|
||||||
final var parameters = parseParametersUntilRightParen();
|
|
||||||
final var rightParen = consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after parameter list");
|
|
||||||
|
|
||||||
final var returnSpec = parseCallableReturnSpec(rightParen);
|
|
||||||
|
|
||||||
long end = returnSpec.returnType().span().getEnd();
|
|
||||||
if (REQUIRE_SEMICOLON) {
|
|
||||||
end = consume(PbsTokenKind.SEMICOLON, "Expected ';' after function signature").end();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new PbsAst.FunctionSignature(
|
|
||||||
name.lexeme(),
|
|
||||||
ReadOnlyList.wrap(parameters),
|
|
||||||
returnSpec.kind(),
|
|
||||||
returnSpec.returnType(),
|
|
||||||
returnSpec.resultErrorType(),
|
|
||||||
attributes,
|
|
||||||
span(fnToken.start(), end));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PbsToken consumeCallableName() {
|
private PbsToken consumeCallableName() {
|
||||||
if (cursor.check(PbsTokenKind.IDENTIFIER) || cursor.check(PbsTokenKind.ERROR)) {
|
throw new UnsupportedOperationException();
|
||||||
return cursor.advance();
|
|
||||||
}
|
|
||||||
final var token = cursor.peek();
|
|
||||||
report(token, ParseErrors.E_PARSE_EXPECTED_TOKEN, "Expected a function name, but found " + token.kind());
|
|
||||||
if (!cursor.isAtEnd()) {
|
|
||||||
return cursor.advance();
|
|
||||||
}
|
|
||||||
return token;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses `declare callback` declaration.
|
* Parses `declare callback` declaration.
|
||||||
*/
|
*/
|
||||||
private PbsAst.CallbackDecl parseCallbackDeclaration(final PbsToken declareToken) {
|
private PbsAst.CallbackDecl parseCallbackDeclaration(final PbsToken declareToken) {
|
||||||
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected callback name");
|
throw new UnsupportedOperationException();
|
||||||
consume(PbsTokenKind.LEFT_PAREN, "Expected '(' after callback name");
|
|
||||||
final var parameters = parseParametersUntilRightParen();
|
|
||||||
final var rightParen = consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after callback parameter list");
|
|
||||||
|
|
||||||
final var returnSpec = parseCallableReturnSpec(rightParen);
|
|
||||||
|
|
||||||
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after callback declaration");
|
|
||||||
return new PbsAst.CallbackDecl(
|
|
||||||
name.lexeme(),
|
|
||||||
ReadOnlyList.wrap(parameters),
|
|
||||||
returnSpec.kind(),
|
|
||||||
returnSpec.returnType(),
|
|
||||||
returnSpec.resultErrorType(),
|
|
||||||
span(declareToken.start(), semicolon.end()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -697,60 +206,14 @@ public final class PbsParser {
|
|||||||
private PbsAst.ConstDecl parseConstDeclaration(
|
private PbsAst.ConstDecl parseConstDeclaration(
|
||||||
final PbsToken declareToken,
|
final PbsToken declareToken,
|
||||||
final ReadOnlyList<PbsAst.Attribute> attributes) {
|
final ReadOnlyList<PbsAst.Attribute> attributes) {
|
||||||
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected constant name");
|
throw new UnsupportedOperationException();
|
||||||
consume(PbsTokenKind.COLON, "Expected ':' after constant name");
|
|
||||||
final var typeRef = parseTypeRef();
|
|
||||||
|
|
||||||
PbsAst.Expression initializer = null;
|
|
||||||
if (cursor.match(PbsTokenKind.EQUAL)) {
|
|
||||||
initializer = exprParser.parseExpression();
|
|
||||||
}
|
|
||||||
|
|
||||||
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after constant declaration");
|
|
||||||
return new PbsAst.ConstDecl(
|
|
||||||
name.lexeme(),
|
|
||||||
typeRef,
|
|
||||||
initializer,
|
|
||||||
attributes,
|
|
||||||
span(declareToken.start(), semicolon.end()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses `implements Contract for Owner using binder { ... }`.
|
* Parses `implements Contract for Owner using binder { ... }`.
|
||||||
*/
|
*/
|
||||||
private PbsAst.ImplementsDecl parseImplementsDeclaration(final PbsToken implementsToken) {
|
private PbsAst.ImplementsDecl parseImplementsDeclaration(final PbsToken implementsToken) {
|
||||||
final var contractName = consume(PbsTokenKind.IDENTIFIER, "Expected contract name in 'implements'");
|
return declarationParser.parseImplementsDeclaration(implementsToken);
|
||||||
consume(PbsTokenKind.FOR, "Expected 'for' in 'implements' declaration");
|
|
||||||
final var ownerName = consume(PbsTokenKind.IDENTIFIER, "Expected owner name after 'for'");
|
|
||||||
consume(PbsTokenKind.USING, "Expected 'using' in 'implements' declaration");
|
|
||||||
final var binderName = consume(PbsTokenKind.IDENTIFIER, "Expected binder name after 'using'");
|
|
||||||
|
|
||||||
final var methods = new ArrayList<PbsAst.FunctionDecl>();
|
|
||||||
long end;
|
|
||||||
if (cursor.check(PbsTokenKind.LEFT_BRACE)) {
|
|
||||||
cursor.advance();
|
|
||||||
while (!cursor.check(PbsTokenKind.RIGHT_BRACE) && !cursor.isAtEnd()) {
|
|
||||||
if (!cursor.match(PbsTokenKind.FN)) {
|
|
||||||
report(cursor.peek(), ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
|
||||||
"Implements body accepts only function declarations");
|
|
||||||
cursor.advance();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
methods.add(parseFunctionLike(cursor.previous()));
|
|
||||||
}
|
|
||||||
end = consume(PbsTokenKind.RIGHT_BRACE, "Expected '}' to end implements body").end();
|
|
||||||
} else {
|
|
||||||
report(cursor.peek(), ParseErrors.E_PARSE_EXPECTED_TOKEN,
|
|
||||||
"Expected '{' to start 'implements' body");
|
|
||||||
end = consumeDeclarationTerminator();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new PbsAst.ImplementsDecl(
|
|
||||||
contractName.lexeme(),
|
|
||||||
ownerName.lexeme(),
|
|
||||||
binderName.lexeme(),
|
|
||||||
ReadOnlyList.wrap(methods),
|
|
||||||
span(implementsToken.start(), end));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArrayList<PbsAst.Parameter> parseParametersUntilRightParen() {
|
private ArrayList<PbsAst.Parameter> parseParametersUntilRightParen() {
|
||||||
|
|||||||
@ -0,0 +1,141 @@
|
|||||||
|
package p.studio.compiler.pbs.parser;
|
||||||
|
|
||||||
|
import p.studio.compiler.pbs.ast.PbsAst;
|
||||||
|
import p.studio.compiler.pbs.lexer.PbsTokenKind;
|
||||||
|
import p.studio.utilities.structures.ReadOnlyList;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
final class PbsTopLevelParser {
|
||||||
|
private final PbsParserContext context;
|
||||||
|
private final PbsTokenCursor cursor;
|
||||||
|
private final PbsAttributeParser attributeParser;
|
||||||
|
private final PbsDeclarationParser declarationParser;
|
||||||
|
|
||||||
|
PbsTopLevelParser(
|
||||||
|
final PbsParserContext context,
|
||||||
|
final PbsAttributeParser attributeParser,
|
||||||
|
final PbsDeclarationParser declarationParser) {
|
||||||
|
this.context = context;
|
||||||
|
this.cursor = context.cursor();
|
||||||
|
this.attributeParser = attributeParser;
|
||||||
|
this.declarationParser = declarationParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
PbsAst.File parseFile() {
|
||||||
|
final var imports = new ArrayList<PbsAst.ImportDecl>();
|
||||||
|
final var topDecls = new ArrayList<PbsAst.TopDecl>();
|
||||||
|
|
||||||
|
while (!cursor.isAtEnd()) {
|
||||||
|
var pendingAttributes = ReadOnlyList.<PbsAst.Attribute>empty();
|
||||||
|
if (cursor.check(PbsTokenKind.LEFT_BRACKET)) {
|
||||||
|
pendingAttributes = attributeParser.parseAttributeList();
|
||||||
|
if (isOrdinaryMode()) {
|
||||||
|
reportAttributesNotAllowed(
|
||||||
|
pendingAttributes,
|
||||||
|
"Attributes are not allowed in ordinary .pbs source modules");
|
||||||
|
pendingAttributes = ReadOnlyList.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor.match(PbsTokenKind.IMPORT)) {
|
||||||
|
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "import declarations");
|
||||||
|
imports.add(declarationParser.parseImport(cursor.previous()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor.match(PbsTokenKind.FN)) {
|
||||||
|
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "top-level functions");
|
||||||
|
topDecls.add(declarationParser.parseFunction(cursor.previous()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor.match(PbsTokenKind.DECLARE)) {
|
||||||
|
topDecls.add(declarationParser.parseDeclareTopDecl(cursor.previous(), pendingAttributes));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor.match(PbsTokenKind.IMPLEMENTS)) {
|
||||||
|
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "implements declarations");
|
||||||
|
topDecls.add(declarationParser.parseImplementsDeclaration(cursor.previous()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor.match(PbsTokenKind.MOD, PbsTokenKind.PUB)) {
|
||||||
|
rejectAttributesBeforeUnsupportedTopDecl(pendingAttributes, "barrel-only visibility modifiers");
|
||||||
|
final var token = cursor.previous();
|
||||||
|
context.report(token, ParseErrors.E_PARSE_VISIBILITY_IN_SOURCE,
|
||||||
|
"Visibility modifiers are barrel-only and cannot appear in .pbs declarations");
|
||||||
|
topDecls.add(new PbsAst.InvalidDecl("visibility modifier in source", context.span(token.start(), token.end())));
|
||||||
|
synchronizeTopLevel();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor.check(PbsTokenKind.EOF)) {
|
||||||
|
if (!pendingAttributes.isEmpty()) {
|
||||||
|
reportAttributesNotAllowed(pendingAttributes, "Attributes must precede a valid declaration");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pendingAttributes.isEmpty()) {
|
||||||
|
reportAttributesNotAllowed(pendingAttributes, "Attributes must precede a valid declaration");
|
||||||
|
}
|
||||||
|
final var token = cursor.peek();
|
||||||
|
context.report(token, ParseErrors.E_PARSE_UNEXPECTED_TOKEN,
|
||||||
|
"Expected top-level declaration ('fn', 'declare', 'implements') or import");
|
||||||
|
topDecls.add(new PbsAst.InvalidDecl("unexpected top-level token", context.span(token.start(), token.end())));
|
||||||
|
synchronizeTopLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
final var eof = cursor.peek();
|
||||||
|
return new PbsAst.File(
|
||||||
|
ReadOnlyList.wrap(imports),
|
||||||
|
ReadOnlyList.wrap(topDecls),
|
||||||
|
context.span(0, eof.end()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void rejectAttributesBeforeUnsupportedTopDecl(
|
||||||
|
final ReadOnlyList<PbsAst.Attribute> attributes,
|
||||||
|
final String targetSurface) {
|
||||||
|
if (attributes.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reportAttributesNotAllowed(attributes, "Attributes are not allowed before " + targetSurface);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reportAttributesNotAllowed(
|
||||||
|
final ReadOnlyList<PbsAst.Attribute> attributes,
|
||||||
|
final String message) {
|
||||||
|
for (final var attribute : attributes) {
|
||||||
|
p.studio.compiler.source.diagnostics.Diagnostics.error(context.diagnostics(),
|
||||||
|
ParseErrors.E_PARSE_ATTRIBUTES_NOT_ALLOWED.name(),
|
||||||
|
message,
|
||||||
|
attribute.span());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isOrdinaryMode() {
|
||||||
|
return context.parseMode() == PbsParser.ParseMode.ORDINARY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void synchronizeTopLevel() {
|
||||||
|
while (!cursor.isAtEnd()) {
|
||||||
|
if (isTopLevelRestartToken(cursor.peek().kind())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cursor.match(PbsTokenKind.SEMICOLON)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cursor.advance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isTopLevelRestartToken(final PbsTokenKind kind) {
|
||||||
|
return kind == PbsTokenKind.FN
|
||||||
|
|| kind == PbsTokenKind.IMPORT
|
||||||
|
|| kind == PbsTokenKind.DECLARE
|
||||||
|
|| kind == PbsTokenKind.IMPLEMENTS
|
||||||
|
|| kind == PbsTokenKind.LEFT_BRACKET;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user