mirror of
https://github.com/Noratrieb/riverdelta.git
synced 2026-01-17 01:45: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;
|
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)]),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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[] = [
|
||||||
"(",
|
"(",
|
||||||
")",
|
")",
|
||||||
|
"}",
|
||||||
|
"{",
|
||||||
"[",
|
"[",
|
||||||
"]",
|
"]",
|
||||||
";",
|
";",
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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(", ")} }`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue