This commit is contained in:
parent
a42917b485
commit
4f9ea23d84
@ -94,6 +94,8 @@ export class Node {
|
|||||||
return visit();
|
return visit();
|
||||||
case "IntExpr":
|
case "IntExpr":
|
||||||
return visit();
|
return visit();
|
||||||
|
case "StrExpr":
|
||||||
|
return visit();
|
||||||
case "ArrayExpr":
|
case "ArrayExpr":
|
||||||
return visit(...k.values);
|
return visit(...k.values);
|
||||||
case "IndexExpr":
|
case "IndexExpr":
|
||||||
@ -141,6 +143,7 @@ export type NodeKind =
|
|||||||
| { tag: "Param"; ident: string; ty: Node | null }
|
| { tag: "Param"; ident: string; ty: Node | null }
|
||||||
| { tag: "IdentExpr"; ident: string }
|
| { tag: "IdentExpr"; ident: string }
|
||||||
| { tag: "IntExpr"; value: number }
|
| { tag: "IntExpr"; value: number }
|
||||||
|
| { tag: "StrExpr"; value: string }
|
||||||
| { tag: "ArrayExpr"; values: Node[] }
|
| { tag: "ArrayExpr"; values: Node[] }
|
||||||
| { tag: "IndexExpr"; value: Node; arg: Node }
|
| { tag: "IndexExpr"; value: Node; arg: Node }
|
||||||
| { tag: "CallExpr"; value: Node; args: Node[] }
|
| { tag: "CallExpr"; value: Node; args: Node[] }
|
||||||
|
|||||||
@ -146,6 +146,10 @@ class Checker {
|
|||||||
return Ty.Int;
|
return Ty.Int;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (node.is("StrExpr")) {
|
||||||
|
return Ty.create("Ptr", { ty: Ty.Str });
|
||||||
|
}
|
||||||
|
|
||||||
if (node.is("ArrayExpr")) {
|
if (node.is("ArrayExpr")) {
|
||||||
let ty: Ty | null = null;
|
let ty: Ty | null = null;
|
||||||
for (const value of node.kind.values) {
|
for (const value of node.kind.values) {
|
||||||
@ -179,6 +183,12 @@ class Checker {
|
|||||||
) {
|
) {
|
||||||
return Ty.create("Slice", { ty: exprTy.kind.ty });
|
return Ty.create("Slice", { ty: exprTy.kind.ty });
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
exprTy.is("Str") &&
|
||||||
|
argTy.compatibleWith(Ty.Int)
|
||||||
|
) {
|
||||||
|
return Ty.Int;
|
||||||
|
}
|
||||||
this.error(
|
this.error(
|
||||||
node.loc,
|
node.loc,
|
||||||
`cannot use index operator on '${exprTy.pretty()}' with '${argTy.pretty()}'`,
|
`cannot use index operator on '${exprTy.pretty()}' with '${argTy.pretty()}'`,
|
||||||
@ -264,6 +274,8 @@ class Checker {
|
|||||||
return Ty.Int;
|
return Ty.Int;
|
||||||
case "bool":
|
case "bool":
|
||||||
return Ty.Bool;
|
return Ty.Bool;
|
||||||
|
case "str":
|
||||||
|
return Ty.Str;
|
||||||
default:
|
default:
|
||||||
this.error(node.loc, `unknown type '${node.kind.ident}'`);
|
this.error(node.loc, `unknown type '${node.kind.ident}'`);
|
||||||
}
|
}
|
||||||
@ -310,9 +322,36 @@ class Checker {
|
|||||||
private checkCall(node: ast.NodeWithKind<"CallExpr">): Ty {
|
private checkCall(node: ast.NodeWithKind<"CallExpr">): Ty {
|
||||||
if (node.kind.value.is("IdentExpr")) {
|
if (node.kind.value.is("IdentExpr")) {
|
||||||
const sym = this.syms.get(node.kind.value);
|
const sym = this.syms.get(node.kind.value);
|
||||||
if (sym && sym.tag === "Builtin") {
|
if (sym.tag === "Builtin") {
|
||||||
if (sym.id === "debug_print") {
|
if (sym.id === "len") {
|
||||||
const _argTys = node.kind.args
|
if (node.kind.args.length !== 1) {
|
||||||
|
this.reportArgsIncorrectAmount(
|
||||||
|
node,
|
||||||
|
node.kind.args.length,
|
||||||
|
0,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const argTy = this.tys.expr(node.kind.args[0]);
|
||||||
|
if (
|
||||||
|
!(argTy.is("Array") ||
|
||||||
|
argTy.is("Ptr") &&
|
||||||
|
(argTy.kind.ty.is("Array") ||
|
||||||
|
argTy.kind.ty.is("Slice") ||
|
||||||
|
argTy.kind.ty.is("Str")))
|
||||||
|
) {
|
||||||
|
this.reportArgTypeNotCompatible(
|
||||||
|
node,
|
||||||
|
[argTy],
|
||||||
|
[Ty.Error],
|
||||||
|
null,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Ty.Int;
|
||||||
|
}
|
||||||
|
if (sym.id === "print") {
|
||||||
|
void node.kind.args
|
||||||
.map((arg) => this.tys.expr(arg));
|
.map((arg) => this.tys.expr(arg));
|
||||||
return Ty.Void;
|
return Ty.Void;
|
||||||
}
|
}
|
||||||
@ -339,11 +378,38 @@ class Checker {
|
|||||||
.map((arg) => this.tys.expr(arg));
|
.map((arg) => this.tys.expr(arg));
|
||||||
const params = callableTy.kind.params;
|
const params = callableTy.kind.params;
|
||||||
if (args.length !== params.length) {
|
if (args.length !== params.length) {
|
||||||
|
this.reportArgsIncorrectAmount(
|
||||||
|
node,
|
||||||
|
args.length,
|
||||||
|
params.length,
|
||||||
|
calleeTy,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (const i of args.keys()) {
|
||||||
|
if (!args[i].compatibleWith(params[i])) {
|
||||||
|
this.reportArgTypeNotCompatible(
|
||||||
|
node,
|
||||||
|
args,
|
||||||
|
params,
|
||||||
|
calleeTy,
|
||||||
|
i,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return callableTy.kind.retTy;
|
||||||
|
}
|
||||||
|
|
||||||
|
private reportArgsIncorrectAmount(
|
||||||
|
node: ast.NodeWithKind<"CallExpr">,
|
||||||
|
argsLength: number,
|
||||||
|
paramsLength: number,
|
||||||
|
calleeTy: Ty | null,
|
||||||
|
): never {
|
||||||
this.error(
|
this.error(
|
||||||
node.loc,
|
node.loc,
|
||||||
`incorrect amount of arguments. got ${args.length} expected ${params.length}`,
|
`incorrect amount of arguments. got ${argsLength} expected ${paramsLength}`,
|
||||||
);
|
);
|
||||||
if (calleeTy.is("FnStmt")) {
|
if (calleeTy?.is("FnStmt")) {
|
||||||
this.info(
|
this.info(
|
||||||
calleeTy.kind.stmt.loc,
|
calleeTy.kind.stmt.loc,
|
||||||
"function defined here",
|
"function defined here",
|
||||||
@ -351,15 +417,21 @@ class Checker {
|
|||||||
}
|
}
|
||||||
this.fail();
|
this.fail();
|
||||||
}
|
}
|
||||||
for (const i of args.keys()) {
|
|
||||||
if (!args[i].compatibleWith(params[i])) {
|
private reportArgTypeNotCompatible(
|
||||||
|
node: ast.NodeWithKind<"CallExpr">,
|
||||||
|
args: Ty[],
|
||||||
|
params: Ty[],
|
||||||
|
calleeTy: Ty | null,
|
||||||
|
i: number,
|
||||||
|
): never {
|
||||||
this.error(
|
this.error(
|
||||||
node.kind.args[i].loc,
|
node.kind.args[i].loc,
|
||||||
`type '${args[i].pretty()}' not compatible with type '${
|
`type '${args[i].pretty()}' not compatible with type '${
|
||||||
params[i].pretty()
|
params[i].pretty()
|
||||||
}', for argument ${i}`,
|
}', for argument ${i}`,
|
||||||
);
|
);
|
||||||
if (calleeTy.is("FnStmt")) {
|
if (calleeTy?.is("FnStmt")) {
|
||||||
this.info(
|
this.info(
|
||||||
calleeTy.kind.stmt.kind.params[i].loc,
|
calleeTy.kind.stmt.kind.params[i].loc,
|
||||||
`parameter '${
|
`parameter '${
|
||||||
@ -370,9 +442,6 @@ class Checker {
|
|||||||
}
|
}
|
||||||
this.fail();
|
this.fail();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return callableTy.kind.retTy;
|
|
||||||
}
|
|
||||||
|
|
||||||
private assertCompatible(left: Ty, right: Ty, loc: Loc): void {
|
private assertCompatible(left: Ty, right: Ty, loc: Loc): void {
|
||||||
if (!left.compatibleWith(right)) {
|
if (!left.compatibleWith(right)) {
|
||||||
|
|||||||
@ -289,6 +289,10 @@ export class Parser {
|
|||||||
const value = Number(this.current.value);
|
const value = Number(this.current.value);
|
||||||
this.step();
|
this.step();
|
||||||
return ast.Node.create(loc, "IntExpr", { value });
|
return ast.Node.create(loc, "IntExpr", { value });
|
||||||
|
} else if (this.test("str")) {
|
||||||
|
const value = this.current.value;
|
||||||
|
this.step();
|
||||||
|
return ast.Node.create(loc, "StrExpr", { value });
|
||||||
} else if (this.eat("(")) {
|
} else if (this.eat("(")) {
|
||||||
const expr = this.parseExpr();
|
const expr = this.parseExpr();
|
||||||
this.mustEat(")");
|
this.mustEat(")");
|
||||||
@ -414,6 +418,25 @@ export function tokenize(text: string, reporter: FileReporter): Tok[] {
|
|||||||
.add(/0|(?:[1-9][0-9]*)/, (loc, value) => {
|
.add(/0|(?:[1-9][0-9]*)/, (loc, value) => {
|
||||||
return { type: "int", value, loc };
|
return { type: "int", value, loc };
|
||||||
})
|
})
|
||||||
|
.add(/"(?:[^\\"]|\\.)*"/, (loc, literal) => {
|
||||||
|
let i = 1;
|
||||||
|
let value = "";
|
||||||
|
while (i < literal.length - 1) {
|
||||||
|
if (literal[i] === "\\") {
|
||||||
|
i += 1;
|
||||||
|
value += {
|
||||||
|
"0": "\0",
|
||||||
|
"t": "\t",
|
||||||
|
"r": "\r",
|
||||||
|
"n": "\n",
|
||||||
|
}[literal[i]] ?? literal[i];
|
||||||
|
} else {
|
||||||
|
value += literal[i];
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
return { type: "str", value, loc };
|
||||||
|
})
|
||||||
.add(/./, (loc, value) => {
|
.add(/./, (loc, value) => {
|
||||||
const escapedChar = JSON.stringify(value[0]).slice(1, -1);
|
const escapedChar = JSON.stringify(value[0]).slice(1, -1);
|
||||||
reporter.error(loc, `illegal character '${escapedChar}'`);
|
reporter.error(loc, `illegal character '${escapedChar}'`);
|
||||||
|
|||||||
@ -113,7 +113,8 @@ class ResolverSyms {
|
|||||||
static root(): ResolverSyms {
|
static root(): ResolverSyms {
|
||||||
return new ResolverSyms(
|
return new ResolverSyms(
|
||||||
new Map([
|
new Map([
|
||||||
["debug_print", { tag: "Builtin", id: "debug_print" }],
|
["len", { tag: "Builtin", id: "len" }],
|
||||||
|
["print", { tag: "Builtin", id: "print" }],
|
||||||
]),
|
]),
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
|
|||||||
14
src/main.ts
14
src/main.ts
@ -46,7 +46,8 @@ function printMirFn(fn: mir.Fn) {
|
|||||||
.split("\n")
|
.split("\n")
|
||||||
.map<[string, string[]]>((line) => [line, []])
|
.map<[string, string[]]>((line) => [line, []])
|
||||||
.map<[string, string[]]>(([line, colors]) => {
|
.map<[string, string[]]>(([line, colors]) => {
|
||||||
line = line.replace(/%(\d+)/g, "__%$1__");
|
line = line.replace(/%\d+/g, "__$&__");
|
||||||
|
line = line.replace(/"(?:[^"\\]|\\.)*"/g, "__$&__");
|
||||||
if (/^\s*__%\d+__ \(.*?\) =/.test(line)) {
|
if (/^\s*__%\d+__ \(.*?\) =/.test(line)) {
|
||||||
line = line.replace(
|
line = line.replace(
|
||||||
/__%(\d+)__ \((.*?)\) =/g,
|
/__%(\d+)__ \((.*?)\) =/g,
|
||||||
@ -56,15 +57,22 @@ function printMirFn(fn: mir.Fn) {
|
|||||||
}
|
}
|
||||||
if (/[A-Z][a-zA-Z]+/.test(line)) {
|
if (/[A-Z][a-zA-Z]+/.test(line)) {
|
||||||
line = line.replace(/([A-Z][a-zA-Z]+)/, "%c$1%c");
|
line = line.replace(/([A-Z][a-zA-Z]+)/, "%c$1%c");
|
||||||
colors.push("color: red;", "");
|
colors.push("color: red; font-weight: bold;", "");
|
||||||
}
|
}
|
||||||
if (/\bptr\b/.test(line)) {
|
if (/\bptr\b/.test(line)) {
|
||||||
line = line.replace(/\b(ptr)\b/, "%c$1%c");
|
line = line.replace(/\b(ptr)\b/, "%c$1%c");
|
||||||
colors.push("color: gray;", "");
|
colors.push("color: gray;", "");
|
||||||
}
|
}
|
||||||
while (/__%\d+__/.test(line)) {
|
while (true) {
|
||||||
|
if (/__%\d+__/.test(line)) {
|
||||||
line = line.replace(/__%(\d+)__/, "%c%$1%c");
|
line = line.replace(/__%(\d+)__/, "%c%$1%c");
|
||||||
colors.push("color: lightblue;", "");
|
colors.push("color: lightblue;", "");
|
||||||
|
} else if (/__"(?:[^"\\]|\\.)*"__/.test(line)) {
|
||||||
|
line = line.replace(/__("(?:[^"\\]|\\.)*")__/, "%c$1%c");
|
||||||
|
colors.push("color: green;", "");
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [line, colors];
|
return [line, colors];
|
||||||
|
|||||||
@ -221,9 +221,7 @@ class FnLowerer {
|
|||||||
const valueTy = this.tys.place(value);
|
const valueTy = this.tys.place(value);
|
||||||
const arg = place.kind.arg;
|
const arg = place.kind.arg;
|
||||||
const argTy = this.tys.expr(arg);
|
const argTy = this.tys.expr(arg);
|
||||||
if (!valueTy.is("Array") && !valueTy.is("Slice")) {
|
if (valueTy.is("Array") || valueTy.is("Slice")) {
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
const valueInst = this.lowerPlace(place.kind.value);
|
const valueInst = this.lowerPlace(place.kind.value);
|
||||||
if (argTy.is("Int")) {
|
if (argTy.is("Int")) {
|
||||||
const argInst = this.lowerExpr(arg);
|
const argInst = this.lowerExpr(arg);
|
||||||
@ -249,6 +247,18 @@ class FnLowerer {
|
|||||||
{ value: valueInst, begin, end },
|
{ value: valueInst, begin, end },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (valueTy.is("Str")) {
|
||||||
|
const valueInst = this.lowerPlace(place.kind.value);
|
||||||
|
if (argTy.is("Int")) {
|
||||||
|
const argInst = this.lowerExpr(arg);
|
||||||
|
return this.pushInst(
|
||||||
|
Ty.create("Ptr", { ty: Ty.Int }),
|
||||||
|
"GetElemPtr",
|
||||||
|
{ base: valueInst, offset: argInst },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`${place.kind.tag} with arg ${argTy.pretty()} not handled`,
|
`${place.kind.tag} with arg ${argTy.pretty()} not handled`,
|
||||||
);
|
);
|
||||||
@ -282,7 +292,10 @@ class FnLowerer {
|
|||||||
throw new Error(`'${sym.tag}' not handled`);
|
throw new Error(`'${sym.tag}' not handled`);
|
||||||
}
|
}
|
||||||
if (expr.is("IntExpr")) {
|
if (expr.is("IntExpr")) {
|
||||||
return this.pushInst(Ty.Int, "Int", { value: expr.kind.value });
|
return this.pushInst(ty, "Int", { value: expr.kind.value });
|
||||||
|
}
|
||||||
|
if (expr.is("StrExpr")) {
|
||||||
|
return this.pushInst(ty, "Str", { value: expr.kind.value });
|
||||||
}
|
}
|
||||||
if (expr.is("ArrayExpr")) {
|
if (expr.is("ArrayExpr")) {
|
||||||
const ty = this.tys.expr(expr);
|
const ty = this.tys.expr(expr);
|
||||||
@ -295,14 +308,16 @@ class FnLowerer {
|
|||||||
return this.pushInst(ty, "Load", { source });
|
return this.pushInst(ty, "Load", { source });
|
||||||
}
|
}
|
||||||
if (expr.is("CallExpr")) {
|
if (expr.is("CallExpr")) {
|
||||||
const ty = this.tys.expr(expr);
|
|
||||||
const args = expr.kind.args
|
const args = expr.kind.args
|
||||||
.map((arg) => this.lowerExpr(arg));
|
.map((arg) => this.lowerExpr(arg));
|
||||||
|
|
||||||
if (expr.kind.value.is("IdentExpr")) {
|
if (expr.kind.value.is("IdentExpr")) {
|
||||||
const sym = this.syms.get(expr.kind.value);
|
const sym = this.syms.get(expr.kind.value);
|
||||||
if (sym.tag === "Builtin") {
|
if (sym.tag === "Builtin") {
|
||||||
if (sym.id === "debug_print") {
|
if (sym.id === "len") {
|
||||||
|
return this.pushInst(ty, "Len", { source: args[0] });
|
||||||
|
}
|
||||||
|
if (sym.id === "print") {
|
||||||
return this.pushInst(ty, "DebugPrint", { args });
|
return this.pushInst(ty, "DebugPrint", { args });
|
||||||
}
|
}
|
||||||
throw new Error(`builtin '${sym.id}' not handled`);
|
throw new Error(`builtin '${sym.id}' not handled`);
|
||||||
@ -316,24 +331,23 @@ class FnLowerer {
|
|||||||
return this.lowerUnaryExpr(expr);
|
return this.lowerUnaryExpr(expr);
|
||||||
}
|
}
|
||||||
if (expr.is("BinaryExpr")) {
|
if (expr.is("BinaryExpr")) {
|
||||||
const resultTy = this.tys.expr(expr);
|
|
||||||
const leftTy = this.tys.expr(expr.kind.left);
|
const leftTy = this.tys.expr(expr.kind.left);
|
||||||
const rightTy = this.tys.expr(expr.kind.right);
|
const rightTy = this.tys.expr(expr.kind.right);
|
||||||
const binaryOp = binaryOpPatterns
|
const binaryOp = binaryOpPatterns
|
||||||
.find((pat) =>
|
.find((pat) =>
|
||||||
expr.kind.op === pat.op &&
|
expr.kind.op === pat.op &&
|
||||||
resultTy.compatibleWith(pat.result) &&
|
ty.compatibleWith(pat.result) &&
|
||||||
leftTy.compatibleWith(pat.left ?? pat.result) &&
|
leftTy.compatibleWith(pat.left ?? pat.result) &&
|
||||||
rightTy.compatibleWith(pat.right ?? pat.left ?? pat.result)
|
rightTy.compatibleWith(pat.right ?? pat.left ?? pat.result)
|
||||||
);
|
);
|
||||||
if (!binaryOp) {
|
if (!binaryOp) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`'${expr.kind.op}' with '${resultTy.pretty()}' not handled`,
|
`'${expr.kind.op}' with '${ty.pretty()}' not handled`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const left = this.lowerExpr(expr.kind.left);
|
const left = this.lowerExpr(expr.kind.left);
|
||||||
const right = this.lowerExpr(expr.kind.right);
|
const right = this.lowerExpr(expr.kind.right);
|
||||||
return this.pushInst(resultTy, binaryOp.tag, { left, right });
|
return this.pushInst(ty, binaryOp.tag, { left, right });
|
||||||
}
|
}
|
||||||
throw new Error(`'${expr.kind.tag}' not handled`);
|
throw new Error(`'${expr.kind.tag}' not handled`);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -121,6 +121,8 @@ export class Inst {
|
|||||||
case "Int":
|
case "Int":
|
||||||
case "Bool":
|
case "Bool":
|
||||||
return `${k.value}`;
|
return `${k.value}`;
|
||||||
|
case "Str":
|
||||||
|
return `${JSON.stringify(k.value)}`;
|
||||||
case "Array":
|
case "Array":
|
||||||
return `[${k.values.map(r).join(", ")}]`;
|
return `[${k.values.map(r).join(", ")}]`;
|
||||||
case "Fn":
|
case "Fn":
|
||||||
@ -169,6 +171,8 @@ export class Inst {
|
|||||||
case "Div":
|
case "Div":
|
||||||
case "Rem":
|
case "Rem":
|
||||||
return `${r(k.left)} ${r(k.right)}`;
|
return `${r(k.left)} ${r(k.right)}`;
|
||||||
|
case "Len":
|
||||||
|
return `${r(k.source)}`;
|
||||||
case "DebugPrint":
|
case "DebugPrint":
|
||||||
return `${k.args.map(r).join(", ")}`;
|
return `${k.args.map(r).join(", ")}`;
|
||||||
}
|
}
|
||||||
@ -181,6 +185,7 @@ export type InstKind =
|
|||||||
| { tag: "Void" }
|
| { tag: "Void" }
|
||||||
| { tag: "Int"; value: number }
|
| { tag: "Int"; value: number }
|
||||||
| { tag: "Bool"; value: boolean }
|
| { tag: "Bool"; value: boolean }
|
||||||
|
| { tag: "Str"; value: string }
|
||||||
| { tag: "Array"; values: Inst[] }
|
| { tag: "Array"; values: Inst[] }
|
||||||
| { tag: "Fn"; fn: Fn }
|
| { tag: "Fn"; fn: Fn }
|
||||||
| { tag: "Param"; idx: number }
|
| { tag: "Param"; idx: number }
|
||||||
@ -196,6 +201,7 @@ export type InstKind =
|
|||||||
| { tag: "Not"; source: Inst }
|
| { tag: "Not"; source: Inst }
|
||||||
| { tag: "Negate"; source: Inst }
|
| { tag: "Negate"; source: Inst }
|
||||||
| { tag: BinaryOp; left: Inst; right: Inst }
|
| { tag: BinaryOp; left: Inst; right: Inst }
|
||||||
|
| { tag: "Len"; source: Inst }
|
||||||
| { tag: "DebugPrint"; args: Inst[] };
|
| { tag: "DebugPrint"; args: Inst[] };
|
||||||
|
|
||||||
export type BinaryOp =
|
export type BinaryOp =
|
||||||
|
|||||||
@ -31,6 +31,7 @@ export class FnInterpreter {
|
|||||||
case "Void":
|
case "Void":
|
||||||
case "Int":
|
case "Int":
|
||||||
case "Bool":
|
case "Bool":
|
||||||
|
case "Str":
|
||||||
this.regs.set(inst, new Val(k));
|
this.regs.set(inst, new Val(k));
|
||||||
break;
|
break;
|
||||||
case "Array":
|
case "Array":
|
||||||
@ -59,7 +60,6 @@ export class FnInterpreter {
|
|||||||
if (base.kind.tag === "Ptr") {
|
if (base.kind.tag === "Ptr") {
|
||||||
const array = base.kind.value;
|
const array = base.kind.value;
|
||||||
if (array.kind.tag !== "Array") {
|
if (array.kind.tag !== "Array") {
|
||||||
console.log({ array });
|
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
@ -91,6 +91,22 @@ export class FnInterpreter {
|
|||||||
mutable: base.kind.mutable,
|
mutable: base.kind.mutable,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
} else if (base.kind.tag === "Str") {
|
||||||
|
const str = base.kind.value;
|
||||||
|
if (
|
||||||
|
offset.kind.value < 0 ||
|
||||||
|
offset.kind.value >= str.length
|
||||||
|
) {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
this.regs.set(
|
||||||
|
inst,
|
||||||
|
new Val({
|
||||||
|
tag: "StrElemPtr",
|
||||||
|
value: str,
|
||||||
|
idx: offset.kind.value,
|
||||||
|
}),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
@ -172,8 +188,17 @@ export class FnInterpreter {
|
|||||||
inst,
|
inst,
|
||||||
source.kind.values[source.kind.idx],
|
source.kind.values[source.kind.idx],
|
||||||
);
|
);
|
||||||
|
} else if (source.kind.tag === "StrElemPtr") {
|
||||||
|
this.regs.set(
|
||||||
|
inst,
|
||||||
|
new Val({
|
||||||
|
tag: "Int",
|
||||||
|
value: source.kind.value.charCodeAt(
|
||||||
|
source.kind.idx,
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
console.log({ source });
|
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -248,6 +273,23 @@ export class FnInterpreter {
|
|||||||
case "Rem":
|
case "Rem":
|
||||||
this.evalBinaryOp(inst, k);
|
this.evalBinaryOp(inst, k);
|
||||||
break;
|
break;
|
||||||
|
case "Len": {
|
||||||
|
const source = this.regs.get(k.source)!;
|
||||||
|
if (source.kind.tag === "Array") {
|
||||||
|
throw new Error();
|
||||||
|
} else if (source.kind.tag === "Str") {
|
||||||
|
this.regs.set(
|
||||||
|
inst,
|
||||||
|
new Val({
|
||||||
|
tag: "Int",
|
||||||
|
value: source.kind.value.length,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "DebugPrint":
|
case "DebugPrint":
|
||||||
console.log(
|
console.log(
|
||||||
k.args
|
k.args
|
||||||
@ -335,8 +377,11 @@ class Val {
|
|||||||
case "Int":
|
case "Int":
|
||||||
case "Bool":
|
case "Bool":
|
||||||
return `${k.value}`;
|
return `${k.value}`;
|
||||||
|
case "Str":
|
||||||
|
return k.value;
|
||||||
case "Ptr":
|
case "Ptr":
|
||||||
case "ArrayElemPtr":
|
case "ArrayElemPtr":
|
||||||
|
case "StrElemPtr":
|
||||||
return `<pointer>`;
|
return `<pointer>`;
|
||||||
case "ArraySlice":
|
case "ArraySlice":
|
||||||
return `[${
|
return `[${
|
||||||
@ -361,6 +406,9 @@ type ValKind =
|
|||||||
| { tag: "Void" }
|
| { tag: "Void" }
|
||||||
| { tag: "Int"; value: number }
|
| { tag: "Int"; value: number }
|
||||||
| { tag: "Bool"; value: boolean }
|
| { tag: "Bool"; value: boolean }
|
||||||
|
| { tag: "Str"; value: string }
|
||||||
|
| { tag: "Array"; values: Val[] }
|
||||||
|
| { tag: "Fn"; fn: mir.Fn }
|
||||||
| { tag: "Ptr"; mutable: boolean; value: Val }
|
| { tag: "Ptr"; mutable: boolean; value: Val }
|
||||||
| { tag: "ArrayElemPtr"; mutable: boolean; values: Val[]; idx: number }
|
| { tag: "ArrayElemPtr"; mutable: boolean; values: Val[]; idx: number }
|
||||||
| {
|
| {
|
||||||
@ -370,5 +418,4 @@ type ValKind =
|
|||||||
begin: number;
|
begin: number;
|
||||||
end: number;
|
end: number;
|
||||||
}
|
}
|
||||||
| { tag: "Array"; values: Val[] }
|
| { tag: "StrElemPtr"; value: string; idx: number };
|
||||||
| { tag: "Fn"; fn: mir.Fn };
|
|
||||||
|
|||||||
@ -25,6 +25,7 @@ export class Ty {
|
|||||||
static Void = Ty.create("Void", {});
|
static Void = Ty.create("Void", {});
|
||||||
static Int = Ty.create("Int", {});
|
static Int = Ty.create("Int", {});
|
||||||
static Bool = Ty.create("Bool", {});
|
static Bool = Ty.create("Bool", {});
|
||||||
|
static Str = Ty.create("Str", {});
|
||||||
|
|
||||||
private internHash(): string {
|
private internHash(): string {
|
||||||
return JSON.stringify(this.kind);
|
return JSON.stringify(this.kind);
|
||||||
@ -59,6 +60,9 @@ export class Ty {
|
|||||||
if (this.is("Bool")) {
|
if (this.is("Bool")) {
|
||||||
return other.is("Bool");
|
return other.is("Bool");
|
||||||
}
|
}
|
||||||
|
if (this.is("Str")) {
|
||||||
|
return other.is("Str");
|
||||||
|
}
|
||||||
if (this.is("Ptr")) {
|
if (this.is("Ptr")) {
|
||||||
if (!other.is("Ptr")) {
|
if (!other.is("Ptr")) {
|
||||||
return false;
|
return false;
|
||||||
@ -123,7 +127,7 @@ export class Ty {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isSized(): boolean {
|
isSized(): boolean {
|
||||||
if (this.is("Slice")) {
|
if (this.is("Slice") || this.is("Str")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -139,6 +143,8 @@ export class Ty {
|
|||||||
return "int";
|
return "int";
|
||||||
case "Bool":
|
case "Bool":
|
||||||
return "bool";
|
return "bool";
|
||||||
|
case "Str":
|
||||||
|
return "str";
|
||||||
case "Ptr":
|
case "Ptr":
|
||||||
return `*${this.kind.ty.pretty()}`;
|
return `*${this.kind.ty.pretty()}`;
|
||||||
case "PtrMut":
|
case "PtrMut":
|
||||||
@ -175,6 +181,7 @@ export type TyKind =
|
|||||||
| { tag: "Void" }
|
| { tag: "Void" }
|
||||||
| { tag: "Int" }
|
| { tag: "Int" }
|
||||||
| { tag: "Bool" }
|
| { tag: "Bool" }
|
||||||
|
| { tag: "Str" }
|
||||||
| { tag: "Ptr"; ty: Ty }
|
| { tag: "Ptr"; ty: Ty }
|
||||||
| { tag: "PtrMut"; ty: Ty }
|
| { tag: "PtrMut"; ty: Ty }
|
||||||
| { tag: "Array"; ty: Ty; length: number }
|
| { tag: "Array"; ty: Ty; length: number }
|
||||||
|
|||||||
@ -5,20 +5,20 @@ fn main()
|
|||||||
|
|
||||||
let elem: int = array[0];
|
let elem: int = array[0];
|
||||||
// expect: 1
|
// expect: 1
|
||||||
debug_print(elem);
|
print(elem);
|
||||||
|
|
||||||
let ptr_to_array: *[int; 3] = &array;
|
let ptr_to_array: *[int; 3] = &array;
|
||||||
// expect: 2
|
// expect: 2
|
||||||
debug_print(ptr_to_array.*[1]);
|
print(ptr_to_array.*[1]);
|
||||||
|
|
||||||
let slice: *[int] = &array[..];
|
let slice: *[int] = &array[..];
|
||||||
// expect: 3
|
// expect: 3
|
||||||
debug_print(slice.*[2]);
|
print(slice.*[2]);
|
||||||
|
|
||||||
let slice_mut: *mut [int] = &mut array[1..3];
|
let slice_mut: *mut [int] = &mut array[1..3];
|
||||||
slice_mut.*[0] = 4;
|
slice_mut.*[0] = 4;
|
||||||
// expect: 4
|
// expect: 4
|
||||||
debug_print(array[1]);
|
print(array[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// vim: syntax=rust commentstring=//\ %s
|
// vim: syntax=rust commentstring=//\ %s
|
||||||
|
|||||||
@ -4,6 +4,6 @@ fn main()
|
|||||||
{
|
{
|
||||||
let v: int = 123;
|
let v: int = 123;
|
||||||
v = 456;
|
v = 456;
|
||||||
debug_print(v);
|
print(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@ fn main()
|
|||||||
a = 3;
|
a = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_print(a);
|
print(a);
|
||||||
|
|
||||||
if false {
|
if false {
|
||||||
a = 4;
|
a = 4;
|
||||||
@ -21,5 +21,5 @@ fn main()
|
|||||||
a = 5;
|
a = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_print(a);
|
print(a);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ fn main()
|
|||||||
a = a + 1;
|
a = a + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_print(a);
|
print(a);
|
||||||
|
|
||||||
while a < 10 {
|
while a < 10 {
|
||||||
if a >= 8 {
|
if a >= 8 {
|
||||||
@ -18,5 +18,5 @@ fn main()
|
|||||||
a = a + 1;
|
a = a + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_print(a);
|
print(a);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,20 +13,20 @@ fn main()
|
|||||||
let a = 5;
|
let a = 5;
|
||||||
let b = 3;
|
let b = 3;
|
||||||
|
|
||||||
debug_print(a + b);
|
print(a + b);
|
||||||
debug_print(a - b);
|
print(a - b);
|
||||||
debug_print(a * b);
|
print(a * b);
|
||||||
debug_print(a * b / 2);
|
print(a * b / 2);
|
||||||
debug_print(a % b);
|
print(a % b);
|
||||||
debug_print(-a);
|
print(-a);
|
||||||
|
|
||||||
let c = false;
|
let c = false;
|
||||||
|
|
||||||
if not c {
|
if not c {
|
||||||
debug_print(123);
|
print(123);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_print(2 * 3 + 4);
|
print(2 * 3 + 4);
|
||||||
debug_print(2 * (3 + 4));
|
print(2 * (3 + 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,21 +9,21 @@ fn main()
|
|||||||
let a = 1;
|
let a = 1;
|
||||||
let b: *int = &a;
|
let b: *int = &a;
|
||||||
// expect: 1
|
// expect: 1
|
||||||
debug_print(*b);
|
print(*b);
|
||||||
|
|
||||||
a = 2;
|
a = 2;
|
||||||
// expect: 2
|
// expect: 2
|
||||||
debug_print(*b);
|
print(*b);
|
||||||
|
|
||||||
let c: *mut int = &mut a;
|
let c: *mut int = &mut a;
|
||||||
*c = 3;
|
*c = 3;
|
||||||
// expect: 3
|
// expect: 3
|
||||||
debug_print(a);
|
print(a);
|
||||||
// expect: 3
|
// expect: 3
|
||||||
debug_print(*c);
|
print(*c);
|
||||||
|
|
||||||
change_to(&mut a, 4);
|
change_to(&mut a, 4);
|
||||||
// expect: 4
|
// expect: 4
|
||||||
debug_print(a);
|
print(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
14
tests/string.ethlang
Normal file
14
tests/string.ethlang
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
fn main()
|
||||||
|
{
|
||||||
|
let my_string: *str = "hello world";
|
||||||
|
// expect: hello world
|
||||||
|
print(my_string);
|
||||||
|
// expect: 104
|
||||||
|
print(my_string.*[0]);
|
||||||
|
// expect: 11
|
||||||
|
print(len(my_string));
|
||||||
|
}
|
||||||
|
|
||||||
|
// vim: syntax=rust commentstring=//\ %s
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user