Repository: ethanblake4/dart_eval

Issue:
Support bool literals
Support bool literals `true` and `false`



Fix patch:
diff --git a/lib/src/eval/compiler/builtins.dart b/lib/src/eval/compiler/builtins.dart
index 75c1d02d..6fbbff6f 100644
--- a/lib/src/eval/compiler/builtins.dart
+++ b/lib/src/eval/compiler/builtins.dart
@@ -7,13 +7,15 @@ import 'package:dart_eval/src/eval/compiler/variable.dart';
 const int dartCoreFile = -1;
 
 class BuiltinValue {
-  BuiltinValue({this.intval, this.doubleval, this.stringval}) {
+  BuiltinValue({this.intval, this.doubleval, this.stringval, this.boolval}) {
     if (intval != null) {
       type = BuiltinValueType.intType;
     } else if (stringval != null) {
       type = BuiltinValueType.stringType;
     } else if (doubleval != null) {
       type = BuiltinValueType.doubleType;
+    } else if (boolval != null) {
+      type = BuiltinValueType.boolType;
     } else {
       type = BuiltinValueType.nullType;
     }
@@ -23,6 +25,7 @@ class BuiltinValue {
   final int? intval;
   final double? doubleval;
   final String? stringval;
+  final bool? boolval;
 
   Variable _push(CompilerContext ctx) {
     if (type == BuiltinValueType.intType) {
@@ -35,6 +38,14 @@ class BuiltinValue {
       final op = PushConstant.make(ctx.constantPool.addOrGet(stringval!));
       ctx.pushOp(op, PushConstant.LEN);
       return Variable.alloc(ctx, EvalTypes.stringType.copyWith(boxed: false));
+    } else if (type == BuiltinValueType.boolType) {
+      ctx.pushOp(PushTrue.make(), PushTrue.LEN);
+      var value = Variable.alloc(ctx, EvalTypes.boolType.copyWith(boxed: false));
+      if (!boolval!) {
+        ctx.pushOp(LogicalNot.make(value.scopeFrameOffset), LogicalNot.LEN);
+        value = Variable.alloc(ctx, EvalTypes.boolType.copyWith(boxed: false));
+      }
+      return value;
     } else if (type == BuiltinValueType.nullType) {
       final op = PushNull.make();
       ctx.pushOp(op, PushNull.LEN);
@@ -53,7 +64,7 @@ class BuiltinValue {
   }
 }
 
-enum BuiltinValueType { intType, stringType, doubleType, nullType }
+enum BuiltinValueType { intType, stringType, doubleType, boolType, nullType }
 
 class KnownMethod {
   const KnownMethod(this.returnType, this.args, this.namedArgs);
@@ -136,6 +147,9 @@ final numBinaryOp = KnownMethod(
     [KnownMethodArg('other', EvalTypes.numType, false, false)],
     {});
 
+final boolBinaryOp = KnownMethod(
+    AlwaysReturnType(EvalTypes.boolType, false), [KnownMethodArg('other', EvalTypes.boolType, false, false)], {});
+
 const listIndexOp =
     KnownMethod(TargetTypeArgDependentReturnType(0), [KnownMethodArg('index', EvalTypes.intType, false, false)], {});
 
@@ -188,6 +202,10 @@ final Map<TypeRef, Map<String, KnownMethod>> knownMethods = {
     '==': numComparisonOp,
     ..._knownObject
   },
+  EvalTypes.boolType: {
+    '&&': boolBinaryOp,
+    '||': boolBinaryOp
+  },
   EvalTypes.stringType: {
     '+': KnownMethod(AlwaysReturnType(EvalTypes.stringType, false),
         [KnownMethodArg('other', EvalTypes.stringType, false, false)], {}),
diff --git a/lib/src/eval/compiler/expression/binary.dart b/lib/src/eval/compiler/expression/binary.dart
index 30cab157..436c4602 100644
--- a/lib/src/eval/compiler/expression/binary.dart
+++ b/lib/src/eval/compiler/expression/binary.dart
@@ -21,7 +21,9 @@ Variable compileBinaryExpression(CompilerContext ctx, BinaryExpression e) {
     TokenType.LT_EQ: '<=',
     TokenType.GT_EQ: '>=',
     TokenType.PERCENT: '%',
-    TokenType.EQ_EQ: '=='
+    TokenType.EQ_EQ: '==',
+    TokenType.AMPERSAND_AMPERSAND: '&&',
+    TokenType.BAR_BAR: '||'
   };
 
   var method = opMap[e.operator.type] ?? (throw CompileError('Unknown binary operator ${e.operator.type}'));
diff --git a/lib/src/eval/compiler/expression/literal.dart b/lib/src/eval/compiler/expression/literal.dart
index ac845128..7cc6dd95 100644
--- a/lib/src/eval/compiler/expression/literal.dart
+++ b/lib/src/eval/compiler/expression/literal.dart
@@ -1,3 +1,5 @@
+import 'dart:ffi';
+
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:dart_eval/src/eval/compiler/collection/list.dart';
 import 'package:dart_eval/src/eval/compiler/collection/set_map.dart';
@@ -14,6 +16,8 @@ BuiltinValue parseConstLiteral(Literal l, CompilerContext ctx) {
     return BuiltinValue(doubleval: l.value);
   } else if (l is SimpleStringLiteral) {
     return BuiltinValue(stringval: l.stringValue);
+  } else if (l is BooleanLiteral) {
+    return BuiltinValue(boolval: l.value);
   } else if (l is NullLiteral) {
     return BuiltinValue();
   }
@@ -21,7 +25,7 @@ BuiltinValue parseConstLiteral(Literal l, CompilerContext ctx) {
 }
 
 Variable parseLiteral(Literal l, CompilerContext ctx) {
-  if (l is IntegerLiteral || l is DoubleLiteral || l is SimpleStringLiteral || l is NullLiteral) {
+  if (l is IntegerLiteral || l is DoubleLiteral || l is SimpleStringLiteral || l is NullLiteral || l is BooleanLiteral) {
     return parseConstLiteral(l, ctx).push(ctx);
   }
   if (l is ListLiteral) {
diff --git a/lib/src/eval/compiler/variable.dart b/lib/src/eval/compiler/variable.dart
index 7c10d541..236be4da 100644
--- a/lib/src/eval/compiler/variable.dart
+++ b/lib/src/eval/compiler/variable.dart
@@ -58,6 +58,8 @@ class Variable {
       ctx.pushOp(BoxNum.make(scopeFrameOffset), BoxNum.LEN);
     } else if (type == EvalTypes.doubleType) {
       ctx.pushOp(BoxDouble.make(scopeFrameOffset), BoxDouble.LEN);
+    } else if (type == EvalTypes.boolType) {
+      ctx.pushOp(BoxBool.make(scopeFrameOffset), BoxBool.LEN);
     } else if (type == EvalTypes.listType) {
       if (!type.specifiedTypeArgs[0].boxed && ctx is CompilerContext) {
         V2 = boxListContents(ctx, this);
diff --git a/lib/src/eval/runtime/ops/all_ops.dart b/lib/src/eval/runtime/ops/all_ops.dart
index 6188a171..dbed70f5 100644
--- a/lib/src/eval/runtime/ops/all_ops.dart
+++ b/lib/src/eval/runtime/ops/all_ops.dart
@@ -162,6 +162,15 @@ class Evc {
   /// [BoxMap]
   static const OP_BOXMAP = 51;
 
+  /// [PushTrue]
+  static const OP_PUSH_TRUE = 52;
+
+  /// [LogicalNot]
+  static const OP_LOGICAL_NOT = 53;
+
+  /// [BoxBool]
+  static const OP_BOXBOOL = 54;
+
   static List<int> i16b(int i16) {
     final x = ByteData(2);
     x.setInt16(0, i16);
@@ -256,5 +265,8 @@ final List<OpLoader> ops = [
   (Runtime rt) => PushConstantDouble(rt), // 48
   (Runtime rt) => SetGlobal(rt), // 49
   (Runtime rt) => LoadGlobal(rt), // 50
-  (Runtime rt) => BoxMap(rt), // 37
+  (Runtime rt) => BoxMap(rt), // 51
+  (Runtime rt) => PushTrue(rt), // 52
+  (Runtime rt) => LogicalNot(rt), // 53
+  (Runtime rt) => BoxBool(rt), // 54
 ];
diff --git a/lib/src/eval/runtime/ops/primitives.dart b/lib/src/eval/runtime/ops/primitives.dart
index bf01fc50..b3c646c7 100644
--- a/lib/src/eval/runtime/ops/primitives.dart
+++ b/lib/src/eval/runtime/ops/primitives.dart
@@ -454,3 +454,58 @@ class IndexMap extends EvcOp {
   @override
   String toString() => 'IndexMap (L$_map[L$_index])';
 }
+
+class PushTrue extends EvcOp {
+  PushTrue(Runtime runtime);
+
+  PushTrue.make();
+
+  static const int LEN = Evc.BASE_OPLEN;
+
+  @override
+  void run(Runtime runtime) {
+    final frame = runtime.frame;
+    frame[runtime.frameOffset++] = true;
+  }
+
+  @override
+  String toString() => 'PushTrue ()';
+}
+
+class LogicalNot extends EvcOp {
+  LogicalNot(Runtime runtime): _index = runtime._readInt16();
+
+  LogicalNot.make(this._index);
+
+  final int _index;
+
+  static const int LEN = Evc.BASE_OPLEN + Evc.I16_LEN;
+
+  @override
+  void run(Runtime runtime) {
+    final frame = runtime.frame;
+    frame[runtime.frameOffset++] = !(frame[_index] as bool);
+  }
+
+  @override
+  String toString() => 'LogicalNot (L$_index)';
+}
+
+class BoxBool implements EvcOp {
+  BoxBool(Runtime runtime) : _reg = runtime._readInt16();
+
+  BoxBool.make(this._reg);
+
+  final int _reg;
+
+  static const int LEN = Evc.BASE_OPLEN + Evc.I16_LEN;
+
+  @override
+  void run(Runtime runtime) {
+    final reg = _reg;
+    runtime.frame[reg] = $bool(runtime.frame[reg] as bool);
+  }
+
+  @override
+  String toString() => 'BoxBool (L$_reg)';
+}
\ No newline at end of file
diff --git a/lib/src/eval/runtime/runtime.dart b/lib/src/eval/runtime/runtime.dart
index ee2b8c76..ab5f4a64 100644
--- a/lib/src/eval/runtime/runtime.dart
+++ b/lib/src/eval/runtime/runtime.dart
@@ -320,6 +320,9 @@ class Runtime {
       case BoxMap:
         op as BoxMap;
         return [Evc.OP_BOXMAP, ...Evc.i16b(op._reg)];
+      case BoxBool:
+        op as BoxBool;
+        return [Evc.OP_BOXBOOL, ...Evc.i16b(op._reg)];
       case PushCaptureScope:
         op as PushCaptureScope;
         return [Evc.OP_CAPTURE_SCOPE];
@@ -353,6 +356,12 @@ class Runtime {
       case LoadGlobal:
         op as LoadGlobal;
         return [Evc.OP_LOAD_GLOBAL, ...Evc.i32b(op._index)];
+      case PushTrue:
+        op as PushTrue;
+        return [Evc.OP_PUSH_TRUE];
+      case LogicalNot:
+        op as LogicalNot;
+        return [Evc.OP_LOGICAL_NOT, ...Evc.i16b(op._index)];
       default:
         throw ArgumentError('Not a valid op $op');
     }
diff --git a/lib/src/eval/shared/stdlib/core/base.dart b/lib/src/eval/shared/stdlib/core/base.dart
index 1566d720..9e678ba1 100644
--- a/lib/src/eval/shared/stdlib/core/base.dart
+++ b/lib/src/eval/shared/stdlib/core/base.dart
@@ -70,6 +70,10 @@ class $bool implements $Instance {
   @override
   $Value? $getProperty(Runtime runtime, String identifier) {
     switch (identifier) {
+      case '&&':
+        return __and;
+      case '||':
+        return __or;
     }
     return _superclass.$getProperty(runtime, identifier);
   }
@@ -77,6 +81,20 @@ class $bool implements $Instance {
   @override
   void $setProperty(Runtime runtime, String identifier, $Value value) {}
 
+  static const $Function __and = $Function(_and);
+
+  static $Value? _and(Runtime runtime, $Value? target, List<$Value?> args) {
+    final other = args[0];
+    return $bool(target!.$value && other!.$value);
+  }
+
+  static const $Function __or = $Function(_or);
+
+  static $Value? _or(Runtime runtime, $Value? target, List<$Value?> args) {
+    final other = args[0];
+    return $bool(target!.$value || other!.$value);
+  }
+
   @override
   bool get $reified => $value;
 


Test patch:
diff --git a/test/dart_eval_test.dart b/test/dart_eval_test.dart
index 09e6b2c9..02971575 100644
--- a/test/dart_eval_test.dart
+++ b/test/dart_eval_test.dart
@@ -556,6 +556,45 @@ void main() {
         exec.executeLib('package:example/main.dart', 'main');
       }, prints('ffy\n'));
     });
+
+    test('Boolean literals', () {
+      final exec = compiler.compileWriteAndLoad({
+        'example': {
+          'main.dart': '''
+            bool main() {
+              final a = true;
+              final b = false;
+              print(a);
+              print(b);
+              return b;
+            }
+          ''',
+        }
+      });
+      expect(() {
+        final a = exec.executeLib('package:example/main.dart', 'main');
+        expect(a, equals(false));
+      }, prints('true\nfalse\n'));
+    });
+
+    test('Boxed bools, logical && and ||', () {
+      final exec = compiler.compileWriteAndLoad({
+        'example': {
+          'main.dart': '''
+            dynamic main() {
+              final a = true;
+              final b = false;
+              print(a && b);
+              print(a || b);
+              return b && a;
+            }
+          ''',
+        }
+      });
+      expect(() {
+        expect(exec.executeLib('package:example/main.dart', 'main'), $bool(false));
+      }, prints('false\ntrue\n'));
+    });
   });
 
   group('Bridge tests', () {