Merge branch 'apgar-test'
This commit is contained in:
commit
b053fc3900
31 changed files with 306 additions and 152 deletions
21
Dockerfile
Normal file
21
Dockerfile
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Use the official Flutter SDK image
|
||||||
|
FROM ghcr.io/cirruslabs/flutter:stable
|
||||||
|
|
||||||
|
# Install cmake, ninja, clang, pkg-config for flutter linux
|
||||||
|
RUN apt-get update && apt-get install -y cmake ninja-build clang pkg-config libgtk-3-dev liblzma-dev
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Configure cache directories
|
||||||
|
ENV PUB_CACHE=/cache/pub-cache
|
||||||
|
ENV GRADLE_USER_HOME=/cache/gradle-cache
|
||||||
|
RUN mkdir -p $PUB_CACHE $GRADLE_USER_HOME
|
||||||
|
|
||||||
|
# Copy pubspec files and get dependencies
|
||||||
|
# COPY pubspec.yaml pubspec.lock ./
|
||||||
|
# RUN flutter pub get
|
||||||
|
|
||||||
|
# Copy the rest of the application code and build
|
||||||
|
# Commented out to avoid building the app during image creation, this will be handled externally by makefile
|
||||||
|
# COPY . .
|
||||||
|
# RUN flutter build apk --release
|
||||||
25
Makefile
Normal file
25
Makefile
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
|
||||||
|
flutter-container-exec = podman-compose run --entrypoint "$(1)" flutter
|
||||||
|
|
||||||
|
all: clean-podman build-linux-debug-podman build-linux-debug-podman
|
||||||
|
|
||||||
|
build-podman:
|
||||||
|
podman-compose build
|
||||||
|
|
||||||
|
clean-podman: build-podman
|
||||||
|
$(call flutter-container-exec, flutter clean)
|
||||||
|
|
||||||
|
pub-get-podman: build-podman
|
||||||
|
$(call flutter-container-exec, flutter pub get)
|
||||||
|
|
||||||
|
build-android-release-podman: pub-get-podman
|
||||||
|
$(call flutter-container-exec, flutter build apk --release)
|
||||||
|
|
||||||
|
build-linux-debug-podman: pub-get-podman
|
||||||
|
$(call flutter-container-exec, flutter build linux --debug)
|
||||||
|
|
||||||
|
run-linux-debug: build-linux-debug-podman
|
||||||
|
build/linux/x64/debug/bundle/d4rt_formulas
|
||||||
|
|
||||||
|
run-web-release-podman: build-web-release-podman
|
||||||
|
cd build/web && python3 -m http.server 8080
|
||||||
12
README.md
12
README.md
|
|
@ -1,3 +1,5 @@
|
||||||
|
https://github.com/Shahxad-Akram/flutter_tex/blob/master/example/lib/tex_view_markdown_example.dart
|
||||||
|
|
||||||
# Math Formulae Manager
|
# Math Formulae Manager
|
||||||
|
|
||||||
A comprehensive command-line application for managing and computing mathematical formulas across various disciplines including mathematics, physics, medicine, and engineering.
|
A comprehensive command-line application for managing and computing mathematical formulas across various disciplines including mathematics, physics, medicine, and engineering.
|
||||||
|
|
@ -123,15 +125,11 @@ Each formula includes:
|
||||||
- **Images** - Visual diagrams, graphs, or illustrations
|
- **Images** - Visual diagrams, graphs, or illustrations
|
||||||
- **Examples** - Sample calculations and use cases
|
- **Examples** - Sample calculations and use cases
|
||||||
|
|
||||||
## Project Structure
|
|
||||||
|
|
||||||
- `bin/` - Main executable and entry point
|
|
||||||
- `lib/` - Core library code and formula engine
|
|
||||||
- `test/` - Unit tests and formula validation tests
|
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
[Installation and usage instructions to be added]
|
This project uses `flutter`, so a valid installation is needed in order to build it.
|
||||||
|
|
||||||
|
For convenience, a containerized build is provided. It is based on `podman` and `podman-compose`. See [Makefile](Makefile) for details.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -120,4 +120,33 @@ Where:
|
||||||
"d4rtCode": "F = m * a;",
|
"d4rtCode": "F = m * a;",
|
||||||
"tags": ["physics", "mechanics", "newton"]
|
"tags": ["physics", "mechanics", "newton"]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Apgar Score
|
||||||
|
{
|
||||||
|
"name": "Apgar Score",
|
||||||
|
"description": "Newborn health assessment scoring system\n\nScores 0-2 for:\n1. Heart rate\n2. Breathing\n3. Muscle tone\n4. Reflexes\n5. Skin color\nTotal score 0-10",
|
||||||
|
"input": [
|
||||||
|
{"name": "HeartRate", "values": ["Absent", "< 100 bpm>", "> 100 bpm"] },
|
||||||
|
{"name": "Breathing", "values": ["Absent", "Weak, irregular", "Strong, robust cry"] },
|
||||||
|
{"name": "MuscleTone", "values": ["None", "Some", "Flexed arms/leg, resists extension"] },
|
||||||
|
{"name": "Reflexes", "values": ["No response", "Grimace on aggressive stimulation", "Cry on stimulation"] },
|
||||||
|
{"name": "SkinColor", "values": ["Blue or pale", "Blue extremities, pink body", "Pink"] }
|
||||||
|
],
|
||||||
|
"output": {"name": "Result", "unit": "scalar"},
|
||||||
|
"d4rtCode": """
|
||||||
|
var total = HeartRate + Breathing + MuscleTone + Reflexes + SkinColor;
|
||||||
|
late var interpretation;
|
||||||
|
if( total < 4 ) {
|
||||||
|
interpretation = 'Critical condition';
|
||||||
|
}
|
||||||
|
else if( total < 7 ){
|
||||||
|
interpretation = 'Needs assistance';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
interpretation = 'Normal';
|
||||||
|
}
|
||||||
|
Result = 'Score: \$total - \$interpretation';
|
||||||
|
""",
|
||||||
|
"tags": ["medical", "pediatrics", "assessment"]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
3
assets/units/scalar.d4rt.units
Normal file
3
assets/units/scalar.d4rt.units
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
[
|
||||||
|
{"name": "scalar", "symbol": "", "isBase": true},
|
||||||
|
]
|
||||||
13
docker-compose.yml
Normal file
13
docker-compose.yml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
flutter:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
image: d4rt-formulas-builder
|
||||||
|
volumes:
|
||||||
|
- ./.build-container-cache:/cache:z
|
||||||
|
- .:/app:z # Link the current directory to /app in the container
|
||||||
|
environment:
|
||||||
|
- FLUTTER_FLAVOR=prod # Example environment variable, adjust as needed
|
||||||
|
|
@ -37,8 +37,8 @@ void main() {
|
||||||
print(' Mass: 10.0 kg');
|
print(' Mass: 10.0 kg');
|
||||||
print(' Acceleration: 9.8 m/s²');
|
print(' Acceleration: 9.8 m/s²');
|
||||||
print(' Calculated Force: $force N');
|
print(' Calculated Force: $force N');
|
||||||
print(' Output variable: ${evaluator.getOutputVariableName(newtonFormula)}');
|
print(' Output variable: ${newtonFormula.output.name}');
|
||||||
print(' Output magnitude: ${evaluator.getOutputVariableMagnitude(newtonFormula)}');
|
print(' Output magnitude: ${newtonFormula.output.unit}');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(' Error: $e');
|
print(' Error: $e');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
# launch as: firejail --profile=firejail-warp-terminal.profile /opt/warpdotdev/warp-terminal/warp
|
|
||||||
private /home/alvaro/repos/d4rt_formulas/
|
|
||||||
blacklist /datos-1T
|
|
||||||
blacklist /datos-luks/
|
|
||||||
blacklist /media
|
|
||||||
|
|
||||||
# net none # disable network
|
|
||||||
# noroot # don't run as root
|
|
||||||
caps.drop all # drop Linux capabilities
|
|
||||||
# seccomp # enable syscall filtering
|
|
||||||
First version of a formula widget
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
import '../formula_models.dart';
|
import '../formula_models.dart';
|
||||||
import '../formula_evaluator.dart';
|
import '../formula_evaluator.dart';
|
||||||
|
|
@ -11,24 +9,22 @@ class FormulaScreen extends StatefulWidget {
|
||||||
final Formula formula;
|
final Formula formula;
|
||||||
final Corpus corpus;
|
final Corpus corpus;
|
||||||
|
|
||||||
const FormulaScreen({
|
const FormulaScreen({super.key, required this.formula, required this.corpus});
|
||||||
super.key,
|
|
||||||
required this.formula,
|
|
||||||
required this.corpus,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<FormulaScreen> createState() => _FormulaScreenState();
|
State<FormulaScreen> createState() => _FormulaScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Create VariableWidget. Depending on VariableSpec.values, it can be a ValueDropdown or a D4rtEditingController
|
||||||
|
// The d4rtValue will be FormulaResult?
|
||||||
|
|
||||||
//// Start of D4rtEditingController class ////
|
//// Start of D4rtEditingController class ////
|
||||||
class D4rtEditingController extends TextEditingController {
|
class D4rtEditingController extends TextEditingController {
|
||||||
String? _lastError;
|
String? _lastError;
|
||||||
|
|
||||||
String? get lastError => _lastError;
|
String? get lastError => _lastError;
|
||||||
FormulaResult? _lastValue;
|
FormulaResult? _lastValue;
|
||||||
|
|
||||||
D4rtEditingController({String? text}) : super(text: text);
|
D4rtEditingController({super.text});
|
||||||
|
|
||||||
bool validate() {
|
bool validate() {
|
||||||
try {
|
try {
|
||||||
|
|
@ -44,9 +40,8 @@ class D4rtEditingController extends TextEditingController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get d4rtValue => _lastValue;
|
FormulaResult? get d4rtValue => _lastValue;
|
||||||
|
|
||||||
@override
|
|
||||||
set text(String newText) {
|
set text(String newText) {
|
||||||
super.text = newText;
|
super.text = newText;
|
||||||
validate();
|
validate();
|
||||||
|
|
@ -97,38 +92,51 @@ class _FormulaScreenState extends State<FormulaScreen> {
|
||||||
final inputValues = <String, dynamic>{};
|
final inputValues = <String, dynamic>{};
|
||||||
for (final input in widget.formula.input) {
|
for (final input in widget.formula.input) {
|
||||||
final controller = _inputControllers[input.name]!;
|
final controller = _inputControllers[input.name]!;
|
||||||
if( controller.d4rtValue == null ){
|
if (controller.d4rtValue == null) {
|
||||||
throw FormulaEvaluationException( "Field ${input.name} is invalid" );
|
//throw FormulaEvaluationException("Field ${input.name} is invalid");
|
||||||
|
_result = "";
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
final value = controller.d4rtValue.value;
|
|
||||||
|
|
||||||
// Convert input to base unit if needed
|
late final dynamic convertedValue;
|
||||||
// Always convert from dropdown unit to variable's base unit
|
|
||||||
late final convertedValue;
|
switch (controller.d4rtValue) {
|
||||||
if( value is Number ) {
|
case NumberResult nr:
|
||||||
convertedValue = widget.corpus.convert(
|
// Convert input to base unit if needed
|
||||||
value,
|
// Always convert from dropdown unit to variable's base unit
|
||||||
_selectedUnits[input.name]!,
|
if (input.unit != null) {
|
||||||
input.unit,
|
convertedValue = widget.corpus.convert(
|
||||||
);
|
nr.value,
|
||||||
}
|
_selectedUnits[input.name]!,
|
||||||
else{
|
input.unit as String,
|
||||||
convertedValue = value;
|
);
|
||||||
|
} else {
|
||||||
|
convertedValue = nr.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
case StringResult sr:
|
||||||
|
convertedValue = sr.value;
|
||||||
|
default:
|
||||||
|
throw FormulaEvaluationException(
|
||||||
|
"Field ${input.name} has unsupported type ${controller.d4rtValue!.runtimeType}",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
inputValues[input.name] = convertedValue;
|
inputValues[input.name] = convertedValue;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final evaluator = FormulaEvaluator();
|
final evaluator = FormulaEvaluator();
|
||||||
final result = evaluator.evaluate(widget.formula, inputValues);
|
final result = evaluator.evaluate(widget.formula, inputValues);
|
||||||
|
|
||||||
// Convert output to selected unit if needed
|
// Convert output to selected unit if needed
|
||||||
_result = widget.corpus.convert(
|
String? unit = widget.formula.output.unit;
|
||||||
result,
|
if (unit != null) {
|
||||||
widget.formula.output.unit,
|
_result = widget.corpus
|
||||||
_selectedOutputUnit!,
|
.convert(result, unit, _selectedOutputUnit!)
|
||||||
).toStringAsFixed(2);
|
.toStringAsFixed(2);
|
||||||
|
} else {
|
||||||
|
_result = result;
|
||||||
|
}
|
||||||
|
|
||||||
//print( "_evaluateFormula: result:${result} _result:${_result}");
|
//print( "_evaluateFormula: result:${result} _result:${_result}");
|
||||||
|
|
||||||
|
|
@ -136,7 +144,7 @@ class _FormulaScreenState extends State<FormulaScreen> {
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
debugPrint('Formula evaluation error: $e');
|
debugPrint('Formula evaluation error: $e');
|
||||||
debugPrint('Stack trace: $stack');
|
debugPrint('Stack trace: $stack');
|
||||||
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text('Error: ${e.toString()}\n${stack.toString()}'),
|
content: Text('Error: ${e.toString()}\n${stack.toString()}'),
|
||||||
|
|
@ -149,9 +157,7 @@ class _FormulaScreenState extends State<FormulaScreen> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(title: Text(widget.formula.name)),
|
||||||
title: Text(widget.formula.name),
|
|
||||||
),
|
|
||||||
body: Form(
|
body: Form(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|
@ -170,7 +176,7 @@ class _FormulaScreenState extends State<FormulaScreen> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDescriptionSection() {
|
Widget _buildDescriptionSection() {
|
||||||
if (widget.formula.description == null ||
|
if (widget.formula.description == null ||
|
||||||
widget.formula.description!.isEmpty) {
|
widget.formula.description!.isEmpty) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
@ -180,9 +186,9 @@ class _FormulaScreenState extends State<FormulaScreen> {
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Description',
|
'Description',
|
||||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
style: Theme.of(
|
||||||
fontWeight: FontWeight.bold,
|
context,
|
||||||
),
|
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Container(
|
Container(
|
||||||
|
|
@ -207,9 +213,9 @@ class _FormulaScreenState extends State<FormulaScreen> {
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Input Variables',
|
'Input Variables',
|
||||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
style: Theme.of(
|
||||||
fontWeight: FontWeight.bold,
|
context,
|
||||||
),
|
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
...widget.formula.input.map((variable) => _buildVariableRow(variable)),
|
...widget.formula.input.map((variable) => _buildVariableRow(variable)),
|
||||||
|
|
@ -223,9 +229,9 @@ class _FormulaScreenState extends State<FormulaScreen> {
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Result',
|
'Result',
|
||||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
style: Theme.of(
|
||||||
fontWeight: FontWeight.bold,
|
context,
|
||||||
),
|
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Row(
|
Row(
|
||||||
|
|
@ -280,6 +286,7 @@ class _FormulaScreenState extends State<FormulaScreen> {
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
border: UnderlineInputBorder(),
|
border: UnderlineInputBorder(),
|
||||||
),
|
),
|
||||||
|
autovalidateMode: AutovalidateMode.always,
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
return 'Required';
|
return 'Required';
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ class Corpus{
|
||||||
|
|
||||||
if( checkUnits ){
|
if( checkUnits ){
|
||||||
for( final inputVar in formula.input + [formula.output] ){
|
for( final inputVar in formula.input + [formula.output] ){
|
||||||
if( !_allUnits.containsKey(inputVar.unit) ){
|
if( inputVar.unit != null && !_allUnits.containsKey(inputVar.unit) ){
|
||||||
throw ArgumentError( "Unit not found: ${inputVar.unit}");
|
throw ArgumentError( "Unit not found: ${inputVar.unit}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -58,6 +58,10 @@ class Corpus{
|
||||||
return _allFormulas.values.toList(growable:false);
|
return _allFormulas.values.toList(growable:false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Formula? getFormula(String name) {
|
||||||
|
return _allFormulas.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
final Multimap<String, String> _baseToUnits = Multimap.create();
|
final Multimap<String, String> _baseToUnits = Multimap.create();
|
||||||
final Map<String, UnitSpec> _allUnits = {};
|
final Map<String, UnitSpec> _allUnits = {};
|
||||||
|
|
||||||
|
|
@ -71,7 +75,10 @@ class Corpus{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> unitsOfSameMagnitude(String unit){
|
List<String> unitsOfSameMagnitude(String? unit){
|
||||||
|
if( unit == null ){
|
||||||
|
return ["scalar"];
|
||||||
|
}
|
||||||
final base = getUnit(unit).baseUnit;
|
final base = getUnit(unit).baseUnit;
|
||||||
return _baseToUnits[base] as List<String>;
|
return _baseToUnits[base] as List<String>;
|
||||||
}
|
}
|
||||||
|
|
@ -87,7 +94,7 @@ class Corpus{
|
||||||
|
|
||||||
String _converterFromCodeStringAsExpression(Number x, String codeString) {
|
String _converterFromCodeStringAsExpression(Number x, String codeString) {
|
||||||
final buffer = StringBuffer();
|
final buffer = StringBuffer();
|
||||||
buffer.writeln("final x = ${x};");
|
buffer.writeln("final x = $x;");
|
||||||
buffer.writeln("main(){return $codeString;}");
|
buffer.writeln("main(){return $codeString;}");
|
||||||
final code = buffer.toString();
|
final code = buffer.toString();
|
||||||
return code;
|
return code;
|
||||||
|
|
@ -95,7 +102,7 @@ class Corpus{
|
||||||
|
|
||||||
String _converterFromCodeStringAsStatement(Number x, String codeString) {
|
String _converterFromCodeStringAsStatement(Number x, String codeString) {
|
||||||
final buffer = StringBuffer();
|
final buffer = StringBuffer();
|
||||||
buffer.writeln("final x = ${x};");
|
buffer.writeln("final x = $x;");
|
||||||
buffer.writeln("main(){ $codeString; return x; }");
|
buffer.writeln("main(){ $codeString; return x; }");
|
||||||
final code = buffer.toString();
|
final code = buffer.toString();
|
||||||
return code;
|
return code;
|
||||||
|
|
@ -113,7 +120,7 @@ class Corpus{
|
||||||
}
|
}
|
||||||
|
|
||||||
final ret = _convertUsingCode(x, unit.codeFromUnitToBase as String);
|
final ret = _convertUsingCode(x, unit.codeFromUnitToBase as String);
|
||||||
return ret as Number;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
Number _convertFromBase(Number x, String toUnit) {
|
Number _convertFromBase(Number x, String toUnit) {
|
||||||
|
|
@ -128,7 +135,7 @@ class Corpus{
|
||||||
}
|
}
|
||||||
|
|
||||||
final ret = _convertUsingCode(x, unit.codeFromBaseToUnit as String);
|
final ret = _convertUsingCode(x, unit.codeFromBaseToUnit as String);
|
||||||
return ret as Number;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -140,17 +147,19 @@ class Corpus{
|
||||||
final ret = _evaluate(completeSourceExpression);
|
final ret = _evaluate(completeSourceExpression);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
catch(e1,stack){
|
catch(e1, stack1){
|
||||||
try{
|
try{
|
||||||
completeSourceStatement = _converterFromCodeStringAsStatement(x, code);
|
completeSourceStatement = _converterFromCodeStringAsStatement(x, code);
|
||||||
final ret = _evaluate(completeSourceStatement);
|
final ret = _evaluate(completeSourceStatement);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
catch( e2, stack ){
|
catch( e2, stack2 ){
|
||||||
print(completeSourceExpression);
|
print(completeSourceExpression);
|
||||||
print(e1);
|
print(e1);
|
||||||
|
print(stack1);
|
||||||
print(completeSourceStatement);
|
print(completeSourceStatement);
|
||||||
print(e2);
|
print(e2);
|
||||||
|
print(stack2);
|
||||||
throw FormulaEvaluationException( "Evaluation as statement and expression failed" );
|
throw FormulaEvaluationException( "Evaluation as statement and expression failed" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,48 @@
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:convert' show utf8;
|
import 'dart:convert' show utf8;
|
||||||
|
import 'package:flutter/services.dart' show rootBundle;
|
||||||
|
|
||||||
import 'package:resource_portable/resource_portable.dart' show Resource;
|
import 'package:resource_portable/resource_portable.dart' show Resource;
|
||||||
|
|
||||||
import '../corpus.dart';
|
import '../corpus.dart';
|
||||||
import '../formula_models.dart';
|
import '../formula_models.dart';
|
||||||
|
|
||||||
|
|
||||||
Future<Corpus> createDefaultCorpus() async{
|
Future<Corpus> createDefaultCorpus() async{
|
||||||
final corpus = Corpus();
|
final corpus = Corpus();
|
||||||
|
|
||||||
|
Future<String> loadResourceAsString(String path) async {
|
||||||
|
return await rootBundle.loadString(path, cache: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Future<void> loadUnits() async {
|
Future<void> loadUnits() async {
|
||||||
final unitResources = [
|
final unitResources = [
|
||||||
"lib/defaults/units/angle.d4rt.units",
|
"assets/units/angle.d4rt.units",
|
||||||
"lib/defaults/units/area.d4rt.units",
|
"assets/units/area.d4rt.units",
|
||||||
"lib/defaults/units/distance.d4rt.units",
|
"assets/units/distance.d4rt.units",
|
||||||
"lib/defaults/units/energy.d4rt.units",
|
"assets/units/energy.d4rt.units",
|
||||||
"lib/defaults/units/force.d4rt.units",
|
"assets/units/force.d4rt.units",
|
||||||
"lib/defaults/units/mass.d4rt.units",
|
"assets/units/mass.d4rt.units",
|
||||||
"lib/defaults/units/pressure.d4rt.units",
|
"assets/units/pressure.d4rt.units",
|
||||||
"lib/defaults/units/temperature.d4rt.units",
|
"assets/units/scalar.d4rt.units",
|
||||||
"lib/defaults/units/time.d4rt.units",
|
"assets/units/temperature.d4rt.units",
|
||||||
"lib/defaults/units/velocity.d4rt.units",
|
"assets/units/time.d4rt.units",
|
||||||
|
"assets/units/velocity.d4rt.units",
|
||||||
];
|
];
|
||||||
|
|
||||||
for (final unitRes in unitResources) {
|
for (final unitRes in unitResources) {
|
||||||
final resource = Resource(unitRes);
|
final literal = await loadResourceAsString(unitRes);
|
||||||
final literal = await resource.readAsString(encoding: utf8);
|
|
||||||
final units = UnitSpec.fromArrayStringLiteral(literal);
|
final units = UnitSpec.fromArrayStringLiteral(literal);
|
||||||
corpus.loadUnits(units);
|
corpus.loadUnits(units);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> loadFormulas() async {
|
Future<void> loadFormulas() async {
|
||||||
final formulaResources = ["lib/defaults/formulas.d4rt"];
|
final formulaResources = ["assets/formulas/formulas.d4rt"];
|
||||||
|
|
||||||
for (final formRes in formulaResources) {
|
for (final formRes in formulaResources) {
|
||||||
final resource = Resource(formRes);
|
final literal = await loadResourceAsString(formRes);
|
||||||
final literal = await resource.readAsString(encoding: utf8);
|
|
||||||
final formulas = Formula.fromArrayStringLiteral(literal);
|
final formulas = Formula.fromArrayStringLiteral(literal);
|
||||||
corpus.loadFormulas(formulas);
|
corpus.loadFormulas(formulas);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,14 +76,14 @@ class FormulaEvaluator {
|
||||||
final d4rtInterpreter = interpreter ?? createDefaultInterpreter();
|
final d4rtInterpreter = interpreter ?? createDefaultInterpreter();
|
||||||
prepareInterpreter(d4rtInterpreter);
|
prepareInterpreter(d4rtInterpreter);
|
||||||
final d4rtCode = """
|
final d4rtCode = """
|
||||||
${d4rtImports}
|
$d4rtImports
|
||||||
main()
|
main()
|
||||||
{
|
{
|
||||||
late var result;
|
late var result;
|
||||||
result = $code;
|
result = $code;
|
||||||
return result;
|
return result;
|
||||||
}""";
|
}""";
|
||||||
//print("evaluateExpression:\n$d4rtCode");
|
print("evaluateExpression:\n$d4rtCode");
|
||||||
final result = d4rtInterpreter.execute(source: d4rtCode);
|
final result = d4rtInterpreter.execute(source: d4rtCode);
|
||||||
switch ( result ){
|
switch ( result ){
|
||||||
case int value:
|
case int value:
|
||||||
|
|
@ -100,8 +100,17 @@ class FormulaEvaluator {
|
||||||
dynamic evaluate(Formula formula, Map<String, dynamic> inputValues) {
|
dynamic evaluate(Formula formula, Map<String, dynamic> inputValues) {
|
||||||
_validateInputValues(formula, inputValues);
|
_validateInputValues(formula, inputValues);
|
||||||
final completeSource = _buildCompleteSource(formula, inputValues);
|
final completeSource = _buildCompleteSource(formula, inputValues);
|
||||||
final result = _interpreter.execute(source: completeSource);
|
try {
|
||||||
return result;
|
final result = _interpreter.execute(source: completeSource);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
print( "Error evaluating formula source:\n$completeSource" );
|
||||||
|
throw FormulaEvaluationException(
|
||||||
|
'Error evaluating formula "${formula.name}": $e',
|
||||||
|
e,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _validateInputValues(Formula formula, Map<String, dynamic> inputValues) {
|
void _validateInputValues(Formula formula, Map<String, dynamic> inputValues) {
|
||||||
|
|
@ -121,13 +130,7 @@ class FormulaEvaluator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String getOutputVariableName(Formula formula) {
|
|
||||||
return formula.output.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getOutputVariableMagnitude(Formula formula) {
|
|
||||||
return formula.output.unit;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> getInputVariableOrder(Formula formula) {
|
List<String> getInputVariableOrder(Formula formula) {
|
||||||
return formula.inputVarNames()..sort();
|
return formula.inputVarNames()..sort();
|
||||||
|
|
@ -166,9 +169,9 @@ class FormulaEvaluator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buffer.writeln("""
|
buffer.writeln("""
|
||||||
late var ${getOutputVariableName(formula)};
|
late var ${formula.output.name};
|
||||||
${formula.d4rtCode}
|
${formula.d4rtCode}
|
||||||
return ${getOutputVariableName(formula)};
|
return ${formula.output.name};
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -101,12 +101,18 @@ class UnitSpec {
|
||||||
|
|
||||||
class VariableSpec {
|
class VariableSpec {
|
||||||
final String name;
|
final String name;
|
||||||
final String unit;
|
final String? unit;
|
||||||
|
final List<dynamic>? values;
|
||||||
|
|
||||||
VariableSpec({required this.name, required this.unit});
|
VariableSpec({required this.name, this.unit, this.values}){
|
||||||
|
final valuesValid = values != null && values?.isNotEmpty == true;
|
||||||
|
if( unit == null && !valuesValid ){
|
||||||
|
throw ArgumentError("$name: at least unit or allowedValues should be valid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'var($name: $unit)';
|
String toString() => 'var($name: $unit${values != null ? ' allowed: $values' : ''})';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
|
|
@ -114,10 +120,11 @@ class VariableSpec {
|
||||||
other is VariableSpec &&
|
other is VariableSpec &&
|
||||||
runtimeType == other.runtimeType &&
|
runtimeType == other.runtimeType &&
|
||||||
unit == other.unit &&
|
unit == other.unit &&
|
||||||
name == other.name;
|
name == other.name &&
|
||||||
|
const DeepCollectionEquality().equals(values, other.values);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(unit, name);
|
int get hashCode => Object.hash(unit, name, values != null ? const DeepCollectionEquality().hash(values!) : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Formula {
|
class Formula {
|
||||||
|
|
@ -139,7 +146,7 @@ class Formula {
|
||||||
validate();
|
validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
validate() {
|
void validate() {
|
||||||
if (name.trim().isEmpty) {
|
if (name.trim().isEmpty) {
|
||||||
throw ArgumentError('Formula name cannot be empty');
|
throw ArgumentError('Formula name cannot be empty');
|
||||||
}
|
}
|
||||||
|
|
@ -196,8 +203,25 @@ class Formula {
|
||||||
factory Formula.fromSet(Map<Object?, Object?> theSet) {
|
factory Formula.fromSet(Map<Object?, Object?> theSet) {
|
||||||
VariableSpec parseVar(Map<Object?, Object?> varSpec) {
|
VariableSpec parseVar(Map<Object?, Object?> varSpec) {
|
||||||
String name = SetUtils.stringValue(varSpec, "name");
|
String name = SetUtils.stringValue(varSpec, "name");
|
||||||
String unit = SetUtils.stringValue(varSpec, "unit");
|
String? unit;
|
||||||
return VariableSpec(name: name, unit: unit);
|
if (varSpec.containsKey("unit")) {
|
||||||
|
unit = SetUtils.stringValue(varSpec, "unit");
|
||||||
|
}
|
||||||
|
final allowed = varSpec['values'] as List<dynamic>?;
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
if (!types.contains(String) && !types.contains(double) && !types.contains(int)) {
|
||||||
|
throw ArgumentError('Allowed values must be Strings or Numbers');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return VariableSpec(
|
||||||
|
name: name,
|
||||||
|
unit: unit,
|
||||||
|
values: allowed?.toList(growable: false),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String name = SetUtils.stringValue(theSet, "name");
|
String name = SetUtils.stringValue(theSet, "name");
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import 'corpus.dart';
|
||||||
import 'defaults/default_corpus.dart';
|
import 'defaults/default_corpus.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
runApp(MaterialApp(
|
runApp(MaterialApp(
|
||||||
home: FutureBuilder<Corpus>(
|
home: FutureBuilder<Corpus>(
|
||||||
future: createDefaultCorpus(),
|
future: createDefaultCorpus(),
|
||||||
|
|
|
||||||
52
pubspec.lock
52
pubspec.lock
|
|
@ -109,10 +109,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: crypto
|
name: crypto
|
||||||
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
|
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.6"
|
version: "3.0.7"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -125,18 +125,18 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: d4rt
|
name: d4rt
|
||||||
sha256: "1eb626145e2ed97332f9e6e842e626f973d3969ce30e2794efb4744bd8aeba63"
|
sha256: eff6a10f31e9e5b60b99146a33204c5f2d74e20ac3eeb14132d8a8ed0921c6e1
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.7"
|
version: "0.1.9"
|
||||||
equatable:
|
equatable:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: equatable
|
name: equatable
|
||||||
sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7"
|
sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.7"
|
version: "2.0.8"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -244,10 +244,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007
|
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.0"
|
version: "1.6.0"
|
||||||
http_multi_server:
|
http_multi_server:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -356,10 +356,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.16.0"
|
version: "1.17.0"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -585,34 +585,34 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_android
|
name: url_launcher_android
|
||||||
sha256: "5c8b6c2d89a78f5a1cca70a73d9d5f86c701b36b42f9c9dac7bad592113c28e9"
|
sha256: "767344bf3063897b5cf0db830e94f904528e6dd50a6dfaf839f0abf509009611"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.3.24"
|
version: "6.3.28"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_ios
|
name: url_launcher_ios
|
||||||
sha256: d80b3f567a617cb923546034cc94bfe44eb15f989fe670b37f26abdb9d939cb7
|
sha256: cfde38aa257dae62ffe79c87fab20165dfdf6988c1d31b58ebf59b9106062aad
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.3.4"
|
version: "6.3.6"
|
||||||
url_launcher_linux:
|
url_launcher_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_linux
|
name: url_launcher_linux
|
||||||
sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935"
|
sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.1"
|
version: "3.2.2"
|
||||||
url_launcher_macos:
|
url_launcher_macos:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_macos
|
name: url_launcher_macos
|
||||||
sha256: c043a77d6600ac9c38300567f33ef12b0ef4f4783a2c1f00231d2b1941fea13f
|
sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.3"
|
version: "3.2.5"
|
||||||
url_launcher_platform_interface:
|
url_launcher_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -625,18 +625,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_web
|
name: url_launcher_web
|
||||||
sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2"
|
sha256: d0412fcf4c6b31ecfdb7762359b7206ffba3bbffd396c6d9f9c4616ece476c1f
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.1"
|
version: "2.4.2"
|
||||||
url_launcher_windows:
|
url_launcher_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_windows
|
name: url_launcher_windows
|
||||||
sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77"
|
sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.4"
|
version: "3.1.5"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -657,10 +657,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: watcher
|
name: watcher
|
||||||
sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a"
|
sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.4"
|
version: "1.2.1"
|
||||||
web:
|
web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -702,5 +702,5 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.9.0 <4.0.0"
|
dart: ">=3.10.7 <4.0.0"
|
||||||
flutter: ">=3.35.0"
|
flutter: ">=3.38.0"
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
version: 1.0.0+1
|
version: 1.0.0+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.8.1
|
sdk: ^3.10.7
|
||||||
|
|
||||||
# Dependencies specify other packages that your package needs in order to work.
|
# Dependencies specify other packages that your package needs in order to work.
|
||||||
# To automatically upgrade your package dependencies to the latest versions
|
# To automatically upgrade your package dependencies to the latest versions
|
||||||
|
|
@ -50,8 +50,8 @@ dev_dependencies:
|
||||||
# activated in the `analysis_options.yaml` file located at the root of your
|
# activated in the `analysis_options.yaml` file located at the root of your
|
||||||
# package. See that file for information about deactivating specific lint
|
# package. See that file for information about deactivating specific lint
|
||||||
# rules and activating additional ones.
|
# rules and activating additional ones.
|
||||||
flutter_lints: ^6.0.0
|
flutter_lints:
|
||||||
test: ^1.26.3
|
test:
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
@ -68,6 +68,9 @@ flutter:
|
||||||
# assets:
|
# assets:
|
||||||
# - images/a_dot_burr.jpeg
|
# - images/a_dot_burr.jpeg
|
||||||
# - images/a_dot_ham.jpeg
|
# - images/a_dot_ham.jpeg
|
||||||
|
assets:
|
||||||
|
- assets/units/
|
||||||
|
- assets/formulas/
|
||||||
|
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
# https://flutter.dev/to/resolution-aware-images
|
# https://flutter.dev/to/resolution-aware-images
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import 'package:d4rt/d4rt.dart';
|
||||||
import 'dart:math' as Math;
|
import 'dart:math' as Math;
|
||||||
|
|
||||||
|
|
||||||
main(){
|
void main(){
|
||||||
test('Access to Math', () {
|
test('Access to Math', () {
|
||||||
|
|
||||||
final completeSource = """
|
final completeSource = """
|
||||||
|
|
@ -29,6 +29,7 @@ main(){
|
||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
final interpreter = D4rt();
|
final interpreter = D4rt();
|
||||||
|
interpreter.grant(FilesystemPermission.readPath("/etc/passwd"));
|
||||||
final result = interpreter.execute(source: completeSource);
|
final result = interpreter.execute(source: completeSource);
|
||||||
|
|
||||||
expect(result, contains("root"));
|
expect(result, contains("root"));
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import 'package:d4rt/d4rt.dart';
|
||||||
import 'dart:math' as Math;
|
import 'dart:math' as Math;
|
||||||
|
|
||||||
|
|
||||||
main(){
|
void main(){
|
||||||
test('for dart grammar tests', () {
|
test('for dart grammar tests', () {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,7 @@ void main() {
|
||||||
d4rtCode: 'force = x;',
|
d4rtCode: 'force = x;',
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(evaluator.getOutputVariableName(formula), 'force');
|
expect(formula.output.name, 'force');
|
||||||
});
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
|
|
@ -165,7 +165,7 @@ void main() {
|
||||||
d4rtCode: 'force = x;',
|
d4rtCode: 'force = x;',
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(evaluator.getOutputVariableMagnitude(formula), 'Newton');
|
expect(formula.output.unit, 'Newton');
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -177,8 +177,8 @@ void main() {
|
||||||
d4rtCode: 'result = x;',
|
d4rtCode: 'result = x;',
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(evaluator.getOutputVariableName(validFormula), 'result');
|
expect(validFormula.output.name, 'result');
|
||||||
expect(evaluator.getOutputVariableMagnitude(validFormula), 'Newton');
|
expect(validFormula.output.unit, 'Newton');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,21 @@
|
||||||
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:test/test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:d4rt_formulas/formula_models.dart';
|
import 'package:d4rt_formulas/formula_models.dart';
|
||||||
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
Future<Corpus> createTestCorpus() async {
|
Future<Corpus> createTestCorpus() async {
|
||||||
return createDefaultCorpus();
|
return createDefaultCorpus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Corpus> testCorpus = createTestCorpus();
|
||||||
|
|
||||||
|
|
||||||
test("Parses unit", () {
|
test("Parses unit", () {
|
||||||
final setLiteral = {"name": "kilometer", "symbol": "km", "baseUnit": "meter", "factor": 1000};
|
final setLiteral = {"name": "kilometer", "symbol": "km", "baseUnit": "meter", "factor": 1000};
|
||||||
final unit = UnitSpec.fromSet(setLiteral);
|
final unit = UnitSpec.fromSet(setLiteral);
|
||||||
|
|
@ -23,37 +28,37 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("From km to in", () async {
|
test("From km to in", () async {
|
||||||
final corpus = await createTestCorpus();
|
final corpus = await testCorpus;
|
||||||
final inches = corpus.convert(1, "kilometer", "inch");
|
final inches = corpus.convert(1, "kilometer", "inch");
|
||||||
expect( inches, closeTo(39370.078,0.001) );
|
expect( inches, closeTo(39370.078,0.001) );
|
||||||
});
|
});
|
||||||
|
|
||||||
test("From furlong to base", () async {
|
test("From furlong to base", () async {
|
||||||
final corpus = await createTestCorpus();
|
final corpus = await testCorpus;
|
||||||
final m = corpus.convert(1, "furlong", "meter");
|
final m = corpus.convert(1, "furlong", "meter");
|
||||||
expect(m,closeTo(201.168,0.001));
|
expect(m,closeTo(201.168,0.001));
|
||||||
});
|
});
|
||||||
|
|
||||||
test("From base to furlong", () async {
|
test("From base to furlong", () async {
|
||||||
final corpus = await createTestCorpus();
|
final corpus = await testCorpus;
|
||||||
final m = corpus.convert(201.168, "meter", "furlong");
|
final m = corpus.convert(201.168, "meter", "furlong");
|
||||||
expect(m,closeTo(1,0.001));
|
expect(m,closeTo(1,0.001));
|
||||||
});
|
});
|
||||||
|
|
||||||
test("From C to F", () async {
|
test("From C to F", () async {
|
||||||
final corpus = await createTestCorpus();
|
final corpus = await testCorpus;
|
||||||
final m = corpus.convert(37, "Celsius", "Fahrenheit");
|
final m = corpus.convert(37, "Celsius", "Fahrenheit");
|
||||||
expect(m,closeTo(98.6,0.001));
|
expect(m,closeTo(98.6,0.001));
|
||||||
});
|
});
|
||||||
|
|
||||||
test("From K to F", () async {
|
test("From K to F", () async {
|
||||||
final corpus = await createTestCorpus();
|
final corpus = await testCorpus;
|
||||||
final m = corpus.convert(37, "Kelvin", "Fahrenheit");
|
final m = corpus.convert(37, "Kelvin", "Fahrenheit");
|
||||||
expect(m,closeTo(-393.07,0.001));
|
expect(m,closeTo(-393.07,0.001));
|
||||||
});
|
});
|
||||||
|
|
||||||
test("From C to K", () async {
|
test("From C to K", () async {
|
||||||
final corpus = await createTestCorpus();
|
final corpus = await testCorpus;
|
||||||
final m = corpus.convert(100, "Celsius", "Kelvin");
|
final m = corpus.convert(100, "Celsius", "Kelvin");
|
||||||
expect(m,closeTo(373.15,0.001));
|
expect(m,closeTo(373.15,0.001));
|
||||||
});
|
});
|
||||||
|
|
@ -108,5 +113,22 @@ void main() {
|
||||||
expect(result, 98.0); // F = m * a = 10 * 9.8 = 98 N
|
expect(result, 98.0); // F = m * a = 10 * 9.8 = 98 N
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('APGAR Score', () {
|
||||||
|
test('evaluates APGAR score formula - Normal case', () async {
|
||||||
|
final corpus = await testCorpus;
|
||||||
|
final formula = corpus.getFormula("Apgar Score")!;
|
||||||
|
final evaluator = FormulaEvaluator();
|
||||||
|
|
||||||
|
final result = evaluator.evaluate(formula, {
|
||||||
|
'HeartRate': 2,
|
||||||
|
'Breathing': 2,
|
||||||
|
'MuscleTone': 2,
|
||||||
|
'Reflexes': 2,
|
||||||
|
'SkinColor': 2
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result, 'Score: 10 - Normal');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
firejail --profile=firejail-warp-terminal.profile warp-terminal
|
|
||||||
Loading…
Reference in a new issue