Skip to content

Commit

Permalink
Merge pull request #7 from caipng/dev
Browse files Browse the repository at this point in the history
v0.1.1
  • Loading branch information
caipng authored Apr 26, 2024
2 parents de8fdac + f9cd520 commit d1f985e
Show file tree
Hide file tree
Showing 9 changed files with 278 additions and 17 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "c-viz",
"version": "0.1.0",
"version": "0.1.1",
"description": "",
"nyc": {
"extension": [
Expand Down
14 changes: 13 additions & 1 deletion src/builtins.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Identifier } from "./ast/types";
import { NO_EFFECTIVE_TYPE } from "./interpreter/effectiveTypeTable";
import { TemporaryObject } from "./interpreter/object";
import { TemporaryObject, stringify } from "./interpreter/object";
import { Runtime } from "./interpreter/runtime";
import { StashItem, isTemporaryObject } from "./interpreter/stash";
import { BIGINT_TO_BYTES, bytesToBigint } from "./typing/representation";
import {
FunctionType,
Type,
_any,
functionType,
int,
isArithmeticType,
Expand Down Expand Up @@ -73,4 +74,15 @@ export const BUILTIN_FUNCTIONS: Record<Identifier, BuiltinFunction> = {
rt.effectiveTypeTable.remove(n, blockSize);
},
},
print: {
type: functionType(voidType(), [{ identifier: "obj", type: _any() }]),
body: (rt: Runtime, args: StashItem[]) => {
if (args.length !== 1) throw "expected 1 arg for print";
const o = args[0];
if (!isTemporaryObject(o)) throw "expected object for print";
rt.appendToStdout(
stringify(o.bytes, o.typeInfo, rt.config.endianness) + "\n",
);
},
},
};
8 changes: 8 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,14 @@ export interface MemoryConfig {

export type Endianness = "little" | "big";

export interface UBConfig {
skipStrictAliasingCheck: boolean;
}

export interface RuntimeConfig {
memory: MemoryConfig;
endianness: Endianness;
UB: UBConfig;
}

export const DEFAULT_CONFIG: RuntimeConfig = {
Expand All @@ -37,4 +42,7 @@ export const DEFAULT_CONFIG: RuntimeConfig = {
},
},
endianness: "little",
UB: {
skipStrictAliasingCheck: false,
},
};
171 changes: 161 additions & 10 deletions src/interpreter/evaluators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,6 @@ export const ASTNodeEvaluator: {
rt: Runtime,
{ value: expr }: TypedJumpStatementReturn,
) => {
// TODO: conversion as if by assignment
rt.agenda.push(returnInstruction());
if (expr) rt.agenda.push(expr);
},
Expand Down Expand Up @@ -274,6 +273,9 @@ export const ASTNodeEvaluator: {
evaluateAsLvalue: boolean,
) => {
switch (op) {
case "!":
case "+":
case "~":
case "-": {
rt.agenda.push(unaryOpInstruction(op));
rt.agenda.push(expr);
Expand Down Expand Up @@ -539,15 +541,16 @@ export const instructionEvaluator: {
[InstructionType.UNARY_OP]: (rt: Runtime, { op }: UnaryOpInstruction) => {
const v = rt.stash.pop();
switch (op) {
case "+":
case "-": {
if (!(isTemporaryObject(v) && isIntegerType(v.typeInfo)))
throw new Error("operand of - should be an integer value");
throw new Error("operand of unary +/- should be an integer value");
let n = bytesToBigint(
v.bytes,
isSigned(v.typeInfo),
rt.config.endianness,
);
n = -n;
if (op === "-") n = -n;
rt.stash.pushWithoutConversions(
new TemporaryObject(
v.typeInfo,
Expand All @@ -556,6 +559,26 @@ export const instructionEvaluator: {
);
return;
}
case "!": {
if (!(isTemporaryObject(v) && isScalarType(v.typeInfo)))
throw new Error("operand of ! should be of scalar type");
let n = bytesToBigint(
v.bytes,
isSigned(v.typeInfo),
rt.config.endianness,
);
n = n === BigInt(0) ? BigInt(1) : BigInt(0);
rt.stash.pushWithoutConversions(
new TemporaryObject(
v.typeInfo,
BIGINT_TO_BYTES[v.typeInfo.type](n, rt.config.endianness),
),
);
return;
}
case "~": {
throw new Error("not implemented");
}
case "*": {
if (!(isTemporaryObject(v) && isPointer(v.typeInfo)))
throw new Error("operand of * should have pointer type");
Expand Down Expand Up @@ -584,7 +607,7 @@ export const instructionEvaluator: {
throw new Error("invalid dereference");
}
case "&": {
throw new Error("not implemented");
throw new Error("invariant broken");
}
}
throw new Error("not implemented");
Expand Down Expand Up @@ -655,9 +678,63 @@ export const instructionEvaluator: {
rt.stash.pushWithoutConversions(res);
return;
}
case "-":
// apply usual arithmetic conversions
break;
case "-": {
let res: TemporaryObject | undefined = undefined;

if (isArithmeticType(t0) && isArithmeticType(t1)) {
let l = bytesToBigint(lo.bytes, isSigned(t0), rt.config.endianness);
let r = bytesToBigint(ro.bytes, isSigned(t1), rt.config.endianness);
const ct = applyUsualArithmeticConversions(t0, t1);
l = convertValue(l, ct, rt.config.endianness);
r = convertValue(r, ct, rt.config.endianness);
res = new TemporaryObject(
ct,
BIGINT_TO_BYTES[ct.type](l - r, rt.config.endianness),
);
}
if (
isPointer(t0) &&
isObjectTypeInfo(t0.referencedType) &&
isIntegerType(t1)
) {
const iv = Number(
bytesToBigint(ro.bytes, isSigned(t1), rt.config.endianness),
);
const pv = Number(
bytesToBigint(lo.bytes, isSigned(t0), rt.config.endianness),
);
res = new TemporaryObject(
t0,
BIGINT_TO_BYTES[t0.type](
BigInt(pv - iv * t0.referencedType.size),
rt.config.endianness,
),
);
}
if (
isPointer(t1) &&
isObjectTypeInfo(t1.referencedType) &&
isIntegerType(t0)
) {
const pv = Number(
bytesToBigint(ro.bytes, isSigned(t1), rt.config.endianness),
);
const iv = Number(
bytesToBigint(lo.bytes, isSigned(t0), rt.config.endianness),
);
res = new TemporaryObject(
t1,
BIGINT_TO_BYTES[t1.type](
BigInt(pv - iv * t1.referencedType.size),
rt.config.endianness,
),
);
}

if (res === undefined) throw new Error("invalid types for - (pointer - pointer not implemented yet)");
rt.stash.pushWithoutConversions(res);
return;
}
case "*":
case "/":
case "%": {
Expand Down Expand Up @@ -690,8 +767,50 @@ export const instructionEvaluator: {
}
case "==":
case "!=": {
// apply usual arithmetic conversions before comparing
break;
let isTruthy: boolean | undefined = undefined;

if (isArithmeticType(t0) && isArithmeticType(t1)) {
let l = bytesToBigint(lo.bytes, isSigned(t0), rt.config.endianness);
let r = bytesToBigint(ro.bytes, isSigned(t1), rt.config.endianness);
const ct = applyUsualArithmeticConversions(t0, t1);
l = convertValue(l, ct, rt.config.endianness);
r = convertValue(r, ct, rt.config.endianness);
switch (op) {
case "==": {
isTruthy = l == r;
break;
}
case "!=": {
isTruthy = l != r;
break;
}
}
}

if (isScalarType(t0) && isScalarType(t1)) { // TODO: improve type checking here for this (supposed to be ptrs)
const l = bytesToBigint(lo.bytes, isSigned(t0), rt.config.endianness);
const r = bytesToBigint(ro.bytes, isSigned(t1), rt.config.endianness);
switch (op) {
case "==": {
isTruthy = l == r;
break;
}
case "!=": {
isTruthy = l != r;
break;
}
}
}

if (isTruthy === undefined)
throw new Error("invalid types for ==, !=");
const res = BIGINT_TO_BYTES[Type.Int](
isTruthy ? BigInt(1) : BigInt(0),
rt.config.endianness,
);
const t = new TemporaryObject(int(), res);
rt.stash.pushWithoutConversions(t);
return;
}
case "<":
case ">":
Expand Down Expand Up @@ -735,11 +854,43 @@ export const instructionEvaluator: {
rt.stash.pushWithoutConversions(t);
return;
}
case "<<":
case ">>":
case "^":
case "&":
case "|": {
// apply usual arithmetic conversions
break;
throw new Error("bitwise binary operators not implemented");
}
case "&&":
case "||": {
// TODO: implement short-circuit evaluation
let isTruthy: boolean | undefined = undefined;

if (isScalarType(t0) && isScalarType(t1)) {
const l = bytesToBigint(lo.bytes, isSigned(t0), rt.config.endianness);
const r = bytesToBigint(ro.bytes, isSigned(t1), rt.config.endianness);
switch (op) {
case "&&": {
isTruthy = (l !== BigInt(0)) && (r !== BigInt(0));
break;
}
case "||": {
isTruthy = (l !== BigInt(0)) || (r !== BigInt(0));
break;
}
}
}

if (isTruthy === undefined)
throw new Error("invalid types for &&, ||");
const res = BIGINT_TO_BYTES[Type.Int](
isTruthy ? BigInt(1) : BigInt(0),
rt.config.endianness,
);
const t = new TemporaryObject(int(), res);
rt.stash.pushWithoutConversions(t);
return;
}
}
throw new Error("unknown binary operator");
Expand Down
49 changes: 47 additions & 2 deletions src/interpreter/object.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import { FunctionType } from "./../typing/types";
import {
FunctionType,
isArray,
isChar,
isPointer,
isScalarType,
isSigned,
isStructure,
} from "./../typing/types";
import { Identifier } from "./../ast/types";
import { ObjectTypeInfo } from "../typing/types";
import { checkValidByte } from "../utils";
import { checkValidByte, decimalAddressToHex } from "../utils";
import { Memory } from "../memory/memory";
import { Endianness } from "../config";
import { bytesToBigint } from "../typing/representation";

interface Identifiable {
identifier: Identifier;
Expand Down Expand Up @@ -113,3 +123,38 @@ export class RuntimeObject
this._identifier = i;
}
}

export const stringify = (
bytes: number[],
t: ObjectTypeInfo,
endianness: Endianness,
): string => {
if (bytes.length !== t.size)
throw new Error("number of bytes do not match type given");
if (isScalarType(t)) {
const n = bytesToBigint(bytes, isSigned(t), endianness);
if (isChar(t)) return "'" + encodeURI(String.fromCharCode(Number(n))) + "'";
if (isPointer(t)) {
if (n === BigInt(0)) return "NULL";
return decimalAddressToHex(Number(n));
}
return n.toString();
}
if (isArray(t)) {
const et = t.elementType;
const res = [];
for (let i = 0; i < t.size; i += et.size) {
res.push(stringify(bytes.slice(i, i + et.size), et, endianness));
}
return "[" + res.join(", ") + "]";
}
if (isStructure(t)) {
const res = [];
for (const i of t.members) {
const b = bytes.slice(i.relativeAddress, i.relativeAddress + i.type.size);
res.push(stringify(b, i.type, endianness));
}
return (t.tag || "") + "{" + res.join(", ") + "}";
}
return "?";
};
Loading

0 comments on commit d1f985e

Please sign in to comment.