From b64c02cf4ab5922f1438ae262103359854c49482 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sun, 30 Jul 2023 21:49:18 +0200 Subject: [PATCH] Add support for tuple literals --- src/ast.ts | 15 ++++++++++++++- src/index.ts | 11 +++-------- src/lower.ts | 16 ++++++---------- src/parser.ts | 52 ++++++++++++++++++++++++++++++++++++++------------ src/printer.ts | 13 +++++++++---- src/typeck.ts | 10 ++++++++++ 6 files changed, 82 insertions(+), 35 deletions(-) diff --git a/src/ast.ts b/src/ast.ts index caaef01..96d89fa 100644 --- a/src/ast.ts +++ b/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)), + }; + } } } diff --git a/src/index.ts b/src/index.ts index b4bf7c9..24aa8a9 100644 --- a/src/index.ts +++ b/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() { diff --git a/src/lower.ts b/src/lower.ts index 629ccbd..8fc0937 100644 --- a/src/lower.ts +++ b/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": diff --git a/src/parser.ts b/src/parser.ts index 0ef00dd..0b0b157 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -79,10 +79,10 @@ function parseItem(t: Token[]): [Token[], Item] { let name; [t, name] = expectNext(t, "identifier"); [t] = expectNext(t, "="); - [t] = expectNext(t, "("); + [t] = expectNext(t, "{"); let fields; - [t, fields] = parseCommaSeparatedList(t, ")", (t) => { + [t, fields] = parseCommaSeparatedList(t, "}", (t) => { let name; [t, name] = expectNext(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( 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]; } diff --git a/src/printer.ts b/src/printer.ts index 001ec1c..7aee2b2 100644 --- a/src/printer.ts +++ b/src/printer.ts @@ -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); } diff --git a/src/typeck.ts b/src/typeck.ts index eca4ea3..0a0dcb2 100644 --- a/src/typeck.ts +++ b/src/typeck.ts @@ -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 }; + } } }, };