Add pkl:syntax stdlib module (#1821)
This commit is contained in:
1 parent
0cb9843af7
commit
939e844c03
48 files changed
+3149
-214
No files matched your search
+4
-11
@@ -30,6 +30,7 @@
|
||||
module pkl.reflect
|
||||
|
||||
import "pkl:base"
|
||||
import "pkl:syntax"
|
||||
|
||||
/// A mirror for the [Any] type.
|
||||
const anyType: DeclaredType = DeclaredType(Class(Any))
|
||||
@@ -166,7 +167,7 @@ external const function TypeVariable(referent: TypeParameter): TypeVariable
|
||||
/// A program declaration.
|
||||
abstract external class Declaration {
|
||||
/// The source location of this declaration.
|
||||
location: SourceLocation
|
||||
location: syntax.SourceLocation
|
||||
|
||||
/// The documentation comment for this declaration, if any.
|
||||
docComment: String?
|
||||
@@ -457,13 +458,5 @@ external class TypeVariable extends Type {
|
||||
}
|
||||
|
||||
/// A source location within a module.
|
||||
external class SourceLocation {
|
||||
/// The 1-based line number of this source location.
|
||||
line: UInt
|
||||
|
||||
/// The 1-based column number of this source location.
|
||||
column: UInt
|
||||
|
||||
/// The display URI of this source location.
|
||||
displayUri: String
|
||||
}
|
||||
@Deprecated { since = "0.33.0"; replaceWith = "syntax.SourceLocation" }
|
||||
typealias SourceLocation = syntax.SourceLocation
|
||||
@@ -0,0 +1,422 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Copyright © 2026 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.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Utilities for managing Pkl source code.
|
||||
@ModuleInfo { minPklVersion = "0.33.0" }
|
||||
@Since { version = "0.33.0" }
|
||||
module pkl.syntax
|
||||
|
||||
/// Utilities to parse Pkl source code to a syntax tree.
|
||||
class Parser {
|
||||
/// Parse the string or resource as a Pkl module, returning either a syntax node or
|
||||
/// throwing an error.
|
||||
external function parseModule(source: String | Resource): Node
|
||||
|
||||
/// Parse the string or resource as a Pkl module, returning either a syntax node or null
|
||||
/// in case of an error.
|
||||
external function parseModuleOrNull(source: String | Resource): Node?
|
||||
|
||||
/// Parse the string or resource as a Pkl expression, returning either a syntax node or
|
||||
/// throwing an error.
|
||||
external function parseExpression(source: String | Resource): Node
|
||||
|
||||
/// Parse the string or resource as a Pkl expression, returning either a syntax node or null
|
||||
/// in case of an error.
|
||||
external function parseExpressionOrNull(source: String | Resource): Node?
|
||||
}
|
||||
|
||||
/// Renders a [Node] back to Pkl source code, using the canonical Pkl formatter.
|
||||
class FormattingRenderer {
|
||||
/// The grammar version to target.
|
||||
///
|
||||
/// `"V1"` matches Pkl 0.25 - 0.29, `"V2"` matches Pkl 0.30+.
|
||||
grammarVersion: "V1" | "V2" = "V2"
|
||||
|
||||
/// Render [node] as Pkl source code.
|
||||
external function render(node: Node): String
|
||||
}
|
||||
|
||||
/// A generic, untyped node in the Pkl syntax tree.
|
||||
///
|
||||
/// A node is either a *leaf*, carrying no [children], or a *branch*, whose content is its
|
||||
/// [children]. Either way, [text] is the source code the node spans.
|
||||
class Node {
|
||||
/// The kind of this node.
|
||||
type: NodeType
|
||||
|
||||
/// The child nodes, in source order. Empty for leaf nodes.
|
||||
children: List<Node>(isTerminal.implies(isEmpty))
|
||||
|
||||
/// The parent node, or [null] for the root and for nodes not attached to a tree.
|
||||
hidden parent: Node?
|
||||
|
||||
/// The verbatim source text for this node.
|
||||
///
|
||||
/// [FormattingRenderer] only renders this text if this is a [terminal][isTerminal] node.
|
||||
text: String?(isTerminal.implies(this != null))
|
||||
|
||||
/// The source location of this node.
|
||||
span: Span?
|
||||
|
||||
/// Tells if this is a terminal node; i.e. a node that spans a single token of the grammar.
|
||||
///
|
||||
/// A terminal never has [children], but not every node without [children] is a terminal.
|
||||
fixed isTerminal: Boolean = type is TerminalNodeType
|
||||
|
||||
/// Fold [operator] over this node and its descendants, top-down in pre-order.
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// // count the if-expressions in a module
|
||||
/// module.fold(0, (acc, n) -> if (n.type == "if_expr") acc + 1 else acc)
|
||||
/// ```
|
||||
external function fold<Result>(initial: Result, operator: (Result, Node) -> Result): Result
|
||||
|
||||
/// Returns the first child matching [predicate], searching depth-first in pre-order.
|
||||
///
|
||||
/// *Child* means any node below this one, at any depth.
|
||||
/// This node itself is not considered.
|
||||
/// The search stops at the first match.
|
||||
///
|
||||
/// Throws if no child matches [predicate].
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// // the name of the first class declared in a module
|
||||
/// module.findChild((n) -> n.type == "class").findChildOfType("identifier").text
|
||||
/// ```
|
||||
external function findChild(predicate: (Node) -> Boolean): Node
|
||||
|
||||
/// Same as [findChild()] but returns [null] if no child matches [predicate].
|
||||
external function findChildOrNull(predicate: (Node) -> Boolean): Node?
|
||||
|
||||
/// Returns every child matching [predicate], in depth-first pre-order.
|
||||
///
|
||||
/// *Child* means any node below this one, at any depth.
|
||||
/// This node itself is not considered.
|
||||
/// A matching node is searched for further matches, so a match may contain another.
|
||||
external function findChildren(predicate: (Node) -> Boolean): List<Node>
|
||||
|
||||
/// Returns the first child whose [type] is [type].
|
||||
///
|
||||
/// Throws if no child has this type.
|
||||
external function findChildOfType(type: NodeType): Node
|
||||
|
||||
/// Same as [findChildOfType()] but returns [null] if no child has this type.
|
||||
external function findChildOfTypeOrNull(type: NodeType): Node?
|
||||
|
||||
/// Returns every child whose [type] is [type], in depth-first pre-order.
|
||||
external function findChildrenOfType(type: NodeType): List<Node>
|
||||
|
||||
/// Returns this node with the first child matching [predicate] replaced by [replacer]'s result.
|
||||
///
|
||||
/// The child replaced is the one [findChild()] returns. If there is none, the tree is
|
||||
/// returned unchanged.
|
||||
/// The replacement is left as it is, so it is not searched for further matches.
|
||||
external function replaceChild(predicate: (Node) -> Boolean, replacer: (Node) -> Node): Node
|
||||
|
||||
/// Returns this node with every child matching [predicate] replaced by [replacer]'s result.
|
||||
///
|
||||
/// The children replaced are those [findChildren()] returns, except that a replacement is left
|
||||
/// as it is, so a match nested in another match is left to [replacer].
|
||||
external function replaceChildren(predicate: (Node) -> Boolean, replacer: (Node) -> Node): Node
|
||||
|
||||
/// Returns this node with the first child whose [type] is [type] replaced by [replacer]'s result.
|
||||
///
|
||||
/// If there is no such child, the tree is returned unchanged.
|
||||
external function replaceChildOfType(type: NodeType, replacer: (Node) -> Node): Node
|
||||
|
||||
/// Returns this node with every child whose [type] is [type] replaced by [replacer]'s result.
|
||||
external function replaceChildrenOfType(type: NodeType, replacer: (Node) -> Node): Node
|
||||
|
||||
/// Returns the nearest parent matching [predicate].
|
||||
///
|
||||
/// Throws if no parent matches [predicate].
|
||||
external function findParent(predicate: (Node) -> Boolean): Node
|
||||
|
||||
/// Same as [findParent()] but returns [null] if no parent matches [predicate].
|
||||
external function findParentOrNull(predicate: (Node) -> Boolean): Node?
|
||||
|
||||
/// Returns the nearest parent whose [type] is [type].
|
||||
///
|
||||
/// Throws if no parent has this type.
|
||||
external function findParentOfType(type: NodeType): Node
|
||||
|
||||
/// Same as [findParentOfType()] but returns [null] if no parent has this type.
|
||||
external function findParentOfTypeOrNull(type: NodeType): Node?
|
||||
|
||||
/// Returns every parent matching [predicate], nearest first.
|
||||
external function findParents(predicate: (Node) -> Boolean): List<Node>
|
||||
|
||||
/// Tells whether any parent matches [predicate].
|
||||
external function hasParent(predicate: (Node) -> Boolean): Boolean
|
||||
|
||||
/// Tells whether any parent's [type] is [type].
|
||||
external function hasParentOfType(type: NodeType): Boolean
|
||||
}
|
||||
|
||||
/// A range of source code, spanning [start] (inclusive) to [end] (exclusive).
|
||||
class Span {
|
||||
/// The start of this span.
|
||||
start: SourceLocation
|
||||
|
||||
/// The end of this span.
|
||||
end: SourceLocation
|
||||
|
||||
/// The display URI of this span.
|
||||
///
|
||||
/// Rendered for the editor configured in the Pkl settings file, the same way a location is
|
||||
/// rendered in a stack trace.
|
||||
displayUri: String
|
||||
}
|
||||
|
||||
/// A position in source code, given as a 1-based line and column.
|
||||
class SourceLocation {
|
||||
/// The 1-based line number of this source location.
|
||||
line: UInt
|
||||
|
||||
/// The 1-based column number of this source location.
|
||||
column: UInt
|
||||
|
||||
/// A URI identifying this source location, suitable for display.
|
||||
///
|
||||
/// The exact format depends on where this location came from and on the editor configured in the
|
||||
/// Pkl settings file.
|
||||
displayUri: String
|
||||
}
|
||||
|
||||
/// The kinds of node in the Pkl syntax tree.
|
||||
typealias NodeType =
|
||||
TerminalNodeType
|
||||
| ExprNodeType
|
||||
| TypeNodeType
|
||||
| ObjectMemberNodeType
|
||||
| ClassMemberNodeType
|
||||
| ModuleMemberNodeType
|
||||
// module structure
|
||||
| "module"
|
||||
| "doc_comment"
|
||||
| "modifier_list"
|
||||
| "amends_clause"
|
||||
| "extends_clause"
|
||||
| "module_declaration"
|
||||
| "module_definition"
|
||||
| "annotation"
|
||||
| "qualified_identifier"
|
||||
| "import"
|
||||
| "import_alias"
|
||||
| "import_list"
|
||||
| "typealias_header"
|
||||
| "typealias_body"
|
||||
| "class_header"
|
||||
| "class_header_extends"
|
||||
| "class_body"
|
||||
| "class_body_elements"
|
||||
| "method_header"
|
||||
| "method_body"
|
||||
| "class_property_header"
|
||||
| "class_property_header_begin"
|
||||
| "class_property_body"
|
||||
| "object_body"
|
||||
| "object_member_elements"
|
||||
| "parameter"
|
||||
| "type_annotation"
|
||||
| "parameter_list"
|
||||
| "parameter_list_elements"
|
||||
| "type_parameter_list"
|
||||
| "type_parameter_list_elements"
|
||||
| "argument_list"
|
||||
| "argument_list_elements"
|
||||
| "type_argument_list"
|
||||
| "type_argument_list_elements"
|
||||
| "object_parameter_list"
|
||||
| "type_parameter"
|
||||
| "string_constant"
|
||||
// parts of members
|
||||
| "object_property_header"
|
||||
| "object_property_header_begin"
|
||||
| "object_property_body"
|
||||
| "object_entry_header"
|
||||
| "member_predicate_header"
|
||||
| "when_generator_header"
|
||||
| "for_generator_header"
|
||||
| "for_generator_header_definition"
|
||||
| "for_generator_header_definition_header"
|
||||
// parts of expressions
|
||||
| "new_header"
|
||||
| "function_literal_body"
|
||||
| "if_header"
|
||||
| "if_condition"
|
||||
| "if_condition_expr"
|
||||
| "if_then_expr"
|
||||
| "if_else_expr"
|
||||
| "let_parameter_definition"
|
||||
| "let_parameter"
|
||||
// parts of types
|
||||
| "function_type_parameters"
|
||||
| "parenthesized_type_elements"
|
||||
| "constrained_type_constraint"
|
||||
| "constrained_type_elements"
|
||||
|
||||
/// The [NodeType]s that describe a terminal: a node that spans a single token of the grammar, such
|
||||
/// as a keyword, a punctuation character, an identifier or a literal.
|
||||
///
|
||||
/// A terminal never has [Node.children].
|
||||
typealias TerminalNodeType =
|
||||
// keywords
|
||||
"amends_keyword"
|
||||
| "as_keyword"
|
||||
| "class_keyword"
|
||||
| "else_keyword"
|
||||
| "extends_keyword"
|
||||
| "for_keyword"
|
||||
| "function_keyword"
|
||||
| "if_keyword"
|
||||
| "import_keyword"
|
||||
| "import_star_keyword"
|
||||
| "in_keyword"
|
||||
| "let_keyword"
|
||||
| "module_keyword"
|
||||
| "new_keyword"
|
||||
| "out_keyword"
|
||||
| "read_keyword"
|
||||
| "read_question_keyword"
|
||||
| "read_star_keyword"
|
||||
| "super_keyword"
|
||||
| "throw_keyword"
|
||||
| "trace_keyword"
|
||||
| "typealias_keyword"
|
||||
| "when_keyword"
|
||||
// punctuation
|
||||
| "arrow"
|
||||
| "assign"
|
||||
| "at"
|
||||
| "colon"
|
||||
| "comma"
|
||||
| "dot"
|
||||
| "gt"
|
||||
| "lbrace"
|
||||
| "lbrack"
|
||||
| "lparen"
|
||||
| "lpred"
|
||||
| "lt"
|
||||
| "minus"
|
||||
| "not"
|
||||
| "qspread"
|
||||
| "question"
|
||||
| "rbrace"
|
||||
| "rbrack"
|
||||
| "rparen"
|
||||
| "spread"
|
||||
| "star"
|
||||
| "underscore"
|
||||
| "union"
|
||||
// string delimiters
|
||||
| "interpolation_start"
|
||||
| "string_start"
|
||||
| "string_multi_start"
|
||||
| "string_end"
|
||||
// string content
|
||||
| "string_chars"
|
||||
| "string_newline"
|
||||
| "string_continuation"
|
||||
| "string_escape"
|
||||
// affixes
|
||||
| "shebang"
|
||||
| "line_comment"
|
||||
| "block_comment"
|
||||
| "semicolon"
|
||||
| "doc_comment_line"
|
||||
// names, modifiers and operators
|
||||
| "identifier"
|
||||
| "modifier"
|
||||
| "operator"
|
||||
// literals
|
||||
| "bool_literal_expr"
|
||||
| "int_literal_expr"
|
||||
| "float_literal_expr"
|
||||
// expressions without operands
|
||||
| "this_expr"
|
||||
| "outer_expr"
|
||||
| "module_expr"
|
||||
| "null_expr"
|
||||
// types without operands
|
||||
| "unknown_type"
|
||||
| "nothing_type"
|
||||
| "module_type"
|
||||
| "this_type"
|
||||
|
||||
/// The [NodeType]s that describe an expression.
|
||||
typealias ExprNodeType =
|
||||
"this_expr"
|
||||
| "outer_expr"
|
||||
| "module_expr"
|
||||
| "null_expr"
|
||||
| "throw_expr"
|
||||
| "trace_expr"
|
||||
| "import_expr"
|
||||
| "read_expr"
|
||||
| "new_expr"
|
||||
| "unary_minus_expr"
|
||||
| "logical_not_expr"
|
||||
| "function_literal_expr"
|
||||
| "parenthesized_expr"
|
||||
| "super_subscript_expr"
|
||||
| "super_access_expr"
|
||||
| "subscript_expr"
|
||||
| "qualified_access_expr"
|
||||
| "if_expr"
|
||||
| "let_expr"
|
||||
| "bool_literal_expr"
|
||||
| "int_literal_expr"
|
||||
| "float_literal_expr"
|
||||
| "single_line_string_literal_expr"
|
||||
| "multi_line_string_literal_expr"
|
||||
| "unqualified_access_expr"
|
||||
| "non_null_expr"
|
||||
| "amends_expr"
|
||||
| "binary_op_expr"
|
||||
|
||||
/// The [NodeType]s that describe a type.
|
||||
typealias TypeNodeType =
|
||||
"unknown_type"
|
||||
| "nothing_type"
|
||||
| "module_type"
|
||||
| "this_type"
|
||||
| "union_type"
|
||||
| "function_type"
|
||||
| "parenthesized_type"
|
||||
| "declared_type"
|
||||
| "nullable_type"
|
||||
| "string_literal_type"
|
||||
| "constrained_type"
|
||||
|
||||
/// The [NodeType]s that describe a member of an object body.
|
||||
typealias ObjectMemberNodeType =
|
||||
"object_element"
|
||||
| "object_property"
|
||||
| "object_method"
|
||||
| "member_predicate"
|
||||
| "object_entry"
|
||||
| "object_spread"
|
||||
| "when_generator"
|
||||
| "for_generator"
|
||||
|
||||
/// The [NodeType]s that describe a member of a class body.
|
||||
typealias ClassMemberNodeType = "class_property" | "class_method"
|
||||
|
||||
/// The [NodeType]s that describe a member of a module.
|
||||
typealias ModuleMemberNodeType = "class_property" | "class_method" | "class" | "typealias"
|
||||
Reference in new issue
Block a user