@yozora/tokenizer-footnote-definition 2.0.4 → 2.0.5-alpha.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/lib/cjs/{index.js → index.cjs} +6 -7
- package/lib/esm/{index.js → index.mjs} +5 -6
- package/lib/types/index.d.ts +3 -3
- package/package.json +18 -14
- package/src/index.ts +10 -0
- package/src/match.ts +115 -0
- package/src/parse.ts +31 -0
- package/src/tokenizer.ts +37 -0
- package/src/types.ts +37 -0
- package/src/util.ts +58 -0
|
@@ -120,20 +120,19 @@ const uniqueName = '@yozora/tokenizer-footnote-definition';
|
|
|
120
120
|
|
|
121
121
|
class FootnoteDefinitionTokenizer extends coreTokenizer.BaseBlockTokenizer {
|
|
122
122
|
constructor(props = {}) {
|
|
123
|
-
var _a, _b;
|
|
124
123
|
super({
|
|
125
|
-
name:
|
|
126
|
-
priority:
|
|
124
|
+
name: props.name ?? uniqueName,
|
|
125
|
+
priority: props.priority ?? coreTokenizer.TokenizerPriority.CONTAINING_BLOCK,
|
|
127
126
|
});
|
|
128
|
-
this.indent = 4;
|
|
129
|
-
this.match = match;
|
|
130
|
-
this.parse = parse;
|
|
131
127
|
}
|
|
128
|
+
indent = 4;
|
|
129
|
+
match = match;
|
|
130
|
+
parse = parse;
|
|
132
131
|
}
|
|
133
132
|
|
|
134
133
|
exports.FootnoteDefinitionTokenizer = FootnoteDefinitionTokenizer;
|
|
135
134
|
exports.FootnoteDefinitionTokenizerName = uniqueName;
|
|
136
|
-
exports
|
|
135
|
+
exports.default = FootnoteDefinitionTokenizer;
|
|
137
136
|
exports.eatFootnoteLabel = eatFootnoteLabel;
|
|
138
137
|
exports.footnoteDefinitionMatch = match;
|
|
139
138
|
exports.footnoteDefinitionParse = parse;
|
|
@@ -116,15 +116,14 @@ const uniqueName = '@yozora/tokenizer-footnote-definition';
|
|
|
116
116
|
|
|
117
117
|
class FootnoteDefinitionTokenizer extends BaseBlockTokenizer {
|
|
118
118
|
constructor(props = {}) {
|
|
119
|
-
var _a, _b;
|
|
120
119
|
super({
|
|
121
|
-
name:
|
|
122
|
-
priority:
|
|
120
|
+
name: props.name ?? uniqueName,
|
|
121
|
+
priority: props.priority ?? TokenizerPriority.CONTAINING_BLOCK,
|
|
123
122
|
});
|
|
124
|
-
this.indent = 4;
|
|
125
|
-
this.match = match;
|
|
126
|
-
this.parse = parse;
|
|
127
123
|
}
|
|
124
|
+
indent = 4;
|
|
125
|
+
match = match;
|
|
126
|
+
parse = parse;
|
|
128
127
|
}
|
|
129
128
|
|
|
130
129
|
export { FootnoteDefinitionTokenizer, uniqueName as FootnoteDefinitionTokenizerName, FootnoteDefinitionTokenizer as default, eatFootnoteLabel, match as footnoteDefinitionMatch, parse as footnoteDefinitionParse };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -20,8 +20,8 @@ import { FootnoteDefinitionType, FootnoteDefinition } from '@yozora/ast';
|
|
|
20
20
|
*/
|
|
21
21
|
declare function eatFootnoteLabel(nodePoints: ReadonlyArray<INodePoint>, firstNonWhitespaceIndex: number, endIndex: number): number;
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
type T = FootnoteDefinitionType;
|
|
24
|
+
type INode = FootnoteDefinition;
|
|
25
25
|
declare const uniqueName = "@yozora/tokenizer-footnote-definition";
|
|
26
26
|
interface IToken extends IPartialYastBlockToken<T> {
|
|
27
27
|
/**
|
|
@@ -46,7 +46,7 @@ interface IToken extends IPartialYastBlockToken<T> {
|
|
|
46
46
|
interface IThis extends ITokenizer {
|
|
47
47
|
indent: number;
|
|
48
48
|
}
|
|
49
|
-
|
|
49
|
+
type ITokenizerProps = Partial<IBaseBlockTokenizerProps>;
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* A footnote reference definition consists of a footnote label, indented up
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yozora/tokenizer-footnote-definition",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5-alpha.0",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "guanghechen",
|
|
6
6
|
"url": "https://github.com/guanghechen/"
|
|
@@ -11,33 +11,37 @@
|
|
|
11
11
|
"directory": "tokenizers/footnote-definition"
|
|
12
12
|
},
|
|
13
13
|
"homepage": "https://github.com/yozorajs/yozora/tree/release-2.x.x/tokenizers/footnote-definition",
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
"type": "module",
|
|
15
|
+
"exports": {
|
|
16
|
+
"types": "./lib/types/index.d.ts",
|
|
17
|
+
"import": "./lib/esm/index.mjs",
|
|
18
|
+
"require": "./lib/cjs/index.cjs"
|
|
19
|
+
},
|
|
20
|
+
"source": "./src/index.ts",
|
|
21
|
+
"types": "./lib/types/index.d.ts",
|
|
22
|
+
"main": "./lib/cjs/index.cjs",
|
|
23
|
+
"module": "./lib/esm/index.mjs",
|
|
18
24
|
"license": "MIT",
|
|
19
25
|
"engines": {
|
|
20
26
|
"node": ">= 16.0.0"
|
|
21
27
|
},
|
|
22
28
|
"files": [
|
|
23
29
|
"lib/",
|
|
24
|
-
"
|
|
25
|
-
"!lib/**/*.d.ts.map",
|
|
30
|
+
"src/",
|
|
26
31
|
"package.json",
|
|
27
32
|
"CHANGELOG.md",
|
|
28
33
|
"LICENSE",
|
|
29
34
|
"README.md"
|
|
30
35
|
],
|
|
31
36
|
"scripts": {
|
|
32
|
-
"build": "cross-env NODE_ENV=production rollup -c ../../rollup.config.
|
|
33
|
-
"prebuild": "rimraf lib/",
|
|
37
|
+
"build": "rimraf lib/ && cross-env NODE_ENV=production rollup -c ../../rollup.config.mjs",
|
|
34
38
|
"prepublishOnly": "cross-env ROLLUP_SHOULD_SOURCEMAP=false yarn build",
|
|
35
|
-
"test": "cross-env TS_NODE_FILES=true jest --config ../../jest.config.
|
|
39
|
+
"test": "cross-env TS_NODE_FILES=true NODE_OPTIONS=--experimental-vm-modules jest --config ../../jest.config.mjs --rootDir ."
|
|
36
40
|
},
|
|
37
41
|
"dependencies": {
|
|
38
|
-
"@yozora/ast": "^2.0.
|
|
39
|
-
"@yozora/character": "^2.0.
|
|
40
|
-
"@yozora/core-tokenizer": "^2.0.
|
|
42
|
+
"@yozora/ast": "^2.0.5-alpha.0",
|
|
43
|
+
"@yozora/character": "^2.0.5-alpha.0",
|
|
44
|
+
"@yozora/core-tokenizer": "^2.0.5-alpha.0"
|
|
41
45
|
},
|
|
42
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "8bf941fe4ef82947165b0f3cc123cd493665e13b"
|
|
43
47
|
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './util'
|
|
2
|
+
export { match as footnoteDefinitionMatch } from './match'
|
|
3
|
+
export { parse as footnoteDefinitionParse } from './parse'
|
|
4
|
+
export { FootnoteDefinitionTokenizer, FootnoteDefinitionTokenizer as default } from './tokenizer'
|
|
5
|
+
export { uniqueName as FootnoteDefinitionTokenizerName } from './types'
|
|
6
|
+
export type {
|
|
7
|
+
IThis as IFootnoteDefinitionHookContext,
|
|
8
|
+
IToken as IFootnoteDefinitionToken,
|
|
9
|
+
ITokenizerProps as IFootnoteDefinitionTokenizerProps,
|
|
10
|
+
} from './types'
|
package/src/match.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { FootnoteDefinitionType } from '@yozora/ast'
|
|
2
|
+
import { AsciiCodePoint, calcStringFromNodePoints } from '@yozora/character'
|
|
3
|
+
import type {
|
|
4
|
+
IMatchBlockHookCreator,
|
|
5
|
+
IPhrasingContentLine,
|
|
6
|
+
IResultOfEatContinuationText,
|
|
7
|
+
IResultOfEatOpener,
|
|
8
|
+
IResultOfOnClose,
|
|
9
|
+
} from '@yozora/core-tokenizer'
|
|
10
|
+
import { calcEndPoint, calcStartPoint, resolveLabelToIdentifier } from '@yozora/core-tokenizer'
|
|
11
|
+
import type { IThis, IToken, T } from './types'
|
|
12
|
+
import { eatFootnoteLabel } from './util'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A footnote reference definition consists of a footnote label, indented up
|
|
16
|
+
* to three spaces, followed by a colon (:), optional whitespace (including up
|
|
17
|
+
* to one line ending), a footnote contents consisted by paragraph-like strings.
|
|
18
|
+
*
|
|
19
|
+
* Unlike the link label, the footnote label should be on the same line and it
|
|
20
|
+
* begins with a left bracket ([) followed by a caret (^), and ends with the
|
|
21
|
+
* first right bracket (]) that is not backslash-escaped. Between the caret of
|
|
22
|
+
* right bracket, there must be at least one non-whitespace character.
|
|
23
|
+
* Unescaped square bracket characters are not allowed inside the opening creat
|
|
24
|
+
* and closing square bracket of footnote labels. A footnote label can have at
|
|
25
|
+
* most 999 characters inside the caret and right bracket.
|
|
26
|
+
*
|
|
27
|
+
* @see https://github.github.com/gfm/#link-label
|
|
28
|
+
* @see https://github.github.com/gfm/#link-reference-definition
|
|
29
|
+
* @see https://github.com/syntax-tree/mdast-util-footnote
|
|
30
|
+
* @see https://github.com/remarkjs/remark-footnotes
|
|
31
|
+
* @see https://www.markdownguide.org/extended-syntax/#footnotes
|
|
32
|
+
*/
|
|
33
|
+
export const match: IMatchBlockHookCreator<T, IToken, IThis> = function (api) {
|
|
34
|
+
const { indent } = this
|
|
35
|
+
return {
|
|
36
|
+
isContainingBlock: true,
|
|
37
|
+
eatOpener,
|
|
38
|
+
eatContinuationText,
|
|
39
|
+
onClose,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function eatOpener(line: Readonly<IPhrasingContentLine>): IResultOfEatOpener<T, IToken> {
|
|
43
|
+
if (line.countOfPrecedeSpaces >= 4) return null
|
|
44
|
+
|
|
45
|
+
const { nodePoints, startIndex, firstNonWhitespaceIndex, endIndex } = line
|
|
46
|
+
const nextIndex = eatFootnoteLabel(nodePoints, firstNonWhitespaceIndex, endIndex)
|
|
47
|
+
|
|
48
|
+
// Try to match the following colon (:).
|
|
49
|
+
if (
|
|
50
|
+
nextIndex < 0 ||
|
|
51
|
+
nextIndex >= endIndex ||
|
|
52
|
+
nodePoints[nextIndex].codePoint !== AsciiCodePoint.COLON
|
|
53
|
+
) {
|
|
54
|
+
return null
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const token: IToken = {
|
|
58
|
+
nodeType: FootnoteDefinitionType,
|
|
59
|
+
position: {
|
|
60
|
+
start: calcStartPoint(nodePoints, startIndex),
|
|
61
|
+
end: calcEndPoint(nodePoints, nextIndex),
|
|
62
|
+
},
|
|
63
|
+
label: {
|
|
64
|
+
nodePoints,
|
|
65
|
+
startIndex: firstNonWhitespaceIndex,
|
|
66
|
+
endIndex: nextIndex,
|
|
67
|
+
},
|
|
68
|
+
children: [],
|
|
69
|
+
}
|
|
70
|
+
return { token, nextIndex: nextIndex + 1 }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function eatContinuationText(line: Readonly<IPhrasingContentLine>): IResultOfEatContinuationText {
|
|
74
|
+
const { startIndex, endIndex, firstNonWhitespaceIndex, countOfPrecedeSpaces } = line
|
|
75
|
+
|
|
76
|
+
// Blank line is allowed
|
|
77
|
+
if (firstNonWhitespaceIndex >= endIndex) {
|
|
78
|
+
return {
|
|
79
|
+
status: 'opening',
|
|
80
|
+
nextIndex: Math.min(endIndex - 1, startIndex + indent),
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Indent of a non-blank line is required.
|
|
85
|
+
if (countOfPrecedeSpaces >= indent) {
|
|
86
|
+
return { status: 'opening', nextIndex: startIndex + indent }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return { status: 'notMatched' }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function onClose(token: IToken): IResultOfOnClose {
|
|
93
|
+
/**
|
|
94
|
+
* Labels are trimmed and case-insensitive
|
|
95
|
+
* @see https://github.github.com/gfm/#example-174
|
|
96
|
+
* @see https://github.github.com/gfm/#example-175
|
|
97
|
+
*/
|
|
98
|
+
const label = calcStringFromNodePoints(
|
|
99
|
+
token.label.nodePoints,
|
|
100
|
+
token.label.startIndex + 2,
|
|
101
|
+
token.label.endIndex - 1,
|
|
102
|
+
)
|
|
103
|
+
const identifier = resolveLabelToIdentifier(label)
|
|
104
|
+
|
|
105
|
+
// Register definition identifier.
|
|
106
|
+
api.registerFootnoteDefinitionIdentifier(identifier)
|
|
107
|
+
|
|
108
|
+
// Cache label and identifier for performance.
|
|
109
|
+
|
|
110
|
+
// eslint-disable-next-line no-param-reassign
|
|
111
|
+
token._label = label
|
|
112
|
+
// eslint-disable-next-line no-param-reassign
|
|
113
|
+
token._identifier = identifier
|
|
114
|
+
}
|
|
115
|
+
}
|
package/src/parse.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Node } from '@yozora/ast'
|
|
2
|
+
import { FootnoteDefinitionType } from '@yozora/ast'
|
|
3
|
+
import type { IParseBlockHookCreator } from '@yozora/core-tokenizer'
|
|
4
|
+
import type { INode, IThis, IToken, T } from './types'
|
|
5
|
+
|
|
6
|
+
export const parse: IParseBlockHookCreator<T, IToken, INode, IThis> = function (api) {
|
|
7
|
+
return {
|
|
8
|
+
parse: tokens =>
|
|
9
|
+
tokens.map(token => {
|
|
10
|
+
const label: string = token._label!
|
|
11
|
+
const identifier: string = token._identifier!
|
|
12
|
+
|
|
13
|
+
const children: Node[] = api.parseBlockTokens(token.children)
|
|
14
|
+
const node: INode = api.shouldReservePosition
|
|
15
|
+
? {
|
|
16
|
+
type: FootnoteDefinitionType,
|
|
17
|
+
position: token.position,
|
|
18
|
+
identifier,
|
|
19
|
+
label,
|
|
20
|
+
children,
|
|
21
|
+
}
|
|
22
|
+
: {
|
|
23
|
+
type: FootnoteDefinitionType,
|
|
24
|
+
identifier,
|
|
25
|
+
label,
|
|
26
|
+
children,
|
|
27
|
+
}
|
|
28
|
+
return node
|
|
29
|
+
}),
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/tokenizer.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IBlockTokenizer,
|
|
3
|
+
IMatchBlockHookCreator,
|
|
4
|
+
IParseBlockHookCreator,
|
|
5
|
+
} from '@yozora/core-tokenizer'
|
|
6
|
+
import { BaseBlockTokenizer, TokenizerPriority } from '@yozora/core-tokenizer'
|
|
7
|
+
import { match } from './match'
|
|
8
|
+
import { parse } from './parse'
|
|
9
|
+
import type { INode, IThis, IToken, ITokenizerProps, T } from './types'
|
|
10
|
+
import { uniqueName } from './types'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Lexical Analyzer for FootnoteDefinition.
|
|
14
|
+
* @see https://github.github.com/gfm/#link-label
|
|
15
|
+
* @see https://github.github.com/gfm/#link-reference-definition
|
|
16
|
+
* @see https://github.com/syntax-tree/mdast-util-footnote
|
|
17
|
+
* @see https://github.com/remarkjs/remark-footnotes
|
|
18
|
+
* @see https://www.markdownguide.org/extended-syntax/#footnotes
|
|
19
|
+
*/
|
|
20
|
+
export class FootnoteDefinitionTokenizer
|
|
21
|
+
extends BaseBlockTokenizer<T, IToken, INode, IThis>
|
|
22
|
+
implements IBlockTokenizer<T, IToken, INode, IThis>
|
|
23
|
+
{
|
|
24
|
+
/* istanbul ignore next */
|
|
25
|
+
constructor(props: ITokenizerProps = {}) {
|
|
26
|
+
super({
|
|
27
|
+
name: props.name ?? uniqueName,
|
|
28
|
+
priority: props.priority ?? TokenizerPriority.CONTAINING_BLOCK,
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public readonly indent = 4
|
|
33
|
+
|
|
34
|
+
public override readonly match: IMatchBlockHookCreator<T, IToken, IThis> = match
|
|
35
|
+
|
|
36
|
+
public override readonly parse: IParseBlockHookCreator<T, IToken, INode, IThis> = parse
|
|
37
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { FootnoteDefinition, FootnoteDefinitionType } from '@yozora/ast'
|
|
2
|
+
import type { INodeInterval, INodePoint } from '@yozora/character'
|
|
3
|
+
import type {
|
|
4
|
+
IBaseBlockTokenizerProps,
|
|
5
|
+
IPartialYastBlockToken,
|
|
6
|
+
ITokenizer,
|
|
7
|
+
IYastBlockToken,
|
|
8
|
+
} from '@yozora/core-tokenizer'
|
|
9
|
+
|
|
10
|
+
export type T = FootnoteDefinitionType
|
|
11
|
+
export type INode = FootnoteDefinition
|
|
12
|
+
export const uniqueName = '@yozora/tokenizer-footnote-definition'
|
|
13
|
+
|
|
14
|
+
export interface IToken extends IPartialYastBlockToken<T> {
|
|
15
|
+
/**
|
|
16
|
+
* Footnote label
|
|
17
|
+
*/
|
|
18
|
+
label: INodeInterval & { nodePoints: ReadonlyArray<INodePoint> }
|
|
19
|
+
/**
|
|
20
|
+
*
|
|
21
|
+
*/
|
|
22
|
+
children: IYastBlockToken[]
|
|
23
|
+
/**
|
|
24
|
+
* Resolved definition label.
|
|
25
|
+
*/
|
|
26
|
+
_label?: string
|
|
27
|
+
/**
|
|
28
|
+
* Resolved definition identifier.
|
|
29
|
+
*/
|
|
30
|
+
_identifier?: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface IThis extends ITokenizer {
|
|
34
|
+
indent: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type ITokenizerProps = Partial<IBaseBlockTokenizerProps>
|
package/src/util.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { INodePoint } from '@yozora/character'
|
|
2
|
+
import { AsciiCodePoint, VirtualCodePoint, isWhitespaceCharacter } from '@yozora/character'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Try to match a footnote label.
|
|
6
|
+
*
|
|
7
|
+
* Unlike the link label, the footnote label should be on the same line and it
|
|
8
|
+
* begins with a left bracket ([) followed by a caret (^), and ends with the
|
|
9
|
+
* first right bracket (]) that is not backslash-escaped. Between the caret of
|
|
10
|
+
* right bracket, there must be at least one non-whitespace character.
|
|
11
|
+
* Unescaped square bracket characters are not allowed inside the opening creat
|
|
12
|
+
* and closing square bracket of footnote labels. A footnote label can have at
|
|
13
|
+
* most 999 characters inside the caret and right bracket.
|
|
14
|
+
*
|
|
15
|
+
* @param nodePoints
|
|
16
|
+
* @param firstNonWhitespaceIndex
|
|
17
|
+
* @param endIndex
|
|
18
|
+
* @see https://github.github.com/gfm/#link-label
|
|
19
|
+
*/
|
|
20
|
+
export function eatFootnoteLabel(
|
|
21
|
+
nodePoints: ReadonlyArray<INodePoint>,
|
|
22
|
+
firstNonWhitespaceIndex: number,
|
|
23
|
+
endIndex: number,
|
|
24
|
+
): number {
|
|
25
|
+
let i = firstNonWhitespaceIndex
|
|
26
|
+
|
|
27
|
+
// Try to match an opening delimiter of a footnote label.
|
|
28
|
+
if (
|
|
29
|
+
i + 1 >= endIndex ||
|
|
30
|
+
nodePoints[i].codePoint !== AsciiCodePoint.OPEN_BRACKET ||
|
|
31
|
+
nodePoints[i + 1].codePoint !== AsciiCodePoint.CARET
|
|
32
|
+
) {
|
|
33
|
+
return -1
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let isEmpty = true
|
|
37
|
+
const lastIndex = Math.min(endIndex, i + 1 + 1000)
|
|
38
|
+
for (i += 2; i < lastIndex; ++i) {
|
|
39
|
+
const c = nodePoints[i].codePoint
|
|
40
|
+
switch (c) {
|
|
41
|
+
case AsciiCodePoint.BACKSLASH:
|
|
42
|
+
i += 1
|
|
43
|
+
break
|
|
44
|
+
case AsciiCodePoint.OPEN_BRACKET:
|
|
45
|
+
return -1
|
|
46
|
+
case AsciiCodePoint.CLOSE_BRACKET:
|
|
47
|
+
return isEmpty ? -1 : i + 1
|
|
48
|
+
case VirtualCodePoint.LINE_END:
|
|
49
|
+
return -1
|
|
50
|
+
default:
|
|
51
|
+
if (isEmpty && !isWhitespaceCharacter(c)) {
|
|
52
|
+
isEmpty = false
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return -1
|
|
58
|
+
}
|