Kenneth Bruen
2 years ago
10 changed files with 944 additions and 44 deletions
@ -0,0 +1,72 @@ |
|||||||
|
import 'dart:async'; |
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart'; |
||||||
|
import 'package:flutter/material.dart'; |
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart'; |
||||||
|
import 'package:logic_circuits_simulator/utils/logic_expressions.dart'; |
||||||
|
import 'package:logic_circuits_simulator/utils/logic_operators.dart'; |
||||||
|
|
||||||
|
class LogicExpressionField extends HookWidget { |
||||||
|
final ValueListenable<List<String>> inputsListener; |
||||||
|
final String outputName; |
||||||
|
final String? initialText; |
||||||
|
final void Function(String input, LogicExpression expression)? onChanged; |
||||||
|
final void Function()? onInputError; |
||||||
|
|
||||||
|
const LogicExpressionField({required this.inputsListener, required this.outputName, this.initialText, this.onChanged, this.onInputError, super.key}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
final inputs = useValueListenable(inputsListener); |
||||||
|
final controller = useTextEditingController(text: initialText); |
||||||
|
final errorText = useState<String?>(null); |
||||||
|
useValueListenable(controller); |
||||||
|
|
||||||
|
final onChg = useMemoized(() => (String newValue) { |
||||||
|
final trimmed = newValue.trim(); |
||||||
|
|
||||||
|
try { |
||||||
|
if (trimmed.isEmpty) { |
||||||
|
onChanged?.call('', LogicExpression(operator: FalseLogicOperator(), arguments: [])); |
||||||
|
} |
||||||
|
else { |
||||||
|
final newLogicExpression = LogicExpression.parse(trimmed); |
||||||
|
|
||||||
|
// Check if unknown inputs are used |
||||||
|
final newInputs = newLogicExpression.inputs; |
||||||
|
final unknownInputs = newInputs.where((input) => !inputs.contains(input)).toList(); |
||||||
|
if (unknownInputs.isNotEmpty) { |
||||||
|
throw Exception('Unknown inputs found: ${unknownInputs.join(", ")}'); |
||||||
|
} |
||||||
|
|
||||||
|
onChanged?.call(trimmed, newLogicExpression); |
||||||
|
} |
||||||
|
errorText.value = null; |
||||||
|
} catch (e) { |
||||||
|
errorText.value = e.toString(); |
||||||
|
onInputError?.call(); |
||||||
|
} |
||||||
|
}, [inputs, errorText]); |
||||||
|
useEffect( |
||||||
|
() { |
||||||
|
if (controller.text.isNotEmpty) { |
||||||
|
scheduleMicrotask(() { |
||||||
|
onChg(controller.text); |
||||||
|
}); |
||||||
|
} |
||||||
|
return null; |
||||||
|
}, |
||||||
|
[inputs], |
||||||
|
); |
||||||
|
|
||||||
|
return TextField( |
||||||
|
controller: controller, |
||||||
|
onChanged: onChg, |
||||||
|
decoration: InputDecoration( |
||||||
|
border: const OutlineInputBorder(), |
||||||
|
labelText: 'Logic Experssion for $outputName', |
||||||
|
errorText: errorText.value, |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,286 @@ |
|||||||
|
import 'dart:math'; |
||||||
|
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart'; |
||||||
|
import 'package:logic_circuits_simulator/utils/iterable_extension.dart'; |
||||||
|
import 'package:logic_circuits_simulator/utils/logic_operators.dart'; |
||||||
|
|
||||||
|
part 'logic_expressions.freezed.dart'; |
||||||
|
|
||||||
|
@freezed |
||||||
|
class LogicExpression with _$LogicExpression { |
||||||
|
const LogicExpression._(); |
||||||
|
|
||||||
|
const factory LogicExpression({ |
||||||
|
required LogicOperator operator, |
||||||
|
required List<dynamic> arguments, |
||||||
|
}) = _LogicExpression; |
||||||
|
|
||||||
|
factory LogicExpression.ofZeroOp(ZeroOpLogicOperator operator) => LogicExpression(operator: operator, arguments: []); |
||||||
|
|
||||||
|
static dynamic _classify(String token) { |
||||||
|
final operators = [ |
||||||
|
FalseLogicOperator(), |
||||||
|
TrueLogicOperator(), |
||||||
|
NotLogicOperator(), |
||||||
|
AndLogicOperator(), |
||||||
|
OrLogicOperator(), |
||||||
|
XorLogicOperator(), |
||||||
|
NandLogicOperator(), |
||||||
|
NorLogicOperator(), |
||||||
|
XnorLogicOperator(), |
||||||
|
]; |
||||||
|
|
||||||
|
for (final op in operators) { |
||||||
|
if (op.representations.contains(token)) { |
||||||
|
return op; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
final inputStart = RegExp('^[A-Za-z]'); |
||||||
|
if (inputStart.hasMatch(token)) { |
||||||
|
return token; |
||||||
|
} |
||||||
|
|
||||||
|
throw Exception('Unknown operator: $token'); |
||||||
|
} |
||||||
|
|
||||||
|
static List<dynamic> _tokenize(String input) { |
||||||
|
final space = ' '.codeUnits[0]; |
||||||
|
final openedParen = '('.codeUnits[0]; |
||||||
|
final closedParen = ')'.codeUnits[0]; |
||||||
|
final transitionToOperator = RegExp('[^A-Za-z0-9]'); |
||||||
|
final transitionToInput = RegExp('[A-Za-z]'); |
||||||
|
|
||||||
|
List<dynamic> result = []; |
||||||
|
final buffer = StringBuffer(); |
||||||
|
bool operator = false; |
||||||
|
int parenDepth = 0; |
||||||
|
|
||||||
|
for (final rune in input.runes) { |
||||||
|
if (rune == openedParen) { |
||||||
|
if (parenDepth == 0 && buffer.isNotEmpty) { |
||||||
|
result.add(_classify(buffer.toString())); |
||||||
|
buffer.clear(); |
||||||
|
} |
||||||
|
else if (parenDepth > 0) { |
||||||
|
buffer.writeCharCode(rune); |
||||||
|
} |
||||||
|
parenDepth++; |
||||||
|
continue; |
||||||
|
} |
||||||
|
else if (rune == closedParen) { |
||||||
|
parenDepth--; |
||||||
|
if (parenDepth == 0) { |
||||||
|
result.add(_tokenize(buffer.toString())); |
||||||
|
buffer.clear(); |
||||||
|
} |
||||||
|
else if (parenDepth < 0) { |
||||||
|
throw Exception('Unmached parenthesis: too many closed parenthesis'); |
||||||
|
} |
||||||
|
else { |
||||||
|
buffer.writeCharCode(rune); |
||||||
|
} |
||||||
|
continue; |
||||||
|
} |
||||||
|
else if (parenDepth > 0) { |
||||||
|
// While inside paren, just add stuff to the buffer to be further |
||||||
|
// processed recursively and put inside of a list. |
||||||
|
// ~(~(A&(A+B))+B& ~A) |
||||||
|
// │ │ └───┘│ │ |
||||||
|
// │ └───────┘ │ |
||||||
|
// └────────────────┘ |
||||||
|
buffer.writeCharCode(rune); |
||||||
|
continue; |
||||||
|
} |
||||||
|
else if (rune == space) { |
||||||
|
if (buffer.isNotEmpty) { |
||||||
|
result.add(_classify(buffer.toString())); |
||||||
|
buffer.clear(); |
||||||
|
} |
||||||
|
} |
||||||
|
else { |
||||||
|
if (buffer.isNotEmpty) { |
||||||
|
// Check if switching from operator to input. |
||||||
|
// This allows an expression such as A&B to be valid. |
||||||
|
// Switching happens when in the middle of a token |
||||||
|
// and changing from [A-Za-z0-9] to [^A-Za-z0-9] |
||||||
|
// or from [^A-Za-z] to [A-Za-z]. |
||||||
|
// Inputs can't start with digits. |
||||||
|
if (!operator && transitionToOperator.hasMatch(String.fromCharCode(rune))) { |
||||||
|
result.add(_classify(buffer.toString())); |
||||||
|
buffer.clear(); |
||||||
|
} |
||||||
|
else if (operator && transitionToInput.hasMatch(String.fromCharCode(rune))) { |
||||||
|
result.add(_classify(buffer.toString())); |
||||||
|
buffer.clear(); |
||||||
|
} |
||||||
|
} |
||||||
|
if (buffer.isEmpty) { |
||||||
|
operator = !transitionToInput.hasMatch(String.fromCharCode(rune)); |
||||||
|
} |
||||||
|
buffer.writeCharCode(rune); |
||||||
|
} |
||||||
|
} |
||||||
|
if (parenDepth != 0) { |
||||||
|
throw Exception('Unmached parenthesis: too many open parenthesis'); |
||||||
|
} |
||||||
|
if (buffer.isNotEmpty) { |
||||||
|
result.add(_classify(buffer.toString())); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
factory LogicExpression.parse(String input) { |
||||||
|
final tokens = _tokenize(input); |
||||||
|
|
||||||
|
final result = LogicExpression._parse(tokens); |
||||||
|
if (result is String) { |
||||||
|
return LogicExpression( |
||||||
|
operator: OrLogicOperator(), |
||||||
|
arguments: [ |
||||||
|
result, |
||||||
|
LogicExpression.ofZeroOp(FalseLogicOperator()), |
||||||
|
], |
||||||
|
); |
||||||
|
} |
||||||
|
else { |
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static dynamic _parse(dynamic input) { |
||||||
|
if (input is List) { |
||||||
|
final tokens = input; |
||||||
|
|
||||||
|
final orderedOpGroups = [ |
||||||
|
[OrLogicOperator(), NorLogicOperator()], |
||||||
|
[XorLogicOperator(), XnorLogicOperator()], |
||||||
|
[AndLogicOperator(), NandLogicOperator()], |
||||||
|
[NotLogicOperator()], |
||||||
|
[FalseLogicOperator(), TrueLogicOperator()], |
||||||
|
]; |
||||||
|
|
||||||
|
for (final ops in orderedOpGroups) { |
||||||
|
for (var i = tokens.length - 1; i >= 0; i--) { |
||||||
|
if (ops.contains(tokens[i])) { |
||||||
|
if (tokens[i] is ZeroOpLogicOperator) { |
||||||
|
// ZeroOp operator should be alone |
||||||
|
if (tokens.length != 1) { |
||||||
|
throw Exception('ZeroOp operator should be alone'); |
||||||
|
} |
||||||
|
return LogicExpression.ofZeroOp(tokens[i]); |
||||||
|
} |
||||||
|
else if (tokens[i] is OneOpLogicOperator) { |
||||||
|
// OneOp operator should appear prefix only |
||||||
|
// So index should be 0 |
||||||
|
if (i != 0) { |
||||||
|
throw Exception('OneOp operator should be prefix'); |
||||||
|
} |
||||||
|
// It should only be possible to get here if there is only one argument |
||||||
|
// follows. The only other case is someone writing: |
||||||
|
// ~ A B |
||||||
|
// which would result in [NotLogicOperator, 'A', 'B']. |
||||||
|
// Such syntax is ambiguous and should not be allowed: |
||||||
|
// (~ A) & B -or- ~ (A & B) ? |
||||||
|
// This is the disadvantage of linear, left-to-right notation (as opposed |
||||||
|
// to the notation with a bar above the NOT-ed expression). |
||||||
|
if (tokens.length > 2) { |
||||||
|
throw Exception('Ambiguous expression: ${tokens[i]} followed by multiple tokens (${tokens.skip(1).toList()})'); |
||||||
|
} |
||||||
|
else if (tokens.length == 1) { |
||||||
|
throw Exception('Unfinished expression'); |
||||||
|
} |
||||||
|
return LogicExpression( |
||||||
|
operator: tokens[0], |
||||||
|
arguments: [_parse(tokens[1])], |
||||||
|
); |
||||||
|
} |
||||||
|
else if (tokens[i] is TwoOpLogicOperator) { |
||||||
|
return LogicExpression( |
||||||
|
operator: tokens[i], |
||||||
|
arguments: [ |
||||||
|
_parse(tokens.getRange(0, i).toList()), |
||||||
|
_parse(tokens.getRange(i + 1, tokens.length).toList()), |
||||||
|
], |
||||||
|
); |
||||||
|
} |
||||||
|
else { |
||||||
|
throw Exception('Matched with operator that somehow isn\'t Zero/One/TwoOp'); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// No operators were found. This means the only tokens are props. |
||||||
|
// If there is only one prop, return it alone: |
||||||
|
// A => [A] => A |
||||||
|
// If there are multiple props, apply AND: |
||||||
|
// A B C => [A, B, C] => A & B & C => (A & B) & C |
||||||
|
// Keep in mind the second case is only possible if the props are separated by spaces, |
||||||
|
// as the nature of prop names allowing multiple characters only allows multiple props |
||||||
|
// to appear one after the other if separated by spaces. |
||||||
|
if (tokens.length == 1) { |
||||||
|
return tokens[0]; |
||||||
|
} |
||||||
|
else if (tokens.isEmpty) { |
||||||
|
// This happens in unfinished expressions: |
||||||
|
// A ^ ! => XOR(A, NOT(?)) |
||||||
|
// A ^ => XOR(A, ?) |
||||||
|
throw Exception('Unfinished expression'); |
||||||
|
} |
||||||
|
else { |
||||||
|
final and = AndLogicOperator(); |
||||||
|
return _parse(tokens.expand((token) => [and, token]).skip(1).toList()); |
||||||
|
} |
||||||
|
} |
||||||
|
else if (input is String) { |
||||||
|
// Prop, just return. |
||||||
|
// Happens in such cases: |
||||||
|
// B & ~ A => & [B, ~ [A]] |
||||||
|
// ^ ^ |
||||||
|
return input; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
Set<String> get inputs { |
||||||
|
Set<String> result = {}; |
||||||
|
for (final arg in arguments) { |
||||||
|
if (arg is String) { |
||||||
|
result.add(arg); |
||||||
|
} |
||||||
|
else if (arg is LogicExpression) { |
||||||
|
result.addAll(arg.inputs); |
||||||
|
} |
||||||
|
else { |
||||||
|
throw Exception('Unknown argument type found: ${arg.runtimeType}'); |
||||||
|
} |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
bool evaluate(Map<String, bool> inputs) { |
||||||
|
return operator.apply( |
||||||
|
arguments |
||||||
|
.map( |
||||||
|
// If the argument is a logical expression, evaluate recursively |
||||||
|
// else it must be an input name, so replace based on supplied mapping. |
||||||
|
(e) => e is LogicExpression ? e.evaluate(inputs) : inputs[e]! |
||||||
|
) |
||||||
|
.toList(), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
List<String> computeTruthTable(List<String> inputs) { |
||||||
|
final ttRows = pow(2, inputs.length) as int; |
||||||
|
return List.generate( |
||||||
|
ttRows, |
||||||
|
(index) => evaluate( |
||||||
|
{ |
||||||
|
for (var element in inputs.reversed.indexedMap((index, input) => [index, input])) |
||||||
|
element[1] as String : (index & (pow(2, element[0] as int) as int)) != 0 |
||||||
|
}, |
||||||
|
) ? '1' : '0', |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,159 @@ |
|||||||
|
// coverage:ignore-file |
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND |
||||||
|
// ignore_for_file: type=lint |
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target |
||||||
|
|
||||||
|
part of 'logic_expressions.dart'; |
||||||
|
|
||||||
|
// ************************************************************************** |
||||||
|
// FreezedGenerator |
||||||
|
// ************************************************************************** |
||||||
|
|
||||||
|
T _$identity<T>(T value) => value; |
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError( |
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); |
||||||
|
|
||||||
|
/// @nodoc |
||||||
|
mixin _$LogicExpression { |
||||||
|
LogicOperator get operator => throw _privateConstructorUsedError; |
||||||
|
List<dynamic> get arguments => throw _privateConstructorUsedError; |
||||||
|
|
||||||
|
@JsonKey(ignore: true) |
||||||
|
$LogicExpressionCopyWith<LogicExpression> get copyWith => |
||||||
|
throw _privateConstructorUsedError; |
||||||
|
} |
||||||
|
|
||||||
|
/// @nodoc |
||||||
|
abstract class $LogicExpressionCopyWith<$Res> { |
||||||
|
factory $LogicExpressionCopyWith( |
||||||
|
LogicExpression value, $Res Function(LogicExpression) then) = |
||||||
|
_$LogicExpressionCopyWithImpl<$Res>; |
||||||
|
$Res call({LogicOperator operator, List<dynamic> arguments}); |
||||||
|
} |
||||||
|
|
||||||
|
/// @nodoc |
||||||
|
class _$LogicExpressionCopyWithImpl<$Res> |
||||||
|
implements $LogicExpressionCopyWith<$Res> { |
||||||
|
_$LogicExpressionCopyWithImpl(this._value, this._then); |
||||||
|
|
||||||
|
final LogicExpression _value; |
||||||
|
// ignore: unused_field |
||||||
|
final $Res Function(LogicExpression) _then; |
||||||
|
|
||||||
|
@override |
||||||
|
$Res call({ |
||||||
|
Object? operator = freezed, |
||||||
|
Object? arguments = freezed, |
||||||
|
}) { |
||||||
|
return _then(_value.copyWith( |
||||||
|
operator: operator == freezed |
||||||
|
? _value.operator |
||||||
|
: operator // ignore: cast_nullable_to_non_nullable |
||||||
|
as LogicOperator, |
||||||
|
arguments: arguments == freezed |
||||||
|
? _value.arguments |
||||||
|
: arguments // ignore: cast_nullable_to_non_nullable |
||||||
|
as List<dynamic>, |
||||||
|
)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// @nodoc |
||||||
|
abstract class _$$_LogicExpressionCopyWith<$Res> |
||||||
|
implements $LogicExpressionCopyWith<$Res> { |
||||||
|
factory _$$_LogicExpressionCopyWith( |
||||||
|
_$_LogicExpression value, $Res Function(_$_LogicExpression) then) = |
||||||
|
__$$_LogicExpressionCopyWithImpl<$Res>; |
||||||
|
@override |
||||||
|
$Res call({LogicOperator operator, List<dynamic> arguments}); |
||||||
|
} |
||||||
|
|
||||||
|
/// @nodoc |
||||||
|
class __$$_LogicExpressionCopyWithImpl<$Res> |
||||||
|
extends _$LogicExpressionCopyWithImpl<$Res> |
||||||
|
implements _$$_LogicExpressionCopyWith<$Res> { |
||||||
|
__$$_LogicExpressionCopyWithImpl( |
||||||
|
_$_LogicExpression _value, $Res Function(_$_LogicExpression) _then) |
||||||
|
: super(_value, (v) => _then(v as _$_LogicExpression)); |
||||||
|
|
||||||
|
@override |
||||||
|
_$_LogicExpression get _value => super._value as _$_LogicExpression; |
||||||
|
|
||||||
|
@override |
||||||
|
$Res call({ |
||||||
|
Object? operator = freezed, |
||||||
|
Object? arguments = freezed, |
||||||
|
}) { |
||||||
|
return _then(_$_LogicExpression( |
||||||
|
operator: operator == freezed |
||||||
|
? _value.operator |
||||||
|
: operator // ignore: cast_nullable_to_non_nullable |
||||||
|
as LogicOperator, |
||||||
|
arguments: arguments == freezed |
||||||
|
? _value._arguments |
||||||
|
: arguments // ignore: cast_nullable_to_non_nullable |
||||||
|
as List<dynamic>, |
||||||
|
)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// @nodoc |
||||||
|
|
||||||
|
class _$_LogicExpression extends _LogicExpression { |
||||||
|
const _$_LogicExpression( |
||||||
|
{required this.operator, required final List<dynamic> arguments}) |
||||||
|
: _arguments = arguments, |
||||||
|
super._(); |
||||||
|
|
||||||
|
@override |
||||||
|
final LogicOperator operator; |
||||||
|
final List<dynamic> _arguments; |
||||||
|
@override |
||||||
|
List<dynamic> get arguments { |
||||||
|
// ignore: implicit_dynamic_type |
||||||
|
return EqualUnmodifiableListView(_arguments); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
String toString() { |
||||||
|
return 'LogicExpression(operator: $operator, arguments: $arguments)'; |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
bool operator ==(dynamic other) { |
||||||
|
return identical(this, other) || |
||||||
|
(other.runtimeType == runtimeType && |
||||||
|
other is _$_LogicExpression && |
||||||
|
const DeepCollectionEquality().equals(other.operator, operator) && |
||||||
|
const DeepCollectionEquality() |
||||||
|
.equals(other._arguments, _arguments)); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
int get hashCode => Object.hash( |
||||||
|
runtimeType, |
||||||
|
const DeepCollectionEquality().hash(operator), |
||||||
|
const DeepCollectionEquality().hash(_arguments)); |
||||||
|
|
||||||
|
@JsonKey(ignore: true) |
||||||
|
@override |
||||||
|
_$$_LogicExpressionCopyWith<_$_LogicExpression> get copyWith => |
||||||
|
__$$_LogicExpressionCopyWithImpl<_$_LogicExpression>(this, _$identity); |
||||||
|
} |
||||||
|
|
||||||
|
abstract class _LogicExpression extends LogicExpression { |
||||||
|
const factory _LogicExpression( |
||||||
|
{required final LogicOperator operator, |
||||||
|
required final List<dynamic> arguments}) = _$_LogicExpression; |
||||||
|
const _LogicExpression._() : super._(); |
||||||
|
|
||||||
|
@override |
||||||
|
LogicOperator get operator => throw _privateConstructorUsedError; |
||||||
|
@override |
||||||
|
List<dynamic> get arguments => throw _privateConstructorUsedError; |
||||||
|
@override |
||||||
|
@JsonKey(ignore: true) |
||||||
|
_$$_LogicExpressionCopyWith<_$_LogicExpression> get copyWith => |
||||||
|
throw _privateConstructorUsedError; |
||||||
|
} |
@ -0,0 +1,138 @@ |
|||||||
|
abstract class LogicOperator { |
||||||
|
bool apply(List<bool> inputs); |
||||||
|
List<String> get representations; |
||||||
|
String get defaultRepresentation => representations[0]; |
||||||
|
bool fromRepresentation(String repr) => representations.contains(repr); |
||||||
|
} |
||||||
|
|
||||||
|
abstract class ZeroOpLogicOperator extends LogicOperator {} |
||||||
|
|
||||||
|
class FalseLogicOperator extends ZeroOpLogicOperator { |
||||||
|
@override |
||||||
|
bool apply(List<bool> inputs) => false; |
||||||
|
|
||||||
|
@override |
||||||
|
List<String> get representations => ['0']; |
||||||
|
|
||||||
|
@override |
||||||
|
bool operator==(other) => other is FalseLogicOperator; |
||||||
|
|
||||||
|
@override |
||||||
|
int get hashCode => defaultRepresentation.hashCode; |
||||||
|
} |
||||||
|
|
||||||
|
class TrueLogicOperator extends ZeroOpLogicOperator { |
||||||
|
@override |
||||||
|
bool apply(List<bool> inputs) => true; |
||||||
|
|
||||||
|
@override |
||||||
|
List<String> get representations => ['1']; |
||||||
|
|
||||||
|
@override |
||||||
|
bool operator==(other) => other is TrueLogicOperator; |
||||||
|
|
||||||
|
@override |
||||||
|
int get hashCode => defaultRepresentation.hashCode; |
||||||
|
} |
||||||
|
|
||||||
|
abstract class OneOpLogicOperator extends LogicOperator {} |
||||||
|
|
||||||
|
class NotLogicOperator extends OneOpLogicOperator { |
||||||
|
@override |
||||||
|
bool apply(List<bool> inputs) => !inputs[0]; |
||||||
|
|
||||||
|
@override |
||||||
|
List<String> get representations => const ['~', '!', '¬']; |
||||||
|
|
||||||
|
@override |
||||||
|
bool operator==(other) => other is NotLogicOperator; |
||||||
|
|
||||||
|
@override |
||||||
|
int get hashCode => defaultRepresentation.hashCode; |
||||||
|
} |
||||||
|
|
||||||
|
abstract class TwoOpLogicOperator extends LogicOperator {} |
||||||
|
|
||||||
|
class AndLogicOperator extends TwoOpLogicOperator { |
||||||
|
@override |
||||||
|
bool apply(List<bool> inputs) => inputs[0] && inputs[1]; |
||||||
|
|
||||||
|
@override |
||||||
|
List<String> get representations => const ['&', '∧']; |
||||||
|
|
||||||
|
@override |
||||||
|
bool operator==(other) => other is AndLogicOperator; |
||||||
|
|
||||||
|
@override |
||||||
|
int get hashCode => defaultRepresentation.hashCode; |
||||||
|
} |
||||||
|
|
||||||
|
class OrLogicOperator extends TwoOpLogicOperator { |
||||||
|
@override |
||||||
|
bool apply(List<bool> inputs) => inputs[0] || inputs[1]; |
||||||
|
|
||||||
|
@override |
||||||
|
List<String> get representations => const ['|', '∨']; |
||||||
|
|
||||||
|
@override |
||||||
|
bool operator==(other) => other is OrLogicOperator; |
||||||
|
|
||||||
|
@override |
||||||
|
int get hashCode => defaultRepresentation.hashCode; |
||||||
|
} |
||||||
|
|
||||||
|
class XorLogicOperator extends TwoOpLogicOperator { |
||||||
|
@override |
||||||
|
bool apply(List<bool> inputs) => inputs[0] != inputs[1]; |
||||||
|
|
||||||
|
@override |
||||||
|
List<String> get representations => const ['^', '⊕', '⊻']; |
||||||
|
|
||||||
|
@override |
||||||
|
bool operator==(other) => other is XorLogicOperator; |
||||||
|
|
||||||
|
@override |
||||||
|
int get hashCode => defaultRepresentation.hashCode; |
||||||
|
} |
||||||
|
|
||||||
|
class NandLogicOperator extends TwoOpLogicOperator { |
||||||
|
@override |
||||||
|
bool apply(List<bool> inputs) => !(inputs[0] && inputs[1]); |
||||||
|
|
||||||
|
@override |
||||||
|
List<String> get representations => const ['~&', '!&', '¬&', '~∧', '!∧', '¬∧']; |
||||||
|
|
||||||
|
@override |
||||||
|
bool operator==(other) => other is NandLogicOperator; |
||||||
|
|
||||||
|
@override |
||||||
|
int get hashCode => defaultRepresentation.hashCode; |
||||||
|
} |
||||||
|
|
||||||
|
class NorLogicOperator extends TwoOpLogicOperator { |
||||||
|
@override |
||||||
|
bool apply(List<bool> inputs) => !(inputs[0] || inputs[1]); |
||||||
|
|
||||||
|
@override |
||||||
|
List<String> get representations => const ['~|', '!|', '¬|', '~∨', '!∨', '¬∨']; |
||||||
|
|
||||||
|
@override |
||||||
|
bool operator==(other) => other is NorLogicOperator; |
||||||
|
|
||||||
|
@override |
||||||
|
int get hashCode => defaultRepresentation.hashCode; |
||||||
|
} |
||||||
|
|
||||||
|
class XnorLogicOperator extends TwoOpLogicOperator { |
||||||
|
@override |
||||||
|
bool apply(List<bool> inputs) => inputs[0] == inputs[1]; |
||||||
|
|
||||||
|
@override |
||||||
|
List<String> get representations => const ['~^', '!^', '¬^', '~⊕', '!⊕', '¬⊕', '~⊻', '!⊻', '¬⊻']; |
||||||
|
|
||||||
|
@override |
||||||
|
bool operator==(other) => other is XnorLogicOperator; |
||||||
|
|
||||||
|
@override |
||||||
|
int get hashCode => defaultRepresentation.hashCode; |
||||||
|
} |
Loading…
Reference in new issue