setutils: static util methods

This commit is contained in:
Álvaro González 2026-03-01 13:51:14 +01:00
parent aadfc9dac5
commit bb468ff601
6 changed files with 80 additions and 112 deletions

View file

@ -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
}

View file

@ -1,3 +1,4 @@
import '../formula_models.dart';
import 'corpus_database_interface.dart'; import 'corpus_database_interface.dart';
import 'formulas_database.dart'; import 'formulas_database.dart';
import 'package:d4rt_formulas/formula_models.dart' as models; import 'package:d4rt_formulas/formula_models.dart' as models;
@ -11,7 +12,7 @@ extension CorpusDatabaseExtension on FormulasDatabase {
for (final element in elements) { for (final element in elements) {
try { try {
final parsed = models.parseCorpusElements('[${element.elementText}]'); final parsed = SetUtils.parseCorpusElements('[${element.elementText}]');
print("PARSED:$element"); print("PARSED:$element");
parsedElements.addAll(parsed); parsedElements.addAll(parsed);
} catch (e) { } catch (e) {

View file

@ -2,6 +2,8 @@ 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';
typedef Number = double;
abstract class SetUtils { abstract class SetUtils {
static Object safeGet(Map<Object?, Object?> map, String key) { static Object safeGet(Map<Object?, Object?> map, String key) {
if (!map.containsKey(key)) { if (!map.containsKey(key)) {
@ -21,11 +23,10 @@ abstract class SetUtils {
static Number numberValue(Map<Object?, Object?> map, String key) { static Number numberValue(Map<Object?, Object?> map, String key) {
return double.parse(stringValue(map, key)); return double.parse(stringValue(map, key));
} }
}
/// Parses a d4rt array literal (containing maps and arrays) to a List<Object?> /// Parses a d4rt array literal (containing maps and arrays) to a List<Object?>
/// using d4rt /// using d4rt
List<Object?> parseD4rtLiteral(String arrayStringLiteral) { static List<Object?> parseD4rtLiteral(String arrayStringLiteral) {
var d4rt = D4rt(); var d4rt = D4rt();
final buffer = StringBuffer(); final buffer = StringBuffer();
buffer.write("main(){ return $arrayStringLiteral; }"); buffer.write("main(){ return $arrayStringLiteral; }");
@ -34,36 +35,32 @@ List<Object?> parseD4rtLiteral(String arrayStringLiteral) {
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 /// Escapes special characters in a string for use in D4RT literals
String escapeD4rtString(String input) { static String escapeD4rtString(String input) {
return input return input
.replaceAll(r'\', r'\\') // Escape backslashes first .replaceAll(r'\\', r'\\\\') // escape backslashes first
.replaceAll('\n', r'\n') // Escape newlines .replaceAll('\n', r'\\n')
.replaceAll('\r', r'\r') // Escape carriage returns .replaceAll('\r', r'\\r')
.replaceAll('\t', r'\t') // Escape tabs .replaceAll('\t', r'\\t')
.replaceAll('"', r'\"'); // Escape quotes last .replaceAll('"', r'\\"');
} }
/// Parses corpus elements from an array string literal. /// Parses corpus elements from an array string literal.
/// Determines if each element is a formula or a unit and converts accordingly. /// Determines if each element is a formula or a unit and converts accordingly.
List<FormulaElement> parseCorpusElements(String arrayStringLiteral) { static List<FormulaElement> parseCorpusElements(String arrayStringLiteral) {
final List<Object?> elements = parseD4rtLiteral(arrayStringLiteral); final List<Object?> elements = parseD4rtLiteral(arrayStringLiteral);
final List<FormulaElement> result = []; final List<FormulaElement> result = [];
for (final element in elements) { for (final element in elements) {
if (element is Map<Object?, Object?>) { 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')) { if (element.containsKey('d4rtCode')) {
result.add(Formula.fromSet(element)); result.add(Formula.fromSet(element));
} } else
// Units typically have 'name', 'symbol', and 'baseUnit' properties if (element.containsKey('name') && element.containsKey('symbol')) {
else if (element.containsKey('name') && element.containsKey('symbol')) {
result.add(UnitSpec.fromSet(element)); result.add(UnitSpec.fromSet(element));
} } else {
else {
throw ArgumentError('Unknown element type: $element'); throw ArgumentError('Unknown element type: $element');
} }
} else { } else {
@ -72,9 +69,9 @@ List<FormulaElement> parseCorpusElements(String arrayStringLiteral) {
} }
return result; return result;
}
} }
typedef Number = double;
/// Abstract base class for formula elements /// Abstract base class for formula elements
abstract class FormulaElement { abstract class FormulaElement {
@ -104,7 +101,7 @@ class UnitSpec implements FormulaElement {
String name = SetUtils.stringValue(theSet, "name"); String name = SetUtils.stringValue(theSet, "name");
String symbol = SetUtils.stringValue(theSet, "symbol"); String symbol = SetUtils.stringValue(theSet, "symbol");
if( theSet.containsKey("isBase") ){ if (theSet.containsKey("isBase")) {
return UnitSpec(name: name, baseUnit: name, symbol: symbol, factorFromUnitToBase: 1); return UnitSpec(name: name, baseUnit: name, symbol: symbol, factorFromUnitToBase: 1);
} }
@ -118,32 +115,24 @@ class UnitSpec implements FormulaElement {
symbol: symbol, symbol: symbol,
factorFromUnitToBase: factorFromUnitToBase, factorFromUnitToBase: factorFromUnitToBase,
); );
} } else if (theSet.containsKey("toBase")) {
else if( theSet.containsKey("toBase")) { String codeFromBaseToUnit = SetUtils.stringValue(theSet, "fromBase");
String codeFromBaseToUnit = SetUtils.stringValue( String codeFromUnitToBase = SetUtils.stringValue(theSet, "toBase");
theSet,
"fromBase",
);
String codeFromUnitToBase = SetUtils.stringValue(
theSet,
"toBase",
);
return UnitSpec(name: name, return UnitSpec(
name: name,
baseUnit: baseUnit, baseUnit: baseUnit,
symbol: symbol, symbol: symbol,
codeFromBaseToUnit: codeFromBaseToUnit, codeFromBaseToUnit: codeFromBaseToUnit,
codeFromUnitToBase: codeFromUnitToBase); codeFromUnitToBase: codeFromUnitToBase,
);
} else {
throw ArgumentError("Need factor or toBase/fromBase");
} }
else{
throw ArgumentError( "Need factor or toBase/fromBase");
}
} }
static List<UnitSpec> fromArrayStringLiteral(String arrayStringLiteral) { 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)); final units = list.map((set) => UnitSpec.fromSet(set as Map));
@ -153,18 +142,17 @@ class UnitSpec implements FormulaElement {
@override @override
String toStringLiteral() { String toStringLiteral() {
final buffer = StringBuffer('{'); final buffer = StringBuffer('{');
buffer.write('"name": "${escapeD4rtString(name)}", "symbol": "${escapeD4rtString(symbol)}"'); buffer.write('"name": "${SetUtils.escapeD4rtString(name)}", "symbol": "${SetUtils.escapeD4rtString(symbol)}"');
if (name == baseUnit && factorFromUnitToBase == 1) { if (name == baseUnit && factorFromUnitToBase == 1) {
// This is a base unit
buffer.write(', "isBase": true'); buffer.write(', "isBase": true');
} else { } else {
buffer.write(', "baseUnit": "${escapeD4rtString(baseUnit)}"'); buffer.write(', "baseUnit": "${SetUtils.escapeD4rtString(baseUnit)}"');
if (factorFromUnitToBase != null) { if (factorFromUnitToBase != null) {
buffer.write(', "factor": $factorFromUnitToBase'); buffer.write(', "factor": $factorFromUnitToBase');
} else if (codeFromUnitToBase != null && codeFromBaseToUnit != null) { } else if (codeFromUnitToBase != null && codeFromBaseToUnit != null) {
buffer.write(', "toBase": "${escapeD4rtString(codeFromUnitToBase!)}", "fromBase": "${escapeD4rtString(codeFromBaseToUnit!)}"'); buffer.write(', "toBase": "${SetUtils.escapeD4rtString(codeFromUnitToBase!)}", "fromBase": "${SetUtils.escapeD4rtString(codeFromBaseToUnit!)}"');
} }
} }
@ -178,16 +166,16 @@ class VariableSpec {
final String? unit; final String? unit;
final List<dynamic>? values; final List<dynamic>? values;
VariableSpec({required this.name, this.unit, this.values}){ VariableSpec({required this.name, this.unit, this.values}) {
validate(); validate();
} }
void validate(){ void validate() {
if( FormulaEvaluator.reservedVariableNames.contains(name) ){ if (FormulaEvaluator.reservedVariableNames.contains(name)) {
throw ArgumentError("$name: is a reserved variable name for FormulaEvaluator"); throw ArgumentError("$name: is a reserved variable name for FormulaEvaluator");
} }
final valuesValid = values != null && values?.isNotEmpty == true; 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"); throw ArgumentError("$name: at least unit or allowedValues should be valid");
} }
} }
@ -210,20 +198,20 @@ class VariableSpec {
@override @override
String toStringLiteral() { String toStringLiteral() {
final buffer = StringBuffer('{'); final buffer = StringBuffer('{');
buffer.write('"name": "${escapeD4rtString(name)}"'); buffer.write('"name": "${SetUtils.escapeD4rtString(name)}"');
if (unit != null) { if (unit != null) {
buffer.write(', "unit": "${escapeD4rtString(unit!)}"'); buffer.write(', "unit": "${SetUtils.escapeD4rtString(unit!)}"');
} }
if (values != null && values!.isNotEmpty) { if (values != null && values!.isNotEmpty) {
buffer.write(', "values": [${values!.map((value) { buffer.write(', "values": [${values!.map((value) {
if (value is String) { if (value is String) {
return '"${escapeD4rtString(value)}"'; return '"${SetUtils.escapeD4rtString(value)}"';
} else { } else {
return value.toString(); return value.toString();
} }
}).join(", ")}]'); }).join(", ")} ]');
} }
buffer.write('}'); buffer.write('}');
@ -276,8 +264,7 @@ class Formula implements FormulaElement {
int get hashCode => 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() => 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();
@ -291,7 +278,7 @@ class Formula implements FormulaElement {
} }
static List<Formula> fromArrayStringLiteral(String arrayStringLiteral) { 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)); final formulas = list.map((set) => Formula.fromSet(set as Map));
@ -323,13 +310,11 @@ class Formula implements FormulaElement {
} }
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) => t.toString()).toList(); List<String> tags = (theSet["tags"] as List<Object?>? ?? []).map((t) => t.toString()).toList();
final List<Object?> inputSet = SetUtils.listValue(theSet, "input"); final List<Object?> inputSet = SetUtils.listValue(theSet, "input");
List<VariableSpec> input = inputSet List<VariableSpec> input = inputSet.map((v) => parseVar(v as Map)).toList(growable: false);
.map((v) => parseVar(v as Map)) Map<Object?, Object?> outputSet = theSet['output'] as Map<Object?, Object?>;
.toList(growable: false);
Map<Object?, Object?> outputSet = theSet.get("output");
VariableSpec output = parseVar(outputSet); VariableSpec output = parseVar(outputSet);
String d4rtCode = SetUtils.stringValue(theSet, "d4rtCode"); String d4rtCode = SetUtils.stringValue(theSet, "d4rtCode");
@ -350,7 +335,7 @@ class Formula implements FormulaElement {
final inputStrings = input.map((varSpec) => varSpec.toStringLiteral()).toList(); final inputStrings = input.map((varSpec) => varSpec.toStringLiteral()).toList();
final buffer = StringBuffer('{'); final buffer = StringBuffer('{');
buffer.write('"name": "$name"'); buffer.write('"name": "${SetUtils.escapeD4rtString(name)}"');
if (description != null) { if (description != null) {
buffer.write(', "description": r"""${description!}"""'); buffer.write(', "description": r"""${description!}"""');
@ -362,7 +347,7 @@ class Formula implements FormulaElement {
buffer.write(', "d4rtCode": r"""$d4rtCode"""'); buffer.write(', "d4rtCode": r"""$d4rtCode"""');
if (tags.isNotEmpty) { if (tags.isNotEmpty) {
buffer.write(', "tags": [${tags.map((tag) => '"${escapeD4rtString(tag)}"').join(", ")}]'); buffer.write(', "tags": [${tags.map((tag) => '"${SetUtils.escapeD4rtString(tag)}"').join(", ")}]');
} }
buffer.write('}'); buffer.write('}');

View file

@ -2,7 +2,6 @@ import 'package:d4rt_formulas/d4rt_formulas.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
import 'database/database_service.dart'; import 'database/database_service.dart';
import 'package:drift/drift.dart' as drift;
import 'service_locator.dart'; import 'service_locator.dart';
import 'ai/formula_list.dart'; import 'ai/formula_list.dart';

View file

@ -1,5 +1,5 @@
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'package:d4rt/d4rt.dart';
void main(){ void main(){

View file

@ -154,7 +154,7 @@ void main() {
); );
final literal = originalUnit.toStringLiteral(); final literal = originalUnit.toStringLiteral();
final parsedList = parseD4rtLiteral('[${literal}]'); final parsedList = SetUtils.parseD4rtLiteral('[${literal}]');
final parsedMap = parsedList[0] as Map<Object?, Object?>; final parsedMap = parsedList[0] as Map<Object?, Object?>;
final parsedUnit = UnitSpec.fromSet(parsedMap); final parsedUnit = UnitSpec.fromSet(parsedMap);