make (load) act like inserting the loaded code at that position. Close #24
This commit is contained in:
@@ -29,10 +29,14 @@ typedef KissState = {
|
|||||||
specialForms:Map<String, SpecialFormFunction>,
|
specialForms:Map<String, SpecialFormFunction>,
|
||||||
macros:Map<String, MacroFunction>,
|
macros:Map<String, MacroFunction>,
|
||||||
wrapListExps:Bool,
|
wrapListExps:Bool,
|
||||||
loadedFiles:Map<String, Bool>,
|
loadedFiles:Map<String, Null<ReaderExp>>,
|
||||||
callAliases:Map<String, ReaderExpDef>,
|
callAliases:Map<String, ReaderExpDef>,
|
||||||
identAliases:Map<String, ReaderExpDef>,
|
identAliases:Map<String, ReaderExpDef>,
|
||||||
fields:Array<Field>,
|
fieldList:Array<Field>,
|
||||||
|
// TODO This map was originally created to track whether the programmer wrote their own main function, but could also
|
||||||
|
// be used to allow macros to edit fields that were already defined (for instance, to decorate a function or add something
|
||||||
|
// to the constructor body)
|
||||||
|
fieldDict:Map<String, Field>,
|
||||||
loadingDirectory:String,
|
loadingDirectory:String,
|
||||||
hscript:Bool
|
hscript:Bool
|
||||||
};
|
};
|
||||||
@@ -49,7 +53,7 @@ class Kiss {
|
|||||||
specialForms: SpecialForms.builtins(),
|
specialForms: SpecialForms.builtins(),
|
||||||
macros: Macros.builtins(),
|
macros: Macros.builtins(),
|
||||||
wrapListExps: true,
|
wrapListExps: true,
|
||||||
loadedFiles: new Map<String, Bool>(),
|
loadedFiles: new Map<String, ReaderExp>(),
|
||||||
// Helpful built-in aliases
|
// Helpful built-in aliases
|
||||||
// These ones might conflict with a programmer's variable names, so they only apply in call expressions:
|
// These ones might conflict with a programmer's variable names, so they only apply in call expressions:
|
||||||
callAliases: [
|
callAliases: [
|
||||||
@@ -93,7 +97,8 @@ class Kiss {
|
|||||||
"zipThrow" => Symbol("Prelude.zipThrow"),
|
"zipThrow" => Symbol("Prelude.zipThrow"),
|
||||||
"joinPath" => Symbol("Prelude.joinPath"),
|
"joinPath" => Symbol("Prelude.joinPath"),
|
||||||
],
|
],
|
||||||
fields: [],
|
fieldList: [],
|
||||||
|
fieldDict: new Map(),
|
||||||
loadingDirectory: "",
|
loadingDirectory: "",
|
||||||
hscript: false
|
hscript: false
|
||||||
};
|
};
|
||||||
@@ -144,37 +149,62 @@ class Kiss {
|
|||||||
if (k == null)
|
if (k == null)
|
||||||
k = defaultKissState();
|
k = defaultKissState();
|
||||||
|
|
||||||
if (useClassFields)
|
if (useClassFields) {
|
||||||
k.fields = Context.getBuildFields();
|
k.fieldList = Context.getBuildFields();
|
||||||
|
for (field in k.fieldList) {
|
||||||
|
k.fieldDict[field.name] = field;
|
||||||
|
}
|
||||||
|
}
|
||||||
k.loadingDirectory = loadingDirectory;
|
k.loadingDirectory = loadingDirectory;
|
||||||
|
|
||||||
load(kissFile, k);
|
var topLevelBegin = load(kissFile, k);
|
||||||
|
|
||||||
k.fields;
|
if (topLevelBegin != null) {
|
||||||
|
|
||||||
|
// TODO this is where an error would be thrown, or main() would be generated from top-level expressions
|
||||||
|
// TODO There are two ideas for how to handle expressions at the top level of a Kiss file:
|
||||||
|
// 1. Throw an error, because the top level should only contain field definitions
|
||||||
|
// 2. Append the expression to the body of an automatically generated main() function
|
||||||
|
// if that result is non-null, try to make main (but this may cause a duplicate declaration)
|
||||||
|
}
|
||||||
|
|
||||||
|
k.fieldList;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function load(kissFile:String, k:KissState, ?loadingDirectory:String) {
|
public static function load(kissFile:String, k:KissState, ?loadingDirectory:String):Null<ReaderExp> {
|
||||||
if (loadingDirectory == null)
|
if (loadingDirectory == null)
|
||||||
loadingDirectory = k.loadingDirectory;
|
loadingDirectory = k.loadingDirectory;
|
||||||
|
|
||||||
var fullPath = Path.join([loadingDirectory, kissFile]);
|
var fullPath = Path.join([loadingDirectory, kissFile]);
|
||||||
if (k.loadedFiles.exists(fullPath)) {
|
if (k.loadedFiles.exists(fullPath)) {
|
||||||
return;
|
return k.loadedFiles[fullPath];
|
||||||
}
|
}
|
||||||
k.loadedFiles[fullPath] = true;
|
|
||||||
var stream = Stream.fromFile(fullPath);
|
var stream = Stream.fromFile(fullPath);
|
||||||
|
var startPosition = stream.position();
|
||||||
|
var loadedExps = [];
|
||||||
Reader.readAndProcess(stream, k, (nextExp) -> {
|
Reader.readAndProcess(stream, k, (nextExp) -> {
|
||||||
#if test
|
#if test
|
||||||
Sys.println(nextExp.def.toString());
|
Sys.println(nextExp.def.toString());
|
||||||
#end
|
#end
|
||||||
|
|
||||||
|
// readerExpToHaxeExpr must be called to process readermacro, alias, and macro definitions
|
||||||
var expr = readerExpToHaxeExpr(nextExp, k);
|
var expr = readerExpToHaxeExpr(nextExp, k);
|
||||||
|
|
||||||
// TODO There are two ideas for how to handle expressions at the top level of a Kiss file:
|
// exps in the loaded file that actually become haxe expressions can be inserted into the file that loaded them at the position
|
||||||
// 1. Throw an error, because the top level should only contain field definitions
|
// (load) was called
|
||||||
// 2. Append the expression to the body of an automatically generated main() function
|
if (expr != null) {
|
||||||
|
loadedExps.push(nextExp);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var exp = if (loadedExps.length > 0) {
|
||||||
|
CallExp(Symbol("begin").withPos(startPosition), loadedExps).withPos(startPosition);
|
||||||
|
} else {
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
k.loadedFiles[fullPath] = exp;
|
||||||
|
return exp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -184,14 +214,18 @@ class Kiss {
|
|||||||
if (k == null)
|
if (k == null)
|
||||||
k = defaultKissState();
|
k = defaultKissState();
|
||||||
|
|
||||||
if (useClassFields)
|
if (useClassFields) {
|
||||||
k.fields = Context.getBuildFields();
|
k.fieldList = Context.getBuildFields();
|
||||||
|
for (field in k.fieldList) {
|
||||||
|
k.fieldDict[field.name] = field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (file in kissFiles) {
|
for (file in kissFiles) {
|
||||||
build(file, k, false);
|
build(file, k, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return k.fields;
|
return k.fieldList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function readerExpToHaxeExpr(exp:ReaderExp, k:KissState):Null<Expr> {
|
public static function readerExpToHaxeExpr(exp:ReaderExp, k:KissState):Null<Expr> {
|
||||||
@@ -216,7 +250,9 @@ class Kiss {
|
|||||||
case StrExp(s):
|
case StrExp(s):
|
||||||
EConst(CString(s)).withMacroPosOf(exp);
|
EConst(CString(s)).withMacroPosOf(exp);
|
||||||
case CallExp({pos: _, def: Symbol(ff)}, args) if (fieldForms.exists(ff)):
|
case CallExp({pos: _, def: Symbol(ff)}, args) if (fieldForms.exists(ff)):
|
||||||
k.fields.push(fieldForms[ff](exp, args, k));
|
var field = fieldForms[ff](exp, args, k);
|
||||||
|
k.fieldList.push(field);
|
||||||
|
k.fieldDict[field.name] = field;
|
||||||
null; // Field forms are no-ops
|
null; // Field forms are no-ops
|
||||||
case CallExp({pos: _, def: Symbol(mac)}, args) if (macros.exists(mac)):
|
case CallExp({pos: _, def: Symbol(mac)}, args) if (macros.exists(mac)):
|
||||||
var expanded = macros[mac](exp, args, k);
|
var expanded = macros[mac](exp, args, k);
|
||||||
|
|||||||
@@ -35,11 +35,10 @@ class Macros {
|
|||||||
wholeExp.checkNumArgs(1, 1, '(load "[file]")');
|
wholeExp.checkNumArgs(1, 1, '(load "[file]")');
|
||||||
switch (args[0].def) {
|
switch (args[0].def) {
|
||||||
case StrExp(otherKissFile):
|
case StrExp(otherKissFile):
|
||||||
Kiss.load(otherKissFile, k);
|
return Kiss.load(otherKissFile, k);
|
||||||
default:
|
default:
|
||||||
throw CompileError.fromExp(args[0], "only argument to load should be a string literal of a .kiss file path");
|
throw CompileError.fromExp(args[0], "only argument to load should be a string literal of a .kiss file path");
|
||||||
}
|
}
|
||||||
null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
macros["loadFrom"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
|
macros["loadFrom"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
|
||||||
|
|||||||
Reference in New Issue
Block a user