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 '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) {

View file

@ -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,60 +23,55 @@ 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;
}
/// 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
}
/// 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);
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');
}
} else {
throw ArgumentError('Element must be a Map: $element');
}
return list;
}
return result;
/// Escapes special characters in a string for use in D4RT literals
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;
}
}
typedef Number = double;
/// Abstract base class for formula elements
abstract class FormulaElement {
@ -104,7 +101,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,32 +115,24 @@ 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));
@ -153,18 +142,17 @@ class UnitSpec implements FormulaElement {
@override
String toStringLiteral() {
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) {
// This is a base unit
buffer.write(', "isBase": true');
} else {
buffer.write(', "baseUnit": "${escapeD4rtString(baseUnit)}"');
buffer.write(', "baseUnit": "${SetUtils.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(', "toBase": "${SetUtils.escapeD4rtString(codeFromUnitToBase!)}", "fromBase": "${SetUtils.escapeD4rtString(codeFromBaseToUnit!)}"');
}
}
@ -178,16 +166,16 @@ class VariableSpec {
final String? unit;
final List<dynamic>? values;
VariableSpec({required this.name, this.unit, this.values}){
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");
}
}
@ -210,20 +198,20 @@ class VariableSpec {
@override
String toStringLiteral() {
final buffer = StringBuffer('{');
buffer.write('"name": "${escapeD4rtString(name)}"');
buffer.write('"name": "${SetUtils.escapeD4rtString(name)}"');
if (unit != null) {
buffer.write(', "unit": "${escapeD4rtString(unit!)}"');
buffer.write(', "unit": "${SetUtils.escapeD4rtString(unit!)}"');
}
if (values != null && values!.isNotEmpty) {
buffer.write(', "values": [${values!.map((value) {
if (value is String) {
return '"${escapeD4rtString(value)}"';
return '"${SetUtils.escapeD4rtString(value)}"';
} else {
return value.toString();
}
}).join(", ")}]');
}).join(", ")} ]');
}
buffer.write('}');
@ -276,8 +264,7 @@ class Formula implements FormulaElement {
int get hashCode =>
Object.hash(name, description, ListEquality().hash(input), output, d4rtCode, ListEquality().hash(tags));
List<String> inputVarNames() =>
input.map((v) => v.name).toList(growable: false);
List<String> inputVarNames() => input.map((v) => v.name).toList(growable: false);
factory Formula.fromStringLiteral(String setStringLiteral) {
var d4rt = D4rt();
@ -291,7 +278,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));
@ -323,13 +310,11 @@ class Formula implements FormulaElement {
}
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();
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");
@ -350,7 +335,7 @@ class Formula implements FormulaElement {
final inputStrings = input.map((varSpec) => varSpec.toStringLiteral()).toList();
final buffer = StringBuffer('{');
buffer.write('"name": "$name"');
buffer.write('"name": "${SetUtils.escapeD4rtString(name)}"');
if (description != null) {
buffer.write(', "description": r"""${description!}"""');
@ -362,7 +347,7 @@ class Formula implements FormulaElement {
buffer.write(', "d4rtCode": r"""$d4rtCode"""');
if (tags.isNotEmpty) {
buffer.write(', "tags": [${tags.map((tag) => '"${escapeD4rtString(tag)}"').join(", ")}]');
buffer.write(', "tags": [${tags.map((tag) => '"${SetUtils.escapeD4rtString(tag)}"').join(", ")}]');
}
buffer.write('}');

View file

@ -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';

View file

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

View file

@ -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);