Skip to content

Commit

Permalink
🥅 Handle errors for tokenizing process
Browse files Browse the repository at this point in the history
  • Loading branch information
fTrestour committed Jan 29, 2024
1 parent 8656bff commit 2df4be3
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 26 deletions.
9 changes: 9 additions & 0 deletions src/domain/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class LexicalError extends Error {
constructor(message: string, public position: number) {
super(message);
}

public toString() {
return `${this.message} at index ${this.position}`;
}
}
10 changes: 8 additions & 2 deletions src/domain/tokenize.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Result, err, ok } from "neverthrow";
import { LexicalError } from "./errors";

type TokenType =
| "NUMBER"
Expand Down Expand Up @@ -48,7 +49,10 @@ export interface Token {
endIndex: number;
}

export function tokenize(code: string, startIndex = 0): Result<Token[], Error> {
export function tokenize(
code: string,
startIndex = 0
): Result<Token[], LexicalError> {
if (code.length === 0) {
return ok([{ type: "EOF", value: "", startIndex, endIndex: startIndex }]);
}
Expand Down Expand Up @@ -306,7 +310,9 @@ export function tokenize(code: string, startIndex = 0): Result<Token[], Error> {
newToken = null;
restIndex += 1;
} else {
return err(new Error("Unexpected character: " + char));
return err(
new LexicalError(`Unexpected character "${char}"`, startIndex)
);
}
}

Expand Down
27 changes: 16 additions & 11 deletions src/web/components/CodeViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,23 @@ export function CodeViewer(
class?: string;
}>
) {
let previousBreakpoint = 0;
let spans = new Array<{ value: string; id: number | null }>();
for (const token of props.tokens) {
spans.push({
value: props.source.slice(previousBreakpoint, token.startIndex),
id: null,
});
spans.push({
value: props.source.slice(token.startIndex, token.endIndex),
id: token.startIndex,
});
previousBreakpoint = token.endIndex;

if (props.tokens.length === 0) {
spans.push({ value: props.source, id: null });
} else {
let previousBreakpoint = 0;
for (const token of props.tokens) {
spans.push({
value: props.source.slice(previousBreakpoint, token.startIndex),
id: null,
});
spans.push({
value: props.source.slice(token.startIndex, token.endIndex),
id: token.startIndex,
});
previousBreakpoint = token.endIndex;
}
}

return (
Expand Down
16 changes: 13 additions & 3 deletions src/web/components/TokensViewer.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
import type { PropsWithChildren } from "@kitajs/html";

import { TokenViewer } from "./TokenViewer";
import type { TokenWithId } from "../utils";
import { codeCss, type TokenWithId } from "../utils";
import type { Result } from "neverthrow";
import type { LexicalError } from "../../domain/errors";

export function TokensViewer(
props: PropsWithChildren<{
source: string;
tokens: TokenWithId[];
tokens: Result<TokenWithId[], LexicalError>;
class?: string;
}>
) {
if (props.tokens.isErr()) {
console.error(props.tokens.error);

return (
<p class={codeCss + " text-red-400"}>{props.tokens.error.toString()}</p>
);
}

return (
<div class="flex-grow flex flex-col space-y-4 overflow-auto">
{props.tokens.map((token) => (
{props.tokens.value.map((token) => (
<TokenViewer
source={props.source.slice(token.startIndex, token.endIndex)}
token={token}
Expand Down
11 changes: 9 additions & 2 deletions src/web/pages/ASTViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@ import { type Ast } from "../../domain/parse";
import { codeCss, type TokenWithId } from "../utils";
import { type PropsWithChildren } from "@kitajs/html";
import { NodeViewer } from "../components/NodeViewer";
import type { Result } from "neverthrow";

export function ASTViewer(
props: PropsWithChildren<{ parsed: Ast<TokenWithId>; class?: string }>
props: PropsWithChildren<{
parsed: Result<Ast<TokenWithId>, Error>;
class?: string;
}>
) {
if (props.parsed.isErr())
return <div class={codeCss + " overflow-auto grow " + props.class}></div>;

return (
<div class={codeCss + " overflow-auto grow " + props.class}>
<NodeViewer node={props.parsed} />
<NodeViewer node={props.parsed.value} />
</div>
);
}
20 changes: 12 additions & 8 deletions src/web/pages/parsed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { randomUUID } from "crypto";
import { Link } from "../components/Button";
import { Nav } from "../components/Nav";
import { ASTViewer } from "./ASTViewer";
import type { TokenWithId } from "../utils";

export default new Elysia().get(
"/parsed",
Expand All @@ -17,14 +18,17 @@ export default new Elysia().get(
// https://stackoverflow.com/a/16587536
const source = decodeURIComponent(req.query.source.replace(/\+/g, "%20"));

const tokens = tokenize(source)
._unsafeUnwrap()
.map((token) => ({
...token,
id: randomUUID(),
}));
const tokens = tokenize(source).map((tokens) =>
tokens.map(
(token) =>
({
...token,
id: randomUUID(),
} as TokenWithId)
)
);

const parsed = parse(tokens);
const parsed = tokens.map(parse);

return (
<App class="grid gap-6 h-auto lg:grid-rows-layout lg:grid-cols-3 auto-rows-auto">
Expand All @@ -38,7 +42,7 @@ export default new Elysia().get(
</Nav>
<CodeViewer
source={source}
tokens={tokens}
tokens={tokens.unwrapOr([])}
class="lg:row-start-2 lg:col-start-1"
/>

Expand Down

0 comments on commit 2df4be3

Please sign in to comment.