Make basic Signal Digith work
This commit is contained in:
parent
bf5eb54932
commit
c0198d419f
15 changed files with 464 additions and 73 deletions
|
|
@ -88,6 +88,7 @@ export namespace Expr {
|
|||
export namespace SignalExpr {
|
||||
export const read = (name: SignalName, span: Span): SignalExpr => ({ tag: "read", name, span });
|
||||
export const signalBinding = (pattern: ProductPattern, expr: SignalExpr, span: Span): SignalExprBinding => ({ pattern, expr, span });
|
||||
export const let_ = (bindings: SignalExprBinding[], body: Expr, span: Span): SignalExpr => ({ tag: "let", bindings, body, span });
|
||||
}
|
||||
|
||||
export namespace ProductPattern {
|
||||
|
|
|
|||
|
|
@ -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, FunctionName, MatchBranch, Pattern, ProductPattern, SignalExpr } from '../expr';
|
||||
import { Expr, ExprBinding, FieldAssignment, FieldPattern, FunctionName, MatchBranch, Pattern, ProductPattern, SignalExpr, SignalExprBinding } 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.
|
||||
|
|
@ -29,6 +29,8 @@ export type ParseError =
|
|||
| { tag: "ExpectedRecordOpen", span: Span } // Expected '(' after ':'
|
||||
| { tag: "ExpectedLetBlockOpen", span: Span } // Expected '{' after 'let'
|
||||
| { tag: "ExpectedLetBlockClose", span: Span } // Expected '}' at end of 'let' expression
|
||||
| { tag: "ExpectedLetSignalBlockOpen", span: Span } // Expected '{' after `let-signal`
|
||||
| { tag: "ExpectedLetSignalBlockClose", span: Span } // Expected '}' at end of 'let-signal' expression
|
||||
| { tag: "ExpectedMatchBlockOpen", span: Span } // Expected '{' after 'match'
|
||||
| { tag: "ExpectedMatchBlockClose", span: Span } // Expected '}' at end of 'match' expression
|
||||
| { tag: "ExpectedLambdaBlockOpen", span: Span } // Expected '{' after `fn`
|
||||
|
|
@ -37,7 +39,7 @@ export type ParseError =
|
|||
| { tag: "ExpectedApplySeparator", span: Span } // Expected '!' inside 'apply'
|
||||
| { tag: "UnexpectedTagPattern", span: Span } // Found #tag where product pattern expected
|
||||
| { tag: "ExpectedPattern", span: Span } // EOF or invalid start of pattern
|
||||
| { tag: "ExpectedRecordPatternOpen", span: Span } // Expected '(' at start of record pattern
|
||||
| { tag: "ExpectedRecordPatternOpen", span: Span } // Expected ':(' at start of record pattern
|
||||
| { tag: "ExpectedRecordField", span: Span }; // Expected identifier in record pattern
|
||||
|
||||
// TODO: Delete?
|
||||
|
|
@ -50,6 +52,8 @@ export type Expectation =
|
|||
| "ExpectedRecordOpen"
|
||||
| "ExpectedLetBlockOpen"
|
||||
| "ExpectedLetBlockClose"
|
||||
| "ExpectedLetSignalBlockOpen"
|
||||
| "ExpectedLetSignalBlockClose"
|
||||
| "ExpectedMatchBlockOpen"
|
||||
| "ExpectedMatchBlockClose"
|
||||
| "ExpectedApplyStart"
|
||||
|
|
@ -314,6 +318,7 @@ function expr(cursor: Cursor): Expr {
|
|||
function signalExpr(cursor: Cursor): SignalExpr {
|
||||
const start = cursor.currentLocation();
|
||||
const token = signalExprStartToken(cursor);
|
||||
|
||||
switch (token.tag) {
|
||||
case "EOF":
|
||||
throw {
|
||||
|
|
@ -323,41 +328,27 @@ function signalExpr(cursor: Cursor): SignalExpr {
|
|||
} as ParseError;
|
||||
case "signal_read":
|
||||
return SignalExpr.read(token.name, token.span);
|
||||
// case "function_name":
|
||||
// TODO: when components are ready
|
||||
// // e.g. my_func(arg1, arg2)
|
||||
// // parse a `,` delimiter sequence of expr
|
||||
// // need to consume )
|
||||
// if (!tryConsume(cursor, char('('))) {
|
||||
// throw {
|
||||
// tag: "ExpectedFunctionCallStart",
|
||||
// span: cursor.makeSpan(cursor.currentLocation())
|
||||
// } as ParseError;
|
||||
// }
|
||||
// const args = delimitedTerminalSequence(cursor, DELIMITER_COMMA, TERMINATOR_CLOSE_PAREN, expr);
|
||||
// return Expr.call(token.name, args, cursor.makeSpan(start));
|
||||
case "keyword":
|
||||
switch (token.kw) {
|
||||
case "let-signal":
|
||||
// TODO:
|
||||
// // let { p0 = e0, p1 = e2 . body }
|
||||
// if (!tryConsume(cursor, char('{'))) {
|
||||
// throw {
|
||||
// tag: "ExpectedLetBlockOpen",
|
||||
// span: cursor.makeSpan(cursor.currentLocation())
|
||||
// } as ParseError;
|
||||
// }
|
||||
// const bindings = delimitedTerminalSequence(cursor, DELIMITER_COMMA, TERMINATOR_DOT, productPatternBinding);
|
||||
// const body = expr(cursor);
|
||||
// let { x := sig-expr, y := sig-expr . normal-expr }
|
||||
// TODO: Decide if to introduce new keyword `:=` or just reuse `=`?
|
||||
if (!tryConsume(cursor, char('{'))) {
|
||||
throw {
|
||||
tag: "ExpectedLetSignalBlockOpen",
|
||||
span: cursor.makeSpan(cursor.currentLocation())
|
||||
} as ParseError;
|
||||
}
|
||||
const bindings = delimitedTerminalSequence(cursor, DELIMITER_COMMA, TERMINATOR_DOT, productPatternSignalBinding);
|
||||
const body = expr(cursor);
|
||||
|
||||
// if (!tryConsume(cursor, TERMINATOR_CLOSE_BRACE)) {
|
||||
// throw {
|
||||
// tag: "ExpectedLetBlockClose",
|
||||
// span: cursor.makeSpan(cursor.currentLocation())
|
||||
// } as ParseError;
|
||||
// }
|
||||
// return Expr.let_(bindings, body, cursor.makeSpan(start));
|
||||
return 0 as any;
|
||||
if (!tryConsume(cursor, TERMINATOR_CLOSE_BRACE)) {
|
||||
throw {
|
||||
tag: "ExpectedLetSignalBlockClose",
|
||||
span: cursor.makeSpan(cursor.currentLocation())
|
||||
} as ParseError;
|
||||
}
|
||||
return SignalExpr.let_(bindings, body, cursor.makeSpan(start));
|
||||
case "let":
|
||||
case "fn":
|
||||
case "match":
|
||||
|
|
@ -415,6 +406,20 @@ function productPatternBinding(cursor: Cursor): ExprBinding {
|
|||
return Expr.exprBinding(pattern, e, cursor.makeSpan(start));
|
||||
}
|
||||
|
||||
function productPatternSignalBinding(cursor: Cursor): SignalExprBinding {
|
||||
const start = cursor.currentLocation();
|
||||
const pattern = productPattern(cursor);
|
||||
|
||||
if (!tryConsume(cursor, char('='))) {
|
||||
throw {
|
||||
tag: "ExpectedPatternBindingSymbol",
|
||||
span: cursor.makeSpan(cursor.currentLocation())
|
||||
} as ParseError;
|
||||
}
|
||||
const e = signalExpr(cursor);
|
||||
return SignalExpr.signalBinding(pattern, e, cursor.makeSpan(start));
|
||||
}
|
||||
|
||||
function fieldAssignment(cursor: Cursor): FieldAssignment {
|
||||
const start = cursor.currentLocation();
|
||||
// `f = e`
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ export type ExprScanError =
|
|||
| NumberError
|
||||
| StringError
|
||||
| { tag: "InvalidIdentifier", text: string, kind: IdentifierKind, reason: IdentifierErrorReason, span: Span }
|
||||
| { tag: "UnexpectedIdentifier", identifier: string, span: Span }
|
||||
|
||||
// What kind of identifier were we trying to parse?
|
||||
export type IdentifierKind =
|
||||
|
|
@ -268,9 +269,11 @@ export function signalExprStart(cursor: Cursor): SignalExprStartToken {
|
|||
case "keyword":
|
||||
return result;
|
||||
case "identifier":
|
||||
// TODO: when we have parametrized signal-expressions
|
||||
// return { tag: "function_name", name: result.name, span: result.span };
|
||||
return 0 as any;
|
||||
throw ({
|
||||
tag: "UnexpectedIdentifier",
|
||||
identifier: result.name,
|
||||
span: result.span,
|
||||
} as ExprScanError);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -349,9 +352,6 @@ export function isNextTokenExprStart(cursor: Cursor): boolean {
|
|||
|
||||
case "EOF":
|
||||
return false;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
|
|
@ -386,7 +386,8 @@ export function isNextTokenProductPatternStart(cursor: Cursor): boolean {
|
|||
case "!":
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
case "tag":
|
||||
case "EOF":
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -377,6 +377,14 @@ export namespace Program {
|
|||
}
|
||||
|
||||
// === Signals ===
|
||||
export function getSignal(program: Program, name: FunctionName): Result<SignalDefinition> {
|
||||
const sigDef = program.signal_definitions.get(name);
|
||||
if (sigDef === undefined) {
|
||||
return Result.error({ tag: "SignalNotFound", name });
|
||||
}
|
||||
return Result.ok(sigDef);
|
||||
}
|
||||
|
||||
export type CreateSignal = {
|
||||
name: SignalName,
|
||||
body: SignalExpr,
|
||||
|
|
@ -386,7 +394,7 @@ export namespace Program {
|
|||
export function registerSignal(
|
||||
program: Program,
|
||||
{ name, body, raw_body }: CreateSignal
|
||||
): Result<void> {
|
||||
): Result<SignalName> {
|
||||
if (program.signal_definitions.has(name)) {
|
||||
return Result.error({ tag: "DuplicateSignalName", name });
|
||||
}
|
||||
|
|
@ -410,7 +418,7 @@ export namespace Program {
|
|||
// TODO: Note that this doesn't actually evaluate the signal and doesn't insert it into signal-runtime.
|
||||
// For that we will use `get_or_create_signal`
|
||||
|
||||
return Result.ok(undefined);
|
||||
return Result.ok(name);
|
||||
}
|
||||
|
||||
export type UpdateSignal = {
|
||||
|
|
@ -547,7 +555,7 @@ export function updateSignal(
|
|||
// TODO: MAY THROW RuntimeError. Should probably switch to `eval_start` - and extend the `Program.Error` with runtime errors.
|
||||
const newValue = eval_expr(program, Env.nil(), body);
|
||||
|
||||
// 2. Find the existing runtime signal
|
||||
// Find the existing runtime signal
|
||||
if (def.signalId === undefined) {
|
||||
// This should theoretically not happen for cells since we initialize them eagerly,
|
||||
// but good to be safe.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue