Merge commit '09dbf221d7491dc8d8839616b27c21b9c025c457'

pull/708/head
Wilfred Hughes 2024-04-28 23:48:30 +07:00
commit 1dd7bbebe8
29 changed files with 18749 additions and 17344 deletions

@ -13,3 +13,7 @@ updates:
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"

@ -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

@ -11,10 +11,10 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 14
node-version: 18
- run: npm i
- run: npm run build
- run: npm run test-only
@ -23,10 +23,10 @@ jobs:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 14
node-version: 18
registry-url: https://registry.npmjs.org/
- run: npm i
- run: npm run build
@ -38,10 +38,10 @@ jobs:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 14
node-version: 18
registry-url: https://npm.pkg.github.com/
- run: npm i
- run: npm run build

@ -8,7 +8,7 @@ jobs:
cargo:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1
with:
toolchain: stable

@ -16,12 +16,15 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest]
node-version: [14, 16]
node-version: [18, 20]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Npm install
@ -29,7 +32,7 @@ jobs:
npm i
- name: Unit tests
run: |
npx tree-sitter test
npm test
- name: Test examples
continue-on-error: true
run: |

@ -16,12 +16,15 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest]
node-version: [14, 16]
node-version: [18, 20]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Npm install
@ -29,7 +32,7 @@ jobs:
npm i
- name: Unit tests
run: |
npx tree-sitter test
npm test
- name: Test examples
run: |
script/parse-examples
@ -44,7 +47,7 @@ jobs:
os: [ubuntu-latest]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1
with:
toolchain: stable

@ -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

@ -6,6 +6,7 @@ build
.idea
.vscode/ipch
target/
/.build/
# Examples generated during automated tests
examples/**

@ -22,13 +22,13 @@ include = [
]
# Keep in sync with package.json
version = "5.6.0"
version = "5.7.0"
[lib]
path = "bindings/rust/lib.rs"
[dependencies]
tree-sitter = "~0.20"
tree-sitter = "0.20.10"
[build-dependencies]
cc = "1.0"

@ -1,5 +1,5 @@
1. Increase the version number in the package.json and the Cargo.toml
2. Run `npm run generate-types && npm run generate-wasm`
2. Run `npm run generate-wasm`
3. Push the code to main
4. Wait for tests to be successful
5. Create a release on github with the name being the version number from before prefixed with `v` for e.g. `v1.1.0`

@ -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")])
]
)

@ -9,7 +9,7 @@
"sources": [
"src/parser.c",
"bindings/node/binding.cc",
"src/scanner.cc"
"src/scanner.c"
],
"cflags_c": [
"-std=c99",

@ -2,7 +2,7 @@ fn main() {
let src_dir = std::path::Path::new("src");
let mut c_config = cc::Build::new();
c_config.include(&src_dir);
c_config.include(src_dir);
c_config
.flag_if_supported("-Wno-unused-parameter")
.flag_if_supported("-Wno-unused-but-set-variable")
@ -10,29 +10,10 @@ fn main() {
let parser_path = src_dir.join("parser.c");
c_config.file(&parser_path);
// If your language uses an external scanner written in C,
// then include this block of code:
/*
let scanner_path = src_dir.join("scanner.c");
c_config.file(&scanner_path);
println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap());
*/
c_config.compile("parser");
println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap());
// If your language uses an external scanner written in C++,
// then include this block of code:
let mut cpp_config = cc::Build::new();
cpp_config.cpp(true);
cpp_config.include(&src_dir);
cpp_config
.flag_if_supported("-Wno-unused-parameter")
.flag_if_supported("-Wno-unused-but-set-variable");
let scanner_path = src_dir.join("scanner.cc");
cpp_config.file(&scanner_path);
cpp_config.compile("scanner");
println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap());
}

@ -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_

File diff suppressed because one or more lines are too long

@ -241,7 +241,7 @@ module.exports = grammar({
prec.left(
seq(
field("name", $.upper_case_identifier),
repeat($._single_type_expression)
repeat(choice($.line_comment, $._single_type_expression))
)
),

@ -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;
}

@ -1,6 +1,6 @@
{
"name": "@elm-tooling/tree-sitter-elm",
"version": "5.6.0",
"version": "5.7.0",
"description": "Tree sitter definitions for elm",
"main": "bindings/node",
"publishConfig": {
@ -16,15 +16,14 @@
"author": "Razze",
"license": "MIT",
"dependencies": {
"nan": "^2.15.0"
"nan": "^2.18.0"
},
"devDependencies": {
"@asgerf/dts-tree-sitter": "^0.1.0",
"tree-sitter-cli": "^0.20.6"
"tree-sitter-cli": "^0.20.8"
},
"scripts": {
"build": "tree-sitter generate",
"parse-basic": "tree-sitter parse ./examples/Basic.elm",
"parse-basic": "tree-sitter parse ./examples/basic.elm",
"parse-test": "tree-sitter parse --debug ./examples/test.elm",
"test": "tree-sitter test && script/parse-examples",
"test-skip-download": "tree-sitter test && script/parse-examples -s",
@ -33,7 +32,6 @@
"test-only": "tree-sitter test",
"test-highlighting": "tree-sitter highlight test/highlight/basic.elm",
"test-tags": "tree-sitter tags test/highlight/basic.elm",
"generate-types": "node ./node_modules/@asgerf/dts-tree-sitter/build/src/index.js src > index.d.ts",
"generate-wasm": "tree-sitter build-wasm && mv ./tree-sitter-elm.wasm ./docs/js/tree-sitter-elm.wasm"
},
"repository": "https://github.com/elm-tooling/tree-sitter-elm",

@ -44,10 +44,10 @@ if [[ $SKIP_DOWNLOAD != 'true' ]]; then
checkout_at "examples/elm-svg" "elm/svg" "08bd432990862bab5b840654dd437fbb2e6176e7"
checkout_at "examples/elm-time" "elm/time" "dc3b75b7366e59b99962706f7bf064d3634a4bba"
checkout_at "examples/elm-url" "elm/url" "4e5ee032515581bf01428d54ee636dd601f4bc90"
checkout_at "examples/elm-virtual-dom" "elm/virtual-dom" "5a5bcf48720bc7d53461b3cd42a9f19f119c5503"
checkout_at "examples/elm-virtual-dom" "elm/virtual-dom" "44cbe2bf3d598cab569045cefcc10de31907598d"
checkout_at "examples/elm-ui" "mdgriffith/elm-ui" "acae8857a02e600cc4b4737ca2f70607228b4489"
checkout_at "examples/elm-markup" "mdgriffith/elm-markup" "b073d85490f71c6491648bcd0b11bf9aca6e53a5"
checkout_at "examples/elm-visualization" "gampleman/elm-visualization" "edbf5e268ecc1572402c7747ae26abb2f92192ec"
checkout_at "examples/elm-visualization" "gampleman/elm-visualization" "6b9c7476507cedbbd8fc841fdecb59f4af2c3f96"
fi
skipped_files=()

File diff suppressed because it is too large Load Diff

@ -744,8 +744,17 @@
{
"type": "REPEAT",
"content": {
"type": "SYMBOL",
"name": "_single_type_expression"
"type": "CHOICE",
"members": [
{
"type": "SYMBOL",
"name": "line_comment"
},
{
"type": "SYMBOL",
"name": "_single_type_expression"
}
]
}
}
]

@ -2602,6 +2602,16 @@
}
]
}
},
"children": {
"multiple": true,
"required": false,
"types": [
{
"type": "line_comment",
"named": true
}
]
}
},
{

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;
}
}

@ -123,6 +123,7 @@ struct TSLanguage {
unsigned (*serialize)(void *, char *);
void (*deserialize)(void *, const char *, unsigned);
} external_scanner;
const TSStateId *primary_state_ids;
};
/*

@ -245,3 +245,95 @@ match input =
(value_expr
(upper_case_qid
(upper_case_identifier)))))
================================================================================
Type declaration with single variant with line comment
================================================================================
type Foo
= Bar -- This is a Bar
--------------------------------------------------------------------------------
(file
(type_declaration
(type)
(upper_case_identifier)
(eq)
(union_variant
(upper_case_identifier)
(line_comment)))
)
================================================================================
Type declaration with line comment per union variant
================================================================================
type Foo
= Bar -- This is a Bar
| Biz -- This is a Biz
--------------------------------------------------------------------------------
(file
(type_declaration
(type)
(upper_case_identifier)
(eq)
(union_variant
(upper_case_identifier)
(line_comment))
(union_variant
(upper_case_identifier)
(line_comment)))
)
================================================================================
Type declaration with union variant and associated data and line comment on new line
================================================================================
type Foo
= Bar
-- First associated data
Int
--------------------------------------------------------------------------------
(file
(type_declaration
(type)
(upper_case_identifier)
(eq)
(union_variant
(upper_case_identifier)
(line_comment)
(type_ref
(upper_case_qid
(upper_case_identifier))))))
================================================================================
Type declaration with union variant and associated data with line comment on same line
================================================================================
type Foo
= Bar
Int -- First associated data
Float -- Second associated data
--------------------------------------------------------------------------------
(file
(type_declaration
(type)
(upper_case_identifier)
(eq)
(union_variant
(upper_case_identifier)
(type_ref
(upper_case_qid
(upper_case_identifier)))
(line_comment)
(type_ref
(upper_case_qid
(upper_case_identifier)))
(line_comment))))