From 955c74be42668f7a9408dc7be9cee6ad6e07b7e2 Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Tue, 22 Sep 2020 21:51:58 +0300 Subject: [PATCH] Split LabelToken to: HeaderToken & IntervalToken --- src/ast.ts | 4 +- src/parser/parser.ts | 10 ++--- src/parser/tokenizer.ts | 83 ++++++++++++++++++++++------------------- 3 files changed, 51 insertions(+), 46 deletions(-) diff --git a/src/ast.ts b/src/ast.ts index 1731bff..a41f8de 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -1,4 +1,4 @@ -import { IntervalLabelTokenValue } from "./parser/tokenizer"; +import { IntervalType } from "./parser/tokenizer"; import { Seconds } from "./types"; export type Workout = { @@ -9,7 +9,7 @@ export type Workout = { }; export type Interval = { - type: IntervalLabelTokenValue; + type: IntervalType; duration: Seconds; intensity: { from: number; to: number }; cadence?: number; diff --git a/src/parser/parser.ts b/src/parser/parser.ts index 1580edf..c9768a4 100644 --- a/src/parser/parser.ts +++ b/src/parser/parser.ts @@ -1,7 +1,7 @@ import { Interval, Workout, Comment } from "../ast"; import { Seconds } from "../types"; import { ParseError } from "./ParseError"; -import { isIntervalLabelTokenValue, SourceLocation, Token } from "./tokenizer"; +import { SourceLocation, Token } from "./tokenizer"; type Header = Partial>; @@ -30,17 +30,17 @@ const parseHeader = (tokens: Token[]): [Header, Token[]] => { if (token.type === "text" && token.value === "") { // Ignore empty lines before header tokens.shift(); - } else if (token.type === "label" && token.value === "Name") { + } else if (token.type === "header" && token.value === "Name") { tokens.shift(); const [name, rest] = extractText(tokens); header.name = name; tokens = rest; - } else if (token.type === "label" && token.value === "Author") { + } else if (token.type === "header" && token.value === "Author") { tokens.shift(); const [author, rest] = extractText(tokens); header.author = author; tokens = rest; - } else if (token.type === "label" && token.value === "Description") { + } else if (token.type === "header" && token.value === "Description") { tokens.shift(); const [description, rest] = extractText(tokens); header.description = description; @@ -125,7 +125,7 @@ const parseIntervals = (tokens: Token[]): Interval[] => { while (tokens[0]) { 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); intervals.push({ type: token.value, diff --git a/src/parser/tokenizer.ts b/src/parser/tokenizer.ts index f29f4ac..a4287ad 100644 --- a/src/parser/tokenizer.ts +++ b/src/parser/tokenizer.ts @@ -1,18 +1,14 @@ import { ParseError } from "./ParseError"; -export type HeaderLabelTokenValue = "Name" | "Author" | "Description"; -export type IntervalLabelTokenValue = "Warmup" | "Rest" | "Interval" | "Cooldown"; -export type LabelTokenValue = HeaderLabelTokenValue | IntervalLabelTokenValue; +export type HeaderType = "Name" | "Author" | "Description"; +export type IntervalType = "Warmup" | "Rest" | "Interval" | "Cooldown"; -export const isHeaderLabelTokenValue = (value: string): value is HeaderLabelTokenValue => { +const isHeaderType = (value: string): value is HeaderType => { 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); }; -export const isLabelTokenValue = (value: string): value is LabelTokenValue => { - return isHeaderLabelTokenValue(value) || isIntervalLabelTokenValue(value); -}; // 0-based row and column indexes. First line is 0th. export type SourceLocation = { @@ -20,9 +16,14 @@ export type SourceLocation = { col: number; }; -export type LabelToken = { - type: "label"; - value: LabelTokenValue; +export type HeaderToken = { + type: "header"; + value: HeaderType; + loc: SourceLocation; +}; +export type IntervalToken = { + type: "interval"; + value: IntervalType; loc: SourceLocation; }; export type TextToken = { @@ -45,7 +46,7 @@ export type CommentStartToken = { value?: undefined; loc: SourceLocation; }; -export type Token = LabelToken | TextToken | NumberToken | IntensityRangeToken | CommentStartToken; +export type Token = HeaderToken | IntervalToken | TextToken | NumberToken | IntensityRangeToken | CommentStartToken; const toInteger = (str: string): number => { 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 [, commentHead, offset, commentText] = line.match(/^(\s*#\s*)([0-9:]+)(.*?)$/) || []; if (!commentHead) { @@ -122,19 +108,38 @@ const tokenizeLabelToken = (line: string, row: number): Token[] | undefined => { if (!label) { 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", - value: label as LabelTokenValue, - loc: { row, col: 0 }, - }; - const params = tokenizeLabelTokenParams(labelToken.value, paramString, { - row, - col: label.length + separator.length, - }); - return [labelToken, ...params]; + + if (isIntervalType(label)) { + const token: IntervalToken = { + type: "interval", + value: label, + loc: { row, col: 0 }, + }; + const params = tokenizeParams(paramString, { + row, + col: label.length + separator.length, + }); + return [token, ...params]; + } + + throw new ParseError(`Unknown label "${label}:"`, { row, col: 0 }); }; const tokenizeRule = (line: string, row: number): Token[] => {