@herb-tools/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/README.md +9 -0
  3. package/dist/herb-core.browser.js +2704 -0
  4. package/dist/herb-core.browser.js.map +1 -0
  5. package/dist/herb-core.cjs +2759 -0
  6. package/dist/herb-core.cjs.map +1 -0
  7. package/dist/herb-core.esm.js +2704 -0
  8. package/dist/herb-core.esm.js.map +1 -0
  9. package/dist/herb-core.umd.js +2765 -0
  10. package/dist/herb-core.umd.js.map +1 -0
  11. package/dist/types/ast.d.ts +4 -0
  12. package/dist/types/backend.d.ts +24 -0
  13. package/dist/types/error.d.ts +16 -0
  14. package/dist/types/errors.d.ts +222 -0
  15. package/dist/types/herb-backend.d.ts +87 -0
  16. package/dist/types/index.d.ts +15 -0
  17. package/dist/types/lex-result.d.ts +50 -0
  18. package/dist/types/location.d.ts +18 -0
  19. package/dist/types/node.d.ts +27 -0
  20. package/dist/types/nodes.d.ts +682 -0
  21. package/dist/types/parse-result.d.ts +62 -0
  22. package/dist/types/position.d.ts +15 -0
  23. package/dist/types/range.d.ts +12 -0
  24. package/dist/types/result.d.ts +10 -0
  25. package/dist/types/token-list.d.ts +16 -0
  26. package/dist/types/token.d.ts +22 -0
  27. package/dist/types/util.d.ts +2 -0
  28. package/dist/types/visitor.d.ts +11 -0
  29. package/dist/types/warning.d.ts +11 -0
  30. package/package.json +49 -0
  31. package/src/ast.ts +7 -0
  32. package/src/backend.ts +85 -0
  33. package/src/error.ts +38 -0
  34. package/src/errors.ts +820 -0
  35. package/src/herb-backend.ts +152 -0
  36. package/src/index.ts +15 -0
  37. package/src/lex-result.ts +78 -0
  38. package/src/location.ts +51 -0
  39. package/src/node.ts +106 -0
  40. package/src/nodes.ts +2294 -0
  41. package/src/parse-result.ts +101 -0
  42. package/src/position.ts +38 -0
  43. package/src/range.ts +35 -0
  44. package/src/result.ts +26 -0
  45. package/src/token-list.ts +57 -0
  46. package/src/token.ts +63 -0
  47. package/src/util.ts +19 -0
  48. package/src/visitor.ts +14 -0
  49. package/src/warning.ts +20 -0
@@ -0,0 +1,101 @@
1
+ import { Result } from "./result.js"
2
+ import { DocumentNode } from "./nodes.js"
3
+ import { HerbError } from "./error.js"
4
+ import { HerbWarning } from "./warning.js"
5
+
6
+ import type { SerializedHerbError } from "./error.js"
7
+ import type { SerializedHerbWarning } from "./warning.js"
8
+ import type { SerializedDocumentNode } from "./nodes.js"
9
+ import type { Visitor } from "./visitor.js"
10
+
11
+ export type SerializedParseResult = {
12
+ value: SerializedDocumentNode
13
+ source: string
14
+ warnings: SerializedHerbWarning[]
15
+ errors: SerializedHerbError[]
16
+ }
17
+
18
+ /**
19
+ * Represents the result of a parsing operation, extending the base `Result` class.
20
+ * It contains the parsed document node, source code, warnings, and errors.
21
+ */
22
+ export class ParseResult extends Result {
23
+ /** The document node generated from the source code. */
24
+ readonly value: DocumentNode
25
+
26
+ /**
27
+ * Creates a `ParseResult` instance from a serialized result.
28
+ * @param result - The serialized parse result containing the value and source.
29
+ * @returns A new `ParseResult` instance.
30
+ */
31
+ static from(result: SerializedParseResult) {
32
+ return new ParseResult(
33
+ DocumentNode.from(result.value),
34
+ result.source,
35
+ result.warnings.map((warning) => HerbWarning.from(warning)),
36
+ result.errors.map((error) => HerbError.from(error)),
37
+ )
38
+ }
39
+
40
+ /**
41
+ * Constructs a new `ParseResult`.
42
+ * @param value - The document node.
43
+ * @param source - The source code that was parsed.
44
+ * @param warnings - An array of warnings encountered during parsing.
45
+ * @param errors - An array of errors encountered during parsing.
46
+ */
47
+ constructor(
48
+ value: DocumentNode,
49
+ source: string,
50
+ warnings: HerbWarning[] = [],
51
+ errors: HerbError[] = [],
52
+ ) {
53
+ super(source, warnings, errors)
54
+ this.value = value
55
+ }
56
+
57
+ /**
58
+ * Determines if the parsing failed.
59
+ * @returns `true` if there are errors, otherwise `false`.
60
+ */
61
+ failed(): boolean {
62
+ // TODO: this should probably be recursive as noted in the Ruby version
63
+ return this.errors.length > 0 || this.value.errors.length > 0
64
+ }
65
+
66
+ /**
67
+ * Determines if the parsing was successful.
68
+ * @returns `true` if there are no errors, otherwise `false`.
69
+ */
70
+ success(): boolean {
71
+ return !this.failed()
72
+ }
73
+
74
+ /**
75
+ * Returns a pretty-printed JSON string of the errors.
76
+ * @returns A string representation of the errors.
77
+ */
78
+ prettyErrors(): string {
79
+ return JSON.stringify([...this.errors, ...this.value.errors], null, 2)
80
+ }
81
+
82
+ recursiveErrors(): HerbError[] {
83
+ return [...this.errors, ...this.value.recursiveErrors()]
84
+ }
85
+
86
+ /**
87
+ * Returns a pretty-printed string of the parse result.
88
+ * @returns A string representation of the parse result.
89
+ */
90
+ inspect(): string {
91
+ return this.value.inspect()
92
+ }
93
+
94
+ /**
95
+ * Accepts a visitor to traverse the document node.
96
+ * @param visitor - The visitor instance.
97
+ */
98
+ visit(visitor: Visitor): void {
99
+ visitor.visit(this.value)
100
+ }
101
+ }
@@ -0,0 +1,38 @@
1
+ export type SerializedPosition = {
2
+ line: number
3
+ column: number
4
+ }
5
+
6
+ export class Position {
7
+ readonly line: number
8
+ readonly column: number
9
+
10
+ static from(position: SerializedPosition) {
11
+ return new Position(position.line, position.column)
12
+ }
13
+
14
+ constructor(line: number, column: number) {
15
+ this.line = line
16
+ this.column = column
17
+ }
18
+
19
+ toHash(): SerializedPosition {
20
+ return { line: this.line, column: this.column }
21
+ }
22
+
23
+ toJSON(): SerializedPosition {
24
+ return this.toHash()
25
+ }
26
+
27
+ treeInspect(): string {
28
+ return `(${this.line}:${this.column})`
29
+ }
30
+
31
+ inspect(): string {
32
+ return `#<Herb::Position ${this.treeInspect()}>`
33
+ }
34
+
35
+ toString(): string {
36
+ return this.inspect()
37
+ }
38
+ }
package/src/range.ts ADDED
@@ -0,0 +1,35 @@
1
+ export type SerializedRange = [number, number]
2
+
3
+ export class Range {
4
+ readonly start: number
5
+ readonly end: number
6
+
7
+ static from(range: SerializedRange) {
8
+ return new Range(range[0], range[1])
9
+ }
10
+
11
+ constructor(start: number, end: number) {
12
+ this.start = start
13
+ this.end = end
14
+ }
15
+
16
+ toArray(): SerializedRange {
17
+ return [this.start, this.end]
18
+ }
19
+
20
+ toJSON(): SerializedRange {
21
+ return this.toArray()
22
+ }
23
+
24
+ treeInspect(): string {
25
+ return `[${this.start}, ${this.end}]`
26
+ }
27
+
28
+ inspect(): string {
29
+ return `#<Herb::Range ${this.toArray()}>`
30
+ }
31
+
32
+ toString(): string {
33
+ return this.inspect()
34
+ }
35
+ }
package/src/result.ts ADDED
@@ -0,0 +1,26 @@
1
+ import { HerbError } from "./error.js"
2
+ import { HerbWarning } from "./warning.js"
3
+
4
+ export class Result {
5
+ readonly source: string
6
+ readonly warnings: HerbWarning[]
7
+ readonly errors: HerbError[]
8
+
9
+ constructor(
10
+ source: string,
11
+ warnings: HerbWarning[] = [],
12
+ errors: HerbError[] = [],
13
+ ) {
14
+ this.source = source
15
+ this.warnings = warnings || []
16
+ this.errors = errors || []
17
+ }
18
+
19
+ success(): boolean {
20
+ return false
21
+ }
22
+
23
+ failed(): boolean {
24
+ return true
25
+ }
26
+ }
@@ -0,0 +1,57 @@
1
+ import { Token, SerializedToken } from "./token.js"
2
+
3
+ export type SerializedTokenList = SerializedToken[]
4
+
5
+ export class TokenList implements Iterable<Token> {
6
+ private list: Token[]
7
+
8
+ static from(list: SerializedTokenList) {
9
+ return new TokenList(
10
+ list.map((token: SerializedToken) => Token.from(token)),
11
+ )
12
+ }
13
+
14
+ constructor(list: Token[]) {
15
+ this.list = list
16
+ }
17
+
18
+ get length(): number {
19
+ return this.list.length
20
+ }
21
+
22
+ [Symbol.iterator](): Iterator<Token> {
23
+ return this.list[Symbol.iterator]()
24
+ }
25
+
26
+ at(index: number): Token | undefined {
27
+ return this.list.at(index)
28
+ }
29
+
30
+ forEach(
31
+ callback: (token: Token, index: number, array: Token[]) => void,
32
+ ): void {
33
+ this.list.forEach(callback)
34
+ }
35
+
36
+ map<U>(callback: (token: Token, index: number, array: Token[]) => U): U[] {
37
+ return this.list.map(callback)
38
+ }
39
+
40
+ filter(
41
+ predicate: (token: Token, index: number, array: Token[]) => boolean,
42
+ ): Token[] {
43
+ return this.list.filter(predicate)
44
+ }
45
+
46
+ __getobj__(): Token[] {
47
+ return this.list
48
+ }
49
+
50
+ inspect(): string {
51
+ return this.list.map((token) => token.inspect()).join("\n") + "\n"
52
+ }
53
+
54
+ toString(): string {
55
+ return this.inspect()
56
+ }
57
+ }
package/src/token.ts ADDED
@@ -0,0 +1,63 @@
1
+ import { Range, SerializedRange } from "./range.js"
2
+ import { Location, SerializedLocation } from "./location.js"
3
+
4
+ export type SerializedToken = {
5
+ value: string
6
+ range: SerializedRange
7
+ location: SerializedLocation
8
+ type: string
9
+ }
10
+
11
+ export class Token {
12
+ readonly value: string
13
+ readonly range: Range
14
+ readonly location: Location
15
+ readonly type: string
16
+
17
+ static from(token: SerializedToken) {
18
+ return new Token(
19
+ token.value,
20
+ Range.from(token.range),
21
+ Location.from(token.location),
22
+ token.type,
23
+ )
24
+ }
25
+
26
+ constructor(value: string, range: Range, location: Location, type: string) {
27
+ this.value = value
28
+ this.range = range
29
+ this.location = location
30
+ this.type = type
31
+ }
32
+
33
+ toHash(): SerializedToken {
34
+ return {
35
+ value: this.value,
36
+ range: this.range?.toArray(),
37
+ location: this.location?.toHash(),
38
+ type: this.type,
39
+ }
40
+ }
41
+
42
+ toJSON(): SerializedToken {
43
+ return this.toHash()
44
+ }
45
+
46
+ treeInspect(): string {
47
+ return `"${this.value}" ${this.location.treeInspectWithLabel()}`
48
+ }
49
+
50
+ valueInspect(): string {
51
+ return this.type === "TOKEN_EOF"
52
+ ? JSON.stringify("<EOF>")
53
+ : JSON.stringify(this.value)
54
+ }
55
+
56
+ inspect(): string {
57
+ return `#<Herb::Token type="${this.type}" value=${this.valueInspect()} range=${this.range.treeInspect()} start=${this.location.start.treeInspect()} end=${this.location.end.treeInspect()}>`
58
+ }
59
+
60
+ toString(): string {
61
+ return this.inspect()
62
+ }
63
+ }
package/src/util.ts ADDED
@@ -0,0 +1,19 @@
1
+ export function ensureString(object: any): string {
2
+ if (typeof object === "string") {
3
+ return object
4
+ }
5
+
6
+ throw new TypeError("Argument must be a string")
7
+ }
8
+
9
+ export function convertToUTF8(string: string) {
10
+ const bytes = []
11
+
12
+ for (let i = 0; i < string.length; i++) {
13
+ bytes.push(string.charCodeAt(i))
14
+ }
15
+
16
+ const decoder = new TextDecoder("utf-8")
17
+
18
+ return decoder.decode(new Uint8Array(bytes))
19
+ }
package/src/visitor.ts ADDED
@@ -0,0 +1,14 @@
1
+ import { Node } from "./node.js"
2
+
3
+ /**
4
+ * Represents a visitor that can traverse nodes.
5
+ */
6
+ export class Visitor {
7
+ /**
8
+ * Visits a node and performs an action.
9
+ * @param node - The node to visit.
10
+ */
11
+ visit(node: Node) {
12
+ console.log("Node", node) // TODO: implement
13
+ }
14
+ }
package/src/warning.ts ADDED
@@ -0,0 +1,20 @@
1
+ import { Location, SerializedLocation } from "./location.js"
2
+
3
+ export interface SerializedHerbWarning {
4
+ message: string
5
+ location: SerializedLocation
6
+ }
7
+
8
+ export class HerbWarning {
9
+ message: string
10
+ location: Location
11
+
12
+ static from(warning: SerializedHerbWarning) {
13
+ return new HerbWarning(warning.message, Location.from(warning.location))
14
+ }
15
+
16
+ constructor(message: string, location: Location) {
17
+ this.message = message
18
+ this.location = location
19
+ }
20
+ }