Add support for tuple literals

This commit is contained in:
nora 2023-07-30 21:49:18 +02:00
parent 0c996eb9bc
commit b64c02cf4a
6 changed files with 82 additions and 35 deletions

View file

@ -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)),
};
}
}
}

View file

@ -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() {

View file

@ -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":

View file

@ -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];
}

View file

@ -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);
}

View file

@ -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 };
}
}
},
};