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.PbsTokenKind;
|
||||
import p.studio.compiler.source.Span;
|
||||
import p.studio.compiler.source.diagnostics.RelatedSpan;
|
||||
import p.studio.compiler.source.identifiers.FileId;
|
||||
import p.studio.utilities.structures.ReadOnlyList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* High-level manual parser for PBS source files.
|
||||
@ -33,6 +30,8 @@ public final class PbsParser {
|
||||
private final PbsExprParser exprParser;
|
||||
private final PbsAttributeParser attributeParser;
|
||||
private final PbsTypeParser typeParser;
|
||||
private final PbsDeclarationParser declarationParser;
|
||||
private final PbsTopLevelParser topLevelParser;
|
||||
|
||||
private PbsParser(
|
||||
final ReadOnlyList<PbsToken> tokens,
|
||||
@ -44,6 +43,15 @@ public final class PbsParser {
|
||||
this.exprParser = new PbsExprParser(context, this::parseExpressionSurfaceBlock);
|
||||
this.attributeParser = new PbsAttributeParser(context, this::isTopLevelRestartToken);
|
||||
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.
|
||||
*/
|
||||
private 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 = 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()));
|
||||
return topLevelParser.parseFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses import syntax and stores it in the AST.
|
||||
*/
|
||||
private 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()));
|
||||
return declarationParser.parseImport(importToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a module reference such as {@code @core:math/tools}.
|
||||
*/
|
||||
private 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));
|
||||
return declarationParser.parseModuleRef();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -200,260 +99,49 @@ public final class PbsParser {
|
||||
private 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 = 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()));
|
||||
return declarationParser.parseDeclareTopDecl(declareToken, pendingAttributes);
|
||||
}
|
||||
|
||||
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 = 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()));
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
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 = 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()));
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
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 = 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;
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private void rejectAttributesBeforeUnsupportedTopDecl(
|
||||
final ReadOnlyList<PbsAst.Attribute> attributes,
|
||||
final String targetSurface) {
|
||||
if (attributes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
reportAttributesNotAllowed(
|
||||
attributes,
|
||||
"Attributes are not allowed before " + targetSurface);
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private boolean isNotInterfaceMode() {
|
||||
return context.parseMode() != ParseMode.INTERFACE_MODULE;
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private boolean isOrdinaryMode() {
|
||||
return context.parseMode() == ParseMode.ORDINARY;
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
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 = consumeDeclarationTerminator();
|
||||
}
|
||||
|
||||
return new PbsAst.StructDecl(
|
||||
name.lexeme(),
|
||||
ReadOnlyList.wrap(fields),
|
||||
ReadOnlyList.wrap(methods),
|
||||
ReadOnlyList.wrap(ctors),
|
||||
hasBody,
|
||||
span(declareToken.start(), end));
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
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 = 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;
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private ReadOnlyList<PbsAst.Attribute> parseAttributeList() {
|
||||
@ -461,234 +149,55 @@ public final class PbsParser {
|
||||
}
|
||||
|
||||
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());
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
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 = 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()));
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
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 = 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()));
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
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()));
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
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()));
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
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())));
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a top-level function declaration.
|
||||
*/
|
||||
private PbsAst.FunctionDecl parseFunction(final PbsToken fnToken) {
|
||||
return parseFunctionLike(fnToken);
|
||||
return declarationParser.parseFunction(fnToken);
|
||||
}
|
||||
|
||||
private PbsAst.FunctionDecl parseFunctionLike(final PbsToken fnToken) {
|
||||
final var name = consumeCallableName();
|
||||
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()));
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
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 = 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));
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
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;
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses `declare callback` declaration.
|
||||
*/
|
||||
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 = 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()));
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -697,60 +206,14 @@ public final class PbsParser {
|
||||
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 = 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()));
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses `implements Contract for Owner using binder { ... }`.
|
||||
*/
|
||||
private 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 = consumeDeclarationTerminator();
|
||||
}
|
||||
|
||||
return new PbsAst.ImplementsDecl(
|
||||
contractName.lexeme(),
|
||||
ownerName.lexeme(),
|
||||
binderName.lexeme(),
|
||||
ReadOnlyList.wrap(methods),
|
||||
span(implementsToken.start(), end));
|
||||
return declarationParser.parseImplementsDeclaration(implementsToken);
|
||||
}
|
||||
|
||||
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