Create proper validation library. Rewrite new-function-draft component.

This commit is contained in:
Yura Dupyn 2026-02-14 15:59:49 +01:00
parent 0941756bf9
commit 8e4dcb5de7
8 changed files with 366 additions and 55 deletions

View file

@ -2,7 +2,7 @@ import { Cursor } from './cursor';
import { ExprScanError, exprStart, ExprStartToken, IdentifierKind, identifierScanner, isNextTokenExprStart, isNextTokenProductPatternStart, patternStart, PatternStartToken, signalExprStart, SignalExprStartToken, skipWhitespaceAndComments } from './scanner';
import { char, CodePoint, SourceText, Span } from './source_text';
import { Result } from '../result';
import { Expr, ExprBinding, FieldAssignment, FieldPattern, MatchBranch, Pattern, ProductPattern, SignalExpr } from '../expr';
import { Expr, ExprBinding, FieldAssignment, FieldPattern, FunctionName, MatchBranch, Pattern, ProductPattern, SignalExpr } from '../expr';
// CONVENTION: Every parser is responsible to consume whitespace/comments at the end.
// Every parser is not responsible for cleaning up whitespace/comments at the start - only the final `parse` that's exposed to the public.
@ -492,8 +492,7 @@ function finishProductPattern(cursor: Cursor, token: PatternStartToken): Product
switch (token.kw) {
case ":": {
// :( a = p, b )
// TODO: parse open-paren
if (!tryConsume(cursor, char('{'))) {
if (!tryConsume(cursor, char('('))) {
throw {
tag: "ExpectedRecordPatternOpen",
span: cursor.makeSpan(cursor.currentLocation())
@ -598,3 +597,25 @@ export function parseFunctionParameters(source: SourceText): Result<ProductPatte
}
}
export function parseFunctionName(source: SourceText): Result<FunctionName, ParseError> {
const cursor = new Cursor(source);
try {
skipWhitespaceAndComments(cursor);
// TODO: We should introduce new `function_name`
const name = identifier(cursor, "function_call");
if (!cursor.eof()) {
return Result.error({
tag: "UnexpectedToken",
expected: "EndOfFile",
span: cursor.makeSpan(cursor.currentLocation())
} as ParseError);
}
return Result.ok(name.name);
} catch (e) {
// TODO: This is a bit sketchy. We maybe forced to have "checked" Exceptions for `ParseError` by wrapping it in something that has a proper tag.
return Result.error(e as ParseError);
}
}

View file

@ -84,7 +84,7 @@ type PrimitiveSignalDefinition = {
export namespace Program {
type Error =
export type Error =
| { tag: "DuplicateFunctionName", name: FunctionName }
| { tag: "FunctionNotFound", name: FunctionName }
| { tag: "CannotEditPrimitiveFunction", name: FunctionName }
@ -98,7 +98,7 @@ export namespace Program {
| { tag: "CannotDeletePrimitiveSignal", name: SignalName }
| { tag: "PrimitiveSignalAlreadyExists", name: SignalName }
type Result<T> =
export type Result<T> =
| { tag: "ok", value: T }
| { tag: "error", error: Error }