Replace ANTLR with hand-rolled parser (#917)

Co-authored-by: Kushal Pisavadia <kushi.p@gmail.com>
Co-authored-by: Daniel Chao <daniel.h.chao@gmail.com>
This commit is contained in:
Islon Scherer
2025-02-12 18:11:00 +01:00
committed by GitHub
parent 7c3f8ad261
commit b526902bf0
197 changed files with 11678 additions and 3274 deletions

View File

@@ -1,387 +0,0 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
lexer grammar PklLexer;
@header {
package org.pkl.core.parser.antlr;
}
@members {
class StringInterpolationScope {
int parenLevel = 0;
int poundLength = 0;
}
java.util.Deque<StringInterpolationScope> interpolationScopes = new java.util.ArrayDeque<>();
StringInterpolationScope interpolationScope;
{ pushInterpolationScope(); }
void pushInterpolationScope() {
interpolationScope = new StringInterpolationScope();
interpolationScopes.push(interpolationScope);
}
void incParenLevel() {
interpolationScope.parenLevel += 1;
}
void decParenLevel() {
if (interpolationScope.parenLevel == 0) {
// guard against syntax errors
if (interpolationScopes.size() > 1) {
interpolationScopes.pop();
interpolationScope = interpolationScopes.peek();
popMode();
}
} else {
interpolationScope.parenLevel -= 1;
}
}
boolean isPounds() {
// optimize for common cases (0, 1)
switch (interpolationScope.poundLength) {
case 0: return true;
case 1: return _input.LA(1) == '#';
default:
int poundLength = interpolationScope.poundLength;
for (int i = 1; i <= poundLength; i++) {
if (_input.LA(i) != '#') return false;
}
return true;
}
}
boolean isQuote() {
return _input.LA(1) == '"';
}
boolean endsWithPounds(String text) {
assert text.length() >= 2;
// optimize for common cases (0, 1)
switch (interpolationScope.poundLength) {
case 0: return true;
case 1: return text.charAt(text.length() - 1) == '#';
default:
int poundLength = interpolationScope.poundLength;
int textLength = text.length();
if (textLength < poundLength) return false;
int stop = textLength - poundLength;
for (int i = textLength - 1; i >= stop; i--) {
if (text.charAt(i) != '#') return false;
}
return true;
}
}
void removeBackTicks() {
String text = getText();
setText(text.substring(1, text.length() - 1));
}
// look ahead in predicate rather than consume in grammar so that newlines
// go to NewlineSemicolonChannel, which is important for consumers of that channel
boolean isNewlineOrEof() {
int input = _input.LA(1);
return input == '\n' || input == '\r' || input == IntStream.EOF;
}
}
channels {
NewlineSemicolonChannel,
WhitespaceChannel,
CommentsChannel,
ShebangChannel
}
ABSTRACT : 'abstract';
AMENDS : 'amends';
AS : 'as';
CLASS : 'class';
CONST : 'const';
ELSE : 'else';
EXTENDS : 'extends';
EXTERNAL : 'external';
FALSE : 'false';
FIXED : 'fixed';
FOR : 'for';
FUNCTION : 'function';
HIDDEN_ : 'hidden';
IF : 'if';
IMPORT : 'import';
IMPORT_GLOB : 'import*';
IN : 'in';
IS : 'is';
LET : 'let';
LOCAL : 'local';
MODULE : 'module';
NEW : 'new';
NOTHING : 'nothing';
NULL : 'null';
OPEN : 'open';
OUT : 'out';
OUTER : 'outer';
READ : 'read';
READ_GLOB : 'read*';
READ_OR_NULL : 'read?';
SUPER : 'super';
THIS : 'this';
THROW : 'throw';
TRACE : 'trace';
TRUE : 'true';
TYPE_ALIAS : 'typealias';
UNKNOWN : 'unknown';
WHEN : 'when';
// reserved for future use, but not used today
PROTECTED : 'protected';
OVERRIDE : 'override';
RECORD : 'record';
DELETE : 'delete';
CASE : 'case';
SWITCH : 'switch';
VARARG : 'vararg';
LPAREN : '(' { incParenLevel(); };
RPAREN : ')' { decParenLevel(); };
LBRACE : '{';
RBRACE : '}';
LBRACK : '[';
RBRACK : ']';
LPRED : '[['; // No RPRED, because that lexes too eager to allow nested index expressions, e.g. foo[bar[baz]]
COMMA : ',';
DOT : '.';
QDOT : '?.';
COALESCE : '??';
NON_NULL : '!!';
AT : '@';
ASSIGN : '=';
GT : '>';
LT : '<';
NOT : '!';
QUESTION : '?';
COLON : ':';
ARROW : '->';
EQUAL : '==';
NOT_EQUAL : '!=';
LTE : '<=';
GTE : '>=';
AND : '&&';
OR : '||';
PLUS : '+';
MINUS : '-';
POW : '**';
STAR : '*';
DIV : '/';
INT_DIV : '~/';
MOD : '%';
UNION : '|';
PIPE : '|>';
SPREAD : '...';
QSPREAD : '...?';
UNDERSCORE : '_';
SLQuote : '#'* '"' { interpolationScope.poundLength = getText().length() - 1; } -> pushMode(SLString);
MLQuote : '#'* '"""' { interpolationScope.poundLength = getText().length() - 3; } -> pushMode(MLString);
IntLiteral
: DecimalLiteral
| HexadecimalLiteral
| BinaryLiteral
| OctalLiteral
;
// leading zeros are allowed (cf. Swift)
fragment DecimalLiteral
: DecimalDigit DecimalDigitCharacters?
;
fragment DecimalDigitCharacters
: DecimalDigitCharacter+
;
fragment DecimalDigitCharacter
: DecimalDigit
| '_'
;
fragment DecimalDigit
: [0-9]
;
fragment HexadecimalLiteral
: '0x' HexadecimalCharacter+ // intentionally allow underscore after '0x'; e.g. `0x_ab`. We will throw an error in AstBuilder.
;
fragment HexadecimalCharacter
: [0-9a-fA-F_]
;
fragment BinaryLiteral
: '0b' BinaryCharacter+ // intentionally allow underscore after '0b'; e.g. `0b_11`. We will throw an error in AstBuilder.
;
fragment BinaryCharacter
: [01_]
;
fragment OctalLiteral
: '0o' OctalCharacter+ // intentionally allow underscore after '0o'; e.g. `0o_34`. We will throw an error in AstBuilder.
;
fragment OctalCharacter
: [0-7_]
;
FloatLiteral
: DecimalLiteral? '.' '_'? DecimalLiteral Exponent? // intentionally allow underscore. We will throw an error in AstBuilder.
| DecimalLiteral Exponent
;
fragment Exponent
: [eE] [+-]? '_'? DecimalLiteral // intentionally allow underscore. We will throw an error in AstBuilder.
;
Identifier
: RegularIdentifier
| QuotedIdentifier { removeBackTicks(); }
;
// Note: Keep in sync with Lexer.isRegularIdentifier()
fragment RegularIdentifier
: IdentifierStart IdentifierPart*
;
fragment QuotedIdentifier
: '`' (~'`')+ '`'
;
fragment
IdentifierStart
: [a-zA-Z$_] // handle common cases without a predicate
| . {Character.isUnicodeIdentifierStart(_input.LA(-1))}?
;
fragment
IdentifierPart
: [a-zA-Z0-9$_] // handle common cases without a predicate
| . {Character.isUnicodeIdentifierPart(_input.LA(-1))}?
;
NewlineSemicolon
: [\r\n;]+ -> channel(NewlineSemicolonChannel)
;
// Note: Java, Scala, and Swift treat \f as whitespace; Dart doesn't.
// Python and C also include vertical tab.
// C# also includes Unicode class Zs (separator, space).
Whitespace
: [ \t\f]+ -> channel(WhitespaceChannel)
;
DocComment
: ([ \t\f]* '///' .*? (Newline|EOF))+
;
BlockComment
: '/*' (BlockComment | .)*? '*/' -> channel(CommentsChannel)
;
LineComment
: '//' .*? {isNewlineOrEof()}? -> channel(CommentsChannel)
;
ShebangComment
: '#!' .*? {isNewlineOrEof()}? -> channel(ShebangChannel)
;
// strict: '\\' Pounds 'u{' HexDigit (HexDigit (HexDigit (HexDigit (HexDigit (HexDigit (HexDigit HexDigit? )?)?)?)?)?)? '}'
fragment UnicodeEscape
: '\\' Pounds 'u{' ~[}\r\n "]* '}'?
;
// strict: '\\' Pounds [tnr"\\]
fragment CharacterEscape
: '\\' Pounds .
;
fragment Pounds
: { interpolationScope.poundLength == 0 }?
| '#' { interpolationScope.poundLength == 1 }?
| '#'+ { endsWithPounds(getText()) }?
;
fragment Newline
: '\n' | '\r' '\n'?
;
mode SLString;
// strict: '"' Pounds
SLEndQuote
: ('"' Pounds | Newline ) -> popMode
;
SLInterpolation
: '\\' Pounds '(' { pushInterpolationScope(); } -> pushMode(DEFAULT_MODE)
;
SLUnicodeEscape
: UnicodeEscape
;
SLCharacterEscape
: CharacterEscape
;
SLCharacters
: ~["\\\r\n]+ SLCharacters?
| ["\\] {!isPounds()}? SLCharacters?
;
mode MLString;
MLEndQuote
: '"""' Pounds -> popMode
;
MLInterpolation
: '\\' Pounds '(' { pushInterpolationScope(); } -> pushMode(DEFAULT_MODE)
;
MLUnicodeEscape
: UnicodeEscape
;
MLCharacterEscape
: CharacterEscape
;
MLNewline
: Newline
;
MLCharacters
: ~["\\\r\n]+ MLCharacters?
| ('\\' | '"""') {!isPounds()}? MLCharacters?
| '"' '"'? {!isQuote()}? MLCharacters?
;

View File

@@ -1,254 +0,0 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
parser grammar PklParser;
@header {
package org.pkl.core.parser.antlr;
}
@members {
/**
* Returns true if and only if the next token to be consumed is not preceded by a newline or semicolon.
*/
boolean noNewlineOrSemicolon() {
for (int i = _input.index() - 1; i >= 0; i--) {
Token token = _input.get(i);
int channel = token.getChannel();
if (channel == PklLexer.DEFAULT_TOKEN_CHANNEL) return true;
if (channel == PklLexer.NewlineSemicolonChannel) return false;
}
return true;
}
}
options {
tokenVocab = PklLexer;
}
replInput
: ((moduleDecl
| importClause
| clazz
| typeAlias
| classProperty
| classMethod
| expr))* EOF
;
exprInput
: expr EOF
;
module
: moduleDecl? (is+=importClause)* ((cs+=clazz | ts+=typeAlias | ps+=classProperty | ms+=classMethod))* EOF
;
moduleDecl
: t=DocComment? annotation* moduleHeader
;
moduleHeader
: modifier* 'module' qualifiedIdentifier moduleExtendsOrAmendsClause?
| moduleExtendsOrAmendsClause
;
moduleExtendsOrAmendsClause
: t=('extends' | 'amends') stringConstant
;
importClause
: t=('import' | 'import*') stringConstant ('as' Identifier)?
;
clazz
: t=DocComment? annotation* classHeader classBody?
;
classHeader
: modifier* 'class' Identifier typeParameterList? ('extends' type)?
;
modifier
: t=('external' | 'abstract' | 'open' | 'local' | 'hidden' | 'fixed' | 'const')
;
classBody
: '{' ((ps+=classProperty | ms+=classMethod))* err='}'?
;
typeAlias
: t=DocComment? annotation* typeAliasHeader '=' type
;
typeAliasHeader
: modifier* 'typealias' Identifier typeParameterList?
;
// allows `foo: Bar { ... }` s.t. AstBuilder can provide better error message
classProperty
: t=DocComment? annotation* modifier* Identifier (typeAnnotation | typeAnnotation? ('=' expr | objectBody+))
;
classMethod
: t=DocComment? annotation* methodHeader ('=' expr)?
;
methodHeader
: modifier* 'function' Identifier typeParameterList? parameterList typeAnnotation?
;
parameterList
: '(' (ts+=parameter (errs+=','? ts+=parameter)*)? err=')'?
;
argumentList
: {noNewlineOrSemicolon()}? '(' (es+=expr (errs+=','? es+=expr)*)? err=')'?
;
annotation
: '@' type objectBody?
;
qualifiedIdentifier
: ts+=Identifier ('.' ts+=Identifier)*
;
typeAnnotation
: ':' type
;
typeParameterList
: '<' ts+=typeParameter (errs+=','? ts+=typeParameter)* err='>'?
;
typeParameter
: t=('in' | 'out')? Identifier
;
typeArgumentList
: '<' ts+=type (errs+=','? ts+=type)* err='>'?
;
type
: 'unknown' # unknownType
| 'nothing' # nothingType
| 'module' # moduleType
| stringConstant # stringLiteralType
| qualifiedIdentifier typeArgumentList? # declaredType
| '(' type err=')'? # parenthesizedType
| type '?' # nullableType
| type {noNewlineOrSemicolon()}? t='(' es+=expr (errs+=','? es+=expr)* err=')'? # constrainedType
| '*' u=type # defaultUnionType
| l=type '|' r=type # unionType
| t='(' (ps+=type (errs+=','? ps+=type)*)? err=')'? '->' r=type # functionType
;
typedIdentifier
: Identifier typeAnnotation?
;
parameter
: '_'
| typedIdentifier
;
// Many languages (e.g., Python) give `**` higher precedence than unary minus.
// The reason is that in Math, `-a^2` means `-(a^2)`.
// To avoid confusion, JS rejects `-a**2` and requires explicit parens.
// `-3.abs()` is a similar problem, handled differently by different languages.
expr
: 'this' # thisExpr
| 'outer' # outerExpr
| 'module' # moduleExpr
| 'null' # nullLiteral
| 'true' # trueLiteral
| 'false' # falseLiteral
| IntLiteral # intLiteral
| FloatLiteral # floatLiteral
| 'throw' '(' expr err=')'? # throwExpr
| 'trace' '(' expr err=')'? # traceExpr
| t=('import' | 'import*') '(' stringConstant err=')'? # importExpr
| t=('read' | 'read?' | 'read*') '(' expr err=')'? # readExpr
| Identifier argumentList? # unqualifiedAccessExpr
| t=SLQuote singleLineStringPart* t2=SLEndQuote # singleLineStringLiteral
| t=MLQuote multiLineStringPart* t2=MLEndQuote # multiLineStringLiteral
| t='new' type? objectBody # newExpr
| expr objectBody # amendExpr
| 'super' '.' Identifier argumentList? # superAccessExpr
| 'super' t='[' e=expr err=']'? # superSubscriptExpr
| expr t=('.' | '?.') Identifier argumentList? # qualifiedAccessExpr
| l=expr {noNewlineOrSemicolon()}? t='[' r=expr err=']'? # subscriptExpr
| expr '!!' # nonNullExpr
| '-' expr # unaryMinusExpr
| '!' expr # logicalNotExpr
| <assoc=right> l=expr t='**' r=expr # exponentiationExpr
// for some reason, moving rhs of rules starting with `l=expr` into a
// separate rule (to avoid repeated parsing of `expr`) messes up precedence
| l=expr t=('*' | '/' | '~/' | '%') r=expr # multiplicativeExpr
| l=expr (t='+' | {noNewlineOrSemicolon()}? t='-') r=expr # additiveExpr
| l=expr t=('<' | '>' | '<=' | '>=') r=expr # comparisonExpr
| l=expr t=('is' | 'as') r=type # typeTestExpr
| l=expr t=('==' | '!=') r=expr # equalityExpr
| l=expr t='&&' r=expr # logicalAndExpr
| l=expr t='||' r=expr # logicalOrExpr
| l=expr t='|>' r=expr # pipeExpr
| <assoc=right> l=expr t='??' r=expr # nullCoalesceExpr
| 'if' '(' c=expr err=')'? l=expr 'else' r=expr # ifExpr
| 'let' '(' parameter '=' l=expr err=')'? r=expr # letExpr
| parameterList '->' expr # functionLiteral
| '(' expr err=')'? # parenthesizedExpr
;
objectBody
: '{' (ps+=parameter (errs+=','? ps+=parameter)* '->')? objectMember* err='}'?
;
objectMember
: modifier* Identifier (typeAnnotation? '=' expr | objectBody+) # objectProperty
| methodHeader '=' expr # objectMethod
| t='[[' k=expr err1=']'? err2=']'? ('=' v=expr | objectBody+) # memberPredicate
| t='[' k=expr err1=']'? err2=']'? ('=' v=expr | objectBody+) # objectEntry
| expr # objectElement
| ('...' | '...?') expr # objectSpread
| 'when' '(' e=expr err=')'? (b1=objectBody ('else' b2=objectBody)?) # whenGenerator
| 'for' '(' t1=parameter (',' t2=parameter)? 'in' e=expr err=')'? objectBody # forGenerator
;
stringConstant
: t=SLQuote (ts+=SLCharacters | ts+=SLCharacterEscape | ts+=SLUnicodeEscape)* t2=SLEndQuote
;
singleLineStringPart
: SLInterpolation e=expr ')'
| (ts+=SLCharacters | ts+=SLCharacterEscape | ts+=SLUnicodeEscape)+
;
multiLineStringPart
: MLInterpolation e=expr ')'
| (ts+=MLCharacters | ts+=MLNewline | ts+=MLCharacterEscape | ts+=MLUnicodeEscape)+
;
// intentionally unused
//TODO: we get a "Mismatched Input" error unless we introduce this parser rule. Why?
reservedKeyword
: 'protected'
| 'override'
| 'record'
| 'delete'
| 'case'
| 'switch'
| 'vararg'
;

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,122 +18,131 @@ package org.pkl.core.ast.builder;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
import java.util.List;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.pkl.core.parser.antlr.PklLexer;
import org.pkl.core.parser.antlr.PklParser.ModifierContext;
import org.pkl.core.parser.antlr.PklParserBaseVisitor;
import org.pkl.core.PklBugException;
import org.pkl.core.parser.BaseParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.parser.ast.DocComment;
import org.pkl.core.parser.ast.Modifier;
import org.pkl.core.parser.ast.Modifier.ModifierValue;
import org.pkl.core.parser.ast.Node;
import org.pkl.core.parser.ast.StringConstant;
import org.pkl.core.parser.ast.StringConstantPart;
import org.pkl.core.parser.ast.StringConstantPart.ConstantPart;
import org.pkl.core.parser.ast.StringConstantPart.StringEscape;
import org.pkl.core.parser.ast.StringConstantPart.StringUnicodeEscape;
import org.pkl.core.runtime.VmExceptionBuilder;
import org.pkl.core.util.Nullable;
public abstract class AbstractAstBuilder<T> extends PklParserBaseVisitor<T> {
public abstract class AbstractAstBuilder<T> extends BaseParserVisitor<T> {
protected final Source source;
protected abstract VmExceptionBuilder exceptionBuilder();
protected AbstractAstBuilder(Source source) {
this.source = source;
}
protected abstract VmExceptionBuilder exceptionBuilder();
protected String doVisitSingleLineConstantStringPart(List<Token> ts) {
if (ts.isEmpty()) return "";
protected String doVisitStringConstant(StringConstant expr) {
return doVisitStringConstant(expr.getStrParts().getParts());
}
protected String doVisitStringConstant(List<StringConstantPart> strs) {
var builder = new StringBuilder();
for (var token : ts) {
switch (token.getType()) {
case PklLexer.SLCharacters -> builder.append(token.getText());
case PklLexer.SLCharacterEscape -> builder.append(parseCharacterEscapeSequence(token));
case PklLexer.SLUnicodeEscape -> builder.appendCodePoint(parseUnicodeEscapeSequence(token));
default -> throw exceptionBuilder().unreachableCode().build();
}
for (var part : strs) {
builder.append(doVisitStringConstantPart(part));
}
return builder.toString();
}
protected int parseUnicodeEscapeSequence(Token token) {
var text = token.getText();
var lastIndex = text.length() - 1;
if (text.charAt(lastIndex) != '}') {
throw exceptionBuilder()
.evalError("unterminatedUnicodeEscapeSequence", token.getText())
.withSourceSection(createSourceSection(token))
.build();
protected String doVisitStringConstantPart(StringConstantPart part) {
if (part instanceof ConstantPart cp) {
return cp.getStr();
}
if (part instanceof StringUnicodeEscape ue) {
var codePoint = parseUnicodeEscapeSequence(ue);
return Character.toString(codePoint);
}
if (part instanceof StringEscape se) {
return switch (se.getType()) {
case NEWLINE -> "\n";
case QUOTE -> "\"";
case BACKSLASH -> "\\";
case TAB -> "\t";
case RETURN -> "\r";
};
}
throw PklBugException.unreachableCode();
}
protected int parseUnicodeEscapeSequence(StringUnicodeEscape escape) {
var text = escape.getEscape();
var lastIndex = text.length() - 1;
var startIndex = text.indexOf('{', 2);
assert startIndex != -1; // guaranteed by lexer
try {
return Integer.parseInt(text.substring(startIndex + 1, lastIndex), 16);
} catch (NumberFormatException e) {
throw exceptionBuilder()
.evalError("invalidUnicodeEscapeSequence", token.getText(), text.substring(0, startIndex))
.withSourceSection(createSourceSection(token))
.evalError("invalidUnicodeEscapeSequence", text, text.substring(0, startIndex))
.withSourceSection(createSourceSection(escape))
.build();
}
}
protected String parseCharacterEscapeSequence(Token token) {
var text = token.getText();
var lastChar = text.charAt(text.length() - 1);
return switch (lastChar) {
case 'n' -> "\n";
case 'r' -> "\r";
case 't' -> "\t";
case '"' -> "\"";
case '\\' -> "\\";
default ->
throw exceptionBuilder()
.evalError(
"invalidCharacterEscapeSequence", text, text.substring(0, text.length() - 1))
.withSourceSection(createSourceSection(token))
.build();
};
protected final @Nullable SourceSection createSourceSection(@Nullable Node node) {
return node == null
? null
: source.createSection(node.span().charIndex(), node.span().length());
}
protected final SourceSection createSourceSection(ParserRuleContext ctx) {
return createSourceSection(ctx.getStart(), ctx.getStop());
protected SourceSection @Nullable [] createDocSourceSection(@Nullable DocComment node) {
return createDocSourceSection(source, node);
}
protected final SourceSection createSourceSection(TerminalNode node) {
return createSourceSection(node.getSymbol());
}
protected final @Nullable SourceSection createSourceSection(@Nullable Token token) {
return token != null ? createSourceSection(token, token) : null;
}
protected final SourceSection createSourceSection(Token start, Token stop) {
return source.createSection(
start.getStartIndex(), stop.getStopIndex() - start.getStartIndex() + 1);
protected SourceSection createSourceSection(Span span) {
return source.createSection(span.charIndex(), span.length());
}
protected final SourceSection createSourceSection(
List<? extends ModifierContext> modifierCtxs, int symbol) {
List<? extends Modifier> modifiers, ModifierValue symbol) {
var modifierCtx =
modifierCtxs.stream().filter(ctx -> ctx.t.getType() == symbol).findFirst().orElseThrow();
modifiers.stream().filter(mod -> mod.getValue() == symbol).findFirst().orElseThrow();
return createSourceSection(modifierCtx);
}
protected static SourceSection createSourceSection(Source source, ParserRuleContext ctx) {
var start = ctx.start.getStartIndex();
var stop = ctx.stop.getStopIndex();
return source.createSection(start, stop - start + 1);
protected static @Nullable SourceSection createSourceSection(Source source, @Nullable Node node) {
if (node == null) return null;
return createSourceSection(source, node.span());
}
protected static @Nullable SourceSection createSourceSection(
Source source, @Nullable Token token) {
if (token == null) return null;
protected static SourceSection @Nullable [] createDocSourceSection(
Source source, @Nullable DocComment node) {
if (node == null) return null;
var spans = node.getSpans();
var sections = new SourceSection[spans.size()];
for (var i = 0; i < sections.length; i++) {
var span = spans.get(i);
sections[i] = source.createSection(span.charIndex(), span.length());
}
return sections;
}
var start = token.getStartIndex();
var stop = token.getStopIndex();
return source.createSection(start, stop - start + 1);
protected static SourceSection createSourceSection(Source source, Span span) {
return source.createSection(span.charIndex(), span.length());
}
protected SourceSection startOf(Node node) {
return startOf(node.span());
}
protected SourceSection startOf(Span span) {
return source.createSection(span.charIndex(), 1);
}
protected SourceSection shrinkLeft(SourceSection section, int length) {
return source.createSection(section.getCharIndex() + length, section.getCharLength() - length);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,14 +24,17 @@ import java.util.List;
import org.pkl.core.ast.builder.ImportsAndReadsParser.Entry;
import org.pkl.core.module.ModuleKey;
import org.pkl.core.module.ResolvedModuleKey;
import org.pkl.core.parser.LexParseException;
import org.pkl.core.parser.Parser;
import org.pkl.core.parser.antlr.PklLexer;
import org.pkl.core.parser.antlr.PklParser.ImportClauseContext;
import org.pkl.core.parser.antlr.PklParser.ImportExprContext;
import org.pkl.core.parser.antlr.PklParser.ModuleExtendsOrAmendsClauseContext;
import org.pkl.core.parser.antlr.PklParser.ReadExprContext;
import org.pkl.core.parser.antlr.PklParser.SingleLineStringLiteralContext;
import org.pkl.core.parser.ParserError;
import org.pkl.core.parser.ast.Expr;
import org.pkl.core.parser.ast.Expr.ImportExpr;
import org.pkl.core.parser.ast.Expr.ReadExpr;
import org.pkl.core.parser.ast.Expr.ReadType;
import org.pkl.core.parser.ast.Expr.SingleLineStringLiteralExpr;
import org.pkl.core.parser.ast.ExtendsOrAmendsClause;
import org.pkl.core.parser.ast.ExtendsOrAmendsClause.Type;
import org.pkl.core.parser.ast.ImportClause;
import org.pkl.core.parser.ast.StringPart.StringConstantParts;
import org.pkl.core.runtime.VmExceptionBuilder;
import org.pkl.core.runtime.VmUtils;
import org.pkl.core.util.IoUtils;
@@ -68,7 +71,7 @@ public class ImportsAndReadsParser extends AbstractAstBuilder<@Nullable List<Ent
var importListParser = new ImportsAndReadsParser(source);
try {
return parser.parseModule(text).accept(importListParser);
} catch (LexParseException e) {
} catch (ParserError e) {
var moduleName = IoUtils.inferModuleName(moduleKey);
throw VmUtils.toVmException(e, source, moduleName);
}
@@ -84,62 +87,63 @@ public class ImportsAndReadsParser extends AbstractAstBuilder<@Nullable List<Ent
}
@Override
public @Nullable List<Entry> visitModuleExtendsOrAmendsClause(
ModuleExtendsOrAmendsClauseContext ctx) {
var importStr = doVisitSingleLineConstantStringPart(ctx.stringConstant().ts);
var sourceSection = createSourceSection(ctx.stringConstant());
public @Nullable List<Entry> visitExtendsOrAmendsClause(ExtendsOrAmendsClause decl) {
var importStr = doVisitStringConstant(decl.getUrl());
var sourceSection = createSourceSection(decl.getUrl());
assert sourceSection != null;
return Collections.singletonList(
new Entry(
true, false, ctx.EXTENDS() != null, ctx.AMENDS() != null, importStr, sourceSection));
true,
false,
decl.getType() == Type.EXTENDS,
decl.getType() == Type.AMENDS,
importStr,
sourceSection));
}
@Override
public List<Entry> visitImportClause(ImportClauseContext ctx) {
var importStr = doVisitSingleLineConstantStringPart(ctx.stringConstant().ts);
var sourceSection = createSourceSection(ctx.stringConstant());
public List<Entry> visitImportClause(ImportClause imp) {
var importStr = doVisitStringConstant(imp.getImportStr());
var sourceSection = createSourceSection(imp.getImportStr());
assert sourceSection != null;
return Collections.singletonList(
new Entry(
true, ctx.t.getType() == PklLexer.IMPORT_GLOB, false, false, importStr, sourceSection));
new Entry(true, imp.isGlob(), false, false, importStr, sourceSection));
}
@Override
public List<Entry> visitImportExpr(ImportExprContext ctx) {
var importStr = doVisitSingleLineConstantStringPart(ctx.stringConstant().ts);
var sourceSection = createSourceSection(ctx.stringConstant());
public List<Entry> visitImportExpr(ImportExpr expr) {
var importStr = doVisitStringConstant(expr.getImportStr());
var sourceSection = createSourceSection(expr.getImportStr());
assert sourceSection != null;
return Collections.singletonList(
new Entry(
true, ctx.t.getType() == PklLexer.IMPORT_GLOB, false, false, importStr, sourceSection));
new Entry(true, expr.isGlob(), false, false, importStr, sourceSection));
}
@Override
public List<Entry> visitReadExpr(ReadExprContext ctx) {
var expr = ctx.expr();
if (!(expr instanceof SingleLineStringLiteralContext slCtx)) {
public @Nullable List<Entry> visitReadExpr(ReadExpr expr) {
return doVisitReadExpr(expr.getExpr(), expr.getReadType() == ReadType.GLOB);
}
@SuppressWarnings("DataFlowIssue")
public List<Entry> doVisitReadExpr(Expr expr, boolean isGlob) {
if (!(expr instanceof SingleLineStringLiteralExpr slStr)) {
return Collections.emptyList();
}
// best-effort approach; only collect read expressions that are string constants.
var singleParts = slCtx.singleLineStringPart();
String importString;
var singleParts = slStr.getParts();
if (singleParts.isEmpty()) {
importString = "";
} else if (singleParts.size() == 1) {
var ts = singleParts.get(0).ts;
if (!ts.isEmpty()) {
importString = doVisitSingleLineConstantStringPart(ts);
} else {
return Collections.emptyList();
}
} else if (singleParts.size() == 1
&& singleParts.get(0) instanceof StringConstantParts cparts
&& !cparts.getParts().isEmpty()) {
importString = doVisitStringConstant(cparts.getParts());
} else {
return Collections.emptyList();
}
return Collections.singletonList(
new Entry(
false,
ctx.t.getType() == PklLexer.READ_GLOB,
false,
false,
importString,
createSourceSection(slCtx)));
new Entry(false, isGlob, false, false, importString, createSourceSection(slStr)));
}
@Override
@@ -156,4 +160,9 @@ public class ImportsAndReadsParser extends AbstractAstBuilder<@Nullable List<Ent
ret.addAll(nextResult);
return ret;
}
@Override
protected List<Entry> defaultValue() {
return List.of();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,7 +23,7 @@ import org.pkl.core.runtime.VmTyped;
import org.pkl.core.util.Nullable;
public abstract class ClassMember extends Member {
protected final @Nullable SourceSection docComment;
protected final SourceSection @Nullable [] docComment;
protected final List<VmTyped> annotations;
// store prototype instead of class because the former is needed much more often
private final VmTyped owner;
@@ -34,7 +34,7 @@ public abstract class ClassMember extends Member {
int modifiers,
Identifier name,
String qualifiedName,
@Nullable SourceSection docComment,
SourceSection @Nullable [] docComment,
List<VmTyped> annotations,
VmTyped owner) {
@@ -45,7 +45,7 @@ public abstract class ClassMember extends Member {
this.owner = owner;
}
public final @Nullable SourceSection getDocComment() {
public final SourceSection @Nullable [] getDocComment() {
return docComment;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,7 +41,7 @@ public final class ClassMethod extends ClassMember {
int modifiers,
Identifier name,
String qualifiedName,
@Nullable SourceSection docComment,
SourceSection @Nullable [] docComment,
List<VmTyped> annotations,
VmTyped owner,
List<TypeParameter> typeParameters,

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,7 +35,7 @@ import org.pkl.core.util.Nullable;
@NodeInfo(shortName = "class")
public final class ClassNode extends ExpressionNode {
private final SourceSection headerSection;
private final @Nullable SourceSection docComment;
private final SourceSection @Nullable [] docComment;
@Children private final ExpressionNode[] annotationNodes;
private final int modifiers;
private final PClassInfo<?> classInfo;
@@ -52,7 +52,7 @@ public final class ClassNode extends ExpressionNode {
public ClassNode(
SourceSection section,
SourceSection headerSection,
@Nullable SourceSection docComment,
SourceSection @Nullable [] docComment,
ExpressionNode[] annotationNodes,
int modifiers,
PClassInfo<?> classInfo,

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,7 +33,7 @@ public final class ClassProperty extends ClassMember {
int modifiers,
Identifier name,
String qualifiedName,
@Nullable SourceSection docComment,
SourceSection @Nullable [] docComment,
List<VmTyped> annotations,
VmTyped owner,
@Nullable PropertyTypeNode typeNode,

View File

@@ -140,7 +140,7 @@ public final class FunctionNode extends RegularMemberNode {
public PClass.Method export(
PClass owner,
@Nullable SourceSection docComment,
SourceSection @Nullable [] docComment,
List<VmTyped> annotations,
int modifiers,
List<TypeParameter> typeParameters) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,7 +31,7 @@ import org.pkl.core.util.Nullable;
public final class TypeAliasNode extends ExpressionNode {
private final SourceSection headerSection;
private final @Nullable SourceSection docComment;
private final SourceSection @Nullable [] docComment;
@Children private final ExpressionNode[] annotationNodes;
private final int modifiers;
private final String simpleName;
@@ -45,7 +45,7 @@ public final class TypeAliasNode extends ExpressionNode {
public TypeAliasNode(
SourceSection sourceSection,
SourceSection headerSection,
@Nullable SourceSection docComment,
SourceSection @Nullable [] docComment,
ExpressionNode[] annotationNodes,
int modifiers,
String simpleName,

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,7 +29,7 @@ public abstract class UnresolvedClassMemberNode extends PklNode {
protected final SourceSection headerSection;
protected final VmLanguage language;
protected final FrameDescriptor descriptor;
protected final @Nullable SourceSection docComment;
protected final SourceSection @Nullable [] docComment;
protected final @Children ExpressionNode[] annotationNodes;
protected final int modifiers;
protected final Identifier name;
@@ -40,7 +40,7 @@ public abstract class UnresolvedClassMemberNode extends PklNode {
SourceSection sourceSection,
SourceSection headerSection,
FrameDescriptor descriptor,
@Nullable SourceSection docComment,
SourceSection @Nullable [] docComment,
ExpressionNode[] annotationNodes,
int modifiers,
Identifier name,

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,7 +40,7 @@ public final class UnresolvedMethodNode extends UnresolvedClassMemberNode {
SourceSection sourceSection,
SourceSection headerSection,
FrameDescriptor descriptor,
@Nullable SourceSection docComment,
SourceSection @Nullable [] docComment,
ExpressionNode[] annotationNodes,
int modifiers,
Identifier name,

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,7 +36,7 @@ public final class UnresolvedPropertyNode extends UnresolvedClassMemberNode {
SourceSection headerSection,
SourceSection propertyNameSection,
FrameDescriptor descriptor,
@Nullable SourceSection docComment,
SourceSection @Nullable [] docComment,
ExpressionNode[] annotationNodes,
int modifiers,
Identifier name,

View File

@@ -0,0 +1,476 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser;
import org.pkl.core.parser.ast.Annotation;
import org.pkl.core.parser.ast.ArgumentList;
import org.pkl.core.parser.ast.Class;
import org.pkl.core.parser.ast.ClassBody;
import org.pkl.core.parser.ast.ClassMethod;
import org.pkl.core.parser.ast.ClassProperty;
import org.pkl.core.parser.ast.DocComment;
import org.pkl.core.parser.ast.Expr.AmendsExpr;
import org.pkl.core.parser.ast.Expr.BinaryOperatorExpr;
import org.pkl.core.parser.ast.Expr.BoolLiteralExpr;
import org.pkl.core.parser.ast.Expr.FloatLiteralExpr;
import org.pkl.core.parser.ast.Expr.FunctionLiteralExpr;
import org.pkl.core.parser.ast.Expr.IfExpr;
import org.pkl.core.parser.ast.Expr.ImportExpr;
import org.pkl.core.parser.ast.Expr.IntLiteralExpr;
import org.pkl.core.parser.ast.Expr.LetExpr;
import org.pkl.core.parser.ast.Expr.LogicalNotExpr;
import org.pkl.core.parser.ast.Expr.ModuleExpr;
import org.pkl.core.parser.ast.Expr.MultiLineStringLiteralExpr;
import org.pkl.core.parser.ast.Expr.NewExpr;
import org.pkl.core.parser.ast.Expr.NonNullExpr;
import org.pkl.core.parser.ast.Expr.NullLiteralExpr;
import org.pkl.core.parser.ast.Expr.OuterExpr;
import org.pkl.core.parser.ast.Expr.ParenthesizedExpr;
import org.pkl.core.parser.ast.Expr.QualifiedAccessExpr;
import org.pkl.core.parser.ast.Expr.ReadExpr;
import org.pkl.core.parser.ast.Expr.SingleLineStringLiteralExpr;
import org.pkl.core.parser.ast.Expr.SubscriptExpr;
import org.pkl.core.parser.ast.Expr.SuperAccessExpr;
import org.pkl.core.parser.ast.Expr.SuperSubscriptExpr;
import org.pkl.core.parser.ast.Expr.ThisExpr;
import org.pkl.core.parser.ast.Expr.ThrowExpr;
import org.pkl.core.parser.ast.Expr.TraceExpr;
import org.pkl.core.parser.ast.Expr.TypeCastExpr;
import org.pkl.core.parser.ast.Expr.TypeCheckExpr;
import org.pkl.core.parser.ast.Expr.UnaryMinusExpr;
import org.pkl.core.parser.ast.Expr.UnqualifiedAccessExpr;
import org.pkl.core.parser.ast.ExtendsOrAmendsClause;
import org.pkl.core.parser.ast.Identifier;
import org.pkl.core.parser.ast.ImportClause;
import org.pkl.core.parser.ast.Modifier;
import org.pkl.core.parser.ast.ModuleDecl;
import org.pkl.core.parser.ast.Node;
import org.pkl.core.parser.ast.ObjectBody;
import org.pkl.core.parser.ast.ObjectMember.ForGenerator;
import org.pkl.core.parser.ast.ObjectMember.MemberPredicate;
import org.pkl.core.parser.ast.ObjectMember.ObjectElement;
import org.pkl.core.parser.ast.ObjectMember.ObjectEntry;
import org.pkl.core.parser.ast.ObjectMember.ObjectMethod;
import org.pkl.core.parser.ast.ObjectMember.ObjectProperty;
import org.pkl.core.parser.ast.ObjectMember.ObjectSpread;
import org.pkl.core.parser.ast.ObjectMember.WhenGenerator;
import org.pkl.core.parser.ast.Parameter;
import org.pkl.core.parser.ast.ParameterList;
import org.pkl.core.parser.ast.QualifiedIdentifier;
import org.pkl.core.parser.ast.ReplInput;
import org.pkl.core.parser.ast.StringConstant;
import org.pkl.core.parser.ast.StringConstantPart;
import org.pkl.core.parser.ast.StringPart;
import org.pkl.core.parser.ast.Type.ConstrainedType;
import org.pkl.core.parser.ast.Type.DeclaredType;
import org.pkl.core.parser.ast.Type.FunctionType;
import org.pkl.core.parser.ast.Type.ModuleType;
import org.pkl.core.parser.ast.Type.NothingType;
import org.pkl.core.parser.ast.Type.NullableType;
import org.pkl.core.parser.ast.Type.ParenthesizedType;
import org.pkl.core.parser.ast.Type.StringConstantType;
import org.pkl.core.parser.ast.Type.UnionType;
import org.pkl.core.parser.ast.Type.UnknownType;
import org.pkl.core.parser.ast.TypeAlias;
import org.pkl.core.parser.ast.TypeAnnotation;
import org.pkl.core.parser.ast.TypeParameter;
import org.pkl.core.parser.ast.TypeParameterList;
public abstract class BaseParserVisitor<T> implements ParserVisitor<T> {
@Override
public T visitUnknownType(UnknownType type) {
return defaultValue();
}
@Override
public T visitNothingType(NothingType type) {
return defaultValue();
}
@Override
public T visitModuleType(ModuleType type) {
return defaultValue();
}
@Override
public T visitStringConstantType(StringConstantType type) {
return visitChildren(type);
}
@Override
public T visitDeclaredType(DeclaredType type) {
return visitChildren(type);
}
@Override
public T visitParenthesizedType(ParenthesizedType type) {
return visitChildren(type);
}
@Override
public T visitNullableType(NullableType type) {
return visitChildren(type);
}
@Override
public T visitConstrainedType(ConstrainedType type) {
return visitChildren(type);
}
@Override
public T visitUnionType(UnionType type) {
return visitChildren(type);
}
@Override
public T visitFunctionType(FunctionType type) {
return visitChildren(type);
}
@Override
public T visitThisExpr(ThisExpr expr) {
return defaultValue();
}
@Override
public T visitOuterExpr(OuterExpr expr) {
return defaultValue();
}
@Override
public T visitModuleExpr(ModuleExpr expr) {
return defaultValue();
}
@Override
public T visitNullLiteralExpr(NullLiteralExpr expr) {
return defaultValue();
}
@Override
public T visitBoolLiteralExpr(BoolLiteralExpr expr) {
return defaultValue();
}
@Override
public T visitIntLiteralExpr(IntLiteralExpr expr) {
return defaultValue();
}
@Override
public T visitFloatLiteralExpr(FloatLiteralExpr expr) {
return defaultValue();
}
@Override
public T visitThrowExpr(ThrowExpr expr) {
return visitChildren(expr);
}
@Override
public T visitTraceExpr(TraceExpr expr) {
return visitChildren(expr);
}
@Override
public T visitImportExpr(ImportExpr expr) {
return visitChildren(expr);
}
@Override
public T visitReadExpr(ReadExpr expr) {
return visitChildren(expr);
}
@Override
public T visitUnqualifiedAccessExpr(UnqualifiedAccessExpr expr) {
return visitChildren(expr);
}
@Override
public T visitStringConstant(StringConstant expr) {
return visitChildren(expr);
}
@Override
public T visitSingleLineStringLiteralExpr(SingleLineStringLiteralExpr expr) {
return visitChildren(expr);
}
@Override
public T visitMultiLineStringLiteralExpr(MultiLineStringLiteralExpr expr) {
return visitChildren(expr);
}
@Override
public T visitNewExpr(NewExpr expr) {
return visitChildren(expr);
}
@Override
public T visitAmendsExpr(AmendsExpr expr) {
return visitChildren(expr);
}
@Override
public T visitSuperAccessExpr(SuperAccessExpr expr) {
return visitChildren(expr);
}
@Override
public T visitSuperSubscriptExpr(SuperSubscriptExpr expr) {
return visitChildren(expr);
}
@Override
public T visitQualifiedAccessExpr(QualifiedAccessExpr expr) {
return visitChildren(expr);
}
@Override
public T visitSubscriptExpr(SubscriptExpr expr) {
return visitChildren(expr);
}
@Override
public T visitNonNullExpr(NonNullExpr expr) {
return visitChildren(expr);
}
@Override
public T visitUnaryMinusExpr(UnaryMinusExpr expr) {
return visitChildren(expr);
}
@Override
public T visitLogicalNotExpr(LogicalNotExpr expr) {
return visitChildren(expr);
}
@Override
public T visitBinaryOperatorExpr(BinaryOperatorExpr expr) {
return visitChildren(expr);
}
@Override
public T visitTypeCheckExpr(TypeCheckExpr expr) {
return visitChildren(expr);
}
@Override
public T visitTypeCastExpr(TypeCastExpr expr) {
return visitChildren(expr);
}
@Override
public T visitIfExpr(IfExpr expr) {
return visitChildren(expr);
}
@Override
public T visitLetExpr(LetExpr expr) {
return visitChildren(expr);
}
@Override
public T visitFunctionLiteralExpr(FunctionLiteralExpr expr) {
return visitChildren(expr);
}
@Override
public T visitParenthesizedExpr(ParenthesizedExpr expr) {
return visitChildren(expr);
}
@Override
public T visitObjectProperty(ObjectProperty member) {
return visitChildren(member);
}
@Override
public T visitObjectMethod(ObjectMethod member) {
return visitChildren(member);
}
@Override
public T visitMemberPredicate(MemberPredicate member) {
return visitChildren(member);
}
@Override
public T visitObjectElement(ObjectElement member) {
return visitChildren(member);
}
@Override
public T visitObjectEntry(ObjectEntry member) {
return visitChildren(member);
}
@Override
public T visitObjectSpread(ObjectSpread member) {
return visitChildren(member);
}
@Override
public T visitWhenGenerator(WhenGenerator member) {
return visitChildren(member);
}
@Override
public T visitForGenerator(ForGenerator member) {
return visitChildren(member);
}
@Override
public T visitModule(org.pkl.core.parser.ast.Module module) {
return visitChildren(module);
}
@Override
public T visitModuleDecl(ModuleDecl decl) {
return visitChildren(decl);
}
@Override
public T visitExtendsOrAmendsClause(ExtendsOrAmendsClause decl) {
return visitChildren(decl);
}
@Override
public T visitImportClause(ImportClause imp) {
return visitChildren(imp);
}
@Override
public T visitClass(Class clazz) {
return visitChildren(clazz);
}
@Override
public T visitModifier(Modifier modifier) {
return defaultValue();
}
@Override
public T visitClassProperty(ClassProperty prop) {
return visitChildren(prop);
}
@Override
public T visitClassMethod(ClassMethod method) {
return visitChildren(method);
}
@Override
public T visitTypeAlias(TypeAlias typeAlias) {
return visitChildren(typeAlias);
}
@Override
public T visitAnnotation(Annotation annotation) {
return visitChildren(annotation);
}
@Override
public T visitParameter(Parameter param) {
return visitChildren(param);
}
@Override
public T visitParameterList(ParameterList paramList) {
return visitChildren(paramList);
}
@Override
public T visitTypeParameterList(TypeParameterList typeParameterList) {
return visitChildren(typeParameterList);
}
@Override
public T visitTypeAnnotation(TypeAnnotation typeAnnotation) {
return visitChildren(typeAnnotation);
}
@Override
public T visitArgumentList(ArgumentList argumentList) {
return visitChildren(argumentList);
}
@Override
public T visitStringPart(StringPart part) {
return visitChildren(part);
}
@Override
public T visitStringConstantPart(StringConstantPart part) {
return defaultValue();
}
@Override
public T visitClassBody(ClassBody classBody) {
return visitChildren(classBody);
}
@Override
public T visitDocComment(DocComment docComment) {
return defaultValue();
}
@Override
public T visitIdentifier(Identifier identifier) {
return defaultValue();
}
@Override
public T visitQualifiedIdentifier(QualifiedIdentifier qualifiedIdentifier) {
return visitChildren(qualifiedIdentifier);
}
@Override
public T visitObjectBody(ObjectBody objectBody) {
return visitChildren(objectBody);
}
@Override
public T visitTypeParameter(TypeParameter typeParameter) {
return visitChildren(typeParameter);
}
@Override
public T visitReplInput(ReplInput replInput) {
return visitChildren(replInput);
}
private T visitChildren(Node node) {
T result = defaultValue();
var children = node.children();
if (children == null) return result;
for (var child : children) {
if (child != null) {
result = aggregateResult(result, child.accept(this));
}
}
return result;
}
protected abstract T defaultValue();
protected T aggregateResult(T result, T nextResult) {
return nextResult;
}
}

View File

@@ -1,120 +0,0 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.misc.IntervalSet;
import org.pkl.core.parser.antlr.PklParser;
final class ErrorStrategy extends DefaultErrorStrategy {
@Override
protected void reportNoViableAlternative(Parser parser, NoViableAltException e) {
var builder = new StringBuilder();
var offendingToken = e.getOffendingToken();
if (Lexer.isKeyword(offendingToken)) {
appendKeywordNotAllowedMessage(builder, e.getOffendingToken(), e.getExpectedTokens());
} else {
builder.append("No viable alternative at input ");
var tokens = parser.getInputStream();
if (e.getStartToken().getType() == Token.EOF) {
builder.append("<EOF>");
} else {
builder.append(escapeWSAndQuote(tokens.getText(e.getStartToken(), offendingToken)));
}
}
parser.notifyErrorListeners(offendingToken, builder.toString(), e);
}
@Override
protected void reportInputMismatch(Parser parser, InputMismatchException e) {
var builder = new StringBuilder();
var offendingToken = e.getOffendingToken();
if (Lexer.isKeyword(offendingToken)) {
appendKeywordNotAllowedMessage(builder, e.getOffendingToken(), e.getExpectedTokens());
} else {
// improve formatting compared to DefaultErrorStrategy
builder
.append("Mismatched input: ")
.append(getTokenErrorDisplay(offendingToken))
.append(". ");
appendExpectedTokensMessage(builder, parser);
}
parser.notifyErrorListeners(offendingToken, builder.toString(), e);
}
// improve formatting compared to DefaultErrorStrategy
@Override
protected void reportUnwantedToken(Parser parser) {
if (inErrorRecoveryMode(parser)) return;
beginErrorCondition(parser);
var builder = new StringBuilder();
var currentToken = parser.getCurrentToken();
if (Lexer.isKeyword(currentToken)) {
appendKeywordNotAllowedMessage(builder, currentToken, parser.getExpectedTokens());
} else {
builder.append("Extraneous input: ").append(getTokenErrorDisplay(currentToken)).append(". ");
appendExpectedTokensMessage(builder, parser);
}
parser.notifyErrorListeners(currentToken, builder.toString(), null);
}
// improve formatting compared to DefaultErrorStrategy
protected void reportMissingToken(Parser parser) {
if (inErrorRecoveryMode(parser)) return;
beginErrorCondition(parser);
var builder = new StringBuilder();
var currentToken = parser.getCurrentToken();
var expecting = getExpectedTokens(parser);
builder
.append("Missing ")
.append(expecting.toString(parser.getVocabulary()))
.append(" at ")
.append(getTokenErrorDisplay(currentToken))
.append(". ");
parser.notifyErrorListeners(currentToken, builder.toString(), null);
}
private void appendExpectedTokensMessage(StringBuilder builder, Parser parser) {
var expectedTokens = parser.getExpectedTokens();
var size = expectedTokens.size();
if (size == 0) return;
builder.append(size == 1 ? "Expected: " : "Expected one of: ");
var msg = expectedTokens.toString(parser.getVocabulary());
if (msg.startsWith("{")) msg = msg.substring(1);
if (msg.endsWith("}")) msg = msg.substring(0, msg.length() - 1);
builder.append(msg);
}
private void appendKeywordNotAllowedMessage(
StringBuilder builder, Token offendingToken, IntervalSet expectedTokens) {
builder.append("Keyword `").append(offendingToken.getText()).append("` is not allowed here.");
if (expectedTokens.contains(PklParser.Identifier)) {
builder.append(" (If you must use this name as identifier, enclose it in backticks.)");
}
}
}

View File

@@ -1,95 +0,0 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser;
import org.antlr.v4.runtime.ParserRuleContext;
import org.pkl.core.util.IoUtils;
import org.pkl.core.util.Nullable;
public abstract class LexParseException extends RuntimeException {
// line of the error's start position, 1-based
private final int line;
// column of the error's start position, 1-based
private final int column;
// number of characters, starting from line/column, belonging to the offending token
private final int length;
private final int relevance;
private @Nullable ParserRuleContext partialParseResult;
public LexParseException(String message, int line, int column, int length, int relevance) {
super(format(message));
this.line = line;
this.column = column;
this.length = length;
this.relevance = relevance;
partialParseResult = null;
}
public int getLine() {
return line;
}
public int getColumn() {
return column;
}
public int getLength() {
return length;
}
public int getRelevance() {
return relevance;
}
public @Nullable ParserRuleContext getPartialParseResult() {
return partialParseResult;
}
public LexParseException withPartialParseResult(ParserRuleContext partialParseResult) {
this.partialParseResult = partialParseResult;
return this;
}
public static final class LexError extends LexParseException {
public LexError(String message, int line, int column, int length) {
super(message, line, column, length, Integer.MAX_VALUE);
}
}
public static class ParseError extends LexParseException {
public ParseError(String message, int line, int column, int length, int relevance) {
super(message, line, column, length, relevance);
}
}
public static final class IncompleteInput extends ParseError {
public IncompleteInput(String message, int line, int column, int length) {
super(message, line, column, length, Integer.MAX_VALUE - 1);
}
}
// format ANTLR error messages like Pkl's own error messages
private static String format(String msg) {
var result = IoUtils.capitalize(msg);
result = result.replace("'", "`");
if (!result.contains(":") && !result.endsWith(")") && !result.endsWith(".")) result += ".";
return result;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,73 +15,712 @@
*/
package org.pkl.core.parser;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.antlr.v4.runtime.*;
import org.pkl.core.parser.antlr.PklLexer;
import org.pkl.core.util.Nullable;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
import org.pkl.core.util.ErrorMessages;
public final class Lexer {
public static final Set<Integer> KEYWORD_TYPES;
public static final Set<String> KEYWORD_NAMES;
public class Lexer {
static {
var keywordTypes = new HashSet<Integer>();
var keywordNames = new HashSet<String>();
var vocabulary = PklLexer.VOCABULARY;
private final char[] source;
private final int size;
protected int cursor = 0;
protected int sCursor = 0;
private char lookahead;
private State state = State.DEFAULT;
private final Deque<InterpolationScope> interpolationStack = new ArrayDeque<>();
private boolean stringEnded = false;
private boolean isEscape = false;
// how many newlines exist between two subsequent tokens
protected int newLinesBetween = 0;
for (var i = 0; i <= vocabulary.getMaxTokenType(); i++) {
var literal = vocabulary.getLiteralName(i);
if (literal == null) continue;
// remove leading and trailing quotes
literal = literal.substring(1, literal.length() - 1);
if (Character.isLetter(literal.charAt(0)) || literal.equals("_")) {
keywordTypes.add(i);
keywordNames.add(literal);
private static final char EOF = Short.MAX_VALUE;
public Lexer(String input) {
source = input.toCharArray();
size = source.length;
if (size > 0) {
lookahead = source[cursor];
} else {
lookahead = EOF;
}
}
// The span of the last lexed token
public Span span() {
return new Span(sCursor, cursor - sCursor);
}
// The text of the last lexed token
public String text() {
return new String(source, sCursor, cursor - sCursor);
}
public char[] getSource() {
return source;
}
public String textFor(int offset, int size) {
return new String(source, offset, size);
}
public Token next() {
sCursor = cursor;
newLinesBetween = 0;
return switch (state) {
case DEFAULT -> nextDefault();
case STRING -> nextString();
};
}
private Token nextDefault() {
var ch = nextChar();
// ignore spaces
while (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\f' || ch == '\r') {
sCursor = cursor;
if (ch == '\n') {
newLinesBetween++;
}
ch = nextChar();
}
return switch (ch) {
case EOF -> {
// when EOF is reached we overshot the span
cursor--;
yield Token.EOF;
}
case ';' -> Token.SEMICOLON;
case '(' -> {
var scope = interpolationStack.peek();
if (scope != null) {
scope.parens++;
}
yield Token.LPAREN;
}
case ')' -> {
var scope = interpolationStack.peek();
if (scope != null) {
scope.parens--;
if (scope.parens <= 0) {
// interpolation is over. Back to string
state = State.STRING;
}
}
yield Token.RPAREN;
}
case '{' -> Token.LBRACE;
case '}' -> Token.RBRACE;
case ',' -> Token.COMMA;
case '@' -> Token.AT;
case ':' -> Token.COLON;
case '+' -> Token.PLUS;
case '%' -> Token.MOD;
case '[' -> {
if (lookahead == '[') {
nextChar();
yield Token.LPRED;
} else yield Token.LBRACK;
}
case ']' -> Token.RBRACK;
case '=' -> {
if (lookahead == '=') {
nextChar();
yield Token.EQUAL;
} else yield Token.ASSIGN;
}
case '>' -> {
if (lookahead == '=') {
nextChar();
yield Token.GTE;
} else yield Token.GT;
}
case '<' -> {
if (lookahead == '=') {
nextChar();
yield Token.LTE;
} else yield Token.LT;
}
case '-' -> {
if (lookahead == '>') {
nextChar();
yield Token.ARROW;
} else yield Token.MINUS;
}
case '!' -> {
if (lookahead == '!') {
nextChar();
yield Token.NON_NULL;
} else if (lookahead == '=') {
nextChar();
yield Token.NOT_EQUAL;
} else yield Token.NOT;
}
case '?' -> {
if (lookahead == '.') {
nextChar();
yield Token.QDOT;
} else if (lookahead == '?') {
nextChar();
yield Token.COALESCE;
} else yield Token.QUESTION;
}
case '&' -> {
if (lookahead == '&') {
nextChar();
yield Token.AND;
} else {
throw unexpectedChar(ch, "&&");
}
}
case '|' -> {
if (lookahead == '>') {
nextChar();
yield Token.PIPE;
} else if (lookahead == '|') {
nextChar();
yield Token.OR;
} else {
yield Token.UNION;
}
}
case '*' -> {
if (lookahead == '*') {
nextChar();
yield Token.POW;
} else yield Token.STAR;
}
case '~' -> {
if (lookahead == '/') {
nextChar();
yield Token.INT_DIV;
} else {
throw unexpectedChar(ch, "~/");
}
}
case '.' -> {
if (lookahead == '.') {
nextChar();
if (lookahead == '.') {
nextChar();
if (lookahead == '?') {
nextChar();
yield Token.QSPREAD;
} else {
yield Token.SPREAD;
}
} else {
throw unexpectedChar("..", ".", "...", "...?");
}
} else if (lookahead >= 48 && lookahead <= 57) {
yield lexNumber(ch);
} else {
yield Token.DOT;
}
}
case '`' -> {
lexQuotedIdentifier();
yield Token.IDENTIFIER;
}
case '/' -> lexSlash();
case '"' -> lexStringStart(0);
case '#' -> {
if (lookahead == '!') {
yield lexShebang();
} else {
yield lexStringStartPounds();
}
}
default -> {
if (Character.isDigit(ch)) {
yield lexNumber(ch);
} else if (isIdentifierStart(ch)) {
yield lexIdentifier();
} else throw lexError(ErrorMessages.create("invalidCharacter", ch), cursor - 1, 1);
}
};
}
private Token nextString() {
var scope = interpolationStack.getFirst();
if (stringEnded) {
lexStringEnd(scope);
stringEnded = false;
interpolationStack.pop();
state = State.DEFAULT;
return Token.STRING_END;
}
if (lookahead == EOF) return Token.EOF;
if (isEscape) {
isEscape = false;
// consume the `\#*`
for (var i = 0; i < scope.pounds + 1; i++) {
nextChar();
}
return lexEscape();
}
if (scope.quotes == 1) {
lexString(scope.pounds);
} else {
if (lookahead == '\r') {
nextChar();
if (lookahead == '\n') {
nextChar();
}
return Token.STRING_NEWLINE;
}
if (lookahead == '\n') {
nextChar();
return Token.STRING_NEWLINE;
}
lexMultiString(scope.pounds);
}
return Token.STRING_PART;
}
private Token lexStringStartPounds() {
int pounds = 1;
while (lookahead == '#') {
nextChar();
pounds++;
}
if (lookahead == EOF) {
throw lexError(ErrorMessages.create("unexpectedEndOfFile"), span());
}
if (lookahead != '"') {
throw unexpectedChar(lookahead, "\"");
}
nextChar();
return lexStringStart(pounds);
}
private Token lexStringStart(int pounds) {
var quotes = 1;
if (lookahead == '"') {
nextChar();
if (lookahead == '"') {
nextChar();
quotes = 3;
} else {
backup();
}
}
KEYWORD_TYPES = Collections.unmodifiableSet(keywordTypes);
KEYWORD_NAMES = Collections.unmodifiableSet(keywordNames);
state = State.STRING;
interpolationStack.push(new InterpolationScope(quotes, pounds));
stringEnded = false;
if (quotes == 1) return Token.STRING_START;
return Token.STRING_MULTI_START;
}
@TruffleBoundary
public static PklLexer createLexer(CharStream source) {
var lexer = new PklLexer(source);
lexer.removeErrorListeners();
lexer.addErrorListener(
new ANTLRErrorListener<>() {
@Override
public <T extends Integer> void syntaxError(
Recognizer<T, ?> recognizer,
T offendingSymbol,
int line,
int charPositionInLine,
String msg,
RecognitionException e) {
var lexer = ((org.antlr.v4.runtime.Lexer) recognizer);
throw new LexParseException.LexError(
msg,
line,
charPositionInLine + 1,
lexer._input.index() - lexer._tokenStartCharIndex);
private void lexStringEnd(InterpolationScope scope) {
// don't actually need to check it here
for (var i = 0; i < scope.quotes + scope.pounds; i++) {
nextChar();
}
}
private void lexString(int pounds) {
var poundsInARow = 0;
var foundQuote = false;
var foundBackslash = false;
while (lookahead != EOF) {
var ch = nextChar();
switch (ch) {
case '\n', '\r' ->
throw lexError(
ErrorMessages.create("missingDelimiter", "\"" + "#".repeat(pounds)), cursor - 1, 1);
case '"' -> {
if (pounds == 0) {
backup();
stringEnded = true;
return;
}
});
return lexer;
foundQuote = true;
foundBackslash = false;
poundsInARow = 0;
}
case '\\' -> {
foundQuote = false;
foundBackslash = true;
poundsInARow = 0;
if (pounds == poundsInARow) {
backup(pounds + 1);
isEscape = true;
return;
}
}
case '#' -> {
poundsInARow++;
if (foundQuote && (pounds == poundsInARow)) {
backup(pounds + 1);
stringEnded = true;
return;
}
if (foundBackslash && pounds == poundsInARow) {
backup(pounds + 1);
isEscape = true;
return;
}
}
default -> {
foundQuote = false;
foundBackslash = false;
poundsInARow = 0;
}
}
}
}
@TruffleBoundary
public static boolean isKeyword(@Nullable Token token) {
return token != null && KEYWORD_TYPES.contains(token.getType());
private void lexMultiString(int pounds) {
var poundsInARow = 0;
var quotesInARow = 0;
var foundBackslash = false;
while (lookahead != EOF && lookahead != '\n' && lookahead != '\r') {
var ch = nextChar();
switch (ch) {
case '"' -> {
quotesInARow++;
if (quotesInARow == 3 && pounds == 0) {
backup(3);
stringEnded = true;
return;
}
poundsInARow = 0;
foundBackslash = false;
}
case '\\' -> {
quotesInARow = 0;
poundsInARow = 0;
foundBackslash = true;
if (pounds == poundsInARow) {
backup(pounds + 1);
isEscape = true;
return;
}
}
case '#' -> {
poundsInARow++;
if (quotesInARow == 3 && pounds == poundsInARow) {
backup(pounds + 3);
stringEnded = true;
return;
}
if (foundBackslash && pounds == poundsInARow) {
backup(pounds + 1);
isEscape = true;
return;
}
}
default -> {
quotesInARow = 0;
poundsInARow = 0;
foundBackslash = false;
}
}
}
}
private Token lexEscape() {
if (lookahead == EOF) throw unexpectedEndOfFile();
var ch = nextChar();
return switch (ch) {
case 'n' -> Token.STRING_ESCAPE_NEWLINE;
case '"' -> Token.STRING_ESCAPE_QUOTE;
case '\\' -> Token.STRING_ESCAPE_BACKSLASH;
case 't' -> Token.STRING_ESCAPE_TAB;
case 'r' -> Token.STRING_ESCAPE_RETURN;
case '(' -> {
var scope = interpolationStack.getFirst();
scope.parens++;
state = State.DEFAULT;
yield Token.INTERPOLATION_START;
}
case 'u' -> lexUnicodeEscape();
default ->
throw lexError(
ErrorMessages.create("invalidCharacterEscapeSequence", "\\" + ch, "\\"),
cursor - 2,
2);
};
}
private Token lexUnicodeEscape() {
if (lookahead != '{') {
throw unexpectedChar(lookahead, "{");
}
do {
nextChar();
} while (lookahead != '}' && lookahead != EOF && Character.isLetterOrDigit(lookahead));
if (lookahead == '}') {
// consume the close bracket
nextChar();
} else {
throw lexError(ErrorMessages.create("unterminatedUnicodeEscapeSequence", text()), span());
}
return Token.STRING_ESCAPE_UNICODE;
}
private Token lexIdentifier() {
while (isIdentifierPart(lookahead)) {
nextChar();
}
var identifierStr = text();
var identifier = getKeywordOrIdentifier(identifierStr);
return switch (identifier) {
case IMPORT -> {
if (lookahead == '*') {
nextChar();
yield Token.IMPORT_STAR;
} else yield Token.IMPORT;
}
case READ ->
switch (lookahead) {
case '*' -> {
nextChar();
yield Token.READ_STAR;
}
case '?' -> {
nextChar();
yield Token.READ_QUESTION;
}
default -> Token.READ;
};
default -> identifier;
};
}
private void lexQuotedIdentifier() {
while (lookahead != '`' && lookahead != '\n' && lookahead != '\r') {
nextChar();
}
if (lookahead == '`') {
nextChar();
} else {
throw unexpectedChar(lookahead, "backquote");
}
}
private Token lexNumber(char start) {
if (start == '0') {
if (lookahead == 'x' || lookahead == 'X') {
nextChar();
lexHexNumber();
return Token.HEX;
}
if (lookahead == 'b' || lookahead == 'B') {
nextChar();
lexBinNumber();
return Token.BIN;
}
if (lookahead == 'o' || lookahead == 'O') {
nextChar();
lexOctNumber();
return Token.OCT;
}
if (lookahead == 'e' || lookahead == 'E') {
nextChar();
lexExponent();
return Token.FLOAT;
}
} else if (start == '.') {
lexDotNumber();
return Token.FLOAT;
}
while ((lookahead >= 48 && lookahead <= 57) || lookahead == '_') {
nextChar();
}
if (lookahead == 'e' || lookahead == 'E') {
nextChar();
lexExponent();
return Token.FLOAT;
} else if (lookahead == '.') {
nextChar();
if (lookahead == '_') {
throw lexError("invalidSeparatorPosition");
}
if (lookahead < 48 || lookahead > 57) {
backup();
return Token.INT;
}
lexDotNumber();
return Token.FLOAT;
}
return Token.INT;
}
private Token lexSlash() {
switch (lookahead) {
case '/':
{
nextChar();
var token = lookahead == '/' ? Token.DOC_COMMENT : Token.LINE_COMMENT;
while (lookahead != '\n' && lookahead != '\r' && lookahead != EOF) {
nextChar();
}
return token;
}
case '*':
{
nextChar();
lexBlockComment();
return Token.BLOCK_COMMENT;
}
default:
return Token.DIV;
}
}
private void lexBlockComment() {
if (lookahead == EOF) throw unexpectedEndOfFile();
var prev = nextChar();
// block comments in Pkl can stack
var stack = 1;
while (stack > 0 && lookahead != EOF) {
if (prev == '*' && lookahead == '/') stack--;
if (prev == '/' && lookahead == '*') stack++;
prev = nextChar();
}
if (lookahead == EOF) throw unexpectedEndOfFile();
}
private void lexHexNumber() {
if (lookahead == '_') {
throw lexError("invalidSeparatorPosition");
}
if (!isHex(lookahead)) {
throw unexpectedChar(lookahead, "hexadecimal number");
}
while (isHex(lookahead) || lookahead == '_') {
nextChar();
}
}
private void lexBinNumber() {
if (lookahead == '_') {
throw lexError("invalidSeparatorPosition");
}
if (!(lookahead == '0' || lookahead == '1')) {
throw unexpectedChar(lookahead, "binary number");
}
while (lookahead == '0' || lookahead == '1' || lookahead == '_') {
nextChar();
}
}
private void lexOctNumber() {
if (lookahead == '_') {
throw lexError("invalidSeparatorPosition");
}
var ch = (int) lookahead;
if (!(ch >= 48 && ch <= 55)) {
throw unexpectedChar((char) ch, "octal number");
}
while ((ch >= 48 && ch <= 55) || ch == '_') {
nextChar();
ch = lookahead;
}
}
private void lexExponent() {
if (lookahead == '+' || lookahead == '-') {
nextChar();
}
if (lookahead == '_') {
throw lexError("invalidSeparatorPosition");
}
if (lookahead < 48 || lookahead > 57) {
throw unexpectedChar(lookahead, "number");
}
while ((lookahead >= 48 && lookahead <= 57) || lookahead == '_') {
nextChar();
}
}
private void lexDotNumber() {
if (lookahead == '_') {
throw lexError("invalidSeparatorPosition");
}
while ((lookahead >= 48 && lookahead <= 57) || lookahead == '_') {
nextChar();
}
if (lookahead == 'e' || lookahead == 'E') {
nextChar();
lexExponent();
}
}
private Token lexShebang() {
do {
nextChar();
} while (lookahead != '\n' && lookahead != '\r' && lookahead != EOF);
return Token.SHEBANG;
}
private boolean isHex(char ch) {
var code = (int) ch;
return (code >= 48 && code <= 57) || (code >= 97 && code <= 102) || (code >= 65 && code <= 70);
}
private static boolean isIdentifierStart(char c) {
return c == '_' || c == '$' || Character.isUnicodeIdentifierStart(c);
}
private static boolean isIdentifierPart(char c) {
return c != EOF && (c == '$' || Character.isUnicodeIdentifierPart(c));
}
private char nextChar() {
var tmp = lookahead;
cursor++;
if (cursor >= size) {
lookahead = EOF;
} else {
lookahead = source[cursor];
}
return tmp;
}
private void backup() {
lookahead = source[--cursor];
}
private void backup(int amount) {
cursor -= amount;
lookahead = source[cursor];
}
private ParserError lexError(String msg, Object... args) {
var length = lookahead == EOF ? 0 : 1;
var index = lookahead == EOF ? cursor - 1 : cursor;
return new ParserError(ErrorMessages.create(msg, args), new Span(index, length));
}
private ParserError lexError(String msg, int charIndex, int length) {
return new ParserError(msg, new Span(charIndex, length));
}
private ParserError lexError(String msg, Span span) {
return new ParserError(msg, span);
}
private ParserError unexpectedChar(char got, String didYouMean) {
return lexError("unexpectedCharacter", got, didYouMean);
}
private ParserError unexpectedChar(String got, String option1, String option2, String option3) {
return lexError("unexpectedCharacter3", got, option1, option2, option3);
}
private ParserError unexpectedEndOfFile() {
return lexError(ErrorMessages.create("unexpectedEndOfFile"), cursor, 0);
}
@TruffleBoundary
public static boolean isRegularIdentifier(String identifier) {
if (identifier.isEmpty()) return false;
if (KEYWORD_NAMES.contains(identifier)) return false;
if (isKeyword(identifier)) return false;
var firstCp = identifier.codePointAt(0);
return (firstCp == '$' || firstCp == '_' || Character.isUnicodeIdentifierStart(firstCp))
@@ -91,8 +730,89 @@ public final class Lexer {
.allMatch(cp -> cp == '$' || Character.isUnicodeIdentifierPart(cp));
}
@TruffleBoundary
public static String maybeQuoteIdentifier(String identifier) {
return isRegularIdentifier(identifier) ? identifier : "`" + identifier + "`";
}
@SuppressWarnings("SuspiciousArrayMethodCall")
private static boolean isKeyword(String text) {
var index = Arrays.binarySearch(KEYWORDS, text);
return index >= 0;
}
@SuppressWarnings("SuspiciousArrayMethodCall")
private static Token getKeywordOrIdentifier(String text) {
var index = Arrays.binarySearch(KEYWORDS, text);
if (index < 0) return Token.IDENTIFIER;
return KEYWORDS[index].token;
}
protected static final KeywordEntry[] KEYWORDS = {
new KeywordEntry("_", Token.UNDERSCORE),
new KeywordEntry("abstract", Token.ABSTRACT),
new KeywordEntry("amends", Token.AMENDS),
new KeywordEntry("as", Token.AS),
new KeywordEntry("case", Token.CASE),
new KeywordEntry("class", Token.CLASS),
new KeywordEntry("const", Token.CONST),
new KeywordEntry("delete", Token.DELETE),
new KeywordEntry("else", Token.ELSE),
new KeywordEntry("extends", Token.EXTENDS),
new KeywordEntry("external", Token.EXTERNAL),
new KeywordEntry("false", Token.FALSE),
new KeywordEntry("fixed", Token.FIXED),
new KeywordEntry("for", Token.FOR),
new KeywordEntry("function", Token.FUNCTION),
new KeywordEntry("hidden", Token.HIDDEN),
new KeywordEntry("if", Token.IF),
new KeywordEntry("import", Token.IMPORT),
new KeywordEntry("in", Token.IN),
new KeywordEntry("is", Token.IS),
new KeywordEntry("let", Token.LET),
new KeywordEntry("local", Token.LOCAL),
new KeywordEntry("module", Token.MODULE),
new KeywordEntry("new", Token.NEW),
new KeywordEntry("nothing", Token.NOTHING),
new KeywordEntry("null", Token.NULL),
new KeywordEntry("open", Token.OPEN),
new KeywordEntry("out", Token.OUT),
new KeywordEntry("outer", Token.OUTER),
new KeywordEntry("override", Token.OVERRIDE),
new KeywordEntry("protected", Token.PROTECTED),
new KeywordEntry("read", Token.READ),
new KeywordEntry("record", Token.RECORD),
new KeywordEntry("super", Token.SUPER),
new KeywordEntry("switch", Token.SWITCH),
new KeywordEntry("this", Token.THIS),
new KeywordEntry("throw", Token.THROW),
new KeywordEntry("trace", Token.TRACE),
new KeywordEntry("true", Token.TRUE),
new KeywordEntry("typealias", Token.TYPE_ALIAS),
new KeywordEntry("unknown", Token.UNKNOWN),
new KeywordEntry("vararg", Token.VARARG),
new KeywordEntry("when", Token.WHEN)
};
protected record KeywordEntry(String name, Token token) implements Comparable<String> {
@Override
public int compareTo(String o) {
return name.compareTo(o);
}
}
private static class InterpolationScope {
final int quotes;
final int pounds;
int parens = 0;
protected InterpolationScope(int quotes, int pounds) {
this.quotes = quotes;
this.pounds = pounds;
}
}
private enum State {
DEFAULT,
STRING
}
}

View File

@@ -0,0 +1,140 @@
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser;
import java.util.ArrayList;
import java.util.List;
import org.pkl.core.parser.ast.Expr;
import org.pkl.core.parser.ast.Expr.BinaryOperatorExpr;
import org.pkl.core.parser.ast.Expr.OperatorExpr;
import org.pkl.core.parser.ast.Expr.TypeCastExpr;
import org.pkl.core.parser.ast.Expr.TypeCheckExpr;
import org.pkl.core.parser.ast.Expr.TypeExpr;
import org.pkl.core.parser.ast.Operator;
import org.pkl.core.util.Nullable;
class OperatorResolver {
private OperatorResolver() {}
private enum Associativity {
LEFT,
RIGHT
}
public static int getPrecedence(Operator op) {
return switch (op) {
case NULL_COALESCE -> 0;
case PIPE -> 1;
case OR -> 2;
case AND -> 3;
case EQ_EQ, NOT_EQ -> 4;
case IS, AS -> 5;
case LT, LTE, GT, GTE -> 6;
case PLUS, MINUS -> 7;
case MULT, DIV, INT_DIV, MOD -> 8;
case POW -> 9;
case DOT, QDOT -> 10;
};
}
private static Associativity getAssociativity(Operator op) {
return switch (op) {
case POW, NULL_COALESCE -> Associativity.RIGHT;
default -> Associativity.LEFT;
};
}
private static @Nullable Operator getHighestPrecedence(List<Expr> exprs, int min) {
var highest = -1;
Operator op = null;
for (var expr : exprs) {
if (expr instanceof OperatorExpr o) {
var precedence = getPrecedence(o.getOp());
if (precedence > highest && precedence >= min) {
highest = precedence;
op = o.getOp();
}
}
}
return op;
}
private static int index(List<Expr> exprs, Associativity associativity, Operator op) {
if (associativity == Associativity.LEFT) {
for (var i = 0; i < exprs.size(); i++) {
if (exprs.get(i) instanceof OperatorExpr operator && operator.getOp() == op) {
return i;
}
}
} else {
for (var i = exprs.size() - 1; i >= 0; i--) {
if (exprs.get(i) instanceof OperatorExpr operator && operator.getOp() == op) {
return i;
}
}
}
return -1;
}
private static List<Expr> resolveOperator(
List<Expr> exprs, Associativity associativity, Operator op) {
var res = new ArrayList<>(exprs);
var i = index(res, associativity, op);
var left = res.get(i - 1);
var right = res.get(i + 1);
var span = left.span().endWith(right.span());
var binOp =
switch (op) {
case IS -> new TypeCheckExpr(left, ((TypeExpr) right).getType(), span);
case AS -> new TypeCastExpr(left, ((TypeExpr) right).getType(), span);
default -> new BinaryOperatorExpr(left, right, op, span);
};
res.remove(i - 1);
res.remove(i - 1);
res.remove(i - 1);
res.add(i - 1, binOp);
return res;
}
/**
* Resolve all operators based on their precedence and associativity. This requires that the list
* has a valid form: `expr` `op` `expr` ...
*/
public static Expr resolveOperators(List<Expr> exprs) {
if (exprs.size() == 1) return exprs.get(0);
var res = resolveOperatorsHigherThan(exprs, 0);
if (res.size() > 1) {
throw new ParserError(
"Malformed expression", exprs.get(0).span().endWith(exprs.get(exprs.size() - 1).span()));
}
return res.get(0);
}
public static List<Expr> resolveOperatorsHigherThan(List<Expr> exprs, int minPrecedence) {
var res = exprs;
var highest = getHighestPrecedence(res, minPrecedence);
while (highest != null) {
var associativity = getAssociativity(highest);
res = resolveOperator(res, associativity, highest);
highest = getHighestPrecedence(res, minPrecedence);
}
return res;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser;
import org.pkl.core.parser.ast.Module;
import org.pkl.core.util.Nullable;
public class ParserError extends RuntimeException {
private final Span span;
private @Nullable Module partialParseResult;
public ParserError(String msg, Span span) {
super(msg);
this.span = span;
}
public Span span() {
return span;
}
public void setPartialParseResult(@Nullable Module partialParseResult) {
this.partialParseResult = partialParseResult;
}
public @Nullable Module getPartialParseResult() {
return partialParseResult;
}
}

View File

@@ -0,0 +1,223 @@
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser;
import org.pkl.core.parser.ast.Annotation;
import org.pkl.core.parser.ast.ArgumentList;
import org.pkl.core.parser.ast.Class;
import org.pkl.core.parser.ast.ClassBody;
import org.pkl.core.parser.ast.ClassMethod;
import org.pkl.core.parser.ast.ClassProperty;
import org.pkl.core.parser.ast.DocComment;
import org.pkl.core.parser.ast.Expr;
import org.pkl.core.parser.ast.Expr.AmendsExpr;
import org.pkl.core.parser.ast.Expr.BinaryOperatorExpr;
import org.pkl.core.parser.ast.Expr.BoolLiteralExpr;
import org.pkl.core.parser.ast.Expr.FloatLiteralExpr;
import org.pkl.core.parser.ast.Expr.FunctionLiteralExpr;
import org.pkl.core.parser.ast.Expr.IfExpr;
import org.pkl.core.parser.ast.Expr.IntLiteralExpr;
import org.pkl.core.parser.ast.Expr.LetExpr;
import org.pkl.core.parser.ast.Expr.LogicalNotExpr;
import org.pkl.core.parser.ast.Expr.ModuleExpr;
import org.pkl.core.parser.ast.Expr.MultiLineStringLiteralExpr;
import org.pkl.core.parser.ast.Expr.NewExpr;
import org.pkl.core.parser.ast.Expr.NonNullExpr;
import org.pkl.core.parser.ast.Expr.NullLiteralExpr;
import org.pkl.core.parser.ast.Expr.OuterExpr;
import org.pkl.core.parser.ast.Expr.ParenthesizedExpr;
import org.pkl.core.parser.ast.Expr.QualifiedAccessExpr;
import org.pkl.core.parser.ast.Expr.ReadExpr;
import org.pkl.core.parser.ast.Expr.SingleLineStringLiteralExpr;
import org.pkl.core.parser.ast.Expr.SubscriptExpr;
import org.pkl.core.parser.ast.Expr.SuperAccessExpr;
import org.pkl.core.parser.ast.Expr.SuperSubscriptExpr;
import org.pkl.core.parser.ast.Expr.ThisExpr;
import org.pkl.core.parser.ast.Expr.ThrowExpr;
import org.pkl.core.parser.ast.Expr.TraceExpr;
import org.pkl.core.parser.ast.Expr.TypeCastExpr;
import org.pkl.core.parser.ast.Expr.TypeCheckExpr;
import org.pkl.core.parser.ast.Expr.UnaryMinusExpr;
import org.pkl.core.parser.ast.Expr.UnqualifiedAccessExpr;
import org.pkl.core.parser.ast.ExtendsOrAmendsClause;
import org.pkl.core.parser.ast.Identifier;
import org.pkl.core.parser.ast.ImportClause;
import org.pkl.core.parser.ast.Modifier;
import org.pkl.core.parser.ast.Module;
import org.pkl.core.parser.ast.ModuleDecl;
import org.pkl.core.parser.ast.ObjectBody;
import org.pkl.core.parser.ast.ObjectMember;
import org.pkl.core.parser.ast.Parameter;
import org.pkl.core.parser.ast.ParameterList;
import org.pkl.core.parser.ast.QualifiedIdentifier;
import org.pkl.core.parser.ast.ReplInput;
import org.pkl.core.parser.ast.StringConstant;
import org.pkl.core.parser.ast.StringConstantPart;
import org.pkl.core.parser.ast.StringPart;
import org.pkl.core.parser.ast.Type;
import org.pkl.core.parser.ast.TypeAlias;
import org.pkl.core.parser.ast.TypeAnnotation;
import org.pkl.core.parser.ast.TypeParameter;
import org.pkl.core.parser.ast.TypeParameterList;
public interface ParserVisitor<Result> {
Result visitUnknownType(Type.UnknownType type);
Result visitNothingType(Type.NothingType type);
Result visitModuleType(Type.ModuleType type);
Result visitStringConstantType(Type.StringConstantType type);
Result visitDeclaredType(Type.DeclaredType type);
Result visitParenthesizedType(Type.ParenthesizedType type);
Result visitNullableType(Type.NullableType type);
Result visitConstrainedType(Type.ConstrainedType type);
Result visitUnionType(Type.UnionType type);
Result visitFunctionType(Type.FunctionType type);
Result visitThisExpr(ThisExpr expr);
Result visitOuterExpr(OuterExpr expr);
Result visitModuleExpr(ModuleExpr expr);
Result visitNullLiteralExpr(NullLiteralExpr expr);
Result visitBoolLiteralExpr(BoolLiteralExpr expr);
Result visitIntLiteralExpr(IntLiteralExpr expr);
Result visitFloatLiteralExpr(FloatLiteralExpr expr);
Result visitThrowExpr(ThrowExpr expr);
Result visitTraceExpr(TraceExpr expr);
Result visitImportExpr(Expr.ImportExpr expr);
Result visitReadExpr(ReadExpr expr);
Result visitUnqualifiedAccessExpr(UnqualifiedAccessExpr expr);
Result visitStringConstant(StringConstant expr);
Result visitSingleLineStringLiteralExpr(SingleLineStringLiteralExpr expr);
Result visitMultiLineStringLiteralExpr(MultiLineStringLiteralExpr expr);
Result visitNewExpr(NewExpr expr);
Result visitAmendsExpr(AmendsExpr expr);
Result visitSuperAccessExpr(SuperAccessExpr expr);
Result visitSuperSubscriptExpr(SuperSubscriptExpr expr);
Result visitQualifiedAccessExpr(QualifiedAccessExpr expr);
Result visitSubscriptExpr(SubscriptExpr expr);
Result visitNonNullExpr(NonNullExpr expr);
Result visitUnaryMinusExpr(UnaryMinusExpr expr);
Result visitLogicalNotExpr(LogicalNotExpr expr);
Result visitBinaryOperatorExpr(BinaryOperatorExpr expr);
Result visitTypeCheckExpr(TypeCheckExpr expr);
Result visitTypeCastExpr(TypeCastExpr expr);
Result visitIfExpr(IfExpr expr);
Result visitLetExpr(LetExpr expr);
Result visitFunctionLiteralExpr(FunctionLiteralExpr expr);
Result visitParenthesizedExpr(ParenthesizedExpr expr);
Result visitObjectProperty(ObjectMember.ObjectProperty member);
Result visitObjectMethod(ObjectMember.ObjectMethod member);
Result visitMemberPredicate(ObjectMember.MemberPredicate member);
Result visitObjectElement(ObjectMember.ObjectElement member);
Result visitObjectEntry(ObjectMember.ObjectEntry member);
Result visitObjectSpread(ObjectMember.ObjectSpread member);
Result visitWhenGenerator(ObjectMember.WhenGenerator member);
Result visitForGenerator(ObjectMember.ForGenerator member);
Result visitModule(Module module);
Result visitModuleDecl(ModuleDecl decl);
Result visitExtendsOrAmendsClause(ExtendsOrAmendsClause decl);
Result visitImportClause(ImportClause imp);
Result visitClass(Class clazz);
Result visitModifier(Modifier modifier);
Result visitClassProperty(ClassProperty entry);
Result visitClassMethod(ClassMethod entry);
Result visitClassBody(ClassBody classBody);
Result visitTypeAlias(TypeAlias typeAlias);
Result visitAnnotation(Annotation annotation);
Result visitParameter(Parameter param);
Result visitParameterList(ParameterList paramList);
Result visitTypeParameter(TypeParameter typeParameter);
Result visitTypeParameterList(TypeParameterList typeParameterList);
Result visitTypeAnnotation(TypeAnnotation typeAnnotation);
Result visitArgumentList(ArgumentList argumentList);
Result visitStringPart(StringPart part);
Result visitStringConstantPart(StringConstantPart part);
Result visitDocComment(DocComment docComment);
Result visitIdentifier(Identifier identifier);
Result visitQualifiedIdentifier(QualifiedIdentifier qualifiedIdentifier);
Result visitObjectBody(ObjectBody objectBody);
Result visitReplInput(ReplInput replInput);
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser;
public record Span(int charIndex, int length) {
/** Returns a span that starts with this span and ends with {@code end}. */
public Span endWith(Span end) {
return new Span(charIndex, end.charIndex - charIndex + end.length);
}
/** Checks wheter {@code other} starts directly after this span ends */
public boolean adjacent(Span other) {
return charIndex + length == other.charIndex;
}
public int stopIndex() {
return charIndex + length - 1;
}
public Span stopSpan() {
return new Span(charIndex + length - 1, 1);
}
public Span move(int amount) {
return new Span(charIndex + amount, length);
}
}

View File

@@ -0,0 +1,200 @@
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser;
public enum Token {
ABSTRACT,
AMENDS,
AS,
CLASS,
CONST,
ELSE,
EXTENDS,
EXTERNAL,
FALSE,
FIXED,
FOR,
FUNCTION,
HIDDEN,
IF,
IMPORT,
IMPORT_STAR,
IN,
IS,
LET,
LOCAL,
MODULE,
NEW,
NOTHING,
NULL,
OPEN,
OUT,
OUTER,
READ,
READ_STAR,
READ_QUESTION,
SUPER,
THIS,
THROW,
TRACE,
TRUE,
TYPE_ALIAS,
UNKNOWN,
WHEN,
// reserved for future use
PROTECTED,
OVERRIDE,
RECORD,
DELETE,
CASE,
SWITCH,
VARARG,
// punctuation
LPAREN,
RPAREN,
LBRACE,
RBRACE,
LBRACK,
RBRACK,
LPRED,
COMMA,
DOT,
QDOT,
COALESCE,
NON_NULL,
AT,
ASSIGN,
GT,
LT,
// rest
NOT,
QUESTION,
COLON,
ARROW,
EQUAL,
NOT_EQUAL,
LTE,
GTE,
AND,
OR,
PLUS,
MINUS,
POW,
STAR,
DIV,
INT_DIV,
MOD,
UNION,
PIPE,
SPREAD,
QSPREAD,
UNDERSCORE,
EOF,
SEMICOLON,
INT,
FLOAT,
BIN,
OCT,
HEX,
IDENTIFIER,
LINE_COMMENT,
BLOCK_COMMENT,
DOC_COMMENT,
SHEBANG,
INTERPOLATION_START,
STRING_START,
STRING_MULTI_START,
STRING_NEWLINE,
STRING_ESCAPE_NEWLINE,
STRING_ESCAPE_TAB,
STRING_ESCAPE_RETURN,
STRING_ESCAPE_QUOTE,
STRING_ESCAPE_BACKSLASH,
STRING_ESCAPE_UNICODE,
STRING_END,
STRING_PART;
public boolean isModifier() {
return switch (this) {
case EXTERNAL, ABSTRACT, OPEN, LOCAL, HIDDEN, FIXED, CONST -> true;
default -> false;
};
}
public boolean isKeyword() {
return switch (this) {
case ABSTRACT,
AMENDS,
AS,
CLASS,
CONST,
ELSE,
EXTENDS,
EXTERNAL,
FALSE,
FIXED,
FOR,
FUNCTION,
HIDDEN,
IF,
IMPORT,
IMPORT_STAR,
IN,
IS,
LET,
LOCAL,
MODULE,
NEW,
NOTHING,
NULL,
OPEN,
OUT,
OUTER,
READ,
READ_STAR,
READ_QUESTION,
SUPER,
THIS,
THROW,
TRACE,
TRUE,
TYPE_ALIAS,
UNKNOWN,
WHEN,
UNDERSCORE,
PROTECTED,
OVERRIDE,
RECORD,
DELETE,
CASE,
SWITCH,
VARARG ->
true;
default -> false;
};
}
public String text() {
if (this == UNDERSCORE) {
return "_";
}
return name().toLowerCase();
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
public abstract class AbstractNode implements Node {
protected final Span span;
protected final @Nullable List<? extends @Nullable Node> children;
protected @Nullable Node parent;
public AbstractNode(Span span, @Nullable List<? extends @Nullable Node> children) {
this.span = span;
if (children != null) {
this.children = Collections.unmodifiableList(children);
} else {
this.children = null;
}
if (children != null) {
for (var node : children) {
if (node != null) {
node.setParent(this);
}
}
}
}
@Override
public Span span() {
return span;
}
@Override
public @Nullable Node parent() {
return parent;
}
@Override
public void setParent(Node parent) {
this.parent = parent;
}
@Override
public @Nullable List<? extends @Nullable Node> children() {
return children;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AbstractNode that = (AbstractNode) o;
return Objects.equals(span, that.span) && Objects.deepEquals(children, that.children);
}
@Override
public int hashCode() {
return Objects.hash(span, children);
}
@Override
public String toString() {
var name = getClass().getSimpleName();
return name + "{span=" + span + ", children=" + children + '}';
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.List;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
public class Annotation extends AbstractNode {
public Annotation(List<Node> nodes, Span span) {
super(span, nodes);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitAnnotation(this);
}
public Type getType() {
assert children != null;
return (Type) children.get(0);
}
public @Nullable ObjectBody getBody() {
assert children != null;
return (ObjectBody) children.get(1);
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.List;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
@SuppressWarnings("unchecked")
public class ArgumentList extends AbstractNode {
public ArgumentList(List<Expr> arguments, Span span) {
super(span, arguments);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitArgumentList(this);
}
public List<Expr> getArguments() {
assert children != null;
return (List<Expr>) children;
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.List;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
@SuppressWarnings({"unchecked", "DataFlowIssue"})
public final class Class extends AbstractNode {
private final int modifiersOffset;
private final int nameOffset;
public Class(List<Node> nodes, int modifiersOffset, int nameOffset, Span span) {
super(span, nodes);
this.modifiersOffset = modifiersOffset;
this.nameOffset = nameOffset;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitClass(this);
}
public @Nullable DocComment getDocComment() {
return (DocComment) children.get(0);
}
public List<Annotation> getAnnotations() {
return (List<Annotation>) children.subList(1, modifiersOffset);
}
public List<Modifier> getModifiers() {
return (List<Modifier>) children.subList(modifiersOffset, nameOffset);
}
public Identifier getName() {
return (Identifier) children.get(nameOffset);
}
public @Nullable TypeParameterList getTypeParameterList() {
return (TypeParameterList) children.get(nameOffset + 1);
}
public @Nullable Type getSuperClass() {
return (Type) children.get(nameOffset + 2);
}
public @Nullable ClassBody getBody() {
return (ClassBody) children.get(nameOffset + 3);
}
public Span getHeaderSpan() {
Span end;
if (getSuperClass() != null) {
end = getSuperClass().span();
} else if (getTypeParameterList() != null) {
end = getTypeParameterList().span();
} else {
end = getName().span();
}
return span.endWith(end);
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.ArrayList;
import java.util.List;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
public class ClassBody extends AbstractNode {
public ClassBody(List<Node> nodes, Span span) {
super(span, nodes);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitClassBody(this);
}
public List<ClassProperty> getProperties() {
var props = new ArrayList<ClassProperty>();
assert children != null;
for (var child : children) {
if (child instanceof ClassProperty prop) {
props.add(prop);
}
}
return props;
}
public List<ClassMethod> getMethods() {
var methods = new ArrayList<ClassMethod>();
assert children != null;
for (var child : children) {
if (child instanceof ClassMethod method) {
methods.add(method);
}
}
return methods;
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.List;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
@SuppressWarnings("unchecked")
public class ClassMethod extends AbstractNode {
private final int modifiersOffset;
private final int nameOffset;
private final Span headerSpan;
public ClassMethod(
List<Node> nodes, int modifiersOffset, int nameOffset, Span headerSpan, Span span) {
super(span, nodes);
this.headerSpan = headerSpan;
this.modifiersOffset = modifiersOffset;
this.nameOffset = nameOffset;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitClassMethod(this);
}
public @Nullable DocComment getDocComment() {
assert children != null;
return (DocComment) children.get(0);
}
public List<Annotation> getAnnotations() {
assert children != null;
return (List<Annotation>) children.subList(1, modifiersOffset);
}
public List<Modifier> getModifiers() {
assert children != null;
return (List<Modifier>) children.subList(modifiersOffset, nameOffset);
}
public Identifier getName() {
assert children != null;
return (Identifier) children.get(nameOffset);
}
public @Nullable TypeParameterList getTypeParameterList() {
assert children != null;
return (TypeParameterList) children.get(nameOffset + 1);
}
public ParameterList getParameterList() {
assert children != null;
return (ParameterList) children.get(nameOffset + 2);
}
public @Nullable TypeAnnotation getTypeAnnotation() {
assert children != null;
return (TypeAnnotation) children.get(nameOffset + 3);
}
public @Nullable Expr getExpr() {
assert children != null;
return (Expr) children.get(nameOffset + 4);
}
public Span getHeaderSpan() {
return headerSpan;
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.List;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
@SuppressWarnings({"DuplicatedCode", "unchecked"})
public final class ClassProperty extends AbstractNode {
private final int modifiersOffset;
private final int nameOffset;
public ClassProperty(List<Node> nodes, int modifiersOffset, int nameOffset, Span span) {
super(span, nodes);
this.modifiersOffset = modifiersOffset;
this.nameOffset = nameOffset;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitClassProperty(this);
}
public @Nullable DocComment getDocComment() {
assert children != null;
return (DocComment) children.get(0);
}
public List<Annotation> getAnnotations() {
assert children != null;
return (List<Annotation>) children.subList(1, modifiersOffset);
}
public List<Modifier> getModifiers() {
assert children != null;
return (List<Modifier>) children.subList(modifiersOffset, nameOffset);
}
public Identifier getName() {
assert children != null;
return (Identifier) children.get(nameOffset);
}
public @Nullable TypeAnnotation getTypeAnnotation() {
assert children != null;
return (TypeAnnotation) children.get(nameOffset + 1);
}
public @Nullable Expr getExpr() {
assert children != null;
return (Expr) children.get(nameOffset + 2);
}
public List<ObjectBody> getBodyList() {
assert children != null;
return (List<ObjectBody>) children.subList(nameOffset + 3, children.size());
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.List;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
public final class DocComment extends AbstractNode {
private final List<Span> spans;
public DocComment(List<Span> spans) {
super(spans.get(0).endWith(spans.get(spans.size() - 1)), null);
this.spans = spans;
}
@Override
public Span span() {
return spans.get(0).endWith(spans.get(spans.size() - 1));
}
public List<Span> getSpans() {
return spans;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitDocComment(this);
}
@Override
public String text(char[] source) {
var builder = new StringBuilder();
for (var span : spans) {
builder.append(new String(source, span.charIndex(), span.length()));
builder.append('\n');
}
return builder.toString();
}
}

View File

@@ -0,0 +1,691 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import org.pkl.core.PklBugException;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
@SuppressWarnings("ALL")
public abstract sealed class Expr extends AbstractNode {
public Expr(Span span, @Nullable List<? extends @Nullable Node> children) {
super(span, children);
}
public static final class ThisExpr extends Expr {
public ThisExpr(Span span) {
super(span, null);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitThisExpr(this);
}
}
public static final class OuterExpr extends Expr {
public OuterExpr(Span span) {
super(span, null);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitOuterExpr(this);
}
}
public static final class ModuleExpr extends Expr {
public ModuleExpr(Span span) {
super(span, null);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitModuleExpr(this);
}
}
public static final class NullLiteralExpr extends Expr {
public NullLiteralExpr(Span span) {
super(span, null);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitNullLiteralExpr(this);
}
}
public static final class BoolLiteralExpr extends Expr {
private final boolean b;
public BoolLiteralExpr(boolean b, Span span) {
super(span, null);
this.b = b;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitBoolLiteralExpr(this);
}
public boolean isB() {
return b;
}
}
public static final class IntLiteralExpr extends Expr {
private final String number;
public IntLiteralExpr(String number, Span span) {
super(span, null);
this.number = number;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitIntLiteralExpr(this);
}
public String getNumber() {
return number;
}
}
public static final class FloatLiteralExpr extends Expr {
private final String number;
public FloatLiteralExpr(String number, Span span) {
super(span, null);
this.number = number;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitFloatLiteralExpr(this);
}
public String getNumber() {
return number;
}
}
public static final class SingleLineStringLiteralExpr extends Expr {
private final Span startDelimiterSpan;
private final Span endDelimiterSpan;
public SingleLineStringLiteralExpr(
List<StringPart> parts, Span startDelimiterSpan, Span endDelimiterSpan, Span span) {
super(span, parts);
this.startDelimiterSpan = startDelimiterSpan;
this.endDelimiterSpan = endDelimiterSpan;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitSingleLineStringLiteralExpr(this);
}
public List<StringPart> getParts() {
assert children != null;
return (List<StringPart>) children;
}
public Span getStartDelimiterSpan() {
return startDelimiterSpan;
}
public Span getEndDelimiterSpan() {
return endDelimiterSpan;
}
}
public static final class MultiLineStringLiteralExpr extends Expr {
private final Span startDelimiterSpan;
private final Span endDelimiterSpan;
public MultiLineStringLiteralExpr(
List<StringPart> parts, Span startDelimiterSpan, Span endDelimiterSpan, Span span) {
super(span, parts);
this.startDelimiterSpan = startDelimiterSpan;
this.endDelimiterSpan = endDelimiterSpan;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitMultiLineStringLiteralExpr(this);
}
public List<StringPart> getParts() {
return (List<StringPart>) children;
}
public Span getStartDelimiterSpan() {
return startDelimiterSpan;
}
public Span getEndDelimiterSpan() {
return endDelimiterSpan;
}
}
public static final class ThrowExpr extends Expr {
public ThrowExpr(Expr expr, Span span) {
super(span, List.of(expr));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitThrowExpr(this);
}
public Expr getExpr() {
return (Expr) children.get(0);
}
}
public static final class TraceExpr extends Expr {
public TraceExpr(Expr expr, Span span) {
super(span, List.of(expr));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitTraceExpr(this);
}
public Expr getExpr() {
return (Expr) children.get(0);
}
}
public static final class ImportExpr extends Expr {
private final boolean isGlob;
public ImportExpr(StringConstant importStr, boolean isGlob, Span span) {
super(span, List.of(importStr));
this.isGlob = isGlob;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitImportExpr(this);
}
public StringConstant getImportStr() {
return (StringConstant) children.get(0);
}
public boolean isGlob() {
return isGlob;
}
}
public static final class ReadExpr extends Expr {
private final ReadType readType;
public ReadExpr(Expr expr, ReadType readType, Span span) {
super(span, List.of(expr));
this.readType = readType;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitReadExpr(this);
}
public Expr getExpr() {
return (Expr) children.get(0);
}
public ReadType getReadType() {
return readType;
}
}
public enum ReadType {
READ,
GLOB,
NULL
}
public static final class UnqualifiedAccessExpr extends Expr {
public UnqualifiedAccessExpr(
Identifier identifier, @Nullable ArgumentList argumentList, Span span) {
super(span, Arrays.asList(identifier, argumentList));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitUnqualifiedAccessExpr(this);
}
public Identifier getIdentifier() {
return (Identifier) children.get(0);
}
public @Nullable ArgumentList getArgumentList() {
return (ArgumentList) children.get(1);
}
}
public static final class QualifiedAccessExpr extends Expr {
private final boolean isNullable;
public QualifiedAccessExpr(
Expr expr,
Identifier identifier,
boolean isNullable,
@Nullable ArgumentList argumentList,
Span span) {
super(span, Arrays.asList(expr, identifier, argumentList));
this.isNullable = isNullable;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitQualifiedAccessExpr(this);
}
public Expr getExpr() {
return (Expr) children.get(0);
}
public Identifier getIdentifier() {
return (Identifier) children.get(1);
}
public boolean isNullable() {
return isNullable;
}
public @Nullable ArgumentList getArgumentList() {
return (ArgumentList) children.get(2);
}
}
public static final class SuperAccessExpr extends Expr {
public SuperAccessExpr(Identifier identifier, @Nullable ArgumentList argumentList, Span span) {
super(span, Arrays.asList(identifier, argumentList));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitSuperAccessExpr(this);
}
public Identifier getIdentifier() {
return (Identifier) children.get(0);
}
public @Nullable ArgumentList getArgumentList() {
return (ArgumentList) children.get(1);
}
}
public static final class SuperSubscriptExpr extends Expr {
public SuperSubscriptExpr(Expr arg, Span span) {
super(span, List.of(arg));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitSuperSubscriptExpr(this);
}
public Expr getArg() {
return (Expr) children.get(0);
}
}
public static final class SubscriptExpr extends Expr {
public SubscriptExpr(Expr expr, Expr arg, Span span) {
super(span, List.of(expr, arg));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitSubscriptExpr(this);
}
public Expr getExpr() {
return (Expr) children.get(0);
}
public Expr getArg() {
return (Expr) children.get(1);
}
}
public static final class IfExpr extends Expr {
public IfExpr(Expr cond, Expr then, Expr els, Span span) {
super(span, List.of(cond, then, els));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitIfExpr(this);
}
public Expr getCond() {
return (Expr) children.get(0);
}
public Expr getThen() {
return (Expr) children.get(1);
}
public Expr getEls() {
return (Expr) children.get(2);
}
}
public static final class LetExpr extends Expr {
public LetExpr(Parameter parameter, Expr bindingExpr, Expr expr, Span span) {
super(span, List.of(parameter, bindingExpr, expr));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitLetExpr(this);
}
public Parameter getParameter() {
return (Parameter) children.get(0);
}
public Expr getBindingExpr() {
return (Expr) children.get(1);
}
public Expr getExpr() {
return (Expr) children.get(2);
}
}
public static final class FunctionLiteralExpr extends Expr {
public FunctionLiteralExpr(ParameterList parameterList, Expr expr, Span span) {
super(span, List.of(parameterList, expr));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitFunctionLiteralExpr(this);
}
public ParameterList getParameterList() {
return (ParameterList) children.get(0);
}
public Expr getExpr() {
return (Expr) children.get(1);
}
}
public static final class ParenthesizedExpr extends Expr {
public ParenthesizedExpr(Expr expr, Span span) {
super(span, List.of(expr));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitParenthesizedExpr(this);
}
public Expr getExpr() {
return (Expr) children.get(0);
}
}
public static final class NewExpr extends Expr {
public NewExpr(@Nullable Type type, ObjectBody body, Span span) {
super(span, Arrays.asList(type, body));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitNewExpr(this);
}
public @Nullable Type getType() {
return (Type) children.get(0);
}
public ObjectBody getBody() {
return (ObjectBody) children.get(1);
}
public Span newSpan() {
return new Span(span.charIndex(), 3);
}
}
public static final class AmendsExpr extends Expr {
public AmendsExpr(Expr expr, ObjectBody body, Span span) {
super(span, List.of(expr, body));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitAmendsExpr(this);
}
public Expr getExpr() {
return (Expr) children.get(0);
}
public ObjectBody getBody() {
return (ObjectBody) children.get(1);
}
}
public static final class NonNullExpr extends Expr {
public NonNullExpr(Expr expr, Span span) {
super(span, List.of(expr));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitNonNullExpr(this);
}
public Expr getExpr() {
return (Expr) children.get(0);
}
}
public static final class UnaryMinusExpr extends Expr {
public UnaryMinusExpr(Expr expr, Span span) {
super(span, List.of(expr));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitUnaryMinusExpr(this);
}
public Expr getExpr() {
return (Expr) children.get(0);
}
}
public static final class LogicalNotExpr extends Expr {
public LogicalNotExpr(Expr expr, Span span) {
super(span, List.of(expr));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitLogicalNotExpr(this);
}
public Expr getExpr() {
return (Expr) children.get(0);
}
}
public static final class BinaryOperatorExpr extends Expr {
private final Operator op;
public BinaryOperatorExpr(Expr left, Expr right, Operator op, Span span) {
super(span, List.of(left, right));
this.op = op;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitBinaryOperatorExpr(this);
}
public Expr getLeft() {
return (Expr) children.get(0);
}
public Expr getRight() {
return (Expr) children.get(1);
}
public Operator getOp() {
return op;
}
@Override
public String toString() {
return "BinaryOp{" + "children=" + children + ", op=" + op + ", span=" + span + '}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
BinaryOperatorExpr binaryOp = (BinaryOperatorExpr) o;
return Objects.deepEquals(children, binaryOp.children)
&& op == binaryOp.op
&& Objects.equals(span, binaryOp.span);
}
@Override
public int hashCode() {
return Objects.hash(children, op, span);
}
}
public static final class TypeCheckExpr extends Expr {
public TypeCheckExpr(Expr expr, Type type, Span span) {
super(span, List.of(expr, type));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitTypeCheckExpr(this);
}
public Expr getExpr() {
return (Expr) children.get(0);
}
public Type getType() {
return (Type) children.get(1);
}
}
public static final class TypeCastExpr extends Expr {
public TypeCastExpr(Expr expr, Type type, Span span) {
super(span, List.of(expr, type));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitTypeCastExpr(this);
}
public Expr getExpr() {
return (Expr) children.get(0);
}
public Type getType() {
return (Type) children.get(1);
}
}
/** This is a synthetic class only used at parse time. */
public static final class OperatorExpr extends Expr {
private final Operator op;
public OperatorExpr(Operator op, Span span) {
super(span, null);
this.op = op;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
// should never be called
throw PklBugException.unreachableCode();
}
public Operator getOp() {
return op;
}
@Override
public String toString() {
return "OperatorExpr{op=" + op + ", span=" + span + '}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
OperatorExpr that = (OperatorExpr) o;
return op == that.op && Objects.equals(span, that.span);
}
@Override
public int hashCode() {
return Objects.hash(op, span);
}
}
/** This is a synthetic class only used at parse time. */
public static final class TypeExpr extends Expr {
public TypeExpr(Type type) {
super(type.span(), List.of(type));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
// should never be called
throw PklBugException.unreachableCode();
}
public Type getType() {
return (Type) children.get(0);
}
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.List;
import java.util.Objects;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
public class ExtendsOrAmendsClause extends AbstractNode {
private final Type type;
public ExtendsOrAmendsClause(StringConstant url, Type type, Span span) {
super(span, List.of(url));
this.type = type;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitExtendsOrAmendsClause(this);
}
public StringConstant getUrl() {
assert children != null;
return (StringConstant) children.get(0);
}
public Type getType() {
return type;
}
@Override
public String toString() {
return "ExtendsOrAmendsClause{"
+ "type="
+ type
+ ", span="
+ span
+ ", children="
+ children
+ '}';
}
@SuppressWarnings("ConstantValue")
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
ExtendsOrAmendsClause that = (ExtendsOrAmendsClause) o;
return type == that.type;
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), type);
}
public enum Type {
EXTENDS,
AMENDS
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.Objects;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
public final class Identifier extends AbstractNode {
private final String value;
public Identifier(String value, Span span) {
super(span, null);
this.value = value;
}
@Override
public <T> T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitIdentifier(this);
}
public String getValue() {
return removeBackticks(value);
}
public String getRawValue() {
return value;
}
private static String removeBackticks(String text) {
if (!text.isEmpty() && text.charAt(0) == '`') {
// lexer makes sure there's a ` at the end
return text.substring(1, text.length() - 1);
}
return text;
}
@Override
public String toString() {
return "Identifier{value='" + value + '\'' + ", span=" + span + '}';
}
@SuppressWarnings("ConstantValue")
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Identifier identifier = (Identifier) o;
return Objects.equals(value, identifier.value) && Objects.equals(span, identifier.span);
}
@Override
public int hashCode() {
return Objects.hash(value, span);
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.Arrays;
import java.util.Objects;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
@SuppressWarnings("DataFlowIssue")
public final class ImportClause extends AbstractNode {
private final boolean isGlob;
public ImportClause(
StringConstant importStr, boolean isGlob, @Nullable Identifier alias, Span span) {
super(span, Arrays.asList(importStr, alias));
this.isGlob = isGlob;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitImportClause(this);
}
public StringConstant getImportStr() {
return (StringConstant) children.get(0);
}
public boolean isGlob() {
return isGlob;
}
public @Nullable Identifier getAlias() {
return (Identifier) children.get(1);
}
@Override
public String toString() {
return "Import{isGlob=" + isGlob + ", span=" + span + ", children=" + children + '}';
}
@SuppressWarnings("ConstantValue")
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
ImportClause anImport = (ImportClause) o;
return isGlob == anImport.isGlob;
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), isGlob);
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.Objects;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
public final class Modifier extends AbstractNode {
private final ModifierValue value;
public Modifier(ModifierValue value, Span span) {
super(span, null);
this.value = value;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitModifier(this);
}
public ModifierValue getValue() {
return value;
}
@Override
public String toString() {
return "Modifier{value=" + value + ", span=" + span + '}';
}
@SuppressWarnings("ConstantValue")
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Modifier modifier = (Modifier) o;
return value == modifier.value && Objects.equals(span, modifier.span);
}
@Override
public int hashCode() {
return Objects.hash(value, span);
}
public enum ModifierValue {
EXTERNAL,
ABSTRACT,
OPEN,
LOCAL,
HIDDEN,
FIXED,
CONST
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.ArrayList;
import java.util.List;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
@SuppressWarnings("DataFlowIssue")
public final class Module extends AbstractNode {
public Module(List<Node> nodes, Span span) {
super(span, nodes);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitModule(this);
}
public @Nullable ModuleDecl getDecl() {
return (ModuleDecl) children.get(0);
}
public List<ImportClause> getImports() {
if (children.size() < 2) return List.of();
var res = new ArrayList<ImportClause>();
for (int i = 1; i < children.size(); i++) {
var child = children.get(i);
if (child instanceof ImportClause imp) {
res.add(imp);
} else {
// imports are sequential
break;
}
}
return res;
}
public List<Class> getClasses() {
var res = new ArrayList<Class>();
for (var child : children) {
if (child instanceof Class clazz) {
res.add(clazz);
}
}
return res;
}
public List<TypeAlias> getTypeAliases() {
var res = new ArrayList<TypeAlias>();
for (var child : children) {
if (child instanceof TypeAlias typeAlias) {
res.add(typeAlias);
}
}
return res;
}
public List<ClassProperty> getProperties() {
var res = new ArrayList<ClassProperty>();
for (var child : children) {
if (child instanceof ClassProperty classProperty) {
res.add(classProperty);
}
}
return res;
}
public List<ClassMethod> getMethods() {
var res = new ArrayList<ClassMethod>();
for (var child : children) {
if (child instanceof ClassMethod classMethod) {
res.add(classMethod);
}
}
return res;
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.List;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
@SuppressWarnings({"DataFlowIssue", "unchecked"})
public final class ModuleDecl extends AbstractNode {
private final int modifiersOffset;
private final int nameOffset;
public ModuleDecl(List<Node> nodes, int modifiersOffset, int nameOffset, Span span) {
super(span, nodes);
this.modifiersOffset = modifiersOffset;
this.nameOffset = nameOffset;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitModuleDecl(this);
}
public @Nullable DocComment getDocComment() {
return (DocComment) children.get(0);
}
public List<Annotation> getAnnotations() {
return (List<Annotation>) children.subList(1, modifiersOffset);
}
public List<Modifier> getModifiers() {
return (List<Modifier>) children.subList(modifiersOffset, nameOffset);
}
public @Nullable QualifiedIdentifier getName() {
return (QualifiedIdentifier) children.get(nameOffset);
}
public @Nullable ExtendsOrAmendsClause getExtendsOrAmendsDecl() {
return (ExtendsOrAmendsClause) children.get(nameOffset + 1);
}
public Span headerSpan() {
Span start = null;
Span end = null;
for (var i = modifiersOffset; i < children.size(); i++) {
var child = children.get(i);
if (child != null) {
if (start == null) {
start = child.span();
}
end = child.span();
}
}
return start.endWith(end);
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.List;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
public interface Node {
Span span();
@Nullable
Node parent();
void setParent(Node parent);
@Nullable
List<? extends @Nullable Node> children();
<T> T accept(ParserVisitor<? extends T> visitor);
default String text(char[] source) {
return new String(source, span().charIndex(), span().length());
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.List;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
@SuppressWarnings({"unchecked", "DataFlowIssue"})
public final class ObjectBody extends AbstractNode {
private final int membersOffset;
public ObjectBody(List<Node> nodes, int membersOffset, Span span) {
super(span, nodes);
this.membersOffset = membersOffset;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitObjectBody(this);
}
public List<Parameter> getParameters() {
return (List<Parameter>) children.subList(0, membersOffset);
}
public List<ObjectMember> getMembers() {
return (List<ObjectMember>) children.subList(membersOffset, children.size());
}
}

View File

@@ -0,0 +1,285 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
@SuppressWarnings("ALL")
public abstract sealed class ObjectMember extends AbstractNode {
public ObjectMember(Span span, @Nullable List<? extends @Nullable Node> children) {
super(span, children);
}
public static final class ObjectElement extends ObjectMember {
public ObjectElement(Expr expr, Span span) {
super(span, List.of(expr));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitObjectElement(this);
}
public Expr getExpr() {
return (Expr) children.get(0);
}
}
public static final class ObjectProperty extends ObjectMember {
private final int identifierOffset;
public ObjectProperty(List<Node> nodes, int identifierOffset, Span span) {
super(span, nodes);
this.identifierOffset = identifierOffset;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitObjectProperty(this);
}
public List<Modifier> getModifiers() {
return (List<Modifier>) children.subList(0, identifierOffset);
}
public Identifier getIdentifier() {
return (Identifier) children.get(identifierOffset);
}
public @Nullable TypeAnnotation getTypeAnnotation() {
return (TypeAnnotation) children.get(identifierOffset + 1);
}
public @Nullable Expr getExpr() {
return (Expr) children.get(identifierOffset + 2);
}
public @Nullable List<ObjectBody> getBodyList() {
return (List<ObjectBody>) children.subList(identifierOffset + 3, children.size());
}
}
public static final class ObjectMethod extends ObjectMember {
private final int identifierOffset;
public ObjectMethod(List<Node> nodes, int identifierOffset, Span span) {
super(span, nodes);
this.identifierOffset = identifierOffset;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitObjectMethod(this);
}
public List<Modifier> getModifiers() {
return (List<Modifier>) children.subList(0, identifierOffset);
}
public Identifier getIdentifier() {
return (Identifier) children.get(identifierOffset);
}
public @Nullable TypeParameterList getTypeParameterList() {
return (TypeParameterList) children.get(identifierOffset + 1);
}
public ParameterList getParamList() {
return (ParameterList) children.get(identifierOffset + 2);
}
public @Nullable TypeAnnotation getTypeAnnotation() {
return (TypeAnnotation) children.get(identifierOffset + 3);
}
public Expr getExpr() {
return (Expr) children.get(identifierOffset + 4);
}
public Span headerSpan() {
Span end;
var typeAnnotation = children.get(identifierOffset + 3);
if (typeAnnotation == null) {
end = children.get(identifierOffset + 2).span();
} else {
end = typeAnnotation.span();
}
return span.endWith(end);
}
}
public static final class MemberPredicate extends ObjectMember {
public MemberPredicate(List<Node> nodes, Span span) {
super(span, nodes);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitMemberPredicate(this);
}
public Expr getPred() {
return (Expr) children.get(0);
}
public @Nullable Expr getExpr() {
return (Expr) children.get(1);
}
public List<ObjectBody> getBodyList() {
return (List<ObjectBody>) children.subList(2, children.size());
}
}
public static final class ObjectEntry extends ObjectMember {
public ObjectEntry(List<Node> nodes, Span span) {
super(span, nodes);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitObjectEntry(this);
}
public Expr getKey() {
return (Expr) children.get(0);
}
public @Nullable Expr getValue() {
return (Expr) children.get(1);
}
public List<ObjectBody> getBodyList() {
return (List<ObjectBody>) children.subList(2, children.size());
}
}
public static final class ObjectSpread extends ObjectMember {
private final boolean isNullable;
public ObjectSpread(Expr expr, boolean isNullable, Span span) {
super(span, List.of(expr));
this.isNullable = isNullable;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitObjectSpread(this);
}
public Expr getExpr() {
return (Expr) children.get(0);
}
public boolean isNullable() {
return isNullable;
}
@Override
public String toString() {
return "ObjectSpread{"
+ "isNullable="
+ isNullable
+ ", span="
+ span
+ ", children="
+ children
+ '}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
ObjectSpread that = (ObjectSpread) o;
return isNullable == that.isNullable;
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), isNullable);
}
}
public static final class WhenGenerator extends ObjectMember {
public WhenGenerator(
Expr thenClause, ObjectBody body, @Nullable ObjectBody elseClause, Span span) {
super(span, Arrays.asList(thenClause, body, elseClause));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitWhenGenerator(this);
}
public Expr getThenClause() {
return (Expr) children.get(0);
}
public ObjectBody getBody() {
return (ObjectBody) children.get(1);
}
public @Nullable ObjectBody getElseClause() {
return (ObjectBody) children.get(2);
}
}
public static final class ForGenerator extends ObjectMember {
public ForGenerator(
Parameter p1, @Nullable Parameter p2, Expr expr, ObjectBody body, Span span) {
super(span, Arrays.asList(p1, p2, expr, body));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitForGenerator(this);
}
public Parameter getP1() {
return (Parameter) children.get(0);
}
public @Nullable Parameter getP2() {
return (Parameter) children.get(1);
}
public Expr getExpr() {
return (Expr) children.get(2);
}
public ObjectBody getBody() {
return (ObjectBody) children.get(3);
}
public Span forSpan() {
return new Span(span.charIndex(), 3);
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
public enum Operator {
POW,
MULT,
DIV,
INT_DIV,
MOD,
PLUS,
MINUS,
LT,
GT,
LTE,
GTE,
IS,
AS,
EQ_EQ,
NOT_EQ,
AND,
OR,
PIPE,
NULL_COALESCE,
DOT,
QDOT,
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.Arrays;
import java.util.List;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
@SuppressWarnings("ALL")
public abstract sealed class Parameter extends AbstractNode {
public Parameter(Span span, @Nullable List<? extends @Nullable Node> children) {
super(span, children);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitParameter(this);
}
public static final class Underscore extends Parameter {
public Underscore(Span span) {
super(span, null);
}
}
public static final class TypedIdentifier extends Parameter {
public TypedIdentifier(
Identifier identifier, @Nullable TypeAnnotation typeAnnotation, Span span) {
super(span, Arrays.asList(identifier, typeAnnotation));
}
public Identifier getIdentifier() {
return (Identifier) children.get(0);
}
public @Nullable TypeAnnotation getTypeAnnotation() {
return (TypeAnnotation) children.get(1);
}
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.List;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
@SuppressWarnings("unchecked")
public class ParameterList extends AbstractNode {
public ParameterList(List<Parameter> parameters, Span span) {
super(span, parameters);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitParameterList(this);
}
public List<Parameter> getParameters() {
assert children != null;
return (List<Parameter>) children;
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.List;
import java.util.stream.Collectors;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.util.Nullable;
public final class QualifiedIdentifier extends AbstractNode {
public QualifiedIdentifier(List<Identifier> identifiers) {
super(
identifiers.get(0).span.endWith(identifiers.get(identifiers.size() - 1).span), identifiers);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitQualifiedIdentifier(this);
}
@SuppressWarnings({"unchecked", "DataFlowIssue"})
public List<Identifier> getIdentifiers() {
return (List<Identifier>) children;
}
public String text() {
return getIdentifiers().stream().map(Identifier::getValue).collect(Collectors.joining("."));
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.List;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
public class ReplInput extends AbstractNode {
public ReplInput(List<Node> nodes, Span span) {
super(span, nodes);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitReplInput(this);
}
@SuppressWarnings("unchecked")
public List<Node> getNodes() {
assert children != null;
return (List<Node>) children;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.List;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.parser.ast.StringPart.StringConstantParts;
import org.pkl.core.util.Nullable;
public class StringConstant extends AbstractNode {
public StringConstant(StringConstantParts strParts, Span span) {
super(span, List.of(strParts));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitStringConstant(this);
}
public StringConstantParts getStrParts() {
assert children != null;
return (StringConstantParts) children.get(0);
}
}

View File

@@ -0,0 +1,154 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.List;
import java.util.Objects;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
@SuppressWarnings("ALL")
public abstract sealed class StringConstantPart extends AbstractNode {
public StringConstantPart(Span span, @Nullable List<? extends @Nullable Node> children) {
super(span, children);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitStringConstantPart(this);
}
public static final class StringNewline extends StringConstantPart {
public StringNewline(Span span) {
super(span, null);
}
}
public static final class ConstantPart extends StringConstantPart {
private final String str;
public ConstantPart(String str, Span span) {
super(span, null);
this.str = str;
}
public String getStr() {
return str;
}
@Override
public String toString() {
return "ConstantPart{str='" + str + '\'' + ", span=" + span + '}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ConstantPart that = (ConstantPart) o;
return Objects.equals(str, that.str) && Objects.equals(span, that.span);
}
@Override
public int hashCode() {
return Objects.hash(str, span);
}
}
public static final class StringUnicodeEscape extends StringConstantPart {
private final String escape;
public StringUnicodeEscape(String escape, Span span) {
super(span, null);
this.escape = escape;
}
public String getEscape() {
return escape;
}
@Override
public String toString() {
return "StringUnicodeEscape{escape='" + escape + '\'' + ", span=" + span + '}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
StringUnicodeEscape that = (StringUnicodeEscape) o;
return Objects.equals(escape, that.escape) && Objects.equals(span, that.span);
}
@Override
public int hashCode() {
return Objects.hash(escape, span);
}
}
public static final class StringEscape extends StringConstantPart {
private final EscapeType type;
public StringEscape(EscapeType type, Span span) {
super(span, null);
this.type = type;
}
public EscapeType getType() {
return type;
}
@Override
public String toString() {
return "StringEscape{type=" + type + ", span=" + span + '}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
StringEscape that = (StringEscape) o;
return type == that.type && Objects.equals(span, that.span);
}
@Override
public int hashCode() {
return Objects.hash(type, span);
}
}
public enum EscapeType {
NEWLINE,
TAB,
RETURN,
QUOTE,
BACKSLASH
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.List;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
@SuppressWarnings("ALL")
public abstract sealed class StringPart extends AbstractNode {
public StringPart(Span span, @Nullable List<? extends @Nullable Node> children) {
super(span, children);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitStringPart(this);
}
public static final class StringConstantParts extends StringPart {
public StringConstantParts(List<StringConstantPart> parts, Span span) {
super(span, parts);
}
public List<StringConstantPart> getParts() {
return (List<StringConstantPart>) children;
}
}
public static final class StringInterpolation extends StringPart {
public StringInterpolation(Expr expr, Span span) {
super(span, List.of(expr));
}
public Expr getExpr() {
return (Expr) children.get(0);
}
}
}

View File

@@ -0,0 +1,219 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.List;
import java.util.Objects;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
@SuppressWarnings("ALL")
public abstract sealed class Type extends AbstractNode {
public Type(Span span, @Nullable List<? extends @Nullable Node> children) {
super(span, children);
}
public static final class UnknownType extends Type {
public UnknownType(Span span) {
super(span, null);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitUnknownType(this);
}
}
public static final class NothingType extends Type {
public NothingType(Span span) {
super(span, null);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitNothingType(this);
}
}
public static final class ModuleType extends Type {
public ModuleType(Span span) {
super(span, null);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitModuleType(this);
}
}
public static final class StringConstantType extends Type {
public StringConstantType(StringConstant str, Span span) {
super(span, List.of(str));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitStringConstantType(this);
}
public StringConstant getStr() {
return (StringConstant) children.get(0);
}
}
public static final class DeclaredType extends Type {
public DeclaredType(List<Node> nodes, Span span) {
super(span, nodes);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitDeclaredType(this);
}
public QualifiedIdentifier getName() {
return (QualifiedIdentifier) children.get(0);
}
public List<Type> getArgs() {
return (List<Type>) children.subList(1, children.size());
}
}
public static final class ParenthesizedType extends Type {
public ParenthesizedType(Type type, Span span) {
super(span, List.of(type));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitParenthesizedType(this);
}
public Type getType() {
return (Type) children.get(0);
}
}
public static final class NullableType extends Type {
public NullableType(Type type, Span span) {
super(span, List.of(type));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitNullableType(this);
}
public Type getType() {
return (Type) children.get(0);
}
}
public static final class ConstrainedType extends Type {
public ConstrainedType(List<Node> nodes, Span span) {
super(span, nodes);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitConstrainedType(this);
}
public Type getType() {
return (Type) children.get(0);
}
public List<Expr> getExprs() {
return (List<Expr>) children.subList(1, children.size());
}
}
public static final class UnionType extends Type {
private final int defaultIndex;
public UnionType(List<Type> types, int defaultIndex, Span span) {
super(span, types);
this.defaultIndex = defaultIndex;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitUnionType(this);
}
public List<Type> getTypes() {
return (List<Type>) children;
}
public int getDefaultIndex() {
return defaultIndex;
}
@Override
public String toString() {
return "UnionType{"
+ "defaultIndex="
+ defaultIndex
+ ", span="
+ span
+ ", children="
+ children
+ '}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
UnionType unionType = (UnionType) o;
return defaultIndex == unionType.defaultIndex;
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), defaultIndex);
}
}
public static final class FunctionType extends Type {
public FunctionType(List<Node> children, Span span) {
super(span, children);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitFunctionType(this);
}
public List<Type> getArgs() {
return (List<Type>) children.subList(0, children.size() - 1);
}
public Type getRet() {
return (Type) children.get(children.size() - 1);
}
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.List;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
@SuppressWarnings("ALL")
public final class TypeAlias extends AbstractNode {
private final int modifiersOffset;
private final int nameOffset;
public TypeAlias(List<Node> children, int modifiersOffset, int nameOffset, Span span) {
super(span, children);
this.modifiersOffset = modifiersOffset;
this.nameOffset = nameOffset;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitTypeAlias(this);
}
public @Nullable DocComment getDocComment() {
return (DocComment) children.get(0);
}
public List<Annotation> getAnnotations() {
return (List<Annotation>) children.subList(1, modifiersOffset);
}
public List<Modifier> getModifiers() {
return (List<Modifier>) children.subList(modifiersOffset, nameOffset);
}
public Identifier getName() {
return (Identifier) children.get(nameOffset);
}
public @Nullable TypeParameterList getTypeParameterList() {
return (TypeParameterList) children.get(nameOffset + 1);
}
public Type getType() {
return (Type) children.get(nameOffset + 2);
}
public Span getHeaderSpan() {
var end = children.get(nameOffset).span();
var tparList = children.get(nameOffset + 1);
if (tparList != null) {
end = tparList.span();
}
return span.endWith(end);
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.List;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
public class TypeAnnotation extends AbstractNode {
public TypeAnnotation(Type type, Span span) {
super(span, List.of(type));
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitTypeAnnotation(this);
}
public Type getType() {
assert children != null;
return (Type) children.get(0);
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.List;
import java.util.Objects;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
public final class TypeParameter extends AbstractNode {
private final @Nullable Variance variance;
public TypeParameter(@Nullable Variance variance, Identifier identifier, Span span) {
super(span, List.of(identifier));
this.variance = variance;
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitTypeParameter(this);
}
public @Nullable Variance getVariance() {
return variance;
}
public Identifier getIdentifier() {
assert children != null;
return (Identifier) children.get(0);
}
@Override
public String toString() {
return "TypeParameter{"
+ "variance="
+ variance
+ ", children="
+ children
+ ", span="
+ span
+ '}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
TypeParameter that = (TypeParameter) o;
return variance == that.variance;
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), variance);
}
public enum Variance {
IN,
OUT
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.parser.ast;
import java.util.List;
import org.pkl.core.parser.ParserVisitor;
import org.pkl.core.parser.Span;
import org.pkl.core.util.Nullable;
public class TypeParameterList extends AbstractNode {
public TypeParameterList(List<TypeParameter> parameters, Span span) {
super(span, parameters);
}
@Override
public <T> @Nullable T accept(ParserVisitor<? extends T> visitor) {
return visitor.visitTypeParameterList(this);
}
@SuppressWarnings("unchecked")
public List<TypeParameter> getParameters() {
assert children != null;
return (List<TypeParameter>) children;
}
}

View File

@@ -0,0 +1,4 @@
@NonnullByDefault
package org.pkl.core.parser.ast;
import org.pkl.core.util.NonnullByDefault;

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,7 +23,6 @@ import java.net.URI;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.graalvm.collections.UnmodifiableEconomicMap;
import org.graalvm.polyglot.Context;
import org.pkl.core.*;
@@ -36,10 +35,14 @@ import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.http.HttpClient;
import org.pkl.core.module.*;
import org.pkl.core.packages.PackageResolver;
import org.pkl.core.parser.LexParseException;
import org.pkl.core.parser.Parser;
import org.pkl.core.parser.antlr.PklParser;
import org.pkl.core.parser.antlr.PklParser.*;
import org.pkl.core.parser.ParserError;
import org.pkl.core.parser.ast.Class;
import org.pkl.core.parser.ast.ClassProperty;
import org.pkl.core.parser.ast.Expr;
import org.pkl.core.parser.ast.ImportClause;
import org.pkl.core.parser.ast.ModuleDecl;
import org.pkl.core.parser.ast.ReplInput;
import org.pkl.core.project.DeclaredDependencies;
import org.pkl.core.repl.ReplRequest.Eval;
import org.pkl.core.repl.ReplRequest.Load;
@@ -166,7 +169,7 @@ public class ReplServer implements AutoCloseable {
.collect(Collectors.toList());
}
@SuppressWarnings("StatementWithEmptyBody")
@SuppressWarnings({"StatementWithEmptyBody", "DataFlowIssue"})
private List<Object> evaluate(
ReplState replState,
String requestId,
@@ -174,14 +177,12 @@ public class ReplServer implements AutoCloseable {
boolean evalDefinitions,
boolean forceResults) {
var parser = new Parser();
PklParser.ReplInputContext replInputContext;
ReplInput replInputContext;
var uri = URI.create("repl:" + requestId);
try {
replInputContext = parser.parseReplInput(text);
} catch (LexParseException.IncompleteInput e) {
return List.of(new ReplResponse.IncompleteInput(e.getMessage()));
} catch (LexParseException e) {
} catch (ParserError e) {
var exception = VmUtils.toVmException(e, text, uri, uri.toString());
var errorMessage = errorRenderer.render(exception);
return List.of(new EvalError(errorMessage));
@@ -205,32 +206,28 @@ public class ReplServer implements AutoCloseable {
language,
replState.module.getModuleInfo(),
moduleResolver);
var childrenExceptEof =
replInputContext.children.subList(0, replInputContext.children.size() - 1);
for (var tree : childrenExceptEof) {
for (var tree : replInputContext.getNodes()) {
try {
if (tree instanceof ExprContext) {
var exprNode = (ExpressionNode) tree.accept(builder);
if (tree instanceof Expr expr) {
var exprNode = builder.visitExpr(expr);
evaluateExpr(replState, exprNode, forceResults, results);
} else if (tree instanceof ImportClauseContext importClause) {
} else if (tree instanceof ImportClause importClause) {
addStaticModuleProperty(builder.visitImportClause(importClause));
} else if (tree instanceof ClassPropertyContext classProperty) {
} else if (tree instanceof ClassProperty classProperty) {
var propertyNode = builder.visitClassProperty(classProperty);
var property = addModuleProperty(propertyNode);
if (evalDefinitions) {
evaluateMemberDef(replState, property, forceResults, results);
}
} else if (tree instanceof ClazzContext clazz) {
addStaticModuleProperty(builder.visitClazz(clazz));
} else if (tree instanceof TypeAliasContext typeAlias) {
} else if (tree instanceof Class clazz) {
addStaticModuleProperty(builder.visitClass(clazz));
} else if (tree instanceof org.pkl.core.parser.ast.TypeAlias typeAlias) {
addStaticModuleProperty(builder.visitTypeAlias(typeAlias));
} else if (tree instanceof ClassMethodContext classMethod) {
} else if (tree instanceof org.pkl.core.parser.ast.ClassMethod classMethod) {
addModuleMethodDef(builder.visitClassMethod(classMethod));
} else if (tree instanceof ModuleDeclContext) {
} else if (tree instanceof ModuleDecl) {
// do nothing for now
} else if (tree instanceof TerminalNode && tree.toString().equals(",")) {
// do nothing
} else {
results.add(
new ReplResponse.InternalError(new IllegalStateException("Unexpected parse result")));
@@ -261,7 +258,7 @@ public class ReplServer implements AutoCloseable {
language, new FrameDescriptor(), propertyNode, replState.module.getVmClass());
var property =
(ClassProperty)
(org.pkl.core.ast.member.ClassProperty)
callNode.call(resolveNode.getCallTarget(), replState.module, replState.module);
replState.module.getVmClass().addProperty(property);
@@ -397,7 +394,7 @@ public class ReplServer implements AutoCloseable {
}
private VmTyped createReplModule(
Iterable<ClassProperty> propertyDefs,
Iterable<org.pkl.core.ast.member.ClassProperty> propertyDefs,
Iterable<ClassMethod> methodDefs,
UnmodifiableEconomicMap<Object, ObjectMember> moduleMembers,
@Nullable VmTyped parent) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,12 +16,12 @@
package org.pkl.core.runtime;
import com.oracle.truffle.api.nodes.Node;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.RuleContext;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.pkl.core.Release;
import org.pkl.core.Version;
import org.pkl.core.parser.antlr.PklParser.*;
import org.pkl.core.parser.ast.Module;
import org.pkl.core.parser.ast.ObjectMember.ObjectProperty;
import org.pkl.core.parser.ast.Type;
import org.pkl.core.parser.ast.Type.DeclaredType;
import org.pkl.core.util.Nullable;
final class MinPklVersionChecker {
@@ -44,26 +44,26 @@ final class MinPklVersionChecker {
}
}
static void check(String moduleName, @Nullable ParserRuleContext ctx, @Nullable Node importNode) {
if (!(ctx instanceof ModuleContext moduleCtx)) return;
static void check(
String moduleName, @Nullable Module mod, @Nullable Node importNode, String source) {
if (mod == null) return;
var moduleDeclCtx = moduleCtx.moduleDecl();
if (moduleDeclCtx == null) return;
var moduleDecl = mod.getDecl();
if (moduleDecl == null) return;
for (var annCtx : moduleDeclCtx.annotation()) {
if (!Identifier.MODULE_INFO.toString().equals(getLastIdText(annCtx.type()))) continue;
for (var ann : moduleDecl.getAnnotations()) {
if (!Identifier.MODULE_INFO.toString().equals(getLastIdText(ann.getType()))) continue;
var objectBodyCtx = annCtx.objectBody();
if (objectBodyCtx == null) continue;
var objectBody = ann.getBody();
if (objectBody == null) continue;
for (var memberCtx : objectBodyCtx.objectMember()) {
if (!(memberCtx instanceof ObjectPropertyContext propertyCtx)) continue;
for (var member : objectBody.getMembers()) {
if (!(member instanceof ObjectProperty prop)) continue;
if (!Identifier.MIN_PKL_VERSION.toString().equals(getText(propertyCtx.Identifier())))
if (!Identifier.MIN_PKL_VERSION.toString().equals(prop.getIdentifier().getValue()))
continue;
var versionText = getText(propertyCtx.expr());
if (versionText == null) continue;
var versionText = prop.getExpr().text(source.toCharArray());
Version version;
try {
@@ -78,20 +78,6 @@ final class MinPklVersionChecker {
}
}
private static @Nullable String getText(@Nullable RuleContext ruleCtx) {
return ruleCtx == null ? null : ruleCtx.getText();
}
private static @Nullable String getLastIdText(@Nullable TypeContext typeCtx) {
if (!(typeCtx instanceof DeclaredTypeContext declCtx)) return null;
var token = declCtx.qualifiedIdentifier().Identifier;
return token == null ? null : token.getText();
}
private static @Nullable String getText(@Nullable TerminalNode idCtx) {
return idCtx == null ? null : idCtx.getText();
}
private static void doCheck(
String moduleName, @Nullable Version requiredVersion, @Nullable Node importNode) {
if (requiredVersion == null || currentMajorMinorPatchVersion.compareTo(requiredVersion) >= 0)
@@ -102,4 +88,10 @@ final class MinPklVersionChecker {
.evalError("incompatiblePklVersion", moduleName, requiredVersion, currentVersion)
.build();
}
private static @Nullable String getLastIdText(@Nullable Type type) {
if (!(type instanceof DeclaredType declType)) return null;
var identifiers = declType.getName().getIdentifiers();
return identifiers.get(identifiers.size() - 1).getValue();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,7 +32,7 @@ import org.pkl.core.util.Nullable;
public final class ModuleInfo {
private final SourceSection headerSection;
private final SourceSection sourceSection;
private final @Nullable SourceSection docComment;
private final SourceSection @Nullable [] docComment;
private final String moduleName;
private final ModuleKey moduleKey;
private final ResolvedModuleKey resolvedModuleKey;
@@ -49,7 +49,7 @@ public final class ModuleInfo {
public ModuleInfo(
SourceSection sourceSection,
SourceSection headerSection,
@Nullable SourceSection docComment,
SourceSection @Nullable [] docComment,
String moduleName,
ModuleKey moduleKey,
ResolvedModuleKey resolvedModuleKey,
@@ -82,7 +82,7 @@ public final class ModuleInfo {
return headerSection;
}
public @Nullable SourceSection getDocComment() {
public SourceSection @Nullable [] getDocComment() {
return docComment;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,7 +46,7 @@ import org.pkl.core.util.Nullable;
public final class VmClass extends VmValue {
private final SourceSection sourceSection;
private final SourceSection headerSection;
private final @Nullable SourceSection docComment;
private final SourceSection @Nullable [] docComment;
private final List<VmTyped> annotations;
private final int modifiers;
private final PClassInfo<?> classInfo;
@@ -126,7 +126,7 @@ public final class VmClass extends VmValue {
public VmClass(
SourceSection sourceSection,
SourceSection headerSection,
@Nullable SourceSection docComment,
SourceSection @Nullable [] docComment,
List<VmTyped> annotations,
int modifiers,
PClassInfo<?> classInfo,
@@ -223,7 +223,7 @@ public final class VmClass extends VmValue {
return headerSection;
}
public @Nullable SourceSection getDocComment() {
public SourceSection @Nullable [] getDocComment() {
return docComment;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
package org.pkl.core.runtime;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.TruffleLanguage.ContextPolicy;
import com.oracle.truffle.api.nodes.Node;
@@ -24,9 +23,9 @@ import com.oracle.truffle.api.source.Source;
import org.pkl.core.ast.builder.AstBuilder;
import org.pkl.core.module.ModuleKey;
import org.pkl.core.module.ResolvedModuleKey;
import org.pkl.core.parser.LexParseException;
import org.pkl.core.parser.Parser;
import org.pkl.core.parser.antlr.PklParser;
import org.pkl.core.parser.ParserError;
import org.pkl.core.parser.ast.Module;
import org.pkl.core.util.IoUtils;
import org.pkl.core.util.Nullable;
@@ -56,7 +55,6 @@ public final class VmLanguage extends TruffleLanguage<VmContext> {
throw new UnsupportedOperationException("parse");
}
@TruffleBoundary
public VmTyped loadModule(ModuleKey moduleKey) {
var context = VmContext.get(null);
@@ -71,7 +69,6 @@ public final class VmLanguage extends TruffleLanguage<VmContext> {
null);
}
@TruffleBoundary
public VmTyped loadModule(ModuleKey moduleKey, @Nullable Node importNode) {
var context = VmContext.get(null);
@@ -86,7 +83,6 @@ public final class VmLanguage extends TruffleLanguage<VmContext> {
importNode);
}
@TruffleBoundary
void initializeModule(
ModuleKey moduleKey,
ResolvedModuleKey resolvedModuleKey,
@@ -95,12 +91,13 @@ public final class VmLanguage extends TruffleLanguage<VmContext> {
VmTyped emptyModule,
@Nullable Node importNode) {
var parser = new Parser();
PklParser.ModuleContext moduleContext;
Module moduleContext;
var sourceStr = source.getCharacters().toString();
try {
moduleContext = parser.parseModule(source.getCharacters().toString());
} catch (LexParseException e) {
moduleContext = parser.parseModule(sourceStr);
} catch (ParserError e) {
var moduleName = IoUtils.inferModuleName(moduleKey);
MinPklVersionChecker.check(moduleName, e.getPartialParseResult(), importNode);
MinPklVersionChecker.check(moduleName, e.getPartialParseResult(), importNode, sourceStr);
throw VmUtils.toVmException(e, source, moduleName);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,7 +37,7 @@ import org.pkl.core.util.Nullable;
public final class VmTypeAlias extends VmValue {
private final SourceSection sourceSection;
private final SourceSection headerSection;
private final @Nullable SourceSection docComment;
private final SourceSection @Nullable [] docComment;
private final int modifiers;
private final List<VmTyped> annotations;
private final String simpleName;
@@ -63,7 +63,7 @@ public final class VmTypeAlias extends VmValue {
public VmTypeAlias(
SourceSection sourceSection,
SourceSection headerSection,
@Nullable SourceSection docComment,
SourceSection @Nullable [] docComment,
int modifiers,
List<VmTyped> annotations,
String simpleName,
@@ -128,7 +128,7 @@ public final class VmTypeAlias extends VmValue {
return typeNode != null;
}
public @Nullable SourceSection getDocComment() {
public SourceSection @Nullable [] getDocComment() {
return docComment;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -56,9 +56,9 @@ import org.pkl.core.ast.type.UnresolvedTypeNode;
import org.pkl.core.module.ModuleKey;
import org.pkl.core.module.ModuleKeys;
import org.pkl.core.module.ResolvedModuleKey;
import org.pkl.core.parser.LexParseException;
import org.pkl.core.parser.Parser;
import org.pkl.core.parser.antlr.PklParser.ExprContext;
import org.pkl.core.parser.ParserError;
import org.pkl.core.parser.ast.Expr;
import org.pkl.core.util.EconomicMaps;
import org.pkl.core.util.Nullable;
@@ -506,7 +506,7 @@ public final class VmUtils {
}
public static VmException toVmException(
LexParseException e, String text, URI moduleUri, String moduleName) {
ParserError e, String text, URI moduleUri, String moduleName) {
var source =
Source.newBuilder("pkl", text, moduleName)
.mimeType(VmLanguage.MIME_TYPE)
@@ -516,47 +516,36 @@ public final class VmUtils {
return toVmException(e, source, moduleName);
}
// wanted to keep Parser/LexParseException API free from
// wanted to keep Parser/ParserError API free from
// Truffle classes (Source), hence put this method here
public static VmException toVmException(LexParseException e, Source source, String moduleName) {
int lineStartOffset;
try {
lineStartOffset = source.getLineStartOffset(e.getLine());
} catch (IllegalArgumentException iae) {
// work around the fact that antlr and truffle disagree on how many lines a file that is
// ending in a newline has
lineStartOffset = source.getLineStartOffset(e.getLine() - 1);
}
public static VmException toVmException(ParserError e, Source source, String moduleName) {
return new VmExceptionBuilder()
.adhocEvalError(e.getMessage())
.withSourceSection(
source.createSection(
// compute char offset manually to work around
// https://github.com/graalvm/truffle/issues/184
lineStartOffset + e.getColumn() - 1, e.getLength()))
.withSourceSection(source.createSection(e.span().charIndex(), e.span().length()))
.withMemberName(moduleName)
.build();
}
public static @Nullable String exportDocComment(@Nullable SourceSection docComment) {
public static @Nullable String exportDocComment(SourceSection @Nullable [] docComment) {
if (docComment == null) return null;
var builder = new StringBuilder();
var matcher = DOC_COMMENT_LINE_START.matcher(docComment.getCharacters());
var firstMatch = true;
while (matcher.find()) {
if (firstMatch) {
matcher.appendReplacement(builder, "");
firstMatch = false;
} else {
matcher.appendReplacement(builder, "\n");
for (var i = 0; i < docComment.length; i++) {
if (i > 0) {
builder.append("\n");
}
var matcher = DOC_COMMENT_LINE_START.matcher(docComment[i].getCharacters());
var firstMatch = true;
while (matcher.find()) {
if (firstMatch) {
matcher.appendReplacement(builder, "");
firstMatch = false;
} else {
matcher.appendReplacement(builder, "\n");
}
}
matcher.appendTail(builder);
}
matcher.appendTail(builder);
var newLength = builder.length() - 1;
assert builder.charAt(newLength) == '\n';
builder.setLength(newLength);
return builder.toString();
}
@@ -857,10 +846,10 @@ public final class VmUtils {
section.getEndColumn());
}
private static ExprContext parseExpressionContext(String expression, Source source) {
private static Expr parseExpressionNode(String expression, Source source) {
try {
return new Parser().parseExpressionInput(expression).expr();
} catch (LexParseException e) {
return new Parser().parseExpressionInput(expression);
} catch (ParserError e) {
throw VmUtils.toVmException(e, source, REPL_TEXT);
}
}
@@ -891,8 +880,8 @@ public final class VmUtils {
false);
var language = VmLanguage.get(null);
var builder = new AstBuilder(source, language, moduleInfo, moduleResolver);
var exprContext = parseExpressionContext(expression, source);
var exprNode = (ExpressionNode) exprContext.accept(builder);
var parsedExpression = parseExpressionNode(expression, source);
var exprNode = builder.visitExpr(parsedExpression);
var rootNode =
new SimpleRootNode(
language, new FrameDescriptor(), exprNode.getSourceSection(), "", exprNode);

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -797,7 +797,8 @@ Modifier `const` cannot be applied to property `{0}`.\n\
Property `{0}` cannot be declared const, because it overrides a non-const property on parent class `{1}`.
unseparatedObjectMembers=\
Object members must be separated by whitespace, newline, or semicolon.
Object members must be separated by whitespace, newline, or semicolon.\n\
Object entries must be separated by newline or semicolon.
unsupportedResourceType=\
Resource reader `{0}` returned unsupported resource type `{1}`.
@@ -1126,3 +1127,58 @@ External {0} reader does not support scheme `{1}`.
externalReaderAlreadyTerminated=\
External reader process has already terminated.
keywordNotAllowedHere=\
Keyword `{0}` is not allowed here.\n\
\n\
If you must use this name as identifier, enclose it in backticks.
unexpectedEndOfFile=\
Unexpected end of file.
wrongHeaders=\
{0} cannot have doc comments, annotations or modifiers.
invalidTopLevelToken=\
Invalid token at position. Expected a class, typealias, method, or property.
interpolationInConstant=\
String constant cannot have interpolated values.
unexpectedToken=\
Unexpected token `{0}`. Expected `{1}`.
unexpectedToken2=\
Unexpected token `{0}`. Expected `{1}` or `{2}`.
unexpectedTokenForExpression=\
Unexpected token `{0}`.
unexpectedTokenForType=\
Unexpected token `{0}`. Expected a type.
unexpectedTokenForType2=\
Unexpected token `{0}`. Expected a type or `{1}`.
typeAnnotationInAmends=\
Properties with type annotations cannot have object bodies.\n\
\n\
To define both a type annotation and an object body, try using assignment instead.\n\
For example: `obj: Bird = new { ... }`.
invalidProperty=\
Invalid property definition. Expected a type annotation, `=` or `{`.
unexpectedCharacter=\
Unexpected character `{0}`. Did you mean `{1}`?
unexpectedCharacter3=\
Unexpected character `{0}`. Did you mean `{1}`, `{2}` or `{3}`?
invalidCharacter=\
Invalid identifier `{0}`.
danglingDocComment=\
Dangling documentation comment.\n\
\n\
Documentation comments must be attached to modules, classes, typealiases, methods, or properties.