Skip to content

Commit

Permalink
Merge pull request sampsyo#172 from sampsyo/brilck-pos
Browse files Browse the repository at this point in the history
brilck: Report source positions
  • Loading branch information
sampsyo authored Feb 8, 2022
2 parents 19c548e + dddfce9 commit 371ff89
Show file tree
Hide file tree
Showing 17 changed files with 119 additions and 70 deletions.
10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,20 @@ TESTS := test/parse/*.bril \
examples/test/*/*.bril \
benchmarks/*.bril

CHECKS := test/parse/*.bril \
test/interp/*.bril \
test/mem/*.bril \
examples/test/*/*.bril \
benchmarks/*.bril

.PHONY: test
test:
turnt $(TURNTARGS) $(TESTS)

.PHONY: check
check:
for fn in $(CHECKS) ; do bril2json -p < $$fn | brilck $$fn ; done

.PHONY: book
book:
rm -rf book
Expand Down
11 changes: 10 additions & 1 deletion bril-ts/bril.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,26 @@ export type PrimType = "int" | "bool" | "float";
/**
* Parameterized types. (We only have pointers for now.)
*/
export type ParamType = {"ptr": Type};
export type ParamType = {ptr: Type};

/**
* Value types.
*/
export type Type = PrimType | ParamType;

/**
* An (always optional) source code position.
*/
export type Position = {row: number, col: number};

/**
* Common fields in any operation.
*/
interface Op {
args?: Ident[];
funcs?: Ident[];
labels?: Ident[];
pos?: Position;
}

/**
Expand Down Expand Up @@ -70,6 +76,7 @@ export interface Constant {
value: Value;
dest: Ident;
type: Type;
pos?: Position;
}


Expand Down Expand Up @@ -109,6 +116,7 @@ export type OpCode = ValueOpCode | EffectOpCode;
*/
export interface Label {
label: Ident;
pos?: Position;
}

/*
Expand All @@ -127,6 +135,7 @@ export interface Function {
args?: Argument[];
instrs: (Instruction | Label)[];
type?: Type;
pos?: Position;
}

/**
Expand Down
87 changes: 57 additions & 30 deletions bril-ts/brilck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,35 @@ interface Env {
ret: bril.Type | undefined;
}

/**
* An optional filename for error messages.
*/
let CHECK_FILE: string | undefined;

/**
* Print an error message, possibly with a source position.
*/
function err(msg: string, pos: bril.Position | undefined) {
if (pos) {
msg = `${pos.row}:${pos.col}: ${msg}`;
}
if (CHECK_FILE) {
msg = `${CHECK_FILE}:${msg}`;
}
console.error(msg);
}

/**
* Set the type of variable `id` to `type` in `env`, checking for conflicts
* with the old type for the variable.
*/
function addType(env: VarEnv, id: bril.Ident, type: bril.Type) {
function addType(env: VarEnv, id: bril.Ident, type: bril.Type, pos: bril.Position | undefined) {
let oldType = env.get(id);
if (oldType) {
if (!typeEq(oldType, type)) {
console.error(
`new type ${type} for ${id} conflicts with old type ${oldType}`
err(
`new type ${type} for ${id} conflicts with old type ${oldType}`,
pos
);
}
} else {
Expand Down Expand Up @@ -152,39 +171,43 @@ function checkSig(env: Env, instr: bril.Operation, psig: Signature | PolySignatu
if ('type' in instr) {
if (sig.dest) {
if (!typeEq(instr.type, sig.dest, tenv)) {
console.error(
err(
`result type of ${name} should be ${typeFmt(typeLookup(sig.dest, tenv))}, ` +
`but found ${typeFmt(instr.type)}`
`but found ${typeFmt(instr.type)}`,
instr.pos
);
}
} else {
console.error(`${name} should have no result type`);
err(`${name} should have no result type`, instr.pos);
}
} else {
if (sig.dest) {
console.error(
`missing result type ${typeFmt(typeLookup(sig.dest, tenv))} for ${name}`
err(
`missing result type ${typeFmt(typeLookup(sig.dest, tenv))} for ${name}`,
instr.pos
);
}
}

// Check arguments.
let args = instr.args ?? [];
if (args.length !== sig.args.length) {
console.error(
`${name} expects ${sig.args.length} args, not ${args.length}`
err(
`${name} expects ${sig.args.length} args, not ${args.length}`,
instr.pos
);
} else {
for (let i = 0; i < args.length; ++i) {
let argType = env.vars.get(args[i]);
if (!argType) {
console.error(`${args[i]} (arg ${i}) undefined`);
err(`${args[i]} (arg ${i}) undefined`, instr.pos);
continue;
}
if (!typeEq(argType, sig.args[i], tenv)) {
console.error(
err(
`${args[i]} has type ${typeFmt(argType)}, but arg ${i} for ${name} ` +
`should have type ${typeFmt(typeLookup(sig.args[i], tenv))}`
`should have type ${typeFmt(typeLookup(sig.args[i], tenv))}`,
instr.pos
);
}
}
Expand All @@ -194,11 +217,11 @@ function checkSig(env: Env, instr: bril.Operation, psig: Signature | PolySignatu
let labs = instr.labels ?? [];
let labCount = sig.labels ?? 0;
if (labs.length !== labCount) {
console.error(`${instr.op} needs ${labCount} labels; found ${labs.length}`);
err(`${instr.op} needs ${labCount} labels; found ${labs.length}`, instr.pos);
} else {
for (let lab of labs) {
if (!env.labels.has(lab)) {
console.error(`label .${lab} undefined`);
err(`label .${lab} undefined`, instr.pos);
}
}
}
Expand All @@ -212,20 +235,20 @@ type CheckFunc = (env: Env, instr: bril.Operation) => void;
const INSTR_CHECKS: {[key: string]: CheckFunc} = {
print: (env, instr) => {
if ('type' in instr) {
console.error(`print should have no result type`);
err(`print should have no result type`, instr.pos);
}
},

call: (env, instr) => {
let funcs = instr.funcs ?? [];
if (funcs.length !== 1) {
console.error(`call should have one function, not ${funcs.length}`);
err(`call should have one function, not ${funcs.length}`, instr.pos);
return;
}

let funcType = env.funcs.get(funcs[0]);
if (!funcType) {
console.error(`function @${funcs[0]} undefined`);
err(`function @${funcs[0]} undefined`, instr.pos);
return;
}

Expand All @@ -240,15 +263,15 @@ const INSTR_CHECKS: {[key: string]: CheckFunc} = {
let args = instr.args ?? [];
if (env.ret) {
if (args.length === 0) {
console.error(`missing return value in function with return type`);
err(`missing return value in function with return type`, instr.pos);
} else if (args.length !== 1) {
console.error(`cannot return multiple values`);
err(`cannot return multiple values`, instr.pos);
} else {
checkSig(env, instr, {args: [env.ret]});
}
} else {
if (args.length !== 0) {
console.error(`returning value in function without a return type`);
err(`returning value in function without a return type`, instr.pos);
}
}
return;
Expand All @@ -268,31 +291,32 @@ function checkOp(env: Env, instr: bril.Operation) {
// General case: use the operation's signature.
let sig = OP_SIGS[instr.op];
if (!sig) {
console.error(`unknown opcode ${instr.op}`);
err(`unknown opcode ${instr.op}`, instr.pos);
return;
}
checkSig(env, instr, sig);
}

function checkConst(instr: bril.Constant) {
if (!('type' in instr)) {
console.error(`const missing type`);
err(`const missing type`, instr!.pos);
return;
}
if (typeof instr.type !== 'string') {
console.error(`const of non-primitive type ${typeFmt(instr.type)}`);
err(`const of non-primitive type ${typeFmt(instr.type)}`, instr.pos);
return;
}

let valType = CONST_TYPES[instr.type];
if (!valType) {
console.error(`unknown const type ${typeFmt(instr.type)}`);
err(`unknown const type ${typeFmt(instr.type)}`, instr.pos);
return;
}

if (typeof instr.value !== valType) {
console.error(
`const value ${instr.value} does not match type ${typeFmt(instr.type)}`
err(
`const value ${instr.value} does not match type ${typeFmt(instr.type)}`,
instr.pos
);
}
}
Expand All @@ -304,17 +328,17 @@ function checkFunc(funcs: FuncEnv, func: bril.Function) {
// Initilize the type environment with the arguments.
if (func.args) {
for (let arg of func.args) {
addType(vars, arg.name, arg.type);
addType(vars, arg.name, arg.type, func.pos);
}
}

// Gather up all the types of the local variables and all the label names.
for (let instr of func.instrs) {
if ('dest' in instr) {
addType(vars, instr.dest, instr.type);
addType(vars, instr.dest, instr.type, instr.pos);
} else if ('label' in instr) {
if (labels.has(instr.label)) {
console.error(`multiply defined label .${instr.label}`);
err(`multiply defined label .${instr.label}`, instr.pos);
} else {
labels.add(instr.label);
}
Expand Down Expand Up @@ -350,6 +374,9 @@ function checkProg(prog: bril.Program) {
}

async function main() {
if (process.argv[2]) {
CHECK_FILE = process.argv[2];
}
let prog = JSON.parse(await readStdin()) as bril.Program;
checkProg(prog);
}
Expand Down
3 changes: 3 additions & 0 deletions docs/tools/brilck.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,6 @@ Just pipe a Bril program into `brilck`:

It will print any problems it finds to standard error.
(If it doesn't find any problems, it doesn't print anything at all.)

You can optionally provide a filename as a (sole) command-line argument.
This filename will appear in any error messages for easier parsing when many files are involved.
2 changes: 1 addition & 1 deletion test/check/argtype.err
Original file line number Diff line number Diff line change
@@ -1 +1 @@
b has type bool, but arg 1 for add should have type int
2:3: b has type bool, but arg 1 for add should have type int
18 changes: 9 additions & 9 deletions test/check/badcall.err
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
returning value in function without a return type
missing return value in function with return type
function @foo undefined
@nothing should have no result type
call should have one function, not 2
result type of @retint should be int, but found bool
b has type bool, but arg 0 for @argint should have type int
@argint expects 1 args, not 0
@argint expects 1 args, not 2
14:3: returning value in function without a return type
18:3: missing return value in function with return type
22:3: function @foo undefined
23:3: @nothing should have no result type
24:3: call should have one function, not 2
25:3: result type of @retint should be int, but found bool
26:3: b has type bool, but arg 0 for @argint should have type int
27:3: @argint expects 1 args, not 0
28:3: @argint expects 1 args, not 2
8 changes: 4 additions & 4 deletions test/check/badconst.err
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const value 4 does not match type bool
const value true does not match type int
const of non-primitive type ptr<int>
unknown const type blah
2:3: const value 4 does not match type bool
3:3: const value true does not match type int
4:3: const of non-primitive type ptr<int>
5:3: unknown const type blah
6 changes: 3 additions & 3 deletions test/check/badid.err
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
a has type int, but arg 0 for id should have type bool
missing result type T for id
id expects 1 args, not 0
2:3: a has type int, but arg 0 for id should have type bool
3:3: missing result type T for id
4:3: id expects 1 args, not 0
20 changes: 10 additions & 10 deletions test/check/badmem.err
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
f has type float, but arg 0 for alloc should have type int
result type of alloc should be ptr<T>, but found int
f has type float, but arg 1 for store should have type int
i has type int, but arg 0 for store should have type ptr<T>
p has type ptr<int>, but arg 0 for load should have type ptr<float>
i has type int, but arg 0 for load should have type ptr<int>
p has type ptr<int>, but arg 0 for ptradd should have type ptr<float>
i has type int, but arg 0 for ptradd should have type ptr<int>
p has type ptr<int>, but arg 1 for ptradd should have type int
i has type int, but arg 0 for free should have type ptr<T>
2:3: f has type float, but arg 0 for alloc should have type int
3:3: result type of alloc should be ptr<T>, but found int
5:3: f has type float, but arg 1 for store should have type int
6:3: i has type int, but arg 0 for store should have type ptr<T>
8:3: p has type ptr<int>, but arg 0 for load should have type ptr<float>
9:3: i has type int, but arg 0 for load should have type ptr<int>
11:3: p has type ptr<int>, but arg 0 for ptradd should have type ptr<float>
12:3: i has type int, but arg 0 for ptradd should have type ptr<int>
12:3: p has type ptr<int>, but arg 1 for ptradd should have type int
14:3: i has type int, but arg 0 for free should have type ptr<T>
2 changes: 1 addition & 1 deletion test/check/extra.err
Original file line number Diff line number Diff line change
@@ -1 +1 @@
add expects 2 args, not 3
2:3: add expects 2 args, not 3
10 changes: 5 additions & 5 deletions test/check/labels.err
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
multiply defined label .bar
label .bad undefined
br needs 2 labels; found 1
br needs 2 labels; found 3
not needs 0 labels; found 1
8:1: multiply defined label .bar
2:3: label .bad undefined
3:3: br needs 2 labels; found 1
4:3: br needs 2 labels; found 3
5:3: not needs 0 labels; found 1
2 changes: 1 addition & 1 deletion test/check/missarg.err
Original file line number Diff line number Diff line change
@@ -1 +1 @@
add expects 2 args, not 1
2:3: add expects 2 args, not 1
2 changes: 1 addition & 1 deletion test/check/missdest.err
Original file line number Diff line number Diff line change
@@ -1 +1 @@
missing result type int for add
2:3: missing result type int for add
2 changes: 1 addition & 1 deletion test/check/printres.err
Original file line number Diff line number Diff line change
@@ -1 +1 @@
print should have no result type
2:3: print should have no result type
2 changes: 1 addition & 1 deletion test/check/ptr.err
Original file line number Diff line number Diff line change
@@ -1 +1 @@
a has type ptr<int>, but arg 0 for id should have type ptr<float>
3:3: a has type ptr<int>, but arg 0 for id should have type ptr<float>
2 changes: 1 addition & 1 deletion test/check/typeconflict.err
Original file line number Diff line number Diff line change
@@ -1 +1 @@
new type int for a conflicts with old type bool
3:3: new type int for a conflicts with old type bool
2 changes: 1 addition & 1 deletion test/check/undef.err
Original file line number Diff line number Diff line change
@@ -1 +1 @@
a (arg 0) undefined
2:3: a (arg 0) undefined

0 comments on commit 371ff89

Please sign in to comment.