beancount 0.0.31 → 0.2.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.
- package/README.md +51 -22
- package/build/src/benchmark.mjs +1 -1
- package/build/src/classes/DatedNode.d.mts +40 -0
- package/build/src/classes/DatedNode.mjs +61 -0
- package/build/src/classes/Node.d.mts +92 -0
- package/build/src/classes/Node.mjs +107 -0
- package/build/src/classes/ParseResult.d.mts +75 -75
- package/build/src/classes/ParseResult.mjs +96 -98
- package/build/src/classes/nodes/Balance.d.mts +32 -0
- package/build/src/classes/nodes/Balance.mjs +50 -0
- package/build/src/classes/nodes/Blankline.d.mts +23 -0
- package/build/src/classes/nodes/Blankline.mjs +37 -0
- package/build/src/classes/nodes/Close.d.mts +20 -0
- package/build/src/classes/nodes/Close.mjs +31 -0
- package/build/src/classes/nodes/Comment.d.mts +25 -0
- package/build/src/classes/nodes/Comment.mjs +42 -0
- package/build/src/classes/nodes/Commodity.d.mts +20 -0
- package/build/src/classes/nodes/Commodity.mjs +31 -0
- package/build/src/classes/nodes/Custom.d.mts +23 -0
- package/build/src/classes/nodes/Custom.mjs +38 -0
- package/build/src/classes/nodes/Document.d.mts +22 -0
- package/build/src/classes/nodes/Document.mjs +34 -0
- package/build/src/classes/nodes/Event.d.mts +23 -0
- package/build/src/classes/nodes/Event.mjs +34 -0
- package/build/src/classes/nodes/Include.d.mts +20 -0
- package/build/src/classes/nodes/Include.mjs +31 -0
- package/build/src/classes/nodes/Note.d.mts +22 -0
- package/build/src/classes/nodes/Note.mjs +34 -0
- package/build/src/classes/nodes/Open.d.mts +27 -0
- package/build/src/classes/nodes/Open.mjs +66 -0
- package/build/src/classes/nodes/Option.d.mts +23 -0
- package/build/src/classes/nodes/Option.mjs +32 -0
- package/build/src/classes/nodes/Pad.d.mts +22 -0
- package/build/src/classes/nodes/Pad.mjs +33 -0
- package/build/src/classes/nodes/Plugin.d.mts +22 -0
- package/build/src/classes/nodes/Plugin.mjs +36 -0
- package/build/src/classes/nodes/Poptag.d.mts +21 -0
- package/build/src/classes/nodes/Poptag.mjs +34 -0
- package/build/src/classes/nodes/Price.d.mts +32 -0
- package/build/src/classes/nodes/Price.mjs +57 -0
- package/build/src/classes/nodes/Pushtag.d.mts +21 -0
- package/build/src/classes/nodes/Pushtag.mjs +34 -0
- package/build/src/classes/nodes/Query.d.mts +22 -0
- package/build/src/classes/nodes/Query.mjs +34 -0
- package/build/src/classes/nodes/Transaction/Posting.d.mts +59 -0
- package/build/src/classes/nodes/Transaction/Posting.mjs +97 -0
- package/build/src/classes/nodes/Transaction/Tag.d.mts +28 -0
- package/build/src/classes/nodes/Transaction/Tag.mjs +28 -0
- package/build/src/classes/nodes/Transaction/index.d.mts +70 -0
- package/build/src/classes/nodes/Transaction/index.mjs +193 -0
- package/build/src/classes/nodes/index.d.mts +19 -0
- package/build/src/classes/nodes/index.mjs +19 -0
- package/build/src/cli.mjs +4 -4
- package/build/src/deserialize.d.mts +54 -54
- package/build/src/deserialize.mjs +89 -89
- package/build/src/directiveTypes.d.mts +10 -0
- package/build/src/directiveTypes.mjs +29 -0
- package/build/src/genericParse.d.mts +27 -20
- package/build/src/genericParse.mjs +30 -30
- package/build/src/main.d.mts +30 -31
- package/build/src/main.mjs +30 -30
- package/build/src/nodeTypeToClass.d.mts +81 -0
- package/build/src/nodeTypeToClass.mjs +37 -0
- package/build/src/parse.d.mts +16 -16
- package/build/src/parse.mjs +37 -37
- package/build/src/parseFile.d.mts +2 -2
- package/build/src/parseFile.mjs +11 -11
- package/build/src/utils/splitStringIntoSourceFragments.d.ts +14 -0
- package/build/src/utils/splitStringIntoSourceFragments.js +48 -0
- package/build/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +7 -7
package/build/src/main.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @remarks
|
|
5
5
|
* The primary function you'll use is {@link parse}, which parses a complete Beancount file
|
|
6
|
-
* and returns a {@link ParseResult} containing
|
|
6
|
+
* and returns a {@link ParseResult} containing parsed nodes.
|
|
7
7
|
*
|
|
8
8
|
* @example
|
|
9
9
|
* ```typescript
|
|
@@ -17,42 +17,42 @@
|
|
|
17
17
|
* `
|
|
18
18
|
*
|
|
19
19
|
* const result = parse(beancountContent)
|
|
20
|
-
* console.log(result.
|
|
20
|
+
* console.log(result.nodes) // Array of parsed Node objects
|
|
21
21
|
* ```
|
|
22
22
|
*
|
|
23
23
|
* @module
|
|
24
24
|
*/
|
|
25
25
|
// Primary parsing functionality
|
|
26
|
-
export { parse,
|
|
27
|
-
export {
|
|
26
|
+
export { parse, parseSourceFragment } from './parse.mjs';
|
|
27
|
+
export { deserializeNode, deserializeNodeFromString, deserializeNodes, deserializeNodesFromString, } from './deserialize.mjs';
|
|
28
28
|
export { parseFile, } from './parseFile.mjs';
|
|
29
29
|
// Core classes
|
|
30
30
|
export { ParseResult, } from './classes/ParseResult.mjs';
|
|
31
|
-
export {
|
|
32
|
-
export {
|
|
31
|
+
export { Node, assertNodeConstructor } from './classes/Node.mjs';
|
|
32
|
+
export { DatedNode } from './classes/DatedNode.mjs';
|
|
33
33
|
export { Value } from './classes/Value.mjs';
|
|
34
|
-
//
|
|
35
|
-
export { Balance } from './classes/
|
|
36
|
-
export { Blankline } from './classes/
|
|
37
|
-
export { Close } from './classes/
|
|
38
|
-
export { Comment } from './classes/
|
|
39
|
-
export { Commodity } from './classes/
|
|
40
|
-
export { Custom } from './classes/
|
|
41
|
-
export { Document } from './classes/
|
|
42
|
-
export { Event } from './classes/
|
|
43
|
-
export { Include } from './classes/
|
|
44
|
-
export { Note } from './classes/
|
|
45
|
-
export { Open } from './classes/
|
|
46
|
-
export { Option } from './classes/
|
|
47
|
-
export { Pad } from './classes/
|
|
48
|
-
export { Plugin } from './classes/
|
|
49
|
-
export { Poptag } from './classes/
|
|
50
|
-
export { Price } from './classes/
|
|
51
|
-
export { Pushtag } from './classes/
|
|
52
|
-
export { Query } from './classes/
|
|
53
|
-
export { Transaction, } from './classes/
|
|
34
|
+
// Node classes
|
|
35
|
+
export { Balance } from './classes/nodes/Balance.mjs';
|
|
36
|
+
export { Blankline } from './classes/nodes/Blankline.mjs';
|
|
37
|
+
export { Close } from './classes/nodes/Close.mjs';
|
|
38
|
+
export { Comment } from './classes/nodes/Comment.mjs';
|
|
39
|
+
export { Commodity } from './classes/nodes/Commodity.mjs';
|
|
40
|
+
export { Custom } from './classes/nodes/Custom.mjs';
|
|
41
|
+
export { Document } from './classes/nodes/Document.mjs';
|
|
42
|
+
export { Event } from './classes/nodes/Event.mjs';
|
|
43
|
+
export { Include } from './classes/nodes/Include.mjs';
|
|
44
|
+
export { Note } from './classes/nodes/Note.mjs';
|
|
45
|
+
export { Open } from './classes/nodes/Open.mjs';
|
|
46
|
+
export { Option } from './classes/nodes/Option.mjs';
|
|
47
|
+
export { Pad } from './classes/nodes/Pad.mjs';
|
|
48
|
+
export { Plugin } from './classes/nodes/Plugin.mjs';
|
|
49
|
+
export { Poptag } from './classes/nodes/Poptag.mjs';
|
|
50
|
+
export { Price } from './classes/nodes/Price.mjs';
|
|
51
|
+
export { Pushtag } from './classes/nodes/Pushtag.mjs';
|
|
52
|
+
export { Query } from './classes/nodes/Query.mjs';
|
|
53
|
+
export { Transaction, } from './classes/nodes/Transaction/index.mjs';
|
|
54
54
|
// Transaction sub-components
|
|
55
|
-
export { Posting } from './classes/
|
|
56
|
-
export { Tag } from './classes/
|
|
57
|
-
export {
|
|
58
|
-
export {
|
|
55
|
+
export { Posting } from './classes/nodes/Transaction/Posting.mjs';
|
|
56
|
+
export { Tag } from './classes/nodes/Transaction/Tag.mjs';
|
|
57
|
+
export { beancountDirectiveNodeTypeToClass, nodeTypeToClass, } from './nodeTypeToClass.mjs';
|
|
58
|
+
export { DATED_DIRECTIVE_TYPES, NON_DATED_DIRECTIVE_TYPES, } from './directiveTypes.mjs';
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Transaction, Balance, Close, Comment, Commodity, Custom, Document, Event, Include, Note, Open, Option, Pad, Plugin, Poptag, Price, Pushtag, Query, Blankline } from './classes/nodes/index.mjs';
|
|
2
|
+
import type { DATED_DIRECTIVE_TYPES, NON_DATED_DIRECTIVE_TYPES } from './directiveTypes.mjs';
|
|
3
|
+
/**
|
|
4
|
+
* Union type of all node types that correspond to a Beancount directive that includes a date field.
|
|
5
|
+
* Derived from {@link DATED_DIRECTIVE_TYPES}.
|
|
6
|
+
*/
|
|
7
|
+
export type DatedDirectiveNodeType = (typeof DATED_DIRECTIVE_TYPES)[number];
|
|
8
|
+
/**
|
|
9
|
+
* Union type of all node types that correspond to a Beancount directive type that does NOT include
|
|
10
|
+
* a date field.
|
|
11
|
+
* Derived from {@link NON_DATED_DIRECTIVE_TYPES}.
|
|
12
|
+
*/
|
|
13
|
+
export type NonDatedDirectiveNodeType = (typeof NON_DATED_DIRECTIVE_TYPES)[number];
|
|
14
|
+
/**
|
|
15
|
+
* Union type of all node type that corrospond to Beancount directives.
|
|
16
|
+
* Directive types derived from https://beancount.github.io/docs/beancount_language_syntax.html#directives-1
|
|
17
|
+
* Node can have additional synthetic types, see {@link SyntheticNodeType}
|
|
18
|
+
*
|
|
19
|
+
* Derived from {@link DATED_DIRECTIVE_TYPES} and {@link NON_DATED_DIRECTIVE_TYPES}.
|
|
20
|
+
*/
|
|
21
|
+
export type BeancountDirectiveNodeType = DatedDirectiveNodeType | NonDatedDirectiveNodeType;
|
|
22
|
+
/**
|
|
23
|
+
* Union type of synthetic node types that are not part of the official Beancount directive syntax.
|
|
24
|
+
*
|
|
25
|
+
* These node types are created during parsing to preserve the complete structure of a Beancount file,
|
|
26
|
+
* enabling lossless round-trip parsing
|
|
27
|
+
*/
|
|
28
|
+
export type SyntheticNodeType = 'comment' | 'blankline';
|
|
29
|
+
/**
|
|
30
|
+
* Union type of all valid node type names, that being the beancount directive types and the additional
|
|
31
|
+
* synthetic node types of comment and blankline
|
|
32
|
+
*/
|
|
33
|
+
export type NodeType = BeancountDirectiveNodeType | SyntheticNodeType;
|
|
34
|
+
/**
|
|
35
|
+
* Mapping of Beancount directive node type names to their corresponding class constructors.
|
|
36
|
+
* @internal
|
|
37
|
+
*/
|
|
38
|
+
export declare const beancountDirectiveNodeTypeToClass: {
|
|
39
|
+
transaction: typeof Transaction;
|
|
40
|
+
balance: typeof Balance;
|
|
41
|
+
close: typeof Close;
|
|
42
|
+
commodity: typeof Commodity;
|
|
43
|
+
custom: typeof Custom;
|
|
44
|
+
document: typeof Document;
|
|
45
|
+
event: typeof Event;
|
|
46
|
+
include: typeof Include;
|
|
47
|
+
note: typeof Note;
|
|
48
|
+
open: typeof Open;
|
|
49
|
+
option: typeof Option;
|
|
50
|
+
pad: typeof Pad;
|
|
51
|
+
plugin: typeof Plugin;
|
|
52
|
+
poptag: typeof Poptag;
|
|
53
|
+
price: typeof Price;
|
|
54
|
+
pushtag: typeof Pushtag;
|
|
55
|
+
query: typeof Query;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Mapping of all node type names (including synthetic) to their corresponding class constructors.
|
|
59
|
+
* @internal
|
|
60
|
+
*/
|
|
61
|
+
export declare const nodeTypeToClass: {
|
|
62
|
+
comment: typeof Comment;
|
|
63
|
+
blankline: typeof Blankline;
|
|
64
|
+
transaction: typeof Transaction;
|
|
65
|
+
balance: typeof Balance;
|
|
66
|
+
close: typeof Close;
|
|
67
|
+
commodity: typeof Commodity;
|
|
68
|
+
custom: typeof Custom;
|
|
69
|
+
document: typeof Document;
|
|
70
|
+
event: typeof Event;
|
|
71
|
+
include: typeof Include;
|
|
72
|
+
note: typeof Note;
|
|
73
|
+
open: typeof Open;
|
|
74
|
+
option: typeof Option;
|
|
75
|
+
pad: typeof Pad;
|
|
76
|
+
plugin: typeof Plugin;
|
|
77
|
+
poptag: typeof Poptag;
|
|
78
|
+
price: typeof Price;
|
|
79
|
+
pushtag: typeof Pushtag;
|
|
80
|
+
query: typeof Query;
|
|
81
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Transaction, Balance, Close, Comment, Commodity, Custom, Document, Event, Include, Note, Open, Option, Pad, Plugin, Poptag, Price, Pushtag, Query, Blankline, } from './classes/nodes/index.mjs';
|
|
2
|
+
/**
|
|
3
|
+
* Mapping of Beancount directive node type names to their corresponding class constructors.
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
6
|
+
export const beancountDirectiveNodeTypeToClass = {
|
|
7
|
+
transaction: Transaction,
|
|
8
|
+
balance: Balance,
|
|
9
|
+
close: Close,
|
|
10
|
+
commodity: Commodity,
|
|
11
|
+
custom: Custom,
|
|
12
|
+
document: Document,
|
|
13
|
+
event: Event,
|
|
14
|
+
include: Include,
|
|
15
|
+
note: Note,
|
|
16
|
+
open: Open,
|
|
17
|
+
option: Option,
|
|
18
|
+
pad: Pad,
|
|
19
|
+
plugin: Plugin,
|
|
20
|
+
poptag: Poptag,
|
|
21
|
+
price: Price,
|
|
22
|
+
pushtag: Pushtag,
|
|
23
|
+
query: Query,
|
|
24
|
+
};
|
|
25
|
+
// Compile-time assertion: beancountDirectiveNodeTypeToClass must have all BeancountDirectiveNodeType keys
|
|
26
|
+
beancountDirectiveNodeTypeToClass;
|
|
27
|
+
/**
|
|
28
|
+
* Mapping of all node type names (including synthetic) to their corresponding class constructors.
|
|
29
|
+
* @internal
|
|
30
|
+
*/
|
|
31
|
+
export const nodeTypeToClass = {
|
|
32
|
+
...beancountDirectiveNodeTypeToClass,
|
|
33
|
+
comment: Comment,
|
|
34
|
+
blankline: Blankline,
|
|
35
|
+
};
|
|
36
|
+
// Compile-time assertion: nodeTypeToClass must have all NodeType keys
|
|
37
|
+
nodeTypeToClass;
|
package/build/src/parse.d.mts
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
import { ParseResult } from './classes/ParseResult.mjs';
|
|
2
|
-
import { Comment, Blankline } from './classes/
|
|
2
|
+
import { Comment, Blankline } from './classes/nodes/index.mjs';
|
|
3
3
|
/**
|
|
4
|
-
* Parses a single
|
|
4
|
+
* Parses a single source fragment into its corresponding Node class instance.
|
|
5
5
|
*
|
|
6
6
|
* This function:
|
|
7
|
-
* - Performs generic parsing to determine
|
|
8
|
-
* - Instantiates the appropriate
|
|
7
|
+
* - Performs generic parsing to determine node type
|
|
8
|
+
* - Instantiates the appropriate Node subclass
|
|
9
9
|
* - Handles special cases for comments and blank lines
|
|
10
10
|
*
|
|
11
|
-
* @param
|
|
12
|
-
* @returns
|
|
11
|
+
* @param sourceFragment - Array of string tokens that should be parsed to a single node
|
|
12
|
+
* @returns A Node instance
|
|
13
13
|
*/
|
|
14
|
-
export declare const
|
|
14
|
+
export declare const parseSourceFragment: (sourceFragment: string[]) => import("./classes/nodes/index.mjs").Transaction | import("./classes/nodes/Balance.mjs").Balance | import("./classes/nodes/Close.mjs").Close | import("./classes/nodes/Commodity.mjs").Commodity | import("./classes/nodes/Custom.mjs").Custom | import("./classes/nodes/Document.mjs").Document | import("./classes/nodes/Event.mjs").Event | import("./classes/nodes/Include.mjs").Include | import("./classes/nodes/Note.mjs").Note | import("./classes/nodes/Open.mjs").Open | import("./classes/nodes/Option.mjs").Option | import("./classes/nodes/Pad.mjs").Pad | import("./classes/nodes/Plugin.mjs").Plugin | import("./classes/nodes/Poptag.mjs").Poptag | import("./classes/nodes/Price.mjs").Price | import("./classes/nodes/Pushtag.mjs").Pushtag | import("./classes/nodes/Query.mjs").Query | Comment | Blankline;
|
|
15
15
|
/**
|
|
16
|
-
* Parses a complete Beancount file string into a ParseResult containing
|
|
16
|
+
* Parses a complete Beancount file string into a ParseResult containing Node objects.
|
|
17
17
|
*
|
|
18
18
|
* This is the main entry point for parsing Beancount files. It handles:
|
|
19
|
-
* - Splitting the
|
|
20
|
-
* - Parsing each
|
|
21
|
-
* - Managing the tag stack for pushtag/poptag
|
|
19
|
+
* - Splitting the source into source fragment
|
|
20
|
+
* - Parsing each source fragment into its appropriate node
|
|
21
|
+
* - Managing the tag stack for pushtag/poptag nodes
|
|
22
22
|
* - Applying tags from the stack to transactions
|
|
23
23
|
*
|
|
24
24
|
* @remarks
|
|
25
25
|
* This is the primary function you'll use from this library. It takes a Beancount file
|
|
26
|
-
* as a string and returns a structured ParseResult object containing
|
|
26
|
+
* as a string and returns a structured ParseResult object containing the resulting parsed nodes.
|
|
27
27
|
*
|
|
28
28
|
* @example
|
|
29
29
|
* Basic usage:
|
|
@@ -38,10 +38,10 @@ export declare const parseEntry: (unparsedEntry: string[]) => import("./classes/
|
|
|
38
38
|
* `
|
|
39
39
|
*
|
|
40
40
|
* const result = parse(content)
|
|
41
|
-
* // result.
|
|
41
|
+
* // result.nodes contains parsed Node objects
|
|
42
42
|
* ```
|
|
43
43
|
*
|
|
44
|
-
* @param
|
|
45
|
-
* @returns A ParseResult instance containing all parsed
|
|
44
|
+
* @param source - The complete Beancount file content as a string
|
|
45
|
+
* @returns A ParseResult instance containing all parsed nodes
|
|
46
46
|
*/
|
|
47
|
-
export declare const parse: (
|
|
47
|
+
export declare const parse: (source: string) => ParseResult;
|
package/build/src/parse.mjs
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import { ParseResult } from './classes/ParseResult.mjs';
|
|
2
|
-
import { Comment, Blankline } from './classes/
|
|
2
|
+
import { Comment, Blankline } from './classes/nodes/index.mjs';
|
|
3
3
|
import { genericParse, } from './genericParse.mjs';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { beancountDirectiveNodeTypeToClass, } from './nodeTypeToClass.mjs';
|
|
5
|
+
import { splitStringIntoSourceFragments } from './utils/splitStringIntoSourceFragments.js';
|
|
6
6
|
/**
|
|
7
|
-
* Parses a single
|
|
7
|
+
* Parses a single source fragment into its corresponding Node class instance.
|
|
8
8
|
*
|
|
9
9
|
* This function:
|
|
10
|
-
* - Performs generic parsing to determine
|
|
11
|
-
* - Instantiates the appropriate
|
|
10
|
+
* - Performs generic parsing to determine node type
|
|
11
|
+
* - Instantiates the appropriate Node subclass
|
|
12
12
|
* - Handles special cases for comments and blank lines
|
|
13
13
|
*
|
|
14
|
-
* @param
|
|
15
|
-
* @returns
|
|
14
|
+
* @param sourceFragment - Array of string tokens that should be parsed to a single node
|
|
15
|
+
* @returns A Node instance
|
|
16
16
|
*/
|
|
17
|
-
export const
|
|
18
|
-
const genericParseResult = genericParse(
|
|
17
|
+
export const parseSourceFragment = (sourceFragment) => {
|
|
18
|
+
const genericParseResult = genericParse(sourceFragment);
|
|
19
19
|
const { type } = genericParseResult;
|
|
20
|
-
if (genericParseResult.
|
|
20
|
+
if (genericParseResult.synthetic) {
|
|
21
21
|
if (type === 'blankline') {
|
|
22
22
|
return Blankline.fromGenericParseResult(genericParseResult);
|
|
23
23
|
}
|
|
@@ -25,26 +25,26 @@ export const parseEntry = (unparsedEntry) => {
|
|
|
25
25
|
return Comment.fromGenericParseResult(genericParseResult);
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
-
const
|
|
29
|
-
if (
|
|
30
|
-
return
|
|
28
|
+
const NodeClass = beancountDirectiveNodeTypeToClass[type];
|
|
29
|
+
if (NodeClass) {
|
|
30
|
+
return NodeClass.fromGenericParseResult(genericParseResult);
|
|
31
31
|
}
|
|
32
32
|
else {
|
|
33
|
-
throw Error(`Could not parse ${
|
|
33
|
+
throw Error(`Could not parse ${sourceFragment.toString()}`);
|
|
34
34
|
}
|
|
35
35
|
};
|
|
36
36
|
/**
|
|
37
|
-
* Parses a complete Beancount file string into a ParseResult containing
|
|
37
|
+
* Parses a complete Beancount file string into a ParseResult containing Node objects.
|
|
38
38
|
*
|
|
39
39
|
* This is the main entry point for parsing Beancount files. It handles:
|
|
40
|
-
* - Splitting the
|
|
41
|
-
* - Parsing each
|
|
42
|
-
* - Managing the tag stack for pushtag/poptag
|
|
40
|
+
* - Splitting the source into source fragment
|
|
41
|
+
* - Parsing each source fragment into its appropriate node
|
|
42
|
+
* - Managing the tag stack for pushtag/poptag nodes
|
|
43
43
|
* - Applying tags from the stack to transactions
|
|
44
44
|
*
|
|
45
45
|
* @remarks
|
|
46
46
|
* This is the primary function you'll use from this library. It takes a Beancount file
|
|
47
|
-
* as a string and returns a structured ParseResult object containing
|
|
47
|
+
* as a string and returns a structured ParseResult object containing the resulting parsed nodes.
|
|
48
48
|
*
|
|
49
49
|
* @example
|
|
50
50
|
* Basic usage:
|
|
@@ -59,25 +59,25 @@ export const parseEntry = (unparsedEntry) => {
|
|
|
59
59
|
* `
|
|
60
60
|
*
|
|
61
61
|
* const result = parse(content)
|
|
62
|
-
* // result.
|
|
62
|
+
* // result.nodes contains parsed Node objects
|
|
63
63
|
* ```
|
|
64
64
|
*
|
|
65
|
-
* @param
|
|
66
|
-
* @returns A ParseResult instance containing all parsed
|
|
65
|
+
* @param source - The complete Beancount file content as a string
|
|
66
|
+
* @returns A ParseResult instance containing all parsed nodes
|
|
67
67
|
*/
|
|
68
|
-
export const parse = (
|
|
69
|
-
const
|
|
70
|
-
const
|
|
68
|
+
export const parse = (source) => {
|
|
69
|
+
const sourceFragments = splitStringIntoSourceFragments(source);
|
|
70
|
+
const nodes = [];
|
|
71
71
|
const tagStack = [];
|
|
72
|
-
for (const
|
|
73
|
-
const
|
|
74
|
-
if (
|
|
75
|
-
if (
|
|
76
|
-
tagStack.push(
|
|
72
|
+
for (const sourceFragment of sourceFragments) {
|
|
73
|
+
const node = parseSourceFragment(sourceFragment);
|
|
74
|
+
if (node) {
|
|
75
|
+
if (node.type === 'pushtag') {
|
|
76
|
+
tagStack.push(node.tag);
|
|
77
77
|
}
|
|
78
|
-
else if (
|
|
78
|
+
else if (node.type === 'poptag') {
|
|
79
79
|
// Find and remove the most recent matching tag from the stack
|
|
80
|
-
const tagToRemove =
|
|
80
|
+
const tagToRemove = node.tag.content;
|
|
81
81
|
for (let i = tagStack.length - 1; i >= 0; i--) {
|
|
82
82
|
if (tagStack[i].content === tagToRemove) {
|
|
83
83
|
tagStack.splice(i, 1);
|
|
@@ -85,11 +85,11 @@ export const parse = (input) => {
|
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
|
-
if (
|
|
89
|
-
|
|
88
|
+
if (node.type === 'transaction') {
|
|
89
|
+
node.tags.push(...tagStack);
|
|
90
90
|
}
|
|
91
|
-
|
|
91
|
+
nodes.push(node);
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
|
-
return new ParseResult(
|
|
94
|
+
return new ParseResult(nodes);
|
|
95
95
|
};
|
|
@@ -18,7 +18,7 @@ export type { FileSystemHelpers };
|
|
|
18
18
|
*
|
|
19
19
|
* @param filepath - Path to the Beancount file to parse
|
|
20
20
|
* @param options - Parsing options
|
|
21
|
-
* @returns A ParseResult containing all parsed
|
|
21
|
+
* @returns A ParseResult containing all parsed nodes
|
|
22
22
|
*
|
|
23
23
|
* @example
|
|
24
24
|
* Basic usage (Node.js):
|
|
@@ -32,7 +32,7 @@ export type { FileSystemHelpers };
|
|
|
32
32
|
* With recursive parsing of includes (Node.js):
|
|
33
33
|
* ```typescript
|
|
34
34
|
* const result = await parseFile('/path/to/main.beancount', { recurse: true })
|
|
35
|
-
* // All
|
|
35
|
+
* // All nodes from included files are merged into the result
|
|
36
36
|
* ```
|
|
37
37
|
*
|
|
38
38
|
* @example
|
package/build/src/parseFile.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { getDefaultFileSystemHelpers, } from './fileSystemHelpers.mjs';
|
|
|
6
6
|
*
|
|
7
7
|
* @param filepath - Path to the Beancount file to parse
|
|
8
8
|
* @param options - Parsing options
|
|
9
|
-
* @returns A ParseResult containing all parsed
|
|
9
|
+
* @returns A ParseResult containing all parsed nodes
|
|
10
10
|
*
|
|
11
11
|
* @example
|
|
12
12
|
* Basic usage (Node.js):
|
|
@@ -20,7 +20,7 @@ import { getDefaultFileSystemHelpers, } from './fileSystemHelpers.mjs';
|
|
|
20
20
|
* With recursive parsing of includes (Node.js):
|
|
21
21
|
* ```typescript
|
|
22
22
|
* const result = await parseFile('/path/to/main.beancount', { recurse: true })
|
|
23
|
-
* // All
|
|
23
|
+
* // All nodes from included files are merged into the result
|
|
24
24
|
* ```
|
|
25
25
|
*
|
|
26
26
|
* @example
|
|
@@ -86,7 +86,7 @@ export const parseFile = async (filepath, options = {}) => {
|
|
|
86
86
|
* @param filepath - Path to the Beancount file to parse
|
|
87
87
|
* @param visited - Set of already visited file paths (absolute)
|
|
88
88
|
* @param fsHelpers - File system helpers for reading files and handling paths
|
|
89
|
-
* @returns A ParseResult containing all parsed
|
|
89
|
+
* @returns A ParseResult containing all parsed nodes
|
|
90
90
|
*/
|
|
91
91
|
const parseFileRecursive = async (filepath, visited, fsHelpers) => {
|
|
92
92
|
const absolutePath = fsHelpers.resolvePath(filepath);
|
|
@@ -97,18 +97,18 @@ const parseFileRecursive = async (filepath, visited, fsHelpers) => {
|
|
|
97
97
|
visited.add(absolutePath);
|
|
98
98
|
const content = await fsHelpers.readFile(absolutePath);
|
|
99
99
|
const result = parse(content);
|
|
100
|
-
const
|
|
100
|
+
const allNodes = [];
|
|
101
101
|
const baseDir = fsHelpers.dirname(absolutePath);
|
|
102
|
-
for (const
|
|
103
|
-
if (
|
|
104
|
-
const
|
|
105
|
-
const includePath = fsHelpers.resolveRelative(baseDir,
|
|
102
|
+
for (const node of result.nodes) {
|
|
103
|
+
if (node.type === 'include') {
|
|
104
|
+
const includeNode = node;
|
|
105
|
+
const includePath = fsHelpers.resolveRelative(baseDir, includeNode.filename);
|
|
106
106
|
const includeResult = await parseFileRecursive(includePath, visited, fsHelpers);
|
|
107
|
-
|
|
107
|
+
allNodes.push(...includeResult.nodes);
|
|
108
108
|
}
|
|
109
109
|
else {
|
|
110
|
-
|
|
110
|
+
allNodes.push(node);
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
|
-
return new ParseResult(
|
|
113
|
+
return new ParseResult(allNodes);
|
|
114
114
|
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Splits a Beancount file string into an array of source fragments.
|
|
3
|
+
* Each fragment is represented as an array of strings.
|
|
4
|
+
*
|
|
5
|
+
* This function handles:
|
|
6
|
+
* - Splitting on blank lines between fragments
|
|
7
|
+
* - Detecting new fragments based on indentation
|
|
8
|
+
* - Preserving multi-line fragments
|
|
9
|
+
*
|
|
10
|
+
* @param source - The complete Beancount file content as a string
|
|
11
|
+
* @returns An array where each element is an array of strings representing one source fragment
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
export declare const splitStringIntoSourceFragments: (source: string) => string[][];
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { countChar } from './countChar.mjs';
|
|
2
|
+
import { stringAwareSplitLine } from './stringAwareSplitLine.mjs';
|
|
3
|
+
/**
|
|
4
|
+
* Splits a Beancount file string into an array of source fragments.
|
|
5
|
+
* Each fragment is represented as an array of strings.
|
|
6
|
+
*
|
|
7
|
+
* This function handles:
|
|
8
|
+
* - Splitting on blank lines between fragments
|
|
9
|
+
* - Detecting new fragments based on indentation
|
|
10
|
+
* - Preserving multi-line fragments
|
|
11
|
+
*
|
|
12
|
+
* @param source - The complete Beancount file content as a string
|
|
13
|
+
* @returns An array where each element is an array of strings representing one source fragment
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
export const splitStringIntoSourceFragments = (source) => {
|
|
17
|
+
const lines = source.split('\n');
|
|
18
|
+
// split up the file into source fragments
|
|
19
|
+
let inFragment = false;
|
|
20
|
+
let inString = false;
|
|
21
|
+
const fragments = lines.reduce((acc, line) => {
|
|
22
|
+
if (!inString) {
|
|
23
|
+
if (line.trim() === '') {
|
|
24
|
+
// empty newline, next fragment starts
|
|
25
|
+
inFragment = false;
|
|
26
|
+
}
|
|
27
|
+
if (!line.startsWith(' ') && inFragment) {
|
|
28
|
+
// no indent, new fragment
|
|
29
|
+
inFragment = false;
|
|
30
|
+
}
|
|
31
|
+
if (!inFragment) {
|
|
32
|
+
acc.push([]);
|
|
33
|
+
inFragment = true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
acc[acc.length - 1].push(line);
|
|
37
|
+
// After a blank line, ensure next line starts a new fragment
|
|
38
|
+
if (!inString && line.trim() === '') {
|
|
39
|
+
inFragment = false;
|
|
40
|
+
}
|
|
41
|
+
// odd number of ", we're in an unclosed string
|
|
42
|
+
if (countChar(line, '"') % 2 === 1) {
|
|
43
|
+
inString = !inString;
|
|
44
|
+
}
|
|
45
|
+
return acc;
|
|
46
|
+
}, []);
|
|
47
|
+
return fragments.map((lines) => stringAwareSplitLine(lines.join('\n')));
|
|
48
|
+
};
|