mirror of
https://github.com/Noratrieb/riverdelta.git
synced 2026-01-14 16:35:03 +01:00
Add support for tuple literals
This commit is contained in:
parent
0c996eb9bc
commit
b64c02cf4a
6 changed files with 82 additions and 35 deletions
15
src/ast.ts
15
src/ast.ts
|
|
@ -146,6 +146,11 @@ export type ExprStructLiteral = {
|
|||
fields: [Identifier, Expr][];
|
||||
};
|
||||
|
||||
export type TupleLiteral = {
|
||||
kind: "tupleLiteral";
|
||||
fields: Expr[];
|
||||
};
|
||||
|
||||
export type ExprKind =
|
||||
| ExprEmpty
|
||||
| ExprLet
|
||||
|
|
@ -159,7 +164,8 @@ export type ExprKind =
|
|||
| ExprIf
|
||||
| ExprLoop
|
||||
| ExprBreak
|
||||
| ExprStructLiteral;
|
||||
| ExprStructLiteral
|
||||
| TupleLiteral;
|
||||
|
||||
export type Expr = ExprKind & {
|
||||
span: Span;
|
||||
|
|
@ -567,6 +573,13 @@ export function superFoldExpr(expr: Expr, folder: Folder): Expr {
|
|||
fields: expr.fields.map(([name, expr]) => [name, folder.expr(expr)]),
|
||||
};
|
||||
}
|
||||
case "tupleLiteral": {
|
||||
return {
|
||||
...expr,
|
||||
kind: "tupleLiteral",
|
||||
fields: expr.fields.map(folder.expr.bind(folder)),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
11
src/index.ts
11
src/index.ts
|
|
@ -11,15 +11,10 @@ import { exec } from "child_process";
|
|||
|
||||
const input = `
|
||||
function main() = (
|
||||
let i = 0;
|
||||
|
||||
loop (
|
||||
if i > 10 then break;
|
||||
|
||||
print("uwu\\n");
|
||||
i = i + 1;
|
||||
);
|
||||
let a = (0,);
|
||||
);
|
||||
|
||||
function uwu(): (Int, Int) = (0, 0);
|
||||
`;
|
||||
|
||||
function main() {
|
||||
|
|
|
|||
16
src/lower.ts
16
src/lower.ts
|
|
@ -616,6 +616,10 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
|
|||
case "structLiteral": {
|
||||
todo("struct literal");
|
||||
}
|
||||
case "tupleLiteral": {
|
||||
expr.fields.forEach((field) => lowerExpr(fcx, instrs, field));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const _: never = expr;
|
||||
}
|
||||
|
|
@ -691,10 +695,7 @@ function computeAbi(ty: TyFn): FnAbi {
|
|||
case "list":
|
||||
todo("list abi");
|
||||
case "tuple":
|
||||
if (param.elems.length === 0) {
|
||||
return [];
|
||||
}
|
||||
todo("complex tuple abi");
|
||||
return param.elems.flatMap(argRetAbi);
|
||||
case "struct":
|
||||
todo("struct ABI");
|
||||
case "never":
|
||||
|
|
@ -741,12 +742,7 @@ function wasmTypeForBody(ty: Ty): wasm.ValType[] {
|
|||
case "list":
|
||||
todo("list types");
|
||||
case "tuple":
|
||||
if (ty.elems.length === 0) {
|
||||
return [];
|
||||
} else if (ty.elems.length === 1) {
|
||||
return wasmTypeForBody(ty.elems[0]);
|
||||
}
|
||||
todo("complex tuples");
|
||||
return ty.elems.flatMap(wasmTypeForBody);
|
||||
case "fn":
|
||||
todo("fn types");
|
||||
case "struct":
|
||||
|
|
|
|||
|
|
@ -79,10 +79,10 @@ function parseItem(t: Token[]): [Token[], Item] {
|
|||
let name;
|
||||
[t, name] = expectNext<TokenIdent>(t, "identifier");
|
||||
[t] = expectNext(t, "=");
|
||||
[t] = expectNext(t, "(");
|
||||
[t] = expectNext(t, "{");
|
||||
|
||||
let fields;
|
||||
[t, fields] = parseCommaSeparatedList<FieldDef>(t, ")", (t) => {
|
||||
[t, fields] = parseCommaSeparatedList<FieldDef>(t, "}", (t) => {
|
||||
let name;
|
||||
[t, name] = expectNext<TokenIdent>(t, "identifier");
|
||||
[t] = expectNext(t, ":");
|
||||
|
|
@ -190,7 +190,7 @@ function parseExpr(t: Token[]): [Token[], Expr] {
|
|||
|
||||
CALL = ATOM { "(" EXPR_LIST ")" }
|
||||
|
||||
ATOM = "(" { EXPR ";" } EXPR ")" | IDENT { STRUCT_INIT } | LITERAL | EMPTY | LET | IF | LOOP | BREAK
|
||||
ATOM = "(" { EXPR ";" | "," } EXPR ")" | IDENT { STRUCT_INIT } | LITERAL | EMPTY | LET | IF | LOOP | BREAK
|
||||
EMPTY =
|
||||
STRUCT_INIT = "{" { NAME ":" EXPR } { "," NAME ":" EXPR } { "," } "}"
|
||||
EXPR_LIST = { EXPR { "," EXPR } { "," } }
|
||||
|
|
@ -292,15 +292,38 @@ function parseExprAtom(startT: Token[]): [Token[], Expr] {
|
|||
let expr: Expr;
|
||||
[t, expr] = parseExpr(t);
|
||||
|
||||
const exprs = [expr];
|
||||
while (next(t)[1].kind !== ")") {
|
||||
[t] = expectNext(t, ";");
|
||||
[t, expr] = parseExpr(t);
|
||||
exprs.push(expr);
|
||||
}
|
||||
[t] = expectNext(t, ")");
|
||||
// This could be a block or a tuple literal. We can only know after
|
||||
// parsing the first expression and looking at the delimiter.
|
||||
|
||||
return [t, { kind: "block", span, exprs }];
|
||||
let peek;
|
||||
[, peek] = next(t);
|
||||
// It's a single element, which we interpret as a block.
|
||||
// `(0,)` is the one elem tuple.
|
||||
if (peek.kind === ")") {
|
||||
[t] = expectNext(t, ")");
|
||||
return [t, { kind: "block", span, exprs: [expr] }];
|
||||
}
|
||||
// It's a block.
|
||||
if (peek.kind === ";") {
|
||||
const exprs = [expr];
|
||||
while (next(t)[1].kind !== ")") {
|
||||
[t] = expectNext(t, ";");
|
||||
[t, expr] = parseExpr(t);
|
||||
exprs.push(expr);
|
||||
}
|
||||
[t] = expectNext(t, ")");
|
||||
|
||||
return [t, { kind: "block", span, exprs }];
|
||||
}
|
||||
// It's a tuple.
|
||||
if (peek.kind === ",") {
|
||||
[t] = expectNext(t, ",");
|
||||
let rest;
|
||||
[t, rest] = parseCommaSeparatedList(t, ")", parseExpr);
|
||||
|
||||
return [t, { kind: "tupleLiteral", span, fields: [expr, ...rest] }];
|
||||
}
|
||||
unexpectedToken(peek, "`,`, `;` or `)`");
|
||||
}
|
||||
|
||||
if (tok.kind === "lit_string") {
|
||||
|
|
@ -454,6 +477,9 @@ function parseType(t: Token[]): [Token[], Type] {
|
|||
return [t, { kind: "list", elem, span }];
|
||||
}
|
||||
case "(": {
|
||||
// `()` is a the unit type, an empty tuple.
|
||||
// `(T)` is just `T`
|
||||
// `(T,)` is a tuple
|
||||
if (next(t)[1]?.kind === ")") {
|
||||
[t] = next(t);
|
||||
return [t, { kind: "tuple", elems: [], span }];
|
||||
|
|
@ -467,6 +493,8 @@ function parseType(t: Token[]): [Token[], Type] {
|
|||
return [t, head];
|
||||
}
|
||||
|
||||
[t] = expectNext(t, ",");
|
||||
|
||||
let tail;
|
||||
[t, tail] = parseCommaSeparatedList(t, ")", parseType);
|
||||
|
||||
|
|
@ -537,7 +565,7 @@ function expectNext<T extends BaseToken>(
|
|||
let tok;
|
||||
[t, tok] = next(t);
|
||||
if (tok.kind !== kind) {
|
||||
throw new CompilerError(`expected ${kind}, found ${tok.kind}`, tok.span);
|
||||
throw new CompilerError(`expected \`${kind}\`, found \`${tok.kind}\``, tok.span);
|
||||
}
|
||||
return [t, tok as unknown as T & Token];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export function printAst(ast: Ast): string {
|
|||
}
|
||||
|
||||
function printStringLiteral(lit: StringLiteral): string {
|
||||
return `"${lit.value}"`;
|
||||
return `"${lit.value.replace("\n", "\\n")}"`;
|
||||
}
|
||||
|
||||
function printItem(item: Item): string {
|
||||
|
|
@ -49,7 +49,7 @@ function printTypeDef(type: TypeDef): string {
|
|||
);
|
||||
|
||||
const fieldPart =
|
||||
type.fields.length === 0 ? "()" : `(\n${fields.join("\n")}\n)`;
|
||||
type.fields.length === 0 ? "{}" : `{\n${fields.join("\n")}\n}`;
|
||||
|
||||
return `type ${type.name} = ${fieldPart};`;
|
||||
}
|
||||
|
|
@ -150,7 +150,7 @@ function printExpr(expr: Expr, indent: number): string {
|
|||
)}${elsePart}`;
|
||||
}
|
||||
case "loop": {
|
||||
return `loop ${printExpr(expr.body, indent + 1)}`;
|
||||
return `loop ${printExpr(expr.body, indent)}`;
|
||||
}
|
||||
case "break": {
|
||||
const target = expr.target !== undefined ? `#${expr.target}` : "";
|
||||
|
|
@ -161,6 +161,11 @@ function printExpr(expr: Expr, indent: number): string {
|
|||
.map(([name, expr]) => `${name.name}: ${printExpr(expr, indent + 1)}`)
|
||||
.join(", ")} }`;
|
||||
}
|
||||
case "tupleLiteral": {
|
||||
return `(${expr.fields
|
||||
.map((expr) => printExpr(expr, indent))
|
||||
.join(", ")})`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -234,5 +239,5 @@ function linebreak(indent: number): string {
|
|||
}
|
||||
|
||||
function ind(indent: number): string {
|
||||
return " ".repeat(indent * 2);
|
||||
return " ".repeat(indent);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -768,6 +768,16 @@ export function checkBody(
|
|||
|
||||
return { ...expr, fields, ty: structTy };
|
||||
}
|
||||
case "tupleLiteral": {
|
||||
const fields = expr.fields.map((expr) => this.expr(expr));
|
||||
|
||||
const ty: Ty = {
|
||||
kind: "tuple",
|
||||
elems: fields.map((field) => field.ty!),
|
||||
};
|
||||
|
||||
return { ...expr, fields, ty };
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue