Adding a New Keyword to the Dart Language

 I'm attempting to add the keyword: foobar which will translate to a literal int 5.

Steps:

  1. Modified lib/src/scanner/token.dart to add "foobar" keyword
  2. Looking at where the Keyword TRUE is used
    1. parser_impl.dart
    2. keyword_contributor.dart
    3. ast_test_factory.dart
    4. tokens_context.dart
    5. tokens_writer.dart
    6. unlinked_token_type.dart
    7. lax_json.dart
    8. <various tests>
  3. Likely need to update: https://github.com/dart-lang/sdk/blob/master/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart#L5362 in order to produce correct AST.
  4. Compiling dart program with "foobar" expecting error...

SDK diff:

# compiling sdk
ninja -C out/ios_debug_sim_unopt                                                       

ninja: Entering directory `out/ios_debug_sim_unopt'
[1/1] Regenerating ninja files
[50/50] STAMP obj/default.stamp

# running on simulator
flutter run --local-engine-src-path='../flojure-engine/src'  --local-engine=ios_debug_sim_unopt

It did not throw an error compiling instead complained that variable was not defined.

dart bin/starter.dart --sdk-root='../../out/ios_debug_sim_unopt/flutter_patched_sdk' --target=flutter tmp/hello.dart                                                                                                                         [ruby-2.6.3p62]
result 8b200e8d-fbf3-4de7-8376-56ccd7e8b320
tmp/hello.dart:3:13: Error: 'foobar' can't be used as an identifier because it's a keyword.
Try renaming this to be an identifier that isn't a keyword.
  final x = foobar;
            ^^^^^^
tmp/hello.dart:3:13: Error: Getter not found: 'foobar'.
  final x = foobar;
            ^^^^^^
8b200e8d-fbf3-4de7-8376-56ccd7e8b320
+file:///Users/johnmcconnell/dev/github.com/johnmcconnell/flojure-engine/src/flutter/flutter_frontend_server/tmp/hello.dart
8b200e8d-fbf3-4de7-8376-56ccd7e8b320 tmp/hello.dart.dill 2


Compiling from frontend server doesn't require recompiling.
Compiling the program:

void main() {
  final x = foobar;
  print('hello world!: $x');
}

$ dart bin/starter.dart --sdk-root='../../out/ios_debug_sim_unopt/flutter_patched_sdk' --target=flutter tmp/hello.dart                                                                       

result df088500-2afb-4488-9013-a23f2c1a5ae8
df088500-2afb-4488-9013-a23f2c1a5ae8
+file:///Users/johnmcconnell/dev/github.com/johnmcconnell/flojure-engine/src/flutter/flutter_frontend_server/tmp/hello.dart
df088500-2afb-4488-9013-a23f2c1a5ae8 tmp/hello.dart.dill 0
$ dart run tmp/hello.dart.dill                                                                                                                                             
hello world!: 5

It worked. However, using flutter and setting the local engine path doesn't seem to work.


Code diff:
diff --git i/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart w/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart
index 93d7a7b9671..b943bd90600 100644
--- i/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart
+++ w/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart
@@ -5289,6 +5275,11 @@ class Parser {
         return parseLiteralBool(token);
       } else if (identical(value, "null")) {
         return parseLiteralNull(token);
+      } else if (identical(value, "foobar")) {
+        final t = StringToken(TokenType.INT, "5", token.next!.charOffset);
+        t.setNext(token.next!.next!);
+        listener.handleLiteralInt(t);
+        return t;
       } else if (identical(value, "this")) {
         return parseThisExpression(token, context);
       } else if (identical(value, "super")) {
diff --git i/pkg/_fe_analyzer_shared/lib/src/scanner/token.dart w/pkg/_fe_analyzer_shared/lib/src/scanner/token.dart
index 4cc7473c8c7..b2e014533a2 100644
--- i/pkg/_fe_analyzer_shared/lib/src/scanner/token.dart
+++ w/pkg/_fe_analyzer_shared/lib/src/scanner/token.dart
@@ -340,6 +340,9 @@ class Keyword extends TokenType {
   static const Keyword YIELD =
       const Keyword("yield", "YIELD", KeywordStyle.pseudo);

+  static const Keyword FOOBAR =
+      const Keyword("foobar", "FOOBAR", KeywordStyle.reserved);
+
   static const List<Keyword> values = const <Keyword>[
     ABSTRACT,
     AS,
@@ -368,6 +371,7 @@ class Keyword extends TokenType {
     FINAL,
     FINALLY,
     FOR,
+    FOOBAR,
     FUNCTION,
     GET,
     HIDE,


Finish for now.
Error with running from flutter (vs the dart command):


/Users/johnmcconnell/dev/github.com/johnmcconnell/flojure-example-app/.dart_tool/flutter_build/a5b6813f1f6085abf8595dcd34b5ad4f/kernel_snapshot.d package:flojure_example_app/main.dart
               [+2644 ms] lib/main.dart:11:15: Error: The getter 'foobar' isn't defined for the class 'MyApp'.
               [   +1 ms]  - 'MyApp' is from 'package:flojure_example_app/main.dart' ('lib/main.dart').
               [        ] Try correcting the name to the name of an existing getter, or defining a getter or field named 'foobar'.
               [        ]     final x = foobar;
               [        ]               ^^^^^^
               [+6619 ms] Persisting file store
               [   +4 ms] Done persisting file store
               [   +3 ms] Target kernel_snapshot failed: Exception
                          #0      KernelSnapshot.build (package:flutter_tools/src/build_system/targets/common.dart:291:7)
                          <asynchronous suspension>
                          #1      _BuildInstance._invokeInternal (package:flutter_tools/src/build_system/build_system.dart:828:9)
                          <asynchronous suspension>
                          #2      Future.wait.<anonymous closure> (dart:async/future.dart)
                          <asynchronous suspension>
                          #3      _BuildInstance.invokeTarget (package:flutter_tools/src/build_system/build_system.dart:766:32)
                          <asynchronous suspension>
                          #4      FlutterBuildSystem.build (package:flutter_tools/src/build_system/build_system.dart:595:16)
                          <asynchronous suspension>
                          #5      AssembleCommand.runCommand (package:flutter_tools/src/commands/assemble.dart:318:32)
                          <asynchronous suspension>
                          #6      FlutterCommand.run.<anonymous closure> (package:flutter_tools/src/runner/flutter_command.dart:1043:27)
                          <asynchronous suspension>
                          #7      AppContext.run.<anonymous closure> (package:flutter_tools/src/base/context.dart:150:19)
                          <asynchronous suspension>
                          #8      CommandRunner.runCommand (package:args/command_runner.dart:196:13)
                          <asynchronous suspension>
                          #9      FlutterCommandRunner.runCommand.<anonymous closure> (package:flutter_tools/src/runner/flutter_command_runner.dart:284:9)
                          <asynchronous suspension>
                          #10     AppContext.run.<anonymous closure> (package:flutter_tools/src/base/context.dart:150:19)
                          <asynchronous suspension>
                          #11     FlutterCommandRunner.runCommand (package:flutter_tools/src/runner/flutter_command_runner.dart:232:5)
                          <asynchronous suspension>
                          #12     run.<anonymous closure>.<anonymous closure> (package:flutter_tools/runner.dart:62:9)
                          <asynchronous suspension>
                          #13     AppContext.run.<anonymous closure> (package:flutter_tools/src/base/context.dart:150:19)
                          <asynchronous suspension>
                          #14     main (package:flutter_tools/executable.dart:91:3)
                          <asynchronous suspension>

               [  +13 ms] "flutter assemble" took 16,412ms.
               [   +4 ms]
                          #0      throwToolExit (package:flutter_tools/src/base/common.dart:10:3)
                          #1      AssembleCommand.runCommand (package:flutter_tools/src/commands/assemble.dart:335:7)
                          <asynchronous suspension>
                          #2      FlutterCommand.run.<anonymous closure> (package:flutter_tools/src/runner/flutter_command.dart:1043:27)
                          <asynchronous suspension>
                          #3      AppContext.run.<anonymous closure> (package:flutter_tools/src/base/context.dart:150:19)
                          <asynchronous suspension>
                          #4      CommandRunner.runCommand (package:args/command_runner.dart:196:13)
                          <asynchronous suspension>
                          #5      FlutterCommandRunner.runCommand.<anonymous closure> (package:flutter_tools/src/runner/flutter_command_runner.dart:284:9)
                          <asynchronous suspension>
                          #6      AppContext.run.<anonymous closure> (package:flutter_tools/src/base/context.dart:150:19)
                          <asynchronous suspension>
                          #7      FlutterCommandRunner.runCommand (package:flutter_tools/src/runner/flutter_command_runner.dart:232:5)
                          <asynchronous suspension>
                          #8      run.<anonymous closure>.<anonymous closure> (package:flutter_tools/runner.dart:62:9)
                          <asynchronous suspension>
                          #9      AppContext.run.<anonymous closure> (package:flutter_tools/src/base/context.dart:150:19)
                          <asynchronous suspension>
                          #10     main (package:flutter_tools/executable.dart:91:3)
                          <asynchronous suspension>

Error occurs running this assemble command:


$ ../flojure-engine/src/out/host_debug_unopt/dart-sdk/bin/dart \
  --disable-dart-dev ../flojure-engine/src/out/host_debug_unopt/gen/frontend_server.dart.snapshot \
    --sdk-root ../flojure-engine/src/out/ios_debug_sim_unopt/flutter_patched_sdk/ \
    --target=flutter --no-print-incremental-dependencies -DFLUTTER_WEB_AUTO_DETECT=true \
    -Ddart.vm.profile=false -Ddart.vm.product=false --enable-asserts --track-widget-creation \
    --no-link-platform \
    --packages /Users/johnmcconnell/dev/github.com/johnmcconnell/flojure-example-app/.dart_tool/package_config.json \
    --output-dill /Users/johnmcconnell/dev/github.com/johnmcconnell/flojure-example-app/.dart_tool/flutter_build/a5b6813f1f6085abf8595dcd34b5ad4f/app.dill \
    --depfile /Users/johnmcconnell/dev/github.com/johnmcconnell/flojure-example-app/.dart_tool/flutter_build/a5b6813f1f6085abf8595dcd34b5ad4f/kernel_snapshot.d package:flojure_example_app/main.dart

However, it is still able to create an app.dill which can be run via: 


 dart run .dart_tool/flutter_build/a5b6813f1f6085abf8595dcd34b5ad4f/app.dill                                                                                               
Unhandled exception:
'package:flojure_example_app/main.dart': error: lib/main.dart:4:13: Error: Getter not found: 'foobar'.
  final x = foobar;
            ^^^^^^
#0      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:283:19)
#1      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)

So it looks like both compilation and runtime fail.

The assemble command is using the host_debug_unopt sdk . Rebuilding the host_debug_unopt sdk with latest language modifications worked as expected:


import 'package:flutter/material.dart';

void main() {
  final x = foobar;
  print('hello world!: $x');
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    final x = foobar;

    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page: $x'),
    );
  }
}



Success. Now time to create an parser in clojure that can export the results to dart. I will need to analyze the AST structure of clojure and dart and see if that are compatible.



Comments

Popular posts from this blog

Setting Up Your Own Local Copy of the Flutter Engine

Discovering How to Extend the Dart Language