Fix tab/space format

This commit is contained in:
2020-11-14 17:26:54 -07:00
parent 3e074776d6
commit fadd9d32e2
11 changed files with 454 additions and 449 deletions

5
hxformat.json Normal file
View File

@@ -0,0 +1,5 @@
{
"indentation": {
"character": " "
}
}

View File

@@ -11,89 +11,89 @@ using StringTools;
typedef FieldFormFunction = (position:String, args:Array<ReaderExp>, convert:ExprConversion) -> Field; typedef FieldFormFunction = (position:String, args:Array<ReaderExp>, convert:ExprConversion) -> Field;
class FieldForms { class FieldForms {
public static function builtins() { public static function builtins() {
var map:Map<String, FieldFormFunction> = []; var map:Map<String, FieldFormFunction> = [];
map["defvar"] = varOrProperty.bind("defvar"); map["defvar"] = varOrProperty.bind("defvar");
map["defprop"] = varOrProperty.bind("defprop"); map["defprop"] = varOrProperty.bind("defprop");
map["defun"] = funcOrMethod.bind("defun"); map["defun"] = funcOrMethod.bind("defun");
map["defmethod"] = funcOrMethod.bind("defmethod"); map["defmethod"] = funcOrMethod.bind("defmethod");
return map; return map;
} }
static function fieldAccess(formName:String, fieldName:String) { static function fieldAccess(formName:String, fieldName:String) {
var access = []; var access = [];
if (formName == "defvar" || formName == "defun") { if (formName == "defvar" || formName == "defun") {
access.push(AStatic); access.push(AStatic);
} }
access.push(if (fieldName.startsWith("_")) APrivate else APublic); access.push(if (fieldName.startsWith("_")) APrivate else APublic);
return access; return access;
} }
static function fieldName(formName:String, position:String, nameExp:ReaderExp) { static function fieldName(formName:String, position:String, nameExp:ReaderExp) {
return switch (nameExp) { return switch (nameExp) {
case Symbol(name): case Symbol(name):
name; name;
default: default:
throw 'The first argument to $formName at $position should be a variable name'; throw 'The first argument to $formName at $position should be a variable name';
}; };
} }
static function varOrProperty(formName:String, position:String, args:Array<ReaderExp>, convert:ExprConversion):Field { static function varOrProperty(formName:String, position:String, args:Array<ReaderExp>, convert:ExprConversion):Field {
if (args.length != 2) { if (args.length != 2) {
throw '$formName with $args at $position is not a valid field definition'; throw '$formName with $args at $position is not a valid field definition';
} }
var name = fieldName(formName, position, args[0]); var name = fieldName(formName, position, args[0]);
var access = fieldAccess(formName, name); var access = fieldAccess(formName, name);
return { return {
name: name, name: name,
access: access, access: access,
kind: FVar(null, // TODO allow type anotations kind: FVar(null, // TODO allow type anotations
convert(args[1])), convert(args[1])),
pos: Context.currentPos() pos: Context.currentPos()
}; };
} }
static function funcOrMethod(formName:String, position:String, args:Array<ReaderExp>, convert:ExprConversion):Field { static function funcOrMethod(formName:String, position:String, args:Array<ReaderExp>, convert:ExprConversion):Field {
if (args.length <= 2) { if (args.length <= 2) {
throw '$formName with $args is not a valid function/method definition'; throw '$formName with $args is not a valid function/method definition';
} }
var name = fieldName(formName, position, args[0]); var name = fieldName(formName, position, args[0]);
var access = fieldAccess(formName, name); var access = fieldAccess(formName, name);
return { return {
name: name, name: name,
access: access, access: access,
kind: FFun({ kind: FFun({
args: switch (args[1]) { args: switch (args[1]) {
case ListExp(funcArgs): case ListExp(funcArgs):
[ [
for (funcArg in funcArgs) for (funcArg in funcArgs)
{ {
name: switch (funcArg) { name: switch (funcArg) {
case Symbol(name): case Symbol(name):
name; name;
default: default:
throw '$funcArg should be a symbol for a function argument'; throw '$funcArg should be a symbol for a function argument';
}, },
type: null type: null
} }
]; ];
default: default:
throw '$args[1] should be an argument list'; throw '$args[1] should be an argument list';
}, },
ret: null, ret: null,
expr: { expr: {
pos: Context.currentPos(), pos: Context.currentPos(),
expr: EReturn(convert(CallExp(Symbol("begin"), args.slice(2)))) expr: EReturn(convert(CallExp(Symbol("begin"), args.slice(2))))
} }
}), }),
pos: Context.currentPos() pos: Context.currentPos()
}; };
} }
} }

View File

@@ -9,91 +9,91 @@ import kiss.SpecialForms;
import kiss.Macros; import kiss.Macros;
class Kiss { class Kiss {
/** /**
Build a Haxe class from a corresponding .kiss file Build a Haxe class from a corresponding .kiss file
**/ **/
macro static public function build(kissFile:String):Array<Field> { macro static public function build(kissFile:String):Array<Field> {
var classFields = Context.getBuildFields(); var classFields = Context.getBuildFields();
var stream = new Stream(kissFile); var stream = new Stream(kissFile);
var readTable = Reader.builtins(); var readTable = Reader.builtins();
var fieldForms = FieldForms.builtins(); var fieldForms = FieldForms.builtins();
var specialForms = SpecialForms.builtins(); var specialForms = SpecialForms.builtins();
var macros = Macros.builtins(); var macros = Macros.builtins();
while (true) { while (true) {
stream.dropWhitespace(); stream.dropWhitespace();
if (stream.isEmpty()) if (stream.isEmpty())
break; break;
var position = stream.position(); var position = stream.position();
var nextExp = Reader.read(stream, readTable); var nextExp = Reader.read(stream, readTable);
#if test #if test
trace(nextExp); trace(nextExp);
#end #end
// The last expression might be a comment, in which case None will be returned // The last expression might be a comment, in which case None will be returned
switch (nextExp) { switch (nextExp) {
case Some(nextExp): case Some(nextExp):
classFields.push(readerExpToField(nextExp, position, fieldForms, macros, specialForms)); classFields.push(readerExpToField(nextExp, position, fieldForms, macros, specialForms));
case None: case None:
stream.dropWhitespace(); // If there was a comment, drop whitespace that comes after stream.dropWhitespace(); // If there was a comment, drop whitespace that comes after
} }
} }
return classFields; return classFields;
} }
static function readerExpToField(exp:ReaderExp, position:String, fieldForms:Map<String, FieldFormFunction>, macros:Map<String, MacroFunction>, static function readerExpToField(exp:ReaderExp, position:String, fieldForms:Map<String, FieldFormFunction>, macros:Map<String, MacroFunction>,
specialForms:Map<String, SpecialFormFunction>):Field { specialForms:Map<String, SpecialFormFunction>):Field {
return switch (exp) { return switch (exp) {
case CallExp(Symbol(formName), args) if (fieldForms.exists(formName)): case CallExp(Symbol(formName), args) if (fieldForms.exists(formName)):
fieldForms[formName](position, args, readerExpToHaxeExpr.bind(_, macros, specialForms)); fieldForms[formName](position, args, readerExpToHaxeExpr.bind(_, macros, specialForms));
default: default:
throw '$exp at $position is not a valid field form'; throw '$exp at $position is not a valid field form';
}; };
} }
static function readerExpToHaxeExpr(exp:ReaderExp, macros:Map<String, MacroFunction>, specialForms:Map<String, SpecialFormFunction>):Expr { static function readerExpToHaxeExpr(exp:ReaderExp, macros:Map<String, MacroFunction>, specialForms:Map<String, SpecialFormFunction>):Expr {
// Bind the table arguments of this function for easy recursive calling/passing // Bind the table arguments of this function for easy recursive calling/passing
var convert = readerExpToHaxeExpr.bind(_, macros, specialForms); var convert = readerExpToHaxeExpr.bind(_, macros, specialForms);
var expr = switch (exp) { var expr = switch (exp) {
case Symbol(name): case Symbol(name):
Context.parse(name, Context.currentPos()); Context.parse(name, Context.currentPos());
case StrExp(s): case StrExp(s):
{ {
pos: Context.currentPos(), pos: Context.currentPos(),
expr: EConst(CString(s)) expr: EConst(CString(s))
}; };
case CallExp(Symbol(mac), args) if (macros.exists(mac)): case CallExp(Symbol(mac), args) if (macros.exists(mac)):
convert(macros[mac](args)); convert(macros[mac](args));
case CallExp(Symbol(specialForm), args) if (specialForms.exists(specialForm)): case CallExp(Symbol(specialForm), args) if (specialForms.exists(specialForm)):
specialForms[specialForm](args, convert); specialForms[specialForm](args, convert);
case CallExp(func, body): case CallExp(func, body):
{ {
pos: Context.currentPos(), pos: Context.currentPos(),
expr: ECall(readerExpToHaxeExpr(func, macros, specialForms), [for (bodyExp in body) readerExpToHaxeExpr(bodyExp, macros, specialForms)]) expr: ECall(readerExpToHaxeExpr(func, macros, specialForms), [for (bodyExp in body) readerExpToHaxeExpr(bodyExp, macros, specialForms)])
}; };
case ListExp(elements): case ListExp(elements):
{ {
pos: Context.currentPos(), pos: Context.currentPos(),
expr: ENew({ expr: ENew({
pack: ["kiss"], pack: ["kiss"],
name: "List" name: "List"
}, [ }, [
{ {
pos: Context.currentPos(), pos: Context.currentPos(),
expr: EArrayDecl([for (elementExp in elements) convert(elementExp)]) expr: EArrayDecl([for (elementExp in elements) convert(elementExp)])
} }
]) ])
} }
case RawHaxe(code): case RawHaxe(code):
Context.parse(code, Context.currentPos()); Context.parse(code, Context.currentPos());
default: default:
throw 'cannot convert $exp yet'; throw 'cannot convert $exp yet';
}; };
#if test #if test
trace(expr.expr); trace(expr.expr);
#end #end
return expr; return expr;
} }
} }

View File

@@ -1,52 +1,52 @@
package kiss; package kiss;
@:forward(length, concat, contains, copy, filter, indexOf, // insert is re-implemented with negative index support @:forward(length, concat, contains, copy, filter, indexOf, // insert is re-implemented with negative index support
iterator, join, keyValueIterator, iterator, join, keyValueIterator,
lastIndexOf, map, pop, push, remove, resize, reverse, shift, // slice is re-implemented with negative index support lastIndexOf, map, pop, push, remove, resize, reverse, shift, // slice is re-implemented with negative index support
sort, sort,
// splice is re-implemented with negative index support, // splice is re-implemented with negative index support,
toString, unshift) toString, unshift)
abstract List<T>(Array<T>) from Array<T> to Array<T> { abstract List<T>(Array<T>) from Array<T> to Array<T> {
public inline function new(a:Array<T>) { public inline function new(a:Array<T>) {
this = a; this = a;
} }
@:from @:from
static public function fromArray<T>(a:Array<T>) { static public function fromArray<T>(a:Array<T>) {
return new List<T>(a); return new List<T>(a);
} }
@:to @:to
public function toArray<T>() { public function toArray<T>() {
return this; return this;
} }
inline function realIndex(idx:Int) { inline function realIndex(idx:Int) {
return if (idx < 0) this.length + idx else idx; return if (idx < 0) this.length + idx else idx;
} }
@:arrayAccess @:arrayAccess
public inline function get<T>(idx:Int):Null<T> { public inline function get<T>(idx:Int):Null<T> {
return this[realIndex(idx)]; return this[realIndex(idx)];
} }
@:arrayAccess @:arrayAccess
public inline function arrayWrite(idx:Int, v:T):T { public inline function arrayWrite(idx:Int, v:T):T {
this[realIndex(idx)] = v; this[realIndex(idx)] = v;
return v; return v;
} }
public function insert(idx:Int, v:T) { public function insert(idx:Int, v:T) {
this.insert(realIndex(idx), v); this.insert(realIndex(idx), v);
} }
public function slice(start:Int, ?end:Int) { public function slice(start:Int, ?end:Int) {
if (end == null) if (end == null)
end = this.length; end = this.length;
return this.slice(realIndex(start), realIndex(end)); return this.slice(realIndex(start), realIndex(end));
} }
public function splice(start:Int, len:Int) { public function splice(start:Int, len:Int) {
return this.splice(realIndex(start), len); return this.splice(realIndex(start), len);
} }
} }

View File

@@ -6,37 +6,37 @@ import kiss.Reader;
typedef MacroFunction = (Array<ReaderExp>) -> ReaderExp; typedef MacroFunction = (Array<ReaderExp>) -> ReaderExp;
class Macros { class Macros {
public static function builtins() { public static function builtins() {
var macros:Map<String, MacroFunction> = []; var macros:Map<String, MacroFunction> = [];
macros["+"] = foldMacro("Prelude.add"); macros["+"] = foldMacro("Prelude.add");
macros["-"] = foldMacro("Prelude.subtract"); macros["-"] = foldMacro("Prelude.subtract");
macros["*"] = foldMacro("Prelude.multiply"); macros["*"] = foldMacro("Prelude.multiply");
macros["/"] = foldMacro("Prelude.divide"); macros["/"] = foldMacro("Prelude.divide");
macros["%"] = (exps:Array<ReaderExp>) -> { macros["%"] = (exps:Array<ReaderExp>) -> {
if (exps.length != 2) { if (exps.length != 2) {
throw 'Got ${exps.length} arguments for % instead of 2.'; throw 'Got ${exps.length} arguments for % instead of 2.';
} }
CallExp(Symbol("Prelude.mod"), [exps[1], exps[0]]); CallExp(Symbol("Prelude.mod"), [exps[1], exps[0]]);
}; };
macros["^"] = (exps:Array<ReaderExp>) -> { macros["^"] = (exps:Array<ReaderExp>) -> {
if (exps.length != 2) { if (exps.length != 2) {
throw 'Got ${exps.length} arguments for ^ instead of 2.'; throw 'Got ${exps.length} arguments for ^ instead of 2.';
} }
CallExp(Symbol("Prelude.pow"), [exps[1], exps[0]]); CallExp(Symbol("Prelude.pow"), [exps[1], exps[0]]);
}; };
return macros; return macros;
} }
static function foldMacro(func:String):MacroFunction { static function foldMacro(func:String):MacroFunction {
return (exps) -> { return (exps) -> {
CallExp(Symbol("Lambda.fold"), [ListExp(exps.slice(1)), Symbol(func), exps[0]]); CallExp(Symbol("Lambda.fold"), [ListExp(exps.slice(1)), Symbol(func), exps[0]]);
}; };
} }
} }

View File

@@ -1,27 +1,27 @@
package kiss; package kiss;
class Prelude { class Prelude {
public static function add(a, b) { public static function add(a, b) {
return a + b; return a + b;
} }
public static function subtract(val, from) { public static function subtract(val, from) {
return from - val; return from - val;
} }
public static function multiply(a, b) { public static function multiply(a, b) {
return a * b; return a * b;
} }
public static function divide(bottom:Float, top:Float) { public static function divide(bottom:Float, top:Float) {
return top / bottom; return top / bottom;
} }
public static function mod(bottom, top) { public static function mod(bottom, top) {
return top % bottom; return top % bottom;
} }
public static function pow(exponent, base) { public static function pow(exponent, base) {
return Math.pow(base, exponent); return Math.pow(base, exponent);
} }
} }

View File

@@ -4,76 +4,76 @@ import haxe.ds.Option;
import kiss.Stream; import kiss.Stream;
enum ReaderExp { enum ReaderExp {
CallExp(func:ReaderExp, args:Array<ReaderExp>); // (f a1 a2...) CallExp(func:ReaderExp, args:Array<ReaderExp>); // (f a1 a2...)
ListExp(exps:Array<ReaderExp>); // [v1 v2 v3] ListExp(exps:Array<ReaderExp>); // [v1 v2 v3]
StrExp(s:String); // "literal" StrExp(s:String); // "literal"
Symbol(name:String); // s Symbol(name:String); // s
RawHaxe(code:String); RawHaxe(code:String);
} }
typedef ReadFunction = (Stream) -> Null<ReaderExp>; typedef ReadFunction = (Stream) -> Null<ReaderExp>;
class Reader { class Reader {
// The built-in readtable // The built-in readtable
public static function builtins() { public static function builtins() {
var readTable:Map<String, ReadFunction> = []; var readTable:Map<String, ReadFunction> = [];
readTable["("] = (stream) -> CallExp(assertRead(stream, readTable), readExpArray(stream, ")", readTable)); readTable["("] = (stream) -> CallExp(assertRead(stream, readTable), readExpArray(stream, ")", readTable));
readTable["["] = (stream) -> ListExp(readExpArray(stream, "]", readTable)); readTable["["] = (stream) -> ListExp(readExpArray(stream, "]", readTable));
readTable["\""] = (stream) -> StrExp(stream.expect("closing \"", () -> stream.takeUntilAndDrop("\""))); readTable["\""] = (stream) -> StrExp(stream.expect("closing \"", () -> stream.takeUntilAndDrop("\"")));
readTable["/*"] = (stream) -> { readTable["/*"] = (stream) -> {
stream.dropUntil("*/"); stream.dropUntil("*/");
stream.dropString("*/"); stream.dropString("*/");
null; null;
}; };
readTable["//"] = (stream) -> { readTable["//"] = (stream) -> {
stream.dropUntil("\n"); stream.dropUntil("\n");
null; null;
}; };
readTable["#|"] = (stream) -> RawHaxe(stream.expect("closing |", () -> stream.takeUntilAndDrop("|#"))); readTable["#|"] = (stream) -> RawHaxe(stream.expect("closing |", () -> stream.takeUntilAndDrop("|#")));
return readTable; return readTable;
} }
public static function assertRead(stream:Stream, readTable:Map<String, ReadFunction>):ReaderExp { public static function assertRead(stream:Stream, readTable:Map<String, ReadFunction>):ReaderExp {
var position = stream.position(); var position = stream.position();
return switch (read(stream, readTable)) { return switch (read(stream, readTable)) {
case Some(exp): case Some(exp):
exp; exp;
case None: case None:
throw 'There were no expressions left in the stream at $position'; throw 'There were no expressions left in the stream at $position';
}; };
} }
public static function read(stream:Stream, readTable:Map<String, ReadFunction>):Option<ReaderExp> { public static function read(stream:Stream, readTable:Map<String, ReadFunction>):Option<ReaderExp> {
stream.dropWhitespace(); stream.dropWhitespace();
if (stream.isEmpty()) if (stream.isEmpty())
return None; return None;
var readTableKeys = [for (key in readTable.keys()) key]; var readTableKeys = [for (key in readTable.keys()) key];
readTableKeys.sort((a, b) -> b.length - a.length); readTableKeys.sort((a, b) -> b.length - a.length);
for (key in readTableKeys) { for (key in readTableKeys) {
switch (stream.peekChars(key.length)) { switch (stream.peekChars(key.length)) {
case Some(k) if (k == key): case Some(k) if (k == key):
stream.dropString(key); stream.dropString(key);
var expOrNull = readTable[key](stream); var expOrNull = readTable[key](stream);
return if (expOrNull != null) Some(expOrNull) else read(stream, readTable); return if (expOrNull != null) Some(expOrNull) else read(stream, readTable);
default: default:
} }
} }
return Some(Symbol(stream.expect("a symbol name", () -> stream.takeUntilOneOf([")", "]", "/*", "\n", " "])))); return Some(Symbol(stream.expect("a symbol name", () -> stream.takeUntilOneOf([")", "]", "/*", "\n", " "]))));
} }
public static function readExpArray(stream:Stream, end:String, readTable:Map<String, ReadFunction>):Array<ReaderExp> { public static function readExpArray(stream:Stream, end:String, readTable:Map<String, ReadFunction>):Array<ReaderExp> {
var array = []; var array = [];
while (stream.expect('$end to terminate list', () -> stream.peekChars(end.length)) != end) { while (stream.expect('$end to terminate list', () -> stream.peekChars(end.length)) != end) {
stream.dropWhitespace(); stream.dropWhitespace();
array.push(assertRead(stream, readTable)); array.push(assertRead(stream, readTable));
} }
stream.dropString(end); stream.dropString(end);
return array; return array;
} }
} }

View File

@@ -9,19 +9,19 @@ import kiss.Types;
typedef SpecialFormFunction = (args:Array<ReaderExp>, convert:ExprConversion) -> Expr; typedef SpecialFormFunction = (args:Array<ReaderExp>, convert:ExprConversion) -> Expr;
class SpecialForms { class SpecialForms {
public static function builtins() { public static function builtins() {
var map:Map<String, SpecialFormFunction> = []; var map:Map<String, SpecialFormFunction> = [];
map["begin"] = (args:Array<ReaderExp>, convert:ExprConversion) -> { map["begin"] = (args:Array<ReaderExp>, convert:ExprConversion) -> {
pos: Context.currentPos(), pos: Context.currentPos(),
expr: EBlock([for (bodyExp in args) convert(bodyExp)]) expr: EBlock([for (bodyExp in args) convert(bodyExp)])
}; };
map["nth"] = (args:Array<ReaderExp>, convert:ExprConversion) -> { map["nth"] = (args:Array<ReaderExp>, convert:ExprConversion) -> {
pos: Context.currentPos(), pos: Context.currentPos(),
expr: EArray(convert(args[0]), convert(args[1])) expr: EArray(convert(args[0]), convert(args[1]))
}; };
return map; return map;
} }
} }

View File

@@ -7,102 +7,102 @@ using StringTools;
using Lambda; using Lambda;
class Stream { class Stream {
var content:String; var content:String;
var file:String; var file:String;
var line:Int; var line:Int;
var column:Int; var column:Int;
public function new(file:String) { public function new(file:String) {
// Banish ye Windows line-endings // Banish ye Windows line-endings
content = File.getContent(file).replace('\r', ''); content = File.getContent(file).replace('\r', '');
// Life is easier with a trailing newline // Life is easier with a trailing newline
if (content.charAt(content.length - 1) != "\n") if (content.charAt(content.length - 1) != "\n")
content += "\n"; content += "\n";
this.file = file; this.file = file;
line = 1; line = 1;
column = 1; column = 1;
} }
public function peekChars(chars:Int):Option<String> { public function peekChars(chars:Int):Option<String> {
if (content.length < chars) if (content.length < chars)
return None; return None;
return Some(content.substr(0, chars)); return Some(content.substr(0, chars));
} }
public function isEmpty() { public function isEmpty() {
return content.length == 0; return content.length == 0;
} }
public function position() { public function position() {
return '$file:$line:$column'; return '$file:$line:$column';
} }
/** Every drop call should end up calling dropChars() or the position tracker will be wrong. **/ /** Every drop call should end up calling dropChars() or the position tracker will be wrong. **/
private function dropChars(count:Int) { private function dropChars(count:Int) {
for (idx in 0...count) { for (idx in 0...count) {
switch (content.charAt(idx)) { switch (content.charAt(idx)) {
case "\n": case "\n":
line += 1; line += 1;
column = 1; column = 1;
default: default:
column += 1; column += 1;
} }
} }
content = content.substr(count); content = content.substr(count);
} }
public function takeChars(count:Int):Option<String> { public function takeChars(count:Int):Option<String> {
if (count > content.length) if (count > content.length)
return None; return None;
var toReturn = content.substr(0, count); var toReturn = content.substr(0, count);
dropChars(count); dropChars(count);
return Some(toReturn); return Some(toReturn);
} }
public function dropString(s:String) { public function dropString(s:String) {
var toDrop = content.substr(0, s.length); var toDrop = content.substr(0, s.length);
if (toDrop != s) { if (toDrop != s) {
throw 'Expected $s at ${position()}'; throw 'Expected $s at ${position()}';
} }
dropChars(s.length); dropChars(s.length);
} }
public function dropUntil(s:String) { public function dropUntil(s:String) {
dropChars(content.indexOf(s)); dropChars(content.indexOf(s));
} }
public function dropWhitespace() { public function dropWhitespace() {
var trimmed = content.ltrim(); var trimmed = content.ltrim();
dropChars(content.length - trimmed.length); dropChars(content.length - trimmed.length);
} }
public function takeUntilOneOf(terminators:Array<String>):Option<String> { public function takeUntilOneOf(terminators:Array<String>):Option<String> {
var indices = [for (term in terminators) content.indexOf(term)].filter((idx) -> idx >= 0); var indices = [for (term in terminators) content.indexOf(term)].filter((idx) -> idx >= 0);
if (indices.length == 0) if (indices.length == 0)
return None; return None;
var firstIndex = Math.floor(indices.fold(Math.min, indices[0])); var firstIndex = Math.floor(indices.fold(Math.min, indices[0]));
return takeChars(firstIndex); return takeChars(firstIndex);
} }
public function takeUntilAndDrop(s:String):Option<String> { public function takeUntilAndDrop(s:String):Option<String> {
var idx = content.indexOf(s); var idx = content.indexOf(s);
if (idx < 0) if (idx < 0)
return None; return None;
var toReturn = content.substr(0, idx); var toReturn = content.substr(0, idx);
dropChars(toReturn.length + s.length); dropChars(toReturn.length + s.length);
return Some(toReturn); return Some(toReturn);
} }
public function expect(whatToExpect:String, f:Void->Option<String>):String { public function expect(whatToExpect:String, f:Void->Option<String>):String {
var position = position(); var position = position();
switch (f()) { switch (f()) {
case Some(s): case Some(s):
return s; return s;
case None: case None:
throw 'Expected $whatToExpect at $position'; throw 'Expected $whatToExpect at $position';
} }
} }
} }

View File

@@ -4,10 +4,10 @@ import utest.Runner;
import utest.ui.Report; import utest.ui.Report;
class TestMain { class TestMain {
public static function main() { public static function main() {
var runner = new Runner(); var runner = new Runner();
runner.addCases(test.cases); runner.addCases(test.cases);
Report.create(runner); Report.create(runner);
runner.run(); runner.run();
} }
} }

View File

@@ -6,72 +6,72 @@ import kiss.Prelude;
@:build(kiss.Kiss.build("src/test/cases/BasicTestCase.kiss")) @:build(kiss.Kiss.build("src/test/cases/BasicTestCase.kiss"))
class BasicTestCase extends Test { class BasicTestCase extends Test {
function testStaticVar() { function testStaticVar() {
Assert.equals("Howdy", BasicTestCase.message); Assert.equals("Howdy", BasicTestCase.message);
} }
function testHaxeInsertion() { function testHaxeInsertion() {
Assert.equals(23, BasicTestCase.mathResult); Assert.equals(23, BasicTestCase.mathResult);
} }
function testStaticFunction() { function testStaticFunction() {
Assert.equals(6, BasicTestCase.myFloor(6.5)); Assert.equals(6, BasicTestCase.myFloor(6.5));
} }
function testFuncall() { function testFuncall() {
Assert.equals(7, BasicTestCase.funResult); Assert.equals(7, BasicTestCase.funResult);
} }
function testField() { function testField() {
Assert.equals(5, new BasicTestCase().myField); Assert.equals(5, new BasicTestCase().myField);
} }
function testMethod() { function testMethod() {
Assert.equals(5, new BasicTestCase().myMethod()); Assert.equals(5, new BasicTestCase().myMethod());
} }
function testArray() { function testArray() {
var arr = BasicTestCase.myArray; var arr = BasicTestCase.myArray;
Assert.equals(3, arr.length); Assert.equals(3, arr.length);
Assert.equals(1, arr[0]); Assert.equals(1, arr[0]);
Assert.equals(2, arr[1]); Assert.equals(2, arr[1]);
Assert.equals(3, arr[2]); Assert.equals(3, arr[2]);
// Kiss arrays can be negatively indexed like Python // Kiss arrays can be negatively indexed like Python
Assert.equals(3, arr[-1]); Assert.equals(3, arr[-1]);
Assert.equals(2, arr[-2]); Assert.equals(2, arr[-2]);
Assert.equals(1, arr[-3]); Assert.equals(1, arr[-3]);
} }
function testArrayAccess() { function testArrayAccess() {
Assert.equals(3, BasicTestCase.myArrayLast); Assert.equals(3, BasicTestCase.myArrayLast);
} }
function testVariadicAdd() { function testVariadicAdd() {
Assert.equals(6, BasicTestCase.mySum); Assert.equals(6, BasicTestCase.mySum);
} }
function testVariadicSubtract() { function testVariadicSubtract() {
Assert.equals(-2, BasicTestCase.myDifference); Assert.equals(-2, BasicTestCase.myDifference);
} }
function testVariadicMultiply() { function testVariadicMultiply() {
Assert.equals(60, BasicTestCase.myProduct); Assert.equals(60, BasicTestCase.myProduct);
} }
function testVariadicDivide() { function testVariadicDivide() {
Assert.equals(0.5, BasicTestCase.myQuotient); Assert.equals(0.5, BasicTestCase.myQuotient);
} }
function testMod() { function testMod() {
Assert.equals(4, BasicTestCase.myRemainder); Assert.equals(4, BasicTestCase.myRemainder);
} }
function testPow() { function testPow() {
Assert.equals(256, BasicTestCase.myPower); Assert.equals(256, BasicTestCase.myPower);
} }
function testUnop() { function testUnop() {
Assert.equals(7, BasicTestCase.myInc); Assert.equals(7, BasicTestCase.myInc);
} }
} }