struct literals

This commit is contained in:
nora 2023-07-27 22:00:04 +02:00
parent b52abed441
commit 39a995b765
6 changed files with 149 additions and 17 deletions

View file

@ -101,6 +101,12 @@ export type ExprIf = {
else?: Expr;
};
export type ExprStructLiteral = {
kind: "structLiteral";
name: Identifier;
fields: [Identifier, Expr][];
};
export type ExprKind =
| ExprEmpty
| ExprLet
@ -110,7 +116,8 @@ export type ExprKind =
| ExprBinary
| ExprUnary
| ExprCall
| ExprIf;
| ExprIf
| ExprStructLiteral;
export type Expr = ExprKind & {
span: Span;
@ -385,19 +392,19 @@ export function superFoldExpr(expr: Expr, folder: Folder): Expr {
}
case "let": {
return {
...expr,
kind: "let",
name: expr.name,
type: expr.type && folder.type(expr.type),
rhs: folder.expr(expr.rhs),
after: folder.expr(expr.after),
span,
};
}
case "block": {
return {
...expr,
kind: "block",
exprs: expr.exprs.map((expr) => folder.expr(expr)),
span,
};
}
case "literal": {
@ -408,36 +415,44 @@ export function superFoldExpr(expr: Expr, folder: Folder): Expr {
}
case "binary": {
return {
...expr,
kind: "binary",
binaryKind: expr.binaryKind,
lhs: folder.expr(expr.lhs),
rhs: folder.expr(expr.rhs),
span,
};
}
case "unary": {
return {
...expr,
kind: "unary",
unaryKind: expr.unaryKind,
rhs: folder.expr(expr.rhs),
span,
};
}
case "call": {
return {
...expr,
kind: "call",
lhs: folder.expr(expr.lhs),
args: expr.args.map((expr) => folder.expr(expr)),
span,
};
}
case "if": {
return {
...expr,
kind: "if",
cond: folder.expr(expr.cond),
then: folder.expr(expr.then),
else: expr.else && folder.expr(expr.else),
span,
};
}
case "structLiteral": {
return {
...expr,
kind: "structLiteral",
name: folder.ident(expr.name),
fields: expr.fields.map(([name, expr]) => [name, folder.expr(expr)]),
};
}
}

View file

@ -12,9 +12,12 @@ import { exec } from "child_process";
const input = `
type Uwu = (
meow: String,
oops: Int,
aaa: (),
);
function main(a: Int, b: Uwu) = ();
function aa(a: Int, b: Uwu): Uwu = Uwu {meow: "",oops:0,aaa:()};
function main() = ();
`;
function main() {

View file

@ -10,6 +10,8 @@ export type DatalessToken =
| "type"
| "("
| ")"
| "{"
| "}"
| "["
| "]"
| ";"
@ -54,6 +56,8 @@ export type BaseToken = { kind: Token["kind"] };
const SINGLE_PUNCT: string[] = [
"(",
")",
"}",
"{",
"[",
"]",
";",

View file

@ -5,9 +5,11 @@ import {
BinaryKind,
COMPARISON_KINDS,
Expr,
ExprStructLiteral,
FieldDef,
FunctionArg,
FunctionDef,
Identifier,
Item,
LOGICAL_KINDS,
Type,
@ -98,10 +100,16 @@ function parseItem(t: Token[]): [Token[], Item] {
[t] = expectNext(t, ":");
let type;
[t, type] = parseType(t);
return [t, { name: {
name: name.ident,
span: name.span,
}, type }];
return [
t,
{
name: {
name: name.ident,
span: name.span,
},
type,
},
];
});
[t] = expectNext(t, ";");
@ -137,8 +145,9 @@ function parseExpr(t: Token[]): [Token[], Expr] {
CALL = ATOM { "(" EXPR_LIST ")" }
ATOM = "(" { EXPR ";" } EXPR ")" | IDENT | LITERAL | EMPTY
ATOM = "(" { EXPR ";" } EXPR ")" | IDENT { STRUCT_INIT } | LITERAL | EMPTY
EMPTY =
STRUCT_INIT = "{" { NAME ":" EXPR } { "," NAME ":" EXPR } { "," } "}"
EXPR_LIST = { EXPR { "," EXPR } { "," } }
*/
const [, peak] = next(t);
@ -309,6 +318,22 @@ function parseExprAtom(startT: Token[]): [Token[], Expr] {
}
if (tok.kind === "identifier") {
console.log(t);
if (maybeNextT(t)[1]?.kind === "{") {
let fields;
[t, fields] = parseStructInit(t);
return [
t,
{
kind: "structLiteral",
name: { name: tok.ident, span },
fields,
span,
},
];
}
return [
t,
{
@ -323,6 +348,23 @@ function parseExprAtom(startT: Token[]): [Token[], Expr] {
return [startT, { kind: "empty", span }];
}
function parseStructInit(t: Token[]): [Token[], ExprStructLiteral["fields"]] {
[t] = expectNext(t, "{");
let fields;
[t, fields] = parseCommaSeparatedList<[Identifier, Expr]>(t, "}", (t) => {
let name;
[t, name] = expectNext<TokenIdent>(t, "identifier");
[t] = expectNext(t, ":");
let expr;
[t, expr] = parseExpr(t);
return [t, [{ name: name.ident, span: name.span }, expr]];
});
return [t, fields];
}
function parseType(t: Token[]): [Token[], Type] {
let tok;
[t, tok] = next(t);

View file

@ -126,6 +126,11 @@ function printExpr(expr: Expr, indent: number): string {
indent + 1
)}${elsePart}`;
}
case "structLiteral": {
return `${printIdent(expr.name)} { ${expr.fields
.map(([name, expr]) => `${name.name}: ${printExpr(expr, indent + 1)}`)
.join(", ")} }`;
}
}
}

View file

@ -84,15 +84,16 @@ function lowerAstTyBase(
export function typeck(ast: Ast): Ast {
const itemTys = new Map<number, Ty | null>();
function typeOfItem(index: ItemId): Ty {
const item = ast.items[index];
const ty = itemTys.get(index);
if (ty) {
return ty;
}
if (ty === null) {
throw Error(`cycle computing type of #G${index}`);
throw new CompilerError(`cycle computing type of #G${index}`, item.span);
}
itemTys.set(index, null);
const item = ast.items[index];
switch (item.kind) {
case "function": {
const args = item.node.params.map((arg) => lowerAstTy(arg.type));
@ -100,15 +101,28 @@ export function typeck(ast: Ast): Ast {
? lowerAstTy(item.node.returnType)
: TY_UNIT;
return { kind: "fn", params: args, returnTy };
const ty: Ty = { kind: "fn", params: args, returnTy };
itemTys.set(item.id, ty);
return ty;
}
case "type": {
const ty: Ty = {
kind: "struct",
name: item.node.name,
fields: [
/*dummy*/
],
};
itemTys.set(item.id, ty);
const fields = item.node.fields.map<[string, Ty]>(({ name, type }) => [
name.name,
lowerAstTy(type),
]);
return { kind: "struct", name: item.node.name, fields };
ty.fields = fields;
return ty;
}
}
}
@ -379,6 +393,11 @@ export function checkBody(
}
break;
}
case "struct": {
if (rhs.kind === "struct" && lhs.name === rhs.name) {
return;
}
}
}
throw new CompilerError(
@ -516,6 +535,50 @@ export function checkBody(
return { ...expr, cond, then, else: elsePart, ty };
}
case "structLiteral": {
const fields = expr.fields.map<[Identifier, Expr]>(([name, expr]) => [
name,
this.expr(expr),
]);
const structTy = typeOf(expr.name.res!, expr.name.span);
if (structTy.kind !== "struct") {
throw new CompilerError(
`struct literal is only allowed for struct types`,
expr.span
);
}
const assignedFields = new Set();
fields.forEach(([name, field], i) => {
const fieldTy = structTy.fields.find((def) => def[0] === name.name);
if (!fieldTy) {
throw new CompilerError(
`field ${name.name} doesn't exist on type ${expr.name.name}`,
name.span
);
}
assign(fieldTy[1], field.ty!, field.span);
assignedFields.add(name.name);
});
const missing: string[] = [];
structTy.fields.forEach(([name]) => {
if (!assignedFields.has(name)) {
missing.push(name);
}
});
if (missing.length > 0) {
throw new CompilerError(
`missing fields in literal: ${missing.join(", ")}`,
expr.span
);
}
return { ...expr, fields, ty: structTy };
}
}
},
};