add strings
All checks were successful
Check / Explore-Gitea-Actions (push) Successful in 8s

This commit is contained in:
sfja 2026-03-17 20:24:15 +01:00
parent a42917b485
commit 4f9ea23d84
16 changed files with 287 additions and 95 deletions

View File

@ -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[] }

View File

@ -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)) {

View File

@ -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}'`);

View File

@ -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,
); );

View File

@ -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];

View File

@ -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`);
} }

View File

@ -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 =

View File

@ -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 };

View File

@ -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 }

View File

@ -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

View File

@ -4,6 +4,6 @@ fn main()
{ {
let v: int = 123; let v: int = 123;
v = 456; v = 456;
debug_print(v); print(v);
} }

View File

@ -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);
} }

View File

@ -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);
} }

View File

@ -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));
} }

View File

@ -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
View 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