mirror of https://github.com/Wilfred/difftastic/
Merge commit '09dbf221d7491dc8d8839616b27c21b9c025c457'
commit
1dd7bbebe8
@ -0,0 +1,22 @@
|
|||||||
|
name: Fuzz Parser
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- src/scanner.c
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- src/scanner.c
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: Parser fuzzing
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: vigoux/tree-sitter-fuzz-action@v1
|
||||||
|
with:
|
||||||
|
language: elm
|
||||||
|
external-scanner: src/scanner.c
|
||||||
|
time: 60
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
name: Validate that changes doesn't break elm-language-server
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest]
|
||||||
|
node-version: [18, 20]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: "tree-sitter-elm"
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
name: Checkout main branch of elm-language-server
|
||||||
|
with:
|
||||||
|
repository: "elm-tooling/elm-language-server"
|
||||||
|
path: "elm-language-server"
|
||||||
|
- name: Install tree-sitter dependencies and generate wasm bundle
|
||||||
|
run: |
|
||||||
|
cd tree-sitter-elm/
|
||||||
|
npm i
|
||||||
|
npm run build
|
||||||
|
npx tree-sitter build-wasm
|
||||||
|
mv ./tree-sitter-elm.wasm ../elm-language-server/tree-sitter-elm.wasm -f
|
||||||
|
|
||||||
|
- name: Install elm-language-server dependencies, compile, and run tests
|
||||||
|
run: |
|
||||||
|
cd elm-language-server/
|
||||||
|
npm i
|
||||||
|
npm run compile
|
||||||
|
npm install -g elm-format
|
||||||
|
npm test
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
// swift-tools-version:5.3
|
||||||
|
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "TreeSitterElm",
|
||||||
|
platforms: [.macOS(.v10_13), .iOS(.v11)],
|
||||||
|
products: [
|
||||||
|
.library(name: "TreeSitterElm", targets: ["TreeSitterElm"]),
|
||||||
|
],
|
||||||
|
dependencies: [],
|
||||||
|
targets: [
|
||||||
|
.target(name: "TreeSitterElm",
|
||||||
|
path: ".",
|
||||||
|
exclude: [
|
||||||
|
"binding.gyp",
|
||||||
|
"bindings",
|
||||||
|
"Cargo.toml",
|
||||||
|
"docs",
|
||||||
|
"examples",
|
||||||
|
"grammar.js",
|
||||||
|
"HOW_TO_RELEASE.md",
|
||||||
|
"index.d.ts",
|
||||||
|
"LICENSE.md",
|
||||||
|
"package.json",
|
||||||
|
"README.md",
|
||||||
|
"script",
|
||||||
|
"src/grammar.json",
|
||||||
|
"src/node-types.json",
|
||||||
|
"test",
|
||||||
|
"tsconfig.json",
|
||||||
|
],
|
||||||
|
sources: [
|
||||||
|
"src/parser.c",
|
||||||
|
"src/scanner.c",
|
||||||
|
],
|
||||||
|
resources: [
|
||||||
|
.copy("queries")
|
||||||
|
],
|
||||||
|
publicHeadersPath: "bindings/swift",
|
||||||
|
cSettings: [.headerSearchPath("src")])
|
||||||
|
]
|
||||||
|
)
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef TREE_SITTER_ELM_H_
|
||||||
|
#define TREE_SITTER_ELM_H_
|
||||||
|
|
||||||
|
typedef struct TSLanguage TSLanguage;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern TSLanguage *tree_sitter_elm();
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // TREE_SITTER_ELM_H_
|
||||||
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -1,882 +0,0 @@
|
|||||||
export interface Parser {
|
|
||||||
parse(input: string | Input, previousTree?: Tree, options?: {bufferSize?: number, includedRanges?: Range[]}): Tree;
|
|
||||||
getLanguage(): any;
|
|
||||||
setLanguage(language: any): void;
|
|
||||||
getLogger(): Logger;
|
|
||||||
setLogger(logFunc: Logger): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Point = {
|
|
||||||
row: number;
|
|
||||||
column: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Range = {
|
|
||||||
startIndex: number,
|
|
||||||
endIndex: number,
|
|
||||||
startPosition: Point,
|
|
||||||
endPosition: Point
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Edit = {
|
|
||||||
startIndex: number;
|
|
||||||
oldEndIndex: number;
|
|
||||||
newEndIndex: number;
|
|
||||||
startPosition: Point;
|
|
||||||
oldEndPosition: Point;
|
|
||||||
newEndPosition: Point;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Logger = (
|
|
||||||
message: string,
|
|
||||||
params: {[param: string]: string},
|
|
||||||
type: "parse" | "lex"
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
export interface Input {
|
|
||||||
seek(index: number): void;
|
|
||||||
read(): any;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SyntaxNodeBase {
|
|
||||||
tree: Tree;
|
|
||||||
type: string;
|
|
||||||
isNamed: boolean;
|
|
||||||
text: string;
|
|
||||||
startPosition: Point;
|
|
||||||
endPosition: Point;
|
|
||||||
startIndex: number;
|
|
||||||
endIndex: number;
|
|
||||||
parent: SyntaxNode | null;
|
|
||||||
children: Array<SyntaxNode>;
|
|
||||||
namedChildren: Array<SyntaxNode>;
|
|
||||||
childCount: number;
|
|
||||||
namedChildCount: number;
|
|
||||||
firstChild: SyntaxNode | null;
|
|
||||||
firstNamedChild: SyntaxNode | null;
|
|
||||||
lastChild: SyntaxNode | null;
|
|
||||||
lastNamedChild: SyntaxNode | null;
|
|
||||||
nextSibling: SyntaxNode | null;
|
|
||||||
nextNamedSibling: SyntaxNode | null;
|
|
||||||
previousSibling: SyntaxNode | null;
|
|
||||||
previousNamedSibling: SyntaxNode | null;
|
|
||||||
|
|
||||||
hasChanges(): boolean;
|
|
||||||
hasError(): boolean;
|
|
||||||
isMissing(): boolean;
|
|
||||||
toString(): string;
|
|
||||||
child(index: number): SyntaxNode | null;
|
|
||||||
namedChild(index: number): SyntaxNode | null;
|
|
||||||
firstChildForIndex(index: number): SyntaxNode | null;
|
|
||||||
firstNamedChildForIndex(index: number): SyntaxNode | null;
|
|
||||||
|
|
||||||
descendantForIndex(index: number): SyntaxNode;
|
|
||||||
descendantForIndex(startIndex: number, endIndex: number): SyntaxNode;
|
|
||||||
namedDescendantForIndex(index: number): SyntaxNode;
|
|
||||||
namedDescendantForIndex(startIndex: number, endIndex: number): SyntaxNode;
|
|
||||||
descendantForPosition(position: Point): SyntaxNode;
|
|
||||||
descendantForPosition(startPosition: Point, endPosition: Point): SyntaxNode;
|
|
||||||
namedDescendantForPosition(position: Point): SyntaxNode;
|
|
||||||
namedDescendantForPosition(startPosition: Point, endPosition: Point): SyntaxNode;
|
|
||||||
descendantsOfType<T extends TypeString>(types: T | readonly T[], startPosition?: Point, endPosition?: Point): NodeOfType<T>[];
|
|
||||||
|
|
||||||
closest<T extends SyntaxType>(types: T | readonly T[]): NamedNode<T> | null;
|
|
||||||
walk(): TreeCursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TreeCursor {
|
|
||||||
nodeType: string;
|
|
||||||
nodeText: string;
|
|
||||||
nodeIsNamed: boolean;
|
|
||||||
startPosition: Point;
|
|
||||||
endPosition: Point;
|
|
||||||
startIndex: number;
|
|
||||||
endIndex: number;
|
|
||||||
readonly currentNode: SyntaxNode
|
|
||||||
|
|
||||||
reset(node: SyntaxNode): void
|
|
||||||
gotoParent(): boolean;
|
|
||||||
gotoFirstChild(): boolean;
|
|
||||||
gotoFirstChildForIndex(index: number): boolean;
|
|
||||||
gotoNextSibling(): boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Tree {
|
|
||||||
readonly rootNode: SyntaxNode;
|
|
||||||
|
|
||||||
edit(delta: Edit): Tree;
|
|
||||||
walk(): TreeCursor;
|
|
||||||
getChangedRanges(other: Tree): Range[];
|
|
||||||
getEditedRange(other: Tree): Range;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface NamedNodeBase extends SyntaxNodeBase {
|
|
||||||
isNamed: true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** An unnamed node with the given type string. */
|
|
||||||
export interface UnnamedNode<T extends string = string> extends SyntaxNodeBase {
|
|
||||||
type: T;
|
|
||||||
isNamed: false;
|
|
||||||
}
|
|
||||||
|
|
||||||
type PickNamedType<Node, T extends string> = Node extends { type: T; isNamed: true } ? Node : never;
|
|
||||||
|
|
||||||
type PickType<Node, T extends string> = Node extends { type: T } ? Node : never;
|
|
||||||
|
|
||||||
/** A named node with the given `type` string. */
|
|
||||||
export type NamedNode<T extends SyntaxType = SyntaxType> = PickNamedType<SyntaxNode, T>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A node with the given `type` string.
|
|
||||||
*
|
|
||||||
* Note that this matches both named and unnamed nodes. Use `NamedNode<T>` to pick only named nodes.
|
|
||||||
*/
|
|
||||||
export type NodeOfType<T extends string> = PickType<SyntaxNode, T>;
|
|
||||||
|
|
||||||
interface TreeCursorOfType<S extends string, T extends SyntaxNodeBase> {
|
|
||||||
nodeType: S;
|
|
||||||
currentNode: T;
|
|
||||||
}
|
|
||||||
|
|
||||||
type TreeCursorRecord = { [K in TypeString]: TreeCursorOfType<K, NodeOfType<K>> };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A tree cursor whose `nodeType` correlates with `currentNode`.
|
|
||||||
*
|
|
||||||
* The typing becomes invalid once the underlying cursor is mutated.
|
|
||||||
*
|
|
||||||
* The intention is to cast a `TreeCursor` to `TypedTreeCursor` before
|
|
||||||
* switching on `nodeType`.
|
|
||||||
*
|
|
||||||
* For example:
|
|
||||||
* ```ts
|
|
||||||
* let cursor = root.walk();
|
|
||||||
* while (cursor.gotoNextSibling()) {
|
|
||||||
* const c = cursor as TypedTreeCursor;
|
|
||||||
* switch (c.nodeType) {
|
|
||||||
* case SyntaxType.Foo: {
|
|
||||||
* let node = c.currentNode; // Typed as FooNode.
|
|
||||||
* break;
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
export type TypedTreeCursor = TreeCursorRecord[keyof TreeCursorRecord];
|
|
||||||
|
|
||||||
export interface ErrorNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.ERROR;
|
|
||||||
hasError(): true;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const enum SyntaxType {
|
|
||||||
ERROR = "ERROR",
|
|
||||||
AnonymousFunctionExpr = "anonymous_function_expr",
|
|
||||||
AnythingPattern = "anything_pattern",
|
|
||||||
AsClause = "as_clause",
|
|
||||||
BinOpExpr = "bin_op_expr",
|
|
||||||
BlockComment = "block_comment",
|
|
||||||
CaseOfBranch = "case_of_branch",
|
|
||||||
CaseOfExpr = "case_of_expr",
|
|
||||||
CharConstantExpr = "char_constant_expr",
|
|
||||||
ConsPattern = "cons_pattern",
|
|
||||||
ExposedOperator = "exposed_operator",
|
|
||||||
ExposedType = "exposed_type",
|
|
||||||
ExposedUnionConstructors = "exposed_union_constructors",
|
|
||||||
ExposedValue = "exposed_value",
|
|
||||||
ExposingList = "exposing_list",
|
|
||||||
Field = "field",
|
|
||||||
FieldAccessExpr = "field_access_expr",
|
|
||||||
FieldAccessorFunctionExpr = "field_accessor_function_expr",
|
|
||||||
FieldType = "field_type",
|
|
||||||
File = "file",
|
|
||||||
FunctionCallExpr = "function_call_expr",
|
|
||||||
FunctionDeclarationLeft = "function_declaration_left",
|
|
||||||
GlslCodeExpr = "glsl_code_expr",
|
|
||||||
IfElseExpr = "if_else_expr",
|
|
||||||
ImportClause = "import_clause",
|
|
||||||
InfixDeclaration = "infix_declaration",
|
|
||||||
LetInExpr = "let_in_expr",
|
|
||||||
ListExpr = "list_expr",
|
|
||||||
ListPattern = "list_pattern",
|
|
||||||
LowerPattern = "lower_pattern",
|
|
||||||
LowerTypeName = "lower_type_name",
|
|
||||||
ModuleDeclaration = "module_declaration",
|
|
||||||
NegateExpr = "negate_expr",
|
|
||||||
NullaryConstructorArgumentPattern = "nullary_constructor_argument_pattern",
|
|
||||||
NumberConstantExpr = "number_constant_expr",
|
|
||||||
Operator = "operator",
|
|
||||||
OperatorAsFunctionExpr = "operator_as_function_expr",
|
|
||||||
OperatorIdentifier = "operator_identifier",
|
|
||||||
ParenthesizedExpr = "parenthesized_expr",
|
|
||||||
Pattern = "pattern",
|
|
||||||
PortAnnotation = "port_annotation",
|
|
||||||
RecordBaseIdentifier = "record_base_identifier",
|
|
||||||
RecordExpr = "record_expr",
|
|
||||||
RecordPattern = "record_pattern",
|
|
||||||
RecordType = "record_type",
|
|
||||||
StringConstantExpr = "string_constant_expr",
|
|
||||||
TupleExpr = "tuple_expr",
|
|
||||||
TuplePattern = "tuple_pattern",
|
|
||||||
TupleType = "tuple_type",
|
|
||||||
TypeAliasDeclaration = "type_alias_declaration",
|
|
||||||
TypeAnnotation = "type_annotation",
|
|
||||||
TypeDeclaration = "type_declaration",
|
|
||||||
TypeExpression = "type_expression",
|
|
||||||
TypeRef = "type_ref",
|
|
||||||
TypeVariable = "type_variable",
|
|
||||||
UnionPattern = "union_pattern",
|
|
||||||
UnionVariant = "union_variant",
|
|
||||||
UnitExpr = "unit_expr",
|
|
||||||
UpperCaseQid = "upper_case_qid",
|
|
||||||
ValueDeclaration = "value_declaration",
|
|
||||||
ValueExpr = "value_expr",
|
|
||||||
ValueQid = "value_qid",
|
|
||||||
Alias = "alias",
|
|
||||||
Arrow = "arrow",
|
|
||||||
As = "as",
|
|
||||||
Backslash = "backslash",
|
|
||||||
Case = "case",
|
|
||||||
CloseChar = "close_char",
|
|
||||||
CloseQuote = "close_quote",
|
|
||||||
Colon = "colon",
|
|
||||||
Dot = "dot",
|
|
||||||
DoubleDot = "double_dot",
|
|
||||||
Effect = "effect",
|
|
||||||
Eq = "eq",
|
|
||||||
Exposing = "exposing",
|
|
||||||
GlslContent = "glsl_content",
|
|
||||||
Import = "import",
|
|
||||||
Infix = "infix",
|
|
||||||
InvalidStringEscape = "invalid_string_escape",
|
|
||||||
LineComment = "line_comment",
|
|
||||||
LowerCaseIdentifier = "lower_case_identifier",
|
|
||||||
Module = "module",
|
|
||||||
NumberLiteral = "number_literal",
|
|
||||||
Of = "of",
|
|
||||||
OpenChar = "open_char",
|
|
||||||
OpenQuote = "open_quote",
|
|
||||||
Port = "port",
|
|
||||||
RegularStringPart = "regular_string_part",
|
|
||||||
StringEscape = "string_escape",
|
|
||||||
Type = "type",
|
|
||||||
Underscore = "underscore",
|
|
||||||
UpperCaseIdentifier = "upper_case_identifier",
|
|
||||||
Where = "where",
|
|
||||||
}
|
|
||||||
|
|
||||||
export type UnnamedType =
|
|
||||||
| "&&"
|
|
||||||
| "("
|
|
||||||
| ")"
|
|
||||||
| "*"
|
|
||||||
| "+"
|
|
||||||
| "++"
|
|
||||||
| ","
|
|
||||||
| "-"
|
|
||||||
| "-}"
|
|
||||||
| "/"
|
|
||||||
| "//"
|
|
||||||
| "/="
|
|
||||||
| "::"
|
|
||||||
| "<"
|
|
||||||
| "</>"
|
|
||||||
| "<<"
|
|
||||||
| "<="
|
|
||||||
| "<?>"
|
|
||||||
| "<|"
|
|
||||||
| "=="
|
|
||||||
| ">"
|
|
||||||
| ">="
|
|
||||||
| ">>"
|
|
||||||
| "["
|
|
||||||
| "]"
|
|
||||||
| "^"
|
|
||||||
| "else"
|
|
||||||
| "if"
|
|
||||||
| "in"
|
|
||||||
| "let"
|
|
||||||
| "then"
|
|
||||||
| "{"
|
|
||||||
| "{-"
|
|
||||||
| "|"
|
|
||||||
| "|."
|
|
||||||
| "|="
|
|
||||||
| "|>"
|
|
||||||
| "||"
|
|
||||||
| "}"
|
|
||||||
;
|
|
||||||
|
|
||||||
export type TypeString = SyntaxType | UnnamedType;
|
|
||||||
|
|
||||||
export type SyntaxNode =
|
|
||||||
| AnonymousFunctionExprNode
|
|
||||||
| AnythingPatternNode
|
|
||||||
| AsClauseNode
|
|
||||||
| BinOpExprNode
|
|
||||||
| BlockCommentNode
|
|
||||||
| CaseOfBranchNode
|
|
||||||
| CaseOfExprNode
|
|
||||||
| CharConstantExprNode
|
|
||||||
| ConsPatternNode
|
|
||||||
| ExposedOperatorNode
|
|
||||||
| ExposedTypeNode
|
|
||||||
| ExposedUnionConstructorsNode
|
|
||||||
| ExposedValueNode
|
|
||||||
| ExposingListNode
|
|
||||||
| FieldNode
|
|
||||||
| FieldAccessExprNode
|
|
||||||
| FieldAccessorFunctionExprNode
|
|
||||||
| FieldTypeNode
|
|
||||||
| FileNode
|
|
||||||
| FunctionCallExprNode
|
|
||||||
| FunctionDeclarationLeftNode
|
|
||||||
| GlslCodeExprNode
|
|
||||||
| IfElseExprNode
|
|
||||||
| ImportClauseNode
|
|
||||||
| InfixDeclarationNode
|
|
||||||
| LetInExprNode
|
|
||||||
| ListExprNode
|
|
||||||
| ListPatternNode
|
|
||||||
| LowerPatternNode
|
|
||||||
| LowerTypeNameNode
|
|
||||||
| ModuleDeclarationNode
|
|
||||||
| NegateExprNode
|
|
||||||
| NullaryConstructorArgumentPatternNode
|
|
||||||
| NumberConstantExprNode
|
|
||||||
| OperatorNode
|
|
||||||
| OperatorAsFunctionExprNode
|
|
||||||
| OperatorIdentifierNode
|
|
||||||
| ParenthesizedExprNode
|
|
||||||
| PatternNode
|
|
||||||
| PortAnnotationNode
|
|
||||||
| RecordBaseIdentifierNode
|
|
||||||
| RecordExprNode
|
|
||||||
| RecordPatternNode
|
|
||||||
| RecordTypeNode
|
|
||||||
| StringConstantExprNode
|
|
||||||
| TupleExprNode
|
|
||||||
| TuplePatternNode
|
|
||||||
| TupleTypeNode
|
|
||||||
| TypeAliasDeclarationNode
|
|
||||||
| TypeAnnotationNode
|
|
||||||
| TypeDeclarationNode
|
|
||||||
| TypeExpressionNode
|
|
||||||
| TypeRefNode
|
|
||||||
| TypeVariableNode
|
|
||||||
| UnionPatternNode
|
|
||||||
| UnionVariantNode
|
|
||||||
| UnitExprNode
|
|
||||||
| UpperCaseQidNode
|
|
||||||
| ValueDeclarationNode
|
|
||||||
| ValueExprNode
|
|
||||||
| ValueQidNode
|
|
||||||
| UnnamedNode<"&&">
|
|
||||||
| UnnamedNode<"(">
|
|
||||||
| UnnamedNode<")">
|
|
||||||
| UnnamedNode<"*">
|
|
||||||
| UnnamedNode<"+">
|
|
||||||
| UnnamedNode<"++">
|
|
||||||
| UnnamedNode<",">
|
|
||||||
| UnnamedNode<"-">
|
|
||||||
| UnnamedNode<"-}">
|
|
||||||
| UnnamedNode<"/">
|
|
||||||
| UnnamedNode<"//">
|
|
||||||
| UnnamedNode<"/=">
|
|
||||||
| UnnamedNode<"::">
|
|
||||||
| UnnamedNode<"<">
|
|
||||||
| UnnamedNode<"</>">
|
|
||||||
| UnnamedNode<"<<">
|
|
||||||
| UnnamedNode<"<=">
|
|
||||||
| UnnamedNode<"<?>">
|
|
||||||
| UnnamedNode<"<|">
|
|
||||||
| UnnamedNode<"==">
|
|
||||||
| UnnamedNode<">">
|
|
||||||
| UnnamedNode<">=">
|
|
||||||
| UnnamedNode<">>">
|
|
||||||
| UnnamedNode<"[">
|
|
||||||
| UnnamedNode<"]">
|
|
||||||
| UnnamedNode<"^">
|
|
||||||
| AliasNode
|
|
||||||
| ArrowNode
|
|
||||||
| AsNode
|
|
||||||
| BackslashNode
|
|
||||||
| CaseNode
|
|
||||||
| CloseCharNode
|
|
||||||
| CloseQuoteNode
|
|
||||||
| ColonNode
|
|
||||||
| DotNode
|
|
||||||
| DoubleDotNode
|
|
||||||
| EffectNode
|
|
||||||
| UnnamedNode<"else">
|
|
||||||
| EqNode
|
|
||||||
| ExposingNode
|
|
||||||
| GlslContentNode
|
|
||||||
| UnnamedNode<"if">
|
|
||||||
| ImportNode
|
|
||||||
| UnnamedNode<"in">
|
|
||||||
| InfixNode
|
|
||||||
| InvalidStringEscapeNode
|
|
||||||
| UnnamedNode<"let">
|
|
||||||
| LineCommentNode
|
|
||||||
| LowerCaseIdentifierNode
|
|
||||||
| ModuleNode
|
|
||||||
| NumberLiteralNode
|
|
||||||
| OfNode
|
|
||||||
| OpenCharNode
|
|
||||||
| OpenQuoteNode
|
|
||||||
| PortNode
|
|
||||||
| RegularStringPartNode
|
|
||||||
| StringEscapeNode
|
|
||||||
| UnnamedNode<"then">
|
|
||||||
| TypeNode
|
|
||||||
| UnderscoreNode
|
|
||||||
| UpperCaseIdentifierNode
|
|
||||||
| WhereNode
|
|
||||||
| UnnamedNode<"{">
|
|
||||||
| UnnamedNode<"{-">
|
|
||||||
| UnnamedNode<"|">
|
|
||||||
| UnnamedNode<"|.">
|
|
||||||
| UnnamedNode<"|=">
|
|
||||||
| UnnamedNode<"|>">
|
|
||||||
| UnnamedNode<"||">
|
|
||||||
| UnnamedNode<"}">
|
|
||||||
| ErrorNode
|
|
||||||
;
|
|
||||||
|
|
||||||
export interface AnonymousFunctionExprNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.AnonymousFunctionExpr;
|
|
||||||
exprNode: AnonymousFunctionExprNode | BinOpExprNode | CaseOfExprNode | CharConstantExprNode | FieldAccessExprNode | FieldAccessorFunctionExprNode | FunctionCallExprNode | GlslCodeExprNode | IfElseExprNode | LetInExprNode | ListExprNode | NegateExprNode | NumberConstantExprNode | OperatorAsFunctionExprNode | ParenthesizedExprNode | RecordExprNode | StringConstantExprNode | TupleExprNode | UnitExprNode | ValueExprNode;
|
|
||||||
paramNodes: PatternNode[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AnythingPatternNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.AnythingPattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AsClauseNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.AsClause;
|
|
||||||
nameNode: UpperCaseIdentifierNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BinOpExprNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.BinOpExpr;
|
|
||||||
partNodes: (AnonymousFunctionExprNode | CaseOfExprNode | CharConstantExprNode | FieldAccessExprNode | FieldAccessorFunctionExprNode | FunctionCallExprNode | GlslCodeExprNode | IfElseExprNode | LetInExprNode | ListExprNode | NegateExprNode | NumberConstantExprNode | OperatorNode | OperatorAsFunctionExprNode | ParenthesizedExprNode | RecordExprNode | StringConstantExprNode | TupleExprNode | UnitExprNode | ValueExprNode)[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BlockCommentNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.BlockComment;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CaseOfBranchNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.CaseOfBranch;
|
|
||||||
exprNode: AnonymousFunctionExprNode | BinOpExprNode | CaseOfExprNode | CharConstantExprNode | FieldAccessExprNode | FieldAccessorFunctionExprNode | FunctionCallExprNode | GlslCodeExprNode | IfElseExprNode | LetInExprNode | ListExprNode | NegateExprNode | NumberConstantExprNode | OperatorAsFunctionExprNode | ParenthesizedExprNode | RecordExprNode | StringConstantExprNode | TupleExprNode | UnitExprNode | ValueExprNode;
|
|
||||||
patternNode: PatternNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CaseOfExprNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.CaseOfExpr;
|
|
||||||
branchNodes: CaseOfBranchNode[];
|
|
||||||
exprNode: AnonymousFunctionExprNode | BinOpExprNode | CaseOfExprNode | CharConstantExprNode | FieldAccessExprNode | FieldAccessorFunctionExprNode | FunctionCallExprNode | GlslCodeExprNode | IfElseExprNode | LetInExprNode | ListExprNode | NegateExprNode | NumberConstantExprNode | OperatorAsFunctionExprNode | ParenthesizedExprNode | RecordExprNode | StringConstantExprNode | TupleExprNode | UnitExprNode | ValueExprNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CharConstantExprNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.CharConstantExpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ConsPatternNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.ConsPattern;
|
|
||||||
partNodes: (UnnamedNode<"("> | UnnamedNode<")"> | AnythingPatternNode | CharConstantExprNode | ConsPatternNode | ListPatternNode | LowerPatternNode | NumberConstantExprNode | PatternNode | RecordPatternNode | StringConstantExprNode | TuplePatternNode | UnionPatternNode | UnitExprNode)[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ExposedOperatorNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.ExposedOperator;
|
|
||||||
operatorNode: OperatorIdentifierNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ExposedTypeNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.ExposedType;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ExposedUnionConstructorsNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.ExposedUnionConstructors;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ExposedValueNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.ExposedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ExposingListNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.ExposingList;
|
|
||||||
doubleDotNode?: DoubleDotNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FieldNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.Field;
|
|
||||||
expressionNode: AnonymousFunctionExprNode | BinOpExprNode | CaseOfExprNode | CharConstantExprNode | FieldAccessExprNode | FieldAccessorFunctionExprNode | FunctionCallExprNode | GlslCodeExprNode | IfElseExprNode | LetInExprNode | ListExprNode | NegateExprNode | NumberConstantExprNode | OperatorAsFunctionExprNode | ParenthesizedExprNode | RecordExprNode | StringConstantExprNode | TupleExprNode | UnitExprNode | ValueExprNode;
|
|
||||||
nameNode: LowerCaseIdentifierNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FieldAccessExprNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.FieldAccessExpr;
|
|
||||||
targetNode: FieldAccessExprNode | ParenthesizedExprNode | RecordExprNode | ValueExprNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FieldAccessorFunctionExprNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.FieldAccessorFunctionExpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FieldTypeNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.FieldType;
|
|
||||||
nameNode: LowerCaseIdentifierNode;
|
|
||||||
typeExpressionNode: TypeExpressionNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FileNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.File;
|
|
||||||
moduleDeclarationNode?: ModuleDeclarationNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FunctionCallExprNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.FunctionCallExpr;
|
|
||||||
argNodes: (AnonymousFunctionExprNode | CaseOfExprNode | CharConstantExprNode | FieldAccessExprNode | FieldAccessorFunctionExprNode | GlslCodeExprNode | IfElseExprNode | LetInExprNode | ListExprNode | NegateExprNode | NumberConstantExprNode | OperatorAsFunctionExprNode | ParenthesizedExprNode | RecordExprNode | StringConstantExprNode | TupleExprNode | UnitExprNode | ValueExprNode)[];
|
|
||||||
targetNode: FieldAccessExprNode | FieldAccessorFunctionExprNode | OperatorAsFunctionExprNode | ParenthesizedExprNode | ValueExprNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FunctionDeclarationLeftNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.FunctionDeclarationLeft;
|
|
||||||
patternNodes: (UnnamedNode<"("> | UnnamedNode<")"> | AnythingPatternNode | CharConstantExprNode | ListPatternNode | LowerPatternNode | NumberConstantExprNode | PatternNode | RecordPatternNode | StringConstantExprNode | TuplePatternNode | UnitExprNode)[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GlslCodeExprNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.GlslCodeExpr;
|
|
||||||
contentNode: GlslContentNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IfElseExprNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.IfElseExpr;
|
|
||||||
exprListNodes: (AnonymousFunctionExprNode | BinOpExprNode | CaseOfExprNode | CharConstantExprNode | FieldAccessExprNode | FieldAccessorFunctionExprNode | FunctionCallExprNode | GlslCodeExprNode | IfElseExprNode | LetInExprNode | ListExprNode | NegateExprNode | NumberConstantExprNode | OperatorAsFunctionExprNode | ParenthesizedExprNode | RecordExprNode | StringConstantExprNode | TupleExprNode | UnitExprNode | ValueExprNode)[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ImportClauseNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.ImportClause;
|
|
||||||
asClauseNode?: AsClauseNode;
|
|
||||||
exposingNode?: ExposingListNode;
|
|
||||||
moduleNameNode: UpperCaseQidNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InfixDeclarationNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.InfixDeclaration;
|
|
||||||
associativityNode: LowerCaseIdentifierNode;
|
|
||||||
operatorNode: OperatorIdentifierNode;
|
|
||||||
precedenceNode: NumberLiteralNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LetInExprNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.LetInExpr;
|
|
||||||
bodyNode: AnonymousFunctionExprNode | BinOpExprNode | CaseOfExprNode | CharConstantExprNode | FieldAccessExprNode | FieldAccessorFunctionExprNode | FunctionCallExprNode | GlslCodeExprNode | IfElseExprNode | LetInExprNode | ListExprNode | NegateExprNode | NumberConstantExprNode | OperatorAsFunctionExprNode | ParenthesizedExprNode | RecordExprNode | StringConstantExprNode | TupleExprNode | UnitExprNode | ValueExprNode;
|
|
||||||
valueDeclarationNodes: ValueDeclarationNode[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ListExprNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.ListExpr;
|
|
||||||
exprListNodes: (AnonymousFunctionExprNode | BinOpExprNode | CaseOfExprNode | CharConstantExprNode | FieldAccessExprNode | FieldAccessorFunctionExprNode | FunctionCallExprNode | GlslCodeExprNode | IfElseExprNode | LetInExprNode | ListExprNode | NegateExprNode | NumberConstantExprNode | OperatorAsFunctionExprNode | ParenthesizedExprNode | RecordExprNode | StringConstantExprNode | TupleExprNode | UnitExprNode | ValueExprNode)[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ListPatternNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.ListPattern;
|
|
||||||
partNodes: PatternNode[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LowerPatternNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.LowerPattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LowerTypeNameNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.LowerTypeName;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ModuleDeclarationNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.ModuleDeclaration;
|
|
||||||
exposingNode: ExposingListNode;
|
|
||||||
nameNode: UpperCaseQidNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NegateExprNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.NegateExpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NullaryConstructorArgumentPatternNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.NullaryConstructorArgumentPattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NumberConstantExprNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.NumberConstantExpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface OperatorNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.Operator;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface OperatorAsFunctionExprNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.OperatorAsFunctionExpr;
|
|
||||||
operatorNode: OperatorIdentifierNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface OperatorIdentifierNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.OperatorIdentifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ParenthesizedExprNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.ParenthesizedExpr;
|
|
||||||
expressionNode: AnonymousFunctionExprNode | BinOpExprNode | CaseOfExprNode | CharConstantExprNode | FieldAccessExprNode | FieldAccessorFunctionExprNode | FunctionCallExprNode | GlslCodeExprNode | IfElseExprNode | LetInExprNode | ListExprNode | NegateExprNode | NumberConstantExprNode | OperatorAsFunctionExprNode | ParenthesizedExprNode | RecordExprNode | StringConstantExprNode | TupleExprNode | UnitExprNode | ValueExprNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PatternNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.Pattern;
|
|
||||||
childNode: AnythingPatternNode | CharConstantExprNode | ConsPatternNode | ListPatternNode | LowerPatternNode | NumberConstantExprNode | PatternNode | RecordPatternNode | StringConstantExprNode | TuplePatternNode | UnionPatternNode | UnitExprNode;
|
|
||||||
patternAsNode?: LowerPatternNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PortAnnotationNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.PortAnnotation;
|
|
||||||
nameNode: LowerCaseIdentifierNode;
|
|
||||||
typeExpressionNode: TypeExpressionNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RecordBaseIdentifierNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.RecordBaseIdentifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RecordExprNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.RecordExpr;
|
|
||||||
baseRecordNode?: RecordBaseIdentifierNode;
|
|
||||||
fieldNodes: FieldNode[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RecordPatternNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.RecordPattern;
|
|
||||||
patternListNodes: LowerPatternNode[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RecordTypeNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.RecordType;
|
|
||||||
baseRecordNode?: RecordBaseIdentifierNode;
|
|
||||||
fieldTypeNodes: FieldTypeNode[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StringConstantExprNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.StringConstantExpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TupleExprNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.TupleExpr;
|
|
||||||
exprNodes: (AnonymousFunctionExprNode | BinOpExprNode | CaseOfExprNode | CharConstantExprNode | FieldAccessExprNode | FieldAccessorFunctionExprNode | FunctionCallExprNode | GlslCodeExprNode | IfElseExprNode | LetInExprNode | ListExprNode | NegateExprNode | NumberConstantExprNode | OperatorAsFunctionExprNode | ParenthesizedExprNode | RecordExprNode | StringConstantExprNode | TupleExprNode | UnitExprNode | ValueExprNode)[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TuplePatternNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.TuplePattern;
|
|
||||||
patternNodes: PatternNode[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TupleTypeNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.TupleType;
|
|
||||||
typeExpressionNodes: TypeExpressionNode[];
|
|
||||||
unitExprNode?: UnitExprNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TypeAliasDeclarationNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.TypeAliasDeclaration;
|
|
||||||
nameNode: UpperCaseIdentifierNode;
|
|
||||||
typeExpressionNode: TypeExpressionNode;
|
|
||||||
typeVariableNodes: LowerTypeNameNode[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TypeAnnotationNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.TypeAnnotation;
|
|
||||||
nameNode: LowerCaseIdentifierNode;
|
|
||||||
typeExpressionNode: TypeExpressionNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TypeDeclarationNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.TypeDeclaration;
|
|
||||||
nameNode: UpperCaseIdentifierNode;
|
|
||||||
typeNameNodes: LowerTypeNameNode[];
|
|
||||||
unionVariantNodes: UnionVariantNode[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TypeExpressionNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.TypeExpression;
|
|
||||||
partNodes: (RecordTypeNode | TupleTypeNode | TypeExpressionNode | TypeRefNode | TypeVariableNode)[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TypeRefNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.TypeRef;
|
|
||||||
partNodes: (RecordTypeNode | TupleTypeNode | TypeExpressionNode | TypeRefNode | TypeVariableNode)[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TypeVariableNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.TypeVariable;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UnionPatternNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.UnionPattern;
|
|
||||||
argPatternNodes: (UnnamedNode<"("> | UnnamedNode<")"> | AnythingPatternNode | CharConstantExprNode | ListPatternNode | LowerPatternNode | NullaryConstructorArgumentPatternNode | NumberConstantExprNode | PatternNode | RecordPatternNode | StringConstantExprNode | TuplePatternNode | UnitExprNode)[];
|
|
||||||
constructorNode: UpperCaseQidNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UnionVariantNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.UnionVariant;
|
|
||||||
nameNode: UpperCaseIdentifierNode;
|
|
||||||
partNodes: (RecordTypeNode | TupleTypeNode | TypeExpressionNode | TypeRefNode | TypeVariableNode)[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UnitExprNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.UnitExpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UpperCaseQidNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.UpperCaseQid;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ValueDeclarationNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.ValueDeclaration;
|
|
||||||
bodyNode: AnonymousFunctionExprNode | BinOpExprNode | CaseOfExprNode | CharConstantExprNode | FieldAccessExprNode | FieldAccessorFunctionExprNode | FunctionCallExprNode | GlslCodeExprNode | IfElseExprNode | LetInExprNode | ListExprNode | NegateExprNode | NumberConstantExprNode | OperatorAsFunctionExprNode | ParenthesizedExprNode | RecordExprNode | StringConstantExprNode | TupleExprNode | UnitExprNode | ValueExprNode;
|
|
||||||
functionDeclarationLeftNode?: FunctionDeclarationLeftNode;
|
|
||||||
patternNode?: PatternNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ValueExprNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.ValueExpr;
|
|
||||||
nameNode: UpperCaseQidNode | ValueQidNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ValueQidNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.ValueQid;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AliasNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.Alias;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ArrowNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.Arrow;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AsNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.As;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BackslashNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.Backslash;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CaseNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.Case;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CloseCharNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.CloseChar;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CloseQuoteNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.CloseQuote;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ColonNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.Colon;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DotNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.Dot;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DoubleDotNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.DoubleDot;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EffectNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.Effect;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EqNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.Eq;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ExposingNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.Exposing;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GlslContentNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.GlslContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ImportNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.Import;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InfixNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.Infix;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InvalidStringEscapeNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.InvalidStringEscape;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LineCommentNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.LineComment;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LowerCaseIdentifierNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.LowerCaseIdentifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ModuleNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.Module;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NumberLiteralNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.NumberLiteral;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface OfNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.Of;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface OpenCharNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.OpenChar;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface OpenQuoteNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.OpenQuote;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PortNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.Port;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RegularStringPartNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.RegularStringPart;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StringEscapeNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.StringEscape;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TypeNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.Type;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UnderscoreNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.Underscore;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UpperCaseIdentifierNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.UpperCaseIdentifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WhereNode extends NamedNodeBase {
|
|
||||||
type: SyntaxType.Where;
|
|
||||||
}
|
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,496 @@
|
|||||||
|
#include "tree_sitter/parser.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||||
|
|
||||||
|
#define VEC_RESIZE(vec, _cap) \
|
||||||
|
void *tmp = realloc((vec).data, (_cap) * sizeof((vec).data[0])); \
|
||||||
|
assert(tmp != NULL); \
|
||||||
|
(vec).data = tmp; \
|
||||||
|
assert((vec).data != NULL); \
|
||||||
|
(vec).cap = (_cap);
|
||||||
|
|
||||||
|
#define VEC_GROW(vec, _cap) \
|
||||||
|
if ((vec).cap < (_cap)) { \
|
||||||
|
VEC_RESIZE((vec), (_cap)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define VEC_PUSH(vec, el) \
|
||||||
|
if ((vec).cap == (vec).len) { \
|
||||||
|
VEC_RESIZE((vec), MAX(16, (vec).len * 2)); \
|
||||||
|
} \
|
||||||
|
(vec).data[(vec).len++] = (el);
|
||||||
|
|
||||||
|
#define VEC_POP(vec) (vec).len--;
|
||||||
|
|
||||||
|
#define VEC_BACK(vec) ((vec).data[(vec).len - 1])
|
||||||
|
|
||||||
|
#define VEC_FREE(vec) \
|
||||||
|
{ \
|
||||||
|
if ((vec).data != NULL) \
|
||||||
|
free((vec).data); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define VEC_CLEAR(vec) (vec).len = 0;
|
||||||
|
|
||||||
|
#define VEC_REVERSE(vec) \
|
||||||
|
do { \
|
||||||
|
if ((vec).len > 1) { \
|
||||||
|
for (size_t i = 0, j = (vec).len - 1; i < j; i++, j--) { \
|
||||||
|
uint8_t tmp = (vec).data[i]; \
|
||||||
|
(vec).data[i] = (vec).data[j]; \
|
||||||
|
(vec).data[j] = tmp; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
enum TokenType {
|
||||||
|
VIRTUAL_END_DECL,
|
||||||
|
VIRTUAL_OPEN_SECTION,
|
||||||
|
VIRTUAL_END_SECTION,
|
||||||
|
MINUS_WITHOUT_TRAILING_WHITESPACE,
|
||||||
|
GLSL_CONTENT,
|
||||||
|
BLOCK_COMMENT_CONTENT,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t len;
|
||||||
|
uint32_t cap;
|
||||||
|
uint8_t *data;
|
||||||
|
} vec;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t indent_length;
|
||||||
|
vec indents;
|
||||||
|
vec runback;
|
||||||
|
} Scanner;
|
||||||
|
|
||||||
|
static inline void advance(TSLexer *lexer) { lexer->advance(lexer, false); }
|
||||||
|
|
||||||
|
static inline void skip(TSLexer *lexer) { lexer->advance(lexer, true); }
|
||||||
|
|
||||||
|
// > You can detect error recovery in the external scanner by the fact that
|
||||||
|
// > _all_ tokens are considered valid at once.
|
||||||
|
// https://github.com/tree-sitter/tree-sitter/pull/1783#issuecomment-1181011411
|
||||||
|
static bool in_error_recovery(const bool *valid_symbols) {
|
||||||
|
return (valid_symbols[VIRTUAL_END_DECL] &&
|
||||||
|
valid_symbols[VIRTUAL_OPEN_SECTION] &&
|
||||||
|
valid_symbols[VIRTUAL_END_SECTION] &&
|
||||||
|
valid_symbols[MINUS_WITHOUT_TRAILING_WHITESPACE] &&
|
||||||
|
valid_symbols[GLSL_CONTENT] &&
|
||||||
|
valid_symbols[BLOCK_COMMENT_CONTENT]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_elm_space(TSLexer *lexer) {
|
||||||
|
return lexer->lookahead == ' ' || lexer->lookahead == '\r' ||
|
||||||
|
lexer->lookahead == '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
static int checkForIn(TSLexer *lexer, const bool *valid_symbols) {
|
||||||
|
// Are we at the end of a let (in) declaration
|
||||||
|
if (valid_symbols[VIRTUAL_END_SECTION] && lexer->lookahead == 'i') {
|
||||||
|
skip(lexer);
|
||||||
|
|
||||||
|
if (lexer->lookahead == 'n') {
|
||||||
|
skip(lexer);
|
||||||
|
if (is_elm_space(lexer) || lexer->eof(lexer)) {
|
||||||
|
return 2; // Success
|
||||||
|
}
|
||||||
|
return 1; // Partial
|
||||||
|
}
|
||||||
|
return 1; // Partial
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool scan_block_comment(TSLexer *lexer) {
|
||||||
|
lexer->mark_end(lexer);
|
||||||
|
if (lexer->lookahead != '{') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
advance(lexer);
|
||||||
|
if (lexer->lookahead != '-') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
advance(lexer);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
switch (lexer->lookahead) {
|
||||||
|
case '{':
|
||||||
|
scan_block_comment(lexer);
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
advance(lexer);
|
||||||
|
if (lexer->lookahead == '}') {
|
||||||
|
advance(lexer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '\0':
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
advance(lexer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void advance_to_line_end(TSLexer *lexer) {
|
||||||
|
while (true) {
|
||||||
|
if (lexer->lookahead == '\n' || lexer->eof(lexer)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
advance(lexer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool scan(Scanner *scanner, TSLexer *lexer, const bool *valid_symbols) {
|
||||||
|
if (in_error_recovery(valid_symbols)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First handle eventual runback tokens, we saved on a previous scan op
|
||||||
|
if (scanner->runback.len > 0 && VEC_BACK(scanner->runback) == 0 &&
|
||||||
|
valid_symbols[VIRTUAL_END_DECL]) {
|
||||||
|
VEC_POP(scanner->runback);
|
||||||
|
lexer->result_symbol = VIRTUAL_END_DECL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (scanner->runback.len > 0 && VEC_BACK(scanner->runback) == 1 &&
|
||||||
|
valid_symbols[VIRTUAL_END_SECTION]) {
|
||||||
|
VEC_POP(scanner->runback);
|
||||||
|
lexer->result_symbol = VIRTUAL_END_SECTION;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
VEC_CLEAR(scanner->runback);
|
||||||
|
|
||||||
|
// Check if we have newlines and how much indentation
|
||||||
|
bool has_newline = false;
|
||||||
|
bool found_in = false;
|
||||||
|
bool can_call_mark_end = true;
|
||||||
|
lexer->mark_end(lexer);
|
||||||
|
while (true) {
|
||||||
|
if (lexer->lookahead == ' ' || lexer->lookahead == '\r') {
|
||||||
|
skip(lexer);
|
||||||
|
} else if (lexer->lookahead == '\n') {
|
||||||
|
skip(lexer);
|
||||||
|
has_newline = true;
|
||||||
|
while (true) {
|
||||||
|
if (lexer->lookahead == ' ') {
|
||||||
|
skip(lexer);
|
||||||
|
} else {
|
||||||
|
scanner->indent_length = lexer->get_column(lexer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (!valid_symbols[BLOCK_COMMENT_CONTENT] &&
|
||||||
|
lexer->lookahead == '-') {
|
||||||
|
advance(lexer);
|
||||||
|
int32_t lookahead = lexer->lookahead;
|
||||||
|
|
||||||
|
// Handle minus without a whitespace for negate
|
||||||
|
if (valid_symbols[MINUS_WITHOUT_TRAILING_WHITESPACE] &&
|
||||||
|
((lookahead >= 'a' && lookahead <= 'z') ||
|
||||||
|
(lookahead >= 'A' && lookahead <= 'Z') || lookahead == '(')) {
|
||||||
|
if (can_call_mark_end) {
|
||||||
|
lexer->result_symbol = MINUS_WITHOUT_TRAILING_WHITESPACE;
|
||||||
|
lexer->mark_end(lexer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Scan past line comments. As far as the special token
|
||||||
|
// types we're scanning for here are concerned line comments
|
||||||
|
// are like whitespace. There is nothing useful to be
|
||||||
|
// learned from, say, their indentation. So we advance past
|
||||||
|
// them here.
|
||||||
|
//
|
||||||
|
// The one thing we need to keep in mind is that we should
|
||||||
|
// not call `lexer->mark_end(lexer)` after this point, or
|
||||||
|
// the comment will be lost.
|
||||||
|
if (lookahead == '-' && has_newline) {
|
||||||
|
can_call_mark_end = false;
|
||||||
|
advance(lexer);
|
||||||
|
advance_to_line_end(lexer);
|
||||||
|
} else if (valid_symbols[BLOCK_COMMENT_CONTENT] &&
|
||||||
|
lexer->lookahead == '}') {
|
||||||
|
lexer->result_symbol = BLOCK_COMMENT_CONTENT;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (lexer->eof(lexer)) {
|
||||||
|
if (valid_symbols[VIRTUAL_END_SECTION]) {
|
||||||
|
lexer->result_symbol = VIRTUAL_END_SECTION;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (valid_symbols[VIRTUAL_END_DECL]) {
|
||||||
|
lexer->result_symbol = VIRTUAL_END_DECL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkForIn(lexer, valid_symbols) == 2) {
|
||||||
|
if (has_newline) {
|
||||||
|
found_in = true;
|
||||||
|
} else {
|
||||||
|
lexer->result_symbol = VIRTUAL_END_SECTION;
|
||||||
|
VEC_POP(scanner->indents);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open section if the grammar lets us but only push to indent stack if
|
||||||
|
// we go further down in the stack
|
||||||
|
if (valid_symbols[VIRTUAL_OPEN_SECTION] && !lexer->eof(lexer)) {
|
||||||
|
VEC_PUSH(scanner->indents, lexer->get_column(lexer));
|
||||||
|
lexer->result_symbol = VIRTUAL_OPEN_SECTION;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (valid_symbols[BLOCK_COMMENT_CONTENT]) {
|
||||||
|
if (!can_call_mark_end) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
lexer->mark_end(lexer);
|
||||||
|
while (true) {
|
||||||
|
if (lexer->lookahead == '\0') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (lexer->lookahead != '{' && lexer->lookahead != '-') {
|
||||||
|
advance(lexer);
|
||||||
|
} else if (lexer->lookahead == '-') {
|
||||||
|
lexer->mark_end(lexer);
|
||||||
|
advance(lexer);
|
||||||
|
if (lexer->lookahead == '}') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (scan_block_comment(lexer)) {
|
||||||
|
lexer->mark_end(lexer);
|
||||||
|
advance(lexer);
|
||||||
|
if (lexer->lookahead == '-') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lexer->result_symbol = BLOCK_COMMENT_CONTENT;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (has_newline) {
|
||||||
|
// We had a newline now it's time to check if we need to add
|
||||||
|
// multiple tokens to get back up to the right level
|
||||||
|
VEC_CLEAR(scanner->runback);
|
||||||
|
|
||||||
|
while (scanner->indent_length <= VEC_BACK(scanner->indents)) {
|
||||||
|
if (scanner->indent_length == VEC_BACK(scanner->indents)) {
|
||||||
|
if (found_in) {
|
||||||
|
VEC_PUSH(scanner->runback, 1);
|
||||||
|
found_in = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Don't insert VIRTUAL_END_DECL when there is a line
|
||||||
|
// comment incoming
|
||||||
|
|
||||||
|
if (lexer->lookahead == '-') {
|
||||||
|
skip(lexer);
|
||||||
|
if (lexer->lookahead == '-') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Don't insert VIRTUAL_END_DECL when there is a block
|
||||||
|
// comment incoming
|
||||||
|
if (lexer->lookahead == '{') {
|
||||||
|
skip(lexer);
|
||||||
|
if (lexer->lookahead == '-') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VEC_PUSH(scanner->runback, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (scanner->indent_length < VEC_BACK(scanner->indents)) {
|
||||||
|
VEC_POP(scanner->indents);
|
||||||
|
VEC_PUSH(scanner->runback, 1);
|
||||||
|
found_in = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needed for some of the more weird cases where let is in the same
|
||||||
|
// line as everything before the in in the next line
|
||||||
|
if (found_in) {
|
||||||
|
VEC_PUSH(scanner->runback, 1);
|
||||||
|
found_in = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Our list is the wrong way around, reverse it
|
||||||
|
VEC_REVERSE(scanner->runback);
|
||||||
|
// Handle the first runback token if we have them, if there are more
|
||||||
|
// they will be handled on the next scan operation
|
||||||
|
if (scanner->runback.len > 0 && VEC_BACK(scanner->runback) == 0 &&
|
||||||
|
valid_symbols[VIRTUAL_END_DECL]) {
|
||||||
|
VEC_POP(scanner->runback);
|
||||||
|
lexer->result_symbol = VIRTUAL_END_DECL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (scanner->runback.len > 0 && VEC_BACK(scanner->runback) == 1 &&
|
||||||
|
valid_symbols[VIRTUAL_END_SECTION]) {
|
||||||
|
VEC_POP(scanner->runback);
|
||||||
|
lexer->result_symbol = VIRTUAL_END_SECTION;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (lexer->eof(lexer) && valid_symbols[VIRTUAL_END_SECTION]) {
|
||||||
|
lexer->result_symbol = VIRTUAL_END_SECTION;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valid_symbols[GLSL_CONTENT]) {
|
||||||
|
if (!can_call_mark_end) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
lexer->result_symbol = GLSL_CONTENT;
|
||||||
|
while (true) {
|
||||||
|
switch (lexer->lookahead) {
|
||||||
|
case '|':
|
||||||
|
lexer->mark_end(lexer);
|
||||||
|
advance(lexer);
|
||||||
|
if (lexer->lookahead == ']') {
|
||||||
|
advance(lexer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '\0':
|
||||||
|
lexer->mark_end(lexer);
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
advance(lexer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------------------------
|
||||||
|
// API
|
||||||
|
// --------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function allocates the persistent state of the parser that is passed
|
||||||
|
* into the other API functions.
|
||||||
|
*/
|
||||||
|
void *tree_sitter_elm_external_scanner_create() {
|
||||||
|
Scanner *scanner = (Scanner *)calloc(1, sizeof(Scanner));
|
||||||
|
return scanner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main logic entry point.
|
||||||
|
* Since the state is a singular vector, it can just be cast and used directly.
|
||||||
|
*/
|
||||||
|
bool tree_sitter_elm_external_scanner_scan(void *payload, TSLexer *lexer,
|
||||||
|
const bool *valid_symbols) {
|
||||||
|
Scanner *scanner = (Scanner *)payload;
|
||||||
|
return scan(scanner, lexer, valid_symbols);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy the current state to another location for later reuse.
|
||||||
|
* This is normally more complex, but since this parser's state constists solely
|
||||||
|
* of a vector of integers, it can just be copied.
|
||||||
|
*/
|
||||||
|
unsigned tree_sitter_elm_external_scanner_serialize(void *payload,
|
||||||
|
char *buffer) {
|
||||||
|
Scanner *scanner = (Scanner *)payload;
|
||||||
|
size_t size = 0;
|
||||||
|
|
||||||
|
if (3 + scanner->indents.len + scanner->runback.len >=
|
||||||
|
TREE_SITTER_SERIALIZATION_BUFFER_SIZE) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t runback_count = scanner->runback.len;
|
||||||
|
if (runback_count > UINT8_MAX) {
|
||||||
|
runback_count = UINT8_MAX;
|
||||||
|
}
|
||||||
|
buffer[size++] = (char)runback_count;
|
||||||
|
|
||||||
|
if (runback_count > 0) {
|
||||||
|
memcpy(&buffer[size], scanner->runback.data, runback_count);
|
||||||
|
}
|
||||||
|
size += runback_count;
|
||||||
|
|
||||||
|
size_t indent_length_length = sizeof(scanner->indent_length);
|
||||||
|
buffer[size++] = (char)indent_length_length;
|
||||||
|
if (indent_length_length > 0) {
|
||||||
|
memcpy(&buffer[size], &scanner->indent_length, indent_length_length);
|
||||||
|
}
|
||||||
|
size += indent_length_length;
|
||||||
|
|
||||||
|
int iter = 1;
|
||||||
|
for (; iter != scanner->indents.len &&
|
||||||
|
size < TREE_SITTER_SERIALIZATION_BUFFER_SIZE;
|
||||||
|
++iter) {
|
||||||
|
buffer[size++] = (char)scanner->indents.data[iter];
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load another parser state into the currently active state.
|
||||||
|
* `payload` is the state of the previous parser execution, while `buffer` is
|
||||||
|
* the saved state of a different position (e.g. when doing incremental
|
||||||
|
* parsing).
|
||||||
|
*/
|
||||||
|
void tree_sitter_elm_external_scanner_deserialize(void *payload,
|
||||||
|
const char *buffer,
|
||||||
|
unsigned length) {
|
||||||
|
Scanner *scanner = (Scanner *)payload;
|
||||||
|
VEC_CLEAR(scanner->runback);
|
||||||
|
VEC_CLEAR(scanner->indents);
|
||||||
|
VEC_PUSH(scanner->indents, 0);
|
||||||
|
|
||||||
|
if (length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size = 0;
|
||||||
|
size_t runback_count = (unsigned char)buffer[size++];
|
||||||
|
VEC_GROW(scanner->runback, runback_count)
|
||||||
|
if (runback_count > 0) {
|
||||||
|
memcpy(scanner->runback.data, &buffer[size], runback_count);
|
||||||
|
scanner->runback.len = runback_count;
|
||||||
|
size += runback_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t indent_length_length = (unsigned char)buffer[size++];
|
||||||
|
if (indent_length_length > 0) {
|
||||||
|
memcpy(&scanner->indent_length, &buffer[size], indent_length_length);
|
||||||
|
size += indent_length_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; size < length; size++) {
|
||||||
|
VEC_PUSH(scanner->indents, (unsigned char)buffer[size]);
|
||||||
|
}
|
||||||
|
assert(size == length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy the state.
|
||||||
|
*/
|
||||||
|
void tree_sitter_elm_external_scanner_destroy(void *payload) {
|
||||||
|
Scanner *scanner = (Scanner *)payload;
|
||||||
|
VEC_FREE(scanner->indents);
|
||||||
|
VEC_FREE(scanner->runback);
|
||||||
|
free(scanner);
|
||||||
|
}
|
||||||
@ -1,507 +0,0 @@
|
|||||||
#include <tree_sitter/parser.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <cwctype>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
using std::string;
|
|
||||||
using std::vector;
|
|
||||||
|
|
||||||
enum TokenType
|
|
||||||
{
|
|
||||||
VIRTUAL_END_DECL,
|
|
||||||
VIRTUAL_OPEN_SECTION,
|
|
||||||
VIRTUAL_END_SECTION,
|
|
||||||
MINUS_WITHOUT_TRAILING_WHITESPACE,
|
|
||||||
GLSL_CONTENT,
|
|
||||||
BLOCK_COMMENT_CONTENT
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Scanner
|
|
||||||
{
|
|
||||||
Scanner() {}
|
|
||||||
|
|
||||||
unsigned serialize(char *buffer)
|
|
||||||
{
|
|
||||||
size_t i = 0;
|
|
||||||
|
|
||||||
size_t runback_count = runback.size();
|
|
||||||
if (runback_count > UINT8_MAX)
|
|
||||||
runback_count = UINT8_MAX;
|
|
||||||
buffer[i++] = runback_count;
|
|
||||||
|
|
||||||
if (runback_count > 0)
|
|
||||||
{
|
|
||||||
memcpy(&buffer[i], runback.data(), runback_count);
|
|
||||||
}
|
|
||||||
i += runback_count;
|
|
||||||
|
|
||||||
size_t indent_length_length = sizeof(indent_length);
|
|
||||||
buffer[i++] = indent_length_length;
|
|
||||||
if (indent_length_length > 0)
|
|
||||||
{
|
|
||||||
memcpy(&buffer[i], &indent_length, indent_length_length);
|
|
||||||
}
|
|
||||||
i += indent_length_length;
|
|
||||||
|
|
||||||
vector<uint32_t>::iterator
|
|
||||||
iter = indent_length_stack.begin() + 1,
|
|
||||||
end = indent_length_stack.end();
|
|
||||||
|
|
||||||
for (; iter != end && i < TREE_SITTER_SERIALIZATION_BUFFER_SIZE; ++iter)
|
|
||||||
{
|
|
||||||
buffer[i++] = *iter;
|
|
||||||
}
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
void deserialize(const char *buffer, unsigned length)
|
|
||||||
{
|
|
||||||
runback.clear();
|
|
||||||
indent_length_stack.clear();
|
|
||||||
indent_length_stack.push_back(0);
|
|
||||||
|
|
||||||
if (length > 0)
|
|
||||||
{
|
|
||||||
size_t i = 0;
|
|
||||||
|
|
||||||
size_t runback_count = (uint8_t)buffer[i++];
|
|
||||||
runback.resize(runback_count);
|
|
||||||
if (runback_count > 0)
|
|
||||||
{
|
|
||||||
memcpy(runback.data(), &buffer[i], runback_count);
|
|
||||||
}
|
|
||||||
i += runback_count;
|
|
||||||
|
|
||||||
size_t indent_length_length = buffer[i++];
|
|
||||||
if (indent_length_length > 0)
|
|
||||||
{
|
|
||||||
memcpy(&indent_length, &buffer[i], indent_length_length);
|
|
||||||
}
|
|
||||||
i += indent_length_length;
|
|
||||||
|
|
||||||
for (; i < length; i++)
|
|
||||||
{
|
|
||||||
indent_length_stack.push_back(buffer[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void advance(TSLexer *lexer)
|
|
||||||
{
|
|
||||||
lexer->advance(lexer, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void skip(TSLexer *lexer)
|
|
||||||
{
|
|
||||||
lexer->advance(lexer, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isElmSpace(TSLexer *lexer)
|
|
||||||
{
|
|
||||||
return lexer->lookahead == ' ' || lexer->lookahead == '\r' || lexer->lookahead == '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
int checkForIn(TSLexer *lexer, const bool *valid_symbols)
|
|
||||||
{
|
|
||||||
// Are we at the end of a let (in) declaration
|
|
||||||
if (valid_symbols[VIRTUAL_END_SECTION] && lexer->lookahead == 'i')
|
|
||||||
{
|
|
||||||
skip(lexer);
|
|
||||||
|
|
||||||
if (lexer->lookahead == 'n')
|
|
||||||
{
|
|
||||||
skip(lexer);
|
|
||||||
if (isElmSpace(lexer) || lexer->eof(lexer))
|
|
||||||
{
|
|
||||||
return 2; // Success
|
|
||||||
}
|
|
||||||
return 1; // Partial
|
|
||||||
}
|
|
||||||
return 1; // Partial
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool scan_block_comment(TSLexer *lexer)
|
|
||||||
{
|
|
||||||
lexer->mark_end(lexer);
|
|
||||||
if (lexer->lookahead != '{')
|
|
||||||
return false;
|
|
||||||
|
|
||||||
advance(lexer);
|
|
||||||
if (lexer->lookahead != '-')
|
|
||||||
return false;
|
|
||||||
|
|
||||||
advance(lexer);
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
switch (lexer->lookahead)
|
|
||||||
{
|
|
||||||
case '{':
|
|
||||||
scan_block_comment(lexer);
|
|
||||||
break;
|
|
||||||
case '-':
|
|
||||||
advance(lexer);
|
|
||||||
if (lexer->lookahead == '}')
|
|
||||||
{
|
|
||||||
advance(lexer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '\0':
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
advance(lexer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void advance_to_line_end(TSLexer *lexer)
|
|
||||||
{
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
if (lexer->lookahead == '\n') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (lexer->eof(lexer)) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
advance(lexer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool scan(TSLexer *lexer, const bool *valid_symbols)
|
|
||||||
{
|
|
||||||
// First handle eventual runback tokens, we saved on a previous scan op
|
|
||||||
if (!runback.empty() && runback.back() == 0 && valid_symbols[VIRTUAL_END_DECL])
|
|
||||||
{
|
|
||||||
runback.pop_back();
|
|
||||||
lexer->result_symbol = VIRTUAL_END_DECL;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!runback.empty() && runback.back() == 1 && valid_symbols[VIRTUAL_END_SECTION])
|
|
||||||
{
|
|
||||||
runback.pop_back();
|
|
||||||
lexer->result_symbol = VIRTUAL_END_SECTION;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
runback.clear();
|
|
||||||
|
|
||||||
// Check if we have newlines and how much indentation
|
|
||||||
bool has_newline = false;
|
|
||||||
bool found_in = false;
|
|
||||||
bool can_call_mark_end = true;
|
|
||||||
lexer->mark_end(lexer);
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (lexer->lookahead == ' ')
|
|
||||||
{
|
|
||||||
skip(lexer);
|
|
||||||
}
|
|
||||||
else if (lexer->lookahead == '\n')
|
|
||||||
{
|
|
||||||
skip(lexer);
|
|
||||||
has_newline = true;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (lexer->lookahead == ' ')
|
|
||||||
{
|
|
||||||
skip(lexer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
indent_length = lexer->get_column(lexer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!valid_symbols[BLOCK_COMMENT_CONTENT] && lexer->lookahead == '-')
|
|
||||||
{
|
|
||||||
|
|
||||||
advance(lexer);
|
|
||||||
int32_t lookahead = lexer->lookahead;
|
|
||||||
|
|
||||||
// Handle minus without a whitespace for negate
|
|
||||||
if (valid_symbols[MINUS_WITHOUT_TRAILING_WHITESPACE]
|
|
||||||
&& ((lookahead >= 'a' && lookahead <= 'z')
|
|
||||||
|| (lookahead >= 'A' && lookahead <= 'Z')
|
|
||||||
|| lookahead == '('))
|
|
||||||
{
|
|
||||||
if (can_call_mark_end)
|
|
||||||
{
|
|
||||||
lexer->result_symbol = MINUS_WITHOUT_TRAILING_WHITESPACE;
|
|
||||||
lexer->mark_end(lexer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Scan past line comments. As far as the special token
|
|
||||||
// types we're scanning for here are concerned line comments
|
|
||||||
// are like whitespace. There is nothing useful to be
|
|
||||||
// learned from, say, their indentation. So we advance past
|
|
||||||
// them here.
|
|
||||||
//
|
|
||||||
// The one thing we need to keep in mind is that we should
|
|
||||||
// not call `lexer->mark_end(lexer)` after this point, or
|
|
||||||
// the comment will be lost.
|
|
||||||
else if (lookahead == '-' && has_newline)
|
|
||||||
{
|
|
||||||
can_call_mark_end = false;
|
|
||||||
advance(lexer);
|
|
||||||
advance_to_line_end(lexer);
|
|
||||||
}
|
|
||||||
else if (valid_symbols[BLOCK_COMMENT_CONTENT] && lexer->lookahead == '}')
|
|
||||||
{
|
|
||||||
lexer->result_symbol = BLOCK_COMMENT_CONTENT;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (lexer->lookahead == '\r')
|
|
||||||
{
|
|
||||||
skip(lexer);
|
|
||||||
}
|
|
||||||
else if (lexer->eof(lexer))
|
|
||||||
{
|
|
||||||
if (valid_symbols[VIRTUAL_END_SECTION])
|
|
||||||
{
|
|
||||||
lexer->result_symbol = VIRTUAL_END_SECTION;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (valid_symbols[VIRTUAL_END_DECL])
|
|
||||||
{
|
|
||||||
lexer->result_symbol = VIRTUAL_END_DECL;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkForIn(lexer, valid_symbols) == 2)
|
|
||||||
{
|
|
||||||
if (has_newline)
|
|
||||||
{
|
|
||||||
found_in = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lexer->result_symbol = VIRTUAL_END_SECTION;
|
|
||||||
indent_length_stack.pop_back();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open section if the grammar lets us but only push to indent stack if we go further down in the stack
|
|
||||||
if (valid_symbols[VIRTUAL_OPEN_SECTION] && !lexer->eof(lexer))
|
|
||||||
{
|
|
||||||
indent_length_stack.push_back(lexer->get_column(lexer));
|
|
||||||
lexer->result_symbol = VIRTUAL_OPEN_SECTION;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (valid_symbols[BLOCK_COMMENT_CONTENT])
|
|
||||||
{
|
|
||||||
if (!can_call_mark_end)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
lexer->mark_end(lexer);
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (lexer->lookahead == '\0')
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (lexer->lookahead != '{' && lexer->lookahead != '-')
|
|
||||||
{
|
|
||||||
advance(lexer);
|
|
||||||
}
|
|
||||||
else if (lexer->lookahead == '-')
|
|
||||||
{
|
|
||||||
lexer->mark_end(lexer);
|
|
||||||
advance(lexer);
|
|
||||||
if (lexer->lookahead == '}')
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (scan_block_comment(lexer))
|
|
||||||
{
|
|
||||||
lexer->mark_end(lexer);
|
|
||||||
advance(lexer);
|
|
||||||
if (lexer->lookahead == '-')
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lexer->result_symbol = BLOCK_COMMENT_CONTENT;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (has_newline)
|
|
||||||
{
|
|
||||||
// We had a newline now it's time to check if we need to add multiple tokens to get back up to the right level
|
|
||||||
runback.clear();
|
|
||||||
|
|
||||||
while (indent_length <= indent_length_stack.back())
|
|
||||||
{
|
|
||||||
if (indent_length == indent_length_stack.back())
|
|
||||||
{
|
|
||||||
if (found_in)
|
|
||||||
{
|
|
||||||
runback.push_back(1);
|
|
||||||
found_in = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Don't insert VIRTUAL_END_DECL when there is a line comment incoming
|
|
||||||
|
|
||||||
if (lexer->lookahead == '-')
|
|
||||||
{
|
|
||||||
skip(lexer);
|
|
||||||
if (lexer->lookahead == '-')
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Don't insert VIRTUAL_END_DECL when there is a block comment incoming
|
|
||||||
if (lexer->lookahead == '{')
|
|
||||||
{
|
|
||||||
skip(lexer);
|
|
||||||
if (lexer->lookahead == '-')
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
runback.push_back(0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (indent_length < indent_length_stack.back())
|
|
||||||
{
|
|
||||||
indent_length_stack.pop_back();
|
|
||||||
runback.push_back(1);
|
|
||||||
found_in = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Needed for some of the more weird cases where let is in the same line as everything before the in in the next line
|
|
||||||
if (found_in)
|
|
||||||
{
|
|
||||||
runback.push_back(1);
|
|
||||||
found_in = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Our list is the wrong way around, reverse it
|
|
||||||
std::reverse(runback.begin(), runback.end());
|
|
||||||
// Handle the first runback token if we have them, if there are more they will be handled on the next scan operation
|
|
||||||
if (!runback.empty() && runback.back() == 0 && valid_symbols[VIRTUAL_END_DECL])
|
|
||||||
{
|
|
||||||
runback.pop_back();
|
|
||||||
lexer->result_symbol = VIRTUAL_END_DECL;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (!runback.empty() && runback.back() == 1 && valid_symbols[VIRTUAL_END_SECTION])
|
|
||||||
{
|
|
||||||
runback.pop_back();
|
|
||||||
lexer->result_symbol = VIRTUAL_END_SECTION;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (lexer->eof(lexer) && valid_symbols[VIRTUAL_END_SECTION])
|
|
||||||
{
|
|
||||||
lexer->result_symbol = VIRTUAL_END_SECTION;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (valid_symbols[GLSL_CONTENT])
|
|
||||||
{
|
|
||||||
if (!can_call_mark_end)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
lexer->result_symbol = GLSL_CONTENT;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
switch (lexer->lookahead)
|
|
||||||
{
|
|
||||||
case '|':
|
|
||||||
lexer->mark_end(lexer);
|
|
||||||
advance(lexer);
|
|
||||||
if (lexer->lookahead == ']')
|
|
||||||
{
|
|
||||||
advance(lexer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '\0':
|
|
||||||
lexer->mark_end(lexer);
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
advance(lexer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The indention of the current line
|
|
||||||
uint32_t indent_length;
|
|
||||||
// Our indentation stack
|
|
||||||
vector<uint32_t> indent_length_stack;
|
|
||||||
// Stack of 0 - for possible VIRTUAL_END_DECL or 1 - for possible VIRTUAL_END_SECTION
|
|
||||||
vector<uint8_t> runback;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
|
|
||||||
void *tree_sitter_elm_external_scanner_create()
|
|
||||||
{
|
|
||||||
return new Scanner();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool tree_sitter_elm_external_scanner_scan(void *payload, TSLexer *lexer,
|
|
||||||
const bool *valid_symbols)
|
|
||||||
{
|
|
||||||
Scanner *scanner = static_cast<Scanner *>(payload);
|
|
||||||
return scanner->scan(lexer, valid_symbols);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned tree_sitter_elm_external_scanner_serialize(void *payload, char *buffer)
|
|
||||||
{
|
|
||||||
Scanner *scanner = static_cast<Scanner *>(payload);
|
|
||||||
return scanner->serialize(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void tree_sitter_elm_external_scanner_deserialize(void *payload, const char *buffer, unsigned length)
|
|
||||||
{
|
|
||||||
Scanner *scanner = static_cast<Scanner *>(payload);
|
|
||||||
scanner->deserialize(buffer, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
void tree_sitter_elm_external_scanner_destroy(void *payload)
|
|
||||||
{
|
|
||||||
Scanner *scanner = static_cast<Scanner *>(payload);
|
|
||||||
delete scanner;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue