Merge branch 'feature/pretty-print-formulas'
This commit is contained in:
commit
a9fd3bfc99
11 changed files with 247 additions and 210 deletions
6
TODO.md
6
TODO.md
|
|
@ -47,5 +47,11 @@
|
|||
- [R] There is one row for the ouput variable, similar to the row for the input variable
|
||||
- [R] d4rtCode is a text area with dart syntax highligthing
|
||||
- [R] At the botton, a button allows to test the edited Formula, launching a FormulaScreen
|
||||
- [X] Create SetUtils.prettyPrint(): receives a dynamic Set, Array, string or number. Convert to a dart representation of than value (a set/array literal), json-like, but for dart language. Do it recursivelly on local functions to that method:
|
||||
- _prettyPrintString(String s, int indent): Only for simple strings
|
||||
- _prettyPrintNumber(Number n, int indent)
|
||||
- _prettyPrintSet(Set s, int indent)
|
||||
- _prettyPrintArray(dynamic[] a, int indent)
|
||||
- _prettyPrintRawString(String s, int indent): Use _prettyPrintRawString when the string contains newlines, $, backlash...
|
||||
- [ ] When _FormulaScreenState._evaluateFormula() detect an error, instead of show an SnackBar, show a ExpansionTile with "⚠️ There were an error. Show details..." with the details of the exception. The ExpansionTile will be invisible if there is no error.
|
||||
- [ ] Investigate https://pub.dev/packages/quantity
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
import 'package:d4rt/d4rt.dart';
|
||||
|
||||
void main() {
|
||||
final code = '''
|
||||
int fib(int n) {
|
||||
if (n <= 1) return n;
|
||||
return fib(n - 1) + fib(n - 2);
|
||||
}
|
||||
main() {
|
||||
return fib(6);
|
||||
}
|
||||
''';
|
||||
|
||||
final interpreter = D4rt();
|
||||
final result = interpreter.execute(source: code);
|
||||
print('Result: $result'); // Result: 8
|
||||
}
|
||||
|
|
@ -53,16 +53,15 @@ class _FormulaListState extends State<FormulaList> {
|
|||
}).toList();
|
||||
}
|
||||
|
||||
String _formulaAndDependenciesToStringLiteral(Formula formula) {
|
||||
// Get the formula and its dependencies
|
||||
final dependencies = widget.corpus.withDependencies(formula);
|
||||
return SetUtils.prettyPrint(dependencies.map((f) => f.toMap()).toList());
|
||||
}
|
||||
|
||||
void _shareFormula(Formula formula) async {
|
||||
try {
|
||||
// Get the formula and its dependencies
|
||||
final dependencies = widget.corpus.withDependencies(formula);
|
||||
|
||||
// Convert each dependency to its string literal representation
|
||||
final literals = dependencies.map((element) => element.toStringLiteral()).toList();
|
||||
|
||||
// Create an array string literal containing all the elements
|
||||
final exportString = '[${literals.join(', ')}]';
|
||||
final exportString = _formulaAndDependenciesToStringLiteral(formula);
|
||||
|
||||
// Share the string
|
||||
await share_plus.SharePlus.instance.share(
|
||||
|
|
@ -90,14 +89,7 @@ class _FormulaListState extends State<FormulaList> {
|
|||
|
||||
void _copyFormula(Formula formula) async {
|
||||
try {
|
||||
// Get the formula and its dependencies
|
||||
final dependencies = widget.corpus.withDependencies(formula);
|
||||
|
||||
// Convert each dependency to its string literal representation
|
||||
final literals = dependencies.map((element) => element.toStringLiteral()).toList();
|
||||
|
||||
// Create an array string literal containing all the elements
|
||||
final exportString = '[${literals.join(', ')}]';
|
||||
final exportString = _formulaAndDependenciesToStringLiteral(formula);
|
||||
|
||||
// Copy to clipboard
|
||||
await Clipboard.setData(ClipboardData(text: exportString));
|
||||
|
|
|
|||
|
|
@ -63,10 +63,10 @@ class D4rtEditingController extends TextEditingController {
|
|||
if( _validateAsD4rtExpression(text) && _lastValue is StringResult ){
|
||||
return true;
|
||||
}
|
||||
if( _validateAsD4rtExpression('"' + text + '"') && _lastValue is StringResult ){
|
||||
if( _validateAsD4rtExpression('"$text"') && _lastValue is StringResult ){
|
||||
return true;
|
||||
}
|
||||
if( _validateAsD4rtExpression("'" + text + "'") && _lastValue is StringResult ){
|
||||
if( _validateAsD4rtExpression("'$text'") && _lastValue is StringResult ){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import '../formula_models.dart';
|
||||
import 'corpus_database_interface.dart';
|
||||
import 'formulas_database.dart';
|
||||
import 'package:d4rt_formulas/formula_models.dart' as models;
|
||||
|
|
@ -11,7 +12,7 @@ extension CorpusDatabaseExtension on FormulasDatabase {
|
|||
|
||||
for (final element in elements) {
|
||||
try {
|
||||
final parsed = models.parseCorpusElements('[${element.elementText}]');
|
||||
final parsed = SetUtils.parseCorpusElements('[${element.elementText}]');
|
||||
print("PARSED:$element");
|
||||
parsedElements.addAll(parsed);
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import 'package:d4rt/d4rt.dart';
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:d4rt_formulas/d4rt_formulas.dart';
|
||||
|
||||
typedef Number = double;
|
||||
|
||||
abstract class SetUtils {
|
||||
static Object safeGet(Map<Object?, Object?> map, String key) {
|
||||
if (!map.containsKey(key)) {
|
||||
|
|
@ -21,69 +23,160 @@ abstract class SetUtils {
|
|||
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
|
||||
List<Object?> parseD4rtLiteral(String arrayStringLiteral) {
|
||||
var d4rt = D4rt();
|
||||
final buffer = StringBuffer();
|
||||
buffer.write("main(){ return $arrayStringLiteral; }");
|
||||
final code = buffer.toString();
|
||||
/// 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);
|
||||
final List<Object?> list = d4rt.execute(source: code);
|
||||
|
||||
return list;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// Escapes special characters in a string for use in D4RT literals
|
||||
String escapeD4rtString(String input) {
|
||||
return input
|
||||
.replaceAll(r'\', r'\\') // Escape backslashes first
|
||||
.replaceAll('\n', r'\n') // Escape newlines
|
||||
.replaceAll('\r', r'\r') // Escape carriage returns
|
||||
.replaceAll('\t', r'\t') // Escape tabs
|
||||
.replaceAll('"', r'\"'); // Escape quotes last
|
||||
}
|
||||
/// 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.
|
||||
List<FormulaElement> parseCorpusElements(String arrayStringLiteral) {
|
||||
final List<Object?> elements = parseD4rtLiteral(arrayStringLiteral);
|
||||
/// 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?>) {
|
||||
// Check if it's a formula by looking for required formula properties
|
||||
// Formulas typically have 'd4rtCode' and 'input'/'output' properties
|
||||
if (element.containsKey('d4rtCode')) {
|
||||
result.add(Formula.fromSet(element));
|
||||
}
|
||||
// Units typically have 'name', 'symbol', and 'baseUnit' properties
|
||||
else if (element.containsKey('name') && element.containsKey('symbol')) {
|
||||
result.add(UnitSpec.fromSet(element));
|
||||
}
|
||||
else {
|
||||
throw ArgumentError('Unknown element type: $element');
|
||||
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 {
|
||||
throw ArgumentError('Element must be a Map: $element');
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
/// 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"""';
|
||||
}
|
||||
}
|
||||
|
||||
typedef Number = double;
|
||||
|
||||
/// Abstract base class for formula elements
|
||||
abstract class FormulaElement {
|
||||
/// Creates a string literal representation of the FormulaElement that can be parsed
|
||||
/// by the D4RT parser to recreate the same FormulaElement object.
|
||||
String toStringLiteral();
|
||||
Map<String,dynamic> toMap();
|
||||
|
||||
String toStringLiteral() {
|
||||
final map = toMap();
|
||||
return SetUtils.prettyPrint(map);
|
||||
}
|
||||
}
|
||||
|
||||
class UnitSpec implements FormulaElement {
|
||||
class UnitSpec extends FormulaElement {
|
||||
final String name;
|
||||
final String baseUnit;
|
||||
final String symbol;
|
||||
|
|
@ -91,6 +184,19 @@ class UnitSpec implements FormulaElement {
|
|||
final String? codeFromUnitToBase;
|
||||
final String? codeFromBaseToUnit;
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
"name": name,
|
||||
"baseUnit": baseUnit,
|
||||
"symbol": symbol,
|
||||
if (factorFromUnitToBase != null) 'factor': factorFromUnitToBase,
|
||||
if (codeFromUnitToBase != null) 'toBase': codeFromUnitToBase,
|
||||
if (codeFromBaseToUnit != null) 'fromBase': codeFromBaseToUnit,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
UnitSpec({
|
||||
required this.name,
|
||||
required this.baseUnit,
|
||||
|
|
@ -104,7 +210,7 @@ class UnitSpec implements FormulaElement {
|
|||
String name = SetUtils.stringValue(theSet, "name");
|
||||
String symbol = SetUtils.stringValue(theSet, "symbol");
|
||||
|
||||
if( theSet.containsKey("isBase") ){
|
||||
if (theSet.containsKey("isBase")) {
|
||||
return UnitSpec(name: name, baseUnit: name, symbol: symbol, factorFromUnitToBase: 1);
|
||||
}
|
||||
|
||||
|
|
@ -118,76 +224,56 @@ class UnitSpec implements FormulaElement {
|
|||
symbol: symbol,
|
||||
factorFromUnitToBase: factorFromUnitToBase,
|
||||
);
|
||||
}
|
||||
else if( theSet.containsKey("toBase")) {
|
||||
String codeFromBaseToUnit = SetUtils.stringValue(
|
||||
theSet,
|
||||
"fromBase",
|
||||
} else if (theSet.containsKey("toBase")) {
|
||||
String codeFromBaseToUnit = SetUtils.stringValue(theSet, "fromBase");
|
||||
String codeFromUnitToBase = SetUtils.stringValue(theSet, "toBase");
|
||||
|
||||
return UnitSpec(
|
||||
name: name,
|
||||
baseUnit: baseUnit,
|
||||
symbol: symbol,
|
||||
codeFromBaseToUnit: codeFromBaseToUnit,
|
||||
codeFromUnitToBase: codeFromUnitToBase,
|
||||
);
|
||||
String codeFromUnitToBase = SetUtils.stringValue(
|
||||
theSet,
|
||||
"toBase",
|
||||
);
|
||||
|
||||
return UnitSpec(name: name,
|
||||
baseUnit: baseUnit,
|
||||
symbol: symbol,
|
||||
codeFromBaseToUnit: codeFromBaseToUnit,
|
||||
codeFromUnitToBase: codeFromUnitToBase);
|
||||
} else {
|
||||
throw ArgumentError("Need factor or toBase/fromBase");
|
||||
}
|
||||
else{
|
||||
throw ArgumentError( "Need factor or toBase/fromBase");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
static List<UnitSpec> fromArrayStringLiteral(String arrayStringLiteral) {
|
||||
final List<Object?> list = parseD4rtLiteral(arrayStringLiteral);
|
||||
final List<Object?> list = SetUtils.parseD4rtLiteral(arrayStringLiteral);
|
||||
|
||||
final units = list.map((set) => UnitSpec.fromSet(set as Map));
|
||||
|
||||
return units.toList(growable: false);
|
||||
}
|
||||
|
||||
@override
|
||||
String toStringLiteral() {
|
||||
final buffer = StringBuffer('{');
|
||||
buffer.write('"name": "${escapeD4rtString(name)}", "symbol": "${escapeD4rtString(symbol)}"');
|
||||
|
||||
if (name == baseUnit && factorFromUnitToBase == 1) {
|
||||
// This is a base unit
|
||||
buffer.write(', "isBase": true');
|
||||
} else {
|
||||
buffer.write(', "baseUnit": "${escapeD4rtString(baseUnit)}"');
|
||||
|
||||
if (factorFromUnitToBase != null) {
|
||||
buffer.write(', "factor": $factorFromUnitToBase');
|
||||
} else if (codeFromUnitToBase != null && codeFromBaseToUnit != null) {
|
||||
buffer.write(', "toBase": "${escapeD4rtString(codeFromUnitToBase!)}", "fromBase": "${escapeD4rtString(codeFromBaseToUnit!)}"');
|
||||
}
|
||||
}
|
||||
|
||||
buffer.write('}');
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
||||
|
||||
class VariableSpec {
|
||||
class VariableSpec extends FormulaElement{
|
||||
final String name;
|
||||
final String? unit;
|
||||
final List<dynamic>? values;
|
||||
|
||||
VariableSpec({required this.name, this.unit, this.values}){
|
||||
@override
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'name': name,
|
||||
if (unit != null) 'unit': unit,
|
||||
if (values != null) 'values': List.from(values!,growable: false),
|
||||
};
|
||||
}
|
||||
|
||||
VariableSpec({required this.name, this.unit, this.values}) {
|
||||
validate();
|
||||
}
|
||||
|
||||
void validate(){
|
||||
if( FormulaEvaluator.reservedVariableNames.contains(name) ){
|
||||
void validate() {
|
||||
if (FormulaEvaluator.reservedVariableNames.contains(name)) {
|
||||
throw ArgumentError("$name: is a reserved variable name for FormulaEvaluator");
|
||||
}
|
||||
final valuesValid = values != null && values?.isNotEmpty == true;
|
||||
if( unit == null && !valuesValid ){
|
||||
if (unit == null && !valuesValid) {
|
||||
throw ArgumentError("$name: at least unit or allowedValues should be valid");
|
||||
}
|
||||
}
|
||||
|
|
@ -207,31 +293,10 @@ class VariableSpec {
|
|||
@override
|
||||
int get hashCode => Object.hash(unit, name, values != null ? const DeepCollectionEquality().hash(values!) : 0);
|
||||
|
||||
@override
|
||||
String toStringLiteral() {
|
||||
final buffer = StringBuffer('{');
|
||||
buffer.write('"name": "${escapeD4rtString(name)}"');
|
||||
|
||||
if (unit != null) {
|
||||
buffer.write(', "unit": "${escapeD4rtString(unit!)}"');
|
||||
}
|
||||
|
||||
if (values != null && values!.isNotEmpty) {
|
||||
buffer.write(', "values": [${values!.map((value) {
|
||||
if (value is String) {
|
||||
return '"${escapeD4rtString(value)}"';
|
||||
} else {
|
||||
return value.toString();
|
||||
}
|
||||
}).join(", ")}]');
|
||||
}
|
||||
|
||||
buffer.write('}');
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
||||
|
||||
class Formula implements FormulaElement {
|
||||
class Formula extends FormulaElement {
|
||||
final String name;
|
||||
final String? description;
|
||||
final List<VariableSpec> input;
|
||||
|
|
@ -239,6 +304,18 @@ class Formula implements FormulaElement {
|
|||
final String d4rtCode;
|
||||
final List<String> tags;
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'name': name,
|
||||
if (description != null) 'description': description,
|
||||
'input': input.map((v) => v.toMap()).toList(growable: false),
|
||||
'output': output.toMap(),
|
||||
'd4rtCode': d4rtCode,
|
||||
if (tags.isNotEmpty) 'tags': List.from(tags, growable: false),
|
||||
};
|
||||
}
|
||||
|
||||
Formula({
|
||||
required this.name,
|
||||
this.description,
|
||||
|
|
@ -251,7 +328,9 @@ class Formula implements FormulaElement {
|
|||
}
|
||||
|
||||
void validate() {
|
||||
if (name.trim().isEmpty) {
|
||||
if (name
|
||||
.trim()
|
||||
.isEmpty) {
|
||||
throw ArgumentError('Formula name cannot be empty');
|
||||
}
|
||||
}
|
||||
|
|
@ -263,18 +342,20 @@ class Formula implements FormulaElement {
|
|||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is Formula &&
|
||||
runtimeType == other.runtimeType &&
|
||||
name == other.name &&
|
||||
description == other.description &&
|
||||
output == other.output &&
|
||||
ListEquality().equals(input, other.input) &&
|
||||
d4rtCode == other.d4rtCode &&
|
||||
ListEquality().equals(tags, other.tags);
|
||||
other is Formula &&
|
||||
runtimeType == other.runtimeType &&
|
||||
name == other.name &&
|
||||
description == other.description &&
|
||||
output == other.output &&
|
||||
ListEquality().equals(input, other.input) &&
|
||||
d4rtCode == other.d4rtCode &&
|
||||
ListEquality().equals(tags, other.tags);
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(name, description, ListEquality().hash(input), output, d4rtCode, ListEquality().hash(tags));
|
||||
Object.hash(
|
||||
name, description, ListEquality().hash(input), output, d4rtCode,
|
||||
ListEquality().hash(tags));
|
||||
|
||||
List<String> inputVarNames() =>
|
||||
input.map((v) => v.name).toList(growable: false);
|
||||
|
|
@ -291,7 +372,7 @@ class Formula implements FormulaElement {
|
|||
}
|
||||
|
||||
static List<Formula> fromArrayStringLiteral(String arrayStringLiteral) {
|
||||
final List<Object?> list = parseD4rtLiteral(arrayStringLiteral);
|
||||
final List<Object?> list = SetUtils.parseD4rtLiteral(arrayStringLiteral);
|
||||
|
||||
final formulas = list.map((set) => Formula.fromSet(set as Map));
|
||||
|
||||
|
|
@ -309,9 +390,11 @@ class Formula implements FormulaElement {
|
|||
if (allowed != null) {
|
||||
final types = allowed.map((v) => v.runtimeType).toSet();
|
||||
if (types.length > 1) {
|
||||
throw ArgumentError('Allowed values must be all Strings or all Numbers');
|
||||
throw ArgumentError(
|
||||
'Allowed values must be all Strings or all Numbers');
|
||||
}
|
||||
if (!types.contains(String) && !types.contains(double) && !types.contains(int)) {
|
||||
if (!types.contains(String) && !types.contains(double) &&
|
||||
!types.contains(int)) {
|
||||
throw ArgumentError('Allowed values must be Strings or Numbers');
|
||||
}
|
||||
}
|
||||
|
|
@ -323,13 +406,13 @@ class Formula implements FormulaElement {
|
|||
}
|
||||
|
||||
String name = SetUtils.stringValue(theSet, "name");
|
||||
String? description = theSet ["description"] as String?;
|
||||
List<String> tags = (theSet["tags"] as List<Object?>? ?? []).map((t) => t.toString()).toList();
|
||||
String? description = theSet["description"] as String?;
|
||||
List<String> tags = (theSet["tags"] as List<Object?>? ?? []).map((t) =>
|
||||
t.toString()).toList();
|
||||
final List<Object?> inputSet = SetUtils.listValue(theSet, "input");
|
||||
List<VariableSpec> input = inputSet
|
||||
.map((v) => parseVar(v as Map))
|
||||
.toList(growable: false);
|
||||
Map<Object?, Object?> outputSet = theSet.get("output");
|
||||
List<VariableSpec> input = inputSet.map((v) => parseVar(v as Map)).toList(
|
||||
growable: false);
|
||||
Map<Object?, Object?> outputSet = theSet['output'] as Map<Object?, Object?>;
|
||||
VariableSpec output = parseVar(outputSet);
|
||||
String d4rtCode = SetUtils.stringValue(theSet, "d4rtCode");
|
||||
|
||||
|
|
@ -342,30 +425,5 @@ class Formula implements FormulaElement {
|
|||
d4rtCode: d4rtCode,
|
||||
);
|
||||
}
|
||||
|
||||
/// Creates a string literal representation of the Formula that can be parsed
|
||||
/// by the D4RT parser to recreate the same Formula object.
|
||||
@override
|
||||
String toStringLiteral() {
|
||||
final inputStrings = input.map((varSpec) => varSpec.toStringLiteral()).toList();
|
||||
|
||||
final buffer = StringBuffer('{');
|
||||
buffer.write('"name": "$name"');
|
||||
|
||||
if (description != null) {
|
||||
buffer.write(', "description": r"""${description!}"""');
|
||||
}
|
||||
|
||||
buffer.write(', "input": [${inputStrings.join(", ")}]');
|
||||
buffer.write(', "output": ${output.toStringLiteral()}');
|
||||
|
||||
buffer.write(', "d4rtCode": r"""$d4rtCode"""');
|
||||
|
||||
if (tags.isNotEmpty) {
|
||||
buffer.write(', "tags": [${tags.map((tag) => '"${escapeD4rtString(tag)}"').join(", ")}]');
|
||||
}
|
||||
|
||||
buffer.write('}');
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import 'package:d4rt_formulas/d4rt_formulas.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'database/database_service.dart';
|
||||
import 'package:drift/drift.dart' as drift;
|
||||
import 'service_locator.dart';
|
||||
|
||||
import 'ai/formula_list.dart';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:d4rt/d4rt.dart';
|
||||
import 'dart:math' as Math;
|
||||
|
||||
|
||||
|
||||
void main(){
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:d4rt_formulas/database/database_service.dart';
|
||||
import 'package:d4rt_formulas/service_locator.dart';
|
||||
|
||||
void main() {
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ void main() {
|
|||
);
|
||||
|
||||
final literal = originalUnit.toStringLiteral();
|
||||
final parsedList = parseD4rtLiteral('[${literal}]');
|
||||
final parsedList = SetUtils.parseD4rtLiteral('[${literal}]');
|
||||
final parsedMap = parsedList[0] as Map<Object?, Object?>;
|
||||
final parsedUnit = UnitSpec.fromSet(parsedMap);
|
||||
|
||||
|
|
@ -246,22 +246,22 @@ void main() {
|
|||
|
||||
test('Corpus.withDependencies returns formula and its dependencies', () async {
|
||||
final corpus = await testCorpus;
|
||||
|
||||
|
||||
// Get a formula that has units associated with it
|
||||
final formula = corpus.getFormula("Newton's Second Law");
|
||||
expect(formula, isNotNull);
|
||||
|
||||
|
||||
// Call withDependencies method
|
||||
final dependencies = corpus.withDependencies(formula!);
|
||||
|
||||
|
||||
// Check that the formula itself is included
|
||||
expect(dependencies.any((element) => element is Formula && element.name == formula.name), true);
|
||||
|
||||
|
||||
// Check that units from input and output are included
|
||||
for (final inputVar in formula.input) {
|
||||
if (inputVar.unit != null) {
|
||||
expect(dependencies.any((element) => element is UnitSpec && element.name == inputVar.unit), true);
|
||||
|
||||
|
||||
// Check that units with same base unit are included
|
||||
final unitsWithSameBase = corpus.unitsOfSameMagnitude(inputVar.unit!);
|
||||
for (final unitName in unitsWithSameBase) {
|
||||
|
|
@ -269,20 +269,21 @@ void main() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (formula.output.unit != null) {
|
||||
expect(dependencies.any((element) => element is UnitSpec && element.name == formula.output.unit), true);
|
||||
|
||||
|
||||
// Check that units with same base unit as output are included
|
||||
final outputUnitsWithSameBase = corpus.unitsOfSameMagnitude(formula.output.unit!);
|
||||
for (final unitName in outputUnitsWithSameBase) {
|
||||
expect(dependencies.any((element) => element is UnitSpec && element.name == unitName), true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Verify that there are no duplicates by checking the length of the list vs the set
|
||||
final uniqueDependencies = dependencies.toSet();
|
||||
expect(dependencies.length, equals(uniqueDependencies.length));
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import 'package:d4rt_formulas/corpus.dart';
|
|||
import 'package:d4rt_formulas/defaults/default_corpus.dart';
|
||||
import 'package:d4rt_formulas/formula_evaluator.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:d4rt_formulas/formula_models.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
|
|
|||
Loading…
Reference in a new issue