Split LabelToken to: HeaderToken & IntervalToken
This commit is contained in:
parent
d998caf91d
commit
955c74be42
|
|
@ -1,4 +1,4 @@
|
||||||
import { IntervalLabelTokenValue } from "./parser/tokenizer";
|
import { IntervalType } from "./parser/tokenizer";
|
||||||
import { Seconds } from "./types";
|
import { Seconds } from "./types";
|
||||||
|
|
||||||
export type Workout = {
|
export type Workout = {
|
||||||
|
|
@ -9,7 +9,7 @@ export type Workout = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Interval = {
|
export type Interval = {
|
||||||
type: IntervalLabelTokenValue;
|
type: IntervalType;
|
||||||
duration: Seconds;
|
duration: Seconds;
|
||||||
intensity: { from: number; to: number };
|
intensity: { from: number; to: number };
|
||||||
cadence?: number;
|
cadence?: number;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Interval, Workout, Comment } from "../ast";
|
import { Interval, Workout, Comment } from "../ast";
|
||||||
import { Seconds } from "../types";
|
import { Seconds } from "../types";
|
||||||
import { ParseError } from "./ParseError";
|
import { ParseError } from "./ParseError";
|
||||||
import { isIntervalLabelTokenValue, SourceLocation, Token } from "./tokenizer";
|
import { SourceLocation, Token } from "./tokenizer";
|
||||||
|
|
||||||
type Header = Partial<Omit<Workout, "intervals">>;
|
type Header = Partial<Omit<Workout, "intervals">>;
|
||||||
|
|
||||||
|
|
@ -30,17 +30,17 @@ const parseHeader = (tokens: Token[]): [Header, Token[]] => {
|
||||||
if (token.type === "text" && token.value === "") {
|
if (token.type === "text" && token.value === "") {
|
||||||
// Ignore empty lines before header
|
// Ignore empty lines before header
|
||||||
tokens.shift();
|
tokens.shift();
|
||||||
} else if (token.type === "label" && token.value === "Name") {
|
} else if (token.type === "header" && token.value === "Name") {
|
||||||
tokens.shift();
|
tokens.shift();
|
||||||
const [name, rest] = extractText(tokens);
|
const [name, rest] = extractText(tokens);
|
||||||
header.name = name;
|
header.name = name;
|
||||||
tokens = rest;
|
tokens = rest;
|
||||||
} else if (token.type === "label" && token.value === "Author") {
|
} else if (token.type === "header" && token.value === "Author") {
|
||||||
tokens.shift();
|
tokens.shift();
|
||||||
const [author, rest] = extractText(tokens);
|
const [author, rest] = extractText(tokens);
|
||||||
header.author = author;
|
header.author = author;
|
||||||
tokens = rest;
|
tokens = rest;
|
||||||
} else if (token.type === "label" && token.value === "Description") {
|
} else if (token.type === "header" && token.value === "Description") {
|
||||||
tokens.shift();
|
tokens.shift();
|
||||||
const [description, rest] = extractText(tokens);
|
const [description, rest] = extractText(tokens);
|
||||||
header.description = description;
|
header.description = description;
|
||||||
|
|
@ -125,7 +125,7 @@ const parseIntervals = (tokens: Token[]): Interval[] => {
|
||||||
|
|
||||||
while (tokens[0]) {
|
while (tokens[0]) {
|
||||||
const token = tokens.shift() as Token;
|
const token = tokens.shift() as Token;
|
||||||
if (token.type === "label" && isIntervalLabelTokenValue(token.value)) {
|
if (token.type === "interval") {
|
||||||
const [{ duration, intensity, cadence, comments }, rest] = parseIntervalParams(tokens, token.loc);
|
const [{ duration, intensity, cadence, comments }, rest] = parseIntervalParams(tokens, token.loc);
|
||||||
intervals.push({
|
intervals.push({
|
||||||
type: token.value,
|
type: token.value,
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,14 @@
|
||||||
import { ParseError } from "./ParseError";
|
import { ParseError } from "./ParseError";
|
||||||
|
|
||||||
export type HeaderLabelTokenValue = "Name" | "Author" | "Description";
|
export type HeaderType = "Name" | "Author" | "Description";
|
||||||
export type IntervalLabelTokenValue = "Warmup" | "Rest" | "Interval" | "Cooldown";
|
export type IntervalType = "Warmup" | "Rest" | "Interval" | "Cooldown";
|
||||||
export type LabelTokenValue = HeaderLabelTokenValue | IntervalLabelTokenValue;
|
|
||||||
|
|
||||||
export const isHeaderLabelTokenValue = (value: string): value is HeaderLabelTokenValue => {
|
const isHeaderType = (value: string): value is HeaderType => {
|
||||||
return ["Name", "Author", "Description"].includes(value);
|
return ["Name", "Author", "Description"].includes(value);
|
||||||
};
|
};
|
||||||
export const isIntervalLabelTokenValue = (value: string): value is IntervalLabelTokenValue => {
|
const isIntervalType = (value: string): value is IntervalType => {
|
||||||
return ["Warmup", "Rest", "Interval", "Cooldown"].includes(value);
|
return ["Warmup", "Rest", "Interval", "Cooldown"].includes(value);
|
||||||
};
|
};
|
||||||
export const isLabelTokenValue = (value: string): value is LabelTokenValue => {
|
|
||||||
return isHeaderLabelTokenValue(value) || isIntervalLabelTokenValue(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 0-based row and column indexes. First line is 0th.
|
// 0-based row and column indexes. First line is 0th.
|
||||||
export type SourceLocation = {
|
export type SourceLocation = {
|
||||||
|
|
@ -20,9 +16,14 @@ export type SourceLocation = {
|
||||||
col: number;
|
col: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LabelToken = {
|
export type HeaderToken = {
|
||||||
type: "label";
|
type: "header";
|
||||||
value: LabelTokenValue;
|
value: HeaderType;
|
||||||
|
loc: SourceLocation;
|
||||||
|
};
|
||||||
|
export type IntervalToken = {
|
||||||
|
type: "interval";
|
||||||
|
value: IntervalType;
|
||||||
loc: SourceLocation;
|
loc: SourceLocation;
|
||||||
};
|
};
|
||||||
export type TextToken = {
|
export type TextToken = {
|
||||||
|
|
@ -45,7 +46,7 @@ export type CommentStartToken = {
|
||||||
value?: undefined;
|
value?: undefined;
|
||||||
loc: SourceLocation;
|
loc: SourceLocation;
|
||||||
};
|
};
|
||||||
export type Token = LabelToken | TextToken | NumberToken | IntensityRangeToken | CommentStartToken;
|
export type Token = HeaderToken | IntervalToken | TextToken | NumberToken | IntensityRangeToken | CommentStartToken;
|
||||||
|
|
||||||
const toInteger = (str: string): number => {
|
const toInteger = (str: string): number => {
|
||||||
return parseInt(str.replace(/[^0-9]/, ""), 10);
|
return parseInt(str.replace(/[^0-9]/, ""), 10);
|
||||||
|
|
@ -87,21 +88,6 @@ const tokenizeParams = (text: string, loc: SourceLocation): Token[] => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const tokenizeLabelTokenParams = (type: LabelTokenValue, text: string, loc: SourceLocation): Token[] => {
|
|
||||||
switch (type) {
|
|
||||||
case "Name":
|
|
||||||
case "Author":
|
|
||||||
case "Description": {
|
|
||||||
return [{ type: "text", value: text, loc }];
|
|
||||||
}
|
|
||||||
case "Warmup":
|
|
||||||
case "Rest":
|
|
||||||
case "Interval":
|
|
||||||
case "Cooldown":
|
|
||||||
return tokenizeParams(text, loc);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const tokenizeComment = (line: string, row: number): Token[] | undefined => {
|
const tokenizeComment = (line: string, row: number): Token[] | undefined => {
|
||||||
const [, commentHead, offset, commentText] = line.match(/^(\s*#\s*)([0-9:]+)(.*?)$/) || [];
|
const [, commentHead, offset, commentText] = line.match(/^(\s*#\s*)([0-9:]+)(.*?)$/) || [];
|
||||||
if (!commentHead) {
|
if (!commentHead) {
|
||||||
|
|
@ -122,19 +108,38 @@ const tokenizeLabelToken = (line: string, row: number): Token[] | undefined => {
|
||||||
if (!label) {
|
if (!label) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
if (!isLabelTokenValue(label)) {
|
|
||||||
throw new ParseError(`Unknown label "${label}:"`, { row, col: 0 });
|
if (isHeaderType(label)) {
|
||||||
|
const token: HeaderToken = {
|
||||||
|
type: "header",
|
||||||
|
value: label,
|
||||||
|
loc: { row, col: 0 },
|
||||||
|
};
|
||||||
|
const param: TextToken = {
|
||||||
|
type: "text",
|
||||||
|
value: paramString,
|
||||||
|
loc: {
|
||||||
|
row,
|
||||||
|
col: label.length + separator.length,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return [token, param];
|
||||||
}
|
}
|
||||||
const labelToken: LabelToken = {
|
|
||||||
type: "label",
|
if (isIntervalType(label)) {
|
||||||
value: label as LabelTokenValue,
|
const token: IntervalToken = {
|
||||||
loc: { row, col: 0 },
|
type: "interval",
|
||||||
};
|
value: label,
|
||||||
const params = tokenizeLabelTokenParams(labelToken.value, paramString, {
|
loc: { row, col: 0 },
|
||||||
row,
|
};
|
||||||
col: label.length + separator.length,
|
const params = tokenizeParams(paramString, {
|
||||||
});
|
row,
|
||||||
return [labelToken, ...params];
|
col: label.length + separator.length,
|
||||||
|
});
|
||||||
|
return [token, ...params];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ParseError(`Unknown label "${label}:"`, { row, col: 0 });
|
||||||
};
|
};
|
||||||
|
|
||||||
const tokenizeRule = (line: string, row: number): Token[] => {
|
const tokenizeRule = (line: string, row: number): Token[] => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue