feat(functions): add function argument helpers
This commit is contained in:
1 parent
6b4fea2082
commit
cd6debac6f
1 file changed
+126
@@ -0,0 +1,126 @@
|
|||||||
|
import '../ast.dart';
|
||||||
|
import '../evaluator.dart';
|
||||||
|
import '../values.dart';
|
||||||
|
|
||||||
|
void arity(List<Expr> args, int min, [int? max]) {
|
||||||
|
final count = args.length;
|
||||||
|
if (count < min || (max != null && count > max)) {
|
||||||
|
throw const EvalException(ErrorValue(ErrorKind.value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double numberArg(Expr arg, EvalContext ctx) => ctx.asNumber(ctx.eval(arg));
|
||||||
|
|
||||||
|
String textArg(Expr arg, EvalContext ctx) => ctx.asText(ctx.eval(arg));
|
||||||
|
|
||||||
|
int intArg(Expr arg, EvalContext ctx) => numberArg(arg, ctx).truncate();
|
||||||
|
|
||||||
|
bool boolArg(Expr arg, EvalContext ctx) => ctx.asBoolean(ctx.eval(arg));
|
||||||
|
|
||||||
|
bool matchesCriterion(CellValue candidate, CellValue criterion, EvalContext ctx) {
|
||||||
|
if (criterion is TextValue) {
|
||||||
|
return _matchTextCriterion(candidate, criterion.value, ctx);
|
||||||
|
}
|
||||||
|
return _equalValues(candidate, criterion, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _matchTextCriterion(CellValue candidate, String spec, EvalContext ctx) {
|
||||||
|
final trimmed = spec.trim();
|
||||||
|
final operator = _leadingOperator(trimmed);
|
||||||
|
if (operator != null) {
|
||||||
|
final operand = trimmed.substring(operator.length).trim();
|
||||||
|
return _compareWithOperator(candidate, operator, operand, ctx);
|
||||||
|
}
|
||||||
|
return _equalsText(candidate, trimmed, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
String? _leadingOperator(String spec) {
|
||||||
|
for (final op in const ['<>', '<=', '>=', '<', '>', '=']) {
|
||||||
|
if (spec.startsWith(op)) return op;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _compareWithOperator(CellValue candidate, String op, String operand, EvalContext ctx) {
|
||||||
|
final operandNumber = double.tryParse(operand);
|
||||||
|
if (operandNumber != null && candidate is NumberValue) {
|
||||||
|
final diff = candidate.value.compareTo(operandNumber);
|
||||||
|
switch (op) {
|
||||||
|
case '=':
|
||||||
|
return diff == 0;
|
||||||
|
case '<>':
|
||||||
|
return diff != 0;
|
||||||
|
case '<':
|
||||||
|
return diff < 0;
|
||||||
|
case '<=':
|
||||||
|
return diff <= 0;
|
||||||
|
case '>':
|
||||||
|
return diff > 0;
|
||||||
|
case '>=':
|
||||||
|
return diff >= 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final candidateText = _asPlainText(candidate, ctx);
|
||||||
|
final compared = candidateText.toUpperCase().compareTo(operand.toUpperCase());
|
||||||
|
switch (op) {
|
||||||
|
case '=':
|
||||||
|
return _wildcardEquals(candidateText, operand);
|
||||||
|
case '<>':
|
||||||
|
return !_wildcardEquals(candidateText, operand);
|
||||||
|
case '<':
|
||||||
|
return compared < 0;
|
||||||
|
case '<=':
|
||||||
|
return compared <= 0;
|
||||||
|
case '>':
|
||||||
|
return compared > 0;
|
||||||
|
case '>=':
|
||||||
|
return compared >= 0;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _equalsText(CellValue candidate, String operand, EvalContext ctx) {
|
||||||
|
final operandNumber = double.tryParse(operand);
|
||||||
|
if (operandNumber != null && candidate is NumberValue) {
|
||||||
|
return candidate.value == operandNumber;
|
||||||
|
}
|
||||||
|
return _wildcardEquals(_asPlainText(candidate, ctx), operand);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _equalValues(CellValue candidate, CellValue criterion, EvalContext ctx) {
|
||||||
|
if (candidate is NumberValue && criterion is NumberValue) {
|
||||||
|
return candidate.value == criterion.value;
|
||||||
|
}
|
||||||
|
if (candidate is BooleanValue && criterion is BooleanValue) {
|
||||||
|
return candidate.value == criterion.value;
|
||||||
|
}
|
||||||
|
return _asPlainText(candidate, ctx).toUpperCase() ==
|
||||||
|
_asPlainText(criterion, ctx).toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
String _asPlainText(CellValue value, EvalContext ctx) {
|
||||||
|
if (value is BlankValue) return '';
|
||||||
|
if (value is ErrorValue) return value.kind.symbol;
|
||||||
|
return ctx.asText(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _wildcardEquals(String text, String pattern) {
|
||||||
|
if (!pattern.contains('*') && !pattern.contains('?')) {
|
||||||
|
return text.toUpperCase() == pattern.toUpperCase();
|
||||||
|
}
|
||||||
|
final buffer = StringBuffer('^');
|
||||||
|
for (final rune in pattern.runes) {
|
||||||
|
final ch = String.fromCharCode(rune);
|
||||||
|
switch (ch) {
|
||||||
|
case '*':
|
||||||
|
buffer.write('.*');
|
||||||
|
case '?':
|
||||||
|
buffer.write('.');
|
||||||
|
default:
|
||||||
|
buffer.write(RegExp.escape(ch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.write(r'$');
|
||||||
|
return RegExp(buffer.toString(), caseSensitive: false).hasMatch(text);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user