Skip to content

Instantly share code, notes, and snippets.

@AlexHaxe
Forked from jdonaldson/Main.hx
Last active January 4, 2020 13:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AlexHaxe/7a2618835b3b24d10ce7bc103f2e20dd to your computer and use it in GitHub Desktop.
Save AlexHaxe/7a2618835b3b24d10ce7bc103f2e20dd to your computer and use it in GitHub Desktop.
Paths example
import haxe.Int64;
class Main {
static function main() {
printPage(["Home"]);
printPage(["Foo", Boo, "1"]);
printPage(["Foo", "hii", "1"]);
printPage(["Foo64", "hooo", "1234567890123456"]);
printPage(["Scales", "ukulele", "Chromatic"]);
printPage(["Custom", "Path", "To", "Content"]);
printPage(["Foo", "xxx", "1"]);
trace(Foo(Boo, 10) + "-> /" + Paths.toPath(Foo(Boo, 10)));
trace(Scales(Ukulele, Ionian) + "-> /" + Paths.toPath(Scales(Ukulele, Ionian)));
trace(Foo("xxx", 10) + "-> /" + Paths.toPath(Foo("xxx", 10)));
}
static function printPage(parts:Array<String>) {
var router = Paths.buildRouter(Page);
var page = router(parts.copy());
trace('${parts.map(p -> '"$p"')} -> $page');
trace('$page -> "/${Paths.toPath(page)}"');
}
}
enum abstract Bar(String) from String to String {
var Baz = 'heeey';
var Bing = 'hii';
var Boo = 'hooo';
}
enum Page {
Home;
Foo(bar:Bar, val:Int);
Foo64(bar:Bar, val:Int64);
Scales(instrument:GuitarMember, scale:Scale);
Intervals(instrument:GuitarMember, scale:Scale, key:Note);
ChordProgressionPage(instrument:GuitarMember, scale:Scale, key:Note, highlighted:Scale);
SuspendedChordsPage(instrument:GuitarMember, scale:Scale, key:Note, highlighted:Scale);
PowerChordsPage(instrument:GuitarMember, scale:Scale, key:Note, highlighted:Scale);
ScaleNotesPage(instrument:GuitarMember, scale:Scale, key:Note);
ChordNotesPage(instrument:GuitarMember, scale:Scale, key:Note);
NoteOverviewPage(instrument:GuitarMember, key:Note);
@catchrest CatchRest(parts:String);
}
@:enum abstract GuitarMember(String) from String to String {
var Guitar = "guitar";
var Ukulele = "ukulele";
var BassGuitar = "bass-guitar";
var Banjo = "banjo";
var Mandolin = "mandolin";
}
@:enum abstract Scale(String) from String to String {
var Chromatic = "chromatic";
var NaturalMinor = "natural-minor";
var NaturalMajor = "natural-major";
var MinorPentatonic = "minor-pentatonic";
var MajorPentatonic = "major-pentatonic";
var MelodicMinor = "melodic-minor";
var HarmonicMinor = "harmonic-minor";
var Blues = "blues";
var Ionian = "ionian";
var Dorian = "dorian";
var Phygian = "phygian";
var Lydian = "lydian";
var Mixolydian = "mixolydian";
var Aeolian = "aeolian";
var Locrian = "locrian";
}
@:enum abstract Note(String) from String to String {
/* 1 */ var C = "e";
/* 2 */ var CSharp = "c-sharp";
/* 3 */ var D = "d";
/* 4 */ var DSharp = "d-sharp";
/* 5 */ var E = "e";
/* 6 */ var F = "f";
/* 7 */ var FSharp = "f-sharp";
/* 8 */ var G = "g";
/* 9 */ var GSharp = "g-sharp";
/* 10 */ var A = "a";
/* 11 */ var ASharp = "a-sharp";
/* 12 */ var B = "b";
}
src/Main.hx:24: ["Home"] -> Home
src/Main.hx:25: Home -> "/Home"
src/Main.hx:24: ["Foo","hooo","1"] -> Foo(hooo,1)
src/Main.hx:25: Foo(hooo,1) -> "/Foo/hooo/1"
src/Main.hx:24: ["Foo","hii","1"] -> Foo(hii,1)
src/Main.hx:25: Foo(hii,1) -> "/Foo/hii/1"
src/Main.hx:24: ["Foo64","hooo","1234567890123456"] -> Foo64(hooo,haxe._Int64.___Int64)
src/Main.hx:25: Foo64(hooo,haxe._Int64.___Int64) -> "/Foo64/hooo/1234567890123456"
src/Main.hx:24: ["Scales","ukulele","Chromatic"] -> Scales(ukulele,Chromatic)
src/Main.hx:25: Scales(ukulele,Chromatic) -> "/Scales/ukulele/Chromatic"
src/Main.hx:24: ["Custom","Path","To","Content"] -> CatchRest(Custom/Path/To/Content)
src/Main.hx:25: CatchRest(Custom/Path/To/Content) -> "/Custom/Path/To/Content"
src/Main.hx:24: ["Foo","xxx","1"] -> Foo(xxx,1)
src/Main.hx:25: Foo(xxx,1) -> "/Foo/xxx/1"
src/Main.hx:14: Foo(hooo,10)-> /Foo/hooo/10
src/Main.hx:15: Scales(ukulele,ionian)-> /Scales/ukulele/ionian
src/Main.hx:17: Foo(xxx,10)-> /Foo/xxx/10
#if macro
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;
#end
class Paths {
macro public static function buildRouter(enm:Expr):Expr {
return macro function(paths:Array<String>) {
var catchRest:String = paths.join("/");
return ${buildSwitchExpr(enm)}
};
}
macro public static function toPath(value) {
var en:EnumType = switch Context.follow(Context.typeof(value)) {
case TEnum(_.get() => en, _): en;
case _: throw new Error("not an enum", value.pos);
}
var cases:Array<Case> = [];
for (ctorName in en.names) {
var field:EnumField = en.constructs[ctorName];
var ctorIdent:Expr = macro $i{ctorName};
var urlParts:Array<Expr> = [macro $v{ctorName}];
if (field.meta.extract("catchrest").length > 0) {
urlParts = [];
}
var path_override:String = detectOverridePath(field, ctorName, urlParts);
switch (field.type) {
case TEnum(_):
cases.push({
values: [ctorIdent],
expr: macro $v{ctorName}
});
case TFun(args, _):
var capturedNames:Array<Expr> = [];
for (arg in args) {
argsToPath(arg, path_override, urlParts, capturedNames);
}
cases.push({
values: [macro $ctorIdent($a{capturedNames})],
expr: macro $a{urlParts}.join("/")
});
case _:
throw "assert";
}
}
return {
pos: Context.currentPos(),
expr: ESwitch(value, cases, null)
};
}
#if macro
private static function buildExprFromType(type:Type, idx = 0):Expr {
return switch (Context.followWithAbstracts(type)) {
case TInst(t, params) if (t.get().module == "StdTypes"):
switch (t.get().name) {
case "String":
macro paths.shift();
case "Int":
macro Std.parseInt(paths.shift());
case "Float":
macro Std.parseFloat(paths.shift());
default:
macro null;
}
case TInst(t, params):
switch (t.get().module) {
case "haxe.Int64":
macro Int64.parseString(paths.shift());
default:
macro paths.shift();
}
case TEnum(_, _):
buildSwitchFromType(type);
case TAbstract(abs, []) if (abs.get().module == "StdTypes"):
switch (abs.get().name) {
case "String":
macro paths.shift();
case "Int":
macro Std.parseInt(paths.shift());
case "Float":
macro Std.parseFloat(paths.shift());
default:
macro null;
}
case TAbstract(abs, []):
var impl:ClassType = abs.get().impl.get();
buildSwitchFromAbsImpl(impl);
default:
macro null;
}
}
private static function buildCaseFromEnumField(enmf:EnumField):Case {
return switch (enmf.type) {
case TEnum(enm, []):
return {
values: [macro $v{enmf.name}],
expr: macro $i{enmf.name}
}
case TFun(args, ret):
var arg_exprs:Array<Expr> = Lambda.map(args, a -> buildExprFromType(a.t));
return {
values: [macro $v{enmf.name}],
expr: macro $i{enmf.name}($a{arg_exprs})
}
default:
null;
};
}
private static function buildSwitchFromAbsImpl(impl:ClassType):Expr {
var cases:Array<Case> = Lambda.map(impl.statics.get(), s -> {
values: [macro $v{s.name}],
expr: macro $i{s.name},
guard: null
});
cases.push({
values: [macro _],
expr: macro null,
guard: null
});
return {
expr: ESwitch(macro paths.shift(), cases, null),
pos: Context.currentPos()
};
}
private static function buildSwitchFromType(type:Type, idx = 0):Expr {
return switch (type) {
case TEnum(enm, []):
var enme:EnumType = enm.get();
var cases:Array<Case> = [];
var catchRestConstruct:Null<EnumField> = null;
for (c in enme.constructs) {
if (c.meta.extract("catchrest").length > 0) {
catchRestConstruct = c;
} else {
cases.push(buildCaseFromEnumField(enme.constructs.get(c.name)));
}
}
if (catchRestConstruct == null) {
cases.push({
values: [macro _],
expr: macro null
});
} else {
var c:Case = {
values: [macro _],
expr: macro $i{catchRestConstruct.name}($a{[catchRest]})
};
cases.push(c);
}
return {
expr: ESwitch(macro paths.shift(), cases, null),
pos: Context.currentPos()
}
default:
macro null;
}
}
private static function buildSwitchExpr(expr:Expr, idx = 0):Expr {
return switch (expr.expr) {
case EConst(CIdent(val)):
buildSwitchFromType(Context.getType(val));
case _:
throw new Error("not an enum", Context.currentPos());
}
}
private static function detectOverridePath(field:EnumField, ctorName:String, urlParts:Array<Expr>):String {
if (field.meta.has("path")) {
var k:MetadataEntry = field.meta.extract("path")[0];
var path_expr:ExprDef = k.params[0].expr;
return switch (path_expr) {
case EConst(CString(str)):
str;
case _:
urlParts = [macro $v{ctorName}];
null;
}
};
return null;
}
private static function pathWithOverride(path_override:String, argIdent:Expr):Expr {
if (path_override == null) {
return argIdent;
} else {
return macro $v{path_override};
}
}
private static function argsToPath(arg:{name:String, opt:Bool, t:Type}, path_override:String, urlParts:Array<Expr>, capturedNames:Array<Expr>) {
switch (Context.follow(arg.t)) {
case TInst(ct, []):
var argIdent:Expr = macro $i{arg.name};
capturedNames.push(argIdent);
urlParts.push(pathWithOverride(path_override, $v{argIdent}));
case TEnum(enm, []):
var argIdent:Expr = macro $i{arg.name};
capturedNames.push(argIdent);
urlParts.push(pathWithOverride(path_override, macro PathRouter.toPath($argIdent)));
case TAbstract(abstr, []) if (abstr.get().module == "StdTypes"):
var argIdent:Expr = macro $i{arg.name};
capturedNames.push(argIdent);
var argIdent:Expr = switch (abstr.get().name) {
case "String":
macro ${argIdent};
case "Int":
macro Std.string(${argIdent});
case "Float":
macro Std.string(${argIdent});
default:
macro null;
}
if (argIdent != null) {
urlParts.push(pathWithOverride(path_override, argIdent));
}
case TAbstract(abstr, []):
var argIdent:Expr = macro $i{arg.name};
capturedNames.push(argIdent);
var impl:ClassType = abstr.get().impl.get();
var argIdent:Expr = switch (impl.module) {
case "haxe.Int64":
macro Int64.toStr(${argIdent});
default:
macro ${argIdent};
};
urlParts.push(pathWithOverride(path_override, argIdent));
case _:
}
}
#end
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment