mirror of
https://github.com/Noratrieb/riverdelta.git
synced 2026-01-14 16:35:03 +01:00
struct literals
This commit is contained in:
parent
b52abed441
commit
39a995b765
6 changed files with 149 additions and 17 deletions
29
src/ast.ts
29
src/ast.ts
|
|
@ -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)]),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ export type DatalessToken =
|
|||
| "type"
|
||||
| "("
|
||||
| ")"
|
||||
| "{"
|
||||
| "}"
|
||||
| "["
|
||||
| "]"
|
||||
| ";"
|
||||
|
|
@ -54,6 +56,8 @@ export type BaseToken = { kind: Token["kind"] };
|
|||
const SINGLE_PUNCT: string[] = [
|
||||
"(",
|
||||
")",
|
||||
"}",
|
||||
"{",
|
||||
"[",
|
||||
"]",
|
||||
";",
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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(", ")} }`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue