diff --git a/src/index.ts b/src/index.ts index 9954615..1e721ca 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ import * as fs from "fs"; +import { parse } from "./parser"; import { tokenizeFile } from "./tokenizer"; const filename = process.argv[2]; @@ -7,6 +8,6 @@ console.log(`Parsing: ${filename}`); const file = fs.readFileSync(filename, "utf8"); -tokenizeFile(file).forEach((token) => { - console.log(token.type, token.value); -}); +const workout = parse(tokenizeFile(file)); + +console.log(workout); diff --git a/src/parser.ts b/src/parser.ts new file mode 100644 index 0000000..c5d164d --- /dev/null +++ b/src/parser.ts @@ -0,0 +1,70 @@ +import { Interval, Workout } from "./ast"; +import { LabelTokenValue, Token } from "./tokenizer"; + +type Header = { + name?: string; + author?: string; + description?: string; +}; + +const extractText = (tokens: Token[]): [string, Token[]] => { + let text; + while (tokens[0] && tokens[0].type === "text") { + if (text === undefined) { + text = tokens[0].value; + } else { + text += "\n" + tokens[0].value; + } + tokens.shift(); + } + return [text || "", tokens]; +}; + +const parseHeader = (tokens: Token[]): [Header, Token[]] => { + const header: Header = {}; + + while (tokens[0]) { + const token = tokens[0]; + if (token.type === "label" && token.value === LabelTokenValue.Name) { + tokens.shift(); + const [name, rest] = extractText(tokens); + header.name = name; + tokens = rest; + } else if ( + token.type === "label" && + token.value === LabelTokenValue.Author + ) { + tokens.shift(); + const [author, rest] = extractText(tokens); + header.author = author; + tokens = rest; + } else if ( + token.type === "label" && + token.value === LabelTokenValue.Description + ) { + tokens.shift(); + const [description, rest] = extractText(tokens); + header.description = description; + tokens = rest; + } else { + // End of header + break; + } + } + + return [header, tokens]; +}; + +const parseIntervals = (tokens: Token[]): Interval[] => { + return []; +}; + +export const parse = (tokens: Token[]): Workout => { + const [header, intervalTokens] = parseHeader(tokens); + return { + name: header.name || "", + author: header.author || "", + description: header.description || "", + intervals: parseIntervals(intervalTokens), + }; +}; diff --git a/src/tokenizer.ts b/src/tokenizer.ts index 2d7f75f..6e2cb67 100644 --- a/src/tokenizer.ts +++ b/src/tokenizer.ts @@ -1,4 +1,4 @@ -enum LabelTokenValue { +export enum LabelTokenValue { Name = "Name", Author = "Author", Description = "Description", @@ -7,23 +7,23 @@ enum LabelTokenValue { Interval = "Interval", Cooldown = "Cooldown", } -type LabelToken = { +export type LabelToken = { type: "label"; value: LabelTokenValue; }; -type TextToken = { +export type TextToken = { type: "text"; value: string; }; -type NumberToken = { +export type NumberToken = { type: "power" | "cadence" | "duration"; value: number; }; -type PowerRangeToken = { +export type PowerRangeToken = { type: "power-range"; value: [number, number]; }; -type Token = LabelToken | TextToken | NumberToken | PowerRangeToken; +export type Token = LabelToken | TextToken | NumberToken | PowerRangeToken; const toInteger = (str: string): number => { return parseInt(str.replace(/[^0-9]/, ""), 10);