ethanblake4 / dart_eval

Extensible Dart interpreter for Dart with full interop
https://pub.dev/packages/dart_eval
BSD 3-Clause "New" or "Revised" License
334 stars 40 forks source link

Support for switch case #143

Open Noobware1 opened 1 year ago

Noobware1 commented 1 year ago

It is complicated to add? If not could you tell me how should I go on about it?

ethanblake4 commented 1 year ago

A full and complete implementation would be complicated, but a very simple implementation is probably fairly easy. Basically, you could just think of a switch case like:

switch(x) {
    case 1:
       i++;
       break;
    case 2:
       i += 2;
       break;
}

as a series of if/else expressions like:

if (x == 1) {
    i++;
} else if (x == 2) {
   i += 2;
}

That second block just uses if.dart and binary.dart to compile and you could look at what those do and basically copy it. To simplify things you could also say that only switch cases with break are allowed for now.

Noobware1 commented 11 months ago

@ethanblake4 I am ashamed to ask but I literally can't find a way to make this happen. I have to main problems with implementing switch cases

  1. how can I know which case to execute
  2. unlike IfStatement's .thenStatement SwitchStatement member has statements a NodeList how can I do anything with this.

I am really sorry for my shortcomings.

import 'package:dart_eval/src/eval/compiler/statement/statement.dart';
import 'package:dart_eval/src/eval/compiler/type.dart';
import 'package:dart_eval/src/eval/compiler/variable.dart';

StatementInfo compileSwitchStatement(
  SwitchStatement s,
  CompilerContext ctx,
  AlwaysReturnType? expectedReturnType,
) {
  final L = compileExpression(s.expression, ctx);

  Variable? R;
  for (var member in s.members) {
    if (member is SwitchPatternCase) {
      final pattern = member.guardedPattern.pattern;
      if (pattern is ConstantPattern) {
        R = compileExpression(pattern.expression, ctx);
        if (L == R) {
          break;
        }
      }
    }
  }
  return StatementInfo(-1, willAlwaysReturn: false, willAlwaysThrow: false);
}
ethanblake4 commented 11 months ago
  1. To generate code that checks if a variable L is equal to variable R (like does the variable match the expression of the switch case) you can do L.invoke(ctx, '==', [R]).

  2. Well basically you'd want to iterate through the NodeList and generate a macroBranch with an elseClause that generates a macroBranch for the next element in the list, so on and so forth, like try.dart does it here. Effectively turning something like this:

switch(x) {
    case 1:
       i++;
       break;
    case 2:
       i += 2;
       break;
    case 3;
       i += 3;
       break;
}

into

if (x == 1) {
    i++;
} else {
  if (x == 2) {
     i += 2;
  } else {
     if (x == 3) {
        i +=3;
     }
  }
}

If this is too hard then don't worry about it. It's not an ideal solution anyway because ultimately we'd actually want to construct a jump table and add a new switch bytecode.