Preparing DerivedFormula evaluation
This commit is contained in:
parent
e5f92d7b1e
commit
6f04921ef0
5 changed files with 271 additions and 204 deletions
|
|
@ -116,8 +116,14 @@ class _FormulaScreenState extends State<FormulaScreen> {
|
||||||
inputValues[input.name] = convertedValue;
|
inputValues[input.name] = convertedValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
late final dynamic result;
|
||||||
|
if( formula is DerivedFormula) {
|
||||||
|
result = formulaSolver(formula, formula.output.name, inputValues,);
|
||||||
|
}
|
||||||
|
else {
|
||||||
final evaluator = FormulaEvaluator();
|
final evaluator = FormulaEvaluator();
|
||||||
final result = evaluator.evaluate(formula, inputValues);
|
result = evaluator.evaluate(formula, inputValues);
|
||||||
|
}
|
||||||
|
|
||||||
// Convert output to selected unit if needed
|
// Convert output to selected unit if needed
|
||||||
String? unit = formula.output.unit;
|
String? unit = formula.output.unit;
|
||||||
|
|
|
||||||
|
|
@ -276,9 +276,9 @@ Number formulaSolver(
|
||||||
String variableToSolve,
|
String variableToSolve,
|
||||||
Map<String, dynamic> fixedInputValues, {
|
Map<String, dynamic> fixedInputValues, {
|
||||||
Number hint = 0,
|
Number hint = 0,
|
||||||
Number step = 10,
|
Number step = 100,
|
||||||
Number maxDelta = 0.01,
|
Number maxDelta = 0.01,
|
||||||
int maxTries = 100,
|
int maxTries = 1000,
|
||||||
}) {
|
}) {
|
||||||
if (variableToSolve == formula.output.name) {
|
if (variableToSolve == formula.output.name) {
|
||||||
return FormulaEvaluator().evaluate(formula, fixedInputValues);
|
return FormulaEvaluator().evaluate(formula, fixedInputValues);
|
||||||
|
|
|
||||||
|
|
@ -1,173 +1,12 @@
|
||||||
import 'package:d4rt/d4rt.dart';
|
import 'package:d4rt/d4rt.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:d4rt_formulas/d4rt_formulas.dart';
|
import 'package:d4rt_formulas/d4rt_formulas.dart';
|
||||||
|
import 'package:d4rt_formulas/set_utils.dart';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
typedef Number = double;
|
typedef Number = double;
|
||||||
|
|
||||||
abstract class SetUtils {
|
|
||||||
static Object safeGet(Map<Object?, Object?> map, String key) {
|
|
||||||
if (!map.containsKey(key)) {
|
|
||||||
throw ArgumentError("Key not found: $key -- $map");
|
|
||||||
}
|
|
||||||
return map[key] ?? "Not possible!!!";
|
|
||||||
}
|
|
||||||
|
|
||||||
static String stringValue(Map<Object?, Object?> map, String key) {
|
|
||||||
return safeGet(map, key).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<Object?> listValue(Map<Object?, Object?> map, String key) {
|
|
||||||
return safeGet(map, key) as List<Object?>;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Number numberValue(Map<Object?, Object?> map, String key) {
|
|
||||||
return double.parse(stringValue(map, key));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parses a d4rt array literal (containing maps and arrays) to a List<Object?>
|
|
||||||
/// using d4rt
|
|
||||||
static List<Object?> parseD4rtLiteral(String arrayStringLiteral) {
|
|
||||||
var d4rt = D4rt();
|
|
||||||
final buffer = StringBuffer();
|
|
||||||
buffer.write("main(){ return $arrayStringLiteral; }");
|
|
||||||
final code = buffer.toString();
|
|
||||||
|
|
||||||
final List<Object?> list = d4rt.execute(source: code);
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Escapes special characters in a string for use in D4RT literals
|
|
||||||
@deprecated
|
|
||||||
static String escapeD4rtString(String input) {
|
|
||||||
return input
|
|
||||||
.replaceAll(r'\\', r'\\\\') // escape backslashes first
|
|
||||||
.replaceAll('\n', r'\\n')
|
|
||||||
.replaceAll('\r', r'\\r')
|
|
||||||
.replaceAll('\t', r'\\t')
|
|
||||||
.replaceAll('"', r'\"');
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parses corpus elements from an array string literal.
|
|
||||||
/// Determines if each element is a formula or a unit and converts accordingly.
|
|
||||||
static List<FormulaElement> parseCorpusElements(String arrayStringLiteral) {
|
|
||||||
final List<Object?> elements = parseD4rtLiteral(arrayStringLiteral);
|
|
||||||
|
|
||||||
final List<FormulaElement> result = [];
|
|
||||||
for (final element in elements) {
|
|
||||||
if (element is Map<Object?, Object?>) {
|
|
||||||
if (element.containsKey('d4rtCode')) {
|
|
||||||
result.add(Formula.fromSet(element));
|
|
||||||
} else
|
|
||||||
if (element.containsKey('name') && element.containsKey('symbol')) {
|
|
||||||
result.add(UnitSpec.fromSet(element));
|
|
||||||
} else {
|
|
||||||
throw ArgumentError('Unknown element type: $element');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw ArgumentError('Element must be a Map: $element');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pretty prints a dynamic value (Set, Array, string or number) as a Dart literal.
|
|
||||||
/// Uses JSON-like formatting but for Dart language, with proper indentation.
|
|
||||||
static String prettyPrint(dynamic value, {int indent = 0}) {
|
|
||||||
if (value is String) {
|
|
||||||
return _prettyPrintString(value, indent);
|
|
||||||
} else if (value is num) {
|
|
||||||
return _prettyPrintNumber(value, indent);
|
|
||||||
} else if (value is Set) {
|
|
||||||
return _prettyPrintSet(value, indent);
|
|
||||||
} else if (value is List) {
|
|
||||||
return _prettyPrintArray(value, indent);
|
|
||||||
} else if (value is Map) {
|
|
||||||
return _prettyPrintMap(value, indent);
|
|
||||||
} else {
|
|
||||||
return value.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pretty prints a simple string, escaping special characters if needed.
|
|
||||||
static String _prettyPrintString(String s, int indent) {
|
|
||||||
// Check if the string needs raw string formatting (newlines, $, backslashes, quotes)
|
|
||||||
final needsRawString = s.contains('\n') ||
|
|
||||||
s.contains(r'$') ||
|
|
||||||
s.contains(r'\\') ||
|
|
||||||
s.contains('"');
|
|
||||||
|
|
||||||
if (needsRawString) {
|
|
||||||
return _prettyPrintRawString(s, indent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple string with escaped quotes
|
|
||||||
return '"${s.replaceAll('"', r'\"')}"';
|
|
||||||
//'
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pretty prints a number.
|
|
||||||
static String _prettyPrintNumber(num n, int indent) {
|
|
||||||
return n.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pretty prints a Set as a Dart set literal.
|
|
||||||
static String _prettyPrintSet(Set s, int indent) {
|
|
||||||
if (s.isEmpty) {
|
|
||||||
return '{}';
|
|
||||||
}
|
|
||||||
|
|
||||||
final indentStr = ' ' * indent;
|
|
||||||
final innerIndent = ' ' * (indent + 1);
|
|
||||||
|
|
||||||
final elements = s.map((e) => '$innerIndent${prettyPrint(e, indent: indent + 1)}').join(',\n');
|
|
||||||
return '{$elements\n$indentStr}';
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pretty prints an Array/List as a Dart list literal.
|
|
||||||
static String _prettyPrintArray(List a, int indent) {
|
|
||||||
if (a.isEmpty) {
|
|
||||||
return '[]';
|
|
||||||
}
|
|
||||||
|
|
||||||
final indentStr = ' ' * indent;
|
|
||||||
final innerIndent = ' ' * (indent + 1);
|
|
||||||
|
|
||||||
final elements = a.map((e) => '$innerIndent${prettyPrint(e, indent: indent + 1)}').join(',\n');
|
|
||||||
return '[\n$elements\n$indentStr]';
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pretty prints a Map as a Dart map literal.
|
|
||||||
static String _prettyPrintMap(Map m, int indent) {
|
|
||||||
if (m.isEmpty) {
|
|
||||||
return '{}';
|
|
||||||
}
|
|
||||||
|
|
||||||
final indentStr = ' ' * indent;
|
|
||||||
final innerIndent = ' ' * (indent + 1);
|
|
||||||
|
|
||||||
final entries = m.entries.map((e) {
|
|
||||||
final key = prettyPrint(e.key, indent: indent + 1);
|
|
||||||
final value = prettyPrint(e.value, indent: indent + 1);
|
|
||||||
return '$innerIndent$key: $value';
|
|
||||||
}).join(',\n');
|
|
||||||
|
|
||||||
return '{\n$entries\n$indentStr}';
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pretty prints a raw string (for strings containing newlines, $, backslashes, etc.)
|
|
||||||
/// Uses Dart's raw string syntax r"""..."""
|
|
||||||
static String _prettyPrintRawString(String s, int indent) {
|
|
||||||
// Escape triple quotes by replacing """ with ""\"
|
|
||||||
final escaped = s.replaceAll('"""', r'""\\"');
|
|
||||||
return 'r"""$escaped"""';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Abstract base class for formula elements
|
/// Abstract base class for formula elements
|
||||||
abstract class FormulaElement {
|
abstract class FormulaElement {
|
||||||
Map<String, dynamic> toMap();
|
Map<String, dynamic> toMap();
|
||||||
|
|
@ -198,7 +37,6 @@ class UnitSpec extends FormulaElement {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
UnitSpec({
|
UnitSpec({
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.baseUnit,
|
required this.baseUnit,
|
||||||
|
|
@ -220,12 +58,7 @@ class UnitSpec extends FormulaElement {
|
||||||
|
|
||||||
if (theSet.containsKey("factor")) {
|
if (theSet.containsKey("factor")) {
|
||||||
Number factorFromUnitToBase = SetUtils.numberValue(theSet, "factor");
|
Number factorFromUnitToBase = SetUtils.numberValue(theSet, "factor");
|
||||||
return UnitSpec(
|
return UnitSpec(name: name, baseUnit: baseUnit, symbol: symbol, factorFromUnitToBase: factorFromUnitToBase);
|
||||||
name: name,
|
|
||||||
baseUnit: baseUnit,
|
|
||||||
symbol: symbol,
|
|
||||||
factorFromUnitToBase: factorFromUnitToBase,
|
|
||||||
);
|
|
||||||
} else if (theSet.containsKey("toBase")) {
|
} else if (theSet.containsKey("toBase")) {
|
||||||
String codeFromBaseToUnit = SetUtils.stringValue(theSet, "fromBase");
|
String codeFromBaseToUnit = SetUtils.stringValue(theSet, "fromBase");
|
||||||
String codeFromUnitToBase = SetUtils.stringValue(theSet, "toBase");
|
String codeFromUnitToBase = SetUtils.stringValue(theSet, "toBase");
|
||||||
|
|
@ -249,7 +82,6 @@ class UnitSpec extends FormulaElement {
|
||||||
|
|
||||||
return units.toList(growable: false);
|
return units.toList(growable: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class VariableSpec extends FormulaElement {
|
class VariableSpec extends FormulaElement {
|
||||||
|
|
@ -294,21 +126,99 @@ class VariableSpec extends FormulaElement{
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(unit, name, values != null ? const DeepCollectionEquality().hash(values!) : 0);
|
int get hashCode => Object.hash(unit, name, values != null ? const DeepCollectionEquality().hash(values!) : 0);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String _generateUuidV4() => Uuid().v4();
|
String _generateUuidV4() => Uuid().v4();
|
||||||
|
|
||||||
class Formula extends FormulaElement {
|
abstract class FormulaInterface {
|
||||||
|
String get uuid;
|
||||||
|
|
||||||
|
String get name;
|
||||||
|
|
||||||
|
String? get description;
|
||||||
|
|
||||||
|
List<VariableSpec> get input;
|
||||||
|
|
||||||
|
VariableSpec get output;
|
||||||
|
|
||||||
|
String get d4rtCode;
|
||||||
|
|
||||||
|
List<String> get tags;
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap();
|
||||||
|
|
||||||
|
Formula get originalFormula;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DerivedFormula implements FormulaInterface {
|
||||||
|
@override
|
||||||
|
String get uuid => originalFormula.uuid;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get name => originalFormula.name;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get description => originalFormula.description;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<VariableSpec> get input => _input;
|
||||||
|
|
||||||
|
@override
|
||||||
|
VariableSpec get output => _output;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get d4rtCode => "signal('no code for derived formula, use formulaSolver')";
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<String> get tags => originalFormula.tags;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final Formula originalFormula;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toMap() => originalFormula.toMap();
|
||||||
|
|
||||||
|
String outputName;
|
||||||
|
late List<VariableSpec> _input;
|
||||||
|
late VariableSpec _output;
|
||||||
|
|
||||||
|
DerivedFormula({required this.outputName, required this.originalFormula}) {
|
||||||
|
|
||||||
|
if( originalFormula.input.any( (vs) => vs.unit == "string") || originalFormula.output.unit == "string") {
|
||||||
|
throw ArgumentError(
|
||||||
|
"Derived formulas are not supported for formulas with string inputs, because we can't solve for them. Original formula: ${originalFormula
|
||||||
|
.toString()}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var newInput = List<VariableSpec>.from(originalFormula.input).where((v) => v.name != outputName).toList();
|
||||||
|
newInput.add(originalFormula.output);
|
||||||
|
_input = newInput.toList(growable: false);
|
||||||
|
_output = originalFormula.input.firstWhere(
|
||||||
|
(v) => v.name == outputName,
|
||||||
|
orElse: () => throw ArgumentError("New output variable $outputName not found in original formula input"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Formula extends FormulaElement implements FormulaInterface {
|
||||||
|
@override
|
||||||
final String uuid;
|
final String uuid;
|
||||||
|
@override
|
||||||
final String name;
|
final String name;
|
||||||
|
@override
|
||||||
final String? description;
|
final String? description;
|
||||||
|
@override
|
||||||
final List<VariableSpec> input;
|
final List<VariableSpec> input;
|
||||||
|
@override
|
||||||
final VariableSpec output;
|
final VariableSpec output;
|
||||||
|
@override
|
||||||
final String d4rtCode;
|
final String d4rtCode;
|
||||||
|
@override
|
||||||
final List<String> tags;
|
final List<String> tags;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Formula get originalFormula => this;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> toMap() {
|
Map<String, dynamic> toMap() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -335,9 +245,7 @@ class Formula extends FormulaElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
void validate() {
|
void validate() {
|
||||||
if (name
|
if (name.trim().isEmpty) {
|
||||||
.trim()
|
|
||||||
.isEmpty) {
|
|
||||||
throw ArgumentError('Formula name cannot be empty');
|
throw ArgumentError('Formula name cannot be empty');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -348,16 +256,12 @@ class Formula extends FormulaElement {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) || other is Formula && runtimeType == other.runtimeType && uuid == other.uuid;
|
||||||
other is Formula &&
|
|
||||||
runtimeType == other.runtimeType &&
|
|
||||||
uuid == other.uuid;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => uuid.hashCode;
|
int get hashCode => uuid.hashCode;
|
||||||
|
|
||||||
List<String> inputVarNames() =>
|
List<String> inputVarNames() => input.map((v) => v.name).toList(growable: false);
|
||||||
input.map((v) => v.name).toList(growable: false);
|
|
||||||
|
|
||||||
factory Formula.fromStringLiteral(String setStringLiteral) {
|
factory Formula.fromStringLiteral(String setStringLiteral) {
|
||||||
var d4rt = D4rt();
|
var d4rt = D4rt();
|
||||||
|
|
@ -389,29 +293,21 @@ class Formula extends FormulaElement {
|
||||||
if (allowed != null) {
|
if (allowed != null) {
|
||||||
final types = allowed.map((v) => v.runtimeType).toSet();
|
final types = allowed.map((v) => v.runtimeType).toSet();
|
||||||
if (types.length > 1) {
|
if (types.length > 1) {
|
||||||
throw ArgumentError(
|
throw ArgumentError('Allowed values must be all Strings or all Numbers');
|
||||||
'Allowed values must be all Strings or all Numbers');
|
|
||||||
}
|
}
|
||||||
if (!types.contains(String) && !types.contains(double) &&
|
if (!types.contains(String) && !types.contains(double) && !types.contains(int)) {
|
||||||
!types.contains(int)) {
|
|
||||||
throw ArgumentError('Allowed values must be Strings or Numbers');
|
throw ArgumentError('Allowed values must be Strings or Numbers');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return VariableSpec(
|
return VariableSpec(name: name, unit: unit, values: allowed?.toList(growable: false));
|
||||||
name: name,
|
|
||||||
unit: unit,
|
|
||||||
values: allowed?.toList(growable: false),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String? uuid = theSet['uuid'] as String?;
|
String? uuid = theSet['uuid'] as String?;
|
||||||
String name = SetUtils.stringValue(theSet, "name");
|
String name = SetUtils.stringValue(theSet, "name");
|
||||||
String? description = theSet["description"] as String?;
|
String? description = theSet["description"] as String?;
|
||||||
List<String> tags = (theSet["tags"] as List<Object?>? ?? []).map((t) =>
|
List<String> tags = (theSet["tags"] as List<Object?>? ?? []).map((t) => t.toString()).toList();
|
||||||
t.toString()).toList();
|
|
||||||
final List<Object?> inputSet = SetUtils.listValue(theSet, "input");
|
final List<Object?> inputSet = SetUtils.listValue(theSet, "input");
|
||||||
List<VariableSpec> input = inputSet.map((v) => parseVar(v as Map)).toList(
|
List<VariableSpec> input = inputSet.map((v) => parseVar(v as Map)).toList(growable: false);
|
||||||
growable: false);
|
|
||||||
Map<Object?, Object?> outputSet = theSet['output'] as Map<Object?, Object?>;
|
Map<Object?, Object?> outputSet = theSet['output'] as Map<Object?, Object?>;
|
||||||
VariableSpec output = parseVar(outputSet);
|
VariableSpec output = parseVar(outputSet);
|
||||||
String d4rtCode = SetUtils.stringValue(theSet, "d4rtCode");
|
String d4rtCode = SetUtils.stringValue(theSet, "d4rtCode");
|
||||||
|
|
@ -427,4 +323,3 @@ class Formula extends FormulaElement {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
165
lib/set_utils.dart
Normal file
165
lib/set_utils.dart
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
|
||||||
|
import 'package:d4rt/d4rt.dart';
|
||||||
|
|
||||||
|
import 'formula_models.dart';
|
||||||
|
|
||||||
|
abstract class SetUtils {
|
||||||
|
static Object safeGet(Map<Object?, Object?> map, String key) {
|
||||||
|
if (!map.containsKey(key)) {
|
||||||
|
throw ArgumentError("Key not found: $key -- $map");
|
||||||
|
}
|
||||||
|
return map[key] ?? "Not possible!!!";
|
||||||
|
}
|
||||||
|
|
||||||
|
static String stringValue(Map<Object?, Object?> map, String key) {
|
||||||
|
return safeGet(map, key).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<Object?> listValue(Map<Object?, Object?> map, String key) {
|
||||||
|
return safeGet(map, key) as List<Object?>;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Number numberValue(Map<Object?, Object?> map, String key) {
|
||||||
|
return double.parse(stringValue(map, key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses a d4rt array literal (containing maps and arrays) to a List<Object?>
|
||||||
|
/// using d4rt
|
||||||
|
static List<Object?> parseD4rtLiteral(String arrayStringLiteral) {
|
||||||
|
var d4rt = D4rt();
|
||||||
|
final buffer = StringBuffer();
|
||||||
|
buffer.write("main(){ return $arrayStringLiteral; }");
|
||||||
|
final code = buffer.toString();
|
||||||
|
|
||||||
|
final List<Object?> list = d4rt.execute(source: code);
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Escapes special characters in a string for use in D4RT literals
|
||||||
|
@deprecated
|
||||||
|
static String escapeD4rtString(String input) {
|
||||||
|
return input
|
||||||
|
.replaceAll(r'\\', r'\\\\') // escape backslashes first
|
||||||
|
.replaceAll('\n', r'\\n')
|
||||||
|
.replaceAll('\r', r'\\r')
|
||||||
|
.replaceAll('\t', r'\\t')
|
||||||
|
.replaceAll('"', r'\"');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses corpus elements from an array string literal.
|
||||||
|
/// Determines if each element is a formula or a unit and converts accordingly.
|
||||||
|
static List<FormulaElement> parseCorpusElements(String arrayStringLiteral) {
|
||||||
|
final List<Object?> elements = parseD4rtLiteral(arrayStringLiteral);
|
||||||
|
|
||||||
|
final List<FormulaElement> result = [];
|
||||||
|
for (final element in elements) {
|
||||||
|
if (element is Map<Object?, Object?>) {
|
||||||
|
if (element.containsKey('d4rtCode')) {
|
||||||
|
result.add(Formula.fromSet(element));
|
||||||
|
} else
|
||||||
|
if (element.containsKey('name') && element.containsKey('symbol')) {
|
||||||
|
result.add(UnitSpec.fromSet(element));
|
||||||
|
} else {
|
||||||
|
throw ArgumentError('Unknown element type: $element');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw ArgumentError('Element must be a Map: $element');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pretty prints a dynamic value (Set, Array, string or number) as a Dart literal.
|
||||||
|
/// Uses JSON-like formatting but for Dart language, with proper indentation.
|
||||||
|
static String prettyPrint(dynamic value, {int indent = 0}) {
|
||||||
|
if (value is String) {
|
||||||
|
return _prettyPrintString(value, indent);
|
||||||
|
} else if (value is num) {
|
||||||
|
return _prettyPrintNumber(value, indent);
|
||||||
|
} else if (value is Set) {
|
||||||
|
return _prettyPrintSet(value, indent);
|
||||||
|
} else if (value is List) {
|
||||||
|
return _prettyPrintArray(value, indent);
|
||||||
|
} else if (value is Map) {
|
||||||
|
return _prettyPrintMap(value, indent);
|
||||||
|
} else {
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pretty prints a simple string, escaping special characters if needed.
|
||||||
|
static String _prettyPrintString(String s, int indent) {
|
||||||
|
// Check if the string needs raw string formatting (newlines, $, backslashes, quotes)
|
||||||
|
final needsRawString = s.contains('\n') ||
|
||||||
|
s.contains(r'$') ||
|
||||||
|
s.contains(r'\\') ||
|
||||||
|
s.contains('"');
|
||||||
|
|
||||||
|
if (needsRawString) {
|
||||||
|
return _prettyPrintRawString(s, indent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple string with escaped quotes
|
||||||
|
return '"${s.replaceAll('"', r'\"')}"';
|
||||||
|
//'
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pretty prints a number.
|
||||||
|
static String _prettyPrintNumber(num n, int indent) {
|
||||||
|
return n.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pretty prints a Set as a Dart set literal.
|
||||||
|
static String _prettyPrintSet(Set s, int indent) {
|
||||||
|
if (s.isEmpty) {
|
||||||
|
return '{}';
|
||||||
|
}
|
||||||
|
|
||||||
|
final indentStr = ' ' * indent;
|
||||||
|
final innerIndent = ' ' * (indent + 1);
|
||||||
|
|
||||||
|
final elements = s.map((e) => '$innerIndent${prettyPrint(e, indent: indent + 1)}').join(',\n');
|
||||||
|
return '{$elements\n$indentStr}';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pretty prints an Array/List as a Dart list literal.
|
||||||
|
static String _prettyPrintArray(List a, int indent) {
|
||||||
|
if (a.isEmpty) {
|
||||||
|
return '[]';
|
||||||
|
}
|
||||||
|
|
||||||
|
final indentStr = ' ' * indent;
|
||||||
|
final innerIndent = ' ' * (indent + 1);
|
||||||
|
|
||||||
|
final elements = a.map((e) => '$innerIndent${prettyPrint(e, indent: indent + 1)}').join(',\n');
|
||||||
|
return '[\n$elements\n$indentStr]';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pretty prints a Map as a Dart map literal.
|
||||||
|
static String _prettyPrintMap(Map m, int indent) {
|
||||||
|
if (m.isEmpty) {
|
||||||
|
return '{}';
|
||||||
|
}
|
||||||
|
|
||||||
|
final indentStr = ' ' * indent;
|
||||||
|
final innerIndent = ' ' * (indent + 1);
|
||||||
|
|
||||||
|
final entries = m.entries.map((e) {
|
||||||
|
final key = prettyPrint(e.key, indent: indent + 1);
|
||||||
|
final value = prettyPrint(e.value, indent: indent + 1);
|
||||||
|
return '$innerIndent$key: $value';
|
||||||
|
}).join(',\n');
|
||||||
|
|
||||||
|
return '{\n$entries\n$indentStr}';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pretty prints a raw string (for strings containing newlines, $, backslashes, etc.)
|
||||||
|
/// Uses Dart's raw string syntax r"""..."""
|
||||||
|
static String _prettyPrintRawString(String s, int indent) {
|
||||||
|
// Escape triple quotes by replacing """ with ""\"
|
||||||
|
final escaped = s.replaceAll('"""', r'""\\"');
|
||||||
|
return 'r"""$escaped"""';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:d4rt_formulas/corpus.dart';
|
import 'package:d4rt_formulas/corpus.dart';
|
||||||
import 'package:d4rt_formulas/defaults/default_corpus.dart';
|
import 'package:d4rt_formulas/defaults/default_corpus.dart';
|
||||||
import 'package:d4rt_formulas/formula_evaluator.dart';
|
import 'package:d4rt_formulas/formula_evaluator.dart';
|
||||||
|
import 'package:d4rt_formulas/set_utils.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:d4rt_formulas/formula_models.dart';
|
import 'package:d4rt_formulas/formula_models.dart';
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue