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

View file

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

View file

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

View file

@ -126,6 +126,11 @@ function printExpr(expr: Expr, indent: number): string {
indent + 1 indent + 1
)}${elsePart}`; )}${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 { export function typeck(ast: Ast): Ast {
const itemTys = new Map<number, Ty | null>(); const itemTys = new Map<number, Ty | null>();
function typeOfItem(index: ItemId): Ty { function typeOfItem(index: ItemId): Ty {
const item = ast.items[index];
const ty = itemTys.get(index); const ty = itemTys.get(index);
if (ty) { if (ty) {
return ty; return ty;
} }
if (ty === null) { 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); itemTys.set(index, null);
const item = ast.items[index];
switch (item.kind) { switch (item.kind) {
case "function": { case "function": {
const args = item.node.params.map((arg) => lowerAstTy(arg.type)); const args = item.node.params.map((arg) => lowerAstTy(arg.type));
@ -100,15 +101,28 @@ export function typeck(ast: Ast): Ast {
? lowerAstTy(item.node.returnType) ? lowerAstTy(item.node.returnType)
: TY_UNIT; : 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": { 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 }) => [ const fields = item.node.fields.map<[string, Ty]>(({ name, type }) => [
name.name, name.name,
lowerAstTy(type), lowerAstTy(type),
]); ]);
return { kind: "struct", name: item.node.name, fields }; ty.fields = fields;
return ty;
} }
} }
} }
@ -379,6 +393,11 @@ export function checkBody(
} }
break; break;
} }
case "struct": {
if (rhs.kind === "struct" && lhs.name === rhs.name) {
return;
}
}
} }
throw new CompilerError( throw new CompilerError(
@ -516,6 +535,50 @@ export function checkBody(
return { ...expr, cond, then, else: elsePart, ty }; 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 };
}
} }
}, },
}; };