Link generated Exprs with their source .kiss positions

This commit is contained in:
2020-12-01 12:54:02 -07:00
parent 67ddd1a89e
commit 690362a316
7 changed files with 72 additions and 40 deletions

View File

@@ -3,6 +3,7 @@ package kiss;
import kiss.Reader;
import kiss.List;
using kiss.Stream;
using kiss.Reader;
class CompileError {
@@ -25,10 +26,12 @@ class CompileError {
public function toString() {
var posPrefix = switch (exps.length) {
case 1:
exps[0].pos;
exps[0].pos.toPrint();
default:
var justLineAndColumnIdx = exps[-1].pos.indexOf(":") + 1;
exps[0].pos + '-' + exps[-1].pos.substr(justLineAndColumnIdx);
var firstPos = exps[0].pos.toPrint();
var lastPos = exps[-1].pos.toPrint();
var justLineAndColumnIdx = lastPos.indexOf(":") + 1;
firstPos + '-' + lastPos.substr(justLineAndColumnIdx);
}
return '\nKiss compilation failed!\n'

View File

@@ -75,7 +75,7 @@ class FieldForms {
Helpers.parseComplexType(type, args[0]);
default: null;
}, k.convert(args[1])),
pos: Context.currentPos()
pos: wholeExp.macroPos()
};
}
@@ -89,7 +89,7 @@ class FieldForms {
name: name,
access: access,
kind: FFun(Helpers.makeFunction(args[0], args[1], args.slice(2), k)),
pos: Context.currentPos()
pos: wholeExp.macroPos()
};
}
}

View File

@@ -2,6 +2,7 @@ package kiss;
import haxe.macro.Expr;
import haxe.macro.Context;
import haxe.macro.PositionTools;
import kiss.Reader;
import kiss.CompileError;
import kiss.Kiss;
@@ -12,9 +13,18 @@ using kiss.Helpers;
using StringTools;
class Helpers {
public static function withContextPos(e:ExprDef):Expr {
public static function macroPos(exp:ReaderExp) {
var kissPos = exp.pos;
return PositionTools.make({
min: kissPos.absoluteChar,
max: kissPos.absoluteChar,
file: kissPos.file
});
}
public static function withMacroPosOf(e:ExprDef, exp:ReaderExp):Expr {
return {
pos: Context.currentPos(),
pos: macroPos(exp),
expr: e
};
}
@@ -67,7 +77,7 @@ class Helpers {
}
// TODO generic type parameter declarations
public static function makeFunction(?name:ReaderExp, argList:ReaderExp, body:Array<ReaderExp>, k:KissState):Function {
public static function makeFunction(?name:ReaderExp, argList:ReaderExp, body:List<ReaderExp>, k:KissState):Function {
var funcName = if (name != null) {
switch (name.def) {
case Symbol(name) | TypedExp(_, {pos: _, def: Symbol(name)}):
@@ -102,7 +112,7 @@ class Helpers {
var realCallArgs = args.slice(0, numArgs);
var restArgs = args.slice(numArgs);
realCallArgs.push(ListExp(restArgs).withPosOf(wholeExp));
ECall(k.convert(Symbol(funcName).withPosOf(wholeExp)), realCallArgs.map(k.convert)).withContextPos();
ECall(k.convert(Symbol(funcName).withPosOf(wholeExp)), realCallArgs.map(k.convert)).withMacroPosOf(wholeExp);
};
opt = true;
@@ -148,7 +158,7 @@ class Helpers {
default:
throw CompileError.fromExp(argList, 'expected an argument list');
},
expr: EReturn(k.convert(CallExp(Symbol("begin").withPos(body[0].pos), body).withPos(body[0].pos))).withContextPos()
expr: EReturn(k.convert(CallExp(Symbol("begin").withPos(body[0].pos), body).withPos(body[0].pos))).withMacroPosOf(body[-1])
}
}

View File

@@ -107,32 +107,30 @@ class Kiss {
var convert = readerExpToHaxeExpr.bind(_, k);
var expr = switch (exp.def) {
case Symbol(name):
Context.parse(name, Context.currentPos());
Context.parse(name, exp.macroPos());
case StrExp(s):
{
pos: Context.currentPos(),
expr: EConst(CString(s))
};
EConst(CString(s)).withMacroPosOf(exp);
case CallExp({pos: _, def: Symbol(mac)}, args) if (macros.exists(mac)):
convert(macros[mac](exp, args, k));
case CallExp({pos: _, def: Symbol(specialForm)}, args) if (specialForms.exists(specialForm)):
specialForms[specialForm](exp, args, k);
case CallExp(func, args):
ECall(convert(func), [for (argExp in args) convert(argExp)]).withContextPos();
ECall(convert(func), [for (argExp in args) convert(argExp)]).withMacroPosOf(exp);
/*
// Typed expressions in the wild become casts:
case TypedExp(type, innerExp):
ECast(convert(innerExp), if (type.length > 0) Helpers.parseComplexType(type, exp) else null).withContextPos();
ECast(convert(innerExp), if (type.length > 0) Helpers.parseComplexType(type, exp) else null).withMacroPosOf(wholeExp);
*/
case ListExp(elements):
ENew({
pack: ["kiss"],
name: "List"
},
[EArrayDecl([for (elementExp in elements) convert(elementExp)]).withContextPos()]).withContextPos();
}, [
EArrayDecl([for (elementExp in elements) convert(elementExp)]).withMacroPosOf(exp)
]).withMacroPosOf(exp);
case RawHaxe(code):
Context.parse(code, Context.currentPos());
Context.parse(code, exp.macroPos());
default:
throw CompileError.fromExp(exp, 'conversion not implemented');
};

View File

@@ -114,14 +114,14 @@ class Macros {
k.specialForms[macroName] = (wholeExp:ReaderExp, callArgs:Array<ReaderExp>, convert) -> {
// Macro functions don't need to check their argument numbers
// because macro function calls expand to function calls that the Haxe compiler will check
ECall(Context.parse('${k.className}.${macroName}', Context.currentPos()), [
ECall(Context.parse('${k.className}.${macroName}', wholeExp.macroPos()), [
for (callArg in callArgs)
EFunction(FArrow, {
args: [],
ret: null,
expr: EReturn(k.convert(callArg)).withContextPos()
}).withContextPos()
]).withContextPos();
expr: EReturn(k.convert(callArg)).withMacroPosOf(wholeExp)
}).withMacroPosOf(wholeExp)
]).withMacroPosOf(wholeExp);
};
CallExp(Symbol("defun").withPosOf(wholeExp), exps).withPosOf(wholeExp);

View File

@@ -22,18 +22,18 @@ class SpecialForms {
map["begin"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
// Sometimes empty blocks are useful, so a checkNumArgs() seems unnecessary here for now.
EBlock([for (bodyExp in args) k.convert(bodyExp)]).withContextPos();
EBlock([for (bodyExp in args) k.convert(bodyExp)]).withMacroPosOf(wholeExp);
};
map["nth"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
wholeExp.checkNumArgs(2, 2, "(nth [list] [idx])");
EArray(k.convert(args[0]), k.convert(args[1])).withContextPos();
EArray(k.convert(args[0]), k.convert(args[1])).withMacroPosOf(wholeExp);
};
function makeQuickNth(idx:Int, name:String) {
map[name] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
wholeExp.checkNumArgs(1, 1, '($name [list])');
EArray(k.convert(args[0]), macro $v{idx}).withContextPos();
EArray(k.convert(args[0]), macro $v{idx}).withMacroPosOf(wholeExp);
};
}
makeQuickNth(0, "first");
@@ -58,12 +58,12 @@ class SpecialForms {
case Symbol(name): name;
default: throw CompileError.fromExp(args[0], 'first arg in (new [type] ...) should be a class to instantiate');
};
ENew(Helpers.parseTypePath(classType, args[0]), args.slice(1).map(k.convert)).withContextPos();
ENew(Helpers.parseTypePath(classType, args[0]), args.slice(1).map(k.convert)).withMacroPosOf(wholeExp);
};
map["set"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
wholeExp.checkNumArgs(2, 2, "(set [variable] [value])");
EBinop(OpAssign, k.convert(args[0]), k.convert(args[1])).withContextPos();
EBinop(OpAssign, k.convert(args[0]), k.convert(args[1])).withMacroPosOf(wholeExp);
};
function toVar(nameExp:ReaderExp, valueExp:ReaderExp, k:KissState, ?isFinal:Bool):Var {
@@ -120,7 +120,7 @@ class SpecialForms {
map["deflocal"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
wholeExp.checkNumArgs(2, 3, "(deflocal [optional :type] [variable] [optional: &mut] [value])");
EVars(toVars(args[0], args[1], k)).withContextPos();
EVars(toVars(args[0], args[1], k)).withMacroPosOf(wholeExp);
};
map["let"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
@@ -142,12 +142,15 @@ class SpecialForms {
throw CompileError.fromArgs(args, '(let....) expression needs a body');
}
EBlock([EVars(varDefs).withContextPos(), EBlock(body.map(k.convert)).withContextPos()]).withContextPos();
EBlock([
EVars(varDefs).withMacroPosOf(wholeExp),
EBlock(body.map(k.convert)).withMacroPosOf(wholeExp)
]).withMacroPosOf(wholeExp);
};
map["lambda"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
wholeExp.checkNumArgs(2, null, "(lambda [[argsNames...]] [body...])");
EFunction(FArrow, Helpers.makeFunction(null, args[0], args.slice(1), k)).withContextPos();
EFunction(FArrow, Helpers.makeFunction(null, args[0], args.slice(1), k)).withMacroPosOf(wholeExp);
};
function forExpr(formName:String, wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) {
@@ -158,12 +161,13 @@ class SpecialForms {
var bodyExps = args.slice(2);
bodyExps.insert(0, CallExp(Symbol("deflocal").withPosOf(args[2]), [namesExp, Symbol(uniqueVarName).withPosOf(args[2])]).withPosOf(args[2]));
var body = CallExp(Symbol("begin").withPosOf(args[2]), bodyExps).withPosOf(args[2]);
return EFor(EBinop(OpIn, EConst(CIdent(uniqueVarName)).withContextPos(), k.convert(listExp)).withContextPos(), k.convert(body)).withContextPos();
return EFor(EBinop(OpIn, EConst(CIdent(uniqueVarName)).withMacroPosOf(wholeExp), k.convert(listExp)).withMacroPosOf(wholeExp),
k.convert(body)).withMacroPosOf(wholeExp);
}
map["doFor"] = forExpr.bind("doFor");
map["for"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
EArrayDecl([forExpr("for", wholeExp, args, k)]).withContextPos();
EArrayDecl([forExpr("for", wholeExp, args, k)]).withMacroPosOf(wholeExp);
};
// TODO (case... ) for switch
@@ -174,7 +178,7 @@ class SpecialForms {
ECheckType(k.convert(args[1]), switch (args[0].def) {
case Symbol(type): Helpers.parseComplexType(type, args[0]);
default: throw CompileError.fromExp(args[0], 'first argument to (the... ) should be a valid type');
}).withContextPos();
}).withMacroPosOf(wholeExp);
};
map["try"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
@@ -207,14 +211,14 @@ class SpecialForms {
'expressions following the first expression in a (try... ) should all be (catch [[error]] [body...]) expressions');
}
}
]).withContextPos();
]).withMacroPosOf(wholeExp);
};
map["throw"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
if (args.length != 1) {
throw CompileError.fromExp(wholeExp, 'throw expression should only throw one value');
}
EThrow(k.convert(args[0])).withContextPos();
EThrow(k.convert(args[0])).withMacroPosOf(wholeExp);
};
map["<"] = foldComparison("_min");
@@ -257,7 +261,7 @@ class SpecialForms {
return (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
var callFoldMacroExpr = k.convert(CallExp(Symbol(func).withPosOf(wholeExp), args).withPosOf(wholeExp));
wholeExp.checkNumArgs(1, null);
EBinop(OpEq, k.convert(args[0]), macro ${callFoldMacroExpr}.toDynamic()).withContextPos();
EBinop(OpEq, k.convert(args[0]), macro ${callFoldMacroExpr}.toDynamic()).withMacroPosOf(wholeExp);
};
}
}

View File

@@ -6,13 +6,19 @@ import haxe.ds.Option;
using StringTools;
using Lambda;
typedef Position = String;
typedef Position = {
file:String,
line:Int,
column:Int,
absoluteChar:Int
};
class Stream {
var content:String;
var file:String;
var line:Int;
var column:Int;
var absoluteChar:Int;
public function new(file:String) {
// Banish ye Windows line-endings
@@ -24,6 +30,7 @@ class Stream {
this.file = file;
line = 1;
column = 1;
absoluteChar = 0;
}
public function peekChars(chars:Int):Option<String> {
@@ -37,7 +44,16 @@ class Stream {
}
public function position():Position {
return '$file:$line:$column';
return {
file: file,
line: line,
column: column,
absoluteChar: absoluteChar
};
}
public static function toPrint(p:Position) {
return '${p.file}:${p.line}:${p.column}';
}
public function startsWith(s:String) {
@@ -50,6 +66,7 @@ class Stream {
/** Every drop call should end up calling dropChars() or the position tracker will be wrong. **/
private function dropChars(count:Int) {
for (idx in 0...count) {
absoluteChar += 1;
switch (content.charAt(idx)) {
case "\n":
line += 1;